PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.1
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 trunk All 48 releases
fluent-cart / app / Services / PDF / DefaultPdfStructures.php

DefaultPdfStructures.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.1, at app/Services/PDF/DefaultPdfStructures.php

321 lines 12.0 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\PDF;
4
5 if (!defined('ABSPATH')) {
6 exit;
7 }
8
9 /**
10 * Default block-editor PDF structures for FluentCart receipts.
11 *
12 * Design system (A4 Portrait) — minimal:
13 * TEXT #111111 primary text
14 * META #666666 labels / secondary text
15 * BORDER #DDDDDD dividers
16 * WHITE #FFFFFF
17 */
18 class DefaultPdfStructures
19 {
20 private const TEXT = '#111111';
21 private const META = '#666666';
22 private const BORDER = '#DDDDDD';
23 private const WHITE = '#FFFFFF';
24
25 /**
26 * Default meta for all PDF templates.
27 */
28 public static function getDefaultMeta(): array
29 {
30 return [
31 '_fluent_cart_paper_size' => 'A4',
32 '_fluent_cart_orientation' => 'Portrait',
33 '_fluent_cart_font' => 'DejaVuSans',
34 '_fluent_cart_watermark_enabled' => 0,
35 '_fluent_cart_watermark_text' => 'PAID',
36 ];
37 }
38
39 // ── Order Receipt ────────────────────────────────────────────────────────
40
41 /**
42 * @return array{content:string, blocks:array, meta:array}
43 */
44 public static function getDefaultReceiptStructure(): array
45 {
46 $blocks = self::buildReceiptBlocks();
47 return [
48 'content' => self::blocksToContent($blocks),
49 'blocks' => $blocks,
50 'meta' => self::getDefaultMeta(),
51 ];
52 }
53
54 private static function buildReceiptBlocks(): array
55 {
56 $blocks = [];
57
58 $blocks[] = self::header(__('RECEIPT', 'fluent-cart'));
59 $blocks[] = self::addressColumns();
60 $blocks[] = self::metaTable([
61 __('Order Number', 'fluent-cart') => '{{order.invoice_no}}',
62 __('Order Date', 'fluent-cart') => '{{order.created_at}}',
63 __('Payment Method', 'fluent-cart') => '{{order.payment_method_title}}',
64 __('Payment Status', 'fluent-cart') => '{{order.payment_status}}',
65 ]);
66 $blocks[] = self::itemsTable();
67 $blocks[] = self::summaryTable([
68 __('Subtotal', 'fluent-cart') => '{{order.subtotal_formatted}}',
69 __('Discount', 'fluent-cart') => '{{order.discount_total_formatted}}',
70 __('Fees', 'fluent-cart') => '{{order.fee_lines}}',
71 __('Tax', 'fluent-cart') => '{{order.tax_breakdown}}',
72 __('Shipping', 'fluent-cart') => '{{order.shipping_total_formatted}}',
73 ], __('Total', 'fluent-cart'), '{{order.total_amount_formatted}}');
74 $blocks[] = self::footer(__('Thank you for your purchase!', 'fluent-cart'));
75
76 return $blocks;
77 }
78
79 // ── Renewal Receipt ───────────────────────────────────────────────────────
80
81 /**
82 * @return array{content:string, blocks:array, meta:array}
83 */
84 public static function getDefaultRenewalReceiptStructure(): array
85 {
86 $blocks = self::buildRenewalReceiptBlocks();
87 return [
88 'content' => self::blocksToContent($blocks),
89 'blocks' => $blocks,
90 'meta' => self::getDefaultMeta(),
91 ];
92 }
93
94 private static function buildRenewalReceiptBlocks(): array
95 {
96 $blocks = [];
97
98 $blocks[] = self::header(__('RENEWAL RECEIPT', 'fluent-cart'));
99 $blocks[] = self::addressColumns();
100 $blocks[] = self::metaTable([
101 __('Order Number', 'fluent-cart') => '{{order.invoice_no}}',
102 __('Renewal Date', 'fluent-cart') => '{{order.created_at}}',
103 __('Payment Method', 'fluent-cart') => '{{order.payment_method_title}}',
104 __('Payment Status', 'fluent-cart') => '{{order.payment_status}}',
105 ]);
106 $blocks[] = self::itemsTable();
107 $blocks[] = self::summaryTable([
108 __('Subtotal', 'fluent-cart') => '{{order.subtotal_formatted}}',
109 __('Discount', 'fluent-cart') => '{{order.discount_total_formatted}}',
110 __('Fees', 'fluent-cart') => '{{order.fee_lines}}',
111 __('Tax', 'fluent-cart') => '{{order.tax_breakdown}}',
112 ], __('Total', 'fluent-cart'), '{{order.total_amount_formatted}}');
113 $blocks[] = self::footer(__('Thank you for your continued subscription!', 'fluent-cart'));
114
115 return $blocks;
116 }
117
118 // ── Refund Notice ─────────────────────────────────────────────────────────
119
120 /**
121 * @return array{content:string, blocks:array, meta:array}
122 */
123 public static function getDefaultRefundNoticeStructure(): array
124 {
125 $blocks = self::buildRefundNoticeBlocks();
126 $meta = self::getDefaultMeta();
127 $meta['_fluent_cart_watermark_text'] = 'REFUNDED';
128 return [
129 'content' => self::blocksToContent($blocks),
130 'blocks' => $blocks,
131 'meta' => $meta,
132 ];
133 }
134
135 private static function buildRefundNoticeBlocks(): array
136 {
137 $blocks = [];
138
139 $blocks[] = self::header(__('REFUND NOTICE', 'fluent-cart'));
140 $blocks[] = self::addressColumns(__('REFUND TO', 'fluent-cart'));
141 $blocks[] = self::metaTable([
142 __('Order Number', 'fluent-cart') => '{{order.invoice_no}}',
143 __('Order Date', 'fluent-cart') => '{{order.created_at}}',
144 __('Payment Method', 'fluent-cart') => '{{order.payment_method_title}}',
145 ]);
146 $blocks[] = self::itemsTable();
147 $blocks[] = self::summaryTable([
148 __('Original Total', 'fluent-cart') => '{{order.total_amount_formatted}}',
149 ], __('Refund Amount', 'fluent-cart'), '{{order.total_refund}}');
150 $blocks[] = self::footer(__('Your refund has been processed. Please allow a few business days for the amount to appear in your account.', 'fluent-cart'));
151
152 return $blocks;
153 }
154
155 // ── Proforma Invoice ──────────────────────────────────────────────────────
156
157 /**
158 * @return array{content:string, blocks:array, meta:array}
159 */
160 public static function getDefaultProformaInvoiceStructure(): array
161 {
162 $blocks = self::buildProformaInvoiceBlocks();
163 $meta = self::getDefaultMeta();
164 $meta['_fluent_cart_watermark_enabled'] = 0;
165 $meta['_fluent_cart_watermark_text'] = '';
166 return [
167 'content' => self::blocksToContent($blocks),
168 'blocks' => $blocks,
169 'meta' => $meta,
170 ];
171 }
172
173 private static function buildProformaInvoiceBlocks(): array
174 {
175 $blocks = [];
176
177 $blocks[] = self::header(__('INVOICE', 'fluent-cart'));
178 $blocks[] = self::addressColumns();
179 $blocks[] = self::metaTable([
180 __('Invoice Number', 'fluent-cart') => '{{order.invoice_no}}',
181 __('Invoice Date', 'fluent-cart') => '{{order.created_at}}',
182 __('Payment Method', 'fluent-cart') => '{{order.payment_method_title}}',
183 __('Payment Status', 'fluent-cart') => '{{order.payment_status}}',
184 ]);
185 $blocks[] = self::itemsTable();
186 $blocks[] = self::summaryTable([
187 __('Subtotal', 'fluent-cart') => '{{order.subtotal_formatted}}',
188 __('Discount', 'fluent-cart') => '{{order.discount_total_formatted}}',
189 __('Fees', 'fluent-cart') => '{{order.fee_lines}}',
190 __('Tax', 'fluent-cart') => '{{order.tax_breakdown}}',
191 __('Shipping', 'fluent-cart') => '{{order.shipping_total_formatted}}',
192 ], __('Amount Due', 'fluent-cart'), '{{order.total_amount_formatted}}');
193 $blocks[] = self::footer(__('Thank you for your order. Please complete the payment to process your order.', 'fluent-cart'));
194
195 return $blocks;
196 }
197
198 // ── Shared block builders ─────────────────────────────────────────────────
199
200 /**
201 * Document header: logo left, title right, bottom rule.
202 */
203 private static function header(string $title): array
204 {
205 return self::block('fluent-cart/receipt-header', ['title' => $title]);
206 }
207
208 /**
209 * Two-column FROM / BILL TO address block.
210 */
211 private static function addressColumns(?string $billingLabel = null): array
212 {
213 if ($billingLabel === null) {
214 $billingLabel = __('BILL TO', 'fluent-cart');
215 }
216
217 return self::block('fluent-cart/receipt-addresses', ['billingLabel' => $billingLabel]);
218 }
219
220 /**
221 * Key/value meta rows (order number, date, etc.).
222 *
223 * @param array<string,string> $rows label => smartcode
224 */
225 private static function metaTable(array $rows): array
226 {
227 $rowsData = [];
228 foreach ($rows as $label => $value) {
229 $rowsData[] = ['label' => $label, 'value' => $value];
230 }
231
232 return self::block('fluent-cart/receipt-meta', ['rows' => $rowsData]);
233 }
234
235 /**
236 * Line-items table (delegated to the order variable).
237 */
238 private static function itemsTable(): array
239 {
240 return self::block('fluent-cart/receipt-item-table', [
241 'headerBg' => self::WHITE,
242 'headerColor' => self::TEXT,
243 'bodyColor' => self::TEXT,
244 'borderColor' => self::BORDER,
245 'fontSize' => 11,
246 ], '{{order.items_table}}');
247 }
248
249 /**
250 * Payment summary with optional sub-rows and a bold total row.
251 *
252 * @param array<string,string> $rows label => smartcode
253 */
254 private static function summaryTable(array $rows, string $totalLabel, string $totalValue): array
255 {
256 $rowsData = [];
257 foreach ($rows as $label => $value) {
258 $rowsData[] = ['label' => $label, 'value' => $value];
259 }
260
261 return self::block('fluent-cart/receipt-payment-summary', [
262 'rows' => $rowsData,
263 'totalLabel' => $totalLabel,
264 'totalValue' => $totalValue,
265 ]);
266 }
267
268 /**
269 * Footer note, centred, small text.
270 */
271 private static function footer(string $text): array
272 {
273 return self::block('fluent-cart/receipt-footer', ['text' => $text]);
274 }
275
276 // ── Low-level helpers ─────────────────────────────────────────────────────
277
278 private static function block(string $name, array $attrs = [], ?string $content = null, array $innerBlocks = []): array
279 {
280 return [
281 'blockName' => $name,
282 'attrs' => $attrs,
283 'innerHTML' => $content ?? '',
284 'innerContent' => $content !== null ? [$content] : [],
285 'innerBlocks' => $innerBlocks,
286 ];
287 }
288
289 /**
290 * Serialize blocks array to Gutenberg block comment format.
291 */
292 public static function blocksToContent(array $blocks): string
293 {
294 $out = '';
295 foreach ($blocks as $block) {
296 $name = $block['blockName'];
297 $attrs = $block['attrs'] ?? [];
298 $innerBlocks = $block['innerBlocks'] ?? [];
299 $innerHTML = $block['innerHTML'] ?? '';
300
301 $attrJson = !empty($attrs) ? ' ' . wp_json_encode($attrs) : '';
302
303 if (!empty($innerBlocks)) {
304 $out .= "<!-- wp:{$name}{$attrJson} -->\n";
305 $out .= $innerHTML ? "{$innerHTML}\n" : '';
306 $out .= self::blocksToContent($innerBlocks);
307 $out .= "<!-- /wp:{$name} -->\n\n";
308 } else {
309 if ($innerHTML) {
310 $out .= "<!-- wp:{$name}{$attrJson} -->\n";
311 $out .= "{$innerHTML}\n";
312 $out .= "<!-- /wp:{$name} -->\n\n";
313 } else {
314 $out .= "<!-- wp:{$name}{$attrJson} /-->\n\n";
315 }
316 }
317 }
318 return $out;
319 }
320 }
321