PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.4.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.4.1
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 / Coupon / Concerns / CanValidateCoupon.php

CanValidateCoupon.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.4.1, at app/Services/Coupon/Concerns/CanValidateCoupon.php

286 lines 8.4 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\Coupon\Concerns;
4
5 use FluentCart\App\Models\AppliedCoupon;
6 use FluentCart\App\Models\Coupon;
7 use FluentCart\App\Models\Customer;
8 use FluentCart\App\Services\DateTime\DateTime;
9 use FluentCart\App\Services\OrderService;
10 use FluentCart\Framework\Support\Arr;
11 use FluentCart\Framework\Support\Collection;
12 use WP_Error;
13
14 trait CanValidateCoupon
15 {
16 /**
17 * @return bool|WP_Error
18 */
19 public function validate($couponCode)
20 {
21 $couponCode = apply_filters('fluent_cart/coupon/validating_coupon', $couponCode,
22 [
23 'coupon_code' => $couponCode,
24 'line_items' => $this->lineItems,
25 'couponService' => $this
26 ]
27 );
28
29 if (is_wp_error($couponCode)) {
30 return $couponCode;
31 }
32
33 if (empty($couponCode)) {
34 return $this->makeError(__('Invalid Coupon Code', 'fluent-cart'), 404);
35 }
36
37 $coupon = $this->applicableCoupons->firstWhere('code', $couponCode);
38
39 if (empty($coupon)) {
40 return $this->makeError(__('Coupon Not Found', 'fluent-cart'), 404);
41 }
42
43 if ($this->isAlreadyApplied($coupon)) {
44 return $this->makeError(__('Coupon already applied', 'fluent-cart'), 401);
45 }
46
47 //Return true at this point is not using cart, because at this point coupon is already used in an order
48 if (!$this->usingCart && $this->previouslyAppliedCoupons->has($couponCode)) {
49 return true;
50 }
51
52 if (!$this->canBeStacked($coupon)) {
53 return $this->makeError(__('Can not apply the coupons together', 'fluent-cart'), 401);
54 }
55
56 if (!$this->isCouponActive($coupon)) {
57
58 return $this->makeError(__('This coupon has expired or is not valid', 'fluent-cart'), 401);
59 }
60
61 if ($this->requiredUserToBeLoggedIn($coupon)) {
62
63 if (!is_user_logged_in()) {
64 return $this->makeError(__('You Need To be logged in', 'fluent-cart'), 403);
65 }
66
67 if (!$this->hasMaxPerUserLimit($coupon)) {
68 return $this->makeError(__('Coupon Uses Limit Exceeded', 'fluent-cart'), 401);
69 }
70 }
71
72 if (!$this->hasUseLimit($coupon)) {
73 return $this->makeError(__('Coupon Uses Limit Exceeded', 'fluent-cart'), 403);
74 }
75
76 if (!$this->ensureMinimumPurchaseAmount($coupon)) {
77 return $this->makeError(__('Purchase amount is smaller than required amount', 'fluent-cart'), 403);
78 }
79
80 if (!$this->isProductValidated($coupon)) {
81 return $this->makeError(__('No applicable products in the cart for this coupon', 'fluent-cart'), 403);
82 }
83
84 return true;
85 }
86
87 public function isProductValidated($coupon): bool
88 {
89 foreach ($this->lineItems as $item) {
90 if ($this->isApplicableToProduct($coupon, $item['post_id'], $item['id'])) {
91 return true;
92 }
93 }
94 return false;
95 }
96
97 public function canBeStacked(Coupon $coupon): bool
98 {
99 //If there is no applied coupon return true;
100 if (empty($this->previouslyAppliedCouponCodes)) {
101 return true;
102 }
103 //If there is any applied coupon, that is not stackable return false;
104 foreach ($this->previouslyAppliedCoupons as $lcoupon) {
105 if (Arr::get($lcoupon, 'stackable') === 'no') {
106 return false;
107 }
108 }
109
110 //return true if the coupon is stackable
111 return (Arr::get($coupon, 'stackable') !== 'no');
112 }
113
114 public function isAlreadyApplied(Coupon $coupon): bool
115 {
116 return in_array($coupon->code, $this->previouslyAppliedCouponCodes);
117 }
118
119 public function ensureMinimumPurchaseAmount(Coupon $coupon): bool
120 {
121 $min_purchase_amount = Arr::get($coupon->conditions, 'min_purchase_amount', null);
122 if (empty($min_purchase_amount)) {
123 return true;
124 } else {
125 $orderTotal = OrderService::getItemsAmountTotal($this->lineItems, false, false);
126 return $min_purchase_amount <= $orderTotal;
127 }
128 }
129
130 public function isCouponActive(Coupon $coupon): bool
131 {
132 $status = $coupon->status;
133 $now = DateTime::gmtNow();
134
135 $hasExpirationDate = true;
136 $hasStartDate = true;
137
138 if (empty($coupon->start_date) || $coupon->start_date === '0000-00-00 00:00:00') {
139 $hasStartDate = false;
140 }
141
142 if (empty($coupon->end_date) || $coupon->end_date === '0000-00-00 00:00:00') {
143 $hasExpirationDate = false;
144 }
145
146
147 if ($status === 'active' && !$hasExpirationDate && !$hasStartDate) {
148 return true;
149 }
150
151
152 if ($status === 'scheduled' || $status === 'active') {
153
154 $startDate = null;
155 $endDate = null;
156 $couponStarted = false;
157 $couponEnded = false;
158
159 if (!empty($coupon->start_date)) {
160 $startDate = DateTime::parse($coupon->start_date);
161 $couponStarted = $startDate <= $now;
162 }
163
164 if (!empty($coupon->end_date)) {
165 $endDate = DateTime::parse($coupon->end_date);
166 $couponEnded = $endDate < $now;
167 }
168
169
170
171
172 if (empty($startDate))
173 return !$couponEnded;
174 if (empty($endDate))
175 return $couponStarted;
176
177 return $couponStarted && !$couponEnded;
178 }
179
180 return false;
181 }
182
183 public function hasUseLimit(Coupon $coupon): bool
184 {
185 $maxUse = Arr::get($coupon->conditions, 'max_uses', null);
186 if (empty($maxUse)) {
187 return true;
188 }
189
190 if (empty($coupon->use_count)) {
191 return true;
192 }
193
194 return $maxUse > $coupon->use_count;
195 }
196
197 public function requiredUserToBeLoggedIn(Coupon $coupon): bool
198 {
199 return Arr::get($coupon->conditions, 'max_per_customer', 0);
200 }
201
202 public function hasMaxPerUserLimit(Coupon $coupon): bool
203 {
204 $maxPerCustomer = Arr::get($coupon->conditions, 'max_per_customer', 0);
205 if (empty($maxPerCustomer)) {
206 return true;
207 }
208
209 $userId = get_current_user_id();
210
211 $customer = Customer::query()->where('user_id', $userId)->first();
212
213 if (empty($customer)) {
214 return true;
215 }
216
217 $appliedCoupons = AppliedCoupon::query()
218 ->where('code', $coupon->code)
219 ->where('customer_id', $customer->id)
220 ->get();
221
222 return $maxPerCustomer > $appliedCoupons->count();
223 }
224
225 public function isApplicableToProduct(Coupon $coupon, $productId, $variationsId): bool
226 {
227 if (empty($variationsId)) {
228 return false;
229 }
230
231 $categories = Arr::get($this->products, ($productId . '') . '.categories');
232
233 $categoryIds = Collection::make($categories)->pluck('term_id')->toArray();
234
235 $canBeApplied = true;
236
237 $conditions = $coupon->conditions;
238
239 $excludedCategories = $this->getArrayValue($conditions, 'excluded_categories');
240 $includedCategories = $this->getArrayValue($conditions, 'included_categories');
241 $excludedProducts = $this->getArrayValue($conditions, 'excluded_products');
242 $includedProducts = $this->getArrayValue($conditions, 'included_products');
243
244 if (in_array($variationsId, $includedProducts)) {
245 return true;
246 }
247
248 if (in_array($variationsId, $excludedProducts)) {
249 return false;
250 }
251
252 foreach ($categoryIds as $categoryId) {
253 if (empty($categoryId)) {
254 continue;
255 }
256 if (in_array($categoryId, $excludedCategories)) {
257 $canBeApplied = false;
258 }
259 if (!empty($includedCategories) && !in_array($categoryId, $includedCategories)) {
260 $canBeApplied = false;
261 }
262
263 if (!$canBeApplied) {
264 break;
265 }
266 }
267
268 if (!empty($includedProducts) && !in_array($variationsId, $includedProducts)) {
269 return false;
270 }
271
272
273 return $canBeApplied;
274 }
275
276 protected function makeError(string $message, $code = null): WP_Error
277 {
278 return new WP_Error($code, $message);
279 }
280
281 private function getArrayValue($array, $key, $defaultValue = [])
282 {
283 return isset($array[$key]) && is_array($array[$key]) ? $array[$key] : $defaultValue;
284 }
285 }
286