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.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 / Filter / OrderFilter.php

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

573 lines 24.5 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\Filter;
4
5 use Closure;
6 use FluentCart\Api\ModuleSettings;
7 use FluentCart\App\App;
8 use FluentCart\App\Helpers\AddressHelper;
9 use FluentCart\App\Helpers\Status;
10 use FluentCart\App\Models\Order;
11 use FluentCart\Framework\Database\Orm\Builder;
12 use FluentCart\Framework\Support\Arr;
13 use FluentCartPro\App\Modules\Licensing\Models\License;
14 use FluentCart\App\Modules\PaymentMethods\Core\GatewayManager;
15
16
17 class OrderFilter extends BaseFilter
18 {
19 protected static function statusFilterKeys(): array
20 {
21 return ['payment_statuses', 'order_statuses', 'shipping_statuses'];
22 }
23
24 public function applySimpleFilter(?string $search = null): void
25 {
26 $search = $search ?? $this->search;
27 $isApplied = $this->applySimpleOperatorFilter($search);
28 if ($isApplied) {
29 return;
30 }
31
32 foreach (static::statusFilterKeys() as $statusKey) {
33 $this->query->when(Arr::get($this->args, $statusKey), function ($query, $values) use ($statusKey) {
34 return $query->whereIn($statusKey, Arr::wrap($values));
35 });
36 }
37
38 if (!empty($search)) {
39 $search = trim($search);
40 $searchLike = addcslashes($search, '\\%_');
41
42 $this->query->where(function ($query) use ($search, $searchLike) {
43 $query->where('invoice_no', 'LIKE', "%{$search}%")
44 ->orWhereHas('customer', function ($customerQuery) use ($search, $searchLike) {
45 $customerQuery
46 ->where('email', 'LIKE', "%{$search}%")
47 ->orWhereRaw("CONCAT(first_name, ' ', last_name) LIKE ?", ["%{$searchLike}%"]);
48 })
49 ->orWhereHas('order_items', function ($query) use ($searchLike) {
50 $query->where('title', 'LIKE', "%{$searchLike}%");
51 $query->orWhere('post_title', 'LIKE', "%{$searchLike}%");
52 });
53 });
54 }
55 }
56
57 public function tabsMap(): array
58 {
59 return [
60 'on-hold' => 'status',
61 'paid' => 'payment_status',
62 //'unpaid' => 'payment_status',
63 'completed' => 'status',
64 'processing' => 'status',
65 'renewal' => 'type',
66 'subscription' => 'type',
67 'onetime' => 'type',
68 'refunded' => 'payment_status',
69 'partially_refunded' => 'payment_status',
70 'upgraded_to' => 'upgraded_to',
71 'upgraded_from' => 'upgraded_from',
72 'b2b_purchase' => 'b2b_purchase',
73 'reverse_charge' => 'reverse_charge',
74 ];
75 }
76
77 public function getModel(): string
78 {
79 return Order::class;
80 }
81
82 public static function getFilterName(): string
83 {
84 return 'orders';
85 }
86
87 public static function parseableKeys(): array
88 {
89 return array_merge(
90 parent::parseableKeys(),
91 static::statusFilterKeys()
92 );
93 }
94
95
96 public static function b2bMetaCondition(): Closure
97 {
98 return function ($q) {
99 $q->where('meta_key', 'business_info')
100 ->whereRaw("(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(meta_value, '$.company_name')), '') IS NOT NULL OR NULLIF(JSON_UNQUOTE(JSON_EXTRACT(meta_value, '$.legal_registration_id')), '') IS NOT NULL OR NULLIF(JSON_UNQUOTE(JSON_EXTRACT(meta_value, '$.tax_number')), '') IS NOT NULL)");
101 };
102 }
103
104 public function applyActiveViewFilter(?string $activeView = null): void
105 {
106 $activeView = $activeView ?? $this->activeView;
107 $tabsMap = $this->tabsMap();
108
109 //Apply Active Tab view
110 $this->query = $this->query->when($activeView, function (Builder $query, $activeView) use ($tabsMap) {
111
112 if ($activeView === 'upgraded_to') {
113 return $query
114 ->whereRaw("JSON_EXTRACT(config, '$.upgraded_to') IS NOT NULL")
115 ->whereRaw("JSON_EXTRACT(config, '$.upgraded_to') != 0");
116 } else if ($activeView === 'upgraded_from') {
117 return $query
118 ->whereRaw("JSON_EXTRACT(config, '$.upgraded_from') IS NOT NULL")
119 ->whereRaw("JSON_EXTRACT(config, '$.upgraded_from') != 0");
120 } else if ($activeView === 'b2b_purchase') {
121 return $query->whereHas('orderMeta', static::b2bMetaCondition());
122 } else if ($activeView === 'reverse_charge') {
123 return $query->whereHas('orderTaxRates', function ($q) {
124 $q->whereRaw("JSON_EXTRACT(meta, '$.reverse_charge_applied') = true");
125 });
126 } else {
127 return $query->where(
128 $tabsMap[$activeView],
129 $activeView
130 );
131 }
132
133
134 });
135 }
136
137 public static function getSearchableFields(): array
138 {
139 $fields = [
140 'id' => [
141 'column' => 'id',
142 'description' => __('Order Id', 'fluent-cart'),
143 'type' => 'numeric',
144 'examples' => [
145 'id = 1',
146 'id > 5',
147 'id :: 1-10'
148 ]
149 ],
150 'status' => [
151 'column' => 'status',
152 'description' => __('Search by order status e.g., completed, processing, on-hold, canceled, failed', 'fluent-cart'),
153 'type' => 'string',
154 'examples' => [
155 'status = completed',
156 ]
157 ],
158 'invoice' => [
159 'column' => 'status',
160 'description' => __('Invoice Number', 'fluent-cart'),
161 'type' => 'string'
162 ],
163
164 'payment' => [
165 'column' => 'payment_status',
166 'description' => __('Search by payment status e.g., paid, pending, partially_paid, refunded, partially_refunded', 'fluent-cart'),
167 'type' => 'string',
168 'examples' => [
169 'payment = paid',
170 'payment = partially_paid',
171 'payment = partially_refunded',
172 ]
173 ],
174 'payment_by' => [
175 'column' => 'payment_method',
176 'description' => __('Search by payment method e.g., stripe, PayPal, offline_payment', 'fluent-cart'),
177 'type' => 'string',
178 'examples' => [
179 'payment_by = stripe',
180 'payment_by = paypal',
181 ]
182 ],
183
184 'customer' => [
185 'description' => __('Search by customer name or email', 'fluent-cart'),
186 'note' => __("only supports '=' operator", 'fluent-cart'),
187 'type' => 'custom',
188 'callback' => function ($query, $search) {
189 $query->whereHas('customer', function ($query) use ($search) {
190 $query->whereRaw("CONCAT(first_name, ' ', last_name) LIKE ?", ["%{$search}%"])
191 ->orWhere('email', 'like', '%' . $search . '%');
192 });
193 },
194 'examples' => [
195 'customer = jhon',
196 ]
197 ],
198 ];
199
200
201 if (class_exists(License::class)) {
202 $fields['license'] = [
203 'description' => __('Search by license key', 'fluent-cart'),
204 'note' => __("only supports '=' operator", 'fluent-cart'),
205 'type' => 'custom',
206 'callback' => function ($query, $search) {
207 $query->whereHas('licenses', function ($query) use ($search) {
208 $query->where('license_key', 'like', '%' . $search . '%');
209 });
210 },
211 'examples' => [
212 'license = ff-78d47b3fed89bda25cdc5b60d0298d60',
213 ]
214 ];
215 }
216
217 return $fields;
218 }
219
220 public static function advanceFilterOptions(): array
221 {
222 $manager = GatewayManager::getInstance();
223 $payment_routes = $manager->getRoutes();
224 $availablePaymentMethods = [];
225
226 foreach ($payment_routes as $route) {
227 $availablePaymentMethods[$route['name']] = Arr::get($route, 'meta.title', $route['name']);
228 }
229
230
231 $filters = [
232 'order' => [
233 'label' => __('Order Property', 'fluent-cart'),
234 'value' => 'order',
235 'children' => [
236 [
237 'label' => __('By Order Items', 'fluent-cart'),
238 'value' => 'order_items',
239 'column' => 'object_id',
240 'filter_type' => 'relation',
241 'relation' => 'order_items',
242 'remote_data_key' => 'product_variations',
243 'type' => 'remote_tree_select',
244 'limit' => 10,
245 ],
246 [
247 'label' => __('Order Status', 'fluent-cart'),
248 'value' => 'status',
249 'type' => 'selections',
250 'options' => [
251 'completed' => __('Completed', 'fluent-cart'),
252 'processing' => __('Processing', 'fluent-cart'),
253 'on-hold' => __('On Hold', 'fluent-cart'),
254 'canceled' => __('Canceled', 'fluent-cart')
255 ],
256 'is_multiple' => true,
257 'is_only_in' => true
258 ],
259 [
260 'label' => __('Payment Status', 'fluent-cart'),
261 'value' => 'payment_status',
262 'type' => 'selections',
263 'options' => [
264 'paid' => __('Paid', 'fluent-cart'),
265 'pending' => __('Pending', 'fluent-cart'),
266 'partially_paid' => __('Partially Paid', 'fluent-cart'),
267 'refunded' => __('Refunded', 'fluent-cart'),
268 'partially_refunded' => __('Partially Refunded', 'fluent-cart'),
269 //'authorized' => __('Authorized', 'fluent-cart')
270 ],
271 'is_multiple' => true,
272 'is_only_in' => true
273 ],
274 // [
275 // 'label' => __('Shipping Status', 'fluent-cart'),
276 // 'value' => 'shipping_status',
277 // 'type' => 'selections',
278 // 'options' => [
279 // 'fulfilled' => __('Fulfilled', 'fluent-cart'),
280 // 'unfulfilled' => __('Unfulfilled', 'fluent-cart'),
281 // 'on_hold' => __('On Hold', 'fluent-cart')
282 // ],
283 // 'is_multiple' => true,
284 // 'is_only_in' => true
285 // ],
286 [
287 'label' => __('Order Type', 'fluent-cart'),
288 'value' => 'type',
289 'type' => 'selections',
290 'options' => [
291 'payment' => __('Single Payment', 'fluent-cart'),
292 'subscription' => __('Subscription', 'fluent-cart'),
293 'renewal' => __('Renewal', 'fluent-cart'),
294 ],
295 'is_multiple' => true,
296 'is_only_in' => true
297 ],
298 [
299 'label' => __('Payment Method', 'fluent-cart'),
300 'value' => 'payment_method',
301 'type' => 'selections',
302 'column' => 'payment_method',
303 'relation' => 'transactions',
304 'filter_type' => 'relation',
305 'options' => $availablePaymentMethods,
306 'is_multiple' => true,
307 'is_only_in' => true
308 ],
309 [
310 'label' => __('Order Amount', 'fluent-cart'),
311 'value' => 'total_amount',
312 'type' => 'numeric',
313 ],
314 [
315 'label' => __('Order Hash (UUID)', 'fluent-cart'),
316 'value' => 'uuid',
317 'type' => 'text',
318 'column' => 'uuid',
319 'operators' => [
320 'equals' => __('Equals', 'fluent-cart'),
321 'not_equals' => __('Not Equals', 'fluent-cart'),
322 ]
323 ],
324 [
325 'label' => __('Order Date', 'fluent-cart'),
326 'value' => 'created_at',
327 'type' => 'dates',
328 'filter_type' => 'date',
329 ],
330 ],
331 ],
332 'customer' => [
333 'label' => __('Customer Property', 'fluent-cart'),
334 'value' => 'customer',
335
336 'children' => [
337 [
338 'label' => __('Customer Name', 'fluent-cart'),
339 'value' => 'customer_full_name',
340 'type' => 'text',
341 'filter_type' => 'custom',
342 'operators' => [
343 'like_all' => __('Contains', 'fluent-cart'),
344 'starts_with' => __('Starts With', 'fluent-cart'),
345 'ends_with' => __('Ends With', 'fluent-cart'),
346 'not_like' => __('Not Contains', 'fluent-cart'),
347 ],
348 'callback' => function ($query, $data) {
349 $query->whereHas('customer', function ($query) use ($data) {
350 $query->searchByFullName($data);
351 });
352 }
353 ],
354
355 [
356 'label' => __('Customer Email', 'fluent-cart'),
357 'value' => 'customer_email',
358 'type' => 'text',
359 'filter_type' => 'relation',
360 'column' => 'email',
361 'relation' => 'customer',
362 ]
363 ],
364 ],
365 'transactions' => [
366 'label' => __('Transactions Property', 'fluent-cart'),
367 'value' => 'transactions',
368
369 'children' => [
370 [
371 'label' => __('Transaction Id', 'fluent-cart'),
372 'value' => 'transaction_id',
373 'type' => 'text',
374 'filter_type' => 'relation',
375 'column' => 'vendor_charge_id',
376 'relation' => 'transactions',
377 ],
378 [
379 'label' => __('Transaction Status', 'fluent-cart'),
380 'value' => 'transaction_status',
381 'type' => 'selections',
382 'filter_type' => 'relation',
383 'column' => 'status',
384 'relation' => 'transactions',
385 'options' => [
386 Status::TRANSACTION_SUCCEEDED => __('Succeeded', 'fluent-cart'),
387 Status::TRANSACTION_PENDING => __('Pending', 'fluent-cart'),
388 Status::TRANSACTION_REFUNDED => __('Refunded', 'fluent-cart'),
389 Status::TRANSACTION_FAILED => __('Failed', 'fluent-cart'),
390 ],
391 'is_multiple' => true,
392 'is_only_in' => true
393 ],
394 [
395 'label' => __('Transaction Type', 'fluent-cart'),
396 'value' => 'transaction_type',
397 'type' => 'selections',
398 'filter_type' => 'relation',
399 'column' => 'transaction_type',
400 'relation' => 'transactions',
401 'options' => [
402 Status::TRANSACTION_TYPE_CHARGE => __('Charge', 'fluent-cart'),
403 Status::TRANSACTION_TYPE_REFUND => __('Refunded', 'fluent-cart'),
404 Status::TRANSACTION_TYPE_DISPUTE => __('Dispute', 'fluent-cart'),
405 ],
406 'is_multiple' => true,
407 'is_only_in' => true
408 ],
409 [
410 'label' => __('Card last 4', 'fluent-cart'),
411 'value' => 'transaction_card_last',
412 'type' => 'text',
413 'filter_type' => 'relation',
414 'column' => 'card_last_4',
415 'relation' => 'transactions',
416 ],
417 [
418 'label' => __('Card Brand', 'fluent-cart'),
419 'value' => 'transaction_card_brand',
420 'type' => 'text',
421 'filter_type' => 'relation',
422 'column' => 'card_brand',
423 'relation' => 'transactions',
424 ],
425 [
426 'label' => __('Payer email', 'fluent-cart'),
427 'value' => 'payer_email',
428 'type' => 'text',
429 'filter_type' => 'custom',
430 'operators' => [
431 'equals' => __('Equals', 'fluent-cart'),
432 'contains' => __('Contains', 'fluent-cart'),
433 'starts_with' => __('Starts With', 'fluent-cart'),
434 'ends_with' => __('Ends With', 'fluent-cart'),
435 'not_like' => __('Not Contains', 'fluent-cart')
436 ],
437 'callback' => function ($query, $data) {
438 $query->whereHas('transactions', function ($query) use ($data) {
439 $query->searchByPayerEmail($data);
440 });
441 },
442 'examples' => [
443 'payer_email = [email protected]',
444 ]
445 ]
446 ]
447 ]
448 ];
449
450 if (ModuleSettings::isActive('license') && App::isProActive()) {
451 $filters['license'] = [
452 'label' => __('License Property', 'fluent-cart'),
453 'value' => 'license',
454 'children' => [
455 [
456 'label' => __('License key', 'fluent-cart'),
457 'value' => 'license_key',
458 'type' => 'text',
459 'filter_type' => 'relation',
460 'column' => 'license_key',
461 'relation' => 'licenses',
462 ],
463 [
464 'label' => __('License Status', 'fluent-cart'),
465 'value' => 'license_status',
466 'type' => 'selections',
467 'filter_type' => 'relation',
468 'column' => 'status',
469 'relation' => 'licenses',
470 'options' => [
471 Status::LICENSE_ACTIVE => __('Active', 'fluent-cart'),
472 Status::LICENSE_DISABLED => __('Disabled', 'fluent-cart'),
473 Status::LICENSE_EXPIRED => __('Expired', 'fluent-cart'),
474 ],
475 'is_multiple' => true,
476 'is_only_in' => true
477 ]
478 ],
479 ];
480 }
481
482 $utmFilters = [
483 'utm_campaign' => __('Utm Campaign', 'fluent-cart'),
484 'utm_term' => __('Utm Term', 'fluent-cart'),
485 'utm_source' => __('Utm Source', 'fluent-cart'),
486 'utm_medium' => __('Utm Medium', 'fluent-cart'),
487 'utm_content' => __('Utm Content', 'fluent-cart'),
488 'utm_id' => __('Utm Id', 'fluent-cart'),
489 'refer_url' => __('Refer Url', 'fluent-cart'),
490 ];
491
492 $utmChildren = [];
493 foreach ($utmFilters as $key => $label) {
494 $utmChildren[] = [
495 'label' => $label,
496 'value' => $key,
497 'type' => 'text',
498 'filter_type' => 'relation',
499 'column' => $key,
500 'relation' => 'orderOperation',
501 ];
502 }
503
504 $filters['utm'] = [
505 'label' => __('Utm Property', 'fluent-cart'),
506 'value' => 'utm',
507
508 'children' => $utmChildren
509 ];
510
511 $filters['tax'] = [
512 'label' => __('Tax Property', 'fluent-cart'),
513 'value' => 'tax',
514 'children' => [
515 [
516 'label' => __('B2B Purchase', 'fluent-cart'),
517 'value' => 'b2b_purchase',
518 'filter_type' => 'custom',
519 'type' => 'selections',
520 'options' => [
521 'yes' => __('Yes', 'fluent-cart'),
522 'no' => __('No', 'fluent-cart'),
523 ],
524 'is_multiple' => false,
525 'is_only_in' => true,
526 'callback' => static function ($query, $item) {
527 $value = Arr::get($item, 'value');
528 $selected = is_array($value) ? Arr::get($value, 0, '') : $value;
529 if ($selected === 'yes') {
530 $query->whereHas('orderMeta', static::b2bMetaCondition());
531 } else {
532 $query->whereDoesntHave('orderMeta', static::b2bMetaCondition());
533 }
534 },
535 ],
536 [
537 'label' => __('Reverse Charge', 'fluent-cart'),
538 'value' => 'reverse_charge',
539 'filter_type' => 'custom',
540 'type' => 'selections',
541 'options' => [
542 'yes' => __('Yes', 'fluent-cart'),
543 'no' => __('No', 'fluent-cart'),
544 ],
545 'is_multiple' => false,
546 'is_only_in' => true,
547 'callback' => static function ($query, $item) {
548 $value = Arr::get($item, 'value');
549 $selected = is_array($value) ? Arr::get($value, 0, '') : $value;
550 if ($selected === 'yes') {
551 $query->whereHas('orderTaxRates', function ($q) {
552 $q->whereRaw("JSON_EXTRACT(meta, '$.reverse_charge_applied') = true");
553 });
554 } else {
555 $query->whereDoesntHave('orderTaxRates', function ($q) {
556 $q->whereRaw("JSON_EXTRACT(meta, '$.reverse_charge_applied') = true");
557 });
558 }
559 },
560 ],
561 ],
562 ];
563
564 return LabelFilter::advanceFilterOptionsForOther($filters);
565 }
566
567
568 public function centColumns(): array
569 {
570 return ['subtotal', 'shipping_total', 'total_amount', 'total_paid', 'total_refund'];
571 }
572 }
573