PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.4
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 1.2.0 All 47 releases
fluent-cart / app / Modules / Shipping / ShippingModule.php

ShippingModule.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.4, at app/Modules/Shipping/ShippingModule.php

184 lines 5.8 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\Modules\Shipping;
4
5 use FluentCart\App\App;
6 use FluentCart\App\Helpers\AddressHelper;
7 use FluentCart\App\Helpers\CartHelper;
8 use FluentCart\App\Modules\Shipping\Http\Handlers\ScriptHandler;
9 use FluentCart\App\Modules\Shipping\Services\Filter\ShippingZoneFilter;
10 use FluentCart\App\Vite;
11 use FluentCart\Framework\Support\Arr;
12
13 class ShippingModule
14 {
15 public static function register()
16 {
17 $self = new static();
18 add_action('fluentcart_loaded', [$self, 'init']);
19
20 add_filter('fluent_cart/checkout/before_patch_checkout_data', [$self, 'maybeRecalculateShippingCharges'], 9, 2);
21
22 add_action('fluent_cart/cart/cart_data_items_updated', [$self, 'handleItemsChanges'], 9, 1);
23 }
24
25 public function init($app)
26 {
27 $app->router->group(function ($router) {
28 require_once __DIR__ . '/Http/shipping-api.php';
29 });
30
31 (new ScriptHandler())->register();
32 }
33
34 public function handleItemsChanges($data)
35 {
36 $scope = Arr::get($data, 'scope');
37
38 $ignores = [
39 'apply_coupons',
40 'discounts_recalculated',
41 'remove_coupon',
42 'payment_method_fee_recalculate',
43 ];
44
45 if (in_array($scope, $ignores)) {
46 return;
47 }
48
49 $cart = Arr::get($data, 'cart');
50 if (!$cart->requireShipping()) {
51 return;
52 }
53
54 $fillData = $this->getRecalculatedCartDataArr([
55 'cart_data' => $cart->cart_data,
56 'checkout_data' => $cart->checkout_data
57 ], $cart);
58
59 $cart->fill($fillData);
60 $cart->save();
61 }
62
63 public function maybeRecalculateShippingCharges($fillData, $data)
64 {
65 $cart = $data['cart'];
66 if (!$cart->requireShipping()) {
67 return $fillData;
68 }
69
70 $watchingKeys = [
71 'shipping_country',
72 'shipping_state',
73 'shipping_method_id'
74 ];
75
76 $changes = Arr::get($data, 'changes', []);
77 if (!array_intersect(array_keys($changes), $watchingKeys)) {
78 // No changes detected
79 return $fillData;
80 }
81
82 Arr::set($fillData, 'hook_changes.shipping', true);
83 $fillData = $this->getRecalculatedCartDataArr($fillData, $cart);
84
85 return $fillData;
86 }
87
88 private function getRecalculatedCartDataArr($fillData, $cart)
89 {
90 $shippingCountry = Arr::get($fillData, 'checkout_data.form_data.shipping_country', null);
91 $shippingState = Arr::get($fillData, 'checkout_data.form_data.shipping_state', null);
92
93 // Checkout renderer resolves shipping methods with a billing fallback when no
94 // shipping address is set (ship-to-different unchecked). Resolve the same way
95 // here, otherwise this chain zeroes the method/charges the renderer just offered.
96 if (!$shippingCountry) {
97 $shippingCountry = Arr::get($fillData, 'checkout_data.form_data.billing_country', null);
98 $shippingState = Arr::get($fillData, 'checkout_data.form_data.billing_state', null);
99 }
100
101 $availableShippingMethods = AddressHelper::getShippingMethods($shippingCountry, $shippingState);
102
103 if (!$availableShippingMethods || is_wp_error($availableShippingMethods)) {
104 Arr::set($fillData, 'checkout_data.shipping_data.shipping_charge', 0);
105 Arr::set($fillData, 'checkout_data.shipping_data.shipping_method_id', null);
106
107 $cartData = Arr::get($fillData, 'cart_data', []);
108 foreach ($cartData as &$cartItem) {
109 $cartItem['shipping_charge'] = 0;
110 $cartItem['itemwise_shipping_charge'] = 0;
111 }
112 unset($cartItem);
113
114 Arr::set($fillData, 'cart_data', $cartData);
115
116 return $fillData;
117 }
118
119 $prevShippingMethodId = Arr::get($fillData, 'checkout_data.shipping_data.shipping_method_id', null);
120
121 $shippingMethod = Arr::first($availableShippingMethods);
122 if ($prevShippingMethodId) {
123 foreach ($availableShippingMethods as $method) {
124 if ($method->id == $prevShippingMethodId) {
125 $shippingMethod = $method;
126 break;
127 }
128 }
129 }
130
131 // Use profile-aware calculation if any cart items have shipping classes
132 $cartData = Arr::get($fillData, 'cart_data', []);
133 $hasShippingClasses = $this->cartHasShippingClasses($cartData);
134
135 if ($hasShippingClasses && $shippingCountry) {
136 $calculations = CartHelper::calculateShippingByProfile(
137 $shippingMethod->id,
138 $cartData,
139 $shippingCountry,
140 $shippingState,
141 'items'
142 );
143 } else {
144 $calculations = CartHelper::calculateShippingMethodCharge($shippingMethod, $cartData, 'items');
145 }
146
147 $newCharge = Arr::get($calculations, 'shipping_amount', 0);
148 $items = Arr::get($calculations, 'items', []);
149
150 Arr::set($fillData, 'cart_data', $items);
151
152 Arr::set($fillData, 'checkout_data.shipping_data.shipping_charge', $newCharge);
153 Arr::set($fillData, 'checkout_data.shipping_data.shipping_method_id', $shippingMethod->id);
154
155 return $fillData;
156 }
157
158 private function cartHasShippingClasses($cartData)
159 {
160 if (empty($cartData)) {
161 return false;
162 }
163
164 $postIds = array_unique(array_column($cartData, 'post_id'));
165 if (empty($postIds)) {
166 return false;
167 }
168
169 $products = \FluentCart\App\Models\Product::query()
170 ->whereIn('ID', $postIds)
171 ->with(['detail'])
172 ->get();
173
174 foreach ($products as $product) {
175 if ($product->detail && !empty($product->detail->other_info['shipping_class'])) {
176 return true;
177 }
178 }
179
180 return false;
181 }
182
183 }
184