Merge pull request #6496 from beganovich/v5-statements

(v5) Statements implementation
This commit is contained in:
David Bomba 2021-08-25 09:41:57 +10:00 committed by GitHub
commit 9c11e5fc4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 739 additions and 350 deletions

View File

@ -71,7 +71,7 @@ class CompanySettings extends BaseSettings
public $inclusive_taxes = false; //@implemented public $inclusive_taxes = false; //@implemented
public $quote_footer = ''; //@implmented public $quote_footer = ''; //@implmented
public $translations; public $translations;
public $counter_number_applied = 'when_saved'; // when_saved , when_sent //@implemented public $counter_number_applied = 'when_saved'; // when_saved , when_sent //@implemented
public $quote_number_applied = 'when_saved'; // when_saved , when_sent //@implemented public $quote_number_applied = 'when_saved'; // when_saved , when_sent //@implemented
@ -594,7 +594,7 @@ class CompanySettings extends BaseSettings
* *
* @return stdClass The stdClass of PDF variables * @return stdClass The stdClass of PDF variables
*/ */
private static function getEntityVariableDefaults() :stdClass public static function getEntityVariableDefaults() :stdClass
{ {
$variables = [ $variables = [
'client_details' => [ 'client_details' => [
@ -676,6 +676,19 @@ class CompanySettings extends BaseSettings
'$paid_to_date', '$paid_to_date',
'$outstanding', '$outstanding',
], ],
'statement_invoice_columns' => [
'$invoice.number',
'$invoice.date',
'$due_date',
'$total',
'$outstanding',
],
'statement_payment_columns' => [
'$invoice.number',
'$payment.date',
'$method',
'$outstanding',
],
]; ];
return json_decode(json_encode($variables)); return json_decode(json_encode($variables));

View File

@ -521,16 +521,6 @@ class ClientController extends BaseController
return $this->listResponse(Client::withTrashed()->whereIn('id', $this->transformKeys($ids))); return $this->listResponse(Client::withTrashed()->whereIn('id', $this->transformKeys($ids)));
} }
/**
* Returns a client statement.
*
* @return void [type] [description]
*/
public function statement()
{
//todo
}
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
* *

View File

@ -11,31 +11,118 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
/** use App\Http\Requests\Statements\CreateStatementRequest;
* Class ClientStatementController. use App\Models\Design;
*/ use App\Models\InvoiceInvitation;
use App\Services\PdfMaker\Design as PdfDesignModel;
use App\Services\PdfMaker\Design as PdfMakerDesign;
use App\Services\PdfMaker\PdfMaker as PdfMakerService;
use App\Utils\HostedPDF\NinjaPdf;
use App\Utils\HtmlEngine;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\Pdf\PdfMaker;
class ClientStatementController extends BaseController class ClientStatementController extends BaseController
{ {
use MakesHash, PdfMaker;
/** @var \App\Models\Invoice|\App\Models\Payment */
protected $entity;
public function __construct() public function __construct()
{ {
parent::__construct(); parent::__construct();
} }
/** public function statement(CreateStatementRequest $request)
* Displays a client statement view for a given
* client_id.
* @return void
*/
public function show()
{ {
$pdf = $this->createStatement($request);
if ($pdf) {
return response()->streamDownload(function () use ($pdf) {
echo $pdf;
}, 'statement.pdf', ['Content-Type' => 'application/pdf']);
}
return response()->json(['message' => 'Something went wrong. Please check logs.']);
} }
/** protected function createStatement(CreateStatementRequest $request): ?string
* Updates the show view data dependent on
* configured variables.
* @return void
*/
public function update()
{ {
$invitation = InvoiceInvitation::first();
if (count($request->getInvoices()) >= 1) {
$this->entity = $request->getInvoices()->first();
}
if (count($request->getPayments()) >= 1) {
$this->entity = $request->getPayments()->first();
}
$entity_design_id = 1;
$entity_design_id = $this->entity->design_id
? $this->entity->design_id
: $this->decodePrimaryKey($this->entity->client->getSetting($entity_design_id));
$design = Design::find($entity_design_id);
if (!$design) {
$design = Design::find($entity_design_id);
}
$html = new HtmlEngine($invitation);
$options = [
'start_date' => $request->start_date,
'end_date' => $request->end_date,
'show_payments_table' => $request->show_payments_table,
'show_aging_table' => $request->show_aging_table,
];
if ($design->is_custom) {
$options['custom_partials'] = \json_decode(\json_encode($design->design), true);
$template = new PdfMakerDesign(PdfDesignModel::CUSTOM, $options);
} else {
$template = new PdfMakerDesign(strtolower($design->name), $options);
}
$variables = $html->generateLabelsAndValues();
$state = [
'template' => $template->elements([
'client' => $this->entity->client,
'entity' => $this->entity,
'pdf_variables' => (array)$this->entity->company->settings->pdf_variables,
'$product' => $design->design->product,
'variables' => $variables,
'invoices' => $request->getInvoices(),
'payments' => $request->getPayments(),
'aging' => $request->getAging(),
], \App\Services\PdfMaker\Design::STATEMENT),
'variables' => $variables,
'options' => [],
'process_markdown' => $this->entity->client->company->markdown_enabled,
];
$maker = new PdfMakerService($state);
$maker
->design($template)
->build();
$pdf = null;
try {
if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') {
$pdf = (new NinjaPdf())->build($maker->getCompiledHTML(true));
} else {
$pdf = $this->makePdf(null, null, $maker->getCompiledHTML(true));
}
} catch (\Exception $e) {
nlog(print_r($e->getMessage(), 1));
}
return $pdf;
} }
} }

View File

@ -0,0 +1,71 @@
<?php
namespace App\Http\Requests\Statements;
use App\Models\Invoice;
use App\Models\Payment;
use Illuminate\Foundation\Http\FormRequest;
class CreateStatementRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return auth()->user()->isAdmin();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'start_date' => ['required'],
'end_date' => ['required'],
];
}
/**
* The collection of invoices for the statement.
*
* @return Invoice[]|\Illuminate\Database\Eloquent\Collection
*/
public function getInvoices()
{
// $this->request->start_date & $this->request->end_date are available.
return Invoice::all();
}
/**
* The collection of payments for the statement.
*
* @return Payment[]|\Illuminate\Database\Eloquent\Collection
*/
public function getPayments()
{
// $this->request->start_date & $this->request->end_date are available.
return Payment::all();
}
/**
* The array of aging data.
*/
public function getAging(): array
{
return [
'0-30' => 1000,
'30-60' => 2000,
'60-90' => 3000,
'90-120' => 4000,
'120+' => 5000,
];
}
}

View File

