mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-08 06:14:31 -04:00
Pdf Service Refactor
This commit is contained in:
parent
2fe91b5707
commit
ca853d29e5
@ -52,7 +52,11 @@ class PdfBuilder
|
|||||||
{
|
{
|
||||||
|
|
||||||
$this->getTemplate()
|
$this->getTemplate()
|
||||||
->buildSections()
|
->buildSections();
|
||||||
|
|
||||||
|
nlog($this->sections);
|
||||||
|
|
||||||
|
$this
|
||||||
->getEmptyElements()
|
->getEmptyElements()
|
||||||
->updateElementProperties()
|
->updateElementProperties()
|
||||||
->updateVariables();
|
->updateVariables();
|
||||||
@ -85,9 +89,11 @@ class PdfBuilder
|
|||||||
$document = new DOMDocument();
|
$document = new DOMDocument();
|
||||||
|
|
||||||
$document->validateOnParse = true;
|
$document->validateOnParse = true;
|
||||||
@$document->loadHTML(mb_convert_encoding($this->service->config->designer->template, 'HTML-ENTITIES', 'UTF-8'));
|
|
||||||
|
@$document->loadHTML(mb_convert_encoding($this->service->designer->template, 'HTML-ENTITIES', 'UTF-8'));
|
||||||
|
|
||||||
$this->document = $document;
|
$this->document = $document;
|
||||||
|
|
||||||
$this->xpath = new DOMXPath($document);
|
$this->xpath = new DOMXPath($document);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
@ -112,6 +118,13 @@ class PdfBuilder
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function mergeSections(array $section) :self
|
||||||
|
{
|
||||||
|
$this->sections = array_merge($this->sections, $section);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates delivery note sections
|
* Generates delivery note sections
|
||||||
*
|
*
|
||||||
@ -124,7 +137,7 @@ class PdfBuilder
|
|||||||
$this->genericSectionBuilder()
|
$this->genericSectionBuilder()
|
||||||
->getProductTotals();
|
->getProductTotals();
|
||||||
|
|
||||||
$this->sections[] = [
|
$this->mergeSections([
|
||||||
'client-details' => [
|
'client-details' => [
|
||||||
'id' => 'client-details',
|
'id' => 'client-details',
|
||||||
'elements' => $this->clientDeliveryDetails(),
|
'elements' => $this->clientDeliveryDetails(),
|
||||||
@ -137,7 +150,7 @@ class PdfBuilder
|
|||||||
'id' => 'entity-details',
|
'id' => 'entity-details',
|
||||||
'elements' => $this->deliveryNoteDetails(),
|
'elements' => $this->deliveryNoteDetails(),
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
||||||
@ -154,7 +167,7 @@ class PdfBuilder
|
|||||||
|
|
||||||
$this->genericSectionBuilder();
|
$this->genericSectionBuilder();
|
||||||
|
|
||||||
$this->sections[] = [
|
$this->mergeSections( [
|
||||||
'statement-invoice-table' => [
|
'statement-invoice-table' => [
|
||||||
'id' => 'statement-invoice-table',
|
'id' => 'statement-invoice-table',
|
||||||
'elements' => $this->statementInvoiceTable(),
|
'elements' => $this->statementInvoiceTable(),
|
||||||
@ -179,7 +192,7 @@ class PdfBuilder
|
|||||||
'id' => 'table-totals',
|
'id' => 'table-totals',
|
||||||
'elements' => $this->statementTableTotals(),
|
'elements' => $this->statementTableTotals(),
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
||||||
@ -308,7 +321,7 @@ class PdfBuilder
|
|||||||
$this->genericSectionBuilder()
|
$this->genericSectionBuilder()
|
||||||
->getProductTotals();
|
->getProductTotals();
|
||||||
|
|
||||||
$this->sections[] = [
|
$this->mergeSections([
|
||||||
'vendor-details' => [
|
'vendor-details' => [
|
||||||
'id' => 'vendor-details',
|
'id' => 'vendor-details',
|
||||||
'elements' => $this->vendorDetails(),
|
'elements' => $this->vendorDetails(),
|
||||||
@ -317,7 +330,7 @@ class PdfBuilder
|
|||||||
'id' => 'entity-details',
|
'id' => 'entity-details',
|
||||||
'elements' => $this->purchaseOrderDetails(),
|
'elements' => $this->purchaseOrderDetails(),
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
|
||||||
@ -333,7 +346,7 @@ class PdfBuilder
|
|||||||
private function genericSectionBuilder(): self
|
private function genericSectionBuilder(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->sections[] = [
|
$this->mergeSections([
|
||||||
'company-details' => [
|
'company-details' => [
|
||||||
'id' => 'company-details',
|
'id' => 'company-details',
|
||||||
'elements' => $this->companyDetails(),
|
'elements' => $this->companyDetails(),
|
||||||
@ -348,7 +361,7 @@ class PdfBuilder
|
|||||||
$this->sharedFooterElements(),
|
$this->sharedFooterElements(),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -394,7 +407,7 @@ class PdfBuilder
|
|||||||
{
|
{
|
||||||
$elements = [];
|
$elements = [];
|
||||||
|
|
||||||
$items = $this->transformLineItems($this->entity->line_items, $type);
|
$items = $this->transformLineItems($this->service->config->entity->line_items, $type);
|
||||||
|
|
||||||
$this->processNewLines($items);
|
$this->processNewLines($items);
|
||||||
|
|
||||||
@ -436,12 +449,12 @@ class PdfBuilder
|
|||||||
$element = ['element' => 'tr', 'elements' => []];
|
$element = ['element' => 'tr', 'elements' => []];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
array_key_exists($type, $this->context) &&
|
array_key_exists($type, $this->service->options) &&
|
||||||
!empty($this->context[$type]) &&
|
!empty($this->service->options[$type]) &&
|
||||||
!is_null($this->context[$type])
|
!is_null($this->service->options[$type])
|
||||||
) {
|
) {
|
||||||
$document = new DOMDocument();
|
$document = new DOMDocument();
|
||||||
$document->loadHTML($this->context[$type], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
$document->loadHTML($this->service->options[$type], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||||
|
|
||||||
$td = $document->getElementsByTagName('tr')->item(0);
|
$td = $document->getElementsByTagName('tr')->item(0);
|
||||||
|
|
||||||
@ -751,12 +764,12 @@ class PdfBuilder
|
|||||||
private function getProductTotals(): self
|
private function getProductTotals(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->sections[] = [
|
$this->mergeSections([
|
||||||
'table-totals' => [
|
'table-totals' => [
|
||||||
'id' => 'table-totals',
|
'id' => 'table-totals',
|
||||||
'elements' => $this->getTableTotals(),
|
'elements' => $this->getTableTotals(),
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -776,33 +789,33 @@ class PdfBuilder
|
|||||||
|
|
||||||
if($this->service->config->entity_string == 'invoice')
|
if($this->service->config->entity_string == 'invoice')
|
||||||
{
|
{
|
||||||
$this->sections[] = [
|
$this->mergeSections( [
|
||||||
'entity-details' => [
|
'entity-details' => [
|
||||||
'id' => 'entity-details',
|
'id' => 'entity-details',
|
||||||
'elements' => $this->invoiceDetails(),
|
'elements' => $this->invoiceDetails(),
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
elseif($this->service->config->entity_string == 'quote')
|
elseif($this->service->config->entity_string == 'quote')
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->sections[] = [
|
$this->mergeSections( [
|
||||||
'entity-details' => [
|
'entity-details' => [
|
||||||
'id' => 'entity-details',
|
'id' => 'entity-details',
|
||||||
'elements' => $this->quoteDetails(),
|
'elements' => $this->quoteDetails(),
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
elseif($this->service->config->entity_string == 'credit')
|
elseif($this->service->config->entity_string == 'credit')
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->sections[] = [
|
$this->mergeSections( [
|
||||||
'entity-details' => [
|
'entity-details' => [
|
||||||
'id' => 'entity-details',
|
'id' => 'entity-details',
|
||||||
'elements' => $this->creditDetails(),
|
'elements' => $this->creditDetails(),
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -820,8 +833,8 @@ class PdfBuilder
|
|||||||
private function buildSections() :self
|
private function buildSections() :self
|
||||||
{
|
{
|
||||||
|
|
||||||
return match ($this->service->config->document_type) {
|
return match ($this->service->document_type) {
|
||||||
PdfService::PRODUCT => $this->getProductSections,
|
PdfService::PRODUCT => $this->getProductSections(),
|
||||||
PdfService::DELIVERY_NOTE => $this->getDeliveryNoteSections(),
|
PdfService::DELIVERY_NOTE => $this->getDeliveryNoteSections(),
|
||||||
PdfService::STATEMENT => $this->getStatementSections(),
|
PdfService::STATEMENT => $this->getStatementSections(),
|
||||||
PdfService::PURCHASE_ORDER => $this->getPurchaseOrderSections(),
|
PdfService::PURCHASE_ORDER => $this->getPurchaseOrderSections(),
|
||||||
@ -919,7 +932,7 @@ class PdfBuilder
|
|||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
if ($this->service->config->document_type == PdfService::DELIVERY_NOTE) {
|
if ($this->service->document_type == PdfService::DELIVERY_NOTE) {
|
||||||
return $elements;
|
return $elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1028,7 +1041,7 @@ class PdfBuilder
|
|||||||
public function getProductAndTaskTables(): self
|
public function getProductAndTaskTables(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->sections[] = [
|
$this->mergeSections( [
|
||||||
'product-table' => [
|
'product-table' => [
|
||||||
'id' => 'product-table',
|
'id' => 'product-table',
|
||||||
'elements' => $this->productTable(),
|
'elements' => $this->productTable(),
|
||||||
@ -1037,7 +1050,7 @@ class PdfBuilder
|
|||||||
'id' => 'task-table',
|
'id' => 'task-table',
|
||||||
'elements' => $this->taskTable(),
|
'elements' => $this->taskTable(),
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -1050,12 +1063,12 @@ class PdfBuilder
|
|||||||
*/
|
*/
|
||||||
public function getClientDetails(): self
|
public function getClientDetails(): self
|
||||||
{
|
{
|
||||||
$this->sections[] = [
|
$this->mergeSections( [
|
||||||
'client-details' => [
|
'client-details' => [
|
||||||
'id' => 'client-details',
|
'id' => 'client-details',
|
||||||
'elements' => $this->clientDetails(),
|
'elements' => $this->clientDetails(),
|
||||||
],
|
],
|
||||||
];
|
]);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -1317,7 +1330,7 @@ class PdfBuilder
|
|||||||
['element' => 'th', 'content' => '$product.quantity_label', 'properties' => ['data-ref' => 'delivery_note-product.quantity_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->config->document_type);
|
$items = $this->transformLineItems($this->service->config->entity->line_items, $this->service->document_type);
|
||||||
|
|
||||||
$this->processNewLines($items);
|
$this->processNewLines($items);
|
||||||
|
|
||||||
@ -1422,9 +1435,9 @@ class PdfBuilder
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
// Dom Traversal
|
// Dom Traversal
|
||||||
///////////////
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
public function getSectionNode(string $selector)
|
public function getSectionNode(string $selector)
|
||||||
@ -1482,7 +1495,7 @@ class PdfBuilder
|
|||||||
$contains_html = false;
|
$contains_html = false;
|
||||||
|
|
||||||
if ($child['element'] !== 'script') {
|
if ($child['element'] !== 'script') {
|
||||||
if (array_key_exists('process_markdown', $this->data) && array_key_exists('content', $child) && $this->data['process_markdown']) {
|
if (array_key_exists('process_markdown', $this->service->options) && array_key_exists('content', $child) && $this->service->options['process_markdown']) {
|
||||||
$child['content'] = str_replace('<br>', "\r", $child['content']);
|
$child['content'] = str_replace('<br>', "\r", $child['content']);
|
||||||
$child['content'] = $this->commonmark->convert($child['content'] ?? '');
|
$child['content'] = $this->commonmark->convert($child['content'] ?? '');
|
||||||
}
|
}
|
||||||
@ -1530,9 +1543,9 @@ class PdfBuilder
|
|||||||
|
|
||||||
public function updateVariables()
|
public function updateVariables()
|
||||||
{
|
{
|
||||||
$html = strtr($this->getCompiledHTML(), $this->service->config->html_variables['labels']);
|
$html = strtr($this->getCompiledHTML(), $this->service->html_variables['labels']);
|
||||||
|
|
||||||
$html = strtr($html, $this->service->config->html_variables['values']);
|
$html = strtr($html, $this->service->html_variables['values']);
|
||||||
|
|
||||||
@$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
|
@$this->document->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
|
||||||
|
|
||||||
@ -1562,7 +1575,7 @@ class PdfBuilder
|
|||||||
{
|
{
|
||||||
foreach ($this->sections as $element) {
|
foreach ($this->sections as $element) {
|
||||||
if (isset($element['elements'])) {
|
if (isset($element['elements'])) {
|
||||||
$this->getEmptyChildrens($element['elements'], $this->service->config->html_variables);
|
$this->getEmptyChildrens($element['elements'], $this->service->html_variables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1573,7 +1586,7 @@ class PdfBuilder
|
|||||||
{
|
{
|
||||||
foreach ($children as $key => $child) {
|
foreach ($children as $key => $child) {
|
||||||
if (isset($child['content']) && isset($child['show_empty']) && $child['show_empty'] === false) {
|
if (isset($child['content']) && isset($child['show_empty']) && $child['show_empty'] === false) {
|
||||||
$value = strtr($child['content'], $this->service->config->html_variables['values']);
|
$value = strtr($child['content'], $this->service->html_variables['values']);
|
||||||
if ($value === '' || $value === ' ') {
|
if ($value === '' || $value === ' ') {
|
||||||
$child['is_empty'] = true;
|
$child['is_empty'] = true;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,15 @@ class PdfDesigner
|
|||||||
/**
|
/**
|
||||||
* If the user has implemented a custom design, then we need to rebuild the design at this point
|
* 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
|
private function composeFromPartials(array $partials) :string
|
||||||
{
|
{
|
||||||
$html = '';
|
$html = '';
|
||||||
@ -73,8 +82,4 @@ class PdfDesigner
|
|||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -69,6 +69,8 @@ class PdfService
|
|||||||
{
|
{
|
||||||
$this->builder->build();
|
$this->builder->build();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPdf()
|
public function getPdf()
|
||||||
@ -78,7 +80,7 @@ class PdfService
|
|||||||
|
|
||||||
public function getHtml()
|
public function getHtml()
|
||||||
{
|
{
|
||||||
|
return $this->builder->getCompiledHTML();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,6 +33,19 @@ class PdfServiceTest extends TestCase
|
|||||||
$this->makeTestData();
|
$this->makeTestData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHtmlGeneration()
|
||||||
|
{
|
||||||
|
|
||||||
|
$invitation = $this->invoice->invitations->first();
|
||||||
|
|
||||||
|
$service = new PdfService($invitation);
|
||||||
|
|
||||||
|
$this->assertIsString($service->build()->getHtml());
|
||||||
|
|
||||||
|
nlog($service->build()->getHtml());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function testInitOfClass()
|
public function testInitOfClass()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -85,4 +98,5 @@ class PdfServiceTest extends TestCase
|
|||||||
$this->assertIsString($service->designer->template);
|
$this->assertIsString($service->designer->template);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user