PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.5.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.5.0
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
fluent-cart / app / Services / Renderer / Receipt / ReceiptRenderer.php

ReceiptRenderer.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.5.0, at app/Services/Renderer/Receipt/ReceiptRenderer.php

1,060 lines 48.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Renderer\Receipt;
4
5 use FluentCart\App\Helpers\Helper;
6 use FluentCart\Framework\Support\Arr;
7
8 use FluentCart\Api\StoreSettings;
9 use FluentCart\App\Helpers\AddressHelper;
10 use FluentCart\App\Modules\Tax\TaxModule;
11 use FluentCart\App\Services\DateTime\DateTime;
12
13 class ReceiptRenderer
14 {
15 protected $config = null;
16
17 protected $is_first_time = false;
18
19 protected $order_operation = null;
20
21 protected $settings = null;
22
23 protected $vat_tax_id = null;
24
25 protected $orderTz;
26
27 public function __construct($config = [])
28 {
29 $this->config = $config;
30
31 $this->is_first_time = Arr::get($this->config, 'is_first_time', false);
32 $this->order_operation = Arr::get($this->config, 'order_operation', false);
33
34 $this->settings = new StoreSettings();
35
36 $this->orderTz = Arr::get($this->config, 'user_tz', 'UTC');
37 }
38
39 public function wrapperStart()
40 {
41 ?>
42 <div
43 class="fct-receipt-page"
44 style="background-color: #fff;max-width: 620px;margin-left: auto;margin-right: auto;border: 1px solid #e7eaee;padding: 10px;border-radius: 8px;">
45 <div
46 class="fct-receipt-page-inner"
47 style=" border-radius: 5px;" id="receipt">
48 <div class="fct-email-template-content email-template-content"
49 style="font-family: Arial, Helvetica, sans-serif; margin: 0 auto; padding: 0px; width: 100%;">
50 <div class="fct-email-template-content-inner" style="font-size: 13px;line-height: 1.4;color: #333;padding: 20px;">
51
52 <?php
53 }
54
55 public function wrapperEnd()
56 {
57 ?>
58 </div>
59 </div>
60 </div>
61 </div>
62 <?php
63 }
64
65 public function render($hideWrapper = false)
66 {
67
68 $order = Arr::get($this->config, 'order', null);
69 if (!$order) {
70 return;
71 }
72
73 $this->vat_tax_id = $order->getCustomerTaxNumber();
74
75 $profilePage = $this->settings->getCustomerProfilePage();
76 $orderTaxRates = $order->getPrimaryOrderTaxRate();
77
78 ?>
79
80 <style>
81 html {
82 background-color: rgb(248, 249, 250);
83 font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
84 }
85
86 body {
87 background-color: rgb(248, 249, 250);
88 padding-top: 40px;
89 padding-bottom: 40px;
90 font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
91 }
92
93 p {
94 font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
95 font-size: 14px;
96 margin: 0px;
97 margin-bottom: 8px;
98 line-height: 24px;
99 }
100
101 .email_footer p {
102 font-size: 12px;
103 color: rgb(127, 140, 141);
104 margin: 0px;
105 line-height: 24px;
106 margin-bottom: 8px;
107 }
108
109 hr {
110 border-color: rgb(229, 231, 235);
111 margin-bottom: 20px;
112 width: 100%;
113 border: none;
114 border-top: 1px solid #eaeaea;
115 }
116
117 .space_bottom_30 {
118 display: block;
119 width: 100%;
120 margin-bottom: 30px;
121 }
122
123 .fct-transaction-table tr:last-child td {
124 border-bottom: none !important;
125 padding-bottom: 0 !important;
126 }
127 </style>
128
129 <?php if (!$hideWrapper) {
130 $this->wrapperStart();
131 } ?>
132 <?php $this->renderHeader(); ?>
133
134 <?php $this->renderAddresses(); ?>
135
136 <?php $this->renderOrderItems(); ?>
137
138 <!-- tax note -->
139 <?php $this->renderTaxNote(); ?>
140
141 <?php $this->renderPaymentHistory(); ?>
142 <?php if (!$hideWrapper) {
143 $this->wrapperEnd();
144 } ?>
145
146
147 <?php if ($this->is_first_time) {
148 do_action('fluent_cart/order/receipt_viewed', [
149 'order' => $order,
150 'order_operation' => $this->order_operation
151 ]);
152 } ?>
153 <?php
154 }
155
156 public function renderHeader()
157 {
158 $order = Arr::get($this->config, 'order', null);
159 ?>
160 <table
161 class="fct-receipt-header"
162 role="presentation"
163 style="width: 100%;border-collapse: collapse;margin-bottom: 10px;border: none;">
164 <tbody style="width:100%">
165 <tr style="width:100%">
166 <td style="width:60%;border: none;">
167 <?php $this->renderHeaderLogo(); ?>
168
169 <!-- show tax content -->
170 <?php $this->renderStoreTaxInformation(); ?>
171 </td>
172 <td
173 class="fct-receipt-header-date"
174 style="vertical-align: middle; width: 15%; text-align: right; border: none;padding-right: 10px;">
175 <?php $this->renderHeaderOrderDate(); ?>
176 </td>
177 <td
178 class="fct-receipt-header-invoice-no"
179 style="width:15%;text-align:right;border:none;vertical-align:baseline;">
180 <?php $this->renderHeaderInvoiceNo(); ?>
181 </td>
182 </tr>
183 </tbody>
184 </table>
185 <?php
186 }
187
188 public function renderHeaderLogo()
189 {
190 ?>
191 <h1 style="font-size:24px;font-weight:700;color:rgb(17,24,39);margin:0px">
192 <?php
193 $imageLink = $this->settings->get('store_logo.url');
194 $storeName = $this->settings->get('store_name');
195 if (empty($imageLink)) {
196 echo esc_html($storeName);
197 } else {
198 echo "<img src=".esc_url($imageLink)." alt=".esc_attr($storeName)." style='max-height: 40px;'>";
199 }
200 ?>
201 </h1>
202 <?php
203 }
204
205 public function renderStoreTaxInformation()
206 {
207 $order = Arr::get($this->config, 'order', null);
208 $orderTaxRates = $order->getPrimaryOrderTaxRate();
209 $storeVatNumber = Arr::get($orderTaxRates->meta ?? [], 'store_vat_number', '');
210 $taxcountry = Arr::get($orderTaxRates->meta ?? [], 'tax_country', '');
211 if (!empty($storeVatNumber)): ?>
212 <span class="fct_invoice_tax_content" style="font-weight: 500; font-size: 14px;">
213 <?php echo esc_html(TaxModule::getCountryTaxTitle($taxcountry)); ?>:
214 <?php
215 if ($storeVatNumber !== '') {
216 echo esc_html($storeVatNumber);
217 }
218 ?>
219 </span>
220 <?php endif;
221 }
222
223 public function renderHeaderOrderDate()
224 {
225 $order = Arr::get($this->config, 'order', null);
226 ?>
227 <p style="white-space: nowrap; color: #94a3b8; text-align: right; margin: 0;font-size:12px;print-color-adjust: exact;">
228 <?php echo esc_html__('Order At', 'fluent-cart'); ?>
229 </p>
230 <p style="white-space: nowrap; font-weight: bold; color: #000; text-align: right; margin: 0;font-size:14px;">
231 <?php
232 $date = wp_date(
233 get_option('date_format'),
234 DateTime::anyTimeToGmt($order->created_at)->getTimestamp(),
235 new \DateTimeZone($this->orderTz)
236 );
237 echo esc_html(Helper::translateNumber($date));
238 ?>
239 </p>
240 <?php
241 }
242
243 public function renderHeaderInvoiceNo()
244 {
245 $order = Arr::get($this->config, 'order', null);
246 ?>
247 <p style="white-space: nowrap; color: #94a3b8; text-align: right; margin: 0;font-size:12px;print-color-adjust: exact;">
248 <?php echo esc_html__('Invoice number #', 'fluent-cart'); ?>
249 </p>
250 <p id="fct-order-invoice-no"
251 style="white-space: nowrap; font-weight: bold; color: #000; text-align: right; margin: 0;font-size:14px;">
252 <?php echo esc_html($order->invoice_no); ?>
253 </p>
254 <?php
255 }
256
257 public function renderAddresses()
258 {
259 $order = Arr::get($this->config, 'order', null);
260 ?>
261
262 <?php if ($order->fulfillment_type === 'physical') : ?>
263 <div
264 class="fct-receipt-page-store-address"
265 style="background: #f8f9fa;padding: 15px;margin-bottom: 20px;print-color-adjust: exact;">
266 <?php $this->renderStoreAddress(); ?>
267 </div>
268 <?php endif; ?>
269 <div
270 class="fct-receipt-page-order-items-addresses"
271 style="display: grid;grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));gap: 20px;">
272 <?php if ($order->fulfillment_type === 'digital') : ?>
273 <div
274 class="fct-receipt-page-store-address"
275 style="border-radius: 5px;print-color-adjust: exact;">
276 <?php $this->renderStoreAddress(true); ?>
277 </div>
278 <?php endif; ?>
279 <?php $this->renderBillToAddress(); ?>
280 <?php if ($order->fulfillment_type === 'physical') : ?>
281 <?php $this->renderShipToAddress(); ?>
282 <?php endif; ?>
283 </div>
284 <?php
285 }
286
287 public function renderStoreAddress($title_border = false)
288 {
289 $order = Arr::get($this->config, 'order', null);
290 ?>
291 <?php if ($title_border) : ?>
292 <h5
293 class="fct-receipt-page-store-address-name"
294 style="font-weight: bold;font-size: 14px;margin: 0 0 10px 0;color: #495057;border-bottom: 1px solid #dee2e6;padding-bottom: 5px;">
295 <?php echo esc_html($this->settings->get('store_name')); ?>
296 </h5>
297 <?php else: ?>
298 <div
299 class="fct-receipt-page-store-address-name"
300 style="font-size: 18px;font-weight: bold;margin-bottom: 8px;">
301 <?php echo esc_html($this->settings->get('store_name')); ?>
302 </div>
303 <?php endif; ?>
304 <div
305 class="fct-receipt-page-store-address-address"
306 style="margin-bottom: 3px;">
307 <?php echo esc_html($this->settings->getFormattedFullAddress()); ?>
308 </div>
309 <?php
310 $orderTaxRates = $order->getPrimaryOrderTaxRate();
311 $storeVatNumber = Arr::get($orderTaxRates->meta ?? [], 'store_vat_number', '');
312 $taxCountry = Arr::get($orderTaxRates->meta ?? [], 'tax_country', '');
313 if (!empty($storeVatNumber)): ?>
314 <div
315 class="fct-receipt-page-store-address-vat"
316 style="margin-top: 5px;font-weight: 500;">
317 <?php echo esc_html(TaxModule::getCountryTaxTitle($taxCountry)) . ': ' . esc_html($storeVatNumber); ?>
318 </div>
319 <?php endif;
320 }
321
322 public function renderBillToAddress()
323 {
324 $order = Arr::get($this->config, 'order', null);
325 $orderTaxRates = $order->getPrimaryOrderTaxRate();
326 ?>
327 <div
328 class="fct-receipt-page-order-items-addresses-bill-to"
329 style="border-radius: 5px;print-color-adjust: exact;">
330 <h5
331 class="fct-receipt-page-order-items-addresses-bill-to-title"
332 style="font-weight: bold;font-size: 14px;margin: 0 0 10px 0;color: #495057;border-bottom: 1px solid #dee2e6;padding-bottom: 5px;">
333 <?php echo esc_html__('Bill To', 'fluent-cart'); ?>
334 </h5>
335 <?php if (!empty($order->billing_address)) : ?>
336
337 <div
338 class="fct-receipt-page-order-items-addresses-bill-to-address"
339 style="margin-bottom: 3px;">
340 <?php echo esc_html($order->billing_address->getAddressAsText()); ?>
341 </div>
342 <div
343 class="fct-receipt-page-order-items-addresses-bill-to-email"
344 style="margin-top: 10px;">
345 <?php echo esc_html($order->customer->email); ?>
346
347 </div>
348 <?php
349 $phoneNumber = Arr::get($order->billing_address->meta ?? [], 'other_data.phone', '');
350 if ($phoneNumber !== ''):
351 ?>
352 <div
353 class="fct-receipt-page-order-items-addresses-bill-to-phone"
354 style="margin-top: 3px;">
355 <?php echo esc_html($phoneNumber); ?>
356 </div>
357 <?php endif; ?>
358 <?php
359 $companyName = Arr::get($order->billing_address->meta ?? [], 'other_data.company_name', '');
360 if ($companyName === '') {
361 $companyName = $order->getCustomerTaxName();
362 }
363 if ($companyName !== ''):
364 ?>
365 <div class="fct-receipt-page-order-items-addresses-bill-to-company-name">
366 <?php echo esc_html($companyName); ?>
367 </div>
368 <?php endif; ?>
369 <?php
370 $legalRegId = Arr::get($order->billing_address->meta ?? [], 'other_data.legal_registration_id', '');
371 if ($legalRegId === '') {
372 $legalRegId = Arr::get($order->getBusinessInfo(), 'legal_registration_id', '');
373 }
374 if ($legalRegId !== ''):
375 ?>
376 <div class="fct-receipt-page-order-items-addresses-bill-to-legal-reg" style="margin-top: 3px;">
377 <?php echo esc_html__('Reg. ID', 'fluent-cart') . ': ' . esc_html($legalRegId); ?>
378 </div>
379 <?php endif; ?>
380 <div
381 class="fct-receipt-page-order-items-addresses-bill-to-vat-number"
382 style="margin-top: 5px;">
383 <?php
384 $vatNumber = $order->getCustomerTaxNumber();
385 if ($vatNumber !== '') {
386 $vatLabel = __('VAT/Tax ID', 'fluent-cart');
387 echo esc_html($vatLabel) . ': ' . esc_html($vatNumber);
388 }
389 ?>
390 </div>
391 <?php
392 $reverseChargeDeclaration = Arr::get($order->getBusinessInfo(), 'reverse_charge_declaration', '');
393 if ($reverseChargeDeclaration !== ''):
394 ?>
395 <div class="fct-receipt-page-order-items-addresses-bill-to-reverse-charge" style="margin-top: 5px;">
396 <strong><?php echo esc_html__('Reverse Charge Declaration', 'fluent-cart'); ?>:</strong>
397 <?php echo esc_html($reverseChargeDeclaration); ?>
398 </div>
399 <?php endif; ?>
400
401 <?php else: ?>
402 <div
403 class="fct-receipt-page-order-items-addresses-bill-to-name"
404 style="margin-top: 10px;">
405 <?php echo esc_html($order->customer->full_name); ?>
406 </div>
407 <div
408 class="fct-receipt-page-order-items-addresses-bill-to-email"
409 style="margin-top: 3px;">
410 <?php echo esc_html($order->customer->email); ?>
411 </div>
412 <?php endif; ?>
413 </div>
414 <?php
415 }
416
417 public function renderShipToAddress()
418 {
419 $order = Arr::get($this->config, 'order', null);
420 ?>
421 <div
422 class="fct-receipt-page-order-items-addresses-ship-to"
423 style="border-radius: 5px;print-color-adjust: exact;">
424 <h5
425 class="fct-receipt-page-order-items-addresses-ship-to-title"
426 style="font-weight: bold;font-size: 14px;margin: 0 0 10px 0;color: #495057;border-bottom: 1px solid #dee2e6;padding-bottom: 5px;">
427 <?php echo esc_html__('Ship To', 'fluent-cart'); ?>
428 </h5>
429 <?php if (!empty($order->shipping_address)) : ?>
430 <div
431 class="fct-receipt-page-order-items-addresses-ship-to-address"
432 style="margin-bottom: 3px;">
433 <?php echo esc_html($order->shipping_address->getAddressAsText()); ?>
434 </div>
435 <?php
436 $phone = Arr::get($order->shipping_address->meta ?? [], 'other_data.phone', '');
437 if ($phone !== ''):
438 ?>
439 <div
440 class="fct-receipt-page-order-items-addresses-ship-to-phone"
441 style="margin-top: 3px;">
442 <?php echo esc_html($phone); ?>
443 </div>
444 <?php endif; ?>
445 <?php else: ?>
446 <div
447 class="fct-receipt-page-order-items-addresses-ship-to-name"
448 style="margin-top: 10px;">
449 <?php echo esc_html($order->customer->full_name); ?>
450 </div>
451 <div
452 class="fct-receipt-page-order-items-addresses-ship-to-email"
453 style="margin-top: 3px;">
454 <?php echo esc_html($order->customer->email); ?>
455 </div>
456 <?php endif; ?>
457 </div>
458 <?php
459 }
460
461 public function renderOrderItems()
462 {
463 $order = Arr::get($this->config, 'order', null);
464 if ($order->order_items) : ?>
465 <?php $this->renderOrderItemsTable(); ?>
466
467 <?php $this->renderOrderItemsFooter(); ?>
468 <?php endif;
469 }
470
471 public function renderOrderItemsTable()
472 {
473 $order = Arr::get($this->config, 'order', null);
474 if ($order->order_items) : ?>
475 <table
476 class="fct-receipt-page-order-items-table"
477 style="margin-top: 20px;margin-bottom: 10px;width: 100%;text-align: left;border-spacing: 0;border-collapse: collapse;border: none;">
478 <thead>
479 <tr>
480 <th style="background-color: #f8f9fa;padding: 12px 8px;font-weight: bold;print-color-adjust: exact;border: none;">
481 <?php echo esc_html__('Description', 'fluent-cart'); ?>
482 </th>
483 <th style="background-color: #f8f9fa;padding: 12px 8px;font-weight: bold;width: 80px;text-align: right;print-color-adjust: exact;border: none;">
484 <?php echo esc_html__('Qty', 'fluent-cart'); ?>
485 </th>
486 <th style="background-color: #f8f9fa;padding: 12px 8px;font-weight: bold;width: 80px;text-align: right;print-color-adjust: exact;border: none;">
487 <?php echo esc_html__('Unit price', 'fluent-cart'); ?>
488 </th>
489 <th style="background-color: #f8f9fa;padding: 12px 8px;font-weight: bold;width: 80px;text-align: right;print-color-adjust: exact;border: none;">
490 <?php echo esc_html__('Amount', 'fluent-cart'); ?>
491 </th>
492 </tr>
493 </thead>
494 <tbody>
495 <?php
496 $orderItems = $order->order_items->toArray();
497 $isReversed = $order->isReverseChargeTaxOrder();
498 $rcMode = $order->getOrderRcMode();
499
500 foreach ($orderItems as $item) :
501 if (($item['payment_type'] ?? '') === 'fee') {
502 continue;
503 }
504 $itemRates = TaxSummaryHelper::getItemTaxRates($item);
505 $itemBorder = empty($itemRates) ? '1px solid #dee2e6' : 'none';
506 ?>
507 <tr>
508 <td style="font-size:15px;padding: 12px 8px;border: none;border-bottom: <?php echo esc_attr($itemBorder); ?>;print-color-adjust: exact;">
509 <?php echo esc_html($item['post_title']); ?>
510 <br>
511 <?php if (!empty($item['title'])) : ?>
512 <small style="font-size: 13px;color: #758195;">- <?php echo esc_html($item['title']); ?></small>
513 <br>
514 <?php endif; ?>
515 <?php if (!empty($item['payment_info'])) : ?>
516 <small style="font-size: 13px;color: #758195;"><?php echo esc_html($item['payment_info']); ?></small>
517 <?php endif; ?>
518 <?php if (empty($itemRates) && !empty($item['tax_amount'])) : ?>
519 <small style="font-size: 12px; color: #94a3b8; display:block; margin-top:2px;">
520 <?php
521 /* translators: %1$s: formatted tax amount */
522 echo esc_html(sprintf(__('Tax: %1$s', 'fluent-cart'), Helper::toDecimal((int) $item['tax_amount']))); ?>
523 </small>
524 <?php endif; ?>
525 </td>
526 <td style="padding: 12px 8px;border: none;border-bottom: <?php echo esc_attr($itemBorder); ?>;text-align: right; vertical-align: top;">
527 <?php echo esc_html(Helper::translateNumber($item['quantity'])); ?>
528 </td>
529 <?php $couponDiscount = (int) Arr::get($item['line_meta'] ?? [], 'coupon_discount', 0); ?>
530 <td style="padding: 12px 8px;border: none;border-bottom: <?php echo esc_attr($itemBorder); ?>;text-align: right; vertical-align: top;">
531 <?php echo esc_html(\FluentCart\Api\CurrencySettings::getFormattedPrice($item['unit_price'], null, false, true, true)); ?>
532 </td>
533 <td style="padding: 12px 8px;border: none;border-bottom: <?php echo esc_attr($itemBorder); ?>;text-align: right; vertical-align: top;">
534 <?php if ($couponDiscount > 0): ?>
535 <span style="text-decoration:line-through;opacity:0.5;font-size:11px;display:block;">
536 <?php echo esc_html(Helper::toDecimal($item['subtotal'])); ?>
537 </span>
538 <?php endif; ?>
539 <?php echo esc_html($item['formatted_total']); ?>
540 </td>
541 </tr>
542 <?php if (!empty($itemRates)) : ?>
543 <tr>
544 <td colspan="4" style="padding:0 8px 8px 8px;border:none;border-bottom:1px solid #dee2e6;">
545 <?php echo $this->renderTaxRatePills($itemRates, $isReversed, $rcMode); ?>
546 </td>
547 </tr>
548 <?php endif; ?>
549 <?php endforeach; ?>
550 </tbody>
551 </table>
552 <?php endif;
553 }
554
555 public function renderOrderItemsFooter()
556 {
557 $order = Arr::get($this->config, 'order', null);
558 ?>
559 <div
560 class="fct-receipt-page-order-items-footer"
561 style="margin-top: 20px;">
562 <table
563 class="fct-receipt-page-order-items-footer-table"
564 style="max-width: 250px;width: 100%;margin-left: auto;border-collapse: collapse;background: #f8f9fa;print-color-adjust: exact;border: none; border-radius: 8px;">
565 <tbody>
566 <?php $this->renderSubtotal(); ?>
567 <?php $this->renderDiscount(); ?>
568 <?php $this->renderShipping(); ?>
569 <?php $this->renderFees(); ?>
570 <?php $this->renderTaxSummaryBox(); ?>
571 <?php $this->renderProrateCredit(); ?>
572 <?php $this->renderRefund(); ?>
573 <?php $this->renderTotal(); ?>
574 <?php $this->renderAmountPaid(); ?>
575 </tbody>
576 </table>
577 </div>
578 <?php
579 }
580
581 public function renderSubtotal()
582 {
583 $order = Arr::get($this->config, 'order', null);
584 if ($order->subtotal != ($order->total_amount - $order->total_refund) || $order->tax_total > 0): ?>
585 <tr>
586 <td style="text-align: right;border: none; padding-top: 20px;">
587 <?php echo esc_html__('Subtotal', 'fluent-cart'); ?>
588 </td>
589 <td style="font-weight:700; padding-right: 8px; padding-top: 20px;width: 100px;text-align: right;border: none;">
590 <?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($order->subtotal));
591 ?>
592 </td>
593 </tr>
594 <?php endif;
595 }
596
597 public function renderDiscount()
598 {
599 $order = Arr::get($this->config, 'order', null);
600 $prorateCredit = (int) \FluentCart\Framework\Support\Arr::get($order->config, 'prorate_credit', 0);
601 $upgradeDiscount = $order->manual_discount_total - $prorateCredit;
602 // When the prorate credit IS the whole discount, label the row directly
603 // instead of showing "Discount" with a single redundant breakdown row.
604 $onlyProrateCredit = $prorateCredit > 0 && $upgradeDiscount <= 0 && $order->coupon_discount_total <= 0;
605
606 if ($order->manual_discount_total + $order->coupon_discount_total > 0): ?>
607 <tr>
608 <td style="text-align: right;border: none;">
609 <?php echo $onlyProrateCredit ? esc_html__('Prorate Credit', 'fluent-cart') : esc_html__('Discount', 'fluent-cart'); ?>
610 </td>
611 <td style="padding-right:8px; width: 100px;text-align: right;border: none;">
612 - <?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($order->manual_discount_total + $order->coupon_discount_total)); ?>
613 </td>
614 </tr>
615 <?php if ($prorateCredit > 0 && $upgradeDiscount > 0): ?>
616 <tr>
617 <td style="padding-left:20px;text-align: right;border: none;font-size:12px;color:rgb(107,114,128);">
618 <?php echo esc_html__('Upgrade Discount', 'fluent-cart'); ?>
619 </td>
620 <td style="padding-right:8px;width: 100px;text-align: right;border: none;font-size:12px;color:rgb(107,114,128);">
621 <?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($upgradeDiscount)); ?>
622 </td>
623 </tr>
624 <?php endif; ?>
625 <?php if ($prorateCredit > 0 && !$onlyProrateCredit): ?>
626 <tr>
627 <td style="padding-left:20px;text-align: right;border: none;font-size:12px;color:rgb(107,114,128);">
628 <?php echo esc_html__('Prorate Credit', 'fluent-cart'); ?>
629 </td>
630 <td style="padding-right:8px;width: 100px;text-align: right;border: none;font-size:12px;color:rgb(107,114,128);">
631 <?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($prorateCredit)); ?>
632 </td>
633 </tr>
634 <?php endif; ?>
635 <?php endif;
636 }
637
638 public function renderProrateCredit()
639 {
640 // Rendered as a sub-row inside renderDiscount.
641 }
642
643 public function renderShipping()
644 {
645 $order = Arr::get($this->config, 'order', null);
646 if ($order->shipping_total <= 0) {
647 return;
648 }
649 $displayShipping = (int) $order->shipping_total;
650 if ($order->isReverseChargeTaxOrder()) {
651 $rcAdj = (int) Arr::get(\FluentCart\App\Services\Renderer\Receipt\TaxSummaryHelper::computeTaxSummary($order), 'rcShippingAdjustment', 0);
652 if ($rcAdj > 0) {
653 $displayShipping = max(0, $displayShipping - $rcAdj);
654 }
655 }
656 ?>
657 <tr>
658 <td style="padding: 8px 20px 8px 0;text-align: right;border: none;">
659 <?php echo esc_html__('Shipping', 'fluent-cart'); ?>
660 </td>
661 <td style="padding: 8px 8px 8px 0;width: 100px;text-align: right;border: none;">
662 <?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($displayShipping)); ?>
663 </td>
664 </tr>
665 <?php
666 }
667
668 public function renderFees()
669 {
670 $order = Arr::get($this->config, 'order', null);
671 if (!$order || $order->fee_total <= 0) {
672 return;
673 }
674 $feeItems = $order->feeItems()->get();
675 foreach ($feeItems as $feeItem): ?>
676 <tr>
677 <td style="padding: 8px 20px 8px 0;text-align: right;border: none;">
678 <?php echo esc_html($feeItem->title); ?>
679 </td>
680 <td style="padding: 8px 8px 8px 0;width: 100px;text-align: right;border: none;">
681 <?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($feeItem->subtotal)); ?>
682 </td>
683 </tr>
684 <?php endforeach;
685 }
686
687 public function renderTaxTotal()
688 {
689 $order = Arr::get($this->config, 'order', null);
690
691 if ($order->isReverseChargeTaxOrder()): ?>
692 <?php
693 $rcReversedTotal = $order->getReversedTaxTotal();
694 $rcChargeLabel = $rcReversedTotal > 0
695 ? sprintf(
696 /* translators: %1$s: formatted reversed tax amount */
697 __('Tax reversed: %1$s', 'fluent-cart'),
698 Helper::toDecimal($rcReversedTotal)
699 )
700 : __('Charge reversed', 'fluent-cart');
701 ?>
702 <tr>
703 <td style="padding: 8px 20px 8px 0;text-align: right;border: none;">
704 <?php echo esc_html__('Tax', 'fluent-cart'); ?>
705 </td>
706 <td style="padding: 8px 8px 8px 0;width: 100px;text-align: right;border: none;">
707 <?php echo esc_html($rcChargeLabel); ?>
708 </td>
709 </tr>
710 <?php elseif ($order->tax_total > 0): ?>
711 <tr>
712 <td style="padding: 8px 20px 8px 0;text-align: right;border: none;">
713 <?php echo esc_html__('Tax', 'fluent-cart');
714 echo esc_html(\FluentCart\App\Helpers\Helper::getOrderTaxLabel($order));
715 ?>
716 </td>
717 <td style="padding: 8px 8px 8px 0;width: 100px;text-align: right;border: none;">
718 <?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($order->tax_total)); ?>
719 </td>
720 </tr>
721 <?php endif;
722 }
723
724 public function renderShippingTax()
725 {
726 $order = Arr::get($this->config, 'order', null);
727
728 if ($order->shipping_tax <= 0) {
729 return;
730 }
731 ?>
732 <tr>
733 <td style="padding: 8px 20px 8px 0;text-align: right;border: none;">
734 <?php echo esc_html__('Shipping Tax', 'fluent-cart');
735 echo esc_html(\FluentCart\App\Helpers\Helper::getOrderTaxLabel($order));
736 ?>
737 </td>
738 <td style="padding: 8px 8px 8px 0;width: 100px;text-align: right;border: none;">
739 <?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($order->shipping_tax)); ?>
740 </td>
741 </tr>
742 <?php
743 }
744
745 public function renderRefund()
746 {
747 $order = Arr::get($this->config, 'order', null);
748 if ($order->total_refund > 0): ?>
749 <tr style="font-weight: bold;font-size: 14px;">
750 <td style="font-weight:500;padding: 8px 20px 8px 0;text-align: right;border:none;">
751 <?php echo esc_html__('Refund', 'fluent-cart'); ?>
752 </td>
753 <td style="padding: 8px 8px 8px 0;width: 100px;text-align: right;border:none;">
754 - <?php echo esc_html(\FluentCart\App\Helpers\Helper::toDecimal($order->total_refund)); ?>
755 </td>
756 </tr>
757 <?php endif;
758 }
759
760 public function renderTotal()
761 {
762 $order = Arr::get($this->config, 'order', null);
763 $displayTotal = (int) $order->total_amount - (int) $order->total_refund;
764 if ($order->isReverseChargeTaxOrder()) {
765 $rcAdj = (int) Arr::get(\FluentCart\App\Services\Renderer\Receipt\TaxSummaryHelper::computeTaxSummary($order), 'rcTotalAdjustment', 0);
766 if ($rcAdj > 0) {
767 $displayTotal = max(0, $displayTotal - $rcAdj);
768 }
769 }
770 ?>
771 <tr style="font-weight: bold;font-size: 14px;">
772 <td style="font-weight:500;text-align: right;border:none;">
773 <?php echo esc_html__('Total', 'fluent-cart'); ?>
774 </td>
775 <td style="width: 100px;text-align: right;border:none; padding-right: 8px;">
776 <?php echo esc_html(\FluentCart\Api\CurrencySettings::getFormattedPrice($displayTotal, null, false, true, true)); ?>
777 </td>
778 </tr>
779 <?php
780 }
781
782 public function renderAmountPaid()
783 {
784 $order = Arr::get($this->config, 'order', null);
785 ?>
786 <tr style="font-weight: bold;font-size: 14px;">
787 <td style="font-weight:500;text-align: right;border:none; padding-bottom: 20px">
788 <?php echo esc_html__('Amount Paid', 'fluent-cart'); ?>
789 </td>
790 <td style="width: 100px;text-align: right;border:none;padding-bottom: 20px; padding-right: 8px;">
791 <?php echo esc_html(\FluentCart\Api\CurrencySettings::getFormattedPrice(($order->total_paid - $order->total_refund), null, false, true, true)); ?>
792 </td>
793 </tr>
794 <?php
795 }
796
797 public function renderTaxNote()
798 {
799 $order = Arr::get($this->config, 'order', null);
800 if ($order->isReverseChargeTaxOrder()): ?>
801
802 <div style="text-align: right; font-size: 14px; margin-top: 10px;">
803 <?php echo esc_html__('* Tax to be paid on reverse charge basis', 'fluent-cart'); ?>
804 </div>
805
806 <?php endif;
807 }
808
809 public function renderPaymentHistory()
810 {
811 $order = Arr::get($this->config, 'order', null);
812 $transactions = $order->transactions;
813 if (!empty($transactions)) :
814 ?>
815 <div
816 class="fct-payment-history"
817 style="margin-top: 30px;">
818 <div
819 class="fct-payment-history-heading"
820 style="font-weight: bold;font-size: 14px;color: #495057;margin: 0;padding: 0;">
821 <?php echo esc_html__('Payment history', 'fluent-cart'); ?>
822 </div>
823 <table class="fct-transaction-table"
824 style="margin-top: 10px;width: 100%;text-align: left;border-spacing: 0;border-collapse: collapse;border: none;">
825 <thead>
826 <tr>
827 <th style="background-color: #f8f9fa;padding: 12px 8px;font-weight: bold;print-color-adjust: exact;border:none;">
828 <?php echo esc_html__('Payment method', 'fluent-cart'); ?>
829 </th>
830 <th style="background-color: #f8f9fa;padding: 12px 8px;font-weight: bold; text-align: center;print-color-adjust: exact;border:none;">
831 <?php echo esc_html__('Date', 'fluent-cart'); ?>
832 </th>
833 <th style="background-color: #f8f9fa;padding: 12px 8px;font-weight: bold; text-align: right;print-color-adjust: exact;border:none;">
834 <?php echo esc_html__('Amount', 'fluent-cart'); ?>
835 </th>
836 </tr>
837 </thead>
838 <tbody>
839 <?php foreach ($transactions as $transaction) :
840 if ($transaction->transaction_type === 'refund') {
841 continue;
842 }
843 ?>
844 <tr>
845 <td style="padding: 10px;border:none;border-bottom: 1px solid #dee2e6;">
846 <?php echo esc_html($transaction->getPaymentMethodText()); ?>
847 </td>
848 <td style="padding: 10px;border:none;border-bottom: 1px solid #dee2e6;text-align: center;">
849 <?php
850 $date = wp_date(
851 get_option('date_format'),
852 DateTime::anyTimeToGmt($transaction->created_at)->getTimestamp(),
853 new \DateTimeZone($this->orderTz)
854 );
855 echo esc_html(Helper::translateNumber($date));
856 ?>
857 </td>
858 <td style="padding: 10px;border:none;border-bottom: 1px solid #dee2e6;text-align: right;">
859 <?php echo esc_html(\FluentCart\Api\CurrencySettings::getFormattedPrice($transaction->total, null, false, true, true)); ?>
860 </td>
861 </tr>
862 <?php endforeach; ?>
863 </tbody>
864 </table>
865 </div>
866 <?php endif;
867 }
868
869 public function renderConfirmationError($errorData)
870 {
871 $failed_reason = Arr::get($errorData, 'failed_reason', '');
872 $custom_payment_url = Arr::get($errorData, 'custom_payment_url', '');
873 ?>
874 <style>
875 .fluent_cart_confirmation_failed {
876 max-width: 700px;
877 margin: 0 auto;
878 padding: 30px;
879 border-radius: 9px;
880 box-shadow: 1px 1px 5px 0px #ccc;
881 border-left: 3px solid red;
882 }
883 </style>
884 <div class="fluent_cart_confirmation_failed">
885 <h3><?php echo esc_html__('Payment Confirmation Failed!', 'fluent-cart'); ?></h3>
886 <span><?php echo esc_html__('We are sorry, your order is placed but payment confirmation has failed.', 'fluent-cart'); ?></span>
887 <?php if ($failed_reason) : ?>
888 <p style="color:red;font-style: italic;"><?php echo esc_html__('Failure reason: ', 'fluent-cart') . esc_html($failed_reason) ?></p>
889 <?php endif; ?>
890 <span><?php echo esc_html__('Please try to complete payment again from here!', 'fluent-cart'); ?></span>
891 <a href="<?php echo esc_url($custom_payment_url ?? ''); ?>"><?php echo esc_url($custom_payment_url); ?></a>
892 </div>
893 <?php }
894
895 private function renderTaxRatePills(array $rates, bool $isReversed = false, string $rcMode = 'fixed')
896 {
897 $html = '';
898 foreach ($rates as $rate) {
899 $isInclusive = !empty($rate['inclusive']);
900 $badgeBg = $isInclusive ? '#EAF3DE' : '#FAEEDA';
901 $textColor = $isInclusive ? '#3B6D11' : '#854F0B';
902 $shouldStrike = $isReversed && (!$isInclusive || $rcMode === 'dynamic');
903 $amountStyle = 'color:' . esc_attr($textColor) . ';font-size:11px;font-weight:500;'
904 . ($shouldStrike ? 'text-decoration:line-through;opacity:0.6;' : '');
905 /* translators: %1$s: formatted tax amount (e.g. $12.00) */
906 $amountDisplay = $isInclusive
907 ? esc_html(sprintf(__('incl. %1$s', 'fluent-cart'), Helper::toDecimal($rate['tax_amount'])))
908 : esc_html(Helper::toDecimal($rate['tax_amount']));
909 $html .= '<table width="100%" cellpadding="0" cellspacing="0" style="border:none;margin-top:3px;">';
910 $html .= '<tr>';
911 $html .= '<td style="padding:0;border:none;">';
912 $html .= '<span style="background-color:' . esc_attr($badgeBg) . ';color:' . esc_attr($textColor) . ';font-size:11px;padding:2px 7px;border-radius:4px;display:inline-block;">' . esc_html($rate['label']) . ' (' . esc_html(number_format((float) $rate['rate_percent'], 0)) . '%)</span>';
913 $html .= '</td>';
914 $html .= '<td style="text-align:right;padding:0;border:none;white-space:nowrap;">';
915 $html .= '<span style="' . $amountStyle . '">' . $amountDisplay . '</span>';
916 $html .= '</td>';
917 $html .= '</tr>';
918 $html .= '</table>';
919 }
920 return $html;
921 }
922
923 public function renderTaxSummaryBox()
924 {
925 $order = Arr::get($this->config, 'order', null);
926 $summary = TaxSummaryHelper::computeTaxSummary($order);
927 if (!$summary['shouldRender']) {
928 return;
929 }
930 ?>
931 <tr>
932 <td colspan="2" style="padding: 0 8px;">
933 <table width="100%" cellpadding="0" cellspacing="0" style="border:none;border-collapse:collapse;">
934 <tr>
935 <td colspan="2" style="padding:6px 0 3px 0; font-size:10px; font-weight:600;
936 text-transform:uppercase; letter-spacing:0.06em; color:#64748b;">
937 <?php echo esc_html__('TAX SUMMARY', 'fluent-cart'); ?>
938 </td>
939 </tr>
940 <?php if ($summary['isReverseCharge']): ?>
941 <?php
942 $rcReversedTotal = (int) Arr::get($summary, 'reversedTaxTotal', 0);
943 $rcReversedShipping = (int) Arr::get($summary, 'reversedShippingTax', 0);
944 $rcReversedValue = $rcReversedTotal > 0
945 ? Helper::toDecimal($rcReversedTotal)
946 : __('Charge reversed', 'fluent-cart');
947 ?>
948 <?php if ($summary['showRcShippingRow'] && $rcReversedShipping > 0): ?>
949 <tr>
950 <td style="padding:3px 0 3px 8px; font-size:11px; color:#94a3b8;">
951 <?php echo esc_html__('Added on shipping', 'fluent-cart'); ?>
952 </td>
953 <td style="padding:3px 0 3px; font-size:11px; text-align:right; color:#94a3b8;">
954 <span style="text-decoration:line-through;opacity:0.6;"><?php echo esc_html(Helper::toDecimal($rcReversedShipping)); ?></span>
955 </td>
956 </tr>
957 <?php endif; ?>
958 <tr>
959 <td style="padding:3px 0 4px 8px; font-size:11px; font-weight:600; color:#1e293b; text-align: left;">
960 <?php echo esc_html__('Tax reversed', 'fluent-cart'); ?>
961 </td>
962 <td style="padding:3px 0 4px; font-size:11px; text-align:right; font-weight:600; color:#1e293b;">
963 <?php echo esc_html($rcReversedValue); ?>
964 </td>
965 </tr>
966 <?php else: ?>
967 <?php
968 $rcFeeRows = Arr::get($summary, 'feeTaxLineRows', []);
969 $rowCount = (int) ($summary['inclusiveTax'] > 0) + (int) ($summary['exclusiveTax'] > 0) + count($rcFeeRows) + (int) ($summary['shippingTax'] > 0);
970 $shouldShowBreakdown = $rowCount >= 2 || ($rowCount === 1 && !($summary['payableTax'] > 0 || $summary['inclusiveTax'] > 0 || (int) Arr::get($summary, 'inclusiveFeeTax', 0) > 0));
971 ?>
972 <?php if ($summary['inclusiveTax'] > 0 && $shouldShowBreakdown): ?>
973 <tr>
974 <td style="padding:3px 0 3px 8px; font-size:11px; color:#94a3b8;">
975 <?php echo esc_html__('Included in item prices', 'fluent-cart'); ?>
976 </td>
977 <td style="padding:3px 0 3px; font-size:11px; text-align:right; color:#94a3b8;">
978 <?php echo esc_html(Helper::toDecimal($summary['inclusiveTax'])); ?>
979 </td>
980 </tr>
981 <?php endif; ?>
982 <?php if ($summary['exclusiveTax'] > 0 && $shouldShowBreakdown): ?>
983 <tr>
984 <td style="padding:3px 0 3px 8px; font-size:11px; color:#334155; text-align: left;">
985 <?php echo esc_html__('Added on products', 'fluent-cart'); ?>
986 </td>
987 <td style="padding:3px 0 3px; font-size:11px; text-align:right; color:#334155;">
988 <?php echo esc_html(Helper::toDecimal($summary['exclusiveTax'])); ?>
989 </td>
990 </tr>
991 <?php endif; ?>
992 <?php if ($shouldShowBreakdown) : ?>
993 <?php foreach ($rcFeeRows as $feeRow) :
994 $feeRowColor = $feeRow['inclusive'] ? '#94a3b8' : '#334155'; ?>
995 <tr>
996 <td style="padding:3px 0 3px 8px; font-size:11px; color:<?php echo esc_attr($feeRowColor); ?>; text-align: left;">
997 <?php echo esc_html($feeRow['display_label']); ?>
998 </td>
999 <td style="padding:3px 0 3px; font-size:11px; text-align:right; color:<?php echo esc_attr($feeRowColor); ?>;">
1000 <?php echo esc_html(Helper::toDecimal($feeRow['tax_amount'])); ?>
1001 </td>
1002 </tr>
1003 <?php endforeach; ?>
1004 <?php endif; ?>
1005 <?php if ($summary['shippingTax'] > 0 && $shouldShowBreakdown):
1006 $isShippingInclusive = (bool) Arr::get($summary, 'isShippingInclusive', false);
1007 $shippingRowColor = $isShippingInclusive ? '#94a3b8' : '#334155';
1008 $shippingTaxLines = Arr::get($summary, 'shippingTaxLines', []);
1009 if (!empty($shippingTaxLines)):
1010 foreach ($shippingTaxLines as $shLine): ?>
1011 <tr>
1012 <td style="padding:3px 0 3px 8px; font-size:11px; color:<?php echo esc_attr($shippingRowColor); ?>;">
1013 <?php echo esc_html($shLine['label']); ?>
1014 </td>
1015 <td style="padding:3px 0 3px; font-size:11px; text-align:right; color:<?php echo esc_attr($shippingRowColor); ?>;">
1016 <?php echo esc_html(Helper::toDecimal($shLine['shipping_tax'])); ?>
1017 </td>
1018 </tr>
1019 <?php endforeach;
1020 else: ?>
1021 <tr>
1022 <td style="padding:3px 0 3px 8px; font-size:11px; color:<?php echo esc_attr($shippingRowColor); ?>;">
1023 <?php echo esc_html($isShippingInclusive ? __('Included in shipping prices', 'fluent-cart') : __('Added on shipping', 'fluent-cart')); ?>
1024 </td>
1025 <td style="padding:3px 0 3px; font-size:11px; text-align:right; color:<?php echo esc_attr($shippingRowColor); ?>;">
1026 <?php echo esc_html(Helper::toDecimal($summary['shippingTax'])); ?>
1027 </td>
1028 </tr>
1029 <?php endif; ?>
1030 <?php endif; ?>
1031 <?php if ($summary['payableTax'] > 0): ?>
1032 <tr>
1033 <td style="padding:4px 0 4px 8px; font-size:11px; font-weight:600; color:#1e293b;
1034 border-top:1px solid #e2e8f0; text-align: left;">
1035 <?php echo esc_html__('Total payable tax', 'fluent-cart'); ?>
1036 </td>
1037 <td style="padding:4px 0 4px; font-size:11px; text-align:right; font-weight:600; color:#1e293b;
1038 border-top:1px solid #e2e8f0;">
1039 <?php echo esc_html(Helper::toDecimal($summary['payableTax'])); ?>
1040 </td>
1041 </tr>
1042 <?php endif; ?>
1043 <?php if ($summary['inclusiveTax'] > 0 || $summary['inclusiveFeeTax'] > 0): ?>
1044 <tr>
1045 <td style="padding:3px 0 4px 8px; font-size:11px; color:#94a3b8;">
1046 <?php echo esc_html__('Total tax in this order', 'fluent-cart'); ?>
1047 </td>
1048 <td style="padding:3px 0 4px; font-size:11px; text-align:right; color:#94a3b8;">
1049 <?php echo esc_html(Helper::toDecimal($summary['totalOrderTax'])); ?>
1050 </td>
1051 </tr>
1052 <?php endif; ?>
1053 <?php endif; ?>
1054 </table>
1055 </td>
1056 </tr>
1057 <?php
1058 }
1059 }
1060