PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.0
1.7.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 All 50 releases
fluent-cart / app / Services / Email / EmailPreviewService.php

EmailPreviewService.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.7.0, at app/Services/Email/EmailPreviewService.php

335 lines 11.9 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\Email;
4
5 use FluentCart\App\Helpers\Status;
6 use FluentCart\App\Models\Product;
7 use FluentCart\App\Models\ProductReview;
8 use FluentCart\App\Services\DateTime\DateTime;
9 use FluentCart\Framework\Support\Collection;
10 use FluentCart\Framework\Support\Str;
11
12 class EmailPreviewService
13 {
14 public function getPreviewData(string $template): array
15 {
16 $order = $this->getDummyOrder();
17 $subscription = $this->getDummySubscription($order);
18 $customer = $order->customer;
19 $transaction = $order->getLatestTransaction();
20
21 $previewData = [
22 'order' => $order,
23 'subscription' => $subscription,
24 'customer' => $customer,
25 'transaction' => $transaction,
26 'reminder' => [
27 'stage' => 'before_0',
28 'due_at' => DateTime::gmtNow()->addDays(2)->format('Y-m-d H:i:s'),
29 'due_amount' => 4900,
30 'payment_link' => '#',
31 'order_ref' => $order->invoice_no,
32 'billing_date' => DateTime::gmtNow()->addDays(30)->format('Y-m-d H:i:s'),
33 'trial_end_date' => DateTime::gmtNow()->addDays(3)->format('Y-m-d H:i:s'),
34 ],
35 ];
36
37 if (Str::startsWith($template, 'order.reminder.renewal_overdue.')) {
38 if (Str::contains($template, '.final.')) {
39 $previewData['reminder']['stage'] = 'overdue_7';
40 } elseif (Str::contains($template, '.followup.')) {
41 $previewData['reminder']['stage'] = 'overdue_3';
42 } else {
43 $previewData['reminder']['stage'] = 'overdue_1';
44 }
45 }
46
47 // The review templates read a review, not an order. Without one the
48 // preview of any of them fatals on the first property it touches.
49 if (Str::startsWith($template, 'review.')) {
50 $previewData['product'] = $this->getPreviewProduct();
51 $previewData['review'] = $this->getDummyReview((int) $previewData['product']->ID);
52
53 if (Str::startsWith($template, 'review.replied.')) {
54 $previewData['reply'] = $this->getDummyReply((int) $previewData['product']->ID);
55 }
56 // A review is not about an order, and a real send carries none —
57 // the order header (date, number) renders only when one is there.
58 // Left in, the preview would show a header the email never has.
59 $previewData['order'] = null;
60 $previewData['transaction'] = null;
61 }
62
63 if (Str::startsWith($template, 'subscription.')) {
64 $previewData['transaction'] = $subscription->getLatestTransaction();
65
66 if (Str::contains($template, 'trial_end')) {
67 $previewData['reminder']['stage'] = 'trial_end_3';
68 }
69
70 if (Str::contains($template, 'renewal_failed')) {
71 $previewData['error'] = __('Your card was declined.', 'fluent-cart');
72 }
73
74 if (Str::contains($template, 'canceled')) {
75 $previewData['reason'] = __('canceled on customer request', 'fluent-cart');
76 $previewData['subscription'] = $this->getDummySubscription($previewData['order']);
77 }
78 }
79
80 return $previewData;
81 }
82
83 /**
84 * A stand-in review with every field the review templates and the
85 * {{review.*}} codes read. A real model, not a stdClass: the templates
86 * call attachItemLabels() on it and the parser type-checks for one.
87 */
88 /**
89 * The product a review preview is about: a real published one when the
90 * store has any, so the preview links where the email will — the
91 * "See your review" button needs a page to point at, and a placeholder
92 * has none. An empty store previews without the button, as the email
93 * would for a product since deleted.
94 */
95 private function getPreviewProduct(): object
96 {
97 $product = Product::query()
98 ->where('post_status', 'publish')
99 ->orderBy('ID', 'DESC')
100 ->first();
101
102 if ($product) {
103 return $product;
104 }
105
106 return (object) [
107 'ID' => 0,
108 'post_title' => 'Sample Product',
109 ];
110 }
111
112 private function getDummyReview(int $postId = 0): ProductReview
113 {
114 $review = new ProductReview([
115 'post_id' => $postId,
116 'reviewer_name' => 'John Doe',
117 'reviewer_email' => '[email protected]',
118 'title' => 'Exactly what I was looking for',
119 'review' => 'Arrived quickly and works just as described. Would happily buy again.',
120 'rating' => 5,
121 ]);
122 $review->id = 0;
123 $review->status = Status::REVIEW_APPROVED;
124
125 return $review;
126 }
127
128 private function getDummyReply(int $postId = 0): ProductReview
129 {
130 $reply = new ProductReview([
131 'post_id' => $postId,
132 'parent_id' => 0,
133 'reviewer_name' => 'The Store Team',
134 'reviewer_email' => '[email protected]',
135 'review' => 'Thank you so much for the kind words — we are thrilled it worked out. Enjoy!',
136 ]);
137 $reply->id = 0;
138 $reply->status = Status::REVIEW_APPROVED;
139 $reply->is_admin_reply = 1;
140
141 return $reply;
142 }
143
144 private function getDummyCustomer(): object
145 {
146 return (object) [
147 'full_name' => 'John Doe',
148 'first_name' => 'John',
149 'last_name' => 'Doe',
150 'email' => '[email protected]',
151 ];
152 }
153
154 private function getDummyTransaction(): object
155 {
156 return new class {
157 public $created_at;
158 public $total = 4900;
159 public $vendor_charge_id = 'ch_preview_123';
160 public $order = null;
161
162 public function __construct()
163 {
164 $this->created_at = DateTime::gmtNow();
165 }
166
167 public function getPaymentMethodText()
168 {
169 return 'card';
170 }
171 };
172 }
173
174 private function getDummyOrder(): object
175 {
176 $customer = $this->getDummyCustomer();
177 $transaction = $this->getDummyTransaction();
178
179 $order = new class($customer, $transaction, new Collection()) {
180 public $id = 1001;
181 public $uuid = 'preview-order-uuid';
182 public $invoice_no = 'FC-1001';
183 public $customer;
184 public $order_items;
185 public $subscriptions;
186 public $orderTaxRates;
187 public $billing_address;
188 public $shipping_address;
189 public $fulfillment_type = 'digital';
190 public $subtotal = 4900;
191 public $manual_discount_total = 0;
192 public $coupon_discount_total = 0;
193 public $shipping_tax = 0;
194 public $shipping_total = 0;
195 public $tax_total = 0;
196 public $tax_behavior = 2;
197 public $total_amount = 4900;
198 public $total_paid = 0;
199 public $total_refund = 0;
200 public $payment_status = 'pending';
201 public $config = [];
202 private $latestTransaction;
203
204 public function __construct($customer, $transaction, $emptyCollection)
205 {
206 $this->customer = $customer;
207 $this->latestTransaction = $transaction;
208 $this->order_items = new Collection([
209 [
210 'post_title' => 'Sample Product',
211 'quantity' => 1,
212 'title' => 'Standard Plan',
213 'payment_type' => 'onetime',
214 'payment_info' => '',
215 'formatted_total' => '$49.00',
216 'package_info' => __('Package: Medium Box · 30 × 20 × 15 cm · Wt: 2 kg · Shipping wt: 2.5 kg', 'fluent-cart'),
217 'other_info' => [
218 'package_name' => 'Medium Box',
219 'package_type' => 'box',
220 'package_length' => 30,
221 'package_width' => 20,
222 'package_height' => 15,
223 'package_dimension_unit' => 'cm',
224 'weight' => 2,
225 'weight_unit' => 'kg',
226 'package_weight' => 0.5,
227 'package_weight_unit' => 'kg',
228 'package_slug' => 'medium-box',
229 ],
230 ],
231 ]);
232 $this->subscriptions = $emptyCollection;
233 $this->orderTaxRates = $emptyCollection;
234 $this->billing_address = null;
235 $this->shipping_address = null;
236 }
237
238 public function getViewUrl($type = 'customer')
239 {
240 return '#';
241 }
242
243 public function getMeta($metaKey, $defaultValue = false)
244 {
245 if ($metaKey === 'due_date') {
246 return gmdate('Y-m-d H:i:s', strtotime('+7 days'));
247 }
248 return $defaultValue;
249 }
250
251 public function isReverseChargeTaxOrder()
252 {
253 return false;
254 }
255
256 public function getOrderRcMode()
257 {
258 return 'fixed';
259 }
260
261 public function getLatestTransaction()
262 {
263 return $this->latestTransaction;
264 }
265
266 public function getDownloads($scope = 'email')
267 {
268 return [];
269 }
270
271 public function getLicenses($with = ['product', 'productVariant'])
272 {
273 return new Collection([]);
274 }
275 };
276
277 // Templates reach the order through the transaction too (e.g. the
278 // renewal admin email's $transaction->order->getViewUrl('admin')).
279 $transaction->order = $order;
280
281 return $order;
282 }
283
284 private function getDummySubscription($order): object
285 {
286 $customer = $order->customer ?? $this->getDummyCustomer();
287 $transaction = $this->getDummyTransaction();
288
289 return new class($customer, $transaction, $order) {
290 public $id = 2001;
291 public $uuid = 'preview-subscription-uuid';
292 public $item_name = 'Sample Subscription';
293 public $display_item_name = 'Sample Subscription';
294 public $billing_interval = 'yearly';
295 public $next_billing_date;
296 public $recurring_total = 4900;
297 public $status = 'active';
298 public $trial_ends_at;
299 public $customer;
300 public $order;
301 private $latestTransaction;
302
303 public function __construct($customer, $transaction, $order)
304 {
305 $this->customer = $customer;
306 $this->latestTransaction = $transaction;
307 $this->order = $order;
308 $transaction->order = $order;
309 $this->next_billing_date = \FluentCart\App\Services\DateTime\DateTime::gmtNow()->addDays(30)->format('Y-m-d H:i:s');
310 $this->trial_ends_at = \FluentCart\App\Services\DateTime\DateTime::gmtNow()->addDays(3)->format('Y-m-d H:i:s');
311 }
312
313 public function getLatestTransaction()
314 {
315 return $this->latestTransaction;
316 }
317
318 public function getViewUrl($type = 'customer')
319 {
320 return '#';
321 }
322
323 public function getReactivateUrl()
324 {
325 return '#';
326 }
327
328 public function getPaymentMethodText()
329 {
330 return 'Visa ***4242';
331 }
332 };
333 }
334 }
335