Cleanup for parsing field stacks

This commit is contained in:
David Bomba 2023-11-06 16:22:45 +11:00
parent 5eb58804e4
commit fe61da24ff

View File

@ -13,6 +13,7 @@ namespace App\Services\Template;
use App\Models\Quote; use App\Models\Quote;
use App\Utils\Number; use App\Utils\Number;
use Twig\Error\Error;
use App\Models\Client; use App\Models\Client;
use App\Models\Credit; use App\Models\Credit;
use App\Models\Design; use App\Models\Design;
@ -23,8 +24,12 @@ use App\Models\Payment;
use App\Models\Project; use App\Models\Project;
use App\Utils\HtmlEngine; use App\Utils\HtmlEngine;
use League\Fractal\Manager; use League\Fractal\Manager;
use Twig\Error\LoaderError;
use Twig\Error\SyntaxError;
use Twig\Error\RuntimeError;
use App\Models\PurchaseOrder; use App\Models\PurchaseOrder;
use App\Utils\VendorHtmlEngine; use App\Utils\VendorHtmlEngine;
use Twig\Sandbox\SecurityError;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
use App\Utils\PaymentHtmlEngine; use App\Utils\PaymentHtmlEngine;
use App\Utils\Traits\MakesDates; use App\Utils\Traits\MakesDates;
@ -40,7 +45,8 @@ use League\Fractal\Serializer\ArraySerializer;
class TemplateService class TemplateService
{ {
use MakesDates, PdfMaker; use MakesDates;
use PdfMaker;
private \DomDocument $document; private \DomDocument $document;
@ -78,7 +84,6 @@ class TemplateService
private function init(): self private function init(): self
{ {
$this->commonmark = new CommonMarkConverter([ $this->commonmark = new CommonMarkConverter([
'allow_unsafe_links' => false, 'allow_unsafe_links' => false,
]); ]);
@ -96,7 +101,7 @@ class TemplateService
$this->twig->addExtension(new \Twig\Extension\DebugExtension()); $this->twig->addExtension(new \Twig\Extension\DebugExtension());
$function = new \Twig\TwigFunction('img', function ($string, $style = '') { $function = new \Twig\TwigFunction('img', function ($string, $style = '') {
return '<img src="'.$string.'" style="'.$style.'"></img>'; return '<img src="' . $string . '" style="' . $style . '"></img>';
}); });
$this->twig->addFunction($function); $this->twig->addFunction($function);
@ -111,7 +116,7 @@ class TemplateService
/** /**
* Iterate through all of the * Iterate through all of the
* ninja nodes * ninja nodes, and field stacks
* *
* @param array $data - the payload to be passed into the template * @param array $data - the payload to be passed into the template
* @return self * @return self
@ -128,6 +133,12 @@ class TemplateService
return $this; return $this;
} }
/**
* Initialized a set of HTMLEngine variables
*
* @param array | Collection $data
* @return self
*/
private function processVariables($data): self private function processVariables($data): self
{ {
$this->variables = $this->resolveHtmlEngine($data); $this->variables = $this->resolveHtmlEngine($data);
@ -135,6 +146,11 @@ class TemplateService
return $this; return $this;
} }
/**
* Returns a Mock Template
*
* @return self
*/
public function mock(): self public function mock(): self
{ {
$tm = new TemplateMock($this->company); $tm = new TemplateMock($this->company);
@ -165,9 +181,9 @@ class TemplateService
/** /**
* Returns the PDF string * Returns the PDF string
* *
* @return mixed * @return string
*/ */
public function getPdf(): mixed public function getPdf(): string
{ {
if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') { if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
@ -193,7 +209,7 @@ class TemplateService
/** /**
* Process data variables * Process data variables
* *
* @param mixed $data * @param array | Collection $data
* @return self * @return self
*/ */
public function processData($data): self public function processData($data): self
@ -221,19 +237,19 @@ class TemplateService
try { try {
$template = $this->twig->createTemplate(html_entity_decode($template)); $template = $this->twig->createTemplate(html_entity_decode($template));
} catch(\Twig\Error\SyntaxError $e) { } catch(SyntaxError $e) {
nlog($e->getMessage()); nlog($e->getMessage());
throw ($e); throw ($e);
} catch(\Twig\Error\Error $e) { } catch(Error $e) {
nlog("error = " .$e->getMessage()); nlog("error = " . $e->getMessage());
throw ($e); throw ($e);
} catch(\Twig\Error\RuntimeError $e) { } catch(RuntimeError $e) {
nlog("runtime = " .$e->getMessage()); nlog("runtime = " . $e->getMessage());
throw ($e); throw ($e);
} catch(\Twig\Error\LoaderError $e) { } catch(LoaderError $e) {
nlog("loader = " . $e->getMessage()); nlog("loader = " . $e->getMessage());
throw ($e); throw ($e);
} catch(\Twig\Error\SecurityError $e) { } catch(SecurityError $e) {
nlog("security = " . $e->getMessage()); nlog("security = " . $e->getMessage());
throw ($e); throw ($e);
} }
@ -340,6 +356,7 @@ class TemplateService
* Resolves the labels and values needed to replace the string * Resolves the labels and values needed to replace the string
* holders in the template. * holders in the template.
* *
* @param array $data
* @return array * @return array
*/ */
private function resolveHtmlEngine(array $data): array private function resolveHtmlEngine(array $data): array
@ -371,6 +388,13 @@ class TemplateService
} }
/**
* Pre Processes the Data Blocks into
* Twig consumables
*
* @param array | Collection $data
* @return array
*/
private function preProcessDataBlocks($data): array private function preProcessDataBlocks($data): array
{ {
return collect($data)->map(function ($value, $key) { return collect($data)->map(function ($value, $key) {
@ -394,6 +418,12 @@ class TemplateService
})->toArray(); })->toArray();
} }
/**
* Process Invoices into consumable form for Twig templates
*
* @param array | Collection $invoices
* @return array
*/
public function processInvoices($invoices): array public function processInvoices($invoices): array
{ {
$invoices = collect($invoices) $invoices = collect($invoices)
@ -448,7 +478,7 @@ class TemplateService
'custom_surcharge_tax2' => (bool) $invoice->custom_surcharge_tax2, 'custom_surcharge_tax2' => (bool) $invoice->custom_surcharge_tax2,
'custom_surcharge_tax3' => (bool) $invoice->custom_surcharge_tax3, 'custom_surcharge_tax3' => (bool) $invoice->custom_surcharge_tax3,
'custom_surcharge_tax4' => (bool) $invoice->custom_surcharge_tax4, 'custom_surcharge_tax4' => (bool) $invoice->custom_surcharge_tax4,
'line_items' => $invoice->line_items ? $this->padLineItems($invoice->line_items, $invoice->client): (array) [], 'line_items' => $invoice->line_items ? $this->padLineItems($invoice->line_items, $invoice->client) : (array) [],
'reminder1_sent' => $this->translateDate($invoice->reminder1_sent, $invoice->client->date_format(), $invoice->client->locale()), 'reminder1_sent' => $this->translateDate($invoice->reminder1_sent, $invoice->client->date_format(), $invoice->client->locale()),
'reminder2_sent' => $this->translateDate($invoice->reminder2_sent, $invoice->client->date_format(), $invoice->client->locale()), 'reminder2_sent' => $this->translateDate($invoice->reminder2_sent, $invoice->client->date_format(), $invoice->client->locale()),
'reminder3_sent' => $this->translateDate($invoice->reminder3_sent, $invoice->client->date_format(), $invoice->client->locale()), 'reminder3_sent' => $this->translateDate($invoice->reminder3_sent, $invoice->client->date_format(), $invoice->client->locale()),
@ -472,6 +502,13 @@ class TemplateService
} }
/**
* Pads Line Items with raw and formatted content
*
* @param array $items
* @param mixed $client
* @return array
*/
public function padLineItems(array $items, Client $client): array public function padLineItems(array $items, Client $client): array
{ {
return collect($items)->map(function ($item) use ($client) { return collect($items)->map(function ($item) use ($client) {
@ -499,6 +536,12 @@ class TemplateService
})->toArray(); })->toArray();
} }
/**
* Transforms a Payment into consumable for twig
*
* @param Payment $payment
* @return array
*/
private function transformPayment(Payment $payment): array private function transformPayment(Payment $payment): array
{ {
@ -625,6 +668,12 @@ class TemplateService
} }
/**
* @todo refactor
*
* @param mixed $quotes
* @return array
*/
public function processQuotes($quotes): array public function processQuotes($quotes): array
{ {
$it = new QuoteTransformer(); $it = new QuoteTransformer();
@ -649,7 +698,7 @@ class TemplateService
* Pushes credits through the appropriate transformer * Pushes credits through the appropriate transformer
* and builds any required relationships * and builds any required relationships
* *
* @param mixed $credits * @param array | Collection $credits
* @return array * @return array
*/ */
public function processCredits($credits): array public function processCredits($credits): array
@ -699,7 +748,7 @@ class TemplateService
'custom_surcharge_tax2' => (bool) $credit->custom_surcharge_tax2, 'custom_surcharge_tax2' => (bool) $credit->custom_surcharge_tax2,
'custom_surcharge_tax3' => (bool) $credit->custom_surcharge_tax3, 'custom_surcharge_tax3' => (bool) $credit->custom_surcharge_tax3,
'custom_surcharge_tax4' => (bool) $credit->custom_surcharge_tax4, 'custom_surcharge_tax4' => (bool) $credit->custom_surcharge_tax4,
'line_items' => $credit->line_items ? $this->padLineItems($credit->line_items, $credit->client): (array) [], 'line_items' => $credit->line_items ? $this->padLineItems($credit->line_items, $credit->client) : (array) [],
'reminder1_sent' => $this->translateDate($credit->reminder1_sent, $credit->client->date_format(), $credit->client->locale()), 'reminder1_sent' => $this->translateDate($credit->reminder1_sent, $credit->client->date_format(), $credit->client->locale()),
'reminder2_sent' => $this->translateDate($credit->reminder2_sent, $credit->client->date_format(), $credit->client->locale()), 'reminder2_sent' => $this->translateDate($credit->reminder2_sent, $credit->client->date_format(), $credit->client->locale()),
'reminder3_sent' => $this->translateDate($credit->reminder3_sent, $credit->client->date_format(), $credit->client->locale()), 'reminder3_sent' => $this->translateDate($credit->reminder3_sent, $credit->client->date_format(), $credit->client->locale()),
@ -723,12 +772,10 @@ class TemplateService
} }
/** /**
* Pushes payments through the appropriate transformer * Pushes payments through the appropriate transformer
* *
* @param mixed $payments * @param array | Collection $payments
* @return array * @return array
*/ */
public function processPayments($payments): array public function processPayments($payments): array
@ -740,9 +787,14 @@ class TemplateService
return $payments; return $payments;
} }
/**
* @todo refactor
*
* @param mixed $tasks
* @return array
*/
public function processTasks($tasks): array public function processTasks($tasks): array
{ {
$it = new TaskTransformer(); $it = new TaskTransformer();
@ -765,6 +817,12 @@ class TemplateService
} }
/**
* @todo refactor
*
* @param mixed $projects
* @return array
*/
public function processProjects($projects): array public function processProjects($projects): array
{ {
@ -778,6 +836,12 @@ class TemplateService
} }
/**
* @todo refactor
*
* @param mixed $purchase_orders
* @return array
*/
public function processPurchaseOrders($purchase_orders): array public function processPurchaseOrders($purchase_orders): array
{ {
@ -791,6 +855,12 @@ class TemplateService
} }
/**
* Set Company
*
* @param mixed $company
* @return self
*/
public function setCompany(Company $company): self public function setCompany(Company $company): self
{ {
$this->company = $company; $this->company = $company;
@ -798,11 +868,23 @@ class TemplateService
return $this; return $this;
} }
/**
* Get Company
*
* @return Company
*/
public function getCompany(): Company public function getCompany(): Company
{ {
return $this->company; return $this->company;
} }
/**
* Setter that allows external variables to override the
* resolved ones from this class
*
* @param mixed $variables
* @return self
*/
public function overrideVariables($variables): self public function overrideVariables($variables): self
{ {
$this->variables = $variables; $this->variables = $variables;
@ -811,7 +893,7 @@ class TemplateService
} }
/** /**
* Parses and finds any stacks to replace * Parses and finds any field stacks to inject into the DOM Document
* *
* @return self * @return self
*/ */
@ -829,7 +911,7 @@ class TemplateService
collect($stacks)->filter(function ($stack) { collect($stacks)->filter(function ($stack) {
$exists = $this->document->getElementById($stack) ?? false; $exists = $this->document->getElementById($stack) ?? false;
return $exists ? ['stack' => $stack, 'labels' => $exists->getAttribute('labels')] : false; return $exists ? ['stack' => $stack, 'labels' => $exists->getAttribute('labels')] : false;
})->each(function ($stack){ })->each(function ($stack) {
$this->parseStack($stack); $this->parseStack($stack);
}); });
@ -846,7 +928,7 @@ class TemplateService
private function parseStack(string $stack): self private function parseStack(string $stack): self
{ {
match($stack['stack']){ match($stack['stack']) {
'entity-details' => $this->entityDetails($stack['labels'] == 'true'), 'entity-details' => $this->entityDetails($stack['labels'] == 'true'),
'client-details' => $this->clientDetails($stack['labels'] == 'true'), 'client-details' => $this->clientDetails($stack['labels'] == 'true'),
'vendor-details' => $this->vendorDetails($stack['labels'] == 'true'), 'vendor-details' => $this->vendorDetails($stack['labels'] == 'true'),
@ -860,17 +942,25 @@ class TemplateService
return $this; return $this;
} }
/**
* Inject the Company Details into the DOM Document
*
* @param bool $include_labels
* @return self
*/
private function companyDetails(bool $include_labels): self private function companyDetails(bool $include_labels): self
{ {
$var_set = $this->getVarSet(); $var_set = $this->getVarSet();
$company_details = $company_details =
collect($this->company->settings->pdf_variables->company_details) collect($this->company->settings->pdf_variables->company_details)
->filter(function ($variable) use($var_set) { ->filter(function ($variable) use ($var_set) {
return isset($var_set['values'][$variable]) && !empty($var_set['values'][$variable]); return isset($var_set['values'][$variable]) && !empty($var_set['values'][$variable]);
}) })
->map(function ($variable) { ->when(!$include_labels, function ($collection) {
return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]]; return $collection->map(function ($variable) {
return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_details-' . substr($variable, 1)]];
});
})->toArray(); })->toArray();
$company_details = $include_labels ? $this->labelledFieldStack($company_details) : $company_details; $company_details = $include_labels ? $this->labelledFieldStack($company_details) : $company_details;
@ -880,7 +970,7 @@ class TemplateService
return $this; return $this;
} }
private function companyAddress(): self private function companyAddress(bool $include_labels = false): self
{ {
$var_set = $this->getVarSet(); $var_set = $this->getVarSet();
@ -890,19 +980,30 @@ class TemplateService
->filter(function ($variable) use ($var_set) { ->filter(function ($variable) use ($var_set) {
return isset($var_set['values'][$variable]) && !empty($var_set['values'][$variable]); return isset($var_set['values'][$variable]) && !empty($var_set['values'][$variable]);
}) })
->map(function ($variable) { ->when(!$include_labels, function ($collection) {
return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_address-' . substr($variable, 1)]]; return $collection->map(function ($variable) {
return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'company_address-' . substr($variable, 1)]];
});
})->toArray(); })->toArray();
$company_address = $include_labels ? $this->labelledFieldStack($company_address) : $company_address;
$this->updateElementProperties('company-address', $company_address); $this->updateElementProperties('company-address', $company_address);
return $this; return $this;
} }
private function shippingDetails(): self /**
* Injects the Shipping Details into the DOM Document
*
* @param bool $include_labels
* @return self
*/
private function shippingDetails(bool $include_labels = false): self
{ {
if(!$this->entity->client) if(!$this->entity->client) {
return $this; return $this;
}
$this->client = $this->entity->client; $this->client = $this->entity->client;
@ -920,7 +1021,7 @@ class TemplateService
]; ];
$shipping_address = $shipping_address =
collect($shipping_address)->filter(function ($address){ collect($shipping_address)->filter(function ($address) {
return isset($address['content']) && !empty($address['content']); return isset($address['content']) && !empty($address['content']);
})->toArray(); })->toArray();
@ -929,7 +1030,13 @@ class TemplateService
return $this; return $this;
} }
private function clientDetails(): self /**
* Injects the Client Details into the DOM Document
*
* @param bool $include_labels
* @return self
*/
private function clientDetails(bool $include_labels = false): self
{ {
$var_set = $this->getVarSet(); $var_set = $this->getVarSet();
@ -938,21 +1045,31 @@ class TemplateService
->filter(function ($variable) use ($var_set) { ->filter(function ($variable) use ($var_set) {
return isset($var_set['values'][$variable]) && !empty($var_set['values'][$variable]); return isset($var_set['values'][$variable]) && !empty($var_set['values'][$variable]);
}) })
->map(function ($variable) { ->when(!$include_labels, function ($collection) {
return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]]; return $collection->map(function ($variable) {
return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'client_details-' . substr($variable, 1)]];
});
})->toArray(); })->toArray();
$client_details = $include_labels ? $this->labelledFieldStack($client_details) : $client_details;
$this->updateElementProperties('client-details', $client_details); $this->updateElementProperties('client-details', $client_details);
return $this; return $this;
} }
/**
* Resolves the entity.
*
* Only required for resolving the entity-details stack
*
* @return string
*/
private function resolveEntity(): string private function resolveEntity(): string
{ {
$entity_string = ''; $entity_string = '';
match($this->entity){ match($this->entity) {
($this->entity instanceof Invoice) => $entity_string = 'invoice', ($this->entity instanceof Invoice) => $entity_string = 'invoice',
($this->entity instanceof Quote) => $entity_string = 'quote', ($this->entity instanceof Quote) => $entity_string = 'quote',
($this->entity instanceof Credit) => $entity_string = 'credit', ($this->entity instanceof Credit) => $entity_string = 'credit',
@ -1031,7 +1148,13 @@ class TemplateService
} }
private function vendorDetails(): self /**
* Inject Vendor Details into DOM Document
*
* @param bool $include_labels
* @return self
*/
private function vendorDetails(bool $include_labels = false): self
{ {
$var_set = $this->getVarSet(); $var_set = $this->getVarSet();
@ -1040,11 +1163,13 @@ class TemplateService
collect($this->company->settings->pdf_variables->vendor_details) collect($this->company->settings->pdf_variables->vendor_details)
->filter(function ($variable) use ($var_set) { ->filter(function ($variable) use ($var_set) {
return isset($var_set['values'][$variable]) && !empty($var_set['values'][$variable]); return isset($var_set['values'][$variable]) && !empty($var_set['values'][$variable]);
}) })->when(!$include_labels, function ($collection) {
->map(function ($variable) { return $collection->map(function ($variable) {
return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'vendor_details-' . substr($variable, 1)]]; return ['element' => 'p', 'content' => $variable, 'show_empty' => false, 'properties' => ['data-ref' => 'vendor_details-' . substr($variable, 1)]];
});
})->toArray(); })->toArray();
$vendor_details = $include_labels ? $this->labelledFieldStack($vendor_details) : $vendor_details;
$this->updateElementProperties('vendor-details', $vendor_details); $this->updateElementProperties('vendor-details', $vendor_details);
@ -1119,7 +1244,7 @@ class TemplateService
} }
public function createElementContent($element, $children) :self public function createElementContent($element, $children): self
{ {
foreach ($children as $child) { foreach ($children as $child) {