From 14b11ecbbd8065f8a7d2111b36705d1cf9f57b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 4 Nov 2020 11:22:43 +0100 Subject: [PATCH 1/4] Refactor total table columns: - $task.cost => $task.rate - $task.quantity => $task.hours --- app/DataMapper/CompanySettings.php | 4 ++-- app/Utils/HtmlEngine.php | 4 ++-- app/Utils/Traits/MakesTemplateData.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php index 24d668064b6c..25297e263bab 100644 --- a/app/DataMapper/CompanySettings.php +++ b/app/DataMapper/CompanySettings.php @@ -624,8 +624,8 @@ class CompanySettings extends BaseSettings 'task_columns' =>[ '$task.product_key', '$task.notes', - '$task.cost', - '$task.quantity', + '$task.rate', + '$task.hours', '$task.discount', '$task.tax', '$task.line_total', diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index e7e75a6c5764..4c33bf564fd6 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -304,8 +304,8 @@ class HtmlEngine $data['$task.discount'] = ['value' => '', 'label' => ctrans('texts.discount')]; $data['$task.product_key'] = ['value' => '', 'label' => ctrans('texts.product_key')]; $data['$task.notes'] = ['value' => '', 'label' => ctrans('texts.notes')]; - $data['$task.cost'] = ['value' => '', 'label' => ctrans('texts.cost')]; - $data['$task.quantity'] = ['value' => '', 'label' => ctrans('texts.quantity')]; + $data['$task.rate'] = ['value' => '', 'label' => ctrans('texts.rate')]; + $data['$task.hours'] = ['value' => '', 'label' => ctrans('texts.hours')]; $data['$task.tax'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$task.tax_name1'] = ['value' => '', 'label' => ctrans('texts.tax')]; $data['$task.tax_name2'] = ['value' => '', 'label' => ctrans('texts.tax')]; diff --git a/app/Utils/Traits/MakesTemplateData.php b/app/Utils/Traits/MakesTemplateData.php index edf69302edc1..599157576da7 100644 --- a/app/Utils/Traits/MakesTemplateData.php +++ b/app/Utils/Traits/MakesTemplateData.php @@ -193,8 +193,8 @@ trait MakesTemplateData $data['$task.discount'] = ['value' => '5%', 'label' => ctrans('texts.discount')]; $data['$task.product_key'] = ['value' => 'key', 'label' => ctrans('texts.product_key')]; $data['$task.notes'] = ['value' => 'Note for Tasks', 'label' => ctrans('texts.notes')]; - $data['$task.cost'] = ['value' => '$100.00', 'label' => ctrans('texts.cost')]; - $data['$task.quantity'] = ['value' => '1', 'label' => ctrans('texts.quantity')]; + $data['$task.rate'] = ['value' => '$100.00', 'label' => ctrans('texts.rate')]; + $data['$task.hours'] = ['value' => '1', 'label' => ctrans('texts.hours')]; $data['$task.tax_name1'] = ['value' => 'GST', 'label' => ctrans('texts.tax')]; $data['$task.tax_name2'] = ['value' => 'VAT', 'label' => ctrans('texts.tax')]; $data['$task.tax_name3'] = ['value' => 'CA Sales Tax', 'label' => ctrans('texts.tax')]; From e222aa86202c50bfbcf03d75186ec78abb485a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 4 Nov 2020 11:23:06 +0100 Subject: [PATCH 2/4] Refactor 'product-table-footer' to 'table-totals' --- app/Services/PdfMaker/Design.php | 10 +++++----- resources/views/pdf-designs/plain.html | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php index f4474c9ec90a..6c7d0563b875 100644 --- a/app/Services/PdfMaker/Design.php +++ b/app/Services/PdfMaker/Design.php @@ -106,9 +106,9 @@ class Design extends BaseDesign 'id' => 'product-table', 'elements' => $this->productTable(), ], - 'product-table-footer' => [ - 'id' => 'product-table-footer', - 'elements' => $this->tableFooter(), + 'table-totals' => [ + 'id' => 'table-totals', + 'elements' => $this->tableTotals(), ], 'footer-elements' => [ 'id' => 'footer', @@ -257,13 +257,13 @@ class Design extends BaseDesign return $elements; } - public function tableFooter() + public function tableTotals(): array { $variables = $this->context['pdf_variables']['total_columns']; $elements = [ ['element' => 'div', 'elements' => [ - ['element' => 'span', 'content' => '$entity.public_notes', 'properties' => ['data-element' => 'product-table-public-notes-label']], + ['element' => 'span', 'content' => '$entity.public_notes', 'properties' => ['data-element' => 'total-table-public-notes-label']], ]], ]; diff --git a/resources/views/pdf-designs/plain.html b/resources/views/pdf-designs/plain.html index c30170d40961..9fb4bc32e214 100644 --- a/resources/views/pdf-designs/plain.html +++ b/resources/views/pdf-designs/plain.html @@ -88,11 +88,11 @@ text-align: right; } - #product-table-footer { + #table-totals { page-break-inside: avoid; } - #product-table-footer > * { + #table-totals > * { display: grid; grid-template-columns: 3fr 1fr 1fr; padding-top: .5rem; @@ -100,16 +100,16 @@ gap: 20px; } - #product-table-footer + #table-totals > * - [data-element='product-table-balance-due-label'], - #product-table-footer + [data-element='total-table-balance-due-label'], + #table-totals > * - [data-element='product-table-balance-due'] { + [data-element='total-table-balance-due'] { font-weight: bold; } - #product-table-footer > * > :last-child { + #table-totals > * > :last-child { text-align: right; padding-right: 1rem; } @@ -137,7 +137,7 @@
- +
From a01c57bd614c2b499c7575347011a3b80e8601da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 4 Nov 2020 14:42:29 +0100 Subject: [PATCH 3/4] Plain: Update references to #task-table --- resources/views/pdf-designs/plain.html | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/resources/views/pdf-designs/plain.html b/resources/views/pdf-designs/plain.html index 9fb4bc32e214..917422737842 100644 --- a/resources/views/pdf-designs/plain.html +++ b/resources/views/pdf-designs/plain.html @@ -58,7 +58,7 @@ flex-direction: column; } - #product-table { + #product-table, #task-table { min-width: 100%; table-layout: fixed; overflow-wrap: break-word; @@ -66,25 +66,29 @@ margin-bottom: 200px; } - #product-table > thead { + #product-table, #task-table > thead { text-align: left; } - #product-table > thead > tr > th { + #product-table > thead > tr > th, + #task-table > thead > tr > th { padding: 1rem; background-color: #e6e6e6; } - #product-table > thead > tr > th:last-child { + #product-table > thead > tr > th:last-child, + #task-table > thead > tr > th:last-child { text-align: right; } - #product-table > tbody > tr > td { + #product-table > tbody > tr > td, + #task-table > tbody > tr > td { border-bottom: 1px solid #e6e6e6; padding: 1rem; } - #product-table > tbody > tr > td:last-child { + #product-table > tbody > tr > td:last-child, + #task-table > tbody > tr > td:last-child { text-align: right; } @@ -137,6 +141,8 @@
+
+
From 5c2bfaa8ce4ada8570c40cc094b35669216b5070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20Beganovi=C4=87?= Date: Wed, 4 Nov 2020 14:56:08 +0100 Subject: [PATCH 4/4] Alpha testing on tasks: - Change 'products' to '$product' with design within: CreateEntityPdf, ActivityRepository, HtmlGenerationTest, Phantom - New 'task-table' element in the Services\PdfMaker\Design.php - buildTableHeader & buildTableBody are now more generic - processTaxColumns() now requires $type to be specified --- app/Jobs/Entity/CreateEntityPdf.php | 2 +- app/Repositories/ActivityRepository.php | 2 +- app/Services/PdfMaker/Design.php | 90 ++++++++++++++++--- .../Designs/Utilities/DesignHelpers.php | 41 ++++++--- app/Utils/PhantomJS/Phantom.php | 2 +- tests/Integration/HtmlGenerationTest.php | 2 +- 6 files changed, 110 insertions(+), 29 deletions(-) diff --git a/app/Jobs/Entity/CreateEntityPdf.php b/app/Jobs/Entity/CreateEntityPdf.php index 1b8e5f4a2d62..6d0a8f98806e 100644 --- a/app/Jobs/Entity/CreateEntityPdf.php +++ b/app/Jobs/Entity/CreateEntityPdf.php @@ -139,7 +139,7 @@ class CreateEntityPdf implements ShouldQueue 'client' => $this->entity->client, 'entity' => $this->entity, 'pdf_variables' => (array) $this->entity->company->settings->pdf_variables, - 'products' => $design->design->product, + '$product' => $design->design->product, ]), 'variables' => $html->generateLabelsAndValues(), 'options' => [ diff --git a/app/Repositories/ActivityRepository.php b/app/Repositories/ActivityRepository.php index fc0be7bbea77..78daafd6081c 100644 --- a/app/Repositories/ActivityRepository.php +++ b/app/Repositories/ActivityRepository.php @@ -133,7 +133,7 @@ class ActivityRepository extends BaseRepository 'client' => $entity->client, 'entity' => $entity, 'pdf_variables' => (array) $entity->company->settings->pdf_variables, - 'products' => $design->design->product, + '$product' => $design->design->product, ]), 'variables' => $html->generateLabelsAndValues(), 'options' => [ diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php index 6c7d0563b875..b3404a053376 100644 --- a/app/Services/PdfMaker/Design.php +++ b/app/Services/PdfMaker/Design.php @@ -106,6 +106,10 @@ class Design extends BaseDesign 'id' => 'product-table', 'elements' => $this->productTable(), ], + 'task-table' => [ + 'id' => 'task-table', + 'elements' => $this->taskTable(), + ], 'table-totals' => [ 'id' => 'table-totals', 'elements' => $this->tableTotals(), @@ -188,47 +192,95 @@ class Design extends BaseDesign return $elements; } + /** + * Parent method for building products table. + * + * @return array + */ public function productTable(): array { + $product_items = collect($this->entity->line_items)->filter(function ($item) { + return $item->type_id == 1; + }); + + if ($product_items->count() == 0) { + return []; + } + return [ - ['element' => 'thead', 'elements' => $this->buildTableHeader()], - ['element' => 'tbody', 'elements' => $this->buildTableBody()], + ['element' => 'thead', 'elements' => $this->buildTableHeader('product')], + ['element' => 'tbody', 'elements' => $this->buildTableBody('$product')], ]; } - public function buildTableHeader(): array + /** + * Parent method for building tasks table. + * + * @return array + */ + public function taskTable(): array { - $this->processTaxColumns(); + $task_items = collect($this->entity->line_items)->filter(function ($item) { + return $item->type_id = 2; + }); + + if ($task_items->count() == 0) { + return []; + } + + return [ + ['element' => 'thead', 'elements' => $this->buildTableHeader('task')], + ['element' => 'tbody', 'elements' => $this->buildTableBody('$task')], + ]; + } + + /** + * Generate the structure of table headers. () + * + * @param string $type "product" or "task" + * @return array + */ + public function buildTableHeader(string $type): array + { + $this->processTaxColumns($type); $elements = []; - foreach ($this->context['pdf_variables']["{$this->type}_columns"] as $column) { + foreach ($this->context['pdf_variables']["{$type}_columns"] as $column) { $elements[] = ['element' => 'th', 'content' => $column . '_label']; } return $elements; } - public function buildTableBody(): array + /** + * Generate the structure of table body. () + * + * @param string $type "$product" or "$task" + * @return array + */ + public function buildTableBody(string $type): array { $elements = []; - $items = $this->transformLineItems($this->entity->line_items); + $items = $this->transformLineItems($this->entity->line_items, $type); if (count($items) == 0) { return []; } + info($this->context); + foreach ($items as $row) { $element = ['element' => 'tr', 'elements' => []]; if ( - isset($this->context['products']) && - !empty($this->context['products']) && - !is_null($this->context['products']) + array_key_exists($type, $this->context) && + !empty($this->context[$type]) && + !is_null($this->context[$type]) ) { $document = new DOMDocument(); - $document->loadHTML($this->context['products'], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); + $document->loadHTML($this->context[$type], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); $td = $document->getElementsByTagName('tr')->item(0); @@ -246,8 +298,20 @@ class Design extends BaseDesign } } } else { - foreach ($this->context['pdf_variables']["{$this->type}_columns"] as $key => $cell) { - $element['elements'][] = ['element' => 'td', 'content' => $row[$cell]]; + $_type = Str::startsWith($type, '$') ? ltrim($type, '$') : $type; + + foreach ($this->context['pdf_variables']["{$_type}_columns"] as $key => $cell) { + // We want to keep aliases like these: + // $task.cost => $task.rate + // $task.quantity => $task.hours + + if ($cell == '$task.rate') { + $element['elements'][] = ['element' => 'td', 'content' => $row['$task.cost']]; + } else if ($cell == '$task.hours') { + $element['elements'][] = ['element' => 'td', 'content' => $row['$task.quantity']]; + } else { + $element['elements'][] = ['element' => 'td', 'content' => $row[$cell]]; + } } } diff --git a/app/Services/PdfMaker/Designs/Utilities/DesignHelpers.php b/app/Services/PdfMaker/Designs/Utilities/DesignHelpers.php index 5c9afc2670c2..7e2e98f0868c 100644 --- a/app/Services/PdfMaker/Designs/Utilities/DesignHelpers.php +++ b/app/Services/PdfMaker/Designs/Utilities/DesignHelpers.php @@ -94,35 +94,52 @@ trait DesignHelpers * * Logic below will help us calculate that & inject the result in the * global state of the $context (design state). - * + * + * @param string $type "product" or "task" * @return void */ - public function processTaxColumns(): void + public function processTaxColumns(string $type): void { - if (in_array('$product.tax', (array) $this->context['pdf_variables']['product_columns'])) { - $line_items = collect($this->entity->line_items); + if ($type == 'product') { + $type_id = 1; + } + + if ($type == 'task') { + $type_id = 2; + } + + // At the moment we pass "task" or "product" as type. + // However, "pdf_variables" contains "$task.tax" or "$product.tax" <-- Notice the dollar sign. + // This sprintf() will help us convert "task" or "product" into "$task" or "$product" without + // evaluating the variable. + + if (in_array(sprintf('%s%s.tax', '$', $type), (array) $this->context['pdf_variables']["{$type}_columns"])) { + $line_items = collect($this->entity->line_items)->filter(function ($item) use ($type_id) { + return $item->type_id = $type_id; + }); + + $tax1 = $line_items->where('tax_name1', '<>', '')->where('type_id', $type_id)->count(); + $tax2 = $line_items->where('tax_name2', '<>', '')->where('type_id', $type_id)->count(); + $tax3 = $line_items->where('tax_name3', '<>', '')->where('type_id', $type_id)->count(); - $tax1 = $line_items->where('tax_name1', '<>', '')->where('type_id', 1)->count(); - $tax2 = $line_items->where('tax_name2', '<>', '')->where('type_id', 1)->count(); - $tax3 = $line_items->where('tax_name3', '<>', '')->where('type_id', 1)->count(); $taxes = []; if ($tax1 > 0) { - array_push($taxes, '$product.tax_rate1'); + array_push($taxes, sprintf('%s%s.tax_rate1', '$', $type)); } if ($tax2 > 0) { - array_push($taxes, '$product.tax_rate2'); + array_push($taxes, sprintf('%s%s.tax_rate2', '$', $type)); } if ($tax3 > 0) { - array_push($taxes, '$product.tax_rate3'); + array_push($taxes, sprintf('%s%s.tax_rate3', '$', $type)); } - $key = array_search('$product.tax', $this->context['pdf_variables']['product_columns'], true); + $key = array_search(sprintf('%s%s.tax', '$', $type), $this->context['pdf_variables']["{$type}_columns"], true); if ($key) { - array_splice($this->context['pdf_variables']['product_columns'], $key, 1, $taxes); + array_splice($this->context['pdf_variables']["{$type}_columns"], $key, 1, $taxes); } } } diff --git a/app/Utils/PhantomJS/Phantom.php b/app/Utils/PhantomJS/Phantom.php index 5dcd14d330c8..9c67bac1a6a4 100644 --- a/app/Utils/PhantomJS/Phantom.php +++ b/app/Utils/PhantomJS/Phantom.php @@ -117,7 +117,7 @@ class Phantom 'client' => $this->entity->client, 'entity' => $this->entity, 'pdf_variables' => (array) $this->entity->company->settings->pdf_variables, - 'products' => $design->design->product, + '$product' => $design->design->product, ]), 'variables' => $html->generateLabelsAndValues(), 'options' => [ diff --git a/tests/Integration/HtmlGenerationTest.php b/tests/Integration/HtmlGenerationTest.php index b8085eef52bc..1871425d3e85 100644 --- a/tests/Integration/HtmlGenerationTest.php +++ b/tests/Integration/HtmlGenerationTest.php @@ -80,7 +80,7 @@ class HtmlGenerationTest extends TestCase 'client' => $entity->client, 'entity' => $entity, 'pdf_variables' => (array) $entity->company->settings->pdf_variables, - 'products' => $design->design->product, + '$product' => $design->design->product, ]), 'variables' => $html->generateLabelsAndValues(), 'options' => [