PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.0
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.0
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 / quote-templates / standard.php

standard.php in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.4.0, at templates/quote-templates/standard.php

424 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Standard Quote Template - Clean SaaS Design
4 * Clean, modern, and professional quote template
5 */
6
7 if ( ! defined( 'ABSPATH' ) ) {
8 exit;
9 }
10 $text_settings = \EasyInvoice\Helpers\TemplateTextHelper::getQuoteTextSettings();
11 $company_info = \EasyInvoice\Helpers\TemplateTextHelper::getCompanyInfo();
12 // Ensure a formatter is available for currency formatting
13 if (!isset($formatter) || !$formatter) {
14 $formatter = new \EasyInvoice\Helpers\QuoteFormatter($quote ?? null);
15 }
16 ?>
17 <style>
18 /* Standard Quote Template - Simple and Clean */
19 .template-standard {
20 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
21 max-width: 800px;
22 margin: 0 auto;
23 padding: 2rem;
24 background: #fff;
25 color: #333;
26 line-height: 1.5;
27 }
28
29 /* Header Section */
30 .template-standard .quote-header {
31 display: flex;
32 justify-content: space-between;
33 margin-bottom: 2rem;
34 padding-bottom: 1rem;
35 border-bottom: 2px solid #eee;
36 }
37
38 .template-standard .company-info {
39 flex: 1;
40 }
41
42 .template-standard .logo-image {
43 max-height: 60px;
44 max-width: 200px;
45 margin-bottom: 1rem;
46 }
47
48 .template-standard .company-name {
49 font-size: 1.5rem;
50 font-weight: bold;
51 margin-bottom: 0.5rem;
52 }
53
54 .template-standard .company-details {
55 font-size: 0.9rem;
56 color: #666;
57 }
58
59 .template-standard .quote-meta {
60 text-align: right;
61 min-width: 200px;
62 }
63
64 .template-standard .quote-title {
65 font-size: 1.5rem;
66 font-weight: bold;
67 margin-bottom: 1rem;
68 color: #2563eb;
69 }
70
71 .template-standard .meta-item {
72 margin-bottom: 0.5rem;
73 }
74
75 .template-standard .meta-label {
76 color: #666;
77 font-size: 0.9rem;
78 }
79
80 .template-standard .meta-value {
81 font-weight: 600;
82 }
83
84 /* Address Section */
85 .template-standard .address-section {
86 margin-bottom: 2rem;
87 }
88
89 .template-standard .address-block {
90 background: #f8f9fa;
91 padding: 1rem;
92 border-radius: 4px;
93 }
94
95 .template-standard .address-block h2 {
96 font-size: 1rem;
97 font-weight: 600;
98 margin-bottom: 0.5rem;
99 text-transform: uppercase;
100 }
101
102 .template-standard .client-name {
103 font-weight: 600;
104 margin-bottom: 0.5rem;
105 }
106
107 .template-standard .address-content {
108 font-size: 0.9rem;
109 color: #666;
110 }
111
112 /* Quote Description */
113 .template-standard .quote-description {
114 margin-bottom: 2rem;
115 }
116
117 .template-standard .description-content {
118 color: #666;
119 white-space: pre-wrap;
120 }
121
122 /* Quote Items Table */
123 .template-standard .quote-items {
124 width: 100%;
125 border-collapse: collapse;
126 margin-bottom: 2rem;
127 border: 1px solid #eee;
128 }
129
130 .template-standard .quote-items th {
131 background: #2563eb;
132 color: white;
133 text-align: left;
134 padding: 0.75rem;
135 font-size: 0.9rem;
136 }
137
138 .template-standard .quote-items td {
139 padding: 0.75rem;
140 border-bottom: 1px solid #eee;
141 }
142
143 .template-standard .item-name .item-title {
144 font-weight: normal;
145 }
146
147 .template-standard .item-description {
148 font-size: 0.9rem;
149 color: #666;
150 margin-top: 0.25rem;
151 font-weight: 300;
152 }
153
154 .template-standard .item-quantity,
155 .template-standard .item-price,
156 .template-standard .item-total {
157 text-align: right;
158 }
159
160 /* Quote Totals */
161 .template-standard .quote-totals {
162 margin-left: auto;
163 width: 300px;
164 margin-bottom: 2rem;
165 }
166
167 .template-standard .quote-total-row {
168 display: flex;
169 justify-content: space-between;
170 padding: 0.5rem 0;
171 border-bottom: 1px solid #eee;
172 }
173
174 .template-standard .quote-total-row:last-child {
175 border-bottom: none;
176 font-weight: bold;
177 font-size: 1.1rem;
178 margin-top: 0.5rem;
179 padding-top: 0.5rem;
180 border-top: 2px solid #2563eb;
181 }
182
183 /* Notes and Terms */
184 .template-standard .quote-notes,
185 .template-standard .quote-terms {
186 margin-top: 2rem;
187 padding: 1rem;
188 background: #f8f9fa;
189 border-radius: 4px;
190 }
191
192 .template-standard .quote-notes h3,
193 .template-standard .quote-terms h3 {
194 font-size: 1rem;
195 font-weight: 600;
196 margin-bottom: 0.5rem;
197 }
198
199 .template-standard .notes-content,
200 .template-standard .terms-content {
201 font-size: 0.9rem;
202 color: #666;
203 }
204
205 /* Footer */
206 .template-standard .quote-footer {
207 margin-top: 2rem;
208 padding-top: 1rem;
209 text-align: center;
210 border-top: 1px solid #eee;
211 color: #666;
212 font-size: 0.9rem;
213 }
214
215 /* Responsive Design */
216 @media (max-width: 768px) {
217 .template-standard {
218 padding: 1rem;
219 }
220
221 .template-standard .quote-header {
222 flex-direction: column;
223 }
224
225 .template-standard .quote-meta {
226 text-align: left;
227 margin-top: 1rem;
228 }
229
230 .template-standard .quote-totals {
231 width: 100%;
232 }
233 }
234
235 /* Shared polish: numbers line up, numeric headings sit over their figures. */
236 .template-standard .quote-items th + th { text-align: right; }
237 .template-standard .quote-items .item-quantity,
238 .template-standard .quote-items .item-price,
239 .template-standard .quote-items .item-adjust,
240 .template-standard .quote-items .item-total,
241 .template-standard .quote-total-value { font-variant-numeric: tabular-nums; white-space: nowrap; }
242 .template-standard .meta-value { white-space: nowrap; }
243 .template-standard .quote-items .item-adjust { text-align: right; }
244 .template-standard .client-contact { font-weight: 500; margin-bottom: 0.15em; }
245 .template-standard .client-tax-number,
246 .template-standard .company-tax-number { margin-top: 0.35em; }
247 .template-standard .quote-items .item-description { white-space: pre-line; }
248 .template-standard .document-kind { display: block; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-muted, #8a8a8a); font-family: var(--font-family-body, inherit); font-style: normal; margin-bottom: 0.3em; line-height: 1.2; }
249
250 /* Standard: ink on white, one rule per section, nothing boxed. */
251 .template-standard { color: #1f2937; }
252 .template-standard .quote-header { border-bottom: 2px solid #1f2937; padding-bottom: 1.5rem; margin-bottom: 1.75rem; align-items: flex-start; }
253 .template-standard .company-name { font-size: 1.375rem; font-weight: 700; letter-spacing: -0.01em; color: #111827; }
254 .template-standard .company-details, .template-standard .address-content, .template-standard .meta-label, .template-standard .quote-total-label { color: #6b7280; }
255 .template-standard .quote-title { font-size: 1.625rem; font-weight: 700; letter-spacing: -0.015em; color: #111827; margin-bottom: 0.75rem; }
256 .template-standard .meta-item { display: flex; justify-content: flex-end; gap: 1rem; margin-bottom: 0.35rem; font-size: 0.9rem; }
257 .template-standard .meta-value { font-weight: 600; color: #111827; }
258 .template-standard .address-block { background: transparent; padding: 0; border: 0; }
259 .template-standard .address-block h2 { font-size: 0.72rem; letter-spacing: 0.14em; color: #6b7280; margin-bottom: 0.5rem; }
260 .template-standard .client-name { font-weight: 600; color: #111827; font-size: 1rem; }
261 .template-standard .quote-items { border: 0; margin-bottom: 1.25rem; }
262 .template-standard .quote-items th { background: transparent; color: #6b7280; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; padding: 0.6rem 0.5rem; border-bottom: 1px solid #1f2937; }
263 .template-standard .quote-items td { padding: 0.8rem 0.5rem; border-bottom: 1px solid #e5e7eb; color: #1f2937; }
264 .template-standard .quote-items th:first-child, .template-standard .quote-items td:first-child { padding-left: 0; }
265 .template-standard .quote-items th:last-child, .template-standard .quote-items td:last-child { padding-right: 0; }
266 .template-standard .item-name .item-title { font-weight: 600; color: #111827; }
267 .template-standard .item-description { font-weight: 400; color: #6b7280; font-size: 0.85rem; }
268 .template-standard .quote-totals { width: 320px; margin-bottom: 1.5rem; }
269 .template-standard .quote-total-row { padding: 0.45rem 0; border-bottom: 1px solid #e5e7eb; font-size: 0.95rem; }
270 .template-standard .quote-total-value { font-weight: 600; color: #111827; }
271 .template-standard .quote-totals .quote-grand-total { border-top: 2px solid #1f2937; border-bottom: 0; margin-top: 0.25rem; padding-top: 0.75rem; font-size: 1.125rem; }
272 .template-standard .quote-totals .quote-grand-total .quote-total-label { color: #111827; font-weight: 700; }
273 .template-standard .quote-totals .quote-grand-total .quote-total-value { font-size: 1.25rem; font-weight: 700; }
274 .template-standard .quote-notes, .template-standard .quote-terms { background: transparent; padding: 1.25rem 0 0; margin-top: 1.25rem; border: 0; border-top: 1px solid #e5e7eb; border-radius: 0; color: #4b5563; font-size: 0.9rem; }
275 .template-standard .quote-notes h3, .template-standard .quote-terms h3 { font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: #6b7280; font-weight: 600; margin-bottom: 0.5rem; }
276 .template-standard .quote-footer { border-top: 1px solid #e5e7eb; color: #6b7280; font-size: 0.85rem; }
277 </style>
278
279 <div class="template template-standard">
280 <div class="quote-header">
281 <div class="company-info">
282 <?php if (!empty($company_info['logo'])): ?>
283 <div class="company-logo">
284 <img src="<?php echo esc_url($company_info['logo']); ?>" alt="<?php echo esc_attr($company_info['name']); ?>" class="logo-image" onerror="this.parentNode.parentNode.removeChild(this.parentNode)">
285 </div>
286 <?php endif; ?>
287 <div class="company-name"><?php echo esc_html($company_info['name']); ?></div>
288 <div class="company-details">
289 <?php echo wp_kses_post(\EasyInvoice\Helpers\TemplateTextHelper::generateFromSection($company_info, false, 'details-only')); ?>
290 </div>
291 </div>
292
293 <div class="quote-meta">
294 <?php $ei_kind = \EasyInvoice\Controllers\SettingsController::getTextQuote(); $ei_title = trim((string) $quote->getTitle()); ?>
295 <div class="quote-title"><?php if ('' !== $ei_title && 0 !== strcasecmp($ei_title, $ei_kind)) : ?><span class="document-kind"><?php echo esc_html($ei_kind); ?></span><?php endif; ?><?php echo esc_html('' !== $ei_title ? $ei_title : $ei_kind); ?></div>
296 <div class="meta-item">
297 <span class="meta-label"><?php echo esc_html($text_settings['quote_number']); ?></span>
298 <span class="meta-value"><?php echo esc_html($quote->getNumber()); ?></span>
299 </div>
300 <div class="meta-item">
301 <span class="meta-label"><?php echo esc_html($text_settings['quote_date']); ?></span>
302 <span class="meta-value"><?php echo esc_html(\EasyInvoice\Controllers\SettingsController::formatDate($quote->getIssueDate())); ?></span>
303 </div>
304 <div class="meta-item">
305 <span class="meta-label"><?php echo esc_html($text_settings['valid_until']); ?></span>
306 <span class="meta-value"><?php echo esc_html(\EasyInvoice\Controllers\SettingsController::formatDate($quote->getExpiryDate())); ?></span>
307 </div>
308 </div>
309 </div>
310
311 <?php $ei_client_html = \EasyInvoice\Helpers\TemplateTextHelper::generateClientBlock($quote); if ('' !== $ei_client_html) : ?>
312 <div class="address-section">
313 <div class="address-block">
314 <h2><?php echo esc_html($text_settings['to']); ?></h2>
315 <div class="address-content">
316 <?php echo wp_kses_post($ei_client_html); ?>
317 </div>
318 </div>
319 </div>
320 <?php endif; ?>
321
322 <?php if ($quote->getDescription()): ?>
323 <div class="quote-description">
324 <div class="description-content"><?php echo wp_kses_post($quote->getDescription()); ?></div>
325 </div>
326 <?php endif; ?>
327
328 <?php
329 // The adjustment column appears only when at least one line uses it.
330 $ei_show_adjust = \EasyInvoice\Controllers\SettingsController::shouldShowQuoteAdjustField();
331 if ($ei_show_adjust) {
332 $ei_show_adjust = false;
333 foreach ($quote->getItems() as $ei_item) {
334 if ((float) $ei_item->getAdjustPercentage() != 0.0) { $ei_show_adjust = true; break; }
335 }
336 }
337 ?>
338 <table class="quote-items">
339 <thead>
340 <tr>
341 <th><?php echo esc_html($text_settings['service']); ?></th>
342 <th><?php echo esc_html($text_settings['qty']); ?></th>
343 <th><?php echo esc_html($text_settings['rate_price']); ?></th>
344 <?php if ($ei_show_adjust): ?>
345 <th><?php echo esc_html($text_settings['adjust']); ?> (%)</th>
346 <?php endif; ?>
347 <th><?php echo esc_html($text_settings['line_total'] ?? $text_settings['total']); ?></th>
348 </tr>
349 </thead>
350 <tbody>
351 <?php if (!$quote->getItems()) : ?>
352 <tr class="no-items"><td colspan="<?php echo $ei_show_adjust ? 5 : 4; ?>" style="text-align:center;color:#9ca3af;padding:18px;"><?php esc_html_e('No items yet.', 'easy-invoice'); ?></td></tr>
353 <?php endif; ?>
354 <?php foreach ($quote->getItems() as $item): ?>
355 <tr>
356 <td class="item-name">
357 <div class="item-title"><?php echo esc_html($item->getName()); ?></div>
358 <?php if ($item->getDescription()): ?>
359 <div class="item-description"><?php echo esc_html($item->getDescription()); ?></div>
360 <?php endif; ?>
361 </td>
362 <td class="item-quantity"><?php echo esc_html($item->getQuantity() ?: '0'); ?></td>
363 <td class="item-price"><?php echo esc_html($formatter->format($item->getPrice())); ?></td>
364 <?php if ($ei_show_adjust): ?>
365 <td class="item-adjust"><?php
366 $adjust_percentage = $item->getAdjustPercentage();
367 if ($adjust_percentage != 0) {
368 echo esc_html($adjust_percentage > 0 ? '+' : '') . esc_html($adjust_percentage) . '%';
369 } else {
370 echo esc_html__('—', 'easy-invoice');
371 }
372 ?></td>
373 <?php endif; ?>
374 <td class="item-total"><?php echo esc_html($formatter->format($item->getAmount())); ?></td>
375 </tr>
376 <?php endforeach; ?>
377 </tbody>
378 </table>
379
380 <div class="quote-totals">
381 <?php echo wp_kses_post(\EasyInvoice\Helpers\TemplateTextHelper::generateTotalsSection($quote, $text_settings, $formatter, 'quote')); ?>
382 </div>
383
384 <?php if ($quote->getNotes()): ?>
385 <div class="quote-notes">
386 <h3><?php esc_html_e('Notes', 'easy-invoice'); ?></h3>
387 <div class="notes-content">
388 <?php echo wp_kses_post($quote->getNotes() ?: ''); ?>
389 </div>
390 </div>
391 <?php endif; ?>
392
393 <?php
394 $terms_conditions = $quote->getTerms() ?: \EasyInvoice\Controllers\SettingsController::getQuoteTermsConditions();
395 if ($terms_conditions):
396 ?>
397 <div class="quote-terms">
398 <h3><?php esc_html_e('Terms & Conditions', 'easy-invoice'); ?></h3>
399 <div class="terms-content">
400 <?php echo wp_kses_post($terms_conditions); ?>
401 </div>
402 </div>
403 <?php endif; ?>
404
405 <?php
406 /**
407 * Fires inside the document card, after notes and terms and before the
408 * footer text: attachments, the signature block, e-invoicing notes. Being
409 * inside the card, it is part of what "Download as PDF" captures.
410 *
411 * @param object $document The quote model.
412 * @param string $type 'quote'.
413 */
414 do_action('easy_invoice_document_body_after', $quote, 'quote');
415
416 $ei_footer_text = \EasyInvoice\Helpers\TemplateTextHelper::footerText($quote, 'quote');
417 if ('' !== trim(wp_strip_all_tags((string) $ei_footer_text))) :
418 ?>
419 <div class="quote-footer">
420 <p><?php echo wp_kses_post($ei_footer_text); ?></p>
421 </div>
422 <?php endif; ?>
423 </div>
424