PluginProbe
Route ‑ Shipping Protection / 2.4.10
Route ‑ Shipping Protection v2.4.10
2.4.19 2.4.18 2.4.17 2.4.16 2.4.15 2.4.14 2.4.13 2.4.12 2.4.11 2.4.10 2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.2.4 2.2.5 2.2.6 2.2.8 2.2.9 2.3.0 2.3.1 All 153 releases
routeapp / includes / class-routeapp-admin-route-fee.php

class-routeapp-admin-route-fee.php in Route ‑ Shipping Protection 2.4.10, at includes/class-routeapp-admin-route-fee.php

346 lines 11.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Automattic\WooCommerce\Utilities\OrderUtil;
4
5 class Routeapp_Admin_Route_Fee {
6
7 const ROUTE_CHARGE_POST_META = '_routeapp_route_charge';
8 const ROUTE_PROTECTION_POST_META = '_routeapp_route_protection';
9
10 /**
11 * Per-request quote cache keyed by admin cart ref.
12 *
13 * @var array<string, object|false>
14 */
15 private $admin_order_quote_cache = array();
16
17 public function __construct()
18 {
19 if (!is_admin()) {
20 return false;
21 }
22 $this->init();
23 }
24
25 public function init()
26 {
27 add_action('woocommerce_order_item_add_action_buttons', array($this, 'routeapp_custom_admin_button'), 10, 1);
28 add_action('woocommerce_new_order_item', array($this, 'recalculate_route_fee'), 10, 1);
29 add_action('woocommerce_add_order_tax', array($this, 'recalculate_route_fee'), 10, 1);
30 add_action('woocommerce_saved_order_items', array($this, 'recalculate_route_fee'), 10, 1);
31 add_action('woocommerce_delete_order_item', array($this, 'recalculate_route_fee'), 10, 1);
32 }
33
34 public static function get_route_public_instance(){
35 global $routeapp_public;
36 return $routeapp_public;
37 }
38
39 public function routeapp_custom_admin_button($order)
40 {
41 echo $this->get_route_fee_button_html($order);
42 }
43
44 public function get_route_fee_button_html($order)
45 {
46 if (!$this->can_show_route_fee_buttons($order)) {
47 return '';
48 }
49
50 if ($this->has_route_fee_applied($order)) {
51 return '<button type="button" id="remove-route-fee" class="button generate-items">' . esc_html__( 'Remove Route Fee', 'routeapp' ) . '</button>';
52 }
53
54 if ($this->is_order_eligible_for_route_coverage($order)) {
55 return '<button type="button" id="add-route-fee" class="button generate-items">' . esc_html__( 'Add Route Fee', 'routeapp' ) . '</button>';
56 }
57
58 return '';
59 }
60
61 public function routeapp_refresh_admin_fee_button()
62 {
63 check_ajax_referer( 'order-item', 'security' );
64
65 if (!isset($_POST['order_id'])) {
66 wp_send_json_error('Missing order_id in the request.');
67 return;
68 }
69
70 $order = wc_get_order(absint($_POST['order_id']));
71 if (!$order) {
72 wp_send_json_error('Invalid order_id.');
73 return;
74 }
75
76 wp_send_json_success(array(
77 'html' => $this->get_route_fee_button_html($order),
78 ));
79 }
80
81 private function get_admin_order_cart_ref($order, $cart_total, $cart_items)
82 {
83 $items_hash = substr(md5(wp_json_encode($cart_items)), 0, 8);
84
85 return 'admin-' . $order->get_id() . '-' . $cart_total . '-' . $items_hash;
86 }
87
88 private function get_admin_order_quote_context($order)
89 {
90 $routeapp_public = self::get_route_public_instance();
91 $cart_total = round($routeapp_public->get_cart_subtotal_with_only_shippable_items($order, 'order'), 2);
92 $cart_items = $routeapp_public->get_cart_shippable_items($order, 'order');
93
94 return array(
95 'total' => $cart_total,
96 'items' => $cart_items,
97 'ref' => $this->get_admin_order_cart_ref($order, $cart_total, $cart_items),
98 'currency' => get_woocommerce_currency(),
99 'public' => $routeapp_public,
100 );
101 }
102
103 private function fetch_admin_order_quote($order)
104 {
105 $context = $this->get_admin_order_quote_context($order);
106
107 if ($context['total'] <= 0) {
108 return false;
109 }
110
111 if (array_key_exists($context['ref'], $this->admin_order_quote_cache)) {
112 return $this->admin_order_quote_cache[$context['ref']];
113 }
114
115 $quote = $context['public']->routeapp_get_quote_from_api(
116 $context['ref'],
117 $context['total'],
118 $context['currency'],
119 $context['items']
120 );
121
122 $this->admin_order_quote_cache[$context['ref']] = $quote;
123
124 return $quote;
125 }
126
127 public function is_order_eligible_for_route_coverage($order)
128 {
129 if (!$order) {
130 return false;
131 }
132
133 $routeapp_public = self::get_route_public_instance();
134 $route_insurance_quote = $this->fetch_admin_order_quote($order);
135
136 return $this->is_quote_eligible_for_customer_coverage($route_insurance_quote, $routeapp_public);
137 }
138
139 private function is_quote_eligible_for_customer_coverage($route_insurance_quote, $routeapp_public)
140 {
141 if (!$route_insurance_quote) {
142 return false;
143 }
144
145 if (!isset($route_insurance_quote->premium->amount) || $route_insurance_quote->premium->amount <= 0) {
146 return false;
147 }
148
149 return isset($route_insurance_quote->payment_responsible->type)
150 && $route_insurance_quote->payment_responsible->type === $routeapp_public->get_allowed_quote_type();
151 }
152
153 private function routeapp_handle_admin_fee($order)
154 {
155 if (!$this->can_add_fee($order)) {
156 return false;
157 }
158 $routeapp_public = self::get_route_public_instance();
159 $route_insurance_quote = $this->fetch_admin_order_quote($order);
160
161 $route_insurance_amount = false;
162 if ($this->is_quote_eligible_for_customer_coverage($route_insurance_quote, $routeapp_public)) {
163 $route_insurance_amount = $route_insurance_quote->premium->amount;
164 }
165 if ($route_insurance_amount > 0) {
166 $this->routeapp_create_fee($order, $route_insurance_amount);
167 $order->calculate_totals();
168 if ($order->save()) {
169
170 $acceptedOrderStatuses = get_option('routeapp_included_order_statuses') ? get_option('routeapp_included_order_statuses') : [];
171 $acceptedCanceledStatuses = get_option('routeapp_cancel_order_statuses') ? get_option('routeapp_cancel_order_statuses') : [];
172
173 if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil')
174 && OrderUtil::custom_orders_table_usage_is_enabled() ) {
175 // HPOS usage is enabled.
176 $order->update_meta_data(self::ROUTE_CHARGE_POST_META, $route_insurance_amount );
177 $order->update_meta_data(self::ROUTE_PROTECTION_POST_META, true );
178
179 $order->update_meta_data('_routeapp_included_order_statuses', json_encode($acceptedOrderStatuses));
180 $order->update_meta_data( '_routeapp_cancel_order_statuses', json_encode($acceptedCanceledStatuses));
181
182 $order->save();
183 } else {
184 // Traditional CPT-based orders are in use.
185 update_post_meta( $order->get_id(), self::ROUTE_CHARGE_POST_META, $route_insurance_amount );
186 update_post_meta( $order->get_id(), self::ROUTE_PROTECTION_POST_META, true );
187
188 update_post_meta( $order->get_id(), '_routeapp_included_order_statuses', json_encode($acceptedOrderStatuses));
189 update_post_meta( $order->get_id(), '_routeapp_cancel_order_statuses', json_encode($acceptedCanceledStatuses));
190
191 }
192 return true;
193 }
194 } else {
195 if ($this->has_route_fee_applied($order)) {
196 $this->routeapp_remove_route_fee($order);
197 $order->calculate_totals();
198 $order->save();
199 }
200 }
201
202 return false;
203 }
204
205 private function routeapp_create_fee($order, $fee_amount) {
206 $routeapp_public = self::get_route_public_instance();
207 if (!$this->has_route_fee_applied($order)) {
208 $item_fee = new WC_Order_Item_Fee();
209 $item_fee->set_name( Routeapp_Public::ROUTE_LABEL );
210 $item_fee->set_tax_class( $routeapp_public->routeapp_get_taxable_class() );
211 $item_fee->set_tax_status( $routeapp_public->routeapp_get_fee_taxable() ? 'taxable' : 'none' );
212 $item_fee->set_amount( $fee_amount );
213 $item_fee->set_total( $fee_amount );
214 $order->add_item( $item_fee );
215 } else {
216 $fees = $this->get_route_fees($order);
217 foreach($fees as $fee_id => $fee) {
218 if ($fee) {
219 $fee->set_amount($fee_amount);
220 $fee->set_total($fee_amount);
221 $fee->save();
222 }
223 }
224 }
225 }
226
227 public function routeapp_add_admin_fee()
228 {
229 check_ajax_referer( 'order-item', 'security' );
230
231 if (!isset($_POST) || !$_POST['order_id']) {
232 wp_send_json_error('Missing order_id in the request.');
233 return;
234 }
235
236 $order_id = isset($_POST['order_id']) ? $_POST['order_id'] : 0;
237 $order = wc_get_order( $order_id );
238 if (!$order) {
239 wp_send_json_error('Invalid order_id.');
240 return;
241 }
242
243 $response = $this->routeapp_handle_admin_fee($order);
244
245 if ($response) {
246 wp_send_json_success( 'order updated!' );
247 return;
248 }
249
250 wp_send_json_error('error while adding route fee to the order');
251 }
252
253 function routeapp_remove_admin_fee()
254 {
255 check_ajax_referer( 'order-item', 'security' );
256
257 if (!isset($_POST) || !$_POST['order_id']) {
258 wp_send_json_error('Missing order_id in the request.');
259 return;
260 }
261
262 $order_id = isset($_POST['order_id']) ? $_POST['order_id'] : 0;
263 $order = wc_get_order( $order_id );
264 if (!$order) {
265 wp_send_json_error('Invalid order_id.');
266 return;
267 }
268
269 if ($this->has_route_fee_applied($order)) {
270 $this->routeapp_remove_route_fee($order);
271 $order->calculate_totals();
272 $order->save();
273 }
274
275 wp_send_json_success('route fee removed from the order');
276 }
277
278 private function routeapp_remove_route_fee($order)
279 {
280 $fees = $this->get_route_fees($order);
281
282 foreach($fees as $fee_id => $fee) {
283 if ($fee) {
284 wc_delete_order_item($fee_id);
285 }
286 }
287
288 if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil')
289 && OrderUtil::custom_orders_table_usage_is_enabled() ) {
290 $order->delete_meta_data(self::ROUTE_CHARGE_POST_META);
291 $order->delete_meta_data(self::ROUTE_PROTECTION_POST_META);
292 } else {
293 delete_post_meta( $order->get_id(), self::ROUTE_CHARGE_POST_META );
294 delete_post_meta( $order->get_id(), self::ROUTE_PROTECTION_POST_META );
295 }
296 }
297
298 public function recalculate_route_fee($order_id)
299 {
300 $order = wc_get_order( $order_id );
301
302 if ($this->has_route_fee_applied($order)) {
303 $this->routeapp_handle_admin_fee($order);
304 }
305 }
306
307 private function get_route_fees($order)
308 {
309 if (!$order) {
310 return [];
311 }
312
313 $fees = $order->get_fees();
314 $route_fees = [];
315 foreach($fees as $fee_id => $fee) {
316 $fee_name = is_object($fee) ? $fee->get_name() : (isset($fee['name']) ? $fee['name'] : '');
317 if ($fee_name === Routeapp_Public::ROUTE_LABEL) {
318 $route_fees[$fee_id] = $fee;
319 }
320 }
321 return $route_fees;
322 }
323
324 public function can_show_route_fee_buttons($order)
325 {
326 return $order
327 && count($order->get_items()) > 0
328 && is_admin()
329 && in_array($order->get_status(), array('pending', 'on-hold', 'auto-draft'), true);
330 }
331
332 public function can_add_fee($order)
333 {
334 return $this->can_show_route_fee_buttons($order);
335 }
336
337 private function has_route_fee_applied($order)
338 {
339 $routeFees = $this->get_route_fees($order);
340 if (is_array($routeFees) || $routeFees instanceof Countable) {
341 return count($routeFees) > 0;
342 }
343 return false;
344 }
345 }
346