@ -13,17 +13,21 @@
namespace App\Services\PdfMaker; namespace App\Services\PdfMaker;
use App\Models\Credit; use App\Models\Credit;
use App\Models\GatewayType;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Quote; use App\Models\Quote;
use App\Services\PdfMaker\Designs\Utilities\BaseDesign; use App\Services\PdfMaker\Designs\Utilities\BaseDesign;
use App\Services\PdfMaker\Designs\Utilities\DesignHelpers; use App\Services\PdfMaker\Designs\Utilities\DesignHelpers;
use App\Utils\Number; use App\Utils\Number;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesInvoiceValues; use App\Utils\Traits\MakesInvoiceValues;
use DOMDocument; use DOMDocument;
use Illuminate\Support\Str; use Illuminate\Support\Str;
class Design extends BaseDesign class Design extends BaseDesign
{ {
use MakesInvoiceValues, DesignHelpers; use MakesInvoiceValues, DesignHelpers, MakesDates;
/** @var App\Models\Invoice || @var App\Models\Quote */ /** @var App\Models\Invoice || @var App\Models\Quote */
public $entity; public $entity;
@ -43,6 +47,15 @@ class Design extends BaseDesign
/** Construct options */ /** Construct options */
public $options; public $options;
/** @var Invoice[] */
public $invoices;
/** @var Payment[] */
public $payments;
/** @var array */
public $aging = [];
const BOLD = 'bold'; const BOLD = 'bold';
const BUSINESS = 'business'; const BUSINESS = 'business';
const CLEAN = 'clean'; const CLEAN = 'clean';
@ -54,6 +67,9 @@ class Design extends BaseDesign
const PLAYFUL = 'playful'; const PLAYFUL = 'playful';
const CUSTOM = 'custom'; const CUSTOM = 'custom';
const DELIVERY_NOTE = 'delivery_note';
const STATEMENT = 'statement';
public function __construct(string $design = null, array $options = []) public function __construct(string $design = null, array $options = [])
{ {
Str::endsWith('.html', $design) ? $this->design = $design : $this->design = "{$design}.html"; Str::endsWith('.html', $design) ? $this->design = $design : $this->design = "{$design}.html";
@ -69,9 +85,7 @@ class Design extends BaseDesign
); );
} }
$path = isset($this->options['custom_path']) $path = $this->options['custom_path'] ?? config('ninja.designs.base_path');
? $this->options['custom_path']
: config('ninja.designs.base_path');
return file_get_contents( return file_get_contents(
$path . $this->design $path . $this->design
@ -115,6 +129,26 @@ class Design extends BaseDesign
'id' => 'task-table', 'id' => 'task-table',
'elements' => $this->taskTable(), 'elements' => $this->taskTable(),
], ],
'statement-invoice-table' => [
'id' => 'statement-invoice-table',
'elements' => $this->statementInvoiceTable(),
],
'statement-invoice-table-totals' => [
'id' => 'statement-invoice-table-totals',
'elements' => $this->statementInvoiceTableTotals(),
],
'statement-payment-table' => [
'id' => 'statement-payment-table',
'elements' => $this->statementPaymentTable(),
],
'statement-payment-table-totals' => [
'id' => 'statement-payment-table-totals',
'elements' => $this->statementPaymentTableTotals(),
],
'statement-aging-table' => [
'id' => 'statement-aging-table',
'elements' => $this->statementAgingTable(),
],
'table-totals' => [ 'table-totals' => [
'id' => 'table-totals', 'id' => 'table-totals',
'elements' => $this->tableTotals(), 'elements' => $this->tableTotals(),
@ -158,7 +192,7 @@ class Design extends BaseDesign
{ {
$elements = []; $elements = [];
if ($this->type == 'delivery_note') { if ($this->type == self::DELIVERY_NOTE) {
$elements = [ $elements = [
['element' => 'p', 'content' => ctrans('texts.delivery_note'), 'properties' => ['data-ref' => 'delivery_note-label', 'style' => 'font-weight: bold; text-transform: uppercase']], ['element' => 'p', 'content' => ctrans('texts.delivery_note'), 'properties' => ['data-ref' => 'delivery_note-label', 'style' => 'font-weight: bold; text-transform: uppercase']],
['element' => 'p', 'content' => $this->entity->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']], ['element' => 'p', 'content' => $this->entity->client->name, 'show_empty' => false, 'properties' => ['data-ref' => 'delivery_note-client.name']],
@ -190,6 +224,19 @@ class Design extends BaseDesign
public function entityDetails(): array public function entityDetails(): array
{ {
if ($this->type === 'statement') {
return [
['element' => 'tr', 'properties' => [], 'elements' => [
['element' => 'th', 'properties' => [], 'content' => ctrans('texts.statement_date')],
['element' => 'th', 'properties' => [], 'content' => $this->options['end_date'] ?? ''],
]],
['element' => 'tr', 'properties' => [], 'elements' => [
['element' => 'th', 'properties' => [], 'content' => '$balance_due_label'],
['element' => 'th', 'properties' => [], 'content' => '$balance_due'],
]],
];
}
$variables = $this->context['pdf_variables']['invoice_details']; $variables = $this->context['pdf_variables']['invoice_details'];
if ($this->entity instanceof Quote) { if ($this->entity instanceof Quote) {
@ -203,7 +250,7 @@ class Design extends BaseDesign
$elements = []; $elements = [];
// We don't want to show account balance or invoice total on PDF.. or any amount with currency. // We don't want to show account balance or invoice total on PDF.. or any amount with currency.
if ($this->type == 'delivery_note') { if ($this->type == self::DELIVERY_NOTE) {
$variables = array_filter($variables, function ($m) { $variables = array_filter($variables, function ($m) {
return !in_array($m, ['$invoice.balance_due', '$invoice.total']); return !in_array($m, ['$invoice.balance_due', '$invoice.total']);
}); });
@ -231,7 +278,7 @@ class Design extends BaseDesign
public function deliveryNoteTable(): array public function deliveryNoteTable(): array
{ {
if ($this->type !== 'delivery_note') { if ($this->type !== self::DELIVERY_NOTE) {
return []; return [];
} }
@ -241,7 +288,7 @@ class Design extends BaseDesign
['element' => 'th', 'content' => '$description_label', 'properties' => ['data-ref' => 'delivery_note-description_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']], ['element' => 'th', 'content' => '$product.quantity_label', 'properties' => ['data-ref' => 'delivery_note-product.quantity_label']],
]], ]],
['element' => 'tbody', 'elements' => $this->buildTableBody('delivery_note')], ['element' => 'tbody', 'elements' => $this->buildTableBody(self::DELIVERY_NOTE)],
]; ];
} }
@ -260,7 +307,7 @@ class Design extends BaseDesign
return []; return [];
} }
if ($this->type == 'delivery_note') { if ($this->type === self::DELIVERY_NOTE || $this->type === self::STATEMENT) {
return []; return [];
} }
@ -285,7 +332,7 @@ class Design extends BaseDesign
return []; return [];
} }
if ($this->type == 'delivery_note') { if ($this->type === self::DELIVERY_NOTE || $this->type === self::STATEMENT) {
return []; return [];
} }
@ -295,6 +342,120 @@ class Design extends BaseDesign
]; ];
} }
/**
* Parent method for building invoices table within statement.
*
* @return array
*/
public function statementInvoiceTable(): array
{
if (is_null($this->invoices) || $this->type !== self::STATEMENT) {
return [];
}
$tbody = [];
foreach ($this->invoices as $invoice) {
$element = ['element' => 'tr', 'elements' => []];
$element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
$element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->date, $invoice->client->date_format(), $invoice->client->locale()) ?: '&nbsp;'];
$element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($invoice->due_date, $invoice->client->date_format(), $invoice->client->locale()) ?: '&nbsp;'];
$element['elements'][] = ['element' => 'td', 'content' => Number::formatMoney($invoice->calc()->getTotal(), $invoice->client) ?: '&nbsp;'];
$element['elements'][] = ['element' => 'td', 'content' => Number::formatMoney($invoice->partial, $invoice->client) ?: '&nbsp;'];
$tbody[] = $element;
}
return [
['element' => 'thead', 'elements' => $this->buildTableHeader('statement_invoice')],
['element' => 'tbody', 'elements' => $tbody],
];
}
public function statementInvoiceTableTotals(): array
{
if ($this->type !== self::STATEMENT) {
return [];
}
return [
['element' => 'p', 'content' => '$outstanding_label: $outstanding'],
];
}
/**
* Parent method for building payments table within statement.
*
* @return array
*/
public function statementPaymentTable(): array
{
if (is_null($this->payments) && $this->type !== self::STATEMENT) {
return [];
}
if (\array_key_exists('show_payment_table', $this->options) && $this->options['show_payment_table'] === false) {
return [];
}
$tbody = [];
foreach ($this->payments as $payment) {
foreach ($payment->invoices as $invoice) {
$element = ['element' => 'tr', 'elements' => []];
$element['elements'][] = ['element' => 'td', 'content' => $invoice->number];
$element['elements'][] = ['element' => 'td', 'content' => $this->translateDate($payment->date, $payment->client->date_format(), $payment->client->locale()) ?: '&nbsp;'];
$element['elements'][] = ['element' => 'td', 'content' => GatewayType::getAlias($payment->gateway_type_id) ?: '&nbsp;'];
$element['elements'][] = ['element' => 'td', 'content' => Number::formatMoney($payment->amount, $payment->client) ?: '&nbsp;'];
$tbody[] = $element;
}
}
return [
['element' => 'thead', 'elements' => $this->buildTableHeader('statement_payment')],
['element' => 'tbody', 'elements' => $tbody],
];
}
public function statementPaymentTableTotals(): array
{
if ($this->type !== self::STATEMENT) {
return [];
}
return [
['element' => 'p', 'content' => \sprintf('%s: %s', ctrans('texts.amount_paid'), 1000)],
];
}
public function statementAgingTable(): array
{
if ($this->type !== self::STATEMENT) {
return [];
}
if (\array_key_exists('show_aging_table', $this->options) && $this->options['show_aging_table'] === false) {
return [];
}
$elements = [
['element' => 'thead', 'elements' => []],
['element' => 'tbody', 'elements' => [
['element' => 'tr', 'elements' => []],
]],
];
foreach ($this->aging as $column => $value) {
$elements[0]['elements'][] = ['element' => 'th', 'content' => $column];
$elements[1]['elements'][] = ['element' => 'td', 'content' => $value];
}
return $elements;
}
/** /**
* Generate the structure of table headers. (<thead/>) * Generate the structure of table headers. (<thead/>)
* *
@ -354,7 +515,7 @@ class Design extends BaseDesign
return []; return [];
} }
if ($type == 'delivery_note') { if ($type == self::DELIVERY_NOTE) {
foreach ($items as $row) { foreach ($items as $row) {
$element = ['element' => 'tr', 'elements' => []]; $element = ['element' => 'tr', 'elements' => []];
@ -453,7 +614,7 @@ class Design extends BaseDesign
['element' => 'div', 'properties' => ['class' => 'totals-table-right-side', 'dir' => '$dir'], 'elements' => []], ['element' => 'div', 'properties' => ['class' => 'totals-table-right-side', 'dir' => '$dir'], 'elements' => []],
]; ];
if ($this->type == 'delivery_note') { if ($this->type == self::DELIVERY_NOTE || $this->type == self::STATEMENT) {
return $elements; return $elements;
} }

View File

@ -28,6 +28,8 @@ trait DesignHelpers
public function setup(): self public function setup(): self
{ {
$this->syncPdfVariables();
if (isset($this->context['client'])) { if (isset($this->context['client'])) {
$this->client = $this->context['client']; $this->client = $this->context['client'];
} }
@ -36,11 +38,38 @@ trait DesignHelpers
$this->entity = $this->context['entity']; $this->entity = $this->context['entity'];
} }
if (isset($this->context['invoices'])) {
$this->invoices = $this->context['invoices'];
$this->entity = $this->invoices->first();
}
if (isset($this->context['payments'])) {
$this->payments = $this->context['payments'];
}
if (isset($this->context['aging'])) {
$this->aging = $this->context['aging'];
}
$this->document(); $this->document();
return $this; return $this;
} }
protected function syncPdfVariables(): void
{
$default = (array) \App\DataMapper\CompanySettings::getEntityVariableDefaults();
$variables = $this->context['pdf_variables'];
foreach ($default as $property => $value) {
if (array_key_exists($property, $variables)) {
continue;
}
$variables[$property] = $value;
}
}
/** /**
* Initialize local dom document instance. Used for getting raw HTML out of template. * Initialize local dom document instance. Used for getting raw HTML out of template.
* *

View File

@ -190,7 +190,7 @@ class HtmlEngine
} }
else{ else{
$data['$balance_due'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: '&nbsp;', 'label' => ctrans('texts.balance_due')]; $data['$balance_due'] = ['value' => Number::formatMoney($this->entity->balance, $this->client) ?: '&nbsp;', 'label' => ctrans('texts.balance_due')];
$data['$balance_due_raw'] = ['value' => $this->entity->balance, 'label' => ctrans('texts.balance_due')]; $data['$balance_due_raw'] = ['value' => $this->entity->balance, 'label' => ctrans('texts.balance_due')];
} }
} }
@ -302,7 +302,7 @@ class HtmlEngine
$data['$contact.full_name'] = ['value' => $this->contact->present()->name(), 'label' => ctrans('texts.name')]; $data['$contact.full_name'] = ['value' => $this->contact->present()->name(), 'label' => ctrans('texts.name')];
$data['$contact'] = &$data['$contact.full_name']; $data['$contact'] = &$data['$contact.full_name'];
$data['$contact.email'] = ['value' => $this->contact->email, 'label' => ctrans('texts.email')]; $data['$contact.email'] = ['value' => $this->contact->email, 'label' => ctrans('texts.email')];
$data['$contact.phone'] = ['value' => $this->contact->phone, 'label' => ctrans('texts.phone')]; $data['$contact.phone'] = ['value' => $this->contact->phone, 'label' => ctrans('texts.phone')];
@ -439,6 +439,9 @@ class HtmlEngine
$data['$dir'] = ['value' => optional($this->client->language())->locale === 'ar' ? 'rtl' : 'ltr', 'label' => '']; $data['$dir'] = ['value' => optional($this->client->language())->locale === 'ar' ? 'rtl' : 'ltr', 'label' => ''];
$data['$dir_text_align'] = ['value' => optional($this->client->language())->locale === 'ar' ? 'right' : 'left', 'label' => '']; $data['$dir_text_align'] = ['value' => optional($this->client->language())->locale === 'ar' ? 'right' : 'left', 'label' => ''];
$data['$payment.date'] = ['value' => '&nbsp;', 'label' => ctrans('texts.payment_date')];
$data['$method'] = ['value' => '&nbsp;', 'label' => ctrans('texts.method')];
$arrKeysLength = array_map('strlen', array_keys($data)); $arrKeysLength = array_map('strlen', array_keys($data));
array_multisort($arrKeysLength, SORT_DESC, $data); array_multisort($arrKeysLength, SORT_DESC, $data);

View File

@ -80,9 +80,7 @@
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
} }
#product-table, [data-ref="table"] {
#task-table,
#delivery-note-table {
min-width: 100%; min-width: 100%;
table-layout: fixed; table-layout: fixed;
overflow-wrap: break-word; overflow-wrap: break-word;
@ -96,46 +94,32 @@
color: grey; color: grey;
} }
#product-table > thead, [data-ref="table"] > thead {
#delivery-note-table > thead,
#task-table > thead {
text-align: left; text-align: left;
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
padding: 1.5rem 3rem; padding: 1.5rem 3rem;
font-size: 1rem; font-size: 1rem;
} }
#product-table > thead > tr > th:last-child, [data-ref="table"] > thead > tr > th:last-child {
#delivery-note-table > thead > tr > th:last-child,
#task-table > thead > tr > th:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
padding: 1.5rem 3rem; padding: 1.5rem 3rem;
} }
#product-table > tbody > tr > td:last-child, [data-ref="table"] > tbody > tr > td:last-child {
#delivery-note-table > tbody > tr > td:last-child,
#task-table > tbody > tr > td:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td:first-child, [data-ref="table"] > tbody > tr > td:first-child {
#delivery-note-table > tbody > tr > td:first-child,
#task-table > tbody > tr > td:first-child {
font-weight: bold; font-weight: bold;
} }
#product-table > tbody > tr:nth-child(odd), [data-ref="table"] > tbody > tr:nth-child(odd) {
#delivery-note-table > tbody > tr:nth-child(odd),
#task-table > tbody > tr:nth-child(odd) {
background-color: #ebebeb; background-color: #ebebeb;
} }
@ -254,12 +238,18 @@
margin-bottom: 0; margin-bottom: 0;
} }
[data-ref="product_table-product.description-th"] { [data-ref="product_table-product.description-th"] {
width: 23%; width: 23%;
} }
[data-ref="statement-totals"] {
margin-top: 1rem;
text-align: right;
margin-right: .75rem;
}
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
/* .company-logo { display: none } */ /* .company-logo { display: none } */
@ -318,11 +308,20 @@
</div> </div>
<!-- Start Print Content --> <!-- Start Print Content -->
<table id="product-table" cellspacing="0" class="print-content"></table> <table id="product-table" cellspacing="0" class="print-content" data-ref="table"></table>
<table id="task-table" cellspacing="0" class="print-content"></table> <table id="task-table" cellspacing="0" class="print-content" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0" class="print-content"></table> <table id="delivery-note-table" cellspacing="0" class="print-content" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" class="print-content" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" class="print-content" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" class="print-content" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
<!-- End Print Content --> <!-- End Print Content -->
</td> </td>
</tr> </tr>
@ -350,7 +349,12 @@
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -32,7 +32,7 @@
.company-logo { .company-logo {
max-width: 65%; max-width: 65%;
} }
.header-container > span { .header-container > span {
display: block; display: block;
} }
@ -93,9 +93,7 @@
text-align: right; text-align: right;
} }
#product-table, [data-ref="table"] {
#delivery-note-table,
#task-table {
margin-top: 3.5rem; margin-top: 3.5rem;
/* margin-bottom: 200px; */ /* margin-bottom: 200px; */
min-width: 100%; min-width: 100%;
@ -109,55 +107,39 @@
color: grey; color: grey;
} }
#product-table > thead, [data-ref="table"] > thead {
#delivery-note-table > thead,
#task-table > thead {
text-align: left; text-align: left;
background: var(--secondary-color); background: var(--secondary-color);
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
padding: 1rem; padding: 1rem;
color: white; color: white;
font-weight: semibold; font-weight: semibold;
} }
#product-table > thead > tr > th:first-child, [data-ref="table"] > thead > tr > th:first-child {
#delivery-note-table > thead > tr > th:first-child,
#task-table > thead > tr > th:first-child {
border-top-left-radius: 1rem; border-top-left-radius: 1rem;
} }
#product-table > thead > tr > th:last-child, [data-ref="table"] > thead > tr > th:last-child {
#delivery-note-table > thead > tr > th:last-child,
#task-table > thead > tr > th:last-child {
border-top-right-radius: 1rem; border-top-right-radius: 1rem;
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
padding: 1rem; padding: 1rem;
} }
#product-table > tbody > tr > td:last-child, [data-ref="table"] > tbody > tr > td:last-child {
#delivery-note-table > tbody > tr > td:last-child,
#task-table > tbody > tr > td:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr:nth-child(odd) > td, [data-ref="table"] > tbody > tr:nth-child(odd) > td {
#delivery-note-table > tbody > tr:nth-child(odd) > td,
#task-table > tbody > tr:nth-child(odd) > td {
background: #F7F7F7; background: #F7F7F7;
} }
#product-table > tbody > tr:nth-child(even) > td, [data-ref="table"] > tbody > tr:nth-child(even) > td {
#delivery-note-table > tbody > tr:nth-child(even) > td,
#task-table > tbody > tr:nth-child(even) > td {
background: #f7f7f7; background: #f7f7f7;
} }
@ -241,9 +223,7 @@
} }
/** Markdown-specific styles. **/ /** Markdown-specific styles. **/
#product-table h3, [data-ref="table"] h3 {
#task-table h3,
#delivery-note-table h3 {
font-size: 1rem; font-size: 1rem;
margin-bottom: 0; margin-bottom: 0;
} }
@ -257,8 +237,14 @@
padding-right: 7px; padding-right: 7px;
} }
[data-ref="statement-totals"] {
margin-top: 1rem;
text-align: right;
margin-right: .75rem;
}
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
/* .company-logo { display: none } */ /* .company-logo { display: none } */
@ -310,12 +296,21 @@
</div> </div>
</div> </div>
<table id="product-table" cellspacing="0"></table> <table id="product-table" cellspacing="0" data-ref="table"></table>
<table id="task-table" cellspacing="0"></table> <table id="task-table" cellspacing="0" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
<table id="delivery-note-table" cellspacing="0"></table>
<div id="table-totals" cellspacing="0"></div> <div id="table-totals" cellspacing="0"></div>
</div> </div>
@ -325,7 +320,12 @@
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -88,9 +88,7 @@
font-weight: bold; font-weight: bold;
} }
#product-table, [data-ref="table"] {
#delivery-note-table,
#task-table {
margin-top: 3rem; margin-top: 3rem;
/* margin-bottom: 200px; */ /* margin-bottom: 200px; */
min-width: 100%; min-width: 100%;
@ -104,43 +102,31 @@
color: grey; color: grey;
} }
#product-table > thead, [data-ref="table"] > thead {
#delivery-note-table > thead,
#task-table > thead {
text-align: left; text-align: left;
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
font-size: 1.1rem; font-size: 1.1rem;
padding-bottom: 1.5rem; padding-bottom: 1.5rem;
padding-left: 1rem; padding-left: 1rem;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
border-top: 1px solid #d8d8d8; border-top: 1px solid #d8d8d8;
border-bottom: 1px solid #d8d8d8; border-bottom: 1px solid #d8d8d8;
padding: 1.5rem; padding: 1.5rem;
} }
#product-table > tbody > tr > td:first-child, [data-ref="table"] > tbody > tr > td:first-child {
#delivery-note-table > tbody > tr > td:first-child,
#task-table > tbody > tr > td:first-child {
color: var(--primary-color); color: var(--primary-color);
} }
#product-table > tbody > tr > td:last-child, [data-ref="table"] > tbody > tr > td:last-child {
#delivery-note-table > tbody > tr > td:last-child,
#task-table > tbody > tr > td:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr:nth-child(odd), [data-ref="table"] > tbody > tr:nth-child(odd) {
#delivery-note-table > tbody > tr:nth-child(odd),
#task-table > tbody > tr:nth-child(odd) {
background-color: #f5f5f5; background-color: #f5f5f5;
} }
@ -214,9 +200,15 @@
flex-direction: column; flex-direction: column;
justify-content: flex-end; justify-content: flex-end;
} }
[data-ref="statement-totals"] {
margin-top: 1rem;
text-align: right;
margin-right: .75rem;
}
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
/* .company-logo { display: none } */ /* .company-logo { display: none } */
@ -266,22 +258,36 @@
<div id="client-details"></div> <div id="client-details"></div>
</div> </div>
<table id="product-table" cellspacing="0"></table> <table id="product-table" cellspacing="0" data-ref="table"></table>
<table id="task-table" cellspacing="0"></table> <table id="task-table" cellspacing="0" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0"></table> <table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
<div id="table-totals" cellspacing="0"></div> <div id="table-totals" cellspacing="0"></div>
</div> </div>
<div id="footer"> <div id="footer">
<p data-ref="total_table-footer">$entity_footer</p> <p data-ref="total_table-footer">$entity_footer</p>
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -84,18 +84,14 @@
font-weight: normal; font-weight: normal;
} }
#product-table, [data-ref="table"] {
#delivery-note-table,
#task-table {
/* margin-bottom: 200px; */ /* margin-bottom: 200px; */
min-width: 100%; min-width: 100%;
table-layout: fixed; table-layout: fixed;
overflow-wrap: break-word; overflow-wrap: break-word;
} }
#product-table:not(:empty), [data-ref="table"]:not(:empty) {
#delivery-note-table:not(:empty),
#task-table:not(:empty) {
border-top: 5px solid var(--primary-color); border-top: 5px solid var(--primary-color);
margin-top: 3rem; margin-top: 3rem;
} }
@ -106,40 +102,28 @@
color: grey; color: grey;
} }
#product-table > thead, [data-ref="table"] > thead {
#delivery-note-table > thead,
#task-table > thead {
text-align: left; text-align: left;
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
font-size: 1.1rem; font-size: 1.1rem;
padding: 1rem; padding: 1rem;
} }
#product-table > thead > tr > th:last-child, [data-ref="table"] > thead > tr > th:last-child {
#delivery-note-table > thead > tr > th:last-child,
#task-table > thead > tr > th:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td:last-child, [data-ref="table"] > tbody > tr > td:last-child {
#delivery-note-table > tbody > tr > td:last-child,
#task-table > tbody > tr > td:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
padding: 1rem; padding: 1rem;
} }
#product-table > tbody > tr:nth-child(odd), [data-ref="table"] > tbody > tr:nth-child(odd) {
#delivery-note-table > tbody > tr:nth-child(odd),
#task-table > tbody > tr:nth-child(odd) {
background-color: #e8e8e8; background-color: #e8e8e8;
} }
@ -171,7 +155,7 @@
margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse))); margin-top: calc(.75rem * calc(1 - var(--tw-space-y-reverse)));
margin-bottom: calc(.75rem * var(--tw-space-y-reverse)); margin-bottom: calc(.75rem * var(--tw-space-y-reverse));
} }
#table-totals>.totals-table-right-side>*> :nth-child(2) { #table-totals>.totals-table-right-side>*> :nth-child(2) {
text-align: right; text-align: right;
} }
@ -201,15 +185,19 @@
} }
/** Markdown-specific styles. **/ /** Markdown-specific styles. **/
#product-table h3, [data-ref="table"] h3 {
#task-table h3,
#delivery-note-table h3 {
font-size: 1rem; font-size: 1rem;
margin-bottom: 0; margin-bottom: 0;
} }
[data-ref="statement-totals"] {
margin-top: 1rem;
text-align: right;
margin-right: .75rem;
}
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
/* .company-logo { display: none } */ /* .company-logo { display: none } */
@ -266,12 +254,21 @@
<table id="entity-details" cellspacing="0" dir="$dir"></table> <table id="entity-details" cellspacing="0" dir="$dir"></table>
</div> </div>
<table id="product-table" cellspacing="0"></table> <table id="product-table" cellspacing="0" data-ref="table"></table>
<table id="task-table" cellspacing="0"></table> <table id="task-table" cellspacing="0" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
<table id="delivery-note-table" cellspacing="0"></table>
<div id="table-totals" cellspacing="0"></div> <div id="table-totals" cellspacing="0"></div>
</div> </div>
@ -281,7 +278,12 @@
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -86,9 +86,7 @@
font-weight: normal; font-weight: normal;
} }
#product-table, [data-ref="table"] {
#delivery-note-table,
#task-table {
margin-top: 3rem; margin-top: 3rem;
/* margin-bottom: 200px; */ /* margin-bottom: 200px; */
min-width: 100%; min-width: 100%;
@ -102,15 +100,11 @@
color: grey; color: grey;
} }
#product-table > thead, [data-ref="table"] > thead {
#delivery-note-table > thead,
#task-table > thead {
text-align: left; text-align: left;
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
font-size: 1.1rem; font-size: 1.1rem;
padding-bottom: 1.5rem; padding-bottom: 1.5rem;
padding-left: 1rem; padding-left: 1rem;
@ -118,30 +112,21 @@
font-weight: bold; font-weight: bold;
} }
#product-table > thead > tr > th:last-child, [data-ref="table"] > thead > tr > th:last-child {
#delivery-note-table > thead > tr > th:last-child,
#task-table > thead > tr > th:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
border-bottom: 1pt solid; border-bottom: 1pt solid;
padding: 1rem; padding: 1rem;
} }
#product-table > tbody > tr:first-child > td, [data-ref="table"] > tbody > tr:first-child > td {
#delivery-note-table > tbody > tr:first-child > td,
#task-table > tbody > tr:first-child > td {
border-top: 1pt solid !important; border-top: 1pt solid !important;
padding: 1rem; padding: 1rem;
} }
[data-ref="table"] > tbody > tr > td:last-child {
#product-table > tbody > tr > td:last-child,
#delivery-note-table > tbody > tr > td:last-child,
#task-table > tbody > tr > td:last-child {
text-align: right; text-align: right;
} }
@ -206,17 +191,21 @@
#footer { #footer {
margin-top: 30px; margin-top: 30px;
} }
/** Markdown-specific styles. **/ /** Markdown-specific styles. **/
#product-table h3, [data-ref="table"] h3 {
#task-table h3,
#delivery-note-table h3 {
font-size: 1rem; font-size: 1rem;
margin-bottom: 0; margin-bottom: 0;
} }
[data-ref="statement-totals"] {
margin-top: 1rem;
text-align: right;
margin-right: .75rem;
}
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
/* .company-logo { display: none } */ /* .company-logo { display: none } */
@ -275,12 +264,21 @@
</div> </div>
</div> </div>
<table id="product-table" cellspacing="0"></table> <table id="product-table" cellspacing="0" data-ref="table"></table>
<table id="task-table" cellspacing="0"></table> <table id="task-table" cellspacing="0" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
<table id="delivery-note-table" cellspacing="0"></table>
<div id="table-totals" cellspacing="0"></div> <div id="table-totals" cellspacing="0"></div>
</div> </div>
@ -290,7 +288,12 @@
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -98,9 +98,7 @@
font-weight: bold; font-weight: bold;
} }
#product-table, [data-ref="table"] {
#delivery-note-table,
#task-table {
margin-top: 3rem; margin-top: 3rem;
/* margin-bottom: 200px; */ /* margin-bottom: 200px; */
min-width: 100%; min-width: 100%;
@ -114,39 +112,29 @@
color: grey; color: grey;
} }
#product-table > thead, [data-ref="table"] > thead {
#product-table > thead,
#task-table > thead {
text-align: left; text-align: left;
text-transform: uppercase; text-transform: uppercase;
font-weight: bold; font-weight: bold;
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
font-size: 1.1rem; font-size: 1.1rem;
padding-bottom: 1.5rem; padding-bottom: 1.5rem;
padding-left: 1rem; padding-left: 1rem;
border-left: 1px solid; border-left: 1px solid;
} }
#product-table > thead > tr > th:last-child, [data-ref="table"] > thead > tr > th:last-child {
#delivery-note-table > thead > tr > th:last-child,
#task-table > thead > tr > th:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
padding: 1rem; padding: 1rem;
border-left: 1px solid; border-left: 1px solid;
} }
#product-table > tbody > tr td:last-child, [data-ref="table"] > tbody > tr td:last-child {
#delivery-note-table > tbody > tr td:last-child,
#task-table > tbody > tr td:last-child {
text-align: right; text-align: right;
} }
@ -208,9 +196,7 @@
} }
/** Markdown-specific styles. **/ /** Markdown-specific styles. **/
#product-table h3, [data-ref="table"] h3 {
#task-table h3,
#delivery-note-table h3 {
font-size: 1rem; font-size: 1rem;
margin-bottom: 0; margin-bottom: 0;
} }
@ -229,8 +215,14 @@
[data-ref="totals_table-outstanding"] { color: var(--primary-color); } [data-ref="totals_table-outstanding"] { color: var(--primary-color); }
[data-ref="statement-totals"] {
margin-top: 1rem;
text-align: right;
margin-right: .75rem;
}
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
/* .company-logo { display: none } */ /* .company-logo { display: none } */
@ -294,17 +286,17 @@
</span> </span>
<span class="entity-property-value">$entity_number</span> <span class="entity-property-value">$entity_number</span>
</div> </div>
<div> <div>
<span class="entity-property-label">$date_label:</span> <span class="entity-property-label">$date_label:</span>
<span class="entity-property-value">$date</span> <span class="entity-property-value">$date</span>
</div> </div>
<div> <div>
<span class="entity-property-label">$payment_due_label:</span> <span class="entity-property-label">$payment_due_label:</span>
<span class="entity-property-value">$payment_due</span> <span class="entity-property-value">$payment_due</span>
</div> </div>
<div> <div>
<span class="entity-property-label">$amount_due_label:</span> <span class="entity-property-label">$amount_due_label:</span>
<span <span
@ -315,11 +307,20 @@
</div> </div>
</div> </div>
<table id="product-table" cellspacing="0"></table> <table id="product-table" cellspacing="0" data-ref="table"></table>
<table id="task-table" cellspacing="0"></table> <table id="task-table" cellspacing="0" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0"></table> <table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
<div id="table-totals" cellspacing="0"></div> <div id="table-totals" cellspacing="0"></div>
</div> </div>
@ -330,7 +331,12 @@
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -77,9 +77,7 @@
margin: 3rem 2rem; margin: 3rem 2rem;
} }
#product-table, [data-ref="table"] {
#delivery-note-table,
#task-table {
min-width: 100%; min-width: 100%;
table-layout: fixed; table-layout: fixed;
overflow-wrap: break-word; overflow-wrap: break-word;
@ -91,49 +89,35 @@
color: grey; color: grey;
} }
#product-table > thead, [data-ref="table"] > thead {
#delivery-note-table > thead,
#task-table > thead {
text-align: left; text-align: left;
width: 100%; width: 100%;
} }
#product-table th + th, [data-ref="table"] th + th {
#delivery-note-table th + th,
#task-table th + th {
border-left: 2px solid white; border-left: 2px solid white;
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
padding: 0.8rem; padding: 0.8rem;
background-color: var(--secondary-color); background-color: var(--secondary-color);
color: white; color: white;
} }
#product-table > thead > tr > th:last-child, [data-ref="table"] > thead > tr > th:last-child {
#delivery-note-table > thead > tr > th:last-child,
#task-table > thead > tr > th:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
border-bottom: 1px solid var(--secondary-color); border-bottom: 1px solid var(--secondary-color);
padding: 1rem; padding: 1rem;
} }
#product-table > tbody > tr > td:first-child, [data-ref="table"] > tbody > tr > td:first-child {
#delivery-note-table > tbody > tr > td:first-child,
#task-table > tbody > tr > td:first-child {
font-weight: bold; font-weight: bold;
} }
#product-table > tbody > tr > td:last-child, [data-ref="table"] > tbody > tr > td:last-child {
#delivery-note-table > tbody > tr > td:last-child,
#task-table > tbody > tr > td:last-child {
text-align: right; text-align: right;
} }
@ -282,6 +266,13 @@
width: 23%; width: 23%;
} }
[data-ref="statement-totals"] {
margin-top: 1rem;
margin-bottom: 1rem;
text-align: right;
margin-right: .75rem;
}
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
@ -339,9 +330,20 @@
<!-- Start Print Content --> <!-- Start Print Content -->
<div class="table-wrapper"> <div class="table-wrapper">
<table id="product-table" cellspacing="0" class="print-content"></table> <table id="product-table" cellspacing="0" class="print-content" data-ref="table"></table>
<table id="task-table" cellspacing="0" class="print-content"></table>
<table id="delivery-note-table" cellspacing="0" class="print-content"></table> <table id="task-table" cellspacing="0" class="print-content" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0" class="print-content" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" class="print-content" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" class="print-content" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" class="print-content" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
</div> </div>
<!-- End Print Content --> <!-- End Print Content -->
</td> </td>
@ -371,7 +373,12 @@
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -2,7 +2,7 @@
:root { :root {
--primary-color: $primary_color; --primary-color: $primary_color;
--secondary-color: $secondary_color; --secondary-color: $secondary_color;
--line-height: 1.6; --line-height: 1.6;
} }
body { body {
@ -68,9 +68,7 @@
line-height: var(--line-height); line-height: var(--line-height);
} }
#product-table, [data-ref="table"] {
#delivery-note-table,
#task-table {
min-width: 100%; min-width: 100%;
table-layout: fixed; table-layout: fixed;
overflow-wrap: break-word; overflow-wrap: break-word;
@ -84,35 +82,25 @@
color: grey; color: grey;
} }
#product-table > thead, [data-ref="table"] > thead {
#delivery-note-table > thead,
#task-table > thead {
text-align: left; text-align: left;
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
padding: 1rem; padding: 1rem;
background-color: #e6e6e6; background-color: #e6e6e6;
} }
#product-table > thead > tr > th:last-child, [data-ref="table"] > thead > tr > th:last-child {
#delivery-note-table > thead > tr > th:last-child,
#task-table > thead > tr > th:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
border-bottom: 1px solid #e6e6e6; border-bottom: 1px solid #e6e6e6;
padding: 1rem; padding: 1rem;
} }
#product-table > tbody > tr > td:last-child, [data-ref="table"] > tbody > tr > td:last-child {
#delivery-note-table > tbody > tr > td:last-child,
#task-table > tbody > tr > td:last-child {
text-align: right; text-align: right;
} }
@ -179,9 +167,7 @@
} }
/** Markdown-specific styles. **/ /** Markdown-specific styles. **/
#product-table h3, [data-ref="table"] h3 {
#task-table h3,
#delivery-note-table h3 {
font-size: 1rem; font-size: 1rem;
margin-bottom: 0; margin-bottom: 0;
} }
@ -195,8 +181,14 @@
padding-right: 7px; padding-right: 7px;
} }
[data-ref="statement-totals"] {
margin-top: 1rem;
text-align: right;
margin-right: .75rem;
}
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
/* .company-logo { display: none } */ /* .company-logo { display: none } */
@ -245,11 +237,20 @@
<div id="client-details"></div> <div id="client-details"></div>
<table id="product-table" cellspacing="0"></table> <table id="product-table" cellspacing="0" data-ref="table"></table>
<table id="task-table" cellspacing="0"></table> <table id="task-table" cellspacing="0" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0"></table> <table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
<div id="table-totals" cellspacing="0"></div> <div id="table-totals" cellspacing="0"></div>
</div> </div>
@ -260,7 +261,12 @@
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -95,9 +95,7 @@
border-bottom: 1px solid var(--primary-color); border-bottom: 1px solid var(--primary-color);
} }
#product-table, [data-ref="table"] {
#delivery-note-table,
#task-table {
padding-left: 3rem; padding-left: 3rem;
padding-right: 3rem; padding-right: 3rem;
margin-top: 3rem; margin-top: 3rem;
@ -113,58 +111,42 @@
color: grey; color: grey;
} }
#product-table > thead, [data-ref="table"] > thead {
#delivery-note-table > thead,
#task-table > thead {
text-align: left; text-align: left;
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
font-size: 1.2rem; font-size: 1.2rem;
padding: 1rem; padding: 1rem;
background: var(--primary-color); background: var(--primary-color);
color: white; color: white;
} }
#product-table > thead tr > th:last-child, [data-ref="table"] > thead tr > th:last-child {
#delivery-note-table > thead tr > th:last-child,
#task-table > thead tr > th:last-child {
text-align: right; text-align: right;
} }
#product-table > thead tr > th:first-child, [data-ref="table"] > thead tr > th:first-child {
#delivery-note-table > thead tr > th:first-child,
#task-table > thead tr > th:first-child {
border-top-left-radius: 10px; border-top-left-radius: 10px;
border-bottom-left-radius: 10px; border-bottom-left-radius: 10px;
} }
#product-table > thead tr > th:last-child, [data-ref="table"] > thead tr > th:last-child {
#delivery-note-table > thead tr > th:last-child,
#task-table > thead tr > th:last-child {
border-top-right-radius: 10px; border-top-right-radius: 10px;
border-bottom-right-radius: 10px; border-bottom-right-radius: 10px;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
background-color: #F7F7F7; background-color: #F7F7F7;
border-bottom: 1px solid var(--primary-color); border-bottom: 1px solid var(--primary-color);
padding: 1rem; padding: 1rem;
} }
#product-table > tbody > tr > td:first-child, [data-ref="table"] > tbody > tr > td:first-child {
#delivery-note-table > tbody > tr > td:first-child,
#task-table > tbody > tr > td:first-child {
color: var(--primary-color); color: var(--primary-color);
} }
#product-table > tbody > tr > td:last-child, [data-ref="table"] > tbody > tr > td:last-child {
#delivery-note-table > tbody > tr > td:last-child,
#task-table > tbody > tr > td:last-child {
text-align: right; text-align: right;
} }
@ -246,9 +228,7 @@
} }
/** Markdown-specific styles. **/ /** Markdown-specific styles. **/
#product-table h3, [data-ref="table"] h3 {
#task-table h3,
#delivery-note-table h3 {
font-size: 1rem; font-size: 1rem;
margin-bottom: 0; margin-bottom: 0;
} }
@ -259,15 +239,22 @@
min-width: 100%; min-width: 100%;
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
margin-left: -10px; margin-left: -10px;
} }
#footer-colors > * { #footer-colors > * {
padding: 10px; padding: 10px;
} }
[data-ref="statement-totals"] {
margin-top: 1rem;
text-align: right;
padding-left: 3rem;
padding-right: 3rem;
}
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
/* .company-logo { display: none } */ /* .company-logo { display: none } */
@ -333,11 +320,20 @@
</div> </div>
</div> </div>
<table id="product-table" cellspacing="0"></table> <table id="product-table" cellspacing="0" data-ref="table"></table>
<table id="task-table" cellspacing="0"></table> <table id="task-table" cellspacing="0" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0"></table> <table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
<div id="table-totals" cellspacing="0"></div> <div id="table-totals" cellspacing="0"></div>
</div> </div>
@ -359,7 +355,12 @@
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -122,18 +122,14 @@
.body-wrapper {} .body-wrapper {}
#product-table, [data-ref="table"] {
#delivery-note-table,
#task-table {
min-width: 100%; min-width: 100%;
table-layout: fixed; table-layout: fixed;
overflow-wrap: break-word; overflow-wrap: break-word;
margin-top: 3rem; margin-top: 3rem;
} }
#product-table > thead > tr > th, [data-ref="table"] > thead > tr > th {
#delivery-note-table > thead > tr > th,
#task-table > thead > tr > th {
text-transform: uppercase; text-transform: uppercase;
font-weight: normal; font-weight: normal;
padding: 1rem; padding: 1rem;
@ -142,28 +138,20 @@
font-size: 1.1rem; font-size: 1.1rem;
} }
#product-table > thead > tr > th:last-child, [data-ref="table"] > thead > tr > th:last-child {
#delivery-note-table > thead > tr > th:last-child,
#task-table > thead > tr > th:last-child {
text-align: right; text-align: right;
} }
#product-table > tbody > tr > td, [data-ref="table"] > tbody > tr > td {
#delivery-note-table > tbody > tr > td,
#task-table > tbody > tr > td {
border-bottom: 1px solid #e6e6e6; border-bottom: 1px solid #e6e6e6;
padding: 1rem; padding: 1rem;
} }
#product-table > tbody > tr > td:first-child, [data-ref="table"] > tbody > tr > td:first-child {
#delivery-note-table > tbody > tr > td:first-child,
#task-table > tbody > tr > td:first-child {
color: var(--primary-color); color: var(--primary-color);
} }
#product-table > tbody > tr > td:last-child, [data-ref="table"] > tbody > tr > td:last-child {
#delivery-note-table > tbody > tr > td:last-child,
#task-table > tbody > tr > td:last-child {
text-align: right; text-align: right;
} }
@ -229,15 +217,13 @@
} }
/** Markdown-specific styles. **/ /** Markdown-specific styles. **/
#product-table h3, [data-ref="table"] h3 {
#task-table h3,
#delivery-note-table h3 {
font-size: 1rem; font-size: 1rem;
margin-bottom: 0; margin-bottom: 0;
} }
/** Useful snippets, uncomment to enable. **/ /** Useful snippets, uncomment to enable. **/
/** Hide company logo **/ /** Hide company logo **/
/* .company-logo { display: none } */ /* .company-logo { display: none } */
@ -309,11 +295,20 @@
</div> </div>
<div class="body-wrapper"> <div class="body-wrapper">
<table id="product-table" cellspacing="0"></table> <table id="product-table" cellspacing="0" data-ref="table"></table>
<table id="task-table" cellspacing="0"></table> <table id="task-table" cellspacing="0" data-ref="table"></table>
<table id="delivery-note-table" cellspacing="0"></table> <table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
<table id="statement-invoice-table" cellspacing="0" data-ref="table"></table>
<div id="statement-invoice-table-totals" data-ref="statement-totals"></div>
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
<div id="table-totals" cellspacing="0"></div> <div id="table-totals" cellspacing="0"></div>
</div> </div>
@ -325,7 +320,12 @@
<script> <script>
// Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present. // Clear up space a bit, if [product-table, tasks-table, delivery-note-table] isn't present.
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
['product-table', 'task-table', 'delivery-note-table'].forEach((tableIdentifier) => { let tables = [
'product-table', 'task-table', 'delivery-note-table',
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
];
tables.forEach((tableIdentifier) => {
document.getElementById(tableIdentifier).childElementCount === 0 document.getElementById(tableIdentifier).childElementCount === 0
? document.getElementById(tableIdentifier).style.display = 'none' ? document.getElementById(tableIdentifier).style.display = 'none'
: ''; : '';

View File

@ -43,7 +43,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::post('connected_account', 'ConnectedAccountController@index'); Route::post('connected_account', 'ConnectedAccountController@index');
Route::post('connected_account/gmail', 'ConnectedAccountController@handleGmailOauth'); Route::post('connected_account/gmail', 'ConnectedAccountController@handleGmailOauth');
Route::resource('client_statement', 'ClientStatementController@statement'); // name = (client_statement. index / create / show / update / destroy / edit Route::post('client_statement', 'ClientStatementController@statement')->name('client.statement');
Route::post('companies/purge/{company}', 'MigrationController@purgeCompany')->middleware('password_protected'); Route::post('companies/purge/{company}', 'MigrationController@purgeCompany')->middleware('password_protected');
Route::post('companies/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected'); Route::post('companies/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected');