PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.21
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.21
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.3.21, at app/Services/PDF/DefaultPdfStructures.php

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