diff --git a/app/Export/CSV/ProductSalesExport.php b/app/Export/CSV/ProductSalesExport.php index 505d0b72ba61..dbd138635332 100644 --- a/app/Export/CSV/ProductSalesExport.php +++ b/app/Export/CSV/ProductSalesExport.php @@ -11,14 +11,14 @@ namespace App\Export\CSV; -use App\Libraries\MultiDB; -use App\Models\Company; -use App\Models\Document; -use App\Models\Invoice; use App\Utils\Ninja; +use League\Csv\Writer; +use App\Models\Company; +use App\Models\Invoice; +use App\Models\Product; +use App\Libraries\MultiDB; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\App; -use League\Csv\Writer; class ProductSalesExport extends BaseExport { @@ -28,6 +28,13 @@ class ProductSalesExport extends BaseExport protected $date_key = 'created_at'; + protected $products; + + public Writer $csv; + + private $sales; + + //translations => keys protected array $entity_keys = [ 'custom_value1' => 'custom_value1', 'custom_value2' => 'custom_value2', @@ -35,23 +42,30 @@ class ProductSalesExport extends BaseExport 'custom_value4' => 'custom_value4', 'product_key' => 'product_key', 'notes' => 'notes', - 'cost' => 'cost', - 'price' => 'price', 'quantity' => 'quantity', - 'tax_rate1' => 'tax_rate1', - 'tax_rate2' => 'tax_rate2', - 'tax_rate3' => 'tax_rate3', + 'cost' => 'price', + 'price' => 'cost', + 'markup' => 'markup', + 'discount' => 'discount', + 'net_total' => 'net_total', + 'profit' => 'profit', 'tax_name1' => 'tax_name1', 'tax_name2' => 'tax_name2', 'tax_name3' => 'tax_name3', + 'tax_rate1' => 'tax_rate1', + 'tax_rate2' => 'tax_rate2', + 'tax_rate3' => 'tax_rate3', + 'tax_amount1' => 'tax_amount1', + 'tax_amount2' => 'tax_amount2', + 'tax_amount3' => 'tax_amount3', 'is_amount_discount' => 'is_amount_discount', - 'discount' => 'discount', 'line_total' => 'line_total', 'gross_line_total' => 'gross_line_total', 'status' => 'status', 'date' => 'date', 'currency' => 'currency', 'client' => 'client', + ]; private array $decorate_keys = [ @@ -64,6 +78,7 @@ class ProductSalesExport extends BaseExport { $this->company = $company; $this->input = $input; + $this->sales = collect(); } public function run() @@ -74,6 +89,8 @@ class ProductSalesExport extends BaseExport $t = app('translator'); $t->replace(Ninja::transformTranslations($this->company->settings)); + $this->products = Product::where('company_id', $this->company->id)->withTrashed()->get(); + //load the CSV document from a string $this->csv = Writer::createFromString(); @@ -99,6 +116,31 @@ class ProductSalesExport extends BaseExport } }); + + $grouped = $this->sales->groupBy('product_key')->map(function ($key, $value){ + + $data = [ + 'product' => $value, + 'quantity' => $key->sum('quantity'), + 'markup' => $key->sum('markup'), + 'profit' => $key->sum('profit'), + 'net_total' => $key->sum('net_total'), + 'discount' => $key->sum('discount'), + 'line_total' => $key->sum('line_total'), + 'tax_name1' => $key->whereNotNull('tax_name1')->where('tax_name1', '!=', '')->first() ? $key->whereNotNull('tax_name1')->where('tax_name1', '!=', '')->first()['tax_name1'] : '', + 'tax_name2' => $key->whereNotNull('tax_name2')->where('tax_name2', '!=', '')->first() ? $key->whereNotNull('tax_name2')->where('tax_name2', '!=', '')->first()['tax_name2'] : '', + 'tax_name3' => $key->whereNotNull('tax_name3')->where('tax_name3', '!=', '')->first() ? $key->whereNotNull('tax_name3')->where('tax_name3', '!=', '')->first()['tax_name3'] : '', + 'tax_amount1' => $key->sum('tax_amount1'), + 'tax_amount2' => $key->sum('tax_amount2'), + 'tax_amount3' => $key->sum('tax_amount3'), + ]; + + return $data; + }); + + + nlog($grouped); + return $this->csv->toString(); } @@ -117,17 +159,109 @@ class ProductSalesExport extends BaseExport $entity[$keyval] = ''; } } + $entity = $this->decorateAdvancedFields($invoice, $entity); + + $this->sales->push($entity); + + return $entity; - return $this->decorateAdvancedFields($invoice, $entity); } private function decorateAdvancedFields(Invoice $invoice, $entity) :array - { + { + $product = $this->getProduct($entity['product_key']); + + $entity['cost'] = $product->cost ?? 0; + $unit_cost = $entity['cost'] == 0 ? 1 : $entity['cost']; + $entity['client'] = $invoice->client->present()->name(); $entity['currency'] = $invoice->client->currency()->code; $entity['status'] = $invoice->stringStatus($invoice->status_id); $entity['date'] = Carbon::parse($invoice->date)->format($this->company->date_format()); + $entity['discount'] = $this->calculateDiscount($invoice, $entity); + $entity['markup'] = round(((($entity['price'] - $entity['discount'] - $entity['cost']) / $unit_cost) * 100),2); + + $entity['net_total'] = $entity['price'] - $entity['discount']; + $entity['profit'] = $entity['price'] - $entity['discount'] - $entity['cost']; + + if(strlen($entity['tax_name1']) > 1) { + $entity['tax_name1'] = $entity['tax_name1'] . ' [' . $entity['tax_rate1'] . '%]'; + $entity['tax_amount1'] = $this->calculateTax($invoice, $entity['line_total'], $entity['tax_rate1']); + } + else + $entity['tax_amount1'] = 0; + + if(strlen($entity['tax_name2']) > 1) { + $entity['tax_name2'] = $entity['tax_name2'] . ' [' . $entity['tax_rate2'] . '%]'; + $entity['tax_amount2'] = $this->calculateTax($invoice, $entity['line_total'], $entity['tax_rate2']); + } + else + $entity['tax_amount2'] = 0; + + if(strlen($entity['tax_name3']) > 1) { + $entity['tax_name3'] = $entity['tax_name3'] . ' [' . $entity['tax_rate3'] . '%]'; + $entity['tax_amount3'] = $this->calculateTax($invoice, $entity['line_total'], $entity['tax_rate3']); + } + else + $entity['tax_amount3'] = 0; + return $entity; } + + /** + * calculateTax + * + * @param mixed $invoice + * @param float $amount + * @param float $tax_rate + * @return float + */ + private function calculateTax(Invoice $invoice, float $amount, float $tax_rate): float + { + $amount = $amount - ($amount * ($invoice->discount / 100)); + + if($invoice->uses_inclusive_taxes) { + return round($amount - ($amount / (1 + ($tax_rate / 100))), 2); + } + else { + return round(($amount * $tax_rate / 100), 2); + } + + } + + + + /** + * calculateDiscount + * + * @param mixed $invoice + * @param mixed $entity + * @return float + */ + private function calculateDiscount(Invoice $invoice , $entity) :float + { + if($entity['discount'] == 0) + return 0; + + if($invoice->is_amount_discount && $entity['discount'] != 0) { + return $entity['discount']; + } + elseif(!$invoice->is_amount_discount && $entity['discount'] != 0) { + return round($entity['line_total'] * ($entity['discount'] / 100), 2); + } + + return 0; + } + + /** + * getProduct + * + * @param string $product_key + * @return Product + */ + private function getProduct(string $product_key) :?Product + { + return $this->products->firstWhere('product_key', $product_key); + } } diff --git a/app/Http/Requests/Report/ProductSalesReportRequest.php b/app/Http/Requests/Report/ProductSalesReportRequest.php index 87b0f5ddf22e..bd78da0afe16 100644 --- a/app/Http/Requests/Report/ProductSalesReportRequest.php +++ b/app/Http/Requests/Report/ProductSalesReportRequest.php @@ -36,7 +36,7 @@ class ProductSalesReportRequest extends Request 'start_date' => 'bail|required_if:date_range,custom|nullable|date', 'report_keys' => 'bail|present|array', 'send_email' => 'bail|required|bool', - 'client_id' => 'bail|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0', + 'client_id' => 'bail|nullable|sometimes|exists:clients,id,company_id,'.auth()->user()->company()->id.',is_deleted,0', ]; } diff --git a/lang/en/texts.php b/lang/en/texts.php index c9ec4d4daa47..85dab719673b 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5011,6 +5011,7 @@ $LANG = array( 'add_company_logo' => 'Add Logo', 'add_stripe' => 'Add Stripe', 'invalid_coupon' => 'Invalid Coupon', + 'product_sales' => 'Product Sales', );