PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
← All changes | app/Services/ShortCodeParser/Parsers/OrderParser.php +181 -28 1.4.2 → 1.6.5 View file →
@@ -9,8 +9,10 @@
9 9 use FluentCart\App\Models\Order;
10 10 use FluentCart\App\Models\Subscription;
11 11 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
12 12 use FluentCart\App\Services\DateTime\DateTime;
13 +use FluentCart\App\Services\DateTime\DateFormatter;
14 +use FluentCart\App\Services\Payments\PaymentHelper;
13 15 use FluentCart\App\Services\Payments\PaymentReceipt;
14 16 use FluentCart\App\Services\TemplateService;
15 17 use FluentCart\App\Services\Renderer\Receipt\TaxSummaryHelper;
16 18 use FluentCart\Framework\Support\Arr;
@@ -103,14 +105,12 @@
103 105 if (in_array($accessor, ['updated_at', 'created_at'])) {
104 106 $date = Arr::get($this->data, $this->attributeMap[$accessor]);
105 107 $timestamp = DateTime::anyTimeToGmt($date)->getTimestamp();
106 108
107 - $date = wp_date(
108 - get_option('date_format'),
109 - $timestamp,
110 - new \DateTimeZone($this->orderTz)
111 - );
112 -
109 + // Through DateFormatter so the store's format and timezone settings
110 + // reach this too; orderTz is only consulted on the Browser source.
111 + $date = DateFormatter::format($timestamp, false, $this->orderTz);
112 +
113 113 return Helper::translateNumber($date);
114 114 }
115 115
116 116 // Handle _formatted suffix for cent columns (e.g. total_amount_formatted)
@@ -360,8 +360,17 @@
360 360
361 361 return is_user_logged_in() ? $orderLink : wp_login_url($orderLink);
362 362 }
363 363
364 + public function getPaymentLink($accessor = null, $code = null)
365 + {
366 + if (empty($this->order)) {
367 + return $code;
368 + }
369 +
370 + return PaymentHelper::getCustomPaymentLink(Arr::get($this->order, 'uuid'));
371 + }
372 +
364 373 public function getAdminOrderLink($accessor, $code = null)
365 374 {
366 375 if (empty($this->order)) {
367 376 return $code;
@@ -537,29 +546,130 @@
537 546 $normalStyle = 'padding:3px 0;font-size:11px;font-weight:600;color:#525866;';
538 547 $totalStyle = 'padding:6px 0 3px;font-size:11px;font-weight:700;color:#0E121B;border-top:1px solid #e2e8f0;';
539 548 $valueStyle = 'text-align:right;';
540 549
541 - $rows = '<tr><td colspan="2" style="' . $headingStyle . '">'
542 - . esc_html__('TAX SUMMARY', 'fluent-cart')
543 - . '</td></tr>';
550 + $rows = '';
544 551
545 - if ($summary['isReverseCharge']) {
546 - $rcReversedTotal = isset($summary['reversedTaxTotal']) ? (int) $summary['reversedTaxTotal'] : 0;
547 - $rcReversedValue = $rcReversedTotal > 0
548 - ? esc_html(Helper::toDecimal($rcReversedTotal))
549 - : esc_html__('Charge reversed', 'fluent-cart');
552 + $foldedRateLines = Arr::get($summary, 'foldedRateLines', []);
553 + $isRcOrder = !empty($summary['isReverseCharge']);
554 + $rcReversedTotal = isset($summary['reversedTaxTotal']) ? (int) $summary['reversedTaxTotal'] : 0;
555 + $rcReversedValue = $rcReversedTotal > 0
556 + ? esc_html(Helper::toDecimal($rcReversedTotal))
557 + : esc_html__('Charge reversed', 'fluent-cart');
558 + $includedInPrices = (int) Arr::get($summary, 'includedInPrices', 0);
559 + $opFeeRows = Arr::get($summary, 'feeTaxLineRows', []);
560 + $taxRateLines = Arr::get($summary, 'taxRateLines', []);
561 + $productTaxRowCount = !empty($taxRateLines)
562 + ? count($taxRateLines)
563 + : (int) ($summary['inclusiveTax'] > 0) + (int) ($summary['exclusiveTax'] > 0);
564 + $rowCount = $productTaxRowCount + count($opFeeRows) + (int) ($summary['shippingTax'] > 0);
565 + $shippingTaxLines = Arr::get($summary, 'shippingTaxLines', []);
566 + $shouldShowBreakdown = !empty($taxRateLines)
567 + || !empty($shippingTaxLines)
568 + || $rowCount >= 2
569 + || ($rowCount === 1 && !($summary['payableTax'] > 0 || $summary['inclusiveTax'] > 0 || (int) Arr::get($summary, 'inclusiveFeeTax', 0) > 0));
570 +
571 + $isSimplifiedMode = ($summary['displayMode'] ?? '') === 'simplified';
572 +
573 + if ($isSimplifiedMode && !empty($summary['simpleLine'])) {
550 574 $rows .= '<tr>'
575 + . '<td style="' . $normalStyle . '">' . esc_html((string) $summary['simpleLine']['label']) . '</td>'
576 + . '<td style="' . $normalStyle . $valueStyle . '">' . esc_html((string) $summary['simpleLine']['value']) . '</td>'
577 + . '</tr>';
578 +
579 + // Simplified mode: single tax line only, skip the detailed breakdown below.
580 + return $this->wrapTaxBreakdownBox($rows);
581 + }
582 +
583 + if (!empty($foldedRateLines)) {
584 + $colHeadStyle = 'width:58%;padding:3px 8px 3px 0;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.06em;color:#64748b;vertical-align:top;';
585 + $colHeadBaseStyle = 'width:24%;padding:3px 8px 3px 0;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.06em;color:#64748b;text-align:right;white-space:nowrap;vertical-align:top;';
586 + $colHeadTaxStyle = 'width:18%;padding:3px 0;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.06em;color:#64748b;text-align:right;white-space:nowrap;vertical-align:top;';
587 +
588 + $rows .= '<tr><td colspan="2" style="padding:5px 0 2px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:0.06em;color:#64748b;">'
589 + . esc_html__('Tax breakdown by rate', 'fluent-cart')
590 + . '</td></tr>';
591 +
592 + $nestedTable = '<table width="100%" cellpadding="0" cellspacing="0" style="border:none;border-collapse:collapse;table-layout:fixed;">';
593 + $nestedTable .= '<tr>'
594 + . '<td style="' . $colHeadStyle . '">' . esc_html__('Rate', 'fluent-cart') . '</td>'
595 + . '<td style="' . $colHeadBaseStyle . '">' . esc_html__('Taxable base', 'fluent-cart') . '</td>'
596 + . '<td style="' . $colHeadTaxStyle . '">' . esc_html__('Tax', 'fluent-cart') . '</td>'
597 + . '</tr>';
598 +
599 + foreach ($foldedRateLines as $foldedRow) {
600 + $foldedStyle = !empty($foldedRow['inclusive']) ? $mutedStyle : $normalStyle;
601 + $foldedStyleR = $foldedStyle . $valueStyle;
602 + $nestedTable .= '<tr>'
603 + . '<td style="width:58%;' . $foldedStyle . 'padding-right:8px;white-space:normal;word-break:break-word;overflow-wrap:break-word;vertical-align:top;">' . esc_html((string) $foldedRow['label']) . '</td>'
604 + . '<td style="width:24%;' . $foldedStyleR . 'padding-right:8px;white-space:nowrap;vertical-align:top;">' . CurrencySettings::getPriceHtml((int) $foldedRow['base'], $currency) . '</td>'
605 + . '<td style="width:18%;' . $foldedStyleR . 'white-space:nowrap;vertical-align:top;">' . CurrencySettings::getPriceHtml((int) $foldedRow['tax'], $currency) . '</td>'
606 + . '</tr>';
607 + }
608 + $nestedTable .= '</table>';
609 +
610 + $rows .= '<tr><td colspan="2" style="padding:2px 0 0 0;">' . $nestedTable . '</td></tr>';
611 +
612 + if ($isRcOrder) {
613 + $rcReversedTotalValue = $rcReversedTotal > 0
614 + ? CurrencySettings::getPriceHtml($rcReversedTotal, $currency)
615 + : esc_html__('Charge reversed', 'fluent-cart');
616 + $rows .= '<tr>'
617 + . '<td style="' . $totalStyle . '">' . esc_html__('VAT reversed', 'fluent-cart') . '</td>'
618 + . '<td style="' . $totalStyle . $valueStyle . '">' . $rcReversedTotalValue . '</td>'
619 + . '</tr>';
620 + return $this->wrapTaxBreakdownBox($rows);
621 + }
622 +
623 + $rows .= '<tr>'
624 + . '<td style="' . $totalStyle . '">' . esc_html__('Total tax', 'fluent-cart') . '</td>'
625 + . '<td style="' . $totalStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml((int) $summary['totalOrderTax'], $currency) . '</td>'
626 + . '</tr>';
627 +
628 + if ($includedInPrices > 0) {
629 + $rows .= '<tr>'
630 + . '<td style="' . $mutedStyle . '">' . esc_html__('of which included in prices', 'fluent-cart') . '</td>'
631 + . '<td style="' . $mutedStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml($includedInPrices, $currency) . '</td>'
632 + . '</tr>';
633 + }
634 +
635 + if ($summary['payableTax'] > 0 && $includedInPrices > 0) {
636 + $payableStyle = $totalStyle;
637 + $rows .= '<tr>'
638 + . '<td style="' . $payableStyle . '">' . esc_html__('Payable now (added)', 'fluent-cart') . '</td>'
639 + . '<td style="' . $payableStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml((int) $summary['payableTax'], $currency) . '</td>'
640 + . '</tr>';
641 + }
642 +
643 + return $this->wrapTaxBreakdownBox($rows);
644 + }
645 +
646 + if ($isRcOrder) {
647 + $rows .= '<tr><td colspan="2" style="' . $headingStyle . '">'
648 + . esc_html__('TAX SUMMARY', 'fluent-cart')
649 + . '</td></tr>';
650 + $rows .= '<tr>'
551 651 . '<td style="' . $totalStyle . '">' . esc_html__('Tax reversed', 'fluent-cart') . '</td>'
552 652 . '<td style="' . $totalStyle . $valueStyle . '">' . $rcReversedValue . '</td>'
553 653 . '</tr>';
554 - return $rows;
654 + return $this->wrapTaxBreakdownBox($rows);
555 655 }
556 656
557 - $opFeeRows = Arr::get($summary, 'feeTaxLineRows', []);
558 - $rowCount = (int) ($summary['inclusiveTax'] > 0) + (int) ($summary['exclusiveTax'] > 0) + count($opFeeRows) + (int) ($summary['shippingTax'] > 0);
559 - $shouldShowBreakdown = $rowCount >= 2 || ($rowCount === 1 && !($summary['payableTax'] > 0 || $summary['inclusiveTax'] > 0 || (int) Arr::get($summary, 'inclusiveFeeTax', 0) > 0));
657 + $rows .= '<tr><td colspan="2" style="' . $headingStyle . '">'
658 + . esc_html__('TAX SUMMARY', 'fluent-cart')
659 + . '</td></tr>';
560 660
561 - if ($summary['inclusiveTax'] > 0 && $shouldShowBreakdown) {
661 + if (!empty($taxRateLines) && $shouldShowBreakdown) {
662 + foreach ($taxRateLines as $taxLine) {
663 + $taxLineStyle = !empty($taxLine['inclusive']) ? $mutedStyle : $normalStyle;
664 + $rows .= '<tr>'
665 + . '<td style="' . $taxLineStyle . '">' . esc_html($taxLine['label']) . '</td>'
666 + . '<td style="' . $taxLineStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml($taxLine['order_tax'], $currency) . '</td>'
667 + . '</tr>';
668 + }
669 + }
670 +
671 + if (empty($taxRateLines) && $summary['inclusiveTax'] > 0 && $shouldShowBreakdown) {
562 672 $rows .= '<tr>'
563 673 . '<td style="' . $mutedStyle . '">' . esc_html__('Included in item prices', 'fluent-cart') . '</td>'
564 674 . '<td style="' . $mutedStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml($summary['inclusiveTax'], $currency) . '</td>'
565 675 . '</tr>';
@@ -564,9 +674,9 @@
564 674 . '<td style="' . $mutedStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml($summary['inclusiveTax'], $currency) . '</td>'
565 675 . '</tr>';
566 676 }
567 677
568 - if ($summary['exclusiveTax'] > 0 && $shouldShowBreakdown) {
678 + if (empty($taxRateLines) && $summary['exclusiveTax'] > 0 && $shouldShowBreakdown) {
569 679 $rows .= '<tr>'
570 680 . '<td style="' . $normalStyle . '">' . esc_html__('Added on products', 'fluent-cart') . '</td>'
571 681 . '<td style="' . $normalStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml($summary['exclusiveTax'], $currency) . '</td>'
572 682 . '</tr>';
@@ -584,15 +694,24 @@
584 694
585 695 if ($summary['shippingTax'] > 0 && $shouldShowBreakdown) {
586 696 $isShippingInclusive = TaxSummaryHelper::isShippingTaxInclusive($this->order);
587 697 $shippingRowStyle = $isShippingInclusive ? $mutedStyle : $normalStyle;
588 - $shippingRowLabel = $isShippingInclusive
589 - ? esc_html__('Included in shipping prices', 'fluent-cart')
590 - : esc_html__('Added on shipping', 'fluent-cart');
591 - $rows .= '<tr>'
592 - . '<td style="' . $shippingRowStyle . '">' . $shippingRowLabel . '</td>'
593 - . '<td style="' . $shippingRowStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml($summary['shippingTax'], $currency) . '</td>'
594 - . '</tr>';
698 + if (!empty($shippingTaxLines)) {
699 + foreach ($shippingTaxLines as $shippingTaxLine) {
700 + $rows .= '<tr>'
701 + . '<td style="' . $shippingRowStyle . '">' . esc_html($shippingTaxLine['label']) . '</td>'
702 + . '<td style="' . $shippingRowStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml($shippingTaxLine['shipping_tax'], $currency) . '</td>'
703 + . '</tr>';
704 + }
705 + } else {
706 + $shippingRowLabel = $isShippingInclusive
707 + ? esc_html__('Included in shipping prices', 'fluent-cart')
708 + : esc_html__('Added on shipping', 'fluent-cart');
709 + $rows .= '<tr>'
710 + . '<td style="' . $shippingRowStyle . '">' . $shippingRowLabel . '</td>'
711 + . '<td style="' . $shippingRowStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml($summary['shippingTax'], $currency) . '</td>'
712 + . '</tr>';
713 + }
595 714 }
596 715
597 716 if ($summary['payableTax'] > 0) {
598 717 $rows .= '<tr>'
@@ -607,9 +726,43 @@
607 726 . '<td style="' . $mutedStyle . $valueStyle . '">' . CurrencySettings::getPriceHtml($summary['totalOrderTax'], $currency) . '</td>'
608 727 . '</tr>';
609 728 }
610 729
611 - return $rows;
730 + return $this->wrapTaxBreakdownBox($rows);
731 + }
732 +
733 + /**
734 + * Wrap the accumulated tax-breakdown rows in their own grey rounded box,
735 + * right-aligned to a fixed 400px column.
736 + *
737 + * The tax breakdown is the only thing that lives inside the grey box; the
738 + * surrounding Subtotal/Discount/Shipping/Fees rows (above) and Refund/Total/
739 + * Payment rows (below) are rendered as plain rows by the summary table and
740 + * stay outside this wrapper. A table-based 400px right-aligned wrapper is used
741 + * (not a div) so PDF renderers (mPDF/Dompdf) and email clients honour the
742 + * width/alignment, matching the boxed treatment on the thank-you/customer
743 + * surfaces. Consumed by the PDF receipt templates via {{order.tax_breakdown}}.
744 + *
745 + * @param string $rows Inner <tr> rows for the tax breakdown.
746 + * @return string A single <tr><td colspan="2"> cell carrying the grey box.
747 + */
748 + private function wrapTaxBreakdownBox(string $rows): string
749 + {
750 + if ($rows === '') {
751 + return '';
752 + }
753 +
754 + $boxStyle = 'background-color:rgb(249,250,251);padding:16px;border-radius:8px;';
755 +
756 + $nestedTable = '<table width="100%" cellpadding="0" cellspacing="0" style="width:100%;border:none;border-collapse:collapse;">'
757 + . $rows
758 + . '</table>';
759 +
760 + return '<tr><td colspan="2" style="padding:8px 0 0 0;">'
761 + . '<table align="right" width="400" cellpadding="0" cellspacing="0" style="width:400px;max-width:100%;border:none;border-collapse:collapse;margin:0 0 0 auto;">'
762 + . '<tr><td style="' . $boxStyle . '">' . $nestedTable . '</td></tr>'
763 + . '</table>'
764 + . '</td></tr>';
612 765 }
613 766
614 767 private function resolveTaxRateLabel($orderTaxRate): string
615 768 {