From b127e12778d6508ff6c634b4df56a24318588e5b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 28 Apr 2022 14:02:59 +1000 Subject: [PATCH] Quote Items Export --- app/Export/CSV/QuoteItemExport.php | 202 ++++++++++++++++++ .../Reports/QuoteItemReportController.php | 84 ++++++++ app/Models/Quote.php | 24 +++ routes/api.php | 1 + 4 files changed, 311 insertions(+) create mode 100644 app/Export/CSV/QuoteItemExport.php create mode 100644 app/Http/Controllers/Reports/QuoteItemReportController.php diff --git a/app/Export/CSV/QuoteItemExport.php b/app/Export/CSV/QuoteItemExport.php new file mode 100644 index 000000000000..aa4f15b65579 --- /dev/null +++ b/app/Export/CSV/QuoteItemExport.php @@ -0,0 +1,202 @@ + 'amount', + 'balance' => 'balance', + 'client' => 'client_id', + 'custom_surcharge1' => 'custom_surcharge1', + 'custom_surcharge2' => 'custom_surcharge2', + 'custom_surcharge3' => 'custom_surcharge3', + 'custom_surcharge4' => 'custom_surcharge4', + 'custom_value1' => 'custom_value1', + 'custom_value2' => 'custom_value2', + 'custom_value3' => 'custom_value3', + 'custom_value4' => 'custom_value4', + 'date' => 'date', + 'discount' => 'discount', + 'due_date' => 'due_date', + 'exchange_rate' => 'exchange_rate', + 'footer' => 'footer', + 'number' => 'number', + 'paid_to_date' => 'paid_to_date', + 'partial' => 'partial', + 'partial_due_date' => 'partial_due_date', + 'po_number' => 'po_number', + 'private_notes' => 'private_notes', + 'public_notes' => 'public_notes', + 'status' => 'status_id', + 'tax_name1' => 'tax_name1', + 'tax_name2' => 'tax_name2', + 'tax_name3' => 'tax_name3', + 'tax_rate1' => 'tax_rate1', + 'tax_rate2' => 'tax_rate2', + 'tax_rate3' => 'tax_rate3', + 'terms' => 'terms', + 'total_taxes' => 'total_taxes', + 'currency' => 'currency_id', + 'qty' => 'item.quantity', + 'unit_cost' => 'item.cost', + 'product_key' => 'item.product_key', + 'cost' => 'item.product_cost', + 'notes' => 'item.notes', + 'discount' => 'item.discount', + 'is_amount_discount' => 'item.is_amount_discount', + 'tax_rate1' => 'item.tax_rate1', + 'tax_rate2' => 'item.tax_rate2', + 'tax_rate3' => 'item.tax_rate3', + 'tax_name1' => 'item.tax_name1', + 'tax_name2' => 'item.tax_name2', + 'tax_name3' => 'item.tax_name3', + 'line_total' => 'item.line_total', + 'gross_line_total' => 'item.gross_line_total', + 'invoice1' => 'item.custom_value1', + 'invoice2' => 'item.custom_value2', + 'invoice3' => 'item.custom_value3', + 'invoice4' => 'item.custom_value4', + ]; + + private array $decorate_keys = [ + 'client', + 'currency', + ]; + + public function __construct(Company $company, array $input) + { + $this->company = $company; + $this->input = $input; + $this->quote_transformer = new QuoteTransformer(); + } + + public function run() + { + + MultiDB::setDb($this->company->db); + App::forgetInstance('translator'); + App::setLocale($this->company->locale()); + $t = app('translator'); + $t->replace(Ninja::transformTranslations($this->company->settings)); + + //load the CSV document from a string + $this->csv = Writer::createFromString(); + + //insert the header + $this->csv->insertOne($this->buildHeader()); + + $query = Quote::query() + ->with('client')->where('company_id', $this->company->id) + ->where('is_deleted',0); + + $query = $this->addDateRange($query); + + $query->cursor() + ->each(function ($quote){ + + $this->iterateItems($quote); + + }); + + return $this->csv->toString(); + + } + + private function iterateItems(Quote $quote) + { + $transformed_quote = $this->buildRow($quote); + + $transformed_items = []; + + foreach($quote->line_items as $item) + { + $item_array = []; + + foreach(array_values($this->input['report_keys']) as $key){ + + if(str_contains($key, "item.")){ + + $key = str_replace("item.", "", $key); + $item_array[$key] = $item->{$key}; + } + + } + + $entity = []; + + $transformed_items = array_merge($transformed_quote, $item_array); + + $transformed_items = $this->decorateAdvancedFields($quote, $transformed_items); + + foreach(array_values($this->input['report_keys']) as $key) + { + $key = str_replace("item.", "", $key); + $entity[$key] = $transformed_items[$key]; + } + + $this->csv->insertOne($entity); + + } + + } + + private function buildRow(Quote $quote) :array + { + + $transformed_quote = $this->quote_transformer->transform($quote); + + $entity = []; + + foreach(array_values($this->input['report_keys']) as $key){ + + if(!str_contains($key, "item.")) + $entity[$key] = $transformed_quote[$key]; + + } + + return $this->decorateAdvancedFields($quote, $entity); + + } + + private function decorateAdvancedFields(Quote $quote, array $entity) :array + { + if(array_key_exists('currency_id', $entity)) + $entity['currency_id'] = $quote->client->currency()->code; + + if(array_key_exists('client_id', $entity)) + $entity['client_id'] = $quote->client->present()->name(); + + if(array_key_exists('status_id', $entity)) + $entity['status_id'] = $quote->stringStatus($quote->status_id); + + return $entity; + } + +} diff --git a/app/Http/Controllers/Reports/QuoteItemReportController.php b/app/Http/Controllers/Reports/QuoteItemReportController.php new file mode 100644 index 000000000000..f1700bf9dff8 --- /dev/null +++ b/app/Http/Controllers/Reports/QuoteItemReportController.php @@ -0,0 +1,84 @@ +user()->company(), $request->all()); + + $csv = $export->run(); + + $headers = array( + 'Content-Disposition' => 'attachment', + 'Content-Type' => 'text/csv', + ); + + return response()->streamDownload(function () use ($csv) { + echo $csv; + }, $this->filename, $headers); + + } + + + +} diff --git a/app/Models/Quote.php b/app/Models/Quote.php index 26542560398d..a917214948d9 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -283,6 +283,30 @@ class Quote extends BaseModel } } + public static function stringStatus(int $status) + { + switch ($status) { + case self::STATUS_DRAFT: + return ctrans('texts.draft'); + break; + case self::STATUS_SENT: + return ctrans('texts.pending'); + break; + case self::STATUS_APPROVED: + return ctrans('texts.approved'); + break; + case self::STATUS_EXPIRED: + return ctrans('texts.expired'); + break; + case self::STATUS_CONVERTED: + return ctrans('texts.converted'); + break; + default: + // code... + break; + } + } + /** * Check if the quote has been approved. * diff --git a/routes/api.php b/routes/api.php index c21c3e9ece27..bc270bdbf847 100644 --- a/routes/api.php +++ b/routes/api.php @@ -162,6 +162,7 @@ Route::group(['middleware' => ['throttle:300,1', 'api_db', 'token_auth', 'locale Route::post('reports/invoices', 'Reports\InvoiceReportController'); Route::post('reports/invoice_items', 'Reports\InvoiceItemReportController'); Route::post('reports/quotes', 'Reports\QuoteReportController'); + Route::post('reports/quote_items', 'Reports\QuoteItemReportController'); Route::post('reports/recurring_invoices', 'Reports\RecurringInvoiceReportController'); Route::post('reports/payments', 'Reports\PaymentReportController'); Route::post('reports/products', 'Reports\ProductReportController');