PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.4.2
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.4.2
2.4.3 2.4.4 2.4.2 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 All 60 releases
← All changes | templates/quote-templates/modern.php +90 -38 2.1.1 → 2.4.2 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,27 +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 - <!-- Header -->
396 436 <div class="quote-header">
397 437 <div class="company-info">
438 + <?php if (!empty($company_info['logo'])): ?>
398 439 <div class="company-logo">
399 - <?php if (!empty($company_info['logo'])): ?>
400 - <img src="<?php echo esc_url($company_info['logo']); ?>" alt="<?php echo esc_attr($company_info['name']); ?>" class="logo-image">
401 - <?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)">
402 441 </div>
442 + <?php endif; ?>
403 443 <div class="company-name"><?php echo esc_html($company_info['name']); ?></div>
404 444 <div class="company-details">
405 - <?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')); ?>
406 446 </div>
407 447 </div>
408 448
409 449 <div class="quote-meta">
410 - <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>
411 452 <div class="meta-item">
412 453 <span class="meta-label"><?php echo esc_html($text_settings['quote_number']); ?></span>
413 454 <span class="meta-value"><?php echo esc_html($quote->getNumber()); ?></span>
414 455 </div>
@@ -422,25 +463,19 @@
422 463 </div>
423 464 </div>
424 465 </div>
425 466
426 - <!-- Address Section -->
467 + <?php $ei_client_html = \EasyInvoice\Helpers\TemplateTextHelper::generateClientBlock($quote); if ('' !== $ei_client_html) : ?>
427 468 <div class="address-section">
428 469 <div class="address-block">
429 470 <h2><?php echo esc_html($text_settings['to']); ?></h2>
430 471 <div class="address-content">
431 - <div class="client-name"><?php echo esc_html($quote->getCustomerName()); ?></div>
432 - <?php if ($quote->getCustomerAddress()): ?>
433 - <?php echo nl2br(esc_html($quote->getCustomerAddress())); ?><br>
434 - <?php endif; ?>
435 - <?php if ($quote->getCustomerEmail()): ?>
436 - <?php echo esc_html($quote->getCustomerEmail()); ?><br>
437 - <?php endif; ?>
472 + <?php echo wp_kses_post($ei_client_html); ?>
438 473 </div>
439 474 </div>
440 475 </div>
476 + <?php endif; ?>
441 477
442 - <!-- Quote Description -->
443 478 <?php if ($quote->getDescription()): ?>
444 479 <div class="quote-description" style="margin-bottom: 1.5rem;">
445 480 <div class="description-content" style="color:#6b7280; line-height:1.6; font-size:0.875rem; white-space:pre-wrap;">
446 481 <?php echo wp_kses_post($quote->getDescription()); ?>
@@ -447,9 +482,18 @@
447 482 </div>
448 483 </div>
449 484 <?php endif; ?>
450 485
451 - <!-- Quote Items -->
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 + ?>
452 496 <table class="quote-items">
453 497 <thead>
454 498 <tr>
455 499 <th><?php echo esc_html($text_settings['service']); ?></th>
@@ -454,15 +498,18 @@
454 498 <tr>
455 499 <th><?php echo esc_html($text_settings['service']); ?></th>
456 500 <th><?php echo esc_html($text_settings['qty']); ?></th>
457 501 <th><?php echo esc_html($text_settings['rate_price']); ?></th>
458 - <?php if (\EasyInvoice\Controllers\SettingsController::shouldShowQuoteAdjustField()): ?>
502 + <?php if ($ei_show_adjust): ?>
459 503 <th><?php echo esc_html($text_settings['adjust']); ?> (%)</th>
460 504 <?php endif; ?>
461 - <th><?php echo esc_html($text_settings['total']); ?></th>
505 + <th><?php echo esc_html($text_settings['line_total'] ?? $text_settings['total']); ?></th>
462 506 </tr>
463 507 </thead>
464 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; ?>
465 512 <?php foreach ($quote->getItems() as $item): ?>
466 513 <tr>
467 514 <td class="item-name">
468 515 <div class="item-title"><?php echo esc_html($item->getName()); ?></div>
@@ -469,11 +516,11 @@
469 516 <?php if ($item->getDescription()): ?>
470 517 <div class="item-description"><?php echo esc_html($item->getDescription()); ?></div>
471 518 <?php endif; ?>
472 519 </td>
473 - <td class="item-quantity"><?php echo esc_html($item->getQuantity()); ?></td>
520 + <td class="item-quantity"><?php echo esc_html($item->getQuantity() ?: '0'); ?></td>
474 521 <td class="item-price"><?php echo esc_html($formatter->format($item->getPrice())); ?></td>
475 - <?php if (\EasyInvoice\Controllers\SettingsController::shouldShowQuoteAdjustField()): ?>
522 + <?php if ($ei_show_adjust): ?>
476 523 <td class="item-adjust"><?php
477 524 $adjust_percentage = $item->getAdjustPercentage();
478 525 if ($adjust_percentage != 0) {
479 526 echo esc_html($adjust_percentage > 0 ? '+' : '') . esc_html($adjust_percentage) . '%';
@@ -487,17 +534,15 @@
487 534 <?php endforeach; ?>
488 535 </tbody>
489 536 </table>
490 537
491 - <!-- Quote Totals -->
492 538 <div class="quote-totals">
493 - <?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')); ?>
494 540 </div>
495 541
496 - <!-- Notes -->
497 542 <?php if ($quote->getNotes()): ?>
498 543 <div class="quote-notes">
499 - <h3><?php _e('Notes:', 'easy-invoice'); ?></h3>
544 + <h3><?php esc_html_e('Notes', 'easy-invoice'); ?></h3>
500 545 <div class="notes-content">
501 546 <?php echo wp_kses_post($quote->getNotes() ?: ''); ?>
502 547 </div>
503 548 </div>
@@ -502,15 +547,14 @@
502 547 </div>
503 548 </div>
504 549 <?php endif; ?>
505 550
506 - <!-- Terms and Conditions -->
507 551 <?php
508 552 $terms_conditions = $quote->getTerms() ?: \EasyInvoice\Controllers\SettingsController::getQuoteTermsConditions();
509 553 if ($terms_conditions):
510 554 ?>
511 555 <div class="quote-terms">
512 - <h3><?php _e('Terms & Conditions:', 'easy-invoice'); ?></h3>
556 + <h3><?php esc_html_e('Terms & Conditions', 'easy-invoice'); ?></h3>
513 557 <div class="terms-content">
514 558 <?php echo wp_kses_post($terms_conditions); ?>
515 559 </div>
516 560 </div>
@@ -515,15 +559,23 @@
515 559 </div>
516 560 </div>
517 561 <?php endif; ?>
518 562
519 - <!-- Footer -->
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 + ?>
520 577 <div class="quote-footer">
521 - <?php
522 - $footer_text = $quote->getFooterText();
523 - if (!$footer_text) {
524 - $footer_text = \EasyInvoice\Controllers\SettingsController::getQuoteFooterText();
525 - }
526 - ?>
527 - <p><?php echo wp_kses_post($footer_text); ?></p>
578 + <p><?php echo wp_kses_post($ei_footer_text); ?></p>
528 579 </div>
529 -</div>
580 + <?php endif; ?>
581 +</div>