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
← All changes | templates/quote-templates/modern.php +89 -29 2.3.6 → 2.4.0 View file →
@@ -3,8 +3,11 @@
3 3 * Modern Quote Template (Refactored to Minimal style)
4 4 * This file reuses the Minimal template structure and styling
5 5 */
6 6
7 +if ( ! defined( 'ABSPATH' ) ) {
8 + exit;
9 +}
7 10 $text_settings = \EasyInvoice\Helpers\TemplateTextHelper::getQuoteTextSettings();
8 11 $company_info = \EasyInvoice\Helpers\TemplateTextHelper::getCompanyInfo();
9 12 // Ensure a formatter is available
10 13 if (!isset($formatter) || !$formatter) {
@@ -63,9 +66,9 @@
63 66 .template-modern .quote-total-row { display: flex; justify-content: space-between; align-items: center; padding: var(--spacing-3) 0; border-bottom: 1px solid var(--border-light); }
64 67 .template-modern .quote-total-row:last-child { border-bottom: none; }
65 68 .template-modern .quote-total-label { font-weight: 500; color: var(--text-secondary); }
66 69 .template-modern .quote-total-value { font-weight: 600; color: var(--text-primary); }
67 -.template-modern .quote-grand-total { font-size: var(--font-size-xl); font-weight: 700; color: var(--text-primary); border-top: 2px solid var(--border-light); padding-top: var(--spacing-4); margin-top: var(--spacing-4); }
70 +.template-modern .quote-totals .quote-grand-total { font-size: var(--font-size-xl); font-weight: 700; color: var(--text-primary); border-top: 2px solid var(--border-light); padding-top: var(--spacing-4); margin-top: var(--spacing-4); }
68 71 .template-modern .quote-notes { margin-top: var(--spacing-12); padding: var(--spacing-6); background: var(--background-accent); border-radius: var(--radius-lg); border-left: 4px solid var(--primary-color); }
69 72 .template-modern .quote-terms { margin-top: var(--spacing-12); padding: var(--spacing-6); background: var(--background-secondary); border-radius: var(--radius-lg); border: 1px solid var(--border-color); }
70 73 .template-modern .quote-footer { margin-top: var(--spacing-12); padding: var(--spacing-6); text-align: center; background: var(--background-secondary); border-radius: var(--radius-lg); border: 1px solid var(--border-color); }
71 74 @media (max-width: 768px) { .template-modern { padding: var(--spacing-4);} .template-modern .quote-header { flex-direction: column; gap: var(--spacing-4);} .template-modern .address-section { grid-template-columns: 1fr; gap: var(--spacing-6);} .template-modern .quote-totals { width: 100%; } }
@@ -211,10 +214,13 @@
211 214 box-shadow: var(--shadow-xl);
212 215 border: 1px solid var(--border-color);
213 216 }
214 217
218 +.template-modern .quote-items thead {
219 + background: linear-gradient(135deg, var(--primary-light), var(--primary-color));
220 +}
215 221 .template-modern .quote-items th {
216 - background: linear-gradient(135deg, var(--primary-light), var(--primary-color));
222 + background: transparent;
217 223 color: white;
218 224 font-weight: 600;
219 225 text-align: left;
220 226 padding: var(--spacing-5) var(--spacing-6);
@@ -293,9 +299,9 @@
293 299 font-weight: 600;
294 300 color: var(--text-primary);
295 301 }
296 302
297 -.template-modern .quote-grand-total {
303 +.template-modern .quote-totals .quote-grand-total {
298 304 font-size: var(--font-size-xl);
299 305 font-weight: 700;
300 306 color: var(--primary-color);
301 307 border-top: 2px solid var(--primary-color);
@@ -388,26 +394,62 @@
388 394 .template-modern .quote-totals {
389 395 width: 100%;
390 396 }
391 397 }
398 +
399 +/* Shared polish: numbers line up, numeric headings sit over their figures. */
400 +.template-modern .quote-items th + th { text-align: right; }
401 +.template-modern .quote-items .item-quantity,
402 +.template-modern .quote-items .item-price,
403 +.template-modern .quote-items .item-adjust,
404 +.template-modern .quote-items .item-total,
405 +.template-modern .quote-total-value { font-variant-numeric: tabular-nums; white-space: nowrap; }
406 +.template-modern .meta-value { white-space: nowrap; }
407 +.template-modern .quote-items .item-adjust { text-align: right; }
408 +.template-modern .client-contact { font-weight: 500; margin-bottom: 0.15em; }
409 +.template-modern .client-tax-number,
410 +.template-modern .company-tax-number { margin-top: 0.35em; }
411 +.template-modern .quote-items .item-description { white-space: pre-line; }
412 +.template-modern .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; }
413 +
414 +/* Modern: one card in the header instead of a card inside a panel; lighter shadows; tighter rhythm. */
415 +.template-modern .quote-header { background: transparent; border: 0; padding: 0 0 var(--spacing-6); margin-bottom: var(--spacing-8); border-bottom: 1px solid var(--border-color); border-radius: 0; }
416 +.template-modern .quote-meta { box-shadow: var(--shadow-sm); gap: 0; padding: var(--spacing-5) var(--spacing-6); }
417 +.template-modern .quote-title { margin-bottom: var(--spacing-2); }
418 +.template-modern .meta-item { padding: var(--spacing-2) 0; }
419 +.template-modern .meta-item:last-child { border-bottom: 0; }
420 +.template-modern .address-section { margin-bottom: var(--spacing-8); }
421 +.template-modern .address-block { padding: var(--spacing-5); }
422 +.template-modern .address-block h2 { font-size: var(--font-size-xs); letter-spacing: 0.14em; color: var(--text-muted); margin-bottom: var(--spacing-2); }
423 +.template-modern .quote-items { box-shadow: var(--shadow-sm); margin-bottom: var(--spacing-8); border: 1px solid var(--border-color); }
424 +.template-modern .quote-items th { padding: var(--spacing-3) var(--spacing-5); font-size: var(--font-size-xs); letter-spacing: 0.1em; }
425 +.template-modern .quote-items td { padding: var(--spacing-4) var(--spacing-5); }
426 +.template-modern .quote-items tr:hover { background: transparent; }
427 +.template-modern .quote-totals { box-shadow: var(--shadow-sm); padding: var(--spacing-5) var(--spacing-6); width: 360px; }
428 +.template-modern .quote-total-row { padding: var(--spacing-2) 0; }
429 +.template-modern .quote-totals .quote-grand-total { padding-top: var(--spacing-3); margin-top: var(--spacing-2); }
430 +.template-modern .quote-notes, .template-modern .quote-terms { margin-top: var(--spacing-8); padding: var(--spacing-5) var(--spacing-6); box-shadow: none; }
431 +.template-modern .quote-notes h3, .template-modern .quote-terms h3 { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: 0.14em; color: var(--text-muted); margin-bottom: var(--spacing-2); }
432 +.template-modern .quote-footer { margin-top: var(--spacing-8); background: transparent; border: 0; border-top: 1px solid var(--border-color); border-radius: 0; padding: var(--spacing-5) 0 0; color: var(--text-muted); }
392 433 </style>
393 434
394 435 <div class="template template-modern">
395 436 <div class="quote-header">
396 437 <div class="company-info">
438 + <?php if (!empty($company_info['logo'])): ?>
397 439 <div class="company-logo">
398 - <?php if (!empty($company_info['logo'])): ?>
399 - <img src="<?php echo esc_url($company_info['logo']); ?>" alt="<?php echo esc_attr($company_info['name']); ?>" class="logo-image">
400 - <?php endif; ?>
440 + <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)">
401 441 </div>
442 + <?php endif; ?>
402 443 <div class="company-name"><?php echo esc_html($company_info['name']); ?></div>
403 444 <div class="company-details">
404 - <?php echo \EasyInvoice\Helpers\TemplateTextHelper::generateFromSection($company_info, false, 'details-only'); ?>
445 + <?php echo wp_kses_post(\EasyInvoice\Helpers\TemplateTextHelper::generateFromSection($company_info, false, 'details-only')); ?>
405 446 </div>
406 447 </div>
407 448
408 449 <div class="quote-meta">
409 - <div class="quote-title"><?php echo esc_html($quote->getTitle() ?: __('Quote', 'easy-invoice')); ?></div>
450 + <?php $ei_kind = \EasyInvoice\Controllers\SettingsController::getTextQuote(); $ei_title = trim((string) $quote->getTitle()); ?>
451 + <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>
410 452 <div class="meta-item">
411 453 <span class="meta-label"><?php echo esc_html($text_settings['quote_number']); ?></span>
412 454 <span class="meta-value"><?php echo esc_html($quote->getNumber()); ?></span>
413 455 </div>
@@ -421,22 +463,18 @@
421 463 </div>
422 464 </div>
423 465 </div>
424 466
467 + <?php $ei_client_html = \EasyInvoice\Helpers\TemplateTextHelper::generateClientBlock($quote); if ('' !== $ei_client_html) : ?>
425 468 <div class="address-section">
426 469 <div class="address-block">
427 470 <h2><?php echo esc_html($text_settings['to']); ?></h2>
428 471 <div class="address-content">
429 - <div class="client-name"><?php echo esc_html($quote->getCustomerName()); ?></div>
430 - <?php if ($quote->getCustomerAddress()): ?>
431 - <?php echo nl2br(esc_html($quote->getCustomerAddress())); ?><br>
432 - <?php endif; ?>
433 - <?php if ($quote->getCustomerEmail()): ?>
434 - <?php echo esc_html($quote->getCustomerEmail()); ?><br>
435 - <?php endif; ?>
472 + <?php echo wp_kses_post($ei_client_html); ?>
436 473 </div>
437 474 </div>
438 475 </div>
476 + <?php endif; ?>
439 477
440 478 <?php if ($quote->getDescription()): ?>
441 479 <div class="quote-description" style="margin-bottom: 1.5rem;">
442 480 <div class="description-content" style="color:#6b7280; line-height:1.6; font-size:0.875rem; white-space:pre-wrap;">
@@ -444,8 +482,18 @@
444 482 </div>
445 483 </div>
446 484 <?php endif; ?>
447 485
486 + <?php
487 + // The adjustment column appears only when at least one line uses it.
488 + $ei_show_adjust = \EasyInvoice\Controllers\SettingsController::shouldShowQuoteAdjustField();
489 + if ($ei_show_adjust) {
490 + $ei_show_adjust = false;
491 + foreach ($quote->getItems() as $ei_item) {
492 + if ((float) $ei_item->getAdjustPercentage() != 0.0) { $ei_show_adjust = true; break; }
493 + }
494 + }
495 + ?>
448 496 <table class="quote-items">
449 497 <thead>
450 498 <tr>
451 499 <th><?php echo esc_html($text_settings['service']); ?></th>
@@ -450,15 +498,18 @@
450 498 <tr>
451 499 <th><?php echo esc_html($text_settings['service']); ?></th>
452 500 <th><?php echo esc_html($text_settings['qty']); ?></th>
453 501 <th><?php echo esc_html($text_settings['rate_price']); ?></th>
454 - <?php if (\EasyInvoice\Controllers\SettingsController::shouldShowQuoteAdjustField()): ?>
502 + <?php if ($ei_show_adjust): ?>
455 503 <th><?php echo esc_html($text_settings['adjust']); ?> (%)</th>
456 504 <?php endif; ?>
457 - <th><?php echo esc_html($text_settings['total']); ?></th>
505 + <th><?php echo esc_html($text_settings['line_total'] ?? $text_settings['total']); ?></th>
458 506 </tr>
459 507 </thead>
460 508 <tbody>
509 + <?php if (!$quote->getItems()) : ?>
510 + <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>
511 + <?php endif; ?>
461 512 <?php foreach ($quote->getItems() as $item): ?>
462 513 <tr>
463 514 <td class="item-name">
464 515 <div class="item-title"><?php echo esc_html($item->getName()); ?></div>
@@ -467,9 +518,9 @@
467 518 <?php endif; ?>
468 519 </td>
469 520 <td class="item-quantity"><?php echo esc_html($item->getQuantity() ?: '0'); ?></td>
470 521 <td class="item-price"><?php echo esc_html($formatter->format($item->getPrice())); ?></td>
471 - <?php if (\EasyInvoice\Controllers\SettingsController::shouldShowQuoteAdjustField()): ?>
522 + <?php if ($ei_show_adjust): ?>
472 523 <td class="item-adjust"><?php
473 524 $adjust_percentage = $item->getAdjustPercentage();
474 525 if ($adjust_percentage != 0) {
475 526 echo esc_html($adjust_percentage > 0 ? '+' : '') . esc_html($adjust_percentage) . '%';
@@ -484,14 +535,14 @@
484 535 </tbody>
485 536 </table>
486 537
487 538 <div class="quote-totals">
488 - <?php echo \EasyInvoice\Helpers\TemplateTextHelper::generateTotalsSection($quote, $text_settings, $formatter, 'quote'); ?>
539 + <?php echo wp_kses_post(\EasyInvoice\Helpers\TemplateTextHelper::generateTotalsSection($quote, $text_settings, $formatter, 'quote')); ?>
489 540 </div>
490 541
491 542 <?php if ($quote->getNotes()): ?>
492 543 <div class="quote-notes">
493 - <h3><?php _e('Notes:', 'easy-invoice'); ?></h3>
544 + <h3><?php esc_html_e('Notes', 'easy-invoice'); ?></h3>
494 545 <div class="notes-content">
495 546 <?php echo wp_kses_post($quote->getNotes() ?: ''); ?>
496 547 </div>
497 548 </div>
@@ -501,9 +552,9 @@
501 552 $terms_conditions = $quote->getTerms() ?: \EasyInvoice\Controllers\SettingsController::getQuoteTermsConditions();
502 553 if ($terms_conditions):
503 554 ?>
504 555 <div class="quote-terms">
505 - <h3><?php _e('Terms & Conditions:', 'easy-invoice'); ?></h3>
556 + <h3><?php esc_html_e('Terms & Conditions', 'easy-invoice'); ?></h3>
506 557 <div class="terms-content">
507 558 <?php echo wp_kses_post($terms_conditions); ?>
508 559 </div>
509 560 </div>
@@ -508,14 +559,23 @@
508 559 </div>
509 560 </div>
510 561 <?php endif; ?>
511 562
563 + <?php
564 + /**
565 + * Fires inside the document card, after notes and terms and before the
566 + * footer text: attachments, the signature block, e-invoicing notes. Being
567 + * inside the card, it is part of what "Download as PDF" captures.
568 + *
569 + * @param object $document The quote model.
570 + * @param string $type 'quote'.
571 + */
572 + do_action('easy_invoice_document_body_after', $quote, 'quote');
573 +
574 + $ei_footer_text = \EasyInvoice\Helpers\TemplateTextHelper::footerText($quote, 'quote');
575 + if ('' !== trim(wp_strip_all_tags((string) $ei_footer_text))) :
576 + ?>
512 577 <div class="quote-footer">
513 - <?php
514 - $footer_text = $quote->getFooterText();
515 - if (!$footer_text) {
516 - $footer_text = \EasyInvoice\Controllers\SettingsController::getQuoteFooterText();
517 - }
518 - ?>
519 - <p><?php echo wp_kses_post($footer_text); ?></p>
578 + <p><?php echo wp_kses_post($ei_footer_text); ?></p>
520 579 </div>
521 -</div>
580 + <?php endif; ?>
581 +</div>