2020-09-04 10:18:41 +02:00
< ? php
/**
2020-09-06 11:38:10 +02:00
* Invoice Ninja ( https :// invoiceninja . com ) .
2020-09-04 10:18:41 +02:00
*
* @ link https :// github . com / invoiceninja / invoiceninja source repository
*
2021-01-03 22:54:54 +01:00
* @ copyright Copyright ( c ) 2021. Invoice Ninja LLC ( https :// invoiceninja . com )
2020-09-04 10:18:41 +02:00
*
2021-06-16 08:58:16 +02:00
* @ license https :// www . elastic . co / licensing / elastic - license
2020-09-04 10:18:41 +02:00
*/
namespace App\Services\PdfMaker ;
2021-06-28 12:11:57 +02:00
use App\Models\Credit ;
2021-08-23 14:22:07 +02:00
use App\Models\GatewayType ;
use App\Models\Invoice ;
use App\Models\Payment ;
2020-10-28 11:10:49 +01:00
use App\Models\Quote ;
2020-09-04 10:18:41 +02:00
use App\Services\PdfMaker\Designs\Utilities\BaseDesign ;
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers ;
2020-09-11 10:07:59 +02:00
use App\Utils\Number ;
2021-08-20 15:39:27 +02:00
use App\Utils\Traits\MakesDates ;
2020-09-06 11:38:10 +02:00
use App\Utils\Traits\MakesInvoiceValues ;
2020-10-06 12:49:00 +02:00
use DOMDocument ;
2020-09-06 11:38:10 +02:00
use Illuminate\Support\Str ;
2020-09-04 10:18:41 +02:00
class Design extends BaseDesign
{
2021-08-20 15:39:27 +02:00
use MakesInvoiceValues , DesignHelpers , MakesDates ;
2020-09-04 10:18:41 +02:00
/** @var App\Models\Invoice || @var App\Models\Quote */
public $entity ;
2020-09-11 10:07:59 +02:00
/** @var App\Models\Client */
public $client ;
2020-09-04 10:18:41 +02:00
/** Global state of the design, @var array */
public $context ;
/** Type of entity => product||task */
public $type ;
/** Design string */
public $design ;
/** Construct options */
public $options ;
2021-08-20 15:39:27 +02:00
/** @var Invoice[] */
public $invoices ;
2021-08-23 14:22:07 +02:00
/** @var Payment[] */
public $payments ;
2021-08-24 15:33:23 +02:00
/** @var array */
public $aging = [];
2020-09-04 13:17:30 +02:00
const BOLD = 'bold' ;
const BUSINESS = 'business' ;
const CLEAN = 'clean' ;
const CREATIVE = 'creative' ;
const ELEGANT = 'elegant' ;
const HIPSTER = 'hipster' ;
const MODERN = 'modern' ;
const PLAIN = 'plain' ;
const PLAYFUL = 'playful' ;
2020-09-08 13:21:07 +02:00
const CUSTOM = 'custom' ;
2020-09-04 10:18:41 +02:00
2021-08-20 15:39:27 +02:00
const DELIVERY_NOTE = 'delivery_note' ;
const STATEMENT = 'statement' ;
2020-09-04 10:18:41 +02:00
public function __construct ( string $design = null , array $options = [])
{
2020-09-04 13:17:30 +02:00
Str :: endsWith ( '.html' , $design ) ? $this -> design = $design : $this -> design = " { $design } .html " ;
2020-09-04 10:18:41 +02:00
$this -> options = $options ;
}
public function html () : ? string
{
2020-09-08 13:21:07 +02:00
if ( $this -> design == 'custom.html' ) {
return $this -> composeFromPartials (
$this -> options [ 'custom_partials' ]
);
}
2021-08-24 15:33:23 +02:00
$path = $this -> options [ 'custom_path' ] ? ? config ( 'ninja.designs.base_path' );
2020-09-04 10:18:41 +02:00
return file_get_contents (
2020-09-09 14:47:26 +02:00
$path . $this -> design
2020-09-04 10:18:41 +02:00
);
}
public function elements ( array $context , string $type = 'product' ) : array
{
$this -> context = $context ;
$this -> type = $type ;
$this -> setup ();
return [
'company-details' => [
'id' => 'company-details' ,
'elements' => $this -> companyDetails (),
],
'company-address' => [
'id' => 'company-address' ,
'elements' => $this -> companyAddress (),
],
'client-details' => [
'id' => 'client-details' ,
'elements' => $this -> clientDetails (),
],
'entity-details' => [
'id' => 'entity-details' ,
'elements' => $this -> entityDetails (),
],
2020-11-09 14:30:50 +01:00
'delivery-note-table' => [
'id' => 'delivery-note-table' ,
'elements' => $this -> deliveryNoteTable (),
],
2020-09-04 10:18:41 +02:00
'product-table' => [
'id' => 'product-table' ,
'elements' => $this -> productTable (),
],
2020-11-04 14:56:08 +01:00
'task-table' => [
'id' => 'task-table' ,
'elements' => $this -> taskTable (),
],
2021-08-20 15:39:27 +02:00
'statement-invoice-table' => [
'id' => 'statement-invoice-table' ,
'elements' => $this -> statementInvoiceTable (),
],
2021-08-24 15:33:23 +02:00
'statement-invoice-table-totals' => [
'id' => 'statement-invoice-table-totals' ,
2021-08-24 16:13:52 +02:00
'elements' => $this -> statementInvoiceTableTotals (),
2021-08-24 15:33:23 +02:00
],
2021-08-23 14:22:07 +02:00
'statement-payment-table' => [
'id' => 'statement-payment-table' ,
'elements' => $this -> statementPaymentTable (),
],
2021-08-24 15:33:23 +02:00
'statement-payment-table-totals' => [
'id' => 'statement-payment-table-totals' ,
2021-08-24 16:13:52 +02:00
'elements' => $this -> statementPaymentTableTotals (),
2021-08-24 15:33:23 +02:00
],
2021-08-23 14:50:39 +02:00
'statement-aging-table' => [
'id' => 'statement-aging-table' ,
'elements' => $this -> statementAgingTable (),
],
2020-11-04 11:23:06 +01:00
'table-totals' => [
'id' => 'table-totals' ,
'elements' => $this -> tableTotals (),
2020-09-08 14:30:20 +02:00
],
2020-09-04 10:18:41 +02:00
'footer-elements' => [
'id' => 'footer' ,
'elements' => [
$this -> sharedFooterElements (),
],
],
];
}
2021-01-05 17:47:50 +01:00
public function companyDetails () : array
2020-09-04 10:18:41 +02:00
{
$variables = $this -> context [ 'pdf_variables' ][ 'company_details' ];
$elements = [];
foreach ( $variables as $variable ) {
2020-12-17 15:44:01 +01:00
$elements [] = [ 'element' => 'p' , 'content' => $variable , 'show_empty' => false , 'properties' => [ 'data-ref' => 'company_details-' . substr ( $variable , 1 )]];
2020-09-04 10:18:41 +02:00
}
return $elements ;
}
public function companyAddress () : array
{
$variables = $this -> context [ 'pdf_variables' ][ 'company_address' ];
$elements = [];
foreach ( $variables as $variable ) {
2020-12-17 15:44:01 +01:00
$elements [] = [ 'element' => 'p' , 'content' => $variable , 'show_empty' => false , 'properties' => [ 'data-ref' => 'company_address-' . substr ( $variable , 1 )]];
2020-09-04 10:18:41 +02:00
}
return $elements ;
}
public function clientDetails () : array
{
$elements = [];
2021-08-20 15:39:27 +02:00
if ( $this -> type == self :: DELIVERY_NOTE ) {
2020-11-09 14:30:50 +01:00
$elements = [
2021-08-03 13:38:33 +02:00
[ 'element' => 'p' , 'content' => ctrans ( 'texts.delivery_note' ), 'properties' => [ 'data-ref' => 'delivery_note-label' , 'style' => 'font-weight: bold; text-transform: uppercase' ]],
2021-10-12 10:10:43 +02:00
[ 'element' => 'p' , 'content' => $this -> client -> name , 'show_empty' => false , 'properties' => [ 'data-ref' => 'delivery_note-client.name' ]],
[ 'element' => 'p' , 'content' => $this -> client -> shipping_address1 , 'show_empty' => false , 'properties' => [ 'data-ref' => 'delivery_note-client.shipping_address1' ]],
[ 'element' => 'p' , 'content' => $this -> client -> shipping_address2 , 'show_empty' => false , 'properties' => [ 'data-ref' => 'delivery_note-client.shipping_address2' ]],
2020-12-17 15:44:01 +01:00
[ 'element' => 'p' , 'show_empty' => false , 'elements' => [
2021-10-12 10:10:43 +02:00
[ 'element' => 'span' , 'content' => " { $this -> client -> shipping_city } " , 'properties' => [ 'ref' => 'delivery_note-client.shipping_city' ]],
[ 'element' => 'span' , 'content' => " { $this -> client -> shipping_state } " , 'properties' => [ 'ref' => 'delivery_note-client.shipping_state' ]],
[ 'element' => 'span' , 'content' => " { $this -> client -> shipping_postal_code } " , 'properties' => [ 'ref' => 'delivery_note-client.shipping_postal_code' ]],
2020-12-17 15:44:01 +01:00
]],
2021-10-12 10:10:43 +02:00
[ 'element' => 'p' , 'content' => optional ( $this -> client -> shipping_country ) -> name , 'show_empty' => false ],
2020-11-09 14:30:50 +01:00
];
if ( ! is_null ( $this -> context [ 'contact' ])) {
2020-12-17 15:44:01 +01:00
$elements [] = [ 'element' => 'p' , 'content' => $this -> context [ 'contact' ] -> email , 'show_empty' => false , 'properties' => [ 'data-ref' => 'delivery_note-contact.email' ]];
2020-11-09 14:30:50 +01:00
}
return $elements ;
}
$variables = $this -> context [ 'pdf_variables' ][ 'client_details' ];
2020-09-04 10:18:41 +02:00
foreach ( $variables as $variable ) {
2020-12-17 15:44:01 +01:00
$elements [] = [ 'element' => 'p' , 'content' => $variable , 'show_empty' => false , 'properties' => [ 'data-ref' => 'client_details-' . substr ( $variable , 1 )]];
2020-09-04 10:18:41 +02:00
}
return $elements ;
}
public function entityDetails () : array
{
2021-08-20 15:39:27 +02:00
if ( $this -> type === 'statement' ) {
return [
[ 'element' => 'tr' , 'properties' => [], 'elements' => [
2021-08-24 15:33:23 +02:00
[ 'element' => 'th' , 'properties' => [], 'content' => ctrans ( 'texts.statement_date' )],
[ 'element' => 'th' , 'properties' => [], 'content' => $this -> options [ 'end_date' ] ? ? '' ],
2021-08-20 15:39:27 +02:00
]],
[ 'element' => 'tr' , 'properties' => [], 'elements' => [
[ 'element' => 'th' , 'properties' => [], 'content' => '$balance_due_label' ],
2021-10-12 10:10:43 +02:00
[ 'element' => 'th' , 'properties' => [], 'content' => Number :: formatMoney ( $this -> invoices -> sum ( 'balance' ), $this -> client )],
2021-08-20 15:39:27 +02:00
]],
2021-08-24 15:33:23 +02:00
];
2021-08-20 15:39:27 +02:00
}
2020-09-04 10:18:41 +02:00
$variables = $this -> context [ 'pdf_variables' ][ 'invoice_details' ];
2020-10-28 11:10:49 +01:00
if ( $this -> entity instanceof Quote ) {
2020-09-04 10:18:41 +02:00
$variables = $this -> context [ 'pdf_variables' ][ 'quote_details' ];
2021-09-20 08:50:22 +02:00
if ( $this -> entity -> partial > 0 ) {
$variables [] = '$quote.balance_due' ;
}
2020-09-04 10:18:41 +02:00
}
2021-06-28 12:11:57 +02:00
if ( $this -> entity instanceof Credit ) {
$variables = $this -> context [ 'pdf_variables' ][ 'credit_details' ];
}
2020-09-04 10:18:41 +02:00
$elements = [];
2020-12-08 13:19:38 +01:00
// We don't want to show account balance or invoice total on PDF.. or any amount with currency.
2021-08-20 15:39:27 +02:00
if ( $this -> type == self :: DELIVERY_NOTE ) {
2020-12-08 13:19:38 +01:00
$variables = array_filter ( $variables , function ( $m ) {
return ! in_array ( $m , [ '$invoice.balance_due' , '$invoice.total' ]);
});
}
2020-09-04 10:18:41 +02:00
foreach ( $variables as $variable ) {
2020-10-20 12:46:08 +02:00
$_variable = explode ( '.' , $variable )[ 1 ];
2020-10-20 13:01:07 +02:00
$_customs = [ 'custom1' , 'custom2' , 'custom3' , 'custom4' ];
2020-10-27 23:51:39 +01:00
2020-10-20 13:01:07 +02:00
if ( in_array ( $_variable , $_customs )) {
2020-10-20 12:46:08 +02:00
$elements [] = [ 'element' => 'tr' , 'elements' => [
2020-12-17 15:44:01 +01:00
[ '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 )]],
2020-10-20 12:46:08 +02:00
]];
} else {
$elements [] = [ 'element' => 'tr' , 'properties' => [ 'hidden' => $this -> entityVariableCheck ( $variable )], 'elements' => [
2020-12-17 15:44:01 +01:00
[ '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 )]],
2020-10-20 12:46:08 +02:00
]];
}
2020-09-04 10:18:41 +02:00
}
return $elements ;
}
2020-11-09 14:30:50 +01:00
public function deliveryNoteTable () : array
{
2021-08-20 15:39:27 +02:00
if ( $this -> type !== self :: DELIVERY_NOTE ) {
2020-11-25 15:19:52 +01:00
return [];
}
2020-11-09 16:10:47 +01:00
2021-12-27 15:26:37 +01:00
$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 -> entity -> line_items , $this -> 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' ]]);
}
}
2020-12-17 15:44:01 +01:00
return [
2021-12-27 15:26:37 +01:00
[ 'element' => 'thead' , 'elements' => $thead ],
2021-08-20 15:39:27 +02:00
[ 'element' => 'tbody' , 'elements' => $this -> buildTableBody ( self :: DELIVERY_NOTE )],
2020-11-09 14:30:50 +01:00
];
}
2020-11-04 14:56:08 +01:00
/**
* Parent method for building products table .
2020-11-25 15:19:52 +01:00
*
* @ return array
2020-11-04 14:56:08 +01:00
*/
2020-09-04 10:18:41 +02:00
public function productTable () : array
{
2020-11-04 14:56:08 +01:00
$product_items = collect ( $this -> entity -> line_items ) -> filter ( function ( $item ) {
2021-04-05 14:21:49 +02:00
return $item -> type_id == 1 || $item -> type_id == 6 ;
2020-11-04 14:56:08 +01:00
});
2020-11-06 13:12:51 +01:00
if ( count ( $product_items ) == 0 ) {
2020-11-04 14:56:08 +01:00
return [];
}
2021-08-20 15:39:27 +02:00
if ( $this -> type === self :: DELIVERY_NOTE || $this -> type === self :: STATEMENT ) {
2020-11-09 14:30:50 +01:00
return [];
}
2020-12-17 15:44:01 +01:00
return [
2020-11-04 14:56:08 +01:00
[ 'element' => 'thead' , 'elements' => $this -> buildTableHeader ( 'product' )],
[ 'element' => 'tbody' , 'elements' => $this -> buildTableBody ( '$product' )],
];
}
/**
* Parent method for building tasks table .
2020-11-25 15:19:52 +01:00
*
* @ return array
2020-11-04 14:56:08 +01:00
*/
public function taskTable () : array
{
$task_items = collect ( $this -> entity -> line_items ) -> filter ( function ( $item ) {
2020-11-09 16:10:47 +01:00
return $item -> type_id == 2 ;
2020-11-04 14:56:08 +01:00
});
2020-11-06 13:12:51 +01:00
if ( count ( $task_items ) == 0 ) {
2020-11-04 14:56:08 +01:00
return [];
}
2021-08-20 15:39:27 +02:00
if ( $this -> type === self :: DELIVERY_NOTE || $this -> type === self :: STATEMENT ) {
2020-11-09 14:30:50 +01:00
return [];
}
2020-11-04 14:56:08 +01:00
return [
[ 'element' => 'thead' , 'elements' => $this -> buildTableHeader ( 'task' )],
[ 'element' => 'tbody' , 'elements' => $this -> buildTableBody ( '$task' )],
2020-09-04 10:18:41 +02:00
];
}
2021-08-20 15:39:27 +02:00
/**
* Parent method for building invoices table within statement .
*
* @ return array
*/
public function statementInvoiceTable () : array
{
2021-08-23 14:22:07 +02:00
if ( is_null ( $this -> invoices ) || $this -> type !== self :: STATEMENT ) {
2021-08-23 14:05:17 +02:00
return [];
}
2021-08-23 14:22:07 +02:00
$tbody = [];
2021-12-19 05:58:59 +01:00
foreach ( $this -> invoices as $invoice ) {
2021-08-20 15:39:27 +02:00
$element = [ 'element' => 'tr' , 'elements' => []];
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $invoice -> number ];
2021-10-12 10:10:43 +02:00
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $this -> translateDate ( $invoice -> date , $this -> client -> date_format (), $this -> client -> locale ()) ? : ' ' ];
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $this -> translateDate ( $invoice -> due_date , $this -> client -> date_format (), $this -> client -> locale ()) ? : ' ' ];
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => Number :: formatMoney ( $invoice -> amount , $this -> client ) ? : ' ' ];
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => Number :: formatMoney ( $invoice -> balance , $this -> client ) ? : ' ' ];
2021-08-20 15:39:27 +02:00
$tbody [] = $element ;
}
return [
[ 'element' => 'thead' , 'elements' => $this -> buildTableHeader ( 'statement_invoice' )],
[ 'element' => 'tbody' , 'elements' => $tbody ],
];
}
2021-08-24 16:13:52 +02:00
public function statementInvoiceTableTotals () : array
{
if ( $this -> type !== self :: STATEMENT ) {
return [];
}
2021-09-20 14:55:16 +02:00
$outstanding = $this -> invoices -> sum ( 'balance' );
2021-08-25 03:41:07 +02:00
2021-08-24 16:13:52 +02:00
return [
2021-10-12 10:10:43 +02:00
[ 'element' => 'p' , 'content' => '$outstanding_label: ' . Number :: formatMoney ( $outstanding , $this -> client )],
2021-08-24 16:13:52 +02:00
];
}
2021-08-23 14:22:07 +02:00
/**
* Parent method for building payments table within statement .
*
* @ return array
*/
2021-08-24 15:33:23 +02:00
public function statementPaymentTable () : array
2021-08-23 14:22:07 +02:00
{
2021-08-24 15:33:23 +02:00
if ( is_null ( $this -> payments ) && $this -> type !== self :: STATEMENT ) {
2021-08-23 14:22:07 +02:00
return [];
}
2021-08-25 03:41:07 +02:00
if ( \array_key_exists ( 'show_payments_table' , $this -> options ) && $this -> options [ 'show_payments_table' ] === false ) {
2021-08-24 15:36:08 +02:00
return [];
}
2021-08-23 14:22:07 +02:00
$tbody = [];
2021-12-19 05:58:59 +01:00
foreach ( $this -> payments as $payment ) {
2021-08-23 14:22:07 +02:00
foreach ( $payment -> invoices as $invoice ) {
$element = [ 'element' => 'tr' , 'elements' => []];
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $invoice -> number ];
2021-10-12 10:10:43 +02:00
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $this -> translateDate ( $payment -> date , $this -> client -> date_format (), $this -> client -> locale ()) ? : ' ' ];
2021-08-25 03:41:07 +02:00
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $payment -> type ? $payment -> type -> name : ctrans ( 'texts.manual_entry' )];
2021-10-12 10:10:43 +02:00
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => Number :: formatMoney ( $payment -> amount , $this -> client ) ? : ' ' ];
2021-08-24 15:33:23 +02:00
2021-08-23 14:22:07 +02:00
$tbody [] = $element ;
}
}
return [
[ 'element' => 'thead' , 'elements' => $this -> buildTableHeader ( 'statement_payment' )],
[ 'element' => 'tbody' , 'elements' => $tbody ],
];
}
2021-08-24 16:13:52 +02:00
public function statementPaymentTableTotals () : array
{
2021-09-21 14:45:28 +02:00
if ( is_null ( $this -> payments ) || ! $this -> payments -> first () || $this -> type !== self :: STATEMENT ) {
2021-08-24 16:13:52 +02:00
return [];
}
2021-09-20 14:55:16 +02:00
if ( \array_key_exists ( 'show_payments_table' , $this -> options ) && $this -> options [ 'show_payments_table' ] === false ) {
return [];
}
2021-08-25 03:41:07 +02:00
$payment = $this -> payments -> first ();
2021-08-24 16:13:52 +02:00
return [
2021-10-12 10:10:43 +02:00
[ 'element' => 'p' , 'content' => \sprintf ( '%s: %s' , ctrans ( 'texts.amount_paid' ), Number :: formatMoney ( $this -> payments -> sum ( 'amount' ), $this -> client ))],
2021-08-24 16:13:52 +02:00
];
}
2021-08-24 15:33:23 +02:00
public function statementAgingTable () : array
2021-08-23 14:50:39 +02:00
{
2021-08-24 15:33:23 +02:00
if ( $this -> type !== self :: STATEMENT ) {
2021-08-23 14:50:39 +02:00
return [];
}
2021-08-24 15:33:23 +02:00
if ( \array_key_exists ( 'show_aging_table' , $this -> options ) && $this -> options [ 'show_aging_table' ] === false ) {
return [];
}
$elements = [
[ 'element' => 'thead' , 'elements' => []],
2021-08-23 14:50:39 +02:00
[ 'element' => 'tbody' , 'elements' => [
2021-08-24 15:33:23 +02:00
[ 'element' => 'tr' , 'elements' => []],
2021-08-23 14:50:39 +02:00
]],
];
2021-08-24 15:33:23 +02:00
foreach ( $this -> aging as $column => $value ) {
$elements [ 0 ][ 'elements' ][] = [ 'element' => 'th' , 'content' => $column ];
$elements [ 1 ][ 'elements' ][] = [ 'element' => 'td' , 'content' => $value ];
}
return $elements ;
2021-08-23 14:50:39 +02:00
}
2020-11-04 14:56:08 +01:00
/**
* Generate the structure of table headers . ( < thead /> )
2020-11-25 15:19:52 +01:00
*
2020-11-04 14:56:08 +01:00
* @ param string $type " product " or " task "
2020-11-25 15:19:52 +01:00
* @ return array
2020-11-04 14:56:08 +01:00
*/
public function buildTableHeader ( string $type ) : array
2020-09-04 10:18:41 +02:00
{
2020-11-04 14:56:08 +01:00
$this -> processTaxColumns ( $type );
2021-03-18 09:32:11 +01:00
// $this->processCustomColumns($type);
2020-09-04 10:18:41 +02:00
$elements = [];
2020-12-01 15:18:48 +01:00
// Some of column can be aliased. This is simple workaround for these.
2020-12-23 15:51:29 +01:00
$aliases = [
'$product.product_key' => '$product.item' ,
2021-02-15 13:16:47 +01:00
'$task.product_key' => '$task.service' ,
2021-04-01 15:00:09 +02:00
'$task.rate' => '$task.cost' ,
2020-12-23 15:51:29 +01:00
];
2020-12-01 15:18:48 +01:00
2020-11-04 14:56:08 +01:00
foreach ( $this -> context [ 'pdf_variables' ][ " { $type } _columns " ] as $column ) {
2020-12-01 15:18:48 +01:00
if ( array_key_exists ( $column , $aliases )) {
2021-04-01 15:00:09 +02:00
$elements [] = [ 'element' => 'th' , 'content' => $aliases [ $column ] . '_label' , 'properties' => [ 'data-ref' => " { $type } _table- " . substr ( $aliases [ $column ], 1 ) . '-th' , 'hidden' => $this -> client -> getSetting ( 'hide_empty_columns_on_pdf' )]];
2020-12-25 13:45:37 +01:00
} elseif ( $column == '$product.discount' && ! $this -> client -> 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 -> client -> company -> enable_product_quantity ) {
$elements [] = [ 'element' => 'th' , 'content' => $column . '_label' , 'properties' => [ 'data-ref' => " { $type } _table- " . substr ( $column , 1 ) . '-th' , 'style' => 'display: none;' ]];
2021-01-28 16:57:32 +01:00
} elseif ( $column == '$product.tax_rate1' ) {
$elements [] = [ 'element' => 'th' , 'content' => $column . '_label' , 'properties' => [ 'data-ref' => " { $type } _table-product.tax1-th " , 'hidden' => $this -> client -> getSetting ( '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 -> client -> getSetting ( '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 -> client -> getSetting ( 'hide_empty_columns_on_pdf' )]];
2020-12-01 15:18:48 +01:00
} else {
2021-01-26 17:41:36 +01:00
$elements [] = [ 'element' => 'th' , 'content' => $column . '_label' , 'properties' => [ 'data-ref' => " { $type } _table- " . substr ( $column , 1 ) . '-th' , 'hidden' => $this -> client -> getSetting ( 'hide_empty_columns_on_pdf' )]];
2020-12-01 15:18:48 +01:00
}
2020-09-04 10:18:41 +02:00
}
return $elements ;
}
2020-11-04 14:56:08 +01:00
/**
* Generate the structure of table body . ( < tbody /> )
2020-11-25 15:19:52 +01:00
*
2020-11-04 14:56:08 +01:00
* @ param string $type " $product " or " $task "
2020-11-25 15:19:52 +01:00
* @ return array
2020-11-04 14:56:08 +01:00
*/
public function buildTableBody ( string $type ) : array
2020-09-04 10:18:41 +02:00
{
$elements = [];
2020-11-04 14:56:08 +01:00
$items = $this -> transformLineItems ( $this -> entity -> line_items , $type );
2020-09-04 10:18:41 +02:00
2021-08-09 16:49:12 +02:00
$this -> processNewLines ( $items );
2020-09-04 10:18:41 +02:00
if ( count ( $items ) == 0 ) {
return [];
}
2021-01-28 16:57:32 +01:00
2021-08-20 15:39:27 +02:00
if ( $type == self :: DELIVERY_NOTE ) {
2021-12-27 15:26:37 +01:00
$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 ;
}
}
}
2020-11-09 14:30:50 +01:00
foreach ( $items as $row ) {
$element = [ 'element' => 'tr' , 'elements' => []];
2020-12-17 15:44:01 +01:00
$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' ]];
2020-12-01 15:18:48 +01:00
2021-12-27 15:26:37 +01:00
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' ]];
}
}
2020-11-09 14:30:50 +01:00
$elements [] = $element ;
}
return $elements ;
}
2020-09-04 10:18:41 +02:00
foreach ( $items as $row ) {
$element = [ 'element' => 'tr' , 'elements' => []];
2020-10-06 12:49:00 +02:00
if (
2020-11-04 14:56:08 +01:00
array_key_exists ( $type , $this -> context ) &&
! empty ( $this -> context [ $type ]) &&
! is_null ( $this -> context [ $type ])
2020-10-06 12:49:00 +02:00
) {
$document = new DOMDocument ();
2020-11-04 14:56:08 +01:00
$document -> loadHTML ( $this -> context [ $type ], LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
2020-10-06 12:49:00 +02:00
$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 {
2020-11-04 14:56:08 +01:00
$_type = Str :: startsWith ( $type , '$' ) ? ltrim ( $type , '$' ) : $type ;
foreach ( $this -> context [ 'pdf_variables' ][ " { $_type } _columns " ] as $key => $cell ) {
// We want to keep aliases like these:
// $task.cost => $task.rate
// $task.quantity => $task.hours
2020-11-06 13:12:51 +01:00
2020-11-04 14:56:08 +01:00
if ( $cell == '$task.rate' ) {
2020-12-17 15:44:01 +01:00
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $row [ '$task.cost' ], 'properties' => [ 'data-ref' => 'task_table-task.cost-td' ]];
2020-12-25 13:45:37 +01:00
} elseif ( $cell == '$product.discount' && ! $this -> client -> 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 -> client -> company -> enable_product_quantity ) {
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $row [ '$product.quantity' ], 'properties' => [ 'data-ref' => 'product_table-product.quantity-td' , 'style' => 'display: none;' ]];
2020-11-25 15:19:52 +01:00
} elseif ( $cell == '$task.hours' ) {
2020-12-17 15:44:01 +01:00
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $row [ '$task.quantity' ], 'properties' => [ 'data-ref' => 'task_table-task.hours-td' ]];
2021-01-28 16:57:32 +01:00
} 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' ]];
2021-04-05 13:22:21 +02:00
} else if ( $cell == '$product.unit_cost' || $cell == '$task.rate' ) {
2021-02-26 07:47:04 +01:00
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $row [ $cell ], 'properties' => [ 'style' => 'white-space: nowrap;' , 'data-ref' => " { $_type } _table- " . substr ( $cell , 1 ) . '-td' ]];
2021-04-05 13:22:21 +02:00
} else {
2020-12-17 15:44:01 +01:00
$element [ 'elements' ][] = [ 'element' => 'td' , 'content' => $row [ $cell ], 'properties' => [ 'data-ref' => " { $_type } _table- " . substr ( $cell , 1 ) . '-td' ]];
2020-11-04 14:56:08 +01:00
}
2020-10-06 12:49:00 +02:00
}
2020-09-04 10:18:41 +02:00
}
$elements [] = $element ;
}
return $elements ;
}
2020-11-04 11:23:06 +01:00
public function tableTotals () : array
2020-09-04 10:18:41 +02:00
{
2021-09-27 18:01:52 +02:00
if ( $this -> type === self :: STATEMENT ) {
return [
[ 'element' => 'div' , 'properties' => [ 'style' => 'display: flex; flex-direction: column;' ], 'elements' => [
[ 'element' => 'div' , 'properties' => [ 'style' => 'margin-top: 1.5rem; display: flex; align-items: flex-start;' ], 'elements' => [
[ 'element' => 'img' , 'properties' => [ 'src' => '$invoiceninja.whitelabel' , 'style' => 'height: 2.5rem;' , 'hidden' => $this -> entity -> user -> account -> isPaid () ? 'true' : 'false' , 'id' => 'invoiceninja-whitelabel-logo' ]],
]],
]],
];
}
2021-02-16 14:42:35 +01:00
$_variables = array_key_exists ( 'variables' , $this -> context )
? $this -> context [ 'variables' ]
2021-03-22 12:08:59 +01:00
: [ 'values' => [ '$entity.public_notes' => $this -> entity -> public_notes , '$entity.terms' => $this -> entity -> terms , '$entity_footer' => $this -> entity -> footer ], 'labels' => []];
2021-02-16 14:42:35 +01:00
2020-09-04 10:18:41 +02:00
$variables = $this -> context [ 'pdf_variables' ][ 'total_columns' ];
2021-12-14 22:34:25 +01:00
/* 'labels' is a protected value - if the user enters labels it attempts to replace this string again - we need to set labels are a protected text label and remove it from the string */
2020-09-04 10:18:41 +02:00
$elements = [
2021-01-14 16:38:40 +01:00
[ 'element' => 'div' , 'properties' => [ 'style' => 'display: flex; flex-direction: column;' ], 'elements' => [
2021-12-14 22:34:25 +01:00
[ 'element' => 'p' , 'content' => strtr ( str_replace ( " labels " , " " , $_variables [ 'values' ][ '$entity.public_notes' ]), $_variables ), 'properties' => [ 'data-ref' => 'total_table-public_notes' , 'style' => 'text-align: left;' ]],
2021-01-14 16:38:40 +01:00
[ 'element' => 'p' , 'content' => '' , 'properties' => [ 'style' => 'text-align: left; display: flex; flex-direction: column;' ], 'elements' => [
2021-03-22 12:10:53 +01:00
[ '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;' ]],
2021-12-14 22:34:25 +01:00
[ 'element' => 'span' , 'content' => strtr ( str_replace ( " labels " , " " , $_variables [ 'values' ][ '$entity.terms' ]), $_variables [ 'labels' ]), 'properties' => [ 'data-ref' => 'total_table-terms' , 'style' => 'text-align: left;' ]],
2021-01-14 16:38:40 +01:00
]],
2021-06-23 11:54:59 +02:00
[ 'element' => 'img' , 'properties' => [ 'style' => 'max-width: 50%; height: auto;' , 'src' => '$contact.signature' , 'id' => 'contact-signature' ]],
2021-02-10 12:31:14 +01:00
[ 'element' => 'div' , 'properties' => [ 'style' => 'margin-top: 1.5rem; display: flex; align-items: flex-start;' ], 'elements' => [
2021-03-03 08:49:02 +01:00
[ 'element' => 'img' , 'properties' => [ 'src' => '$invoiceninja.whitelabel' , 'style' => 'height: 2.5rem;' , 'hidden' => $this -> entity -> user -> account -> isPaid () ? 'true' : 'false' , 'id' => 'invoiceninja-whitelabel-logo' ]],
2021-02-10 12:31:14 +01:00
]],
2020-09-04 10:18:41 +02:00
]],
2021-08-16 11:11:34 +02:00
[ 'element' => 'div' , 'properties' => [ 'class' => 'totals-table-right-side' , 'dir' => '$dir' ], 'elements' => []],
2020-09-04 10:18:41 +02:00
];
2021-09-27 18:01:52 +02:00
if ( $this -> type == self :: DELIVERY_NOTE ) {
2021-05-20 15:04:24 +02:00
return $elements ;
}
2021-07-29 12:55:30 +02:00
if ( $this -> entity instanceof Quote ) {
// We don't want to show Balanace due on the quotes.
if ( in_array ( '$outstanding' , $variables )) {
$variables = \array_diff ( $variables , [ '$outstanding' ]);
}
2021-09-20 15:00:54 +02:00
if ( $this -> entity -> partial > 0 ) {
$variables [] = '$partial_due' ;
}
2021-07-29 12:55:30 +02:00
}
2021-04-05 13:22:21 +02:00
foreach ([ 'discount' ] as $property ) {
2020-09-09 14:47:26 +02:00
$variable = sprintf ( '%s%s' , '$' , $property );
if (
2020-09-11 16:46:49 +02:00
! is_null ( $this -> entity -> { $property }) &&
! empty ( $this -> entity -> { $property }) &&
$this -> entity -> { $property } != 0
2020-09-09 14:47:26 +02:00
) {
continue ;
}
$variables = array_filter ( $variables , function ( $m ) use ( $variable ) {
return $m != $variable ;
});
}
2020-09-04 10:18:41 +02:00
foreach ( $variables as $variable ) {
2020-09-11 10:07:59 +02:00
if ( $variable == '$total_taxes' ) {
$taxes = $this -> entity -> calc () -> getTotalTaxMap ();
if ( ! $taxes ) {
continue ;
}
2021-01-15 12:56:05 +01:00
foreach ( $taxes as $i => $tax ) {
2021-02-09 16:47:41 +01:00
$elements [ 1 ][ 'elements' ][] = [ 'element' => 'div' , 'elements' => [
2021-01-15 12:56:05 +01:00
[ 'element' => 'span' , 'content' , 'content' => $tax [ 'name' ], 'properties' => [ 'data-ref' => 'totals-table-total_tax_' . $i . '-label' ]],
[ 'element' => 'span' , 'content' , 'content' => Number :: formatMoney ( $tax [ 'total' ], $this -> context [ 'client' ]), 'properties' => [ 'data-ref' => 'totals-table-total_tax_' . $i ]],
2020-09-11 10:07:59 +02:00
]];
}
} elseif ( $variable == '$line_taxes' ) {
$taxes = $this -> entity -> calc () -> getTaxMap ();
if ( ! $taxes ) {
continue ;
}
2021-01-15 12:56:05 +01:00
foreach ( $taxes as $i => $tax ) {
2021-02-09 16:47:41 +01:00
$elements [ 1 ][ 'elements' ][] = [ 'element' => 'div' , 'elements' => [
2021-01-15 12:56:05 +01:00
[ 'element' => 'span' , 'content' , 'content' => $tax [ 'name' ], 'properties' => [ 'data-ref' => 'totals-table-line_tax_' . $i . '-label' ]],
[ 'element' => 'span' , 'content' , 'content' => Number :: formatMoney ( $tax [ 'total' ], $this -> context [ 'client' ]), 'properties' => [ 'data-ref' => 'totals-table-line_tax_' . $i ]],
2020-09-11 10:07:59 +02:00
]];
}
2021-06-16 16:09:30 +02:00
} elseif ( Str :: startsWith ( $variable , '$custom_surcharge' )) {
$_variable = ltrim ( $variable , '$' ); // $custom_surcharge1 -> custom_surcharge1
2021-06-23 11:54:59 +02:00
$visible = $this -> entity -> { $_variable } > 0 || $this -> entity -> { $_variable } > '0' ;
2021-06-16 16:09:30 +02:00
$elements [ 1 ][ 'elements' ][] = [ 'element' => 'div' , 'elements' => [
2021-06-23 11:54:59 +02:00
[ '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 )]],
2021-06-16 16:09:30 +02:00
]];
2021-04-05 13:22:21 +02:00
} elseif ( Str :: startsWith ( $variable , '$custom' )) {
$field = explode ( '_' , $variable );
2021-04-06 14:38:59 +02:00
$visible = is_object ( $this -> client -> company -> custom_fields ) && property_exists ( $this -> client -> company -> custom_fields , $field [ 1 ]) && ! empty ( $this -> client -> company -> custom_fields -> { $field [ 1 ]});
2021-04-05 13:22:21 +02:00
$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 )]],
]];
2020-09-11 10:07:59 +02:00
} else {
2021-02-09 16:47:41 +01:00
$elements [ 1 ][ 'elements' ][] = [ 'element' => 'div' , 'elements' => [
2021-01-15 12:56:05 +01:00
[ '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 )]],
2020-09-11 10:07:59 +02:00
]];
2020-09-04 13:17:30 +02:00
}
2020-09-04 10:18:41 +02:00
}
2021-02-09 16:47:41 +01:00
$elements [ 1 ][ 'elements' ][] = [ 'element' => 'div' , 'elements' => [
[ 'element' => 'span' , 'content' => '' ,],
[ 'element' => 'span' , 'content' => '' ],
]];
2020-09-04 10:18:41 +02:00
return $elements ;
}
}