mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge pull request #6780 from joshuadwire/v5-develop
Fixes for Bold and Modern PDF designs
This commit is contained in:
commit
bed68743ec
@ -77,8 +77,6 @@ class PdfMaker
|
|||||||
$this->updateVariables($this->data['variables']);
|
$this->updateVariables($this->data['variables']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->processOptions();
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,144 +163,6 @@ trait PdfMakerUtilities
|
|||||||
return $element;
|
return $element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processOptions()
|
|
||||||
{
|
|
||||||
if (!isset($this->options['all_pages_header']) || $this->options['all_pages_header'] == false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($this->options['all_pages_footer']) || $this->options['all_pages_footer'] == false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->insertPrintCSS();
|
|
||||||
$this->wrapIntoTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function insertPrintCSS()
|
|
||||||
{
|
|
||||||
$css = <<<'EOT'
|
|
||||||
table.page-container {
|
|
||||||
page-break-after: always;
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead.page-header {
|
|
||||||
display: table-header-group;
|
|
||||||
}
|
|
||||||
|
|
||||||
tfoot.page-footer {
|
|
||||||
display: table-footer-group;
|
|
||||||
}
|
|
||||||
EOT;
|
|
||||||
|
|
||||||
$css_node = $this->document->createTextNode($css);
|
|
||||||
|
|
||||||
$style = $this->document->getElementsByTagName('style')->item(0);
|
|
||||||
|
|
||||||
if ($style) {
|
|
||||||
return $style->appendChild($css_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
$head = $this->document->getElementsByTagName('head')->item(0);
|
|
||||||
|
|
||||||
if ($head) {
|
|
||||||
$style_node = $this->document->createElement('style', $css);
|
|
||||||
|
|
||||||
return $head->appendChild($style_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function wrapIntoTable()
|
|
||||||
{
|
|
||||||
$markup = <<<'EOT'
|
|
||||||
<table class="page-container" id="page-container">
|
|
||||||
<thead class="page-report">
|
|
||||||
<tr>
|
|
||||||
<th class="page-report-cell" id="repeat-header">
|
|
||||||
<!-- Repeating header goes here.. -->
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tfoot class="report-footer">
|
|
||||||
<tr>
|
|
||||||
<td class="report-footer-cell" id="repeat-footer">
|
|
||||||
<!-- Repeating footer goes here -->
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
<tbody class="report-content">
|
|
||||||
<tr>
|
|
||||||
<td class="report-content-cell" id="repeat-content">
|
|
||||||
<!-- Rest of the content goes here -->
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
EOT;
|
|
||||||
|
|
||||||
$document = new DOMDocument();
|
|
||||||
$document->loadHTML($markup);
|
|
||||||
|
|
||||||
$table = $document->getElementById('page-container');
|
|
||||||
|
|
||||||
$body = $this->document->getElementsByTagName('body')
|
|
||||||
->item(0);
|
|
||||||
|
|
||||||
$body->appendChild(
|
|
||||||
$this->document->importNode($table, true)
|
|
||||||
);
|
|
||||||
|
|
||||||
for ($i = 0; $i < $body->childNodes->length; $i++) {
|
|
||||||
$element = $body->childNodes->item($i);
|
|
||||||
|
|
||||||
if ($element->nodeType !== 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
$element->getAttribute('id') == 'header' ||
|
|
||||||
$element->getAttribute('id') == 'footer' ||
|
|
||||||
$element->getAttribute('id') === 'page-container'
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$clone = $element->cloneNode(true);
|
|
||||||
$element->parentNode->removeChild($element);
|
|
||||||
|
|
||||||
$this->document->getElementById('repeat-content')->appendChild($clone);
|
|
||||||
}
|
|
||||||
|
|
||||||
// info($this->data['options']);
|
|
||||||
|
|
||||||
if (
|
|
||||||
$header = $this->document->getElementById('header') &&
|
|
||||||
isset($this->data['options']['all_pages_header']) &&
|
|
||||||
$this->data['options']['all_pages_header']
|
|
||||||
) {
|
|
||||||
$header = $this->document->getElementById('header');
|
|
||||||
$clone = $header->cloneNode(true);
|
|
||||||
|
|
||||||
$header->parentNode->removeChild($header);
|
|
||||||
$this->document->getElementById('repeat-header')->appendChild($clone);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
$footer = $this->document->getElementById('footer') &&
|
|
||||||
isset($this->data['options']['all_pages_footer']) &&
|
|
||||||
$this->data['options']['all_pages_footer']
|
|
||||||
) {
|
|
||||||
$footer = $this->document->getElementById('footer');
|
|
||||||
$clone = $footer->cloneNode(true);
|
|
||||||
|
|
||||||
$footer->parentNode->removeChild($footer);
|
|
||||||
$this->document->getElementById('repeat-footer')->appendChild($clone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEmptyElements(array &$elements, array $variables)
|
public function getEmptyElements(array &$elements, array $variables)
|
||||||
{
|
{
|
||||||
foreach ($elements as &$element) {
|
foreach ($elements as &$element) {
|
||||||
|
@ -83,10 +83,19 @@ class Phantom
|
|||||||
info($url);
|
info($url);
|
||||||
|
|
||||||
$key = config( 'ninja.phantomjs_key' );
|
$key = config( 'ninja.phantomjs_key' );
|
||||||
$secret = config('ninja.phantomjs_key');
|
$phantom_url = "https://phantomjscloud.com/api/browser/v2/{$key}/";
|
||||||
|
$pdf = CurlUtils::post( $phantom_url, json_encode( [
|
||||||
$phantom_url = "https://phantomjscloud.com/api/browser/v2/{$key}/?request=%7Burl:%22{$url}%22,renderType:%22pdf%22%7D";
|
'url' => $url,
|
||||||
$pdf = CurlUtils::get($phantom_url);
|
'renderType' => 'pdf',
|
||||||
|
'outputAsJson' => false,
|
||||||
|
'renderSettings' => [
|
||||||
|
'emulateMedia' => 'print',
|
||||||
|
'pdfOptions' => [
|
||||||
|
'preferCSSPageSize' => true,
|
||||||
|
'printBackground' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
] ) );
|
||||||
|
|
||||||
$this->checkMime($pdf, $invitation, $entity);
|
$this->checkMime($pdf, $invitation, $entity);
|
||||||
|
|
||||||
@ -100,14 +109,20 @@ class Phantom
|
|||||||
|
|
||||||
public function convertHtmlToPdf($html)
|
public function convertHtmlToPdf($html)
|
||||||
{
|
{
|
||||||
$hash = Str::random(32);
|
|
||||||
Cache::put($hash, $html, 300);
|
|
||||||
|
|
||||||
$url = route('tmp_pdf', ['hash' => $hash]);
|
|
||||||
info($url);
|
|
||||||
$key = config( 'ninja.phantomjs_key' );
|
$key = config( 'ninja.phantomjs_key' );
|
||||||
$phantom_url = "https://phantomjscloud.com/api/browser/v2/{$key}/?request=%7Burl:%22{$url}%22,renderType:%22pdf%22%7D";
|
$phantom_url = "https://phantomjscloud.com/api/browser/v2/{$key}/";
|
||||||
$pdf = CurlUtils::get($phantom_url);
|
$pdf = CurlUtils::post( $phantom_url, json_encode( [
|
||||||
|
'content' => $html,
|
||||||
|
'renderType' => 'pdf',
|
||||||
|
'outputAsJson' => false,
|
||||||
|
'renderSettings' => [
|
||||||
|
'emulateMedia' => 'print',
|
||||||
|
'pdfOptions' => [
|
||||||
|
'preferCSSPageSize' => true,
|
||||||
|
'printBackground' => true,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
] ) );
|
||||||
|
|
||||||
$response = Response::make($pdf, 200);
|
$response = Response::make($pdf, 200);
|
||||||
$response->header('Content-Type', 'application/pdf');
|
$response->header('Content-Type', 'application/pdf');
|
||||||
|
@ -13,31 +13,51 @@
|
|||||||
zoom: 80%;
|
zoom: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
@page {
|
@page {
|
||||||
margin: -0.25cm !important;
|
margin: 0 !important;
|
||||||
size: $page_size $page_layout;
|
size: $page_size $page_layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
/* page-break-after: always; */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-wrapper {
|
#spacer-table > * > tr > td {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#spacer-table{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.5fr 1fr 1fr;
|
grid-template-columns: 1.5fr 1fr 1fr;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
background-color: #2d2c2a;
|
background-color: #2d2c2a;
|
||||||
padding: 3rem;
|
|
||||||
color: white;
|
color: white;
|
||||||
min-width: 100%;
|
|
||||||
line-height: var(--line-height);
|
line-height: var(--line-height);
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#header, #header-spacer {
|
||||||
|
height: 160px;
|
||||||
|
padding: 3rem;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
}
|
||||||
.company-logo {
|
.company-logo {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-right: 120px;
|
max-width: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
object-position: left center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#company-details,
|
#company-details,
|
||||||
@ -46,6 +66,12 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#company-details,
|
||||||
|
#company-address,
|
||||||
|
.logo-container {
|
||||||
|
max-height: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
#client-details {
|
#client-details {
|
||||||
margin: 2rem;
|
margin: 2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -61,7 +87,6 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.5fr 1fr;
|
grid-template-columns: 1.5fr 1fr;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
padding-top: 3rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.entity-details-wrapper {
|
.entity-details-wrapper {
|
||||||
@ -86,7 +111,11 @@
|
|||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
margin-top: 3rem;
|
margin-top: 3rem;
|
||||||
/* margin-bottom: 200px; */
|
margin-bottom: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-ref="table"]:last-child{
|
||||||
|
margin-bottom:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-time-details {
|
.task-time-details {
|
||||||
@ -186,51 +215,40 @@
|
|||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-wrapper {
|
#footer {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
background-color: #2d2c2a;
|
background-color: #2d2c2a;
|
||||||
height: 160px;
|
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
padding: 1rem 3rem;
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#footer, #footer-spacer {
|
||||||
|
height: 160px;
|
||||||
|
padding: 1rem 3rem;
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
[data-ref="total_table-footer"] {
|
[data-ref="total_table-footer"] {
|
||||||
padding-top: 0.5rem
|
padding-top: 0.5rem
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Repeating header & footer styling. */
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.print-content {}
|
table[data-ref="table"] th,
|
||||||
|
table[data-ref="table"] td {
|
||||||
table.print-content th,
|
|
||||||
table.print-content td {
|
|
||||||
padding: .2rem .4rem;
|
padding: .2rem .4rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
border-top: 1px solid #dee2e6;
|
border-top: 1px solid #dee2e6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
|
||||||
.print-footer {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-print {
|
|
||||||
display: none
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Markdown-specific styles. **/
|
/** Markdown-specific styles. **/
|
||||||
#product-table h3,
|
#product-table h3,
|
||||||
#task-table h3,
|
#task-table h3,
|
||||||
@ -253,10 +271,6 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo-container {
|
|
||||||
max-height: 160px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#statement-invoice-table-totals > p {
|
#statement-invoice-table-totals > p {
|
||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
@ -292,24 +306,25 @@
|
|||||||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<table>
|
<div id="header">
|
||||||
<!-- Start Header -->
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="header-wrapper" id="header">
|
|
||||||
<div class="logo-container">
|
<div class="logo-container">
|
||||||
<img class="company-logo" src="$company.logo" alt="$company.name logo"/>
|
<img class="company-logo" src="$company.logo" alt="$company.name logo"/>
|
||||||
</div>
|
</div>
|
||||||
<div id="company-details"></div>
|
<div id="company-details"></div>
|
||||||
<div id="company-address"></div>
|
<div id="company-address"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="body">
|
||||||
|
<table id="spacer-table" cellspacing="0" >
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div id="header-spacer"></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<!-- End Header -->
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td id="body">
|
<td>
|
||||||
<div class="client-entity-wrapper">
|
<div class="client-entity-wrapper">
|
||||||
<div class="client-wrapper-left-side">
|
<div class="client-wrapper-left-side">
|
||||||
<h4 class="entity-label">$entity_label</h4>
|
<h4 class="entity-label">$entity_label</h4>
|
||||||
@ -323,42 +338,34 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Start Print Content -->
|
<table id="product-table" cellspacing="0" data-ref="table"></table>
|
||||||
<table id="product-table" cellspacing="0" class="print-content" data-ref="table"></table>
|
|
||||||
|
|
||||||
<table id="task-table" cellspacing="0" class="print-content" data-ref="table"></table>
|
<table id="task-table" cellspacing="0" data-ref="table"></table>
|
||||||
|
|
||||||
<table id="delivery-note-table" cellspacing="0" class="print-content" data-ref="table"></table>
|
<table id="delivery-note-table" cellspacing="0" data-ref="table"></table>
|
||||||
|
|
||||||
<table id="statement-invoice-table" cellspacing="0" class="print-content" 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>
|
<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>
|
<table id="statement-payment-table" cellspacing="0" data-ref="table"></table>
|
||||||
<div id="statement-payment-table-totals" data-ref="statement-totals"></div>
|
<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>
|
<table id="statement-aging-table" cellspacing="0" data-ref="table"></table>
|
||||||
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
<div id="statement-aging-table-totals" data-ref="statement-totals"></div>
|
||||||
<!-- End Print Content -->
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div id="table-totals" cellspacing="0"></div>
|
<div id="table-totals" cellspacing="0"></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- Start Space For Footer -->
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="height: 180px">
|
<td>
|
||||||
<!-- Leave this empty and don't remove it. This space is where footer placed on print. -->
|
<div id="footer-spacer"></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
<!-- End Space For Footer -->
|
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
<!-- Start Footer -->
|
<div id="footer">
|
||||||
<div class="footer-wrapper print-footer" id="footer">
|
|
||||||
<div>
|
<div>
|
||||||
<p data-ref="total_table-footer">$entity_footer</p>
|
<p data-ref="total_table-footer">$entity_footer</p>
|
||||||
|
|
||||||
@ -366,14 +373,13 @@
|
|||||||
// 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', () => {
|
||||||
let tables = [
|
let tables = [
|
||||||
'product-table', 'task-table', 'delivery-note-table',
|
'product-table', 'task-table', 'delivery-note-table', 'statement-invoice-table-totals', 'statement-payment-table-totals','statement-invoice-table-totals',
|
||||||
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
|
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table', 'statement-aging-table-totals', 'statement-payment-table-totals'
|
||||||
];
|
];
|
||||||
|
|
||||||
tables.forEach((tableIdentifier) => {
|
tables.forEach((tableIdentifier) => {
|
||||||
document.getElementById(tableIdentifier).childElementCount === 0
|
const el =document.getElementById(tableIdentifier);
|
||||||
? document.getElementById(tableIdentifier).style.display = 'none'
|
if(el && el.childElementCount === 0)el.remove()
|
||||||
: '';
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -381,4 +387,3 @@
|
|||||||
<div> <!-- #2 column --> </div>
|
<div> <!-- #2 column --> </div>
|
||||||
<div> <!-- #3 column --> </div>
|
<div> <!-- #3 column --> </div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Footer -->
|
|
||||||
|
@ -9,37 +9,53 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
font-size: "7px";
|
font-size: "7px";
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
zoom: 80%;
|
zoom: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body, html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
@page {
|
@page {
|
||||||
margin: -0.22cm;
|
margin: 0 !important;
|
||||||
size: A4 portrait;
|
size: $page_size $page_layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
/* page-break-after: always; */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-container {
|
#spacer-table > * > tr > td {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#spacer-table{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header {
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
color: white;
|
color: white;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.5fr 1fr;
|
grid-template-columns: 1.5fr 1fr;
|
||||||
padding: 3rem;
|
position:fixed;
|
||||||
min-width: 100%;
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header, #header-spacer {
|
||||||
height: 160px;
|
height: 160px;
|
||||||
|
padding: 3rem;
|
||||||
|
margin-bottom: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.company-name {
|
.company-name {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-container .company-name {
|
#header .company-name {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +71,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logo-client-wrapper {
|
.logo-client-wrapper {
|
||||||
margin: 3rem 2rem;
|
margin: 0 2rem 3rem;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1.5fr 1fr;
|
grid-template-columns: 1.5fr 1fr;
|
||||||
}
|
}
|
||||||
@ -121,17 +137,20 @@
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-wrapper {
|
#footer {
|
||||||
margin-top: 1rem;
|
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
padding-left: 3rem;
|
|
||||||
padding-right: 3rem;
|
|
||||||
height: 220px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#footer, #footer-spacer {
|
||||||
|
height: 220px;
|
||||||
|
padding: 1rem 3rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.footer-content {
|
.footer-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
@ -204,56 +223,23 @@
|
|||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.page-container {
|
|
||||||
page-break-after: always;
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead.page-header {
|
|
||||||
display: table-header-group;
|
|
||||||
}
|
|
||||||
|
|
||||||
tfoot.page-footer {
|
|
||||||
display: table-footer-group;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-content-cell {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-ref="total_table-footer"] {
|
[data-ref="total_table-footer"] {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Repeating header & footer styling. */
|
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.print-content {
|
table[data-ref="table"] th,
|
||||||
}
|
table[data-ref="table"] td {
|
||||||
|
|
||||||
table.print-content th,
|
|
||||||
table.print-content td {
|
|
||||||
padding: 0.2rem 0.4rem;
|
padding: 0.2rem 0.4rem;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
border-top: 1px solid #dee2e6;
|
border-top: 1px solid #dee2e6;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
|
||||||
.print-footer {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-print {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Markdown-specific styles. **/
|
/** Markdown-specific styles. **/
|
||||||
#product-table h3,
|
#product-table h3,
|
||||||
#task-table h3,
|
#task-table h3,
|
||||||
@ -306,70 +292,57 @@
|
|||||||
/** For more info, please check our docs: https://invoiceninja.github.io **/
|
/** For more info, please check our docs: https://invoiceninja.github.io **/
|
||||||
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
/** To find out selectors on your own: https://invoiceninja.github.io/docs/custom-fields/#snippets **/
|
||||||
</style>
|
</style>
|
||||||
|
<div id="header">
|
||||||
<table>
|
|
||||||
<!-- Start Header -->
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<div class="header-container" id="header">
|
|
||||||
<h1 class="company-name">$company.name</h1>
|
<h1 class="company-name">$company.name</h1>
|
||||||
<table id="entity-details" cellspacing="0" dir="$dir"></table>
|
<table id="entity-details" cellspacing="0" dir="$dir"></table>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="body">
|
||||||
|
<table id="spacer-table" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div id="header-spacer"></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<!-- End Header -->
|
<tbody>
|
||||||
<tr>
|
|
||||||
<td id="body">
|
|
||||||
<div class="logo-client-wrapper">
|
|
||||||
<img
|
|
||||||
class="company-logo"
|
|
||||||
src="$company.logo"
|
|
||||||
alt="$company.name logo"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div id="client-details"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Start Print Content -->
|
|
||||||
<div class="table-wrapper">
|
|
||||||
<table id="product-table" cellspacing="0" class="print-content" data-ref="table"></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>
|
|
||||||
<!-- End Print Content -->
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
<div class="logo-client-wrapper">
|
||||||
|
<img class="company-logo" src="$company.logo" alt="$company.name logo"/>
|
||||||
|
<div id="client-details"></div>
|
||||||
|
</div>
|
||||||
|
<div class="table-wrapper">
|
||||||
|
<table id="product-table" cellspacing="0" data-ref="table"></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>
|
||||||
|
</div>
|
||||||
<div id="table-totals" cellspacing="0"></div>
|
<div id="table-totals" cellspacing="0"></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- Start Space For Footer -->
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="height: 230px">
|
<td>
|
||||||
<!-- Leave this empty and don't remove it. This space is where footer placed on print -->
|
<div id="footer-spacer"></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
<!-- End Space For Footer -->
|
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Start Footer -->
|
<div id="footer">
|
||||||
<div class="footer-wrapper print-footer" id="footer">
|
|
||||||
<div class="footer-content">
|
<div class="footer-content">
|
||||||
<div>
|
<div>
|
||||||
<p data-ref="total_table-footer">$entity_footer</p>
|
<p data-ref="total_table-footer">$entity_footer</p>
|
||||||
@ -378,13 +351,13 @@
|
|||||||
// 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', () => {
|
||||||
let tables = [
|
let tables = [
|
||||||
'product-table', 'task-table', 'delivery-note-table',
|
'product-table', 'task-table', 'delivery-note-table', 'statement-invoice-table-totals', 'statement-payment-table-totals','statement-invoice-table-totals',
|
||||||
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table-totals',
|
'statement-invoice-table', 'statement-payment-table', 'statement-aging-table', 'statement-aging-table-totals', 'statement-payment-table-totals'
|
||||||
];
|
];
|
||||||
|
|
||||||
tables.forEach((tableIdentifier) => {
|
tables.forEach((tableIdentifier) => {
|
||||||
document.getElementById(tableIdentifier).childElementCount === 0
|
document.getElementById(tableIdentifier).childElementCount === 0
|
||||||
? document.getElementById(tableIdentifier).style.display = 'none'
|
? document.getElementById(tableIdentifier).remove()
|
||||||
: '';
|
: '';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -396,4 +369,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- End Footer -->
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user