diff --git a/.gitignore b/.gitignore
index a860fb777bdf..ed99e3129ed5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ yarn-error.log
local_version.txt
.env
.phpunit.result.cache
+_ide_helper.php
/resources/assets/bower
/public/logo
diff --git a/app/Factory/ClientContactFactory.php b/app/Factory/ClientContactFactory.php
index 20e4d69e96f4..9d6216452485 100644
--- a/app/Factory/ClientContactFactory.php
+++ b/app/Factory/ClientContactFactory.php
@@ -22,7 +22,7 @@ class ClientContactFactory
$client_contact->first_name = '';
$client_contact->user_id = $user_id;
$client_contact->company_id = $company_id;
- $client_contact->contact_key = Str::random(40);
+ $client_contact->contact_key = Str::random(32);
$client_contact->id = 0;
$client_contact->send_email = true;
diff --git a/app/Factory/VendorContactFactory.php b/app/Factory/VendorContactFactory.php
index 05031eda3bbf..499377b1ac64 100644
--- a/app/Factory/VendorContactFactory.php
+++ b/app/Factory/VendorContactFactory.php
@@ -22,7 +22,7 @@ class VendorContactFactory
$vendor_contact->first_name = '';
$vendor_contact->user_id = $user_id;
$vendor_contact->company_id = $company_id;
- $vendor_contact->contact_key = Str::random(40);
+ $vendor_contact->contact_key = Str::random(32);
$vendor_contact->id = 0;
return $vendor_contact;
diff --git a/app/Http/Controllers/VendorPortal/InvitationController.php b/app/Http/Controllers/VendorPortal/InvitationController.php
index 80bc02e5d5df..fcf88380d5e0 100644
--- a/app/Http/Controllers/VendorPortal/InvitationController.php
+++ b/app/Http/Controllers/VendorPortal/InvitationController.php
@@ -91,7 +91,7 @@ class InvitationController extends Controller
$file_name = $invitation->purchase_order->numberFormatter().'.pdf';
- $file = (new CreatePurchaseOrderPdf($invitation))->rawPdf();
+ $file = (new CreatePurchaseOrderPdf($invitation))->handle();
$headers = ['Content-Type' => 'application/pdf'];
diff --git a/app/Models/Client.php b/app/Models/Client.php
index 173bd945310f..288e68c374da 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -327,21 +327,6 @@ class Client extends BaseModel implements HasLocalePreference
return $this->service()->updateBalance($amount);
}
- /**
- * Adjusts client "balances" when a client
- * makes a payment that goes on file, but does
- * not effect the client.balance record.
- *
- * @param float $amount Adjustment amount
- * @return Client
- */
- // public function processUnappliedPayment($amount) :Client
- // {
- // return $this->service()->updatePaidToDate($amount)
- // ->adjustCreditBalance($amount)
- // ->save();
- // }
-
/**
* Returns the entire filtered set
* of settings which have been merged from
diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php
index 20582ad23ab0..3c5d7ddc095c 100644
--- a/app/Models/Vendor.php
+++ b/app/Models/Vendor.php
@@ -169,6 +169,11 @@ class Vendor extends BaseModel
return '';
}
+ public function getMergedSettings() :object
+ {
+ return $this->company->settings;
+ }
+
public function purchase_order_filepath($invitation)
{
$contact_key = $invitation->contact->contact_key;
diff --git a/app/Services/Pdf/PdfBuilder.php b/app/Services/Pdf/PdfBuilder.php
new file mode 100644
index 000000000000..af43a120f584
--- /dev/null
+++ b/app/Services/Pdf/PdfBuilder.php
@@ -0,0 +1,1573 @@
+service = $service;
+
+ $this->commonmark = new CommonMarkConverter([
+ 'allow_unsafe_links' => false,
+ ]);
+ }
+
+ /**
+ * Builds the template sections
+ *
+ * @return self
+ *
+ */
+ public function build(): self
+ {
+ $this->getTemplate()
+ ->buildSections()
+ ->getEmptyElements()
+ ->updateElementProperties()
+ ->updateVariables();
+
+ return $this;
+ }
+
+ /**
+ * Final method to get compiled HTML.
+ *
+ * @param bool $final @deprecated // is it? i still see it being called elsewhere
+ * @return string
+ */
+ public function getCompiledHTML($final = false)
+ {
+ $html = $this->document->saveHTML();
+
+ return str_replace('%24', '$', $html);
+ }
+
+ /**
+ * Generate the template
+ *
+ * @return self
+ *
+ */
+ private function getTemplate() :self
+ {
+ $document = new DOMDocument();
+
+ $document->validateOnParse = true;
+
+ @$document->loadHTML(mb_convert_encoding($this->service->designer->template, 'HTML-ENTITIES', 'UTF-8'));
+
+ $this->document = $document;
+
+ // $this->xpath = new DOMXPath($document);
+
+ return $this;
+ }
+
+ /**
+ * Generates product entity sections
+ *
+ * @return self
+ *
+ */
+ private function getProductSections(): self
+ {
+ $this->genericSectionBuilder()
+ ->getClientDetails()
+ ->getProductAndTaskTables()
+ ->getProductEntityDetails()
+ ->getProductTotals();
+
+ return $this;
+ }
+
+ private function mergeSections(array $section) :self
+ {
+ $this->sections = array_merge($this->sections, $section);
+
+ return $this;
+ }
+
+ /**
+ * Generates delivery note sections
+ *
+ * @return self
+ *
+ */
+ private function getDeliveryNoteSections(): self
+ {
+ $this->genericSectionBuilder()
+ ->getProductTotals();
+
+ $this->mergeSections([
+ 'client-details' => [
+ 'id' => 'client-details',
+ 'elements' => $this->clientDeliveryDetails(),
+ ],
+ 'delivery-note-table' => [
+ 'id' => 'delivery-note-table',
+ 'elements' => $this->deliveryNoteTable(),
+ ],
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->deliveryNoteDetails(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates statement sections
+ *
+ * @return self
+ *
+ */
+ private function getStatementSections(): self
+ {
+ $this->genericSectionBuilder();
+
+ $this->mergeSections([
+ 'statement-invoice-table' => [
+ 'id' => 'statement-invoice-table',
+ 'elements' => $this->statementInvoiceTable(),
+ ],
+ 'statement-invoice-table-totals' => [
+ 'id' => 'statement-invoice-table-totals',
+ 'elements' => $this->statementInvoiceTableTotals(),
+ ],
+ 'statement-payment-table' => [
+ 'id' => 'statement-payment-table',
+ 'elements' => $this->statementPaymentTable(),
+ ],
+ 'statement-payment-table-totals' => [
+ 'id' => 'statement-payment-table-totals',
+ 'elements' => $this->statementPaymentTableTotals(),
+ ],
+ 'statement-aging-table' => [
+ 'id' => 'statement-aging-table',
+ 'elements' => $this->statementAgingTable(),
+ ],
+ 'table-totals' => [
+ 'id' => 'table-totals',
+ 'elements' => $this->statementTableTotals(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Parent method for building invoice table totals
+ * for statements.
+ *
+ * @return array
+ */
+ public function statementInvoiceTableTotals(): array
+ {
+ $outstanding = $this->service->options['invoices']->sum('balance');
+
+ return [
+ ['element' => 'p', 'content' => '$outstanding_label: ' . $this->service->config->formatMoney($outstanding)],
+ ];
+ }
+
+
+ /**
+ * Parent method for building payments table within statement.
+ *
+ * @return array
+ */
+ public function statementPaymentTable(): array
+ {
+ if (is_null($this->service->options['payments'])) {
+ return [];
+ }
+
+ if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
+ return [];
+ }
+
+ $tbody = [];
+
+ //24-03-2022 show payments per invoice
+ foreach ($this->service->options['invoices'] as $invoice) {
+ foreach ($invoice->payments as $payment) {
+ if ($payment->is_deleted) {
+ continue;
+ }
+
+ $element = ['element' => 'tr', 'elements' => []];
+
+ $element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $payment->type ? $payment->type->name : ctrans('texts.manual_entry')];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($payment->pivot->amount) ?: ' '];
+
+ $tbody[] = $element;
+
+ $this->payment_amount_total += $payment->pivot->amount;
+ }
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $this->buildTableHeader('statement_payment')],
+ ['element' => 'tbody', 'elements' => $tbody],
+ ];
+ }
+
+ /**
+ * Generates the statement payments table
+ *
+ * @return array
+ *
+ */
+ public function statementPaymentTableTotals(): array
+ {
+ if (is_null($this->service->options['payments']) || !$this->service->options['payments']->first()) {
+ return [];
+ }
+
+ if (\array_key_exists('show_payments_table', $this->service->options) && $this->service->options['show_payments_table'] === false) {
+ return [];
+ }
+
+ $payment = $this->service->options['payments']->first();
+
+ return [
+ ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.amount_paid'), $this->service->config->formatMoney($this->payment_amount_total))],
+ ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_method'), $payment->type ? $payment->type->name : ctrans('texts.manual_entry'))],
+ ['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.payment_date'), $this->translateDate($payment->date, $this->service->config->date_format, $this->service->config->locale) ?: ' ')],
+ ];
+ }
+
+ /**
+ * Generates the statement aging table
+ *
+ * @return array
+ *
+ */
+ public function statementAgingTable(): array
+ {
+ if (\array_key_exists('show_aging_table', $this->service->options) && $this->service->options['show_aging_table'] === false) {
+ return [];
+ }
+
+ $elements = [
+ ['element' => 'thead', 'elements' => []],
+ ['element' => 'tbody', 'elements' => [
+ ['element' => 'tr', 'elements' => []],
+ ]],
+ ];
+
+ foreach ($this->service->options['aging'] as $column => $value) {
+ $elements[0]['elements'][] = ['element' => 'th', 'content' => $column];
+ $elements[1]['elements'][] = ['element' => 'td', 'content' => $value];
+ }
+
+ return $elements;
+ }
+
+
+ /**
+ * Generates the purchase order sections
+ *
+ * @return self
+ *
+ */
+ private function getPurchaseOrderSections(): self
+ {
+ $this->genericSectionBuilder()
+ ->getProductAndTaskTables()
+ ->getProductTotals();
+
+ $this->mergeSections([
+ 'vendor-details' => [
+ 'id' => 'vendor-details',
+ 'elements' => $this->vendorDetails(),
+ ],
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->purchaseOrderDetails(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the generic section which apply
+ * across all design templates
+ *
+ * @return self
+ *
+ */
+ private function genericSectionBuilder(): self
+ {
+ $this->mergeSections([
+ 'company-details' => [
+ 'id' => 'company-details',
+ 'elements' => $this->companyDetails(),
+ ],
+ 'company-address' => [
+ 'id' => 'company-address',
+ 'elements' => $this->companyAddress(),
+ ],
+ 'footer-elements' => [
+ 'id' => 'footer',
+ 'elements' => [
+ $this->sharedFooterElements(),
+ ],
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the invoices table for statements
+ *
+ * @return array
+ *
+ */
+ public function statementInvoiceTable(): array
+ {
+ $tbody = [];
+
+ foreach ($this->service->options['invoices'] as $invoice) {
+ $element = ['element' => 'tr', 'elements' => []];
+
+ $element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->date, $this->service->config->client->date_format(), $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->due_date, $this->service->config->client->date_format(), $this->service->config->locale) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($invoice->amount) ?: ' '];
+ $element['elements'][] = ['element' => 'td', 'content' => $this->service->config->formatMoney($invoice->balance) ?: ' '];
+
+ $tbody[] = $element;
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $this->buildTableHeader('statement_invoice')],
+ ['element' => 'tbody', 'elements' => $tbody],
+ ];
+ }
+
+
+ /**
+ * 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->service->config->entity->line_items, $type);
+
+ $this->processNewLines($items);
+
+ if (count($items) == 0) {
+ return [];
+ }
+
+ if ($type == PdfService::DELIVERY_NOTE) {
+ $product_customs = [false, false, false, false];
+
+ foreach ($items as $row) {
+ for ($i = 0; $i < count($product_customs); $i++) {
+ if (!empty($row['delivery_note.delivery_note' . ($i + 1)])) {
+ $product_customs[$i] = true;
+ }
+ }
+ }
+
+ foreach ($items as $row) {
+ $element = ['element' => 'tr', 'elements' => []];
+
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.product_key'], 'properties' => ['data-ref' => 'delivery_note_table.product_key-td']];
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.notes'], 'properties' => ['data-ref' => 'delivery_note_table.notes-td']];
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.quantity'], 'properties' => ['data-ref' => 'delivery_note_table.quantity-td']];
+
+ for ($i = 0; $i < count($product_customs); $i++) {
+ if ($product_customs[$i]) {
+ $element['elements'][] = ['element' => 'td', 'content' => $row['delivery_note.delivery_note' . ($i + 1)], 'properties' => ['data-ref' => 'delivery_note_table.product' . ($i + 1) . '-td']];
+ }
+ }
+
+ $elements[] = $element;
+ }
+
+ return $elements;
+ }
+
+ foreach ($items as $row) {
+ $element = ['element' => 'tr', 'elements' => []];
+
+ if (
+ array_key_exists($type, $this->service->options) &&
+ !empty($this->service->options[$type]) &&
+ !is_null($this->service->options[$type])
+ ) {
+ $document = new DOMDocument();
+ $document->loadHTML($this->service->options[$type], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+
+ $td = $document->getElementsByTagName('tr')->item(0);
+
+ if ($td) {
+ foreach ($td->childNodes as $child) {
+ if ($child->nodeType !== 1) {
+ continue;
+ }
+
+ if ($child->tagName !== 'td') {
+ continue;
+ }
+
+ $element['elements'][] = ['element' => 'td', 'content' => strtr($child->nodeValue, $row)];
+ }
+ }
+ } else {
+ $_type = Str::startsWith($type, '$') ? ltrim($type, '$') : $type;
+
+ foreach ($this->service->config->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'], 'properties' => ['data-ref' => 'task_table-task.cost-td']];
+ } elseif ($cell == '$product.discount' && !$this->service->company->enable_product_discount) {
+ $element['elements'][] = ['element' => 'td', 'content' => $row['$product.discount'], 'properties' => ['data-ref' => 'product_table-product.discount-td', 'style' => 'display: none;']];
+ } elseif ($cell == '$product.quantity' && !$this->service->company->enable_product_quantity) {
+ $element['elements'][] = ['element' => 'td', 'content' => $row['$product.quantity'], 'properties' => ['data-ref' => 'product_table-product.quantity-td', 'style' => 'display: none;']];
+ } elseif ($cell == '$task.hours') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row['$task.quantity'], 'properties' => ['data-ref' => 'task_table-task.hours-td']];
+ } elseif ($cell == '$product.tax_rate1') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax1-td']];
+ } elseif ($cell == '$product.tax_rate2') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax2-td']];
+ } elseif ($cell == '$product.tax_rate3') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => 'product_table-product.tax3-td']];
+ } elseif ($cell == '$product.unit_cost' || $cell == '$task.rate') {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['style' => 'white-space: nowrap;', 'data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
+ } else {
+ $element['elements'][] = ['element' => 'td', 'content' => $row[$cell], 'properties' => ['data-ref' => "{$_type}_table-" . substr($cell, 1) . '-td']];
+ }
+ }
+ }
+
+ $elements[] = $element;
+ }
+
+ $document = null;
+
+ return $elements;
+ }
+
+ /**
+ * Formats the line items for display.
+ *
+ * @param mixed $items
+ * @param string $table_type
+ * @param mixed|null $custom_fields
+ *
+ * @return array
+ */
+ public function transformLineItems($items, $table_type = '$product') :array
+ {
+ $data = [];
+
+ $locale_info = localeconv();
+
+ // $this->service->config->entity_currency = $this->service->config->currency;
+
+ foreach ($items as $key => $item) {
+ if ($table_type == '$product' && $item->type_id != 1) {
+ if ($item->type_id != 4 && $item->type_id != 6 && $item->type_id != 5) {
+ continue;
+ }
+ }
+
+ if ($table_type == '$task' && $item->type_id != 2) {
+ // if ($item->type_id != 4 && $item->type_id != 5) {
+ continue;
+ // }
+ }
+
+ $helpers = new Helpers();
+ $_table_type = ltrim($table_type, '$'); // From $product -> product.
+
+ $data[$key][$table_type.'.product_key'] = is_null(optional($item)->product_key) ? $item->item : $item->product_key;
+ $data[$key][$table_type.'.item'] = is_null(optional($item)->item) ? $item->product_key : $item->item;
+ $data[$key][$table_type.'.service'] = is_null(optional($item)->service) ? $item->product_key : $item->service;
+
+ $currentDateTime = null;
+ if (isset($this->service->config->entity->next_send_date)) {
+ $currentDateTime = Carbon::parse($this->service->config->entity->next_send_date);
+ }
+
+ $data[$key][$table_type.'.notes'] = Helpers::processReservedKeywords($item->notes, $this->service->config->currency_entity, $currentDateTime);
+ $data[$key][$table_type.'.description'] = Helpers::processReservedKeywords($item->notes, $this->service->config->currency_entity, $currentDateTime);
+
+ $data[$key][$table_type.".{$_table_type}1"] = strlen($item->custom_value1) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}1", $item->custom_value1, $this->service->config->currency_entity) : '';
+ $data[$key][$table_type.".{$_table_type}2"] = strlen($item->custom_value2) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}2", $item->custom_value2, $this->service->config->currency_entity) : '';
+ $data[$key][$table_type.".{$_table_type}3"] = strlen($item->custom_value3) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}3", $item->custom_value3, $this->service->config->currency_entity) : '';
+ $data[$key][$table_type.".{$_table_type}4"] = strlen($item->custom_value4) >= 1 ? $helpers->formatCustomFieldValue($this->service->company->custom_fields, "{$_table_type}4", $item->custom_value4, $this->service->config->currency_entity) : '';
+
+ if ($item->quantity > 0 || $item->cost > 0) {
+ $data[$key][$table_type.'.quantity'] = $this->service->config->formatMoney($item->quantity);
+
+ $data[$key][$table_type.'.unit_cost'] = $this->service->config->formatMoney($item->cost);
+
+ $data[$key][$table_type.'.cost'] = $this->service->config->formatMoney($item->cost);
+
+ $data[$key][$table_type.'.line_total'] = $this->service->config->formatMoney($item->line_total);
+ } else {
+ $data[$key][$table_type.'.quantity'] = '';
+
+ $data[$key][$table_type.'.unit_cost'] = '';
+
+ $data[$key][$table_type.'.cost'] = '';
+
+ $data[$key][$table_type.'.line_total'] = '';
+ }
+
+ if (property_exists($item, 'gross_line_total')) {
+ $data[$key][$table_type.'.gross_line_total'] = ($item->gross_line_total == 0) ? '' : $this->service->config->formatMoney($item->gross_line_total);
+ } else {
+ $data[$key][$table_type.'.gross_line_total'] = '';
+ }
+
+ if (property_exists($item, 'tax_amount')) {
+ $data[$key][$table_type.'.tax_amount'] = ($item->tax_amount == 0) ? '' : $this->service->config->formatMoney($item->tax_amount);
+ } else {
+ $data[$key][$table_type.'.tax_amount'] = '';
+ }
+
+ if (isset($item->discount) && $item->discount > 0) {
+ if ($item->is_amount_discount) {
+ $data[$key][$table_type.'.discount'] = $this->service->config->formatMoney($item->discount);
+ } else {
+ $data[$key][$table_type.'.discount'] = floatval($item->discount).'%';
+ }
+ } else {
+ $data[$key][$table_type.'.discount'] = '';
+ }
+
+ // Previously we used to check for tax_rate value,
+ // but that's no longer necessary.
+
+ if (isset($item->tax_rate1)) {
+ $data[$key][$table_type.'.tax_rate1'] = floatval($item->tax_rate1).'%';
+ $data[$key][$table_type.'.tax1'] = &$data[$key][$table_type.'.tax_rate1'];
+ }
+
+ if (isset($item->tax_rate2)) {
+ $data[$key][$table_type.'.tax_rate2'] = floatval($item->tax_rate2).'%';
+ $data[$key][$table_type.'.tax2'] = &$data[$key][$table_type.'.tax_rate2'];
+ }
+
+ if (isset($item->tax_rate3)) {
+ $data[$key][$table_type.'.tax_rate3'] = floatval($item->tax_rate3).'%';
+ $data[$key][$table_type.'.tax3'] = &$data[$key][$table_type.'.tax_rate3'];
+ }
+
+ $data[$key]['task_id'] = property_exists($item, 'task_id') ? $item->task_id : '';
+ }
+
+ //nlog(microtime(true) - $start);
+
+ return $data;
+ }
+
+ /**
+ * Generate the structure of table headers. ()
+ *
+ * @param string $type "product" or "task"
+ * @return array
+ *
+ */
+ public function buildTableHeader(string $type): array
+ {
+ $this->processTaxColumns($type);
+
+ $elements = [];
+
+ // Some of column can be aliased. This is simple workaround for these.
+ $aliases = [
+ '$product.product_key' => '$product.item',
+ '$task.product_key' => '$task.service',
+ '$task.rate' => '$task.cost',
+ ];
+
+ foreach ($this->service->config->pdf_variables["{$type}_columns"] as $column) {
+ if (array_key_exists($column, $aliases)) {
+ $elements[] = ['element' => 'th', 'content' => $aliases[$column] . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($aliases[$column], 1) . '-th', 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ } elseif ($column == '$product.discount' && !$this->service->company->enable_product_discount) {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
+ } elseif ($column == '$product.quantity' && !$this->service->company->enable_product_quantity) {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'style' => 'display: none;']];
+ } elseif ($column == '$product.tax_rate1') {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax1-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ } elseif ($column == '$product.tax_rate2') {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax2-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ } elseif ($column == '$product.tax_rate3') {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-product.tax3-th", 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ } else {
+ $elements[] = ['element' => 'th', 'content' => $column . '_label', 'properties' => ['data-ref' => "{$type}_table-" . substr($column, 1) . '-th', 'hidden' => $this->service->config->settings->hide_empty_columns_on_pdf]];
+ }
+ }
+
+ return $elements;
+ }
+
+ /**
+ * This method will help us decide either we show
+ * one "tax rate" column in the table or 3 custom tax rates.
+ *
+ * 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(string $type): void
+ {
+ 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->service->config->pdf_variables["{$type}_columns"])) {
+ $line_items = collect($this->service->config->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();
+
+ $taxes = [];
+
+ if ($tax1 > 0) {
+ array_push($taxes, sprintf('%s%s.tax_rate1', '$', $type));
+ }
+
+ if ($tax2 > 0) {
+ array_push($taxes, sprintf('%s%s.tax_rate2', '$', $type));
+ }
+
+ if ($tax3 > 0) {
+ array_push($taxes, sprintf('%s%s.tax_rate3', '$', $type));
+ }
+
+ $key = array_search(sprintf('%s%s.tax', '$', $type), $this->service->config->pdf_variables["{$type}_columns"], true);
+
+ if ($key !== false) {
+ array_splice($this->service->config->pdf_variables["{$type}_columns"], $key, 1, $taxes);
+ }
+ }
+ }
+
+ /**
+ * Generates the javascript block for
+ * hiding elements which need to be hidden
+ *
+ * @return array
+ *
+ */
+ public function sharedFooterElements(): array
+ {
+ // We want to show headers for statements, no exceptions.
+ $statements = "
+ document.querySelectorAll('#statement-invoice-table > thead > tr > th, #statement-payment-table > thead > tr > th, #statement-aging-table > thead > tr > th').forEach(t => {
+ t.hidden = false;
+ });
+ ";
+
+ $javascript = 'document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(t=>{if(""!==t.innerText){let e=t.getAttribute("data-ref").slice(0,-3);document.querySelector(`th[data-ref="${e}-th"]`).removeAttribute("hidden")}}),document.querySelectorAll("#product-table > tbody > tr > td, #task-table > tbody > tr > td, #delivery-note-table > tbody > tr > td").forEach(t=>{let e=t.getAttribute("data-ref").slice(0,-3);(e=document.querySelector(`th[data-ref="${e}-th"]`)).hasAttribute("hidden")&&""==t.innerText&&t.setAttribute("hidden","true")})},!1);';
+
+ // Previously we've been decoding the HTML on the backend and XML parsing isn't good options because it requires,
+ // strict & valid HTML to even output/decode. Decoding is now done on the frontend with this piece of Javascript.
+
+ $html_decode = 'document.addEventListener("DOMContentLoaded",function(){document.querySelectorAll(`[data-state="encoded-html"]`).forEach(e=>e.innerHTML=e.innerText)},!1);';
+
+ return ['element' => 'div', 'elements' => [
+ ['element' => 'script', 'content' => $statements],
+ ['element' => 'script', 'content' => $javascript],
+ ['element' => 'script', 'content' => $html_decode],
+ ]];
+ }
+
+ /**
+ * Generates the totals table for
+ * the product type entities
+ *
+ * @return self
+ *
+ */
+ private function getProductTotals(): self
+ {
+ $this->mergeSections([
+ 'table-totals' => [
+ 'id' => 'table-totals',
+ 'elements' => $this->getTableTotals(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the entity details for
+ * Credits
+ * Quotes
+ * Invoices
+ *
+ * @return self
+ *
+ */
+ private function getProductEntityDetails(): self
+ {
+ if ($this->service->config->entity_string == 'invoice') {
+ $this->mergeSections([
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->invoiceDetails(),
+ ],
+ ]);
+ } elseif ($this->service->config->entity_string == 'quote') {
+ $this->mergeSections([
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->quoteDetails(),
+ ],
+ ]);
+ } elseif ($this->service->config->entity_string == 'credit') {
+ $this->mergeSections([
+ 'entity-details' => [
+ 'id' => 'entity-details',
+ 'elements' => $this->creditDetails(),
+ ],
+ ]);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Parent entry point when building sections of the design content
+ *
+ * @return self
+ *
+ */
+ private function buildSections() :self
+ {
+ return match ($this->service->document_type) {
+ PdfService::PRODUCT => $this->getProductSections(),
+ PdfService::DELIVERY_NOTE => $this->getDeliveryNoteSections(),
+ PdfService::STATEMENT => $this->getStatementSections(),
+ PdfService::PURCHASE_ORDER => $this->getPurchaseOrderSections(),
+ };
+ }
+
+ /**
+ * Generates the table totals for statements
+ *
+ * @return array
+ *
+ */
+ private function statementTableTotals(): array
+ {
+ return [
+ ['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
+ ['element' => 'div', 'properties' => ['style' => 'margin-top: 1.5rem; display: block; align-items: flex-start; page-break-inside: avoid; visible !important;'], 'elements' => [
+ ['element' => 'img', 'properties' => ['src' => '$invoiceninja.whitelabel', 'style' => 'height: 2.5rem;', 'hidden' => $this->service->account->isPaid() ? 'true' : 'false', 'id' => 'invoiceninja-whitelabel-logo']],
+ ]],
+ ]],
+ ];
+ }
+
+ /**
+ * Performs a variable check to ensure
+ * the variable exists
+ *
+ * @param string $variables
+ * @return bool
+ *
+ */
+ public function entityVariableCheck(string $variable): bool
+ {
+ // When it comes to invoice balance, we'll always show it.
+ if ($variable == '$invoice.total') {
+ return false;
+ }
+
+ // Some variables don't map 1:1 to table columns. This gives us support for such cases.
+ $aliases = [
+ '$quote.balance_due' => 'partial',
+ ];
+
+ try {
+ $_variable = explode('.', $variable)[1];
+ } catch (\Exception $e) {
+ throw new \Exception('Company settings seems to be broken. Missing $this->service->config->entity.variable type.');
+ }
+
+ if (\in_array($variable, \array_keys($aliases))) {
+ $_variable = $aliases[$variable];
+ }
+
+ if (is_null($this->service->config->entity->{$_variable})) {
+ return true;
+ }
+
+ if (empty($this->service->config->entity->{$_variable})) {
+ return true;
+ }
+
+ return false;
+ }
+
+ //First pass done, need a second pass to abstract this content completely.
+ /**
+ * Builds the table totals for all entities, we'll want to split this
+ *
+ * @return array
+ *
+ */
+ public function getTableTotals() :array
+ {
+ //need to see where we don't pass all these particular variables. try and refactor thisout
+ $_variables = array_key_exists('variables', $this->service->options)
+ ? $this->service->options['variables']
+ : ['values' => ['$entity.public_notes' => $this->service->config->entity->public_notes, '$entity.terms' => $this->service->config->entity->terms, '$entity_footer' => $this->service->config->entity->footer], 'labels' => []];
+
+ $variables = $this->service->config->pdf_variables['total_columns'];
+
+ $elements = [
+ ['element' => 'div', 'properties' => ['style' => 'display: flex; flex-direction: column;'], 'elements' => [
+ ['element' => 'p', 'content' => strtr(str_replace(["labels","values"], ["",""], $_variables['values']['$entity.public_notes']), $_variables), 'properties' => ['data-ref' => 'total_table-public_notes', 'style' => 'text-align: left;']],
+ ['element' => 'p', 'content' => '', 'properties' => ['style' => 'text-align: left; display: flex; flex-direction: column; page-break-inside: auto;'], 'elements' => [
+ ['element' => 'span', 'content' => '$entity.terms_label: ', 'properties' => ['hidden' => $this->entityVariableCheck('$entity.terms'), 'data-ref' => 'total_table-terms-label', 'style' => 'font-weight: bold; text-align: left; margin-top: 1rem;']],
+ ['element' => 'span', 'content' => strtr(str_replace("labels", "", $_variables['values']['$entity.terms']), $_variables['labels']), 'properties' => ['data-ref' => 'total_table-terms', 'style' => 'text-align: left;']],
+ ]],
+ ['element' => 'img', 'properties' => ['style' => 'max-width: 50%; height: auto;', 'src' => '$contact.signature', 'id' => 'contact-signature']],
+ ['element' => 'div', 'properties' => ['style' => 'margin-top: 1.5rem; display: flex; align-items: flex-start; page-break-inside: auto;'], 'elements' => [
+ ['element' => 'img', 'properties' => ['src' => '$invoiceninja.whitelabel', 'style' => 'height: 2.5rem;', 'hidden' => $this->service->account->isPaid() ? 'true' : 'false', 'id' => 'invoiceninja-whitelabel-logo']],
+ ]],
+ ]],
+ ['element' => 'div', 'properties' => ['class' => 'totals-table-right-side', 'dir' => '$dir'], 'elements' => []],
+ ];
+
+
+ if ($this->service->document_type == PdfService::DELIVERY_NOTE) {
+ return $elements;
+ }
+
+ if ($this->service->config->entity instanceof Quote) {
+ // We don't want to show Balanace due on the quotes.
+ if (in_array('$outstanding', $variables)) {
+ $variables = \array_diff($variables, ['$outstanding']);
+ }
+
+ if ($this->service->config->entity->partial > 0) {
+ $variables[] = '$partial_due';
+ }
+ }
+
+ if ($this->service->config->entity instanceof Credit) {
+ // We don't want to show Balanace due on the quotes.
+ if (in_array('$paid_to_date', $variables)) {
+ $variables = \array_diff($variables, ['$paid_to_date']);
+ }
+ }
+
+ foreach (['discount'] as $property) {
+ $variable = sprintf('%s%s', '$', $property);
+
+ if (
+ !is_null($this->service->config->entity->{$property}) &&
+ !empty($this->service->config->entity->{$property}) &&
+ $this->service->config->entity->{$property} != 0
+ ) {
+ continue;
+ }
+
+ $variables = array_filter($variables, function ($m) use ($variable) {
+ return $m != $variable;
+ });
+ }
+
+ foreach ($variables as $variable) {
+ if ($variable == '$total_taxes') {
+ $taxes = $this->service->config->entity->total_tax_map;
+
+ if (!$taxes) {
+ continue;
+ }
+
+ foreach ($taxes as $i => $tax) {
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i . '-label']],
+ ['element' => 'span', 'content', 'content' => $this->service->config->formatMoney($tax['total']), 'properties' => ['data-ref' => 'totals-table-total_tax_' . $i]],
+ ]];
+ }
+ } elseif ($variable == '$line_taxes') {
+ $taxes = $this->service->config->entity->tax_map;
+
+ if (!$taxes) {
+ continue;
+ }
+
+ foreach ($taxes as $i => $tax) {
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content', 'content' => $tax['name'], 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i . '-label']],
+ ['element' => 'span', 'content', 'content' => $this->service->config->formatMoney($tax['total']), 'properties' => ['data-ref' => 'totals-table-line_tax_' . $i]],
+ ]];
+ }
+ } elseif (Str::startsWith($variable, '$custom_surcharge')) {
+ $_variable = ltrim($variable, '$'); // $custom_surcharge1 -> custom_surcharge1
+
+ $visible = intval($this->service->config->entity->{$_variable}) != 0;
+
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
+ ['element' => 'span', 'content' => $variable, 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1)]],
+ ]];
+ } elseif (Str::startsWith($variable, '$custom')) {
+ $field = explode('_', $variable);
+ $visible = is_object($this->service->company->custom_fields) && property_exists($this->service->company->custom_fields, $field[1]) && !empty($this->service->company->custom_fields->{$field[1]});
+
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
+ ['element' => 'span', 'content' => $variable, 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1)]],
+ ]];
+ } else {
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
+ ['element' => 'span', 'content' => $variable, 'properties' => ['data-ref' => 'totals_table-' . substr($variable, 1)]],
+ ]];
+ }
+ }
+
+ $elements[1]['elements'][] = ['element' => 'div', 'elements' => [
+ ['element' => 'span', 'content' => '',],
+ ['element' => 'span', 'content' => ''],
+ ]];
+
+ return $elements;
+ }
+
+ /**
+ * Generates the product and task tables
+ *
+ * @return self
+ *
+ */
+ public function getProductAndTaskTables(): self
+ {
+ $this->mergeSections([
+ 'product-table' => [
+ 'id' => 'product-table',
+ 'elements' => $this->productTable(),
+ ],
+ 'task-table' => [
+ 'id' => 'task-table',
+ 'elements' => $this->taskTable(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the client details
+ *
+ * @return self
+ *
+ */
+ public function getClientDetails(): self
+ {
+ $this->mergeSections([
+ 'client-details' => [
+ 'id' => 'client-details',
+ 'elements' => $this->clientDetails(),
+ ],
+ ]);
+
+ return $this;
+ }
+
+ /**
+ * Generates the product table
+ *
+ * @return array
+ */
+ public function productTable(): array
+ {
+ $product_items = collect($this->service->config->entity->line_items)->filter(function ($item) {
+ return $item->type_id == 1 || $item->type_id == 6 || $item->type_id == 5;
+ });
+
+ if (count($product_items) == 0) {
+ return [];
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $this->buildTableHeader('product')],
+ ['element' => 'tbody', 'elements' => $this->buildTableBody('$product')],
+ ];
+ }
+
+ /**
+ * Generates the task table
+ *
+ * @return array
+ */
+ public function taskTable(): array
+ {
+ $task_items = collect($this->service->config->entity->line_items)->filter(function ($item) {
+ return $item->type_id == 2;
+ });
+
+ if (count($task_items) == 0) {
+ return [];
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $this->buildTableHeader('task')],
+ ['element' => 'tbody', 'elements' => $this->buildTableBody('$task')],
+ ];
+ }
+
+
+ /**
+ * Generates the statement details
+ *
+ * @return array
+ *
+ */
+ public function statementDetails(): array
+ {
+ $s_date = $this->translateDate(now(), $this->service->config->date_format, $this->service->config->locale);
+
+ return [
+ ['element' => 'tr', 'properties' => ['data-ref' => 'statement-label'], 'elements' => [
+ ['element' => 'th', 'properties' => [], 'content' => ""],
+ ['element' => 'th', 'properties' => [], 'content' => "".ctrans('texts.statement')."
"],
+ ]],
+ ['element' => 'tr', 'properties' => [], 'elements' => [
+ ['element' => 'th', 'properties' => [], 'content' => ctrans('texts.statement_date')],
+ ['element' => 'th', 'properties' => [], 'content' => $s_date ?? ''],
+ ]],
+ ['element' => 'tr', 'properties' => [], 'elements' => [
+ ['element' => 'th', 'properties' => [], 'content' => '$balance_due_label'],
+ ['element' => 'th', 'properties' => [], 'content' => $this->service->config->formatMoney($this->service->options['invoices']->sum('balance'))],
+ ]],
+ ];
+ }
+
+ /**
+ * Generates the invoice details
+ *
+ * @return array
+ *
+ */
+ public function invoiceDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['invoice_details'];
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+ /**
+ * Generates the quote details
+ *
+ * @return array
+ *
+ */
+ public function quoteDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['quote_details'];
+
+ if ($this->service->config->entity->partial > 0) {
+ $variables[] = '$quote.balance_due';
+ }
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+
+ /**
+ * Generates the credit note details
+ *
+ * @return array
+ *
+ */
+ public function creditDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['credit_details'];
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+ /**
+ * Generates the purchase order details
+ *
+ * @return array
+ */
+ public function purchaseOrderDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['purchase_order_details'];
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+ /**
+ * Generates the deliveyr note details
+ *
+ * @return array
+ *
+ */
+ public function deliveryNoteDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['invoice_details'];
+
+ $variables = array_filter($variables, function ($m) {
+ return !in_array($m, ['$invoice.balance_due', '$invoice.total']);
+ });
+
+ return $this->genericDetailsBuilder($variables);
+ }
+
+ /**
+ * Generates the custom values for the
+ * entity.
+ *
+ * @param array
+ * @return array
+ */
+ public function genericDetailsBuilder(array $variables): array
+ {
+ $elements = [];
+
+
+ foreach ($variables as $variable) {
+ $_variable = explode('.', $variable)[1];
+ $_customs = ['custom1', 'custom2', 'custom3', 'custom4'];
+
+ $var = str_replace("custom", "custom_value", $_variable);
+
+ if (in_array($_variable, $_customs) && !empty($this->service->config->entity->{$var})) {
+ $elements[] = ['element' => 'tr', 'elements' => [
+ ['element' => 'th', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1) . '_label']],
+ ['element' => 'th', 'content' => $variable, 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1)]],
+ ]];
+ } else {
+ $elements[] = ['element' => 'tr', 'properties' => ['hidden' => $this->entityVariableCheck($variable)], 'elements' => [
+ ['element' => 'th', 'content' => $variable . '_label', 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1) . '_label']],
+ ['element' => 'th', 'content' => $variable, 'properties' => ['data-ref' => 'entity_details-' . substr($variable, 1)]],
+ ]];
+ }
+ }
+
+ return $elements;
+ }
+
+
+ /**
+ * Generates the client delivery
+ * details array
+ *
+ * @return array
+ *
+ */
+ public function clientDeliveryDetails(): array
+ {
+ $elements = [];
+
+ if (!$this->service->config->client) {
+ return $elements;
+ }
+
+ $elements = [
+ ['element' => 'p', 'content' => ctrans('texts.delivery_note'), 'properties' => ['data-ref' => 'delivery_note-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
+ ['element' => 'p', 'content' => $this->service->config->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
+ ['element' => 'p', 'content' => $this->service->config->client->shipping_address1, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address1']],
+ ['element' => 'p', 'content' => $this->service->config->client->shipping_address2, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.shipping_address2']],
+ ['element' => 'p', 'show_empty' => false, 'elements' => [
+ ['element' => 'span', 'content' => "{$this->service->config->client->shipping_city} ", 'properties' => ['ref' => 'delivery_note-client.shipping_city']],
+ ['element' => 'span', 'content' => "{$this->service->config->client->shipping_state} ", 'properties' => ['ref' => 'delivery_note-client.shipping_state']],
+ ['element' => 'span', 'content' => "{$this->service->config->client->shipping_postal_code} ", 'properties' => ['ref' => 'delivery_note-client.shipping_postal_code']],
+ ]],
+ ['element' => 'p', 'content' => optional($this->service->config->client->shipping_country)->name, 'show_empty' => false],
+ ];
+
+ if (!is_null($this->service->config->contact)) {
+ $elements[] = ['element' => 'p', 'content' => $this->service->config->contact->email, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-contact.email']];
+ }
+
+ return $elements;
+ }
+
+ /**
+ * Generates the client details section
+ *
+ * @return array
+ */
+ public function clientDetails(): array
+ {
+ $elements = [];
+
+ if (!$this->service->config->client) {
+ return $elements;
+ }
+
+ $variables = $this->service->config->pdf_variables['client_details'];
+
+ foreach ($variables as $variable) {
+ $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]];
+ }
+
+ return $elements;
+ }
+
+ /**
+ * Generates the delivery note table
+ *
+ * @return array
+ */
+ public function deliveryNoteTable(): array
+ {
+ /* Static array of delivery note columns*/
+ $thead = [
+ ['element' => 'th', 'content' => '$item_label', 'properties' => ['data-ref' => 'delivery_note-item_label']],
+ ['element' => 'th', 'content' => '$description_label', 'properties' => ['data-ref' => 'delivery_note-description_label']],
+ ['element' => 'th', 'content' => '$product.quantity_label', 'properties' => ['data-ref' => 'delivery_note-product.quantity_label']],
+ ];
+
+ $items = $this->transformLineItems($this->service->config->entity->line_items, $this->service->document_type);
+
+ $this->processNewLines($items);
+
+ $product_customs = [false, false, false, false];
+
+ foreach ($items as $row) {
+ for ($i = 0; $i < count($product_customs); $i++) {
+ if (!empty($row['delivery_note.delivery_note' . ($i + 1)])) {
+ $product_customs[$i] = true;
+ }
+ }
+ }
+
+ for ($i = 0; $i < count($product_customs); $i++) {
+ if ($product_customs[$i]) {
+ array_push($thead, ['element' => 'th', 'content' => '$product.product' . ($i + 1) . '_label', 'properties' => ['data-ref' => 'delivery_note-product.product' . ($i + 1) . '_label']]);
+ }
+ }
+
+ return [
+ ['element' => 'thead', 'elements' => $thead],
+ ['element' => 'tbody', 'elements' => $this->buildTableBody(PdfService::DELIVERY_NOTE)],
+ ];
+ }
+
+ /**
+ * Passes an array of items by reference
+ * and performs a nl2br
+ *
+ * @param array
+ * @return void
+ *
+ */
+ public function processNewLines(array &$items): void
+ {
+ foreach ($items as $key => $item) {
+ foreach ($item as $variable => $value) {
+ $item[$variable] = str_replace("\n", '
', $value);
+ }
+
+ $items[$key] = $item;
+ }
+ }
+
+ /**
+ * Generates an arary of the company details
+ *
+ * @return array
+ *
+ */
+ public function companyDetails(): array
+ {
+ $variables = $this->service->config->pdf_variables['company_details'];
+
+ $elements = [];
+
+ foreach ($variables as $variable) {
+ $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
+ }
+
+ return $elements;
+ }
+
+ /**
+ *
+ * Generates an array of the company address
+ *
+ * @return array
+ *
+ */
+ public function companyAddress(): array
+ {
+ $variables = $this->service->config->pdf_variables['company_address'];
+
+ $elements = [];
+
+ foreach ($variables as $variable) {
+ $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_address-' . substr($variable, 1)]];
+ }
+
+ return $elements;
+ }
+
+ /**
+ *
+ * Generates an array of vendor details
+ *
+ * @return array
+ *
+ */
+ public function vendorDetails(): array
+ {
+ $elements = [];
+
+ $variables = $this->service->config->pdf_variables['vendor_details'];
+
+ foreach ($variables as $variable) {
+ $elements[] = ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'vendor_details-' . substr($variable, 1)]];
+ }
+
+ return $elements;
+ }
+
+
+ ////////////////////////////////////////
+ // Dom Traversal
+ ///////////////////////////////////////
+
+
+ public function getSectionNode(string $selector)
+ {
+ return $this->document->getElementById($selector);
+ }
+
+ public function updateElementProperties() :self
+ {
+ foreach ($this->sections as $element) {
+ if (isset($element['tag'])) {
+ $node = $this->document->getElementsByTagName($element['tag'])->item(0);
+ } elseif (! is_null($this->document->getElementById($element['id']))) {
+ $node = $this->document->getElementById($element['id']);
+ } else {
+ continue;
+ }
+
+ if (isset($element['properties'])) {
+ foreach ($element['properties'] as $property => $value) {
+ $this->updateElementProperty($node, $property, $value);
+ }
+ }
+
+ if (isset($element['elements'])) {
+ $this->createElementContent($node, $element['elements']);
+ }
+ }
+
+ return $this;
+ }
+
+ public function updateElementProperty($element, string $attribute, ?string $value)
+ {
+ // We have exception for "hidden" property.
+ // hidden="true" or hidden="false" will both hide the element,
+ // that's why we have to create an exception here for this rule.
+
+ if ($attribute == 'hidden' && ($value == false || $value == 'false')) {
+ return $element;
+ }
+
+ $element->setAttribute($attribute, $value);
+
+ if ($element->getAttribute($attribute) === $value) {
+ return $element;
+ }
+
+ return $element;
+ }
+
+ public function createElementContent($element, $children) :self
+ {
+ foreach ($children as $child) {
+ $contains_html = false;
+
+ if ($child['element'] !== 'script') {
+ if ($this->service->company->markdown_enabled && array_key_exists('content', $child)) {
+ $child['content'] = str_replace('
', "\r", $child['content']);
+ $child['content'] = $this->commonmark->convert($child['content'] ?? '');
+ }
+ }
+
+ if (isset($child['content'])) {
+ if (isset($child['is_empty']) && $child['is_empty'] === true) {
+ continue;
+ }
+
+ $contains_html = preg_match('#(?<=<)\w+(?=[^<]*?>)#', $child['content'], $m) != 0;
+ }
+
+ if ($contains_html) {
+ // If the element contains the HTML, we gonna display it as is. Backend is going to
+ // encode it for us, preventing any errors on the processing stage.
+ // Later, we decode this using Javascript so it looks like it's normal HTML being injected.
+ // To get all elements that need frontend decoding, we use 'data-state' property.
+
+ $_child = $this->document->createElement($child['element'], '');
+ $_child->setAttribute('data-state', 'encoded-html');
+ $_child->nodeValue = htmlspecialchars($child['content']);
+ } else {
+ // .. in case string doesn't contain any HTML, we'll just return
+ // raw $content.
+
+ $_child = $this->document->createElement($child['element'], isset($child['content']) ? htmlspecialchars($child['content']) : '');
+ }
+
+ $element->appendChild($_child);
+
+ if (isset($child['properties'])) {
+ foreach ($child['properties'] as $property => $value) {
+ $this->updateElementProperty($_child, $property, $value);
+ }
+ }
+
+ if (isset($child['elements'])) {
+ $this->createElementContent($_child, $child['elements']);
+ }
+ }
+
+ return $this;
+ }
+
+ public function updateVariables()
+ {
+ $html = strtr($this->getCompiledHTML(), $this->service->html_variables['labels']);
+
+ $html = strtr($html, $this->service->html_variables['values']);
+
+ @$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
+
+ $this->document->saveHTML();
+
+ return $this;
+ }
+
+ public function updateVariable(string $element, string $variable, string $value)
+ {
+ $element = $this->document->getElementById($element);
+
+ $original = $element->nodeValue;
+
+ $element->nodeValue = '';
+
+ $replaced = strtr($original, [$variable => $value]);
+
+ $element->appendChild(
+ $this->document->createTextNode($replaced)
+ );
+
+ return $element;
+ }
+
+ public function getEmptyElements() :self
+ {
+ foreach ($this->sections as $element) {
+ if (isset($element['elements'])) {
+ $this->getEmptyChildrens($element['elements'], $this->service->html_variables);
+ }
+ }
+
+ return $this;
+ }
+
+ public function getEmptyChildrens(array $children)
+ {
+ foreach ($children as $key => $child) {
+ if (isset($child['content']) && isset($child['show_empty']) && $child['show_empty'] === false) {
+ $value = strtr($child['content'], $this->service->html_variables['values']);
+ if ($value === '' || $value === ' ') {
+ $child['is_empty'] = true;
+ }
+ }
+
+ if (isset($child['elements'])) {
+ $this->getEmptyChildrens($child['elements']);
+ }
+ }
+
+ return $this;
+ }
+}
diff --git a/app/Services/Pdf/PdfConfiguration.php b/app/Services/Pdf/PdfConfiguration.php
new file mode 100644
index 000000000000..614b1cab8b99
--- /dev/null
+++ b/app/Services/Pdf/PdfConfiguration.php
@@ -0,0 +1,352 @@
+setEntityType()
+ ->setDateFormat()
+ ->setPdfVariables()
+ ->setDesign()
+ ->setCurrencyForPdf()
+ ->setLocale();
+
+ return $this;
+ }
+
+ /**
+ * setLocale
+ *
+ * @return self
+ */
+ private function setLocale(): self
+ {
+ App::forgetInstance('translator');
+
+ $t = app('translator');
+
+ App::setLocale($this->settings_object->locale());
+
+ $t->replace(Ninja::transformTranslations($this->settings));
+
+ $this->locale = $this->settings_object->locale();
+
+ return $this;
+ }
+
+ /**
+ * setCurrency
+ *
+ * @return self
+ */
+ private function setCurrencyForPdf(): self
+ {
+ $this->currency = $this->client ? $this->client->currency() : $this->vendor->currency();
+
+ $this->currency_entity = $this->client ? $this->client : $this->vendor;
+
+ return $this;
+ }
+
+ /**
+ * setPdfVariables
+ *
+ * @return self
+ */
+ public function setPdfVariables() :self
+ {
+ $default = (array) CompanySettings::getEntityVariableDefaults();
+
+ // $variables = (array)$this->service->company->settings->pdf_variables;
+ $variables = (array)$this->settings->pdf_variables;
+
+ foreach ($default as $property => $value) {
+ if (array_key_exists($property, $variables)) {
+ continue;
+ }
+
+ $variables[$property] = $value;
+ }
+
+ $this->pdf_variables = $variables;
+
+ return $this;
+ }
+
+ /**
+ * setEntityType
+ *
+ * @return self
+ */
+ private function setEntityType(): self
+ {
+ $entity_design_id = '';
+
+ if ($this->service->invitation instanceof InvoiceInvitation) {
+ $this->entity = $this->service->invitation->invoice;
+ $this->entity_string = 'invoice';
+ $this->client = $this->entity->client;
+ $this->contact = $this->service->invitation->contact;
+ $this->path = $this->client->invoice_filepath($this->service->invitation);
+ $this->entity_design_id = 'invoice_design_id';
+ $this->settings = $this->client->getMergedSettings();
+ $this->settings_object = $this->client;
+ $this->country = $this->client->country;
+ } elseif ($this->service->invitation instanceof QuoteInvitation) {
+ $this->entity = $this->service->invitation->quote;
+ $this->entity_string = 'quote';
+ $this->client = $this->entity->client;
+ $this->contact = $this->service->invitation->contact;
+ $this->path = $this->client->quote_filepath($this->service->invitation);
+ $this->entity_design_id = 'quote_design_id';
+ $this->settings = $this->client->getMergedSettings();
+ $this->settings_object = $this->client;
+ $this->country = $this->client->country;
+ } elseif ($this->service->invitation instanceof CreditInvitation) {
+ $this->entity = $this->service->invitation->credit;
+ $this->entity_string = 'credit';
+ $this->client = $this->entity->client;
+ $this->contact = $this->service->invitation->contact;
+ $this->path = $this->client->credit_filepath($this->service->invitation);
+ $this->entity_design_id = 'credit_design_id';
+ $this->settings = $this->client->getMergedSettings();
+ $this->settings_object = $this->client;
+ $this->country = $this->client->country;
+ } elseif ($this->service->invitation instanceof RecurringInvoiceInvitation) {
+ $this->entity = $this->service->invitation->recurring_invoice;
+ $this->entity_string = 'recurring_invoice';
+ $this->client = $this->entity->client;
+ $this->contact = $this->service->invitation->contact;
+ $this->path = $this->client->recurring_invoice_filepath($this->service->invitation);
+ $this->entity_design_id = 'invoice_design_id';
+ $this->settings = $this->client->getMergedSettings();
+ $this->settings_object = $this->client;
+ $this->country = $this->client->country;
+ } elseif ($this->service->invitation instanceof PurchaseOrderInvitation) {
+ $this->entity = $this->service->invitation->purchase_order;
+ $this->entity_string = 'purchase_order';
+ $this->vendor = $this->entity->vendor;
+ $this->vendor_contact = $this->service->invitation->contact;
+ $this->path = $this->vendor->purchase_order_filepath($this->service->invitation);
+ $this->entity_design_id = 'invoice_design_id';
+ $this->entity_design_id = 'purchase_order_design_id';
+ $this->settings = $this->vendor->company->settings;
+ $this->settings_object = $this->vendor;
+ $this->client = null;
+ $this->country = $this->vendor->country ?: $this->vendor->company->country();
+ } else {
+ throw new \Exception('Unable to resolve entity', 500);
+ }
+
+ $this->setTaxMap($this->entity->calc()->getTaxMap());
+ $this->setTotalTaxMap($this->entity->calc()->getTotalTaxMap());
+
+ $this->path = $this->path.$this->entity->numberFormatter().'.pdf';
+
+ return $this;
+ }
+
+ public function setTaxMap($map): self
+ {
+ $this->tax_map = $map;
+
+ return $this;
+ }
+
+ public function setTotalTaxMap($map): self
+ {
+ $this->total_tax_map = $map;
+
+ return $this;
+ }
+
+ public function setCurrency(Currency $currency): self
+ {
+ $this->currency = $currency;
+
+ return $this;
+ }
+
+ public function setCountry(Country $country): self
+ {
+ $this->country = $country;
+
+ return $this;
+ }
+
+ /**
+ * setDesign
+ *
+ * @return self
+ */
+ private function setDesign(): self
+ {
+ $design_id = $this->entity->design_id ? : $this->decodePrimaryKey($this->settings_object->getSetting($this->entity_design_id));
+
+ $this->design = Design::find($design_id ?: 2);
+
+ return $this;
+ }
+
+ /**
+ * formatMoney
+ *
+ * @param float $value
+ * @return string
+ */
+ public function formatMoney($value): string
+ {
+ $value = floatval($value);
+
+ $thousand = $this->currency->thousand_separator;
+ $decimal = $this->currency->decimal_separator;
+ $precision = $this->currency->precision;
+ $code = $this->currency->code;
+ $swapSymbol = $this->currency->swap_currency_symbol;
+
+ if (isset($this->country->thousand_separator) && strlen($this->country->thousand_separator) >= 1) {
+ $thousand = $this->country->thousand_separator;
+ }
+
+ if (isset($this->country->decimal_separator) && strlen($this->country->decimal_separator) >= 1) {
+ $decimal = $this->country->decimal_separator;
+ }
+
+ if (isset($this->country->swap_currency_symbol) && strlen($this->country->swap_currency_symbol) >= 1) {
+ $swapSymbol = $this->country->swap_currency_symbol;
+ }
+
+ $value = number_format($value, $precision, $decimal, $thousand);
+ $symbol = $this->currency->symbol;
+
+ if ($this->settings->show_currency_code === true && $this->currency->code == 'CHF') {
+ return "{$code} {$value}";
+ } elseif ($this->settings->show_currency_code === true) {
+ return "{$value} {$code}";
+ } elseif ($swapSymbol) {
+ return "{$value} ".trim($symbol);
+ } elseif ($this->settings->show_currency_code === false) {
+ return "{$symbol}{$value}";
+ } else {
+
+ $value = floatval($value);
+ $thousand = $this->currency->thousand_separator;
+ $decimal = $this->currency->decimal_separator;
+ $precision = $this->currency->precision;
+
+ return number_format($value, $precision, $decimal, $thousand);
+ }
+
+ }
+
+ /**
+ * date_format
+ *
+ * @return self
+ */
+ public function setDateFormat(): self
+ {
+ $date_formats = Cache::get('date_formats');
+
+ if (! $date_formats) {
+ $this->buildCache(true);
+ }
+
+ $this->date_format = $date_formats->filter(function ($item) {
+ return $item->id == $this->settings->date_format_id;
+ })->first()->format;
+
+ return $this;
+ }
+
+
+}
diff --git a/app/Services/Pdf/PdfDesigner.php b/app/Services/Pdf/PdfDesigner.php
new file mode 100644
index 000000000000..5db92b32a390
--- /dev/null
+++ b/app/Services/Pdf/PdfDesigner.php
@@ -0,0 +1,73 @@
+service->config->design->is_custom) {
+ $this->template = $this->composeFromPartials(json_decode(json_encode($this->service->config->design->design), true));
+ } else {
+ $this->template = file_get_contents(config('ninja.designs.base_path') . strtolower($this->service->config->design->name) . '.html');
+ }
+
+ return $this;
+ }
+
+ /**
+ * If the user has implemented a custom design, then we need to rebuild the design at this point
+ */
+
+ /**
+ * Returns the custom HTML design as
+ * a string
+ *
+ * @param array
+ * @return string
+ *
+ */
+ private function composeFromPartials(array $partials) :string
+ {
+ $html = '';
+
+ $html .= $partials['includes'];
+ $html .= $partials['header'];
+ $html .= $partials['body'];
+ $html .= $partials['footer'];
+
+ return $html;
+ }
+}
diff --git a/app/Services/Pdf/PdfMock.php b/app/Services/Pdf/PdfMock.php
new file mode 100644
index 000000000000..af9e55ec2270
--- /dev/null
+++ b/app/Services/Pdf/PdfMock.php
@@ -0,0 +1,665 @@
+make();
+ $mock->client = Client::factory()->make();
+ $mock->tax_map = $this->getTaxMap();
+ $mock->total_tax_map = $this->getTotalTaxMap();
+ $mock->invitation = InvoiceInvitation::factory()->make();
+ $mock->invitation->company = Company::factory()->make();
+ $mock->invitation->company->account = Account::factory()->make();
+
+ return $mock;
+
+ }
+
+ private function getTaxMap()
+ {
+
+ return collect( [['name' => 'GST', 'total' => 10]]);
+
+ }
+
+ private function getTotalTaxMap()
+ {
+ return [['name' => 'GST', 'total' => 10]];
+ }
+
+ public function getStubVariables()
+ {
+ return ['values' =>
+ [
+ '$client.shipping_postal_code' => '46420',
+ '$client.billing_postal_code' => '11243',
+ '$company.city_state_postal' => '90210',
+ '$company.postal_city_state' => 'CA',
+ '$product.gross_line_total' => '100',
+ '$client.postal_city_state' => '11243 Aufderharchester, North Carolina',
+ '$client.shipping_address1' => '453',
+ '$client.shipping_address2' => '66327 Waters Trail',
+ '$client.city_state_postal' => 'Aufderharchester, North Carolina 11243',
+ '$client.shipping_address' => '453
66327 Waters Trail
Aufderharchester, North Carolina 11243
Afghanistan
',
+ '$client.billing_address2' => '63993 Aiyana View',
+ '$client.billing_address1' => '8447',
+ '$client.shipping_country' => 'USA',
+ '$invoiceninja.whitelabel' => 'https://raw.githubusercontent.com/invoiceninja/invoiceninja/v5-develop/public/images/new_logo.png',
+ '$client.billing_address' => '8447
63993 Aiyana View
Aufderharchester, North Carolina 11243
Afghanistan
',
+ '$client.billing_country' => 'USA',
+ '$task.gross_line_total' => '100',
+ '$contact.portal_button' => 'View client portal',
+ '$client.shipping_state' => 'Delaware',
+ '$invoice.public_notes' => 'These are some public notes for your document',
+ '$client.shipping_city' => 'Kesslerport',
+ '$client.billing_state' => 'North Carolina',
+ '$product.description' => 'A Product Description',
+ '$product.product_key' => 'A Product Key',
+ '$entity.public_notes' => 'Entity Public notes',
+ '$invoice.balance_due' => '$0.00',
+ '$client.public_notes' => ' ',
+ '$company.postal_code' => ' ',
+ '$client.billing_city' => 'Aufderharchester',
+ '$secondary_font_name' => 'Roboto',
+ '$product.line_total' => '',
+ '$product.tax_amount' => '',
+ '$company.vat_number' => ' ',
+ '$invoice.invoice_no' => '0029',
+ '$quote.quote_number' => '0029',
+ '$client.postal_code' => '11243',
+ '$contact.first_name' => 'Benedict',
+ '$secondary_font_url' => 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
+ '$contact.signature' => '',
+ '$product.tax_name1' => '',
+ '$product.tax_name2' => '',
+ '$product.tax_name3' => '',
+ '$product.unit_cost' => '',
+ '$quote.valid_until' => '2023-10-24',
+ '$custom_surcharge1' => '$0.00',
+ '$custom_surcharge2' => '$0.00',
+ '$custom_surcharge3' => '$0.00',
+ '$custom_surcharge4' => '$0.00',
+ '$quote.balance_due' => '$0.00',
+ '$company.id_number' => ' ',
+ '$invoice.po_number' => ' ',
+ '$invoice_total_raw' => 0.0,
+ '$postal_city_state' => '11243 Aufderharchester, North Carolina',
+ '$client.vat_number' => '975977515',
+ '$city_state_postal' => 'Aufderharchester, North Carolina 11243',
+ '$contact.full_name' => 'Benedict Eichmann',
+ '$contact.last_name' => 'Eichmann',
+ '$company.country_2' => 'US',
+ '$product.product1' => '',
+ '$product.product2' => '',
+ '$product.product3' => '',
+ '$product.product4' => '',
+ '$statement_amount' => '',
+ '$task.description' => '',
+ '$product.discount' => '',
+ '$entity_issued_to' => '',
+ '$assigned_to_user' => '',
+ '$product.quantity' => '',
+ '$total_tax_labels' => '',
+ '$total_tax_values' => '',
+ '$invoice.discount' => '$0.00',
+ '$invoice.subtotal' => '$0.00',
+ '$company.address2' => ' ',
+ '$partial_due_date' => ' ',
+ '$invoice.due_date' => ' ',
+ '$client.id_number' => ' ',
+ '$credit.po_number' => ' ',
+ '$company.address1' => ' ',
+ '$credit.credit_no' => '0029',
+ '$invoice.datetime' => '25/Feb/2023 1:10 am',
+ '$contact.custom1' => NULL,
+ '$contact.custom2' => NULL,
+ '$contact.custom3' => NULL,
+ '$contact.custom4' => NULL,
+ '$task.line_total' => '',
+ '$line_tax_labels' => '',
+ '$line_tax_values' => '',
+ '$secondary_color' => '#7081e0',
+ '$invoice.balance' => '$0.00',
+ '$invoice.custom1' => ' ',
+ '$invoice.custom2' => ' ',
+ '$invoice.custom3' => ' ',
+ '$invoice.custom4' => ' ',
+ '$company.custom1' => ' ',
+ '$company.custom2' => ' ',
+ '$company.custom3' => ' ',
+ '$company.custom4' => ' ',
+ '$quote.po_number' => ' ',
+ '$company.website' => ' ',
+ '$balance_due_raw' => '0.00',
+ '$entity.datetime' => '25/Feb/2023 1:10 am',
+ '$credit.datetime' => '25/Feb/2023 1:10 am',
+ '$client.address2' => '63993 Aiyana View',
+ '$client.address1' => '8447',
+ '$user.first_name' => 'Derrick Monahan DDS',
+ '$created_by_user' => 'Derrick Monahan DDS Erna Wunsch',
+ '$client.currency' => 'USD',
+ '$company.country' => 'United States',
+ '$company.address' => 'United States
',
+ '$tech_hero_image' => 'http://ninja.test:8000/images/pdf-designs/tech-hero-image.jpg',
+ '$task.tax_name1' => '',
+ '$task.tax_name2' => '',
+ '$task.tax_name3' => '',
+ '$client.balance' => '$0.00',
+ '$client_balance' => '$0.00',
+ '$credit.balance' => '$0.00',
+ '$credit_balance' => '$0.00',
+ '$gross_subtotal' => '$0.00',
+ '$invoice.amount' => '$0.00',
+ '$client.custom1' => ' ',
+ '$client.custom2' => ' ',
+ '$client.custom3' => ' ',
+ '$client.custom4' => ' ',
+ '$emailSignature' => ' ',
+ '$invoice.number' => '0029',
+ '$quote.quote_no' => '0029',
+ '$quote.datetime' => '25/Feb/2023 1:10 am',
+ '$client_address' => '8447
63993 Aiyana View
Aufderharchester, North Carolina 11243
Afghanistan
',
+ '$client.address' => '8447
63993 Aiyana View
Aufderharchester, North Carolina 11243
Afghanistan
',
+ '$payment_button' => 'Pay Now',
+ '$payment_qrcode' => '',
+ '$client.country' => 'Afghanistan',
+ '$user.last_name' => 'Erna Wunsch',
+ '$client.website' => 'http://www.parisian.org/',
+ '$dir_text_align' => 'left',
+ '$entity_images' => '',
+ '$task.discount' => '',
+ '$contact.email' => '',
+ '$primary_color' => '#298AAB',
+ '$credit_amount' => '$0.00',
+ '$invoice.total' => '$0.00',
+ '$invoice.taxes' => '$0.00',
+ '$quote.custom1' => ' ',
+ '$quote.custom2' => ' ',
+ '$quote.custom3' => ' ',
+ '$quote.custom4' => ' ',
+ '$company.email' => ' ',
+ '$client.number' => ' ',
+ '$company.phone' => ' ',
+ '$company.state' => ' ',
+ '$credit.number' => '0029',
+ '$entity_number' => '0029',
+ '$credit_number' => '0029',
+ '$global_margin' => '6.35mm',
+ '$contact.phone' => '681-480-9828',
+ '$portal_button' => 'View client portal',
+ '$paymentButton' => 'Pay Now',
+ '$entity_footer' => 'Default invoice footer',
+ '$client.lang_2' => 'en',
+ '$product.date' => '',
+ '$client.email' => '',
+ '$product.item' => '',
+ '$public_notes' => '',
+ '$task.service' => '',
+ '$credit.total' => '$0.00',
+ '$net_subtotal' => '$0.00',
+ '$paid_to_date' => '$0.00',
+ '$quote.amount' => '$0.00',
+ '$company.city' => ' ',
+ '$payment.date' => ' ',
+ '$client.phone' => ' ',
+ '$number_short' => '0029',
+ '$quote.number' => '0029',
+ '$invoice.date' => '25/Feb/2023',
+ '$company.name' => '434343',
+ '$portalButton' => 'View client portal',
+ '$contact.name' => 'Benedict Eichmann',
+ '$entity.terms' => 'Default company invoice terms',
+ '$client.state' => 'North Carolina',
+ '$company.logo' => 'data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAABoMAAAgACAMAAADaPboGAAAArlBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABeyFOlAAAAOXRSTlMA/fn17wQS6YkM37VObtcKuyLbgcfBVDAWZjooBpkao6t2XuOTLNNANh4CWM9EfALLSJ2nYo9yaq963gYxAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAgAElEQVR4nOzd1ZobSRIGUEvNzMxMhm7zvP+Lre2BNbtBqj+r8pyr/fZqFCFHdJUyI549A2i78Z2Lm7mVd3uHi6vn06Mz6f8cADpvbOnF25XXM7cn/b++MZH+DwOgu/YvtlYOVk96f/3Cfvo/EIDu+fTkM7e3OPGr3vOv2fR/JwCdsn/38nC+/6fu87eV9H8sAB3xaml2ZeaPzz5fO0z/JwPQAVc371ZHHtJ+vphP/2cD0G4L23MH0w9uP1/0dtP/8QC01ub66fNfnnq7h7P0BwCglT71n/MntJ8v1tMfAoDW+dR/5p/afz47Sn8QAFrl1cbK7VPev33tNP1hAGiPpa3D0QH1n89MjAPgXhbuTpcH2H8+e57+TAC0wPjx4cPv//zRaPpjAVC6pbnFQf0C9J2p9EcDoGDX25MDOQL3cy4IAfAL19unJ8NrQJ/cpT8hAGU6mxxuA/rkQ/ozAlCg7XdDb0CfTKY/JgClOZt85BTSh9pLf1IAirI/t9pMA/rLBiEAvrK7PjOkY9g/tZj+vAAU4np7bwgXUX/HoAQAPrtaaeIUwrem0x8agLyx2cMm38H9y7AegOrtTE4EGtAnvVfpjw5A0u76YqYBfTae/vQA5Cy9G+RCoAdbSn9+AEKuX0R+BfrKRjoEAESMHzU0DeE3LtJBACBgo+m7QD9lcDZAdcZumpvH81uz6UgA0KypufxLuH+sp2MBQJP2V6In4b51k44GAM3ZOOin+87XjtPxAKAh17Nr6abznbfpkADQiOv1+XTL+YFl3gA1GDteTjecn5hLhwWAoZuaa34xw328TwcGgCEbL+ko3DdepkMDwFDtvythIsLP6UEAXbY5WW4H+uuvo3R4ABia8aI7kN+DALqr3N+B/uVcHEA3ld+B9CCAbhpfKfst3N/cUQXonrGtiXR7uZetdKAAGLDr9WKWM/yBeXEAHVPgXLhfMTcboFNe3KYbywPoQQAdcrmYbisPYo8qQGfsH6SbygPdpSMGwGAstOI49jcu0jEDYBCuj9txHPsbl+moATAAL87T/eQxztJhA+DJdg7T3eRxltKBA+CJxvd66WbySPvp0AHwJK38IegfU+ngAfAUl6vpRvIEY+noAfB4m227EfSNfjp8ADza2Fz5K4J+ZyIdQAAe66KV57G/Mp+OIACPs9/S89hfuU3HEIBHOW73a7gvZtJBBOARztp8Gu4/b9JhBODBFlb66fYxEHvpQALwULNtWdX9J5PpSALwMK1bEvRr79OxBOAhrj+0bknQr22lownAAyytpfvGIFnlDdAe11sdegj6ZDsdUADu6+w23TQGzPoggJYYO+rGgeyvWN0A0A4bz9MdY+BG0jEF4D7GJtu6KvU3ptNRBeAezrr3EPTJajqsAPzR9Vznfgn64mM6sAD8yVWn7gR9xbg4gNIdd+tO0FdW0qEF4Lf2Z9KdYng+pIMLwO/cdGBT3S/NpqMLwK9N7aXbxFCdpeMLwC9dLqe7xHCNpwMMwK909ET2f4xJACjVZocPI/ztPB1iAH5utsuHEf42k44xAD+ze5puEA1wRRWgRGfn6f7QhJfpMAPwo+5ORvjGcTrOAHxv4XW6OTTEJm+A0ix1ck3Dz2ymQw3Atyo4D/cP14MAylLFebh/PE8HG4CvXa2mG0ODDtPRBuArd9W8h/tsMh1uAP7vqJduC416m443AP+a+phuCg27SEccgH/szKd7QtP20yEH4G/rdYxG+Iqj2QBleDWZ7gjNczQboAjji+mGEPAmHXUAPjmbTveDBFOzAQpQ162g/8ym4w7As7m6bgX9ZycdeIDq7dayqOF7/bF06AFqN76W7gUpjsUBhNV5GuELx+IAsio9jfCFY3EAUe8rPY3whWNxAEGvKlpX9xNL6fgDVGxhJt0FokbT8Qeo2OZtugtkraUTAFCvpeV0Ewh7l84AQLW2J9I9IO04nQKAWq330y0g7iydA4BKHaUbQJ5JPQAZFe6r+8FtOgkAVXpV65DSb7xOpwGgRpVfC/rXh3QeACpU75zsb22kEwFQn83n6eJfBkcSABpX/c3Uf62mMwFQnbOTdO0vhSkJAA27rHhb0HfW07kAqMzFSLryl+MqnQyAutxpQf+ZSCcDoC6zRsT938d0NgCqYkrp116m0wFQk5teuuwX5SKdD4CKbGlBX+svpBMCUI+5dNEvjBuqAI3Rgr4zmc4IQDW20iW/OLPplADU4m264henN57OCUAl3jqO8L3zdE4AKnGsBf3gNJ0UgDpoQT9xk84KQBVcTf2Z/XRaAGqwrgX9xHw6LQA1eGFG3M/4OQhg+LYta/gpt4MAhu7M1tSfcjsIYOiWJtLFvlC36cwAdN7+dLrWl8qwOIAh25xPl/pivUjnBqDjpp6nK32x7A4CGK7dtXSlL9diOjkA3XZ9mC70BTtKZweg296l63zJztLZAei09+kyX7KTdHYAOs2c0t95nU4PQJddGBL3O+vp/AB02I4JPb/Tn0onCKC7jEf4vbV0ggC6a+o8XeQL52Q2wLC8mknX+NI5mQ0wLHvpEl86J7MBhuUoXeKLZ4UqwJCsuxj0J2ZmAwzHpdXdfzI6lk4SQDfZm/pnb9JJAugmp7LvwZAEgGG4dir7zwxJABiKyXR9b4OZdJYAOuk4Xd5bYSudJoAu2jYr+z7203kC6KArR+LuYzWdJ4AOWnieru7tMJdOFEAHvU4X95a4SicKoHvm0rW9JbyKAxg45xHuyas4gEFzHuG+vIoDGLDd23RpbwtbvAEG7SBd2lvDqziAAfuQruyt0XNBFWCwNpxHuC+v4gAGa3w5Xdnbw6w4gMH6mC7s7dEfTycLoFuO0oW9RT6mkwXQLRe9dGFvERtUAQZp0+XU+xvdTacLoEuuF9N1vU1ep9MF0Ckv02W9VS7S6QLokks3gx7g5FU6XwAdMj6dLuutMpnOF0CXuBn0IDvpfAF0iLV1D2J7HcDgnI2kq3q7mNMDMDAL8+mi3i4j5vQADMxeuqi3zEE6YQDdcZeu6W3jchDAoJjR80DT6YwBdMdMuqa3zct0xgA6w/buB+pdpVMG0BU7jmU/0Ew6ZQBdMfY8XdJbZzadM4CuWElX9NYxrhRgQDZMy34oJxIABmP3PF3RW6e3n04aQEe8S1f09jlM5wygI7Z76YrePi/SSQPohoXldEFvn+V00gA6wqjSh3ufThpAN1yk63kL2doAMBDexD3CXjprAN3gTNwjnKWzBtAJl87EPdxiOmsAnTDmduojGBUHMAjmxD3CtFFxAANwZk7cI8yl0wbQBWO36XLeRg5mAwzCXLqct9JpOm0AXXBld+oj9HbSeQPogo/pct5KH9NpA+iC9XQ1b6ftdN4AOmDqJF3NW2k1nTeALjhNV/N2cj8V4OkM6XmU+et04gDab+x5upq301Y6cQAd4GrQo0wspBMH0H6bo+lq3k4v04kD6ICDdDFvJ2N6AJ5uO13MW2oynTiA9nvlQMKj9PfTmQNoPwcSHse0UoAncyDhcfpX6cwBtJ8DCY9zkE4cQPtdpmt5S/XO0pkDaL/VdDFvqcN04gDa7zhdy9tqI505gNZbmE7X8payuw7gyVbStbytPAYBPNXVSLqWt9RhOnMA7XeYruVt5TEI4KkMinukw3TmANrPuezHcTcI4Mlu0rW8rd6kMwfQemPL6VreUh6DAJ7MvOxHMikO4KnGzct+nP5SOnUArTeZruVtZW8QwFPtu576OCOb6dQBtJ61QY+0ks4cQOtt9NK1vKVGx9OpA2i9mXQtb6v36cwBtJ7tqY90spBOHUDrraVreVttpTMH0Hp36VLeVvNj6dQBtN5tupa31V06cwCtt54u5W21ls4cQOu9mk/X8pbqWV0H8FTH6VreVoaVAjzVKzsbHmfkKp06gNZ7m67lbWVKD8BTeQx6pImpdOoAWs9j0CO5ngrwVB6DHun5q3TqAFrPY9AjXaQzB9B67gY90pt05gDaz2PQ4ziXDfBkfg16pJfpzAG03026lrfUtLVBAE/2PF3MW2o2nTiA9rM36HEW04kD6IDVdDFvp/5OOnEA7XeRLuYtNZlOHEAHLKaLeTs5kADwdBvpYt5SDiQAPN1hupi300w6bwAdsNRLV/NWMiEBYAD20tW8nY7SeQPogPGRdDVvpfmxdOIAOuBlupq3k5UNAE+3O5Gu5q20l84bQBdY2vAYE+PpvAF0wXm6nLfSejptAF1gWuljuBoEMAjG9DzC6H46bQBdsJMu5620lU4bQCe4n/oIq9fptAF0gfupj2BrEMBAvE/X8zZ6n84aQDfMp+t5C62+SmcNoBNm0/W8hbyJAxgMB7Mfzps4gIGwOOjhvIkDGIzTdEFvH2/iAAZjYTRd0dvHmziAwTAx+8G8iQMYkNt0RW+dkaV0zgA64ixd0dvnbTpnAF1hVNxDfUynDKArnEh4qInNdM4AusKJhIeaTacMoDOcSHigvXTGADpjI13S22Z5Kp0ygM4wI+Fh+pfpjAF0xthEuqi3zFE6YwDdsZ6u6S2zZn03wMDMpIt6u0zspxMG0B2btjY8iGPZAIPzPl3U2+VdOl8AXXKeruqt8nwsnS+ADrlMV/VWMS0bYJBcDnqIm3S6ALpkzLjSBzhNpwugU2bTZb1Nnu+m0wXQKW/Sdb1FRv0YBDBICyPpwt4i6+lsAXTLcbqut4ibQQCDZU7Pva26GQQwUOP9dGVvDWPiAAZsK13ZW6N3kc4VQNespUt7a8ylUwXQNftGZt/Tm3SqADpnLl3a2+J8IZ0qgM7xKu5+XE4FGDjb6+6nd5fOFED3bKWLe0scpRMF0EGL6eLeDofpPAF00LhXcffx3HkEgMF7m67urWA+AsAwmBV3D/3LdJoAumjKrLh7sLwbYBhu0uW9DSbTWQLoJhtU/2zmOp0lgE56NZou8OWbn0pnCaCbLtIFvnxG9AAMyWS6whevb2UQwJDMp0t88bbSKQLoqqV0hS+eI3EAw2J10B/MvEqnCKCzzCv9PUfiAIbGkITfm7hKZwigu2bTRb5sIxvpBAF02F66yhetb3EqwBAtp8t80d6m0wPQZVfpKl+0lXR6ADptK13mS/Y6nR2AbjtM1/mCzYylswPQaWNmZv/ScxeDAIZqO13oy7W8mU4OQMetpCt9sU7cTQUYsrV0qS/VxE46NQBdt2tQz8+NGo8AMGxWqP7ciKV1AEPn56CfMqEHoAF+DvqZ3k06LwAV2B1Jl/siWd0N0AC3g37mQzotAFV4mS73JZpLZwWgDtZ4/+gonRSAOoz5OegH79NJAajEZbrgl+dlOicAtZhLV/ziaEEATXmTLvml0YIAGjOdrvmF0YIAGrOZrvmFcSgboDmz6aJfFldTARo0ma76Jekdp9MBUBUDS/+vv57OBkBV3FD9v/5sOhsAddlIF/5yjLxIJwOgMm/Tlb8Yo9vpXADU5jRd+ksxcZZOBUB1VtO1vxDzV+lMAFTn2pGEL24305kAqM9OuviXYXEqnQiACt2kq38RDnfTeQCokSkJn5xep9MAUCV7vP/6ayWdBIBKTaQbQFz/Jp0DgErtpztA3ISbqQAhd+kWkLa8k04BQLXep3tA2KprQQAxr9NNIOtwIZ0AgIrVPalnMh1+gKqNpttAUN/OVICkmo/FTVykow9QtxfpRpDz3JxsgKy5dCeIcRoBIG0v3QpSJk2IA0hbS/eCjJH1dOABeHaS7gYR0xvpuAPwbLeXbgcJa2YjABSgyiWqk6/SYQfgWZUTS0f9FARQhvqOZs8bkw1QiHfpltC0N24FAZRiJt0TmtU7SgccgP/Mp7tCo6ZtTAUoyEi6LTRpZjwdbgD+bzPdFhrUWzGdB6Akl+nG0Bzv4QAKM5vuDI356D0cQGE+pFtDQ/pz6UgD8L3JdHNoxrwRpQDlOUh3h0YcTKXjDMCPFtPtoQETs+koA/AzFVxRXdxPBxmAn+r8FdX+kUtBAGWaSreIYTs/S4cYgF/o+Aa73rvddIQB+JXtdJcYqumLdHwB+LVOj0nYcyIboGRv031ieKZfpIMLwG8dpTvF0BwaDwdQuK6O6nEtFaB8r9PNYjheewgCKN/HdLcYhmW/BAG0wWq6Xwxe79RxOIBW6N64uOe2NAC0xES6ZQzYyMpYOqQA3FPHRpau7aQDCsC99dJdY5AmjtPhBOD+FtJtY4B6Bw5kA7TJZrpxDM7zy3QwAXiQpXTnGJTRuVfpWALwMBvp3jEgB5vpSALwUN1YH/R8Ox1HAB7uLt0+BsBrOIB2Wk83kCfrT5rMA9BON+kW8lSLLqUCtNVxuoc8zfxdOoAAPFqre9DEBz8EAbTYVrqPPN7IpLEIAK32Id1JHqt3eJWOHQBPM5fuJY+0aEkQQOu1swfdXqTjBsDTvU+3k0dYXk9HDYBBOEo3lAc7mdtNBw2AgXiZbikPdPLBqm6ArmjXc9DE0UI6YAAMTJt+DxpdMRkOoEvacy5u9KUOBNAtbbmjOrFiKAJA12ylm8u9+B0IoIvaMLN0+a2zcABdVP4Ou/NjHQigm0rf5b02e50OEQBDsp1uMr/Tm9lOxweA4TlL95lfG9mzphug067SneZXHMYG6LypdK/5uVsHEQAq0E+3mx/1D1+kowJAE07SHed7E5P76ZgA0Izn6Z7zLS/hACqymO46Xxk9PUuHA4AGHaQbz39utwyFA6jLZLr1/G1icikdCQCaVsICod7iul+BACqUH1p6/t5BOIA6XWYb0MnpRjoCAKTsBxvQyOGsd3AAFbtODUrof7xxDg6gcvORBjRzPJX+4ADEfdSAAAhp+ILQyOGNBgTA37YabEDTe7O76c8LQDkuGuo/vdsVx7AB+EYjh7Mn3rzdTH9QAMozMuT+M7J4tHGd/pAAFGmYG4R6t5Mv/AIEwK+8GVL/6a9N3jkCB8DvvB9C/xlZXHlhCgIAfzLgg3H92723G6/SHwqAVpgaWPvpzR/MbXv8AeD+lgfQfqZnJo83nD4A4IFOnzI6uz8/8+7tpbMHADzO2M76yzfPRx/Ue3oTt29W3l5cufkDwABMbczOvXuzdj7R+1XjGTmZX53ZW9mavdy3fA6AoRjf2X5xt36ztXV0dLS1tXWzvj77YmNpXN8BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqMX1+NLZ9t3N1tHK6evDxcXV29vny8vLE6Ojo3991hv9x/Ly+e3q4szhm73Tycmjo6236y8uz67G0//5ALTN1M72+oeVvY+r0yN/PdHIyfnax9eTR1vrF2ebr9IfDIBSbV6uvz+dWT3pP7Xx/Epv4nzx4N3R8d2ZByQAvvjUe45OZ+af/MzzECPziwcrH2Y3NCOASi1s3Ky8ed5o7/mxGZ1/fDc3e7aQjgXUYGF8aWfj4sX6zdbc0eQnp3ufHBx+cvDpf3z6P14eHc1t3dxtn+1Ppf9b6bL9i613i9PR5vOdidU3KzeXvvYwWJtnF+tbR5N7bxZX5yce9J69N7r8fO3j65UPs9tXu+mPQWcsXG7trY4Oq5M82cni3tzd0nU6StBqUzsvbuYmD2aen/QG9C/z82+5kx/udjQjHm//7uhwflBfyaHqnx++nF1KxwtaZuxq++bodOZ8mH9knqwdvDze3kx/VFpm52ZycWKI38uhGFnd+3Dh0AL80dTG+tHrtYE99NznX+f5m5frO2PpD04LjN+tzJT77u3PTmYm1z0SwU8tfGk+sb8vP7+x0In4pd3LuYPl1LdzoEYXJ9ev0uGEguxuHE/OLBfxbr1/fjC37Wgr39q/OV0d2mXTjImZldn9dFwhbWzjZvJjGd3nK735T43IwVa+WDp+3Y3Hn5+Y+Ph+26M/ldq93Nq7Lfhvy8+N6NK/z7qdfXhzkv4iDlt/bXLWWQXqMrU9d3Be2sPPT43491mr6425j607/PZo86/f7qQjDk14dbZ10I5bFf/n32d1lrYO6+k//5r4+MH3nE7bnF1Zy47TerzRj3Nn6fjRiPH1vc7+/vNHE2/eOrpNF11vfGj/udaJQ38ndtzCi8nnLXtKH7zp1zcOzNElrzbet/pa3zdODt66WdFRO3OLBZ+Radb86ayDoXTBq8sO9Z9/Te/599k1u3enrX9OH7D+2nuvn2m16y49/3ynv3a0kY4vg3L1Yaatv1MO2ac/t1zYpp32j990/WDRxMGNY9utN3YxOZ/+JhWtvzjnlAIts/Di3Xn6X04zercrHodabGr9TVef1Adq+d0Lt7Vpi7P3i3W91zg5vbOIqI32t2YcQbi30QNv5Sjfq+0632uMzGxZQtQuS3Or1Z/BfqiRmWNncSjY+PFhxe81emvvvTVvievtSWfgHqc/89ZvoBRpydWKv/6a9+NQ+a63Tzs/hXSoeqtzbrBSmMs638D9jDZUtFcXp10/rdkIbYiCnK1oQN+YPt1O54SfebWtAQ1Mb3XLb0MU4PLddPofQ4mm32lDhRl7sacBDdbI4awD20Sdrfhl95dOPA2V49oT0HCMHrxI55Zqbbzzy+4fzL80ZbsEl76qQzQ9aagczbs6qmQOwlM9f2/IdpaH9eF7/t7tOJo0/nbN7b77W/3gQkXK0kt/KzWif3h3nU42lXj14qCuSTwD0Fs8NuWkeePHi+nM1+Rk0iM/w7dxWvEkhKcYfX2Rzl1ddtfNgmtab8Y5OYZqaut5+lveZienbq825Hrb30oZE5MGVjEsF2/8XflUt3N+Ghq+s0nH4IIWb0yRZ/D2XzpcNBB91/qGa3POw3ra6DsPQwzU2OyMc3CDM/HOfYohGZv96GG9BL2Pfv5kYDaPTOMZtPkj9ykGb2fSLIRyzM85C8ogbPsVaCj6h3ev0rntlPEPt+mc8q2JSbO1eaLdY+/Wh+fEEaJBuXZprUi9GdPkeIKlSedbh8vd1YHYWfG2uFjPt5yS43Hu3DFvwsQ7Y02fZOrtajqH/NbESz9+8mC7x8ZsNebWLrBH29jzDq58/QN/aPEgm0fu+DVqxBKWx9g/cmmtJXozl+lvC+2x89pJuOY9N0HhYXZvFl1aa5NFf2dxLy9m0t/VWvUPXOu7t513rgK1zu1s+mtD8cacxY5adnX1PhaOHUNop/MbS4b4jbFjr9fTeovrrq7+3o4rAy22vGVcIr8w9d7bjSJ4GPqNqS3TEFpuWhfiZ6aO/HFZjP6hn29/yknsTjiZc22V72yu6EBl8TD0g3EbFDvDsxDfuDr112V5+m8ck/uKDYrdsnzsd0/+sb/nH3eh5t+7M/TFpsuo3TPvjByfbU56BiqYX4Y+z8Q+9FdSJ52vp79bxI3rQMWbP6r6YWjfBsUOW/W+uW7jTiK0Qr0PQ69eHJrH022LG+kvGTE6UIvM1zhNbslq7gr0Dq7SXzQiFl7qQK0ycrCd/s40avdmLR1ymtGftLukPtfHljO0T0W/DO14BKrJ6JHrQpV5YUFdO9Xxy9DuuhW+tZl3RK4ml15ytFjnH4bOjCSt0mpd75prtvMx/WXjaUZed3cl5fgH83hq1XttPFUNxk+dde2Ajj4MbRtJWrWRFbNMu25hxb/xjhh53bVXF1cvXUat3vxd+mvIUK37R94l8x0arT026zIqny2epb+LDM2l/cdd0/8424nhw2fvnMTmH24LddX+nr8zu2hicin91XqiqWMnsfnayXH6O8ngjc0579pZt1sL6e/X49mMyo8Wd9LfSwZs1vaVThvda+cBhf05l6X5mf6kE3JdcuZOavedt26o6e76jNfD/MqyE3KdMfXOv/Qq9N/cteiAwuWpt8P81mF3jn3WbdZ57HpMnLZjGcv+nGkI/NHonGXf7bc0k/4e0azyLw15B8d9rTmb0HK7K/30l4jG9WZuyj0nN3Z34Bwc99Z/aalDm71wGq5So69fFPnT0OWpu6g8zHl3B/R23v5B+ttD0OhBaZuGro7m00GhhXp75T7W8xvXH7zyqN30ZDmjt67e36bDQVvNt/P2W+V2zIbjk/OVEtrQ1XtfR56g986jUMuMHTmLwD+mT7N/RV7NaUA81fRF9EvMA126fMHXlidTv+sueQJiIHqnHoVaY2HS7Qu+d3L6ovEBXBsrpsExMPMOyLXEtsNH/NTIzNZ+Y1/DV9unJ+kPTLf0Jt0VaoHxvfQXhYL1Vl82cUZh8+aNYXAM3q2xCcV74W9P/mDicKiPQ682jla9DGY4Ro5MkCvagocg7uX83d1QfuG92vroAYhhWmzufTIPtm00D/fWX1t5MTXIr9/V8WtfQIZudHaQ31oGaGzFGxAepje/d7w0iC/f0vGeFSE05MAp7SJtOAbLo0y/ef/iCStYN+9efjSIlCadlzD8g2+NvTQYgSeY/rgye/XQb93mi5cfPf7QvJEPw6iiPMHSWvpLQQf05w8nt17c4+Xc2NKLub1FTz/EfHzCozuDZ0Q2AzT6fOb1ytzNxc74tzuIxsZ3Lo7fv3uztuyXR9JOzNIux/jH9NeBzhoZPVk+v71dnh515pqi9I7SlZd/XLiWCtRncTNdfHnmRDZQK+/jCnBlMj5Qqd6K0T1hxw4jAPWacT4uaeog/QUASJq2VShn2/VAoHIjN+lKXK0tkxEA9qy2S5g6TCceoARr9jk0b8OUfIAvHNJunPdwAP/qG2LaqIXX6YwDlMSPQg3asSkI4BurJvc05ca9VIDvnGyka3MdxvbSmQYo0MhsujzXYNOyOoCfWkkX6O67tKcB4BfeLKRrdMcZUQrwa7dOJgzR2GQ6vwBFm95JF+ru8lMQwB+M3KVLdVdtmJIN8Ce9rXSx7iY/BQHcx6ntqgP36jSdVYCWeLObLtldMzWTzilAa6xZ8T1Q+8/TGQVokfmrdNnuksuJdD4BWmXC9LiBWXcaAeBhRi/SpbsrjnrpXAK0Tn89Xbw7YfcgnUiANupZrvp0m6vpNAK0lDnaT3VmNgLAY7mt+jTbo+kMArTYm7F0GW8zB+YgYAgAACAASURBVOIAnmTGRqFHm3MgDuBp1qbSpbytVtKpA2i/W3N7HmPsTTpxAF0wv5+u5y00bl8dwEAsGx73UIaUAgzK9FK6prfM2Uk6ZQDdMbGTruqt4loQwCCdaEL3d+FaEMBATZylK3trzPbTyQLoGguF7unYzVSAgRu9TFf3VnifzhNAJ2lC93CUzhJAR2lCf/QunSOAzhr1m9BvvXqdzhBAhzkd9zu7h+n8AHSay6q/trCYzg5Ax2lCv6IFAQzdidlxP7VgUDbA8E2bov0TWhBAI5Y30wW/PFNaEEAzzm1W/c7UajonANVYnUoX/bKMa0EAzVncTZf9kozfpvMBUJWPY+nCXw4tCKBhB+nKX4zx83QuAKrzLl37CzHlKQigeS/T1b8I7gUBRLxN1/8CaEEAGb3ZdAeIG5tJJwGgViPb6R4QNvYxnQKAelU+RPvVm3QCAGo2XfXoOFtTAaJqHh23lw4+QO3Wqp3ac5oOPQC1DkxYSQcegFrvqn5Ihx2Az47T/SDgppeOOgCf9S/SHaFxL/rpoAPwt9GldE9o2OVIOuQA/Gu5rhPaZ6PpgAPwf1Wd0N6fTocbgK+9STeG5mzOp4MNwLeqOaFtZx1AcWpZ5DC2mI40AD8YOUu3h0YcpOMMwE9M13A4zoQegDKtjaU7xNC9TccYgF84TbeIYbswHgGgWFvpJjFc7qYCFKzbk+P2T9LxBeA3Jq7SjWJ4pp6nowvAbz1fSLeKYXExCKB4e+leMSx76cgC8Edv081iOF6m4wrAn/U30u1iGGbtTQVogy7OS9iwtA6gHRZfpVvGoG3aGATQFivpnjFgY2vpiAJwbx3b42BWNkCLjHbqqupROpwAPMRth0Zo3zkSB9Au79KdY2B2DCoFaJuu/CQ0vpyOJAAP1ZGfhByJA2ij1U78JHSaDiMAjzGZ7h8DcJMOIgCP0/6fhM6M6AFoqdb/JOQ8AkB7rbV7cNz1TDqAADzey3QbeZKVdPgAeILedrqPPIGVQQDttjyV7iSPdmU+AkDLHaRbyWMtPE+HDoCnukk3k0eyrwGg/Vp6QPtDOm4ADEArD2hv9NNhA2AQWnhAe8rlVIBO6L9Pd5SHe5MOGgCDcL6RbigP58cggC7oTbZwg4MfgwC6oI0PQc+m5tNhA+DJWvkQ5GYQQBe08iHo2bOtdNwAeKp+Ox+CrK0DaL/n7XwI8mMQQOv1j9r5EOTHIIDWOz9Lt5LHWk+HDoCnOd1Nt5LH2rczCKDVpi/SneTRrtfSwQPgKQ7H053k8Y7SwQPgCSZm033kCczoAWizxf10H3mCBceyAdprZC7dRp5kLx0/AB5tdSndRZ5kNh0/AB6r97KNO7v/b3MiHUEAHumkvSey/zaTjiAAj/SxxSeyv7A6FaClRrbSLeSplkzLBmin+daOh/uXAQkALfV6Id1CnmwuHUMAHmPkbbqBPJ03cQCt1P73cN7EAbTUwVS6gQzA+3QUAXi4fruH8/xjx5s4gPaZvky3j0F4tZqOIwAPNtOF93CWBgG00eR1unsMxI6lQQBtM9rmZXVfGbtNRxKAB3re7j0N/+dNHEDbHLR/NMLfrpyJA2iX3lG6dQzMYjqWADzIxHa6cwzMcTqWADzI+VW6cwzMuN2pAK3SkVtBXxykgwnAQ3TkVtAXF+lgAvAA/eN03xigheV0OAG4v5NODIj712Q6nADc3+pmum0M0kYvHU8A7u1gN902BunauGyA9lhJd43BmkvHE4D76t+km8ZgbY6mIwrAPY1epJvGgL1JRxSAe5reSfeMAdtORxSAe+rWgbhPxs7TIQXgfg67sqnhP+/TIQXgfk47NJ7nb/u2BgG0Qm8r3TEGz4EEgFbor6cbxuCZVQrQCp07k/3MgQSAlpjYSDeMIThKRxWAe1heSveLIXAgAaANbrt2LeiLw3RYAfizxQ5t7f4/ExIAWuCwU6sa/nV9m44rAH/UvZupXxyn4wrAH71MN4vhmDpJBxaAP3mfbhZDspIOLAB/MpfuFUPiXDZA6XrH6V4xLAbFARSuiyPi/naZDi0Av9efTbeKoVlLxxaA3xp5ke4UQ3OTji0AvzW6ne4UQ7M7nQ4uAL8zepnuFMPzMh1cAH5n4izdKIZn07lsgJJN7KQbxRCdpqMLwG90+Sno2VU/HV4Afm20i0tT/+N6KkDBunwc4dmzs146vgD8Urdb0LOZdHwB+KWOt6CLdHwB+KWOt6Bnq+kAA/ArXW9B6+kAA/ArIxfpJjFcr+bTEQbgF0a6OyPub2/TEQbgF/p36R4xZLsn6RAD8HO9zq6s+9f7dIgB+IXOLu7+19REOsQA/Nz7dIsYOjsbAAq1ku4QQ+cxCKBQp+kOMXwegwDKdHCd7hBDNzWaDjIAPzMzlu4Qw+cxCKBIa7vpBjF8HoMAivR8PN0gGuAxCKBE8zW0II9BACWaWEr3hyaspMMMwI9GOr6t4W8egwAK1JtNt4dGeAwCKNCHdHdoxLjHIIDyTKa7QzMcigMoz2H3xyN8tmBSHEBxarib+tlcOtAAfK+Ki0GfjE2nIw3Ad+q4GPTJ23SkAfhOHReDPrmeT4cagG9VcjHok/V0qAH4zly6NTTmNh1qAL71Ot0ZGnOXDjUA31qt5FT2J2vpWAPwjenNdGdozGU61gB8Y2Qj3RmaM5MONgDfWE83hubs9NLBBuBrK+nG0KCDdLAB+Nphui80aL+fjjYAX3m+kG4MDZpMRxuAr4zWMiXuM0sbAErSv0j3hSZtpcMNwFe20m2hUaaVAhTkNN0VGmVMD0BBbsfSbaFRi+l4A/Cf0at0V2jUWTreAPynd5fuCs16nQ44AP+paT7CJ5vupwIUY+1Vuis0ayUdcAD+NbGfbgrN2nU/FaAUve10U2jY23TEAfjX+3RPaNrzdMQB+MdMuiU0bTsdcQD+MT2e7glNszgIoBD9ipZ3/23cwWyAQtQ1qfSzo3TIAfjbQbojNO56Oh1zAL5Ynkq3hMbNpmMOwBf9y3RHaN5MOugAfFHdzaBnz5Z66aAD8NnadbojNG8yHXQAPqttTNxnRsUBlGE23RACjtNBB+Cz03Q/SLhNRx2AT+YX0v0gYCMddQD+qnFGz2en6bAD8MnLdDtI2B1Nhx2Av/5arWx7999u0mEH4K+/RpbS7SBiMR13AGqclv3ZUjrsAFS4OvVvK+m4A/DXaIUDEp7Z2gBQhON0N8i4S8cdgFrfxD07TAcegErfxD0b76cjD0Clb+KezaUDD0Ctb+KenacjD1C9Wt/EPbtMRx6At+lekLKXjjxA9dbSrSBlzLhSgLD+TroXpKynQw9QvaN0K4hxOQgg7Hws3QpSXA4CCOtVuTv1i6107AFq9y7dCXLW0rEHqNzJVLoTxFz10sEHqNxsuhPkvEzHHqBy1Q7peWZOD0DYyFK6EeRspIMPULl6rwY9e/YuHXyAutV7NejZs+uTdPQB6naRbgRBL9LBB6jbQboPJB2kow9QtdHNdB8I2jUyGyDpfboPJM2mow9QteXddB9I8ioOIOku3QaSvIoDSPqYbgNRXsUBBPUrnpDwzKs4gKiVdBeI8ioOIOhkId0GoryKAwh6m+4CWV7FAeQ8f5XuAlFexQEEvUh3gSyv4gByat5c95lXcQAxvbN0E8ga8yoOIOY03QTCvIoDiBmpeV72Z6/TGQCo18t0Dwh7NZHOAEC1Juq+nvrs2UU6AwD1+pDuAWnv0hkAqNb0WLoHpM2nUwBQrZt0C0jbSWcAoFrn1+kekHaUTgFAtarenvrFajoFALVaS3eAuM1eOgcAtdpOt4C4t+kUANRqMd0B8mbSOQColceghZF0DgAqVfvOhmfmlQLEXKY7QJ55pQAZHoOeXZtXCpDhMejZZToHAJX6mG4ABVhJJwGgUhvpBlAAQxIAIvwa9OzZuCEJABHuBj17dpNOAkCdTIr75CCdBYA6vUjX/xI4mQ2QcJsu/yXYSGcBoE6z6fpfAuvrABLmq1+f+tlaOg0AVbpJl/8STPXTaQCo0fRYuv6XwMxsgIS5dPkvwl46DQA1Gp1Kl/8iLKfzAFCjyXT1L8JOOg0ANervp8t/EebSeQCo0UG6+pfhYzoPADWytOGzV6PpPABUaDFd/ctghSpAgGmlXxjUA9C8+XTxL8RMOhEAFfqQLv5lGBtJJwKgPiPup36xnU4EQIXepYt/IV6mEwFQoZ108S/EYjoRAPWZSdf+Quz6OQigcXfp4l+Ii3QiAOqzbH/q31bSmQCoz/t07S/FajoTANXpj6drfyEWrPEGaJqJ2f+4S2cCoD7b6dpfisl0JgCqY1Tcv9bSqQCozly69JfCsDiApjmR8C+7gwCa5kTCv96nUwFQnYt06S/GYToVALVxIuE/E+lcANTGjIR/LaVTAVCb3n669BfjOJ0LgNrY2vCf1+lcANTmJl35yzGfzgVAZUYX0pW/GOPpXADUZi9d+csxm84FQG2MK/2PgaUAzVpOF/6C2F8H0KyX6cJfjjH76wCadZWu/OUwsBSgWavpwl+QD+lkAFTmQ7rwF+QgnQyAuvQ204W/IG6oAjRqMV33CzLVS2cDoC5v04W/IBfpZADUxRLvr9ihCtCoj+m6XxI7VAEaZWT2V6bT2QCoyoiR2f9naDZAow7Tdb8kd+lsANTFq7ivvExnA6AqTsV9bSadDoCqzKTLflEm0ukAqMpWuuyX5CqdDYCqmBX3tfV0OgCqspYu+0VZSacDoCpz6bJfFEcSAJq0lC77RTElAaBBz9NVvyimJAA0aSVd9oticQNAky7TZb8oc+l0ANRk4jpd9ovyOp0PgJq8Tlf9sjxP5wOgJuvpql+UsX46HwAVMa/0G2fpfADUZDFd9ctyk84HQE3ep6t+WSbT+QCoyU666pfFpB6A5iyni35hLA8CaM5euuiXZTOdD4CaOJn9jRfpfABUpOdk9jdM6gFozm266BfGpB6A5kymi35hVtMJAajIRbroF2Y0nRCAevQX0kW/LI7FATTHoJ5vWWAH0ByDer71IZ0QgIpspIt+YU7TCQGox+irdNEvzGI6IwD1mEnX/NKYFgfQmKN0zS/MeDohABXZThf9wmynEwJQj5GxdNEvzNt0RgDq4XbQdyxRBWjMSrrml8YSVYDGGBb3nel0RgCqYVjcdxZ66ZQAVGM1XfNLc5bOCEA93qVrfmlm0xkBqMd6uuaX5n06IwD1uErX/NLspTMCUI2JdMkvjomlAE35mC75xVlOpwSgGgaWfmfM0WyAprih+p2ldEYAqtGbStf80tylUwJQjfN0yS/Oh3RKAKrxOl3yi/MunRKAamylS35xTM0GaMpluuQXZz6dEoBa9AzN/t5IOicAtXAk4XtX6ZQAVOMgXfKLc5FOCUA13qdLfnHeplMCUA1TEr63kk4JQDXG0yW/OK/TKQGoxXS64pfH5gaAhljc8AObGwAa8jJd8cvTT+cEoBaz6YpfnM10SgCqsZMu+cW5TKcEoBb9sXTJL856OicAtTCp5wdz6ZwA1OJNuuKXx/YggIY4FveDw3ROAGqxnq745blN5wSgFo7F/WAinROASjgW94PddE4AajGfrvjlWUrnBKAWh+mKXx4b7AAaspKu+OU5TucEoBZv0xW/PEfpnADUYjtd8cvjiipAQ/bTFb88b9I5AajESLrgF2gtnRSASphY+qP5dFIAKmGR949G00kBqMRkuuCXZyGdE4BabKUrfnmMSQBoyIt0xS/PdjonALVYSlf88symcwJQiZ6p2T/4kE4KQCWm0wW/QCvppABUYjVd8Au0l04KQCXepAt+gT6mkwJQCdeDfnSbTgpAJebSBb9A0+mkAFRiNl3wC9RPJwWgEpfpgl+eqXROAGqxma745TGqB6AZ/XTBL9BlOikAlVhOF/wC3aWTAlCJtXTBL9BNOikAlXBF9Udz6aQAVOI0XfALZFwcQDNepgt+gU7TSQGohC2qP3qTTgpAJYxJ+NFiOikAlTAm4UdGlgI0wybvHxlZCtCMhXTBL9BoOikAdRhJ1/sCjaWTAlAJo3p+tJlOCkAlVtMFv0A76aQAVGImXfALtJ1OCkAlDtIFv0Cz6aQAVMK4uB8dp5MCUImVdMEv0Id0UgAq8T5d8At0lE4KQCXepgt+gaxuAGjGerrgF8jqBoBmXKQLfoFep5MCUImNdMEv0GE6KQCVMDb7R9YHATRjPF3wC7SaTgpAJV6lC36BztNJAaiD1Q0/YYUdQCMm0vW+RFbYATRiOl3vS9RPZwWgDvPpel+g3XRSACrxPF3wC2SNKkAzrFH90VI6KQCVWEwX/AJtpJMCUImP6YJfIKu8AZrxJl3wC3SXTgpAJV6nC36BZtNJAajEabrgF+gmnRSASkymC36BjtNJAajESrrgF2grnRSASuhBP5pLJwWgEi/TBb9AR+mkAFRCD/rRy3RSACpxlC74BZpMJwWgEu/TBb9A79JJAajEXLrgF+g0nRSASuhBP3qdTgpAJT6kC36B3qSTAlCJrXTBL9BhOikAldCDfjSTTgpAJd6mC36BFtNJAaiEHvSj1XRSACqhB/3oNp0UgEr4PehH5+mkAFTC2ewf6UEAzXBH9Ufz6aQAVEIP+pEeBNAMM0t/tJxOCkAl7G740XQ6KQCVsMPuR3oQQDP0oB+dpJMCUImVdMEv0EQ6KQCVmEwX/ALpQQDN0IN+pAcBNONduuAXaDSdFIBKnKYLfoH0IIBm7KULfoFG0kkBqMSbdMEvkB4E0IyP6YJfoH46KQCVWEwX/ALpQQDNWE0X/AL10kkBqMTzdMEvUDonALVYThf8AnkOAmjGSbrgF8jvQQDNGE0X/ALpQQDN6KULfoHcDwJoyFi64pfHrB6AhoynK3559CCAhuynK3557G4AaMhOuuKXRw8CaMh2uuKX5ySdE4Ba3KUrfnn0IICGHKcrfnmm0zkBqMVcuuKXZzmdE4BarKQrfnnm0zkBqMVpuuKXRw8CaMhBuuKX5zydE4BazKQrfnmep3MCUAuLVH+gBwE0ZD5d8ctzm84JQC0m0hW/PKvpnADUwgKhH6ylcwJQjal0yS+OHgTQlKV0yS+OHgTQFIOzv7eYTglANdbTJb84M+mUAFTD0NLvHaZTAlCNyXTJL85BOiUA1TAw7nt76ZQAVGMxXfKLc5pOCUA1ztMlvziT6ZQAVGM0XfKL8zKdEoB67KZrfmnepzMCUI+rdM0vzYd0RgDqcZmu+aV5m84IQD1m0zW/NDfpjADUw6CE78ymMwJQj3fpml+au3RGAOpxmK75pblIZwSgHs/TNb80l+mMANTDJdXvnKUzAlAR27y/tZROCEBFztJFvzD76YQAVMQFoW+NpxMCUBEXhL61kE4IQEVcEPpOOiEAFfmYrvml6aczAlAPF4S+M5rOCEA9XBD6zkk6IwAVGU8X/cLMpxMCUJHtdNEvzGo6IQAVeZsu+oWZSScEoCKT6aJfmDfphABUZCZd9Auzl04IQEWW00W/MJPphABUpLebrvpleZlOCEBNdtJVvyxz6XwA1MTk7G8cp/MBUJOjdNUvy2w6HwA1OUhX/bJcpPMBUJPVdNUvy0Y6HwA1MbX0G0vpfABU5Spd9otimTdAkxyM+9pYOh0AVXmZLvtlGUnnA6Amh+mqX5aJdD4AamJi3DcssQNoUG8qXfaLcpvOB0BVrFL92mI6HQBV2UqX/aIcptMBUJW9dNkvyut0OgCqYlrP196l0wFQlZHrdN0viSV2AI1aStf9kmylswFQl5t03S/JejobAHV5l677JdlOZwOgLg4lfGUnnQ2AuvTH0oW/IJY3ADRrI134C3LdS2cDoC4mJXzF4GyARr1O1/2SGJwN0Kj5dN0vyVo6GwCVGU8X/oIYWgrQrBfpwl+QvXQyACrzMl34C7KSTgZAZT6mC39B5tLJAKjMRLrwF+QmnQyA2uykK385XqRzAVAbt1T/s5HOBUBtDtKVvxxX6VwA1OYkXfnLsZvOBUB1rtKlvxwj6VwA1OY4XfnLMZ3OBUBtjC39z206FwC1Mbb0PzPpXABUZz9d+ovxOp0KgOrcpEt/MSbTqQCozmm69BfjQzoVANXxg9C/ZtOpAKiPG0L/uExnAqA+b9O1vxSG9QA07k269pdirJdOBUB1Rq/Txb8UE+lUANTnMl37S/E8nQmA+rxM1/5SGJQA0Li1dO0vxV46EwD16U+li38hVtKZAKjQXbr4F2IrnQiACr1LF/9CGJQA0Dzjev62kU4EQI2W0tW/DPvpPADUaC5d/ctwbVACQPOczv7bSToRABXqjaerfxlu04kAqJFlql98TOcBoEZmZ39hUAJAwOhYuvwX4WU6DwBVepEu/0V4m04DQJWMSvjsLp0GgCotp8t/EXbSaQCo01m6/pdgIZ0FgDpZZPeZbd4ACefp8l8El1QBInbS9b8Eh+ksANTJy7hPJtNZAKiTl3GffEhnAaBSXsbZpAqQ4mXcs2dn6SQAVMrLuGfPptJJAKiVl3HPno2mkwBQqZV0AyjA83QSACrlZZwtdgAxZsY9e5fOAUCtJtMdIG8unQOAWp28SreAOBeEAFJsU91IpwCgWgfpFhA3nk4BQLVGptI9IM4FIYCU43QLiDtPpwCgWovpFhA3k04BQLV6++kekHaaTgFAvd6ne0Da+3QGAOpV/bweF4QAci7TTSDMBiGAnL10Ewhb6KUzAFCv6q8InaQzAFCxt+kmELaWTgBAxW7TTSBsL50AgJptpLtAlsPZAEGn6S6Q5XA2QNDoQroNRO2k4w9QtboHl+46nA0QtJpuA1nT6fgDVO0s3QaiFtPhB6ha3bMSHM4GSBoZT/eBJIezAaKq3uDgcDZA1PSrdCMIcjgbIGs23QiCHM4GyFpMN4Ikh7MBsmo+nu1wNkBWzUPjTtPBB6hczcez59LBB6jdXLoT5DicDRB2MpZuBTEOZwOk3aRbQcxYPx17gNo9T7eCnPl07AGq9yLdCmIO06EHqF6991RX0qEHYCPdC1Ju0pEH4E26F6RspCMPQG8p3QxCTC0FyKt2YM9yOvIAVDuw52M68gD8tZJuBiGT6cAD8NdopQ9Cx+nAA/DXXy/T3SDDwTiAAoxOpdtBxIKDcQAFOEq3gwzrvAEKUOmD0Ew67gB88j7dDiIcjAMowcRCuh8kvE2HHYDPqlzqfZmOOgCfndT4IDSVjjoAX1T5i9BJOuoAfDZR49G4xXTUAfiixqlx79JBB+CLkc10R2jeVjroAPxtMt0RmredjjkAfxvZT7eExk2ZGAdQiL10S2ieVaoAhejtpFtC4w7TMQfgHwfpltC4l+mQA/CP3ka6JzRtNh1yAP61lu4JTbtKRxyA/8ymm0LTRtMRB+Bf82PpptCwtXTEAfjPh3RTaNhpOuAA/Ke20aXW2AEUpLKJPdbYARSkf5VuC43aNa0HoCCVXVSdT8cbgP/rXabbQqPepOMNwFdur9N9oUlH6XAD8LXjdF9o0l062gB8bWI83RgatJ+ONgDfeJduDE2aSEcbgK/1ztKNoUGL6WgD8I3FdGNo0GQ62AB8az3dGZpznI41AN86WUi3hsacpWMNwHdW0q2hMWP9dKwB+FZ/J90bGnObjjUA36lnrfdeOtQAfO9tujc0xQohgOKMbqabQ0M20pEG4Ae1LHF4NZKONAA/eJHuDg1ZTQcagB8s76a7QzNO04EG4EeVXBIyKQGgQJVcEtpJxxmAn1h9le4PTbh2KAGgRO/T/aERa+kwA/ATI0vp/tCEd+kwA/Azq9fpBtGAm3SUAfipD+kG0YCldJAB+Kkq3saNpqMMwE/VMEB7MR1kAH5uK90hhm8yHWMAfq6Ct3Hr6RgD8Avdv6l6lQ4xAL9ylO4RQzeRDjEAv9DfSPeIYZtJhxiAX5nv+haHlXSEAfilyXSTGLLZdIAB+KXedrpLDNdmOsAA/Nr0VLpNDNd0OsAA/NpBuksM15t0fAH4jeN0mxiqD+nwAvAbI51e7L2RDi8Av3M7lm4UQzRmnzdA0Tp9QNs+b4Ci9V6kG8UQGZ0NULaJzXSnGB63VAEKN5PuFMPjlipA6d6nW8XwLKdjC8DvdXhmz0E6tgD8wcR+ulcMi1uqAMVb6+pSVbdUAcq3km4WQ/JqNB1ZAP6kd5fuFkOymI4sAH80epXuFsNhlypAC6x2c3DcXTquANzDabpdDMV4OqwA3MfbdL8Yivl0WAG4h/5lul8Mw+t0WAG4j5MuTi/dSkcVgHtZ6+C5hLN0UAG4n3fpjjF4126pArTEcbplDN5MOqYA3M/IRrplDNz7dEwBuKeTzo3QvkyHFID7ul1IN40BGxtJhxSA+zpMN41BM7YUoD1eppvGgL1MBxSA+7tJd43B2k7HE4D7G+nW0J7dfjqgANxfx4b2rKXjCcADdOtwnD12AK0y06XJcS/S0QTgQfbSjWOAFvwgBNAuc+nOMUCr6WAC8CC99XTnGJzJdDABeJj+drp1DMxdOpYAPNDoUrp3DMpULx1LAB5ouTPXhJ6nQwnAQ51PpZvHgLxLRxKAB1vbTXePwVhPBxKAh3tznW4fAzGejiMAj3Cabh+DMZ+OIwCPcJRuHwOxlw4jAI/xNt0/BsEPQgCt1ImBCeNuCAG0Uv8u3UEGwA0hgHYauUh3kKdzQwigpUbbv9zbyDiAtprYSfeQp7JDCKC1Tlo/v3QtHUIAHmt5P91EnmglHUEAHq3tTWg7HUAAHm++3U1obCQdQAAeb77d64Rm0vED4AnOx9N95Cnep8MHwFM8b3MT2khHD4AnaXUTmkhHD4AnuW1xEzpMBw+Ap2nxk9BWOnYAPFF7m9BSOnQAPFV77wlNp0MHwFO1tgm9TkcOgCdraxO6SQcOgKdbvkq3k0fZTMcNgAGYbuc+ofN03AAYgImNdD95DAu9ATqhleu9LfQG6IaRF+mO8nAL9jcAdEN/Nt1SHm4xHTQABqN/k24pDzaXjhkAZq7hgQAAH11JREFUg/I+3VMeaicdMQAGZjLdVB7KuB6A7jgYS3eVh9lLBwyAwfm4kG4rDzKbjhcAA7Q2le4rDzHVT8cLgAE6b9UE07V0uAAYpFbN7TlKRwuAgRpp0W3VjXSwABis3od0a7m/k3SwABiw0+t0b7kvy1QBOuewLWe0LVMF6J7VzXR3uZ/xXjpSAAzcckt2q66mAwXA4I1epNvLvayk4wTAEPSP0/3lPi7TYQJgKFbSDeYeXo2mowTAUBzsplvMn71JBwmA4Xh+lW4xf3ScjhEAQzJR/MmETaezAbqqv5VuMn/idDZAd+0Vvlz1ZTpAAAxP4TMTztLxAWCITi7Tfea3ptPxAWCIyr6uepoODwBDVfKPQnfp4AAwXIvj6VbzS7sj6eAAMFzLG+le80uH6dgAMGT9uXSv+RWjEgC673Aq3W1+zqgEgArMn6Xbzc8ZlQBQgUIn9xyl4wJAEw4W0g3nJ4xKAKhDke/jltNRAaARIzfpjvMjoxIAalHe+7gX6ZAA0JTbpXTT+c7YaDokADRlpLT7qkYlAFTkY1nz44xKAKjJxF2673xt3KgEgKqc7qY7z1fW0tEAoFHnBV0VMioBoDIj5YzuWUrHAoCmLW6mm8+/ztOhAKBpEy/SzecfK+lIANC8vTK2Cm2k4wBAwHQZj0LmlgJUqYhHocl0FACImL5Id6Bnz7bTQQAgpIBHoZN0DAAIyT8KWSIEUK+98FohS4QAKja/He1BYxPpAAAQdBDd6PA6/fEBSJo4Dvag2fSnByBr5irWg3ZH0h8egKyRo1epJmSjN0D1nl+GetBN+pMDENc7zdxYneqnPzkAect3kSY0k/7cAJRgZinQg96mPzUARRiZbH5uwngv/akBKMNJ85eF1tKfGYBSLO403IM+pD8xAMXorzT7Qm7fyzgA/jO93mgT8jIOgK+sNjlOeyv9aQEoy2FzM+ScjAPgWyOTjQ1OWEx/VgBKc7LV0CRT11QB+MHtRSM9aNzMOAB+tLjRRBMyMw6An+gdNjBE7jj9KQEoU39vc9g9yMs4AH5h+EfkvIwD4Fcm5naH2oO8jAPg1yaOhtmFpkbSnw+Akp0M81noY/rTAVC26a2xYfWgm/RnA6B0y2+H1IUWvIwD4E+m54azXegw/cEAaIGJlWGc1F5PfywAWmF0ZfC3VndH058KgHboH5wNugm9SX8mAFpjdXawPWg2/YEAaJHzrUFeGPIyDoCHmDga4A9Dr9OfBoB26R++GFQPukh/FgBaZ3lAPej6JP1JAGibiQH1oGeT6U8CQNscDqoHnaU/CQBtszWoHvTsPP1RAGiZq4H1oKP0RwGgXc4H1oKe7ffSHwaAVpkcXA96tpb+MAC0yvYAe9Db9IcBoE1GB7nUbsomOwDu72CALcgmOwAeYn2gPcjwbADurT/YnapjhmcDcF8zA21Bz57tpT8QAK3xdsA9aDv9gQBoi94A9wf9bTr9kQBoibVBt6BnK+mPBEBLvB94D9pJfyQAWmJp4D3o2W36MwHwv/buay2OYwsD6MyQc845gwgCBAK//4sd2Tq2EkKE6f47rHVjf75yV9XsTVfv2lULF/1PQZ2p9EMBUAt7BeSgVc2zAXiBArbiOp2J9FMBUANFbMV1OsvpxwKgBq4KyUFDI+nnAqD6NgrJQZ379HMBUHl9vMX7B0fpBwOg8orZivviIv1kAFRdQVtxjggB8CdFbcV1OsO99LMBUG2FbcW50huAPyhsK67TuUk/GwCVdl1cCnJECIBnTRWYgzqj6acDoMK620XmILcIAfB7s0WmoE5nMf18AFTXXbE5aC39fABUVm+42Bw0NpB+QgCq6rHYFNTpfEo/IQBVtVx0DrpMPyEAFTUwU3QO+jCefkYAqmmn6BTU6SyknxGAaropPgfNpZ8RgEoaGSo+B3Vm008JQBUdlpCCOufppwSgik7LyEEalwLwq+kyUpDGpQA8Ya+cHKRxKQA/656Vk4M6S+knBaBqCm6Z/Y2qBAB+sl9WDlKVAMCPBsbKykGd+/SzAlAtt6WlIFUJAPzopLwcpCoBgO+NfygxB+2nnxaAKlkoMQV1NgfTjwtAhWyUmYNUJQDwzWKpKUhVAgDfrJWbgzqL6QcGoCp6wyXnIFUJAPzfp5JTUGdGVQIAX5V5OOirw/QjA1ANpR4O+uoo/cwAVMNV6SlIVQIA/+huB3KQqgQAvpgIpKDOphscAPjrr8lEDuqMph8bgLyRoUgOOuumHxyAuNFICup0HtMPDkBcue1Kv7lMPzgAaUuhFNTpTKcfHYCw/VgOmko/OgBZgzOxHDQ8kH54AKIOYymo09lKPzwAUSvBHLSSfngAko6DKUjTOIB2e4jmoPP04wOQM1D2Bao/GjpIDwAAMVvRFNTpLKQHAICYZEXC37Z76REAIGQxnII6nd30EAAQkuuR8K+T9BAAkBHskfCf4/QgABBxn05AXzykBwGAiNStDd8bG0yPAgABs+n884/79DAAELCcTj//cKc3QAuNDKXTz1fu9AZon4V08vk/5dkArdPdTieff12nhwKAkj2mU89/9tNDAUDJLtOp5z9DI+mxAKBU8x/Sqecb3bMB2uVzOvF8Z1X3bIA26a2mE8/3btPDAUCJdtJp5wdH6eEAoESn6bTzo9n0eABQmut00vnJZHpAACjNeTrp/Gw6PSIAlOSgIq3ivplKDwkAJblKp5xfuEYIoCWqVZj9lWuEANrhNp1wnuAaIYB2WEknnKfspkcFgBIspdPNk9bTwwJACSbT6eZpi+lxAaBw4x/T2eZpy+mBAaBwVeqY/b0P8+mRAaBgA8PpZPM7a+mhAaBgh+lU81ub7lMFaLiNdKr5vav02ABQqIl0onnG8EB6dAAo0mU60TxHwx6AJptOp5lnnfXS4wNAcdbSaeZ5n9LjA0BhBmfSWeZ5R+kBAqAwo+kk8yez6RECoCDds3SO+ZPL9BABUJBP6RTzZxfpMQKgGJW8OOhH5+kxAqAQs+kE8wIfdS4FaKRKn0/911R6lAAowHE6vbzIjM6lAA10nk4vL7OQHicA+u5gKJ1dXmZV51KAxqnq/am/OEyPFAB9NjiWzi0vpXMpQNNUvk3PN7fpsQKgr3rb6czycnPd9GgB0E+36cTyGq5wAGiUjXReeY0jL0IADfKYTiuv85geLwD6Zz2dVV7nND1eAPTNdTqpvJa77AAaYzmdU17rJD1iAPTJ/Md0Tnm1pfSYAdAfa+mM8no36TEDoC9GZtIZ5Q2u06MGQD9cpfPJWyynRw2APhgYTueTNzlOjxsA73efziZvs58eNwDerXeWziZv83E+PXIAvNdWOpm81Vp65AB4p+5cOpe81eZBeuwAeJ9P6VTydlPpsQPgfY7SmeTtvAgB1FvNLm34kRchgFqr2aUNP/IiBFBns+k08j5ehABq7CSdRd7HixBAfS2mk8h7PaRHEIC3mkznkPfyIgRQVxfpFPJ+XoQAaqr2r0GdztB4ehABeIvjD+kM0gdehABqaTmdP/rBixBAHU034TVI+2yAWjpPZ4/+8CIEUD/zQ+ns0Sdr6ZEE4LX207mjX7wIAdRNY16DvAgB1M5dOnP0z9B8ejABeI3x5rwGdTp36dEE4DXW0nmjn7wIAdRJo16DOp399HgC8HIN+hr0t4/H6QEF4KUa9hrU6SynRxSAl2rM2aD/LKaHFICXadDZoH9dpscUgJdp3mtQpzObHlQAXqKBr0Gdzml6VAF4iYY0zP7JY3pYAfizhtwb9LOjbnpgAfijRlyf+oSd9MAC8CfHzXwN6nTmeumhBeAPmvoa1OlspYcWgOddpDNFcbYH0oMLwLNu0pmiQKPpwQXgOYvpPFGk4cH08ALwjJN0nijUVXp4Afi92XSWKNbMSHqAAfit03SWKNhUeoAB+J3HdI4o2uZ4eogBeFr3KJ0jCudWb4CK2klniOJ9uE4PMgBP6c2lM0QJTtKjDMBTttL5oRQT6WEG4Fe9s3R6KIU7HAAq6D6dHUpymx5oAH42OJxODiXRuhSgcvbSuaE0C+mhBuBHBzPp1FCaMR17AKplLZ0ZSvSQHmwAvjc9lE4MJRqaTg83AN9p7g3eT5lMDzcA31yns0LJltIDDsB/mn113a9O0wMOwL8m0jmhdLvpIQfgq+5KOiWUbq6XHnQA/tGCOxt+cZgedAD+1pJmpT8aHkwPOwBfjKbzQcReetgBaFGz0h8NzacHHoC/ptLZIMRBVYC4+c10MkiZTQ89QOu1q0vP99yoChC2mM4EQVvpwQdoufV0IghSnw0QtZvOA1Gf08MP0Ga9uXQaiHKREEDQfToLhKnPBohp6fHU70ykpwCgtT6nU0DchvpsgIzx1h5P/Ub/bICM9h5P/WZ4JD0LAK3U5uOp3zykpwGgjbqn6fBfCR+P0xMB0EK36ehfEZfpiQBon4HtdPCvCvXZAGXbS4f+ytjopecCoGXUZX8zmp4MgJZRl/3NzHh6NgBaRV3295bT0wHQJt2VdNivFmUJAOVRl/2jOWUJAGVRl/0zZQkAZVGX/TNlCQAlmVeX/QtlCQDluEkH/CpSlgBQhol0uK8kZQkAJehtpMN9NSlLACjeaDrYV5SyBIDCHYylg31VKUsAKNp5OtRXl7IEgGItpQN9hSlLACiURnHPUZYAUKTDdJivNGUJAAUaHE6H+Wq7SU8QQIOtpYN81e2mZwigsa4/pGN81W0PpucIoKG6p+kQX30P6UkCaKitdICvgQ+L6VkCaCQFCS9x5JAQQAH20+G9HhwSAug/HRJeZmY+PVMAjaNDwks5JATQb65seLFP6bkCaBhXNrzcqkNCAH21nA7sdbKWni2ARplNh/Va+bCUni+ABultpMN6vWw4JATQN1fpoF43C+kZA2iM+c10TK+bzen0nAE0xWU6pNfPSXrOABpiJx3Q6+g2PWsAjTC4mo7ndTR2kJ43gCbQq/RNJtPzBtAAepW+kZY9AO/laNBbDY+k5w6g7hwNerPz9NwB1Ny0o0Fvt5uePYBa666n43idaaAN8B5b6TBeb/vp+QOosZHhdBSvucf0DALUl1uD3mnbbhzAG02kQ3j9raXnEKCmBs7SEbwBZtOzCFBPD+n43QRnA+lpBKijxQ/p+N0IU+l5BKghTXr648NSeiYB6mcvHbybYsNuHMArXQylY3dj2I0DeJ3uSjpyN4jaOIBXWUjH7SZxUhXgNbTL7it94wBeTrvsPnOLA8CL3adjdtO4UxXgpcbH0jG7cSbTcwpQF5fpiN1At+lJBaiH23S8bqKx8fS0AtTBgYvrirDeTU8sQA3cpKN1Q92nJxag+rbSsbqpNo/TUwtQdWriCrPSS08uQLV1T9KRusEW0rMLUG2H6TjdZEPX6ekFqDI7cYVylRDA7+kTV7CH9AwDVJc+cUV7TE8xQFVNz6RDdOOtal4K8CQ7cSW4TM8yQDWNpuNzK2iXAPAEd6eWYvMiPdEA1dM9TUfnllCgDfCLq3Rsbo219FQDVM3ix3Robo/d9GQDVMvAXDowt8iw++wAvneXjsutcuI+O4BvHtNRuWVG0xMOUB0jq+mg3DJDi+kpB6iMyXRMbp05BdoAX7m+u3x36UkHqIZ5lwYFfEpPO0AVaJAQoUAb4C8NElLWFWgDXA+lg3FbKdAGWm9gIx2KW0uBNtB6a+lI3GJzg+nZB4iaSMfhVttPTz9AkgYJWQq0gTZbTgfhlhtToA20lwYJaQq0gdbSICFvIb0IADK66+kAjAJtoK0W0vGXL84UaANtdKFBQiUspxcCQPk0SKiK2/RSACidBglVsXmcXgsAJdMgoTqOXKoKtIsGCVXykF4OAKW6SYddvveYXg8AJbpPB11+4FJVoEWOZ9JBlx/p2QO0Ru8oHXL5mZ49QFtMpQMuv/iwlF4VAKWYTcdbnrCtZw/QBiPb6XDLU/TsAdpgMh1sedpWemUAFM69dVWlZw/QeO6tqy49e4CG652mAy2/p2cP0Gyf02GW53xKrw+AAi19TEdZnqNnD9Bgg2fpIMvz9OwBmus8HWL5Ez17gKa6TQdY/kjPHqChlGXXgZ49QCN1lWXXwk16oQAU4CodXHkZPXuA5llUll0TmxfptQLQZ8qy62POJyGgYZRl18h5erUA9JVu2bVym14vAH10PJOOqryGT0JAg/SO0kGV15lzjQPQGGvpkMpr+SQENMVuOqDyej4JAc1wMJyOp7yeT0JAI3TX0+GUt9jwSQhogL10MOVtfBIC6s/VqbXlkxBQdyPb6UjKW81Mp1cPwPtMpgMpb3fkkxBQa6PpMMp73KXXD8A7XA+loyjvspNeQQBvNrCRjqG8j09CQH25sKH2fBIC6uo2HUB5P5+EgHqaH0vHT/rAJyGgjnor6ehJP/gkBNTRQzp40h8+CQH1M5EOnfTLWnotAbySCxsa5FN6NQG8igsbmmTMJyGgVlzY0Cg+CQF1MvEhHTXpK3cJAfXhY1DjuEsIqAsfg5pn8zq9qgBe5nM6YNJ/Z4PpZQXwEk4GNdJkel0BvICPQQ01ml5ZAH/U8zGooT7OptcWwJ9MpUMlRVk9SC8ugOf5GNRgJ9308gJ4zryPQU22l15fAM9wZ1DDPaZXGMDv3aVjJMXSvRSorp10iKRoupcCVXU8k46QFE73UqCaBjbS8ZESHKbXGcBTltPRkTIMLaUXGsCvRtPBkXI4qgpUz9JQOjZSkvVeerEB/GhkOx0ZKc1UerUB/KB7ko6LlGgnvd4AvufaulaZuUgvOIBvHtNBkXK5VRWojumxdEykZDdaaAMVMTiXjoiU7iq96gD+0b1Jx0MCdtPrDuBvV+loSMLYcXrhAfz118SHdDQkYk5dAhCnHqG1LtUlAGEDR+lISIyrvYEwzbLb7FN6+QHtpll2q82oSwCCZj+moyBRZyPpJQi01/hwOgYSpi4BSFGPQOdzehUCLdWdTMc/KsA9DkCE/gh8sbmYXohAG+2mgx/VsHqQXopA+1zMpGMfFbEykF6MQNuMuK+Bfy2nVyPQMr2TdNyjQhbS6xFol7V01KNSXCYElOgwHfOolpmL9JIE2mN2KB3zqBhNe4CyzGvRw8/We+llCbTD4EY63lFB++l1CbSCkjiedJ9emUAb3KVjHdX04TG9NIHmc2sdvzFznV6cQNNNuLWO39nWOQ4o1MVYOs5RYTrHAUUaOUtHOSrtxrWqQGEGTtMxjoqbSq9RoLG6y+kIR+Udplcp0FSf0/GN6vs4kV6mQDNtpcMbdTB2nF6oQBNNaFTKS2hfCvTftbu7eZlTFdpAn82vpiMbtaFCG+gvvbJ5hbX0egUaRa9sXmU0vWKBJjlPxzRqZie9ZIHmcDCIVxqaTS9aoCkcDOLVHBMC+uPRdQ283vZ4euECTbC0mY5m1NLRYHrpAvV3PJyOZdTUSS+9eIG6czaVNzt3VhV4lxFnU3m7h/T6BWptcCUdxag1Z1WBt+tdpmMYNXebXsNAbbk3lff6uJtexUBdTaUDGPWnYQLwNlfp8EUTjF2kFzJQR/fp4EUzrE6nlzJQP7fp0EVTzLncG3ilXU3i6Bdde4DXeRxKxy0a5HQgvaCBOpnVp5R+mtQ6DnixpZl0zKJhlrWOA17oQqts+m1fEgJexG0NFOAuva6BWpjfTkcrGulzemUDNTB+lo5VNNRCem0DlTc+l45UNJabHIDnSUEU6DC9voFKk4IolOuEgN+TgijWx0/pNQ5UlhRE0SQh4DekIIonCQFPkoIogyQEPEEKohySEPALKYiyDO2mVztQMbojUB5vQsAPpvWIo0SSEPCdi9V0UKJdJCHgP9cua6BkkhDwf0tj6YBE+yhMAP4xsZkOR7TRR73jgL/++jSUDka0lC7a7TF4MH09+7izNXq1t/ewtrZ2vry8fHP51eSXf9//8t/W9vauRrd2Hmevj8cHe+n/Y8py+zEdiWgt9wk1WO/gYmLnfm9/8nRu9S27/UPDZyuX51MLW7tL0yPph6E4932PK/BiV+n1T7+NXO/eT02uz/X3I/OH1Y2T/autieOB9PPRZwt9XSjwSlPpXwB9Mr60s3B3uVH4x+Xho8mH0Z3FwfTz0hfdtaIXDDxvv5v+FfA+vend0f2V0mtrh1eW93auvRbVW2+57HUDPzv38bmuDibu107CHVbOLh8Ol7wU1dTgenb1wN9uJKHa6c5PLExWqLnK9uTezrQ36roZ30gvHPjbpQ2VGhlYPFw7nUmvmaeMnTzcXkhE9XGhSykVcXSQ/jXwIvM7U+sVP044sz61M54eJ15Cfx6qY246/XvgDwYWRydr01Zye3J00Q5vxe3qz0OFrF6nfxH83vjtfv027jfXr2Zt8lbX/Yf0CoHvjc2mfxM8aXxnv77XW26eLCx5H6qi7lR6bcBPhnbSPwt+drBzN5deF+82c7mwqFChYgZv0ssCfqV5XJUMPD7UP//8a2z5VtlLhYwfpVcEPGXKn6sVMX942bgPxnNTs7blqmGxQifL4HtaJlTA4O5dU49tjC1veR3K22nc3zc0x4mmK1nTo1U///Nep6POAWRdpZcAPMNBoaDphZX0/JdibmoxPdTt1TtPTz88a3gp/SNpp+7iVFN34J5ytjZh3zdh5DQ99fAHQ7fpn0n7DHxabl/blNWHJTUwZVts0x861JbyuFL1Jvbbl4C+2rYpV66thn9spCmWtVgpS4sT0Fdn0lBpenfp2YYXWlFBW4be43m7E9BXR6OWWxnG21HxQjNsX6R/MM23dFebNthF+3C549W7aLOWG3Uytpv+yTTb9FV9G5EWYuxOQWahFrTJpmb2VCYUZeRegewTzq7m0zPTWIOT6dmFV7vUM6EIvU83ipN+5+STU0NFOG5O+1va5MxHob67frAr/6zhqeP0HDXP7Ux6WuFNNh1X7avBLZVJL7CypUChnwZ156G+pmyN9Et3YtIe3AuNrXkF75tr+3DU2fpI+ifUDPN7rmx5ldMdf/70Q3fUXz7U27Zj7O/W271MT2MNja3p4f5uI+7spvaGFhRpv4tXoDe73LX23mVWi1Ka4MZ+3Jv1Pp2kp6/WzkYtvjfrfU5PH/THtiPsbzPuFejdNu/UJ7zNtJPQNIf6uNfrzi77HNwXK+oT3mDLoSCa5ERr49cZ3NpIz1mDbC/YknudcXvANMzwRPpXVSfXa/4I7a/NZQWar7DlUhCax37cC/V219Nz1Ui25F7KSxDNtOK8xgvMf9YRriiq5F7k0EsQDbU56rjG87oTN65pKZIquT/yEkSTrbvh5Rkjo1pzFe/k0V9Cv9f1JYhmm/Eq9DsX6hBKMjfqaqvfuPApksbzKvQULeFKpZfckwb2nEijBca20j+1yhkZ1ZerbJcOC/xs4iw9KVCOGwdWv3e976/PhLlDd919Z16LbNpj2KvQvwa2jtKz0V7De/4a+r/ewmZ6NqBM68fpH10lzO85DBT1YXI2vQYqYUlnKNpmaK/1GyEOA1XC6W3r2yfML6cnAQLm2v1RePDeYaCKGN4bT6+GpJEp3yNpqcn27sZfrDkKWCEf2nvjau/QdjDt1dITqw4DVVBLD67uehun3U7b11FfU9KKmllrXS+5JVelwmSr+iZ0Z5c/pkec31rZalN9wrRSBPhiaKo1myAjo06iV9xqa+oTxteUIsBXq4et+Cy0dO5HXwNDy204MjSvOQd8Z2M3/Zssmn4INbLd9Jch70Dws8tGN064VopdL0OTE819N5/e900SfvFxv6nFCYOHK+nB5fW2r5r5MjR9LgPBk4YamYWWzjWDrKkPlzuNayh1ca4/FPzW0F3DspDbuWtubH8pvYb6aeIkPaBQcU16F+pNLPvyW3/bew1Zkr0dZTHwZ0N3zdiGX3zQDqEp1rdG0svp3Q6uVtPDCDUxdFf7GrmDUfexNMrQzW2tT1IveSWH17ip8zHBwZ1LlUfNszn5qaYVCoOHNuHgtY5qerNYb3d5Jj12FGTmfLd+aWhpX2EmvMXqVe024bsT+86iNtvM5G2dluWBwkx4u81afRjqLj346tsGH0/u61E2M7Bz6TAQvM/6bT02P3qzaxJQi6wsXFe8l09vYt+mMPTBZvXbGPcmJKD2GV7equxd9N3FKSsS+mZjtMJb8IOfln0DaquVvaXqlc70Ju4cTYP+Gpp8rN5v/Yvj0RPnLtptbHK0SttyI7eTtuCgCKtrSxX6qX8x8LjmVlT+NnYzuliBv5G61wvrihCgOKsPVUlD3Yv7S6cu+M7MycJsspXCwe25HTgo3PB+/pTg/NayXztP2Z4cXQysz5HdKVdUQVlmloOnBOdvz7fTA0ClDa2s3V6UtjPXPb69cwwVyrbyebb0/ffe4v2yD0C8yNDG8sLudME7x+OPeydqMiFk5mZ0sbSvQ+Ofpk4VwPFKmyvno4/HBWzO9S52pk5sCEPa2M3oUtHb7/OPC5O233iH1dPzq9vZ8b78xTSyeLs3OefvIaiMj0drt8eFbMwNLG49rNvroE+GztYnHxa2JhYPXr9cuwfXE4dTk0eWI1TS5srd4WLfKmO747NbnyfnHLWgIMMbJ5Pna3ujhzuPs9fzI4ODv67dgZH54+vZT4cLU/s3K6vWItTA6snD4ez8O96JBo///mNzw04HAUPDw8Nnc3Nzq1/+6X0Hauvj2cndwtbE9cgL9997I9NLn+6nztfnNDkBoE8+rm6sT+5PXY0e3u5OzM7OLl5fX3/5x+7u7s7W1uHo3sP5zfrGtsQDFOB/pFNVw7wpnT0AAAAASUVORK5CYII=',
+ '$company_logo' => 'data:image/png;base64, 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',
+ '$payment_link' => 'http://ninja.test:8000/client/pay/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
+ '$status_logo' => '',
+ '$description' => '',
+ '$product.tax' => '',
+ '$valid_until' => '',
+ '$your_entity' => '',
+ '$balance_due' => '$0.00',
+ '$outstanding' => '$0.00',
+ '$partial_due' => '$0.00',
+ '$quote.total' => '$0.00',
+ '$payment_due' => ' ',
+ '$credit.date' => '25/Feb/2023',
+ '$invoiceDate' => '25/Feb/2023',
+ '$view_button' => 'View Invoice',
+ '$client.city' => 'Aufderharchester',
+ '$spc_qr_code' => 'SPC
+0200
+1
+
+K
+434343
+
+
+
+
+CH
+
+
+
+
+
+
+
+0.000000
+USD
+
+
+
+
+
+
+
+NON
+
+0029
+EPD
+',
+ '$client_name' => 'cypress',
+ '$client.name' => 'cypress',
+ '$paymentLink' => 'http://ninja.test:8000/client/pay/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
+ '$payment_url' => 'http://ninja.test:8000/client/pay/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
+ '$page_layout' => 'portrait',
+ '$task.task1' => '',
+ '$task.task2' => '',
+ '$task.task3' => '',
+ '$task.task4' => '',
+ '$task.hours' => '',
+ '$amount_due' => '$0.00',
+ '$amount_raw' => '0.00',
+ '$invoice_no' => '0029',
+ '$quote.date' => '25/Feb/2023',
+ '$vat_number' => '975977515',
+ '$viewButton' => 'View Invoice',
+ '$portal_url' => 'http://ninja.test:8000/client/',
+ '$task.date' => '',
+ '$task.rate' => '',
+ '$task.cost' => '',
+ '$statement' => '',
+ '$user_iban' => ' ',
+ '$signature' => ' ',
+ '$id_number' => ' ',
+ '$credit_no' => '0029',
+ '$font_size' => '16px',
+ '$view_link' => 'View Invoice',
+ '$page_size' => 'A4',
+ '$country_2' => 'AF',
+ '$firstName' => 'Benedict',
+ '$user.name' => 'Derrick Monahan DDS Erna Wunsch',
+ '$font_name' => 'Roboto',
+ '$auto_bill' => 'This invoice will automatically be billed to your credit card on file on the due date.',
+ '$payments' => '',
+ '$task.tax' => '',
+ '$discount' => '$0.00',
+ '$subtotal' => '$0.00',
+ '$company1' => ' ',
+ '$company2' => ' ',
+ '$company3' => ' ',
+ '$company4' => ' ',
+ '$due_date' => ' ',
+ '$poNumber' => ' ',
+ '$quote_no' => '0029',
+ '$address2' => '63993 Aiyana View',
+ '$address1' => '8447',
+ '$viewLink' => 'View Invoice',
+ '$autoBill' => 'This invoice will automatically be billed to your credit card on file on the due date.',
+ '$view_url' => 'http://ninja.test:8000/client/invoice/UAUY8vIPuno72igmXbbpldwo5BDDKIqs',
+ '$font_url' => 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
+ '$details' => '',
+ '$balance' => '$0.00',
+ '$partial' => '$0.00',
+ '$client1' => ' ',
+ '$client2' => ' ',
+ '$client3' => ' ',
+ '$client4' => ' ',
+ '$dueDate' => ' ',
+ '$invoice' => '0029',
+ '$account' => '434343',
+ '$country' => 'Afghanistan',
+ '$contact' => 'Benedict Eichmann',
+ '$app_url' => 'http://ninja.test:8000',
+ '$website' => 'http://www.parisian.org/',
+ '$entity' => '',
+ '$thanks' => '',
+ '$amount' => '$0.00',
+ '$method' => ' ',
+ '$number' => '0029',
+ '$footer' => 'Default invoice footer',
+ '$client' => 'cypress',
+ '$email' => '',
+ '$notes' => '',
+ '_rate1' => '',
+ '_rate2' => '',
+ '_rate3' => '',
+ '$taxes' => '$0.00',
+ '$total' => '$0.00',
+ '$phone' => ' ',
+ '$terms' => 'Default company invoice terms',
+ '$from' => '',
+ '$item' => '',
+ '$date' => '25/Feb/2023',
+ '$tax' => '',
+ '$dir' => 'ltr',
+ '$to' => '',
+ ],
+ 'labels' =>
+ [
+ '$client.shipping_postal_code_label' => 'Shipping Postal Code',
+ '$client.billing_postal_code_label' => 'Postal Code',
+ '$company.city_state_postal_label' => 'City/State/Postal',
+ '$company.postal_city_state_label' => 'Postal/City/State',
+ '$product.gross_line_total_label' => 'Gross line total',
+ '$client.postal_city_state_label' => 'Postal/City/State',
+ '$client.shipping_address1_label' => 'Shipping Street',
+ '$client.shipping_address2_label' => 'Shipping Apt/Suite',
+ '$client.city_state_postal_label' => 'City/State/Postal',
+ '$client.shipping_address_label' => 'Shipping Address',
+ '$client.billing_address2_label' => 'Apt/Suite',
+ '$client.billing_address1_label' => 'Street',
+ '$client.shipping_country_label' => 'Shipping Country',
+ '$invoiceninja.whitelabel_label' => '',
+ '$client.billing_address_label' => 'Address',
+ '$client.billing_country_label' => 'Country',
+ '$task.gross_line_total_label' => 'Gross line total',
+ '$contact.portal_button_label' => 'view_client_portal',
+ '$client.shipping_state_label' => 'Shipping State/Province',
+ '$invoice.public_notes_label' => 'Public Notes',
+ '$client.shipping_city_label' => 'Shipping City',
+ '$client.billing_state_label' => 'State/Province',
+ '$product.description_label' => 'Description',
+ '$product.product_key_label' => 'Product',
+ '$entity.public_notes_label' => 'Public Notes',
+ '$invoice.balance_due_label' => 'Balance Due',
+ '$client.public_notes_label' => 'Notes',
+ '$company.postal_code_label' => 'Postal Code',
+ '$client.billing_city_label' => 'City',
+ '$secondary_font_name_label' => '',
+ '$product.line_total_label' => 'Line Total',
+ '$product.tax_amount_label' => 'Tax',
+ '$company.vat_number_label' => 'VAT Number',
+ '$invoice.invoice_no_label' => 'Invoice Number',
+ '$quote.quote_number_label' => 'Quote Number',
+ '$client.postal_code_label' => 'Postal Code',
+ '$contact.first_name_label' => 'First Name',
+ '$secondary_font_url_label' => '',
+ '$contact.signature_label' => '',
+ '$product.tax_name1_label' => 'Tax',
+ '$product.tax_name2_label' => 'Tax',
+ '$product.tax_name3_label' => 'Tax',
+ '$product.unit_cost_label' => 'Unit Cost',
+ '$quote.valid_until_label' => 'Valid Until',
+ '$custom_surcharge1_label' => '',
+ '$custom_surcharge2_label' => '',
+ '$custom_surcharge3_label' => '',
+ '$custom_surcharge4_label' => '',
+ '$quote.balance_due_label' => 'Balance Due',
+ '$company.id_number_label' => 'ID Number',
+ '$invoice.po_number_label' => 'PO Number',
+ '$invoice_total_raw_label' => 'Invoice Total',
+ '$postal_city_state_label' => 'Postal/City/State',
+ '$client.vat_number_label' => 'VAT Number',
+ '$city_state_postal_label' => 'City/State/Postal',
+ '$contact.full_name_label' => 'Name',
+ '$contact.last_name_label' => 'Last Name',
+ '$company.country_2_label' => 'Country',
+ '$product.product1_label' => '',
+ '$product.product2_label' => '',
+ '$product.product3_label' => '',
+ '$product.product4_label' => '',
+ '$statement_amount_label' => 'Amount',
+ '$task.description_label' => 'Description',
+ '$product.discount_label' => 'Discount',
+ '$entity_issued_to_label' => 'Invoice issued to',
+ '$assigned_to_user_label' => 'Name',
+ '$product.quantity_label' => 'Quantity',
+ '$total_tax_labels_label' => 'Taxes',
+ '$total_tax_values_label' => 'Taxes',
+ '$invoice.discount_label' => 'Discount',
+ '$invoice.subtotal_label' => 'Subtotal',
+ '$company.address2_label' => 'Apt/Suite',
+ '$partial_due_date_label' => 'Due Date',
+ '$invoice.due_date_label' => 'Due Date',
+ '$client.id_number_label' => 'ID Number',
+ '$credit.po_number_label' => 'PO Number',
+ '$company.address1_label' => 'Street',
+ '$credit.credit_no_label' => 'Invoice Number',
+ '$invoice.datetime_label' => 'Date',
+ '$contact.custom1_label' => '',
+ '$contact.custom2_label' => '',
+ '$contact.custom3_label' => '',
+ '$contact.custom4_label' => '',
+ '$task.line_total_label' => 'Line Total',
+ '$line_tax_labels_label' => 'Taxes',
+ '$line_tax_values_label' => 'Taxes',
+ '$secondary_color_label' => '',
+ '$invoice.balance_label' => 'Balance',
+ '$invoice.custom1_label' => '',
+ '$invoice.custom2_label' => '',
+ '$invoice.custom3_label' => '',
+ '$invoice.custom4_label' => '',
+ '$company.custom1_label' => '',
+ '$company.custom2_label' => '',
+ '$company.custom3_label' => '',
+ '$company.custom4_label' => '',
+ '$quote.po_number_label' => 'PO Number',
+ '$company.website_label' => 'Website',
+ '$balance_due_raw_label' => 'Balance Due',
+ '$entity.datetime_label' => 'Date',
+ '$credit.datetime_label' => 'Date',
+ '$client.address2_label' => 'Apt/Suite',
+ '$client.address1_label' => 'Street',
+ '$user.first_name_label' => 'First Name',
+ '$created_by_user_label' => 'Name',
+ '$client.currency_label' => '',
+ '$company.country_label' => 'Country',
+ '$company.address_label' => 'Address',
+ '$tech_hero_image_label' => '',
+ '$task.tax_name1_label' => 'Tax',
+ '$task.tax_name2_label' => 'Tax',
+ '$task.tax_name3_label' => 'Tax',
+ '$client.balance_label' => 'Account balance',
+ '$client_balance_label' => 'Account balance',
+ '$credit.balance_label' => 'Balance',
+ '$credit_balance_label' => 'Credit Balance',
+ '$gross_subtotal_label' => 'Subtotal',
+ '$invoice.amount_label' => 'Total',
+ '$client.custom1_label' => '',
+ '$client.custom2_label' => '',
+ '$client.custom3_label' => '',
+ '$client.custom4_label' => '',
+ '$emailSignature_label' => '',
+ '$invoice.number_label' => 'Invoice Number',
+ '$quote.quote_no_label' => 'Quote Number',
+ '$quote.datetime_label' => 'Date',
+ '$client_address_label' => 'Address',
+ '$client.address_label' => 'Address',
+ '$payment_button_label' => 'Pay Now',
+ '$payment_qrcode_label' => 'Pay Now',
+ '$client.country_label' => 'Country',
+ '$user.last_name_label' => 'Last Name',
+ '$client.website_label' => 'Website',
+ '$dir_text_align_label' => '',
+ '$entity_images_label' => '',
+ '$task.discount_label' => 'Discount',
+ '$contact.email_label' => 'Email',
+ '$primary_color_label' => '',
+ '$credit_amount_label' => 'Credit Amount',
+ '$invoice.total_label' => 'Invoice Total',
+ '$invoice.taxes_label' => 'Taxes',
+ '$quote.custom1_label' => '',
+ '$quote.custom2_label' => '',
+ '$quote.custom3_label' => '',
+ '$quote.custom4_label' => '',
+ '$company.email_label' => 'Email',
+ '$client.number_label' => 'Number',
+ '$company.phone_label' => 'Phone',
+ '$company.state_label' => 'State/Province',
+ '$credit.number_label' => 'Credit Number',
+ '$entity_number_label' => 'Invoice Number',
+ '$credit_number_label' => 'Invoice Number',
+ '$global_margin_label' => '',
+ '$contact.phone_label' => 'Phone',
+ '$portal_button_label' => 'view_client_portal',
+ '$paymentButton_label' => 'Pay Now',
+ '$entity_footer_label' => '',
+ '$client.lang_2_label' => '',
+ '$product.date_label' => 'Date',
+ '$client.email_label' => 'Email',
+ '$product.item_label' => 'Item',
+ '$public_notes_label' => 'Public Notes',
+ '$task.service_label' => 'Service',
+ '$credit.total_label' => 'Credit Total',
+ '$net_subtotal_label' => 'Net',
+ '$paid_to_date_label' => 'Paid to Date',
+ '$quote.amount_label' => 'Quote Total',
+ '$company.city_label' => 'City',
+ '$payment.date_label' => 'Payment Date',
+ '$client.phone_label' => 'Phone',
+ '$number_short_label' => 'Invoice #',
+ '$quote.number_label' => 'Quote Number',
+ '$invoice.date_label' => 'Invoice Date',
+ '$company.name_label' => 'Company Name',
+ '$portalButton_label' => 'view_client_portal',
+ '$contact.name_label' => 'Contact Name',
+ '$entity.terms_label' => 'Invoice Terms',
+ '$client.state_label' => 'State/Province',
+ '$company.logo_label' => 'Logo',
+ '$company_logo_label' => 'Logo',
+ '$payment_link_label' => 'Pay Now',
+ '$status_logo_label' => '',
+ '$description_label' => 'Description',
+ '$product.tax_label' => 'Tax',
+ '$valid_until_label' => 'Valid Until',
+ '$your_entity_label' => 'Your Invoice',
+ '$balance_due_label' => 'Balance Due',
+ '$outstanding_label' => 'Balance Due',
+ '$partial_due_label' => 'Partial Due',
+ '$quote.total_label' => 'Total',
+ '$payment_due_label' => 'Payment due',
+ '$credit.date_label' => 'Credit Date',
+ '$invoiceDate_label' => 'Invoice Date',
+ '$view_button_label' => 'View Invoice',
+ '$client.city_label' => 'City',
+ '$spc_qr_code_label' => '',
+ '$client_name_label' => 'Client Name',
+ '$client.name_label' => 'Client Name',
+ '$paymentLink_label' => 'Pay Now',
+ '$payment_url_label' => 'Pay Now',
+ '$page_layout_label' => '',
+ '$task.task1_label' => '',
+ '$task.task2_label' => '',
+ '$task.task3_label' => '',
+ '$task.task4_label' => '',
+ '$task.hours_label' => 'Hours',
+ '$amount_due_label' => 'Amount due',
+ '$amount_raw_label' => 'Amount',
+ '$invoice_no_label' => 'Invoice Number',
+ '$quote.date_label' => 'Quote Date',
+ '$vat_number_label' => 'VAT Number',
+ '$viewButton_label' => 'View Invoice',
+ '$portal_url_label' => '',
+ '$task.date_label' => 'Date',
+ '$task.rate_label' => 'Rate',
+ '$task.cost_label' => 'Rate',
+ '$statement_label' => 'Statement',
+ '$user_iban_label' => '',
+ '$signature_label' => '',
+ '$id_number_label' => 'ID Number',
+ '$credit_no_label' => 'Invoice Number',
+ '$font_size_label' => '',
+ '$view_link_label' => 'View Invoice',
+ '$page_size_label' => '',
+ '$country_2_label' => 'Country',
+ '$firstName_label' => 'First Name',
+ '$user.name_label' => 'Name',
+ '$font_name_label' => '',
+ '$auto_bill_label' => '',
+ '$payments_label' => 'Payments',
+ '$task.tax_label' => 'Tax',
+ '$discount_label' => 'Discount',
+ '$subtotal_label' => 'Subtotal',
+ '$company1_label' => '',
+ '$company2_label' => '',
+ '$company3_label' => '',
+ '$company4_label' => '',
+ '$due_date_label' => 'Due Date',
+ '$poNumber_label' => 'PO Number',
+ '$quote_no_label' => 'Quote Number',
+ '$address2_label' => 'Apt/Suite',
+ '$address1_label' => 'Street',
+ '$viewLink_label' => 'View Invoice',
+ '$autoBill_label' => '',
+ '$view_url_label' => 'View Invoice',
+ '$font_url_label' => '',
+ '$details_label' => 'Details',
+ '$balance_label' => 'Balance',
+ '$partial_label' => 'Partial Due',
+ '$client1_label' => '',
+ '$client2_label' => '',
+ '$client3_label' => '',
+ '$client4_label' => '',
+ '$dueDate_label' => 'Due Date',
+ '$invoice_label' => 'Invoice Number',
+ '$account_label' => 'Company Name',
+ '$country_label' => 'Country',
+ '$contact_label' => 'Name',
+ '$app_url_label' => '',
+ '$website_label' => 'Website',
+ '$entity_label' => 'Invoice',
+ '$thanks_label' => 'Thanks',
+ '$amount_label' => 'Total',
+ '$method_label' => 'Method',
+ '$number_label' => 'Invoice Number',
+ '$footer_label' => '',
+ '$client_label' => 'Client Name',
+ '$email_label' => 'Email',
+ '$notes_label' => 'Public Notes',
+ '_rate1_label' => 'Tax',
+ '_rate2_label' => 'Tax',
+ '_rate3_label' => 'Tax',
+ '$taxes_label' => 'Taxes',
+ '$total_label' => 'Total',
+ '$phone_label' => 'Phone',
+ '$terms_label' => 'Invoice Terms',
+ '$from_label' => 'From',
+ '$item_label' => 'Item',
+ '$date_label' => 'Invoice Date',
+ '$tax_label' => 'Tax',
+ '$dir_label' => '',
+ '$to_label' => 'To',
+ ],
+];
+ }
+}
diff --git a/app/Services/Pdf/PdfService.php b/app/Services/Pdf/PdfService.php
new file mode 100644
index 000000000000..bb4b098dd39b
--- /dev/null
+++ b/app/Services/Pdf/PdfService.php
@@ -0,0 +1,157 @@
+invitation = $invitation;
+
+ $this->company = $invitation->company;
+
+ $this->account = $this->company->account;
+
+ $this->document_type = $document_type;
+
+ $this->options = $options;
+
+ }
+
+ /**
+ * Resolves the PDF generation type and
+ * attempts to generate a PDF from the HTML
+ * string.
+ *
+ * @return mixed | Exception
+ *
+ */
+ public function getPdf()
+ {
+ try {
+
+ $pdf = $this->resolvePdfEngine();
+
+ $numbered_pdf = $this->pageNumbering($pdf, $this->company);
+
+ if ($numbered_pdf) {
+ $pdf = $numbered_pdf;
+ }
+
+ } catch (\Exception $e) {
+ nlog(print_r($e->getMessage(), 1));
+ throw new \Exception($e->getMessage(), $e->getCode());
+ }
+
+ return $pdf;
+ }
+
+ /**
+ * Renders the dom document to HTML
+ *
+ * @return string
+ *
+ */
+ public function getHtml(): string
+ {
+ $html = $this->builder->getCompiledHTML();
+
+ if (config('ninja.log_pdf_html')) {
+ info($html);
+ }
+
+ return $html;
+ }
+
+ /**
+ * Initialize all the services to build the PDF
+ *
+ * @return self
+ */
+ public function init(): self
+ {
+
+ $this->config = (new PdfConfiguration($this))->init();
+
+
+ $this->html_variables = $this->config->client ?
+ (new HtmlEngine($this->invitation))->generateLabelsAndValues() :
+ (new VendorHtmlEngine($this->invitation))->generateLabelsAndValues();
+
+ $this->designer = (new PdfDesigner($this))->build();
+
+ $this->builder = (new PdfBuilder($this))->build();
+
+ return $this;
+
+ }
+
+ /**
+ * resolvePdfEngine
+ *
+ * @return mixed
+ */
+ private function resolvePdfEngine(): mixed
+ {
+
+ if (config('ninja.phantomjs_pdf_generation') || config('ninja.pdf_generator') == 'phantom') {
+ $pdf = (new Phantom)->convertHtmlToPdf($this->getHtml());
+ } elseif (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
+ $pdf = (new NinjaPdf())->build($this->getHtml());
+ } else {
+ $pdf = $this->makePdf(null, null, $this->getHtml());
+ }
+
+ return $pdf;
+ }
+
+}
diff --git a/app/Services/PdfMaker/Design.php b/app/Services/PdfMaker/Design.php
index b8a2da23707d..a633cfb7a902 100644
--- a/app/Services/PdfMaker/Design.php
+++ b/app/Services/PdfMaker/Design.php
@@ -175,10 +175,6 @@ class Design extends BaseDesign
$this->sharedFooterElements(),
],
],
- // 'swiss-qr' => [
- // 'id' => 'swiss-qr',
- // 'elements' => $this->swissQrCodeElement(),
- // ]
];
}
diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php
index 5bef865ce085..c3c9ad8a10ba 100644
--- a/app/Utils/Helpers.php
+++ b/app/Utils/Helpers.php
@@ -105,7 +105,7 @@ class Helpers
* Process reserved keywords on PDF.
*
* @param string $value
- * @param Client|Company $entity
+ * @param Client|Company|Vendor $entity
* @param null|Carbon $currentDateTime
* @return null|string
*/
diff --git a/database/factories/VendorContactFactory.php b/database/factories/VendorContactFactory.php
index 03b8556e8b74..9f26356f2213 100644
--- a/database/factories/VendorContactFactory.php
+++ b/database/factories/VendorContactFactory.php
@@ -26,7 +26,12 @@ class VendorContactFactory extends Factory
'first_name' => $this->faker->firstName(),
'last_name' => $this->faker->lastName(),
'phone' => $this->faker->phoneNumber(),
+ 'email_verified_at' => now(),
'email' => $this->faker->unique()->safeEmail(),
+ 'send_email' => true,
+ 'password' => bcrypt('password'),
+ 'remember_token' => \Illuminate\Support\Str::random(10),
+ 'contact_key' => \Illuminate\Support\Str::random(32),
];
}
}
diff --git a/tests/Pdf/PdfGenerationTest.php b/tests/Pdf/PdfGenerationTest.php
index 66d8e77cd5c2..22fcacf01673 100644
--- a/tests/Pdf/PdfGenerationTest.php
+++ b/tests/Pdf/PdfGenerationTest.php
@@ -17,7 +17,7 @@ use Tests\TestCase;
/**
* @test
- //@covers App\DataMapper\BaseSettings
+ * @covers App\DataMapper\BaseSettings
*/
class PdfGenerationTest extends TestCase
{
diff --git a/tests/Pdf/PdfServiceTest.php b/tests/Pdf/PdfServiceTest.php
new file mode 100644
index 000000000000..d892bcc4c779
--- /dev/null
+++ b/tests/Pdf/PdfServiceTest.php
@@ -0,0 +1,111 @@
+makeTestData();
+ }
+
+ public function testPdfGeneration()
+ {
+
+ $invitation = $this->invoice->invitations->first();
+
+ $service = new PdfService($invitation);
+
+ $this->assertNotNull($service->getPdf());
+
+ }
+
+ public function testHtmlGeneration()
+ {
+
+ $invitation = $this->invoice->invitations->first();
+
+ $service = new PdfService($invitation);
+
+ $this->assertIsString($service->getHtml());
+
+ }
+
+ public function testInitOfClass()
+ {
+
+ $invitation = $this->invoice->invitations->first();
+
+ $service = new PdfService($invitation);
+
+ $this->assertInstanceOf(PdfService::class, $service);
+
+ }
+
+ public function testEntityResolution()
+ {
+
+ $invitation = $this->invoice->invitations->first();
+
+ $service = new PdfService($invitation);
+
+ $this->assertInstanceOf(PdfConfiguration::class, $service->config);
+
+
+ }
+
+ public function testDefaultDesign()
+ {
+ $invitation = $this->invoice->invitations->first();
+
+ $service = new PdfService($invitation);
+
+ $this->assertEquals(2, $service->config->design->id);
+
+ }
+
+ public function testHtmlIsArray()
+ {
+ $invitation = $this->invoice->invitations->first();
+
+ $service = new PdfService($invitation);
+
+ $this->assertIsArray($service->html_variables);
+
+ }
+
+ public function testTemplateResolution()
+ {
+ $invitation = $this->invoice->invitations->first();
+
+ $service = new PdfService($invitation);
+
+ $this->assertIsString($service->designer->template);
+
+ }
+
+}
\ No newline at end of file
diff --git a/tests/Pdf/PdfmockTest.php b/tests/Pdf/PdfmockTest.php
new file mode 100644
index 000000000000..ee66688657f0
--- /dev/null
+++ b/tests/Pdf/PdfmockTest.php
@@ -0,0 +1,99 @@
+build();
+
+ $this->assertInstanceOf(Invoice::class, $entity);
+ $this->assertNotNull($entity->client);
+
+
+ $pdf_service = new PdfService($entity->invitation);
+
+ $this->assertNotNull($pdf_service);
+
+ $pdf_config = (new PdfConfiguration($pdf_service));
+
+ $this->assertNotNull($pdf_config);
+
+
+ }
+
+ public function testHtmlGeneration()
+ {
+ $pdf_mock = (new PdfMock());
+ $mock = $pdf_mock->build();
+
+ $pdf_service = new PdfService($mock->invitation);
+
+ $pdf_config = (new PdfConfiguration($pdf_service));
+ $pdf_config->entity = $mock;
+ $pdf_config->setTaxMap($mock->tax_map);
+ $pdf_config->setTotalTaxMap($mock->total_tax_map);
+ $pdf_config->setCurrency(Currency::find(1));
+ $pdf_config->setCountry(Country::find(840));
+ $pdf_config->client = $mock->client;
+ $pdf_config->entity_design_id = 'invoice_design_id';
+ $pdf_config->settings_object = $mock->client;
+ $pdf_config->entity_string = 'invoice';
+ $pdf_config->settings = (object)$pdf_config->service->company->settings;
+ $pdf_config->setPdfVariables();
+ $pdf_config->design = Design::find(2);
+ $pdf_config->currency_entity = $mock->client;
+
+ $pdf_service->config = $pdf_config;
+
+ $pdf_designer = (new \App\Services\Pdf\PdfDesigner($pdf_service))->build();
+ $pdf_service->designer = $pdf_designer;
+
+ $pdf_service->html_variables = $pdf_mock->getStubVariables();
+
+ $pdf_builder = (new PdfBuilder($pdf_service))->build();
+ $pdf_service->builder = $pdf_builder;
+ $this->assertNotNull($pdf_config);
+
+ $html = $pdf_service->getHtml();
+
+ nlog($html);
+ $this->assertNotNull($html);
+ }
+
+}