mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-08 20:22:42 +01:00
Working on UBL2PDF
This commit is contained in:
parent
fe6613524b
commit
8dbd7462ea
@ -12,6 +12,7 @@
|
|||||||
namespace App\Services\EDocument\Imports;
|
namespace App\Services\EDocument\Imports;
|
||||||
|
|
||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
|
use App\Utils\Number;
|
||||||
use App\Models\Vendor;
|
use App\Models\Vendor;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use App\Models\Country;
|
use App\Models\Country;
|
||||||
@ -26,6 +27,7 @@ use InvoiceNinja\EInvoice\EInvoice;
|
|||||||
use App\Utils\Traits\SavesDocuments;
|
use App\Utils\Traits\SavesDocuments;
|
||||||
use App\Factory\VendorContactFactory;
|
use App\Factory\VendorContactFactory;
|
||||||
use App\Repositories\ExpenseRepository;
|
use App\Repositories\ExpenseRepository;
|
||||||
|
use App\Services\Template\TemplateService;
|
||||||
|
|
||||||
class Ubl2Pdf extends AbstractService
|
class Ubl2Pdf extends AbstractService
|
||||||
{
|
{
|
||||||
@ -44,18 +46,57 @@ class Ubl2Pdf extends AbstractService
|
|||||||
$t = app('translator');
|
$t = app('translator');
|
||||||
App::setLocale($this->company->locale());
|
App::setLocale($this->company->locale());
|
||||||
$t->replace(Ninja::transformTranslations($this->company->settings));
|
$t->replace(Ninja::transformTranslations($this->company->settings));
|
||||||
|
$template = file_get_contents(resource_path('views/templates/ubl/td14.html'));
|
||||||
// nlog($client);
|
// nlog($client);
|
||||||
// nlog($supplier);
|
// nlog($supplier);
|
||||||
// nlog($invoiceDetails);
|
// nlog($invoiceDetails);
|
||||||
// nlog($totals);
|
// nlog($totals);
|
||||||
|
|
||||||
$data = [
|
$data = [
|
||||||
'client' => $this->clientDetails();
|
'client' => $this->clientDetails(),
|
||||||
'supplier' => $this->supplierDetails();
|
'supplier' => $this->supplierDetails(),
|
||||||
'invoiceDetails' => $this->invoiceDetails();
|
'invoiceDetails' => $this->invoiceDetails(),
|
||||||
'totals' => $this->totals();
|
'totals' => $this->totals(),
|
||||||
]
|
'metadata' => $this->metadata(),
|
||||||
|
'translations' => $this->getGenericTranslations(),
|
||||||
|
'css' => $this->customCss(),
|
||||||
|
];
|
||||||
|
|
||||||
|
$ts = new TemplateService();
|
||||||
|
|
||||||
|
$ts_instance = $ts->setCompany($this->company)
|
||||||
|
->setData($data)
|
||||||
|
->setRawTemplate($template)
|
||||||
|
->parseNinjaBlocks()
|
||||||
|
->save();
|
||||||
|
|
||||||
|
nlog($ts_instance->getHtml());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getGenericTranslations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'to' => ctrans('texts.to'),
|
||||||
|
'from' => ctrans('texts.from'),
|
||||||
|
'invoice' => ctrans('texts.invoice'),
|
||||||
|
'credit' => ctrans('texts.credit'),
|
||||||
|
'details' => ctrans('texts.details'),
|
||||||
|
'number' => ctrans('texts.number'),
|
||||||
|
'tax' => ctrans('texts.tax'),
|
||||||
|
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
// 'from' => ctrans('texts.from'),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processValues(array $array): array
|
private function processValues(array $array): array
|
||||||
@ -63,9 +104,11 @@ class Ubl2Pdf extends AbstractService
|
|||||||
|
|
||||||
foreach($array as $key => $value)
|
foreach($array as $key => $value)
|
||||||
{
|
{
|
||||||
if(empty($value))
|
if($value === null || $value === '')
|
||||||
unset($array[$key]);
|
unset($array[$key]);
|
||||||
|
|
||||||
|
if($value instanceof \DateTime)
|
||||||
|
$array[$key] = $value->format($this->company->date_format());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $array;
|
return $array;
|
||||||
@ -98,33 +141,57 @@ class Ubl2Pdf extends AbstractService
|
|||||||
ctrans('texts.city') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CityName', ''),
|
ctrans('texts.city') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CityName', ''),
|
||||||
ctrans('texts.state') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CountrySubentity', ''),
|
ctrans('texts.state') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.CountrySubentity', ''),
|
||||||
ctrans('texts.postal_code') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.PostalZone', ''),
|
ctrans('texts.postal_code') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.PostalZone', ''),
|
||||||
ctrans('texts.country_id') => $this->resolveCountry(data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.Country.IdentificationCode.value', '')),
|
ctrans('texts.country_id') => data_get($this->invoice, 'AccountingSupplierParty.Party.PostalAddress.Country.IdentificationCode.value', ''),
|
||||||
ctrans('texts.routing_id') => data_get($this->invoice, 'AccountingSupplierParty.Party.EndpointID.value', ''),
|
ctrans('texts.routing_id') => data_get($this->invoice, 'AccountingSupplierParty.Party.EndpointID.value', ''),
|
||||||
ctrans('texts.id_number') => data_get($this->invoice, 'AccountingSupplierParty.Party.PartyIdentification.0.ID.value', false),
|
ctrans('texts.id_number') => data_get($this->invoice, 'AccountingSupplierParty.Party.PartyIdentification.0.ID.value', false),
|
||||||
ctrans('texts.vat_number') => data_get($this->invoice, 'AccountingSupplierParty.Party.PartyTaxScheme.0.CompanyID.value', ''),
|
ctrans('texts.vat_number') => data_get($this->invoice, 'AccountingSupplierParty.Party.PartyTaxScheme.0.CompanyID.value', ''),
|
||||||
ctrans('texts.currency_id') => $this->resolveCurrencyId(data_get($this->invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code)),
|
// ctrans('texts.currency_id') => $this->resolveCurrencyId(data_get($this->invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code)),
|
||||||
ctrans('texts.contact_name') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Name', ''),
|
ctrans('texts.contact_name') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Name', ''),
|
||||||
ctrans('texts.phone') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Telephone', ''),
|
ctrans('texts.phone') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.Telephone', ''),
|
||||||
ctrans('texts.email') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.ElectronicMail', ''),
|
ctrans('texts.email') => data_get($this->invoice, 'AccountingCustomerParty.Party.Contact.ElectronicMail', ''),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function customCss(): string
|
||||||
|
{
|
||||||
|
$css = '';
|
||||||
|
$css .= ".".str_replace(" ", "", ctrans('texts.product_key'))." { width: 15%;} ";
|
||||||
|
$css .= ".".str_replace(" ", "", ctrans('texts.quantity'))." { width: 8%;} ";
|
||||||
|
$css .= ".".str_replace(" ", "", ctrans('texts.notes'))." { width: 40%; } ";
|
||||||
|
$css .= ".".str_replace(" ", "", ctrans('texts.cost'))." { width:10%;} ";
|
||||||
|
$css .= ".".str_replace(" ", "", ctrans('texts.tax'))." { width:10%;} ";
|
||||||
|
$css .= ".".str_replace(" ", "", ctrans('texts.line_total'))." { width:15%;} ";
|
||||||
|
|
||||||
|
return $css;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private function invoiceDetails(): array
|
private function invoiceDetails(): array
|
||||||
{
|
{
|
||||||
|
|
||||||
$data = $this->processValues([
|
$data = $this->processValues([
|
||||||
'currency' => data_get($this->invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code),
|
ctrans('texts.currency') => data_get($this->invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code),
|
||||||
'doctype' => data_get($this->invoice, 'InvoiceTypeCode.value', "380"),
|
ctrans('texts.currency_code') => data_get($this->invoice, 'InvoiceTypeCode.value', "380"),
|
||||||
ctrans('texts.number') => data_get($this->invoice, 'ID.value', ''),
|
ctrans('texts.number') => data_get($this->invoice, 'ID.value', ''),
|
||||||
ctrans('texts.date') => data_get($this->invoice, 'IssueDate', ''),
|
ctrans('texts.date') => data_get($this->invoice, 'IssueDate', ''),
|
||||||
ctrans('texts.due_date') => data_get($this->invoice, 'DueDate', ''),
|
ctrans('texts.due_date') => data_get($this->invoice, 'DueDate', ''),
|
||||||
ctrans('texts.terms') => $this->harvestTerms(),
|
|
||||||
ctrans('texts.public_notes') => data_get($this->invoice, 'Note', '')
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$data['line_items'] = $this->invoiceLines(),
|
$data['line_items'] = $this->invoiceLines();
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function metadata(): array
|
||||||
|
{
|
||||||
|
|
||||||
|
return $this->processValues([
|
||||||
|
'currency' => data_get($this->invoice, 'DocumentCurrencyCode.value', $this->company->currency()->code),
|
||||||
|
ctrans('texts.terms') => $this->harvestTerms(),
|
||||||
|
ctrans('texts.public_notes') => data_get($this->invoice, 'Note', '')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
private function harvestTerms(): string
|
private function harvestTerms(): string
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -151,18 +218,18 @@ class Ubl2Pdf extends AbstractService
|
|||||||
|
|
||||||
return array_map(function ($line) {
|
return array_map(function ($line) {
|
||||||
return [
|
return [
|
||||||
ctrans('texts.quantity') => data_get($line, 'InvoicedQuantity.amount', 0),
|
|
||||||
ctrans('texts.unit_code') => data_get($line, 'InvoicedQuantity.UnitCode','C62'),
|
|
||||||
ctrans('texts.product_key') => data_get($line, 'Item.Name', ''),
|
ctrans('texts.product_key') => data_get($line, 'Item.Name', ''),
|
||||||
|
// ctrans('texts.ocde') => data_get($line, 'InvoicedQuantity.UnitCode',''),
|
||||||
|
ctrans('texts.quantity') => Number::formatValue(data_get($line, 'InvoicedQuantity.amount', 0), $this->company->currency()),
|
||||||
ctrans('texts.notes') => data_get($line, 'Item.Description', ''),
|
ctrans('texts.notes') => data_get($line, 'Item.Description', ''),
|
||||||
ctrans('texts.cost') => data_get($line, 'Price.PriceAmount.value', 0),
|
ctrans('texts.cost') => Number::formatValue(data_get($line, 'Price.PriceAmount.amount', 0), $this->company->currency()),
|
||||||
ctrans('texts.line_total') => data_get($line, 'LineExtensionAmount.amount', 0),
|
|
||||||
'tax_name1' => data_get($line, 'Item.ClassifiedTaxCategory.0.TaxScheme.ID.value', ''),
|
'tax_name1' => data_get($line, 'Item.ClassifiedTaxCategory.0.TaxScheme.ID.value', ''),
|
||||||
'tax_rate1' => data_get($line, 'Item.ClassifiedTaxCategory.0.Percent', 0),
|
'tax_rate1' => data_get($line, 'Item.ClassifiedTaxCategory.0.Percent', 0),
|
||||||
'tax_name2' => data_get($line, 'Item.ClassifiedTaxCategory.1.TaxScheme.ID.value', ''),
|
'tax_name2' => data_get($line, 'Item.ClassifiedTaxCategory.1.TaxScheme.ID.value', ''),
|
||||||
'tax_rate2' => data_get($line, 'Item.ClassifiedTaxCategory.1.Percent', 0),
|
'tax_rate2' => data_get($line, 'Item.ClassifiedTaxCategory.1.Percent', 0),
|
||||||
'tax_name3' => data_get($line, 'Item.ClassifiedTaxCategory.2.TaxScheme.ID.value', ''),
|
'tax_name3' => data_get($line, 'Item.ClassifiedTaxCategory.2.TaxScheme.ID.value', ''),
|
||||||
'tax_rate3' => data_get($line, 'Item.ClassifiedTaxCategory.2.Percent', 0),
|
'tax_rate3' => data_get($line, 'Item.ClassifiedTaxCategory.2.Percent', 0),
|
||||||
|
ctrans('texts.line_total') => Number::formatValue(data_get($line, 'LineExtensionAmount.amount', 0), $this->company->currency()),
|
||||||
];
|
];
|
||||||
}, $lines);
|
}, $lines);
|
||||||
}
|
}
|
||||||
@ -178,15 +245,15 @@ class Ubl2Pdf extends AbstractService
|
|||||||
foreach(data_get($this->invoice, 'TaxTotal.0.TaxSubtotal', []) as $tax_subtotal)
|
foreach(data_get($this->invoice, 'TaxTotal.0.TaxSubtotal', []) as $tax_subtotal)
|
||||||
{
|
{
|
||||||
$taxes[] = [
|
$taxes[] = [
|
||||||
'subtotal' => data_get($tax_subtotal, 'TaxableAmount.amount', 0),
|
'subtotal' => data_get($tax_subtotal, 'TaxAmount.amount', 0),
|
||||||
'tax_name' => data_get($tax_subtotal, 'TaxCategory.TaxScheme.ID.value', ''),
|
'tax_name' => data_get($tax_subtotal, 'TaxCategory.TaxScheme.ID.value', ''),
|
||||||
'tax_rate' => data_get($tax_subtotal, 'TaxAmount.amount', 0),
|
'tax_rate' => data_get($tax_subtotal, 'TaxCategory.Percent', 0),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'subtotal' => data_get($this->invoice, 'LegalMonetaryTotal.LineExtensionAmount.amount', 0),
|
ctrans('texts.subtotal') => data_get($this->invoice, 'LegalMonetaryTotal.LineExtensionAmount.amount', 0),
|
||||||
'total' => data_get($this->invoice, 'LegalMonetaryTotal.TaxInclusiveAmount.amount', 0),
|
ctrans('balance') => data_get($this->invoice, 'LegalMonetaryTotal.TaxInclusiveAmount.amount', 0),
|
||||||
'taxes' => $taxes,
|
'taxes' => $taxes,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -292,7 +292,7 @@ class TemplateService
|
|||||||
*
|
*
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
private function parseNinjaBlocks(): self
|
public function parseNinjaBlocks(): self
|
||||||
{
|
{
|
||||||
$replacements = [];
|
$replacements = [];
|
||||||
|
|
||||||
@ -350,6 +350,12 @@ class TemplateService
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setData(array $data): self
|
||||||
|
{
|
||||||
|
$this->data = $data;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Parses all variables in the document
|
* Parses all variables in the document
|
||||||
*
|
*
|
||||||
@ -379,7 +385,7 @@ class TemplateService
|
|||||||
*
|
*
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
private function save(): self
|
public function save(): self
|
||||||
{
|
{
|
||||||
$this->compiled_html = str_replace('%24', '$', $this->document->saveHTML());
|
$this->compiled_html = str_replace('%24', '$', $this->document->saveHTML());
|
||||||
|
|
||||||
@ -409,6 +415,14 @@ class TemplateService
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setRawTemplate(string $template):self
|
||||||
|
{
|
||||||
|
|
||||||
|
@$this->document->loadHTML(mb_convert_encoding($template, 'HTML-ENTITIES', 'UTF-8'));
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Inject the template components
|
* Inject the template components
|
||||||
* manually
|
* manually
|
||||||
|
328
resources/views/templates/ubl/td14.html
Normal file
328
resources/views/templates/ubl/td14.html
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- UBL to PDF Design - TemplateID #TD14 -->
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: Roboto, Helvetica, sans-serif;
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
margin-top: 1rem;
|
||||||
|
min-width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-header>tr>th {
|
||||||
|
border-bottom: solid 1px #efefef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-body>tr>td {
|
||||||
|
border-bottom: solid 1px #efefef;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-row {
|
||||||
|
border-bottom: 1px #000 dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.totals-row-label {
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.totals-row-value {
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-totals {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.centered {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-title {
|
||||||
|
font-size: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
padding-right: 5px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div .label {
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div .value {
|
||||||
|
text-align: left;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.two-col-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 2fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.three-col-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
column-gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-detail-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.client-details {
|
||||||
|
display:flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supplier-details {}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-margin {
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-style: italic;
|
||||||
|
color: #454545;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-color-highlight {
|
||||||
|
color: #4DA8DA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-color-highlight {
|
||||||
|
color: 3e3e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-details {
|
||||||
|
display:flex;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 1.4;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
display:flex;
|
||||||
|
text-align: left;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
margin-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<ninja>
|
||||||
|
<style>
|
||||||
|
{{ css }}
|
||||||
|
</style>
|
||||||
|
</ninja>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<ninja>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<table width="100%" cellspacing="0" cellpadding="0" class="" border="0">
|
||||||
|
<tr>
|
||||||
|
<td align="left" class="doc-title">{{ translations.invoice }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table width="100%" height="100%" cellspacing="0" cellpadding="0" class="" border="0">
|
||||||
|
<tr>
|
||||||
|
<td style="">
|
||||||
|
<div class="three-col-grid">
|
||||||
|
|
||||||
|
<div class="" style="margin-left:0; margin-right:auto;">
|
||||||
|
<div class="client-details">
|
||||||
|
<p class="section-title">{{ translations.to }}:</p>
|
||||||
|
</div>
|
||||||
|
<div id="client-details">
|
||||||
|
{% for key,value in client %}
|
||||||
|
<div class="two-col-grid">
|
||||||
|
<dt class="order-details">{{ key }}</dt>
|
||||||
|
<dd class="align-left">{{ value }}</dd>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="" style="margin-left:auto; margin-right:auto;">
|
||||||
|
<div class="supplier-details">
|
||||||
|
<p class="section-title">{{ translations.from }}:</p>
|
||||||
|
</div>
|
||||||
|
<div id="supplier-details">
|
||||||
|
{% for key,value in supplier %}
|
||||||
|
<div class="two-col-grid">
|
||||||
|
<dt class="order-details">{{ key }}</dt>
|
||||||
|
<dd class="align-left">{{ value }}</dd>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div style="margin-right:0; margin-left: auto;">
|
||||||
|
<div class="client-details">
|
||||||
|
<p class="section-title" style="padding-right:10px;">{{ translations.details }}:</p>
|
||||||
|
</div>
|
||||||
|
<div class="two-col-grid">
|
||||||
|
|
||||||
|
{% for key,value in invoiceDetails %}
|
||||||
|
{% if key != 'line_items' %}
|
||||||
|
<dt class="order-details">{{ key }}</dt>
|
||||||
|
<dd class="align-left">{{ value }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- If you are using this template for other entities such as quotes, you'll want to ensure that you change the variable from invoices => quotes here -->
|
||||||
|
|
||||||
|
{% if invoiceDetails.line_items is defined and invoiceDetails.line_items is not empty %}
|
||||||
|
|
||||||
|
|
||||||
|
<table width="100%" cellspacing="0" cellpadding="0" class="">
|
||||||
|
<thead class="table-header">
|
||||||
|
<tr class="table-header primary-color-highlight">
|
||||||
|
|
||||||
|
{% for key,value in invoiceDetails.line_items|first %}
|
||||||
|
|
||||||
|
{% if key not in ['tax_rate1', 'tax_rate2', 'tax_rate3'] %}
|
||||||
|
|
||||||
|
{% if key in ['tax_name1', 'tax_name2', 'tax_name3'] %}
|
||||||
|
|
||||||
|
{% if value|length > 1 %}
|
||||||
|
<th class="{{ translations.tax|replace({'/\\s+/': ''}) }}">{{ translations.tax }}</th>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<th class="{{ key|replace({'/\\s+/': ''}) }}">{{ key }}</th>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="table-body">
|
||||||
|
{% for item in invoiceDetails.line_items %}
|
||||||
|
<tr class="item-row">
|
||||||
|
|
||||||
|
{% for key,value in item %}
|
||||||
|
{% if key not in ['tax_name1', 'tax_name2', 'tax_name3'] %}
|
||||||
|
|
||||||
|
{% if key in ['tax_rate1', 'tax_rate2', 'tax_rate3'] %}
|
||||||
|
{% if value > 0 %}
|
||||||
|
<td class="{{ key|replace({'/\\s+/': ''}) }}">{{ value }}%</td>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<td class="{{ key|replace({'/\\s+/': ''}) }}">{{ value }}</td>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<table width="100%" cellspacing="0" cellpadding="0" class="" border="0"></table>
|
||||||
|
<tr>
|
||||||
|
<td align="left" class="doc-title" width="50%"></td>
|
||||||
|
<td align="right" width="50%">
|
||||||
|
|
||||||
|
<div class="two-col-grid">
|
||||||
|
{% for key,value in totals.taxes %}
|
||||||
|
<dt class="order-details">{{ value.tax_name }} {{ value.tax_rate }}%</dt>
|
||||||
|
<dd class="align-left">{{ value.subtotal|format_currency(metadata.currency) }}</dd>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for key,value in totals %}
|
||||||
|
{% if key != 'taxes' %}
|
||||||
|
<dt class="order-details">{{ key }}</dt>
|
||||||
|
<dd class="align-left">{{ value|format_currency(metadata.currency) }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table width="100%" cellspacing="0" cellpadding="0" class="">
|
||||||
|
{% for key,value in metadata %}
|
||||||
|
|
||||||
|
{% if key != 'currency' %}
|
||||||
|
<div class="container">
|
||||||
|
<p class="bottom-margin primary-color-highlight">{{ key }}:</p>
|
||||||
|
|
||||||
|
<p>{{ value|nl2br }}</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</ninja>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user