From e75218e5575fe2a4489f6c01c7ed3d814f80fb84 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 5 Jul 2023 12:56:37 +1000 Subject: [PATCH] Improvements for reports --- app/Export/CSV/BaseExport.php | 295 +++++++++++++++++++++++++++++++ app/Export/CSV/CreditExport.php | 25 ++- app/Export/CSV/InvoiceExport.php | 1 + 3 files changed, 318 insertions(+), 3 deletions(-) diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index f2a4e1001d84..a0a04a2a234d 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -11,11 +11,16 @@ namespace App\Export\CSV; +use App\Utils\Number; use App\Models\Client; use App\Models\Invoice; +use League\Fractal\Manager; use Illuminate\Support\Carbon; use App\Utils\Traits\MakesHash; +use App\Transformers\ClientTransformer; +use App\Transformers\PaymentTransformer; use Illuminate\Database\Eloquent\Builder; +use League\Fractal\Serializer\ArraySerializer; class BaseExport { @@ -35,6 +40,191 @@ class BaseExport public array $forced_keys = []; + protected array $client_report_keys = [ + "name" => "client.name", + "user" => "client.user_id", + "balance" => "client.balance", + "paid_to_date" => "client.paid_to_date", + "currency" => "client.currency_id", + "website" => "client.website", + "private_notes" => "client.private_notes", + "industry" => "client.industry_id", + "size" => "client.size_id", + "address1" => "client.address1", + "address2" => "client.address2", + "city" => "client.city", + "state" => "client.state", + "postal_code" => "client.postal_code", + "country" => "client.country_id", + "custom_value4" => "contact.custom_value4", + "shipping_address1" => "client.shipping_address1", + "shipping_address2" => "client.shipping_address2", + "shipping_city" => "client.shipping_city", + "shipping_state" => "client.shipping_state", + "shipping_postal_code" => "client.shipping_postal_code", + "shipping_country" => "client.shipping_country_id", + "payment_terms" => "client.payment_terms", + "vat_number" => "client.vat_number", + "id_number" => "client.id_number", + "public_notes" => "client.public_notes", + "phone" => "contact.phone", + "first_name" => "contact.first_name", + "last_name" => "contact.last_name", + "email" => "contact.email", + ]; + + protected array $invoice_report_keys = [ + "invoice_number" => "invoice.number", + "amount" => "invoice.amount", + "balance" => "invoice.balance", + "paid_to_date" => "invoice.paid_to_date", + "discount" => "item.discount", + "po_number" => "invoice.po_number", + "date" => "invoice.date", + "due_date" => "invoice.due_date", + "terms" => "invoice.terms", + "footer" => "invoice.footer", + "status" => "invoice.status", + "public_notes" => "invoice.public_notes", + "private_notes" => "invoice.private_notes", + "uses_inclusive_taxes" => "invoice.uses_inclusive_taxes", + "is_amount_discount" => "invoice.is_amount_discount", + "partial" => "invoice.partial", + "partial_due_date" => "invoice.partial_due_date", + "custom_value1" => "item.custom_value1", + "custom_value2" => "item.custom_value2", + "custom_value3" => "item.custom_value3", + "custom_value4" => "item.custom_value4", + "surcharge1" => "invoice.custom_surcharge1", + "surcharge2" => "invoice.custom_surcharge2", + "surcharge3" => "invoice.custom_surcharge3", + "surcharge4" => "invoice.custom_surcharge4", + "exchange_rate" => "invoice.exchange_rate", + "tax_amount" => "invoice.total_taxes", + "quantity" => "item.quantity", + "cost" => "item.cost", + "product_key" => "item.product_key", + "notes" => "item.notes", + "item_tax1" => "item.tax_name1", + "item_tax_rate1" => "item.tax_rate1", + "item_tax2" => "item.tax_name2", + "item_tax_rate2" => "item.tax_rate2", + "item_tax3" => "item.tax_name3", + "item_tax_rate3" => "item.tax_rate3", + "type" => "item.type_id", + "tax_category" => "item.tax_id", + "assigned_user" => "invoice.assigned_user_id", + "user" => "invoice.user_id", + ]; + + protected array $quote_report_keys = [ + "quote_number" => "quote.number", + "amount" => "quote.amount", + "balance" => "quote.balance", + "paid_to_date" => "quote.paid_to_date", + "discount" => "item.discount", + "po_number" => "quote.po_number", + "date" => "quote.date", + "due_date" => "quote.due_date", + "terms" => "quote.terms", + "footer" => "quote.footer", + "status" => "quote.status", + "public_notes" => "quote.public_notes", + "private_notes" => "quote.private_notes", + "uses_inclusive_taxes" => "quote.uses_inclusive_taxes", + "is_amount_discount" => "quote.is_amount_discount", + "partial" => "quote.partial", + "partial_due_date" => "quote.partial_due_date", + "custom_value1" => "item.custom_value1", + "custom_value2" => "item.custom_value2", + "custom_value3" => "item.custom_value3", + "custom_value4" => "item.custom_value4", + "surcharge1" => "quote.custom_surcharge1", + "surcharge2" => "quote.custom_surcharge2", + "surcharge3" => "quote.custom_surcharge3", + "surcharge4" => "quote.custom_surcharge4", + "exchange_rate" => "quote.exchange_rate", + "tax_amount" => "quote.total_taxes", + "quantity" => "item.quantity", + "cost" => "item.cost", + "product_key" => "item.product_key", + "notes" => "item.notes", + "item_tax1" => "item.tax_name1", + "item_tax_rate1" => "item.tax_rate1", + "item_tax2" => "item.tax_name2", + "item_tax_rate2" => "item.tax_rate2", + "item_tax3" => "item.tax_name3", + "item_tax_rate3" => "item.tax_rate3", + "type" => "item.type_id", + "tax_category" => "item.tax_id", + "assigned_user" => "quote.assigned_user_id", + "user" => "quote.user_id", + ]; + + protected array $credit_report_keys = [ + "credit_number" => "credit.number", + "amount" => "credit.amount", + "balance" => "credit.balance", + "paid_to_date" => "credit.paid_to_date", + "discount" => "item.discount", + "po_number" => "credit.po_number", + "date" => "credit.date", + "due_date" => "credit.due_date", + "terms" => "credit.terms", + "footer" => "credit.footer", + "status" => "credit.status", + "public_notes" => "credit.public_notes", + "private_notes" => "credit.private_notes", + "uses_inclusive_taxes" => "credit.uses_inclusive_taxes", + "is_amount_discount" => "credit.is_amount_discount", + "partial" => "credit.partial", + "partial_due_date" => "credit.partial_due_date", + "custom_value1" => "item.custom_value1", + "custom_value2" => "item.custom_value2", + "custom_value3" => "item.custom_value3", + "custom_value4" => "item.custom_value4", + "surcharge1" => "credit.custom_surcharge1", + "surcharge2" => "credit.custom_surcharge2", + "surcharge3" => "credit.custom_surcharge3", + "surcharge4" => "credit.custom_surcharge4", + "exchange_rate" => "credit.exchange_rate", + "tax_amount" => "credit.total_taxes", + "quantity" => "item.quantity", + "cost" => "item.cost", + "product_key" => "item.product_key", + "notes" => "item.notes", + "item_tax1" => "item.tax_name1", + "item_tax_rate1" => "item.tax_rate1", + "item_tax2" => "item.tax_name2", + "item_tax_rate2" => "item.tax_rate2", + "item_tax3" => "item.tax_name3", + "item_tax_rate3" => "item.tax_rate3", + "type" => "item.type_id", + "tax_category" => "item.tax_id", + "assigned_user" => "credit.assigned_user_id", + "user" => "credit.user_id", + ]; + + protected array $payment_report_keys = [ + "date" => "payment.date", + "amount" => "payment.amount", + "refunded" => "payment.refunded", + "applied" => "payment.applied", + "transaction_reference" => "payment.transaction_reference", + "currency" => "payment.currency", + "exchange_rate" => "payment.exchange_rate", + "number" => "payment.number", + "method" => "payment.method", + "status" => "payment.status", + "private_notes" => "payment.private_notes", + "custom_value1" => "payment.custom_value1", + "custom_value2" => "payment.custom_value2", + "custom_value3" => "payment.custom_value3", + "custom_value4" => "payment.custom_value4", + "user" => "payment.user_id", + "assigned_user" => "payment.assigned_user_id", + ]; + protected function filterByClients($query) { if (isset($this->input['client_id']) && $this->input['client_id'] != 'all') { @@ -50,6 +240,90 @@ class BaseExport return $query; } + protected function resolveKey($key, $entity, $transformer) :string + { + $parts = explode(".", $key); + + if(!is_array($parts) || count($parts) < 2) + return ''; + + match($parts[0]) { + 'client' => $value = $this->resolveClientKey($parts[1], $entity, $transformer), + 'invoice' => $value = $this->resolveInvoiceKey($parts[1], $entity, $transformer), + 'payment' => $value = $this->resolvePaymentKey($parts[1], $entity, $transformer), + default => $value = '' + }; + + return $value; + } + + private function resolveClientKey($column, $entity, $transformer) + { + $transformed_client = $transformer->includeClient($entity); + + $manager = new Manager(); + $manager->setSerializer(new ArraySerializer()); + $transformed_client = $manager->createData($transformed_client)->toArray(); + + if($column == 'name') + return $transformed_client['display_name']; + + if($column == 'user_id') + return $entity->client->user->present()->name(); + + if(array_key_exists($column, $transformed_client)) + return $transformed_client[$column]; + + $primary_contact = $entity->client->primary_contact()->first() ?? $entity->client->contacts()->first(); + + if($primary_contact && array_key_exists($column, $primary_contact->toArray())) + return $primary_contact->{$column}; + + return ''; + + } + + private function resolveInvoiceKey($column, $entity, $transformer) + { + if($transformer instanceof PaymentTransformer) + $transformed_invoices = $transformer->includeInvoices($entity); + } + + private function resolvePaymentKey($column, $entity, $transformer) + { + if($column == 'amount') + return $entity->payments()->exists() ? Number::formatMoney($entity->payments()->sum('paymentables.amount'), $entity->company) : ctrans('texts.unpaid'); + + if($column == 'refunded') { + return $entity->payments()->exists() ? Number::formatMoney($entity->payments()->sum('paymentables.refunded'), $entity->company) : 0; + } + + if($column == 'applied') { + $refunded = $entity->payments()->sum('paymentables.refunded'); + $amount = $entity->payments()->sum('paymentables.amount'); + + return $entity->payments()->exists() ? Number::formatMoney(($amount - $refunded), $entity->company) : 0; + } + + $payment = $entity->payments()->first(); + + if(!$payment) + return ''; + + $payment_transformer = new PaymentTransformer(); + $transformed_payment = $payment_transformer->transform($payment); + + if($column == 'status'){ + return $payment->stringStatus($transformed_payment['status_id']); + } + + if(array_key_exists($column, $transformed_payment)) + return $transformed_payment[$column]; + + return ''; + + } + protected function addInvoiceStatusFilter($query, $status): Builder { @@ -175,10 +449,31 @@ class BaseExport foreach (array_merge($this->input['report_keys'], $this->forced_keys) as $value) { $key = array_search($value, $this->entity_keys); + if(!$key) { + $key = array_search($value, $this->client_report_keys); + } + + if(!$key) { + $key = array_search($value, $this->invoice_report_keys); + } + + if(!$key) { + $key = array_search($value, $this->quote_report_keys); + } + + if(!$key) { + $key = array_search($value, $this->credit_report_keys); + } + + if(!$key) { + $key = array_search($value, $this->payment_report_keys); + } + $key = str_replace('item.', '', $key); $key = str_replace('invoice.', '', $key); $key = str_replace('client.', '', $key); $key = str_replace('contact.', '', $key); + $key = str_replace('payment.', '', $key); $header[] = ctrans("texts.{$key}"); } diff --git a/app/Export/CSV/CreditExport.php b/app/Export/CSV/CreditExport.php index e21ec1f5ec60..0f3ce39044ff 100644 --- a/app/Export/CSV/CreditExport.php +++ b/app/Export/CSV/CreditExport.php @@ -123,10 +123,19 @@ class CreditExport extends BaseExport foreach (array_values($this->input['report_keys']) as $key) { $keyval = array_search($key, $this->entity_keys); + if(!$keyval) + $keyval = array_search(str_replace("credit.", "", $key), $this->entity_keys) ?? $key; + + if(!$keyval) + $keyval = $key; + if (array_key_exists($key, $transformed_credit)) { $entity[$keyval] = $transformed_credit[$key]; - } else { - $entity[$keyval] = ''; + } elseif (array_key_exists($keyval, $transformed_credit)) { + $entity[$keyval] = $transformed_credit[$keyval]; + } + else { + $entity[$keyval] = $this->resolveKey($keyval, $credit, $this->credit_transformer); } } @@ -138,9 +147,13 @@ class CreditExport extends BaseExport if (in_array('country_id', $this->input['report_keys'])) { $entity['country'] = $credit->client->country ? ctrans("texts.country_{$credit->client->country->name}") : ''; } + + if (in_array('client.country_id', $this->input['report_keys'])) { + $entity['client.country_id'] = $credit->client->country ? ctrans("texts.country_{$credit->client->country->name}") : ''; + } if (in_array('currency_id', $this->input['report_keys'])) { - $entity['currency_id'] = $credit->client->currency() ? $credit->client->currency()->code : $invoice->company->currency()->code; + $entity['currency_id'] = $credit->client->currency() ? $credit->client->currency()->code : $credit->company->currency()->code; } if (in_array('invoice_id', $this->input['report_keys'])) { @@ -155,6 +168,12 @@ class CreditExport extends BaseExport $entity['status'] = $credit->stringStatus($credit->status_id); } + if(in_array('credit.status', $this->input['report_keys'])) { + $entity['credit.status'] = $credit->stringStatus($credit->status_id); + } + + nlog($entity); + return $entity; } } diff --git a/app/Export/CSV/InvoiceExport.php b/app/Export/CSV/InvoiceExport.php index 2141358fa134..5086c0e05f29 100644 --- a/app/Export/CSV/InvoiceExport.php +++ b/app/Export/CSV/InvoiceExport.php @@ -80,6 +80,7 @@ class InvoiceExport extends BaseExport 'project', ]; + public function __construct(Company $company, array $input) { $this->company = $company;