PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.1
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.1
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / templates / pdf / document.php

document.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.1, at templates/pdf/document.php

333 lines 15.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Shared invoice/quote layout for server-side PDF rendering.
4 *
5 * Receives from PdfRenderer::buildHtml(), via invoice.php or quote.php:
6 * $invoice — Invoice or Quote model (named for the invoice case, which came first)
7 * $formatter — currency formatter with format()
8 * $ei_pdf_type — 'invoice' or 'quote'
9 *
10 * One body for both documents. The plugin already carries ~6,500 lines of
11 * near-identical parallel invoice and quote templates, and every fix to one of
12 * them is a fix someone forgets to make to the other. The two differ only in
13 * four labels and which date they show, so they differ here in four labels and
14 * which date they show.
15 *
16 * Laid out with tables on purpose. dompdf implements CSS 2.1 and ignores
17 * `display: flex`, so the flexbox designs in templates/invoice-templates/
18 * collapse into stacked rows when rendered here. Tables are the one layout
19 * primitive every PDF engine agrees on.
20 *
21 * Override with the `easy_invoice_pdf_template_path` filter.
22 *
23 * @package Easy_Invoice
24 */
25
26 if ( ! defined( 'ABSPATH' ) ) {
27 exit;
28 }
29
30 $ei_pdf_type = ( isset( $ei_pdf_type ) && 'quote' === $ei_pdf_type ) ? 'quote' : 'invoice';
31 $ei_is_quote = 'quote' === $ei_pdf_type;
32 $ei_is_credit = ! $ei_is_quote && \EasyInvoice\Helpers\TemplateTextHelper::isCreditNote( $invoice );
33
34 $text_settings = $ei_is_quote
35 ? \EasyInvoice\Helpers\TemplateTextHelper::getQuoteTextSettings()
36 : \EasyInvoice\Helpers\TemplateTextHelper::getInvoiceTextSettings();
37 $company_info = \EasyInvoice\Helpers\TemplateTextHelper::getCompanyInfo();
38
39 $t = static function ( $key, $fallback ) use ( $text_settings ) {
40 return ! empty( $text_settings[ $key ] ) ? $text_settings[ $key ] : $fallback;
41 };
42
43 $items = is_callable( [ $invoice, 'getItems' ] ) ? (array) $invoice->getItems() : [];
44 $subtotal = is_callable( [ $invoice, 'getSubtotal' ] ) ? (float) $invoice->getSubtotal() : 0.0;
45 $tax = is_callable( [ $invoice, 'getTaxAmount' ] ) ? (float) $invoice->getTaxAmount() : 0.0;
46 $discount = is_callable( [ $invoice, 'getDiscountAmount' ] ) ? (float) $invoice->getDiscountAmount() : 0.0;
47 $total = is_callable( [ $invoice, 'getTotal' ] ) ? (float) $invoice->getTotal() : 0.0;
48 ?>
49 <!DOCTYPE html>
50 <html>
51 <head>
52 <meta charset="utf-8">
53 <style>
54 @page { margin: 18mm 15mm; }
55
56 body {
57 font-family: "DejaVu Sans", sans-serif;
58 font-size: 10pt;
59 line-height: 1.45;
60 color: #222222;
61 margin: 0;
62 }
63
64 table { width: 100%; border-collapse: collapse; }
65 td, th { vertical-align: top; }
66
67 /* Masthead: company on the left, document meta on the right. A table,
68 because this is exactly the two-column arrangement flex would have
69 given us on screen and cannot give us here. */
70 .masthead td { padding: 0 0 18pt 0; }
71 .masthead .company-name { font-size: 15pt; font-weight: bold; color: #111111; }
72 .masthead .company-detail { font-size: 8.5pt; color: #555555; }
73 .masthead .doc-type {
74 font-size: 20pt; font-weight: bold; color: #111111;
75 text-align: right; letter-spacing: 1pt;
76 }
77 .masthead .doc-meta { text-align: right; font-size: 9pt; color: #555555; }
78 .masthead .doc-meta strong { color: #222222; }
79
80 .rule { border-bottom: 1px solid #d8d8d8; height: 1px; margin-bottom: 14pt; }
81
82 .parties td { padding-bottom: 16pt; width: 50%; }
83 .label {
84 font-size: 7.5pt; text-transform: uppercase; letter-spacing: 0.8pt;
85 color: #888888; padding-bottom: 3pt;
86 }
87 .party-name { font-weight: bold; font-size: 10.5pt; }
88 .party-detail { font-size: 9pt; color: #555555; }
89
90 /* Line items */
91 .items { margin-bottom: 14pt; }
92 .items thead th {
93 font-size: 7.5pt; text-transform: uppercase; letter-spacing: 0.6pt;
94 color: #666666; text-align: left; font-weight: normal;
95 border-bottom: 1px solid #cccccc; padding: 0 6pt 6pt 0;
96 }
97 .items tbody td {
98 padding: 7pt 6pt 7pt 0;
99 border-bottom: 1px solid #eeeeee;
100 font-size: 9.5pt;
101 }
102 .items .num { text-align: right; padding-right: 0; }
103 .items .item-name { font-weight: bold; }
104 .items .item-desc { font-size: 8.5pt; color: #666666; }
105
106 /* Totals sit in a right-aligned block; a nested table keeps the
107 label/value pairs aligned without floats, which dompdf handles poorly. */
108 .totals { width: 46%; margin-left: 54%; }
109 .totals td { padding: 4pt 0; font-size: 9.5pt; }
110 .totals td.v { text-align: right; }
111 .totals tr.grand td {
112 border-top: 1.5px solid #222222;
113 padding-top: 7pt; font-size: 12pt; font-weight: bold; color: #111111;
114 }
115
116 .blocks { margin-top: 20pt; }
117 .blocks td { padding-bottom: 12pt; font-size: 8.8pt; color: #555555; }
118 .blocks .label { color: #888888; }
119
120 .footer {
121 margin-top: 24pt; padding-top: 10pt;
122 border-top: 1px solid #e4e4e4;
123 font-size: 8pt; color: #888888; text-align: center;
124 }
125 </style>
126 </head>
127 <body>
128
129 <table class="masthead">
130 <tr>
131 <td style="width:55%">
132 <?php if ( ! empty( $company_info['logo'] ) ) : ?>
133 <img src="<?php echo esc_url( $company_info['logo'] ); ?>" style="max-height:52pt; margin-bottom:6pt;" alt="">
134 <br>
135 <?php endif; ?>
136 <?php if ( ! empty( $company_info['name'] ) ) : ?>
137 <div class="company-name"><?php echo esc_html( $company_info['name'] ); ?></div>
138 <?php endif; ?>
139 <?php foreach ( [ 'address', 'email', 'phone', 'website' ] as $field ) : ?>
140 <?php if ( ! empty( $company_info[ $field ] ) ) : ?>
141 <div class="company-detail"><?php echo nl2br( esc_html( $company_info[ $field ] ) ); ?></div>
142 <?php endif; ?>
143 <?php endforeach; ?>
144 <?php if ( ! empty( $company_info['tax_number'] ) ) : ?>
145 <div class="company-detail"><?php echo esc_html( $company_info['tax_number'] ); ?></div>
146 <?php endif; ?>
147 </td>
148 <td style="width:45%">
149 <div class="doc-type"><?php
150 echo esc_html( $ei_is_quote
151 ? $t( 'quote_title', __( 'QUOTE', 'easy-invoice' ) )
152 : ( $ei_is_credit ? __( 'CREDIT NOTE', 'easy-invoice' ) : $t( 'invoice_title', __( 'INVOICE', 'easy-invoice' ) ) ) );
153 ?></div>
154 <div class="doc-meta">
155 <?php if ( is_callable( [ $invoice, 'getNumber' ] ) && $invoice->getNumber() ) : ?>
156 <div><?php echo esc_html( $ei_is_quote
157 ? $t( 'quote_number', __( 'Quote No', 'easy-invoice' ) )
158 : ( $ei_is_credit ? __( 'Credit Note No', 'easy-invoice' ) : $t( 'invoice_number', __( 'Invoice No', 'easy-invoice' ) ) ) ); ?>:
159 <strong><?php echo esc_html( $invoice->getNumber() ); ?></strong></div>
160 <?php endif; ?>
161 <?php if ( is_callable( [ $invoice, 'getIssueDate' ] ) && $invoice->getIssueDate() ) : ?>
162 <div><?php echo esc_html( $ei_is_credit ? __( 'Date', 'easy-invoice' ) : $t( 'invoice_date', __( 'Date', 'easy-invoice' ) ) ); ?>:
163 <strong><?php echo esc_html( $invoice->getIssueDate() ); ?></strong></div>
164 <?php endif; ?>
165 <?php
166 // A quote expires; an invoice falls due. Different field, different word.
167 $ei_end_date = $ei_is_quote
168 ? ( is_callable( [ $invoice, 'getExpiryDate' ] ) ? $invoice->getExpiryDate() : '' )
169 : ( ( ! $ei_is_credit && is_callable( [ $invoice, 'getDueDate' ] ) ) ? $invoice->getDueDate() : '' );
170 ?>
171 <?php if ( $ei_end_date ) : ?>
172 <div><?php echo esc_html( $ei_is_quote
173 ? $t( 'valid_until', __( 'Valid until', 'easy-invoice' ) )
174 : $t( 'due_date', __( 'Due', 'easy-invoice' ) ) ); ?>:
175 <strong><?php echo esc_html( $ei_end_date ); ?></strong></div>
176 <?php endif; ?>
177 </div>
178 </td>
179 </tr>
180 </table>
181
182 <div class="rule"></div>
183
184 <table class="parties">
185 <tr>
186 <td>
187 <div class="label"><?php echo esc_html( $t( 'to', $ei_is_quote
188 ? __( 'Quote for', 'easy-invoice' )
189 : __( 'Bill to', 'easy-invoice' ) ) ); ?></div>
190 <?php if ( is_callable( [ $invoice, 'getCustomerName' ] ) && $invoice->getCustomerName() ) : ?>
191 <div class="party-name"><?php echo esc_html( $invoice->getCustomerName() ); ?></div>
192 <?php endif; ?>
193 <?php if ( is_callable( [ $invoice, 'getCustomerAddress' ] ) && $invoice->getCustomerAddress() ) : ?>
194 <div class="party-detail"><?php echo nl2br( esc_html( $invoice->getCustomerAddress() ) ); ?></div>
195 <?php endif; ?>
196 <?php if ( is_callable( [ $invoice, 'getCustomerEmail' ] ) && $invoice->getCustomerEmail() ) : ?>
197 <div class="party-detail"><?php echo esc_html( $invoice->getCustomerEmail() ); ?></div>
198 <?php endif; ?>
199 </td>
200 <td>
201 <?php if ( is_callable( [ $invoice, 'getTitle' ] ) && $invoice->getTitle() ) : ?>
202 <div class="label"><?php esc_html_e( 'Reference', 'easy-invoice' ); ?></div>
203 <div class="party-detail"><?php echo esc_html( $invoice->getTitle() ); ?></div>
204 <?php endif; ?>
205 </td>
206 </tr>
207 </table>
208
209 <table class="items">
210 <thead>
211 <tr>
212 <th style="width:52%"><?php echo esc_html( $t( 'service', __( 'Description', 'easy-invoice' ) ) ); ?></th>
213 <th style="width:12%" class="num"><?php echo esc_html( $t( 'qty', __( 'Qty', 'easy-invoice' ) ) ); ?></th>
214 <th style="width:18%" class="num"><?php echo esc_html( $t( 'rate_price', __( 'Rate', 'easy-invoice' ) ) ); ?></th>
215 <th style="width:18%" class="num"><?php echo esc_html( $t( 'line_total', $t( 'total', __( 'Amount', 'easy-invoice' ) ) ) ); ?></th>
216 </tr>
217 </thead>
218 <tbody>
219 <?php if ( empty( $items ) ) : ?>
220 <tr><td colspan="4" style="color:#888888"><?php esc_html_e( 'No line items.', 'easy-invoice' ); ?></td></tr>
221 <?php else : ?>
222 <?php foreach ( $items as $item ) :
223 $name = is_callable( [ $item, 'getName' ] ) ? (string) $item->getName() : '';
224 $desc = is_callable( [ $item, 'getDescription' ] ) ? (string) $item->getDescription() : '';
225 $qty = is_callable( [ $item, 'getQuantity' ] ) ? (float) $item->getQuantity() : 0.0;
226 $rate = is_callable( [ $item, 'getPrice' ] ) ? (float) $item->getPrice() : 0.0;
227 $amt = is_callable( [ $item, 'getAmount' ] ) ? (float) $item->getAmount() : ( $qty * $rate );
228 ?>
229 <tr>
230 <td>
231 <?php if ( $name !== '' ) : ?><div class="item-name"><?php echo esc_html( $name ); ?></div><?php endif; ?>
232 <?php if ( $desc !== '' ) : ?><div class="item-desc"><?php echo nl2br( esc_html( $desc ) ); ?></div><?php endif; ?>
233 </td>
234 <td class="num"><?php echo esc_html( rtrim( rtrim( number_format( $qty, 2 ), '0' ), '.' ) ); ?></td>
235 <td class="num"><?php echo esc_html( $formatter->format( $rate ) ); ?></td>
236 <td class="num"><?php echo esc_html( $formatter->format( $amt ) ); ?></td>
237 </tr>
238 <?php endforeach; ?>
239 <?php endif; ?>
240 </tbody>
241 </table>
242
243 <table class="totals">
244 <tr>
245 <td><?php echo esc_html( $t( 'sub_total', __( 'Subtotal', 'easy-invoice' ) ) ); ?></td>
246 <td class="v"><?php echo esc_html( $formatter->format( $subtotal ) ); ?></td>
247 </tr>
248 <?php if ( $discount > 0 ) : ?>
249 <tr>
250 <td><?php echo esc_html( $t( 'discount', __( 'Discount', 'easy-invoice' ) ) ); ?></td>
251 <td class="v">-<?php echo esc_html( $formatter->format( $discount ) ); ?></td>
252 </tr>
253 <?php endif; ?>
254 <?php if ( $tax > 0 ) : ?>
255 <tr>
256 <td><?php echo esc_html( $t( 'tax', __( 'Tax', 'easy-invoice' ) ) ); ?></td>
257 <td class="v"><?php echo esc_html( $formatter->format( $tax ) ); ?></td>
258 </tr>
259 <?php endif; ?>
260 <?php
261 /**
262 * Extra total lines — the hook the Additional Tax addon renders into on screen,
263 * so a PDF shows the same figures a customer sees in the browser.
264 */
265 do_action( 'easy_invoice_pdf_totals_after_tax', $invoice );
266 ?>
267 <tr class="grand">
268 <td><?php echo esc_html( $t( 'total', __( 'Total', 'easy-invoice' ) ) ); ?></td>
269 <td class="v"><?php echo esc_html( $formatter->format( $total ) ); ?></td>
270 </tr>
271 <?php
272 // Money already received and credit notes issued: the total stays the
273 // total, and what is still owed is spelled out underneath it.
274 $ei_pdf_id = ( ! $ei_is_quote && is_callable( [ $invoice, 'getId' ] ) ) ? (int) $invoice->getId() : 0;
275 $ei_pdf_paid = $ei_pdf_id ? \EasyInvoice\Services\InvoiceBalance::paid( $ei_pdf_id ) : 0.0;
276 $ei_pdf_credited = $ei_pdf_id ? \EasyInvoice\Services\InvoiceBalance::credited( $ei_pdf_id ) : 0.0;
277 if ( $ei_pdf_paid > 0 || $ei_pdf_credited > 0 ) :
278 foreach ( \EasyInvoice\Services\CreditNote::forInvoice( $ei_pdf_id ) as $ei_credit_id ) :
279 $ei_credit_amount = (float) get_post_meta( $ei_credit_id, '_easy_invoice_total', true );
280 if ( $ei_credit_amount <= 0 ) {
281 continue;
282 }
283 ?>
284 <tr>
285 <td><?php /* translators: %s: credit note number. */ echo esc_html( sprintf( __( 'Credit note %s', 'easy-invoice' ), (string) get_post_meta( $ei_credit_id, '_easy_invoice_number', true ) ) ); ?></td>
286 <td class="v"><?php echo esc_html( '-' . $formatter->format( $ei_credit_amount ) ); ?></td>
287 </tr>
288 <?php endforeach; ?>
289 <?php if ( $ei_pdf_paid > 0 ) : ?>
290 <tr>
291 <td><?php esc_html_e( 'Paid', 'easy-invoice' ); ?></td>
292 <td class="v"><?php echo esc_html( '-' . $formatter->format( $ei_pdf_paid ) ); ?></td>
293 </tr>
294 <?php endif; ?>
295 <tr class="grand">
296 <td><?php esc_html_e( 'Balance due', 'easy-invoice' ); ?></td>
297 <td class="v"><?php echo esc_html( $formatter->format( max( 0, $total - $ei_pdf_paid - $ei_pdf_credited ) ) ); ?></td>
298 </tr>
299 <?php endif; ?>
300 </table>
301
302 <table class="blocks">
303 <?php if ( is_callable( [ $invoice, 'getNotes' ] ) && $invoice->getNotes() ) : ?>
304 <tr><td>
305 <div class="label"><?php esc_html_e( 'Notes', 'easy-invoice' ); ?></div>
306 <?php echo nl2br( esc_html( $invoice->getNotes() ) ); ?>
307 </td></tr>
308 <?php endif; ?>
309 <?php if ( ! $ei_is_credit && is_callable( [ $invoice, 'getTerms' ] ) && $invoice->getTerms() ) : ?>
310 <tr><td>
311 <div class="label"><?php esc_html_e( 'Terms', 'easy-invoice' ); ?></div>
312 <?php echo nl2br( esc_html( $invoice->getTerms() ) ); ?>
313 </td></tr>
314 <?php endif; ?>
315 </table>
316
317 <?php
318 /**
319 * Fires after notes and terms on the PDF, before the footer.
320 *
321 * @param object $invoice The document (invoice or quote model).
322 * @param string $ei_pdf_type 'invoice' or 'quote'.
323 */
324 do_action( 'easy_invoice_pdf_after_notes', $invoice, $ei_pdf_type );
325 ?>
326 <?php $ei_pdf_footer = \EasyInvoice\Helpers\TemplateTextHelper::footerText( $invoice, $ei_pdf_type ); ?>
327 <?php if ( '' !== trim( wp_strip_all_tags( $ei_pdf_footer ) ) ) : ?>
328 <div class="footer"><?php echo wp_kses_post( $ei_pdf_footer ); ?></div>
329 <?php endif; ?>
330
331 </body>
332 </html>
333