PluginProbe
Route ‑ Shipping Protection / 2.4.13
Route ‑ Shipping Protection v2.4.13
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-cron-schedules.php

class-routeapp-cron-schedules.php in Route ‑ Shipping Protection 2.4.13, at includes/class-routeapp-cron-schedules.php

470 lines 18.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * WooCommerce Routeapp Cron Schedules
5 *
6 * @link https://route.com/
7 * @since 1.0.0
8 *
9 * @package Routeapp
10 * @subpackage Routeapp/includes
11 */
12
13 use Automattic\WooCommerce\Utilities\OrderUtil;
14
15 class Routeapp_Cron_Schedules
16 {
17
18 private $routeapp_webhooks;
19
20 /**
21 * Initialize the class and set its properties.
22 *
23 * @since 1.0.0
24 */
25 public function __construct() {
26 $this->routeapp_webhooks = new Routeapp_Webhooks();
27 add_filter('cron_schedules', array($this, 'wpcron_schedule'));
28 add_action('routeapp_check_for_missing_orders', array($this, 'routeapp_validation_worker'));
29 add_action('routeapp_check_for_missing_shipments', array($this, 'routeapp_missing_shipments_worker'));
30 add_action('routeapp_check_for_invalid_webhooks', array($this, 'routeapp_webhooks_validator'));
31 $this->init();
32 }
33
34 /**
35 * Add wp_cron Minutely and Biminutely Interval Schedules if not is scheduled
36 *
37 * @since 1.0.3
38 * @return void
39 */
40 public function init() {
41 $this->set_schedule_event('routeapp_daily', 'routeapp_check_for_missing_orders');
42 $this->set_schedule_event('routeapp_5_hours', 'routeapp_check_for_missing_shipments');
43 $this->set_schedule_event('routeapp_5_hours', 'routeapp_check_for_invalid_webhooks');
44 }
45
46 public function set_schedule_event($event, $action) {
47 if (!wp_next_scheduled($action)) {
48 wp_schedule_event(time(), $event, $action);
49 };
50 }
51
52 /**
53 * @param $routeapp_client
54 * @param array $trackingPayload
55 * @param $order
56 * @param $params
57 * @return bool
58 */
59 public static function resend_shipment(array $trackingPayload)
60 {
61 $routeapp_client = Routeapp_API_Client::getInstance();
62
63 try {
64 $response = $routeapp_client->create_shipment($trackingPayload['tracking_number'], $trackingPayload);
65
66 if (!is_wp_error($response) &&
67 (wp_remote_retrieve_response_code($response) == 409 ||
68 wp_remote_retrieve_response_code($response) == 201)
69 ) {
70 return true;
71 }
72
73 return false;
74
75 } catch (Exception $exception) {
76 self::get_route_public_instance()->routeapp_log($exception, [
77 'endpoint' => 'shipments',
78 'params' => $trackingPayload,
79 'method' => 'POST'
80 ]);
81 return false;
82 }
83 }
84
85
86 /**
87 * Add wp_cron Daily, Daily Interval Schedules
88 *
89 * @since 1.0.3
90 * @param array $schedules
91 * @return array $schedules
92 */
93
94 public function wpcron_schedule($schedules) {
95
96 $schedules['routeapp_daily'] = array(
97 'interval' => 60 * 60 * 24,
98 'display' => __('Once Daily', 'routeapp')
99 );
100
101 $schedules['routeapp_5_hours'] = array(
102 'interval' => 60 * 60 * 5,
103 'display' => __('Every 5 hours', 'routeapp')
104 );
105
106 return $schedules;
107 }
108
109 /**
110 *
111 * Check if the order is created on Route API.
112 *
113 */
114 public static function has_route_order_id( $order_id ) {
115 return self::get_meta_data( $order_id, '_routeapp_order_id');
116 }
117
118 /**
119 * get meta data if custom order table is enabled
120 * @param $order_id
121 * @param $post_meta
122 * @return array|mixed|string
123 */
124 public static function get_meta_data( $order_id, $post_meta ) {
125 if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil')
126 && OrderUtil::custom_orders_table_usage_is_enabled() ) {
127 $order = wc_get_order($order_id);
128 return $order->get_meta($post_meta);
129 } else {
130 // Traditional CPT-based orders are in use.
131 return get_post_meta($order_id, $post_meta, true);
132 }
133 }
134
135
136 /**
137 *
138 * Get the Route Public class instance
139 *
140 */
141 public static function get_route_public_instance() {
142 global $routeapp_public;
143 return $routeapp_public;
144 }
145
146 /**
147 *
148 * Set the 'routeapp_shipment_cron_api_called' as a flag to avoid multiple requests to Route
149 *
150 */
151 public static function has_reconcile_shipping_triggered( $order_id ) {
152 return self::get_meta_data( $order_id, 'routeapp_shipment_cron_api_called') == 'success';
153 }
154
155 /**
156 *
157 * Get the 'routeapp_shipment_cron_api_called' as a flag see if the order has already be sent to Route
158 *
159 */
160 public static function set_reconcile_shipping_triggered( $order_id ) {
161 self::update_post_meta_value( $order_id, 'routeapp_shipment_cron_api_called', 'success' );
162 }
163
164 /**
165 * Update post meta value
166 *
167 * @param $order_id
168 * @param $post_meta
169 * @param $value
170 */
171 public static function update_post_meta_value( $order_id, $post_meta, $value) {
172 if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil')
173 && OrderUtil::custom_orders_table_usage_is_enabled() ) {
174 $order = wc_get_order($order_id);
175 $order->update_meta_data($post_meta, $value );
176 $order->save();
177 } else {
178 // Traditional CPT-based orders are in use.
179 update_post_meta( $order_id, $post_meta, $value );
180 }
181 }
182
183 /**
184 * Periodic Worker Triggering Missing Shippings on Route
185 *
186 * @return void
187 */
188 public static function routeapp_reconcile_shippings($orders)
189 {
190 $routeapp_client = Routeapp_API_Client::getInstance();
191 if (count($orders) > 0) {
192 foreach ($orders as $order) {
193
194 if (!$order || self::has_reconcile_shipping_triggered($order->get_id()) || !self::has_route_order_id($order->get_id())) {
195 continue;
196 }
197
198 try {
199
200 $trackingPayloads = self::format_shipping_data_api($order->get_id());
201 $tracking_numbers = array();
202 foreach ($trackingPayloads as $trackingPayload){
203 $shipmentResponse = $routeapp_client->get_shipment($trackingPayload['tracking_number'], $order->get_id());
204 $tracking_numbers[] = $trackingPayload['tracking_number'];
205 if (is_wp_error($shipmentResponse) || (isset($shipmentResponse['response']['code']) && $shipmentResponse['response']['code'] == 200)) {
206 continue;
207 }
208
209 self::resend_shipment($trackingPayload);
210 }
211
212 //save on post_meta the tracking ids
213 if (!empty($tracking_numbers)) {
214 $tracking_numbers = implode(Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_PIPE, $tracking_numbers);
215 self::update_post_meta_value($order->get_id(), 'routeapp_shipment_tracking_number', $tracking_numbers);
216 }
217
218 self::set_reconcile_shipping_triggered($order->get_id());
219
220 } catch (Exception $exception) {
221 continue;
222 }
223 }
224 }
225 }
226
227
228 /**
229 * create shipping data sending for API
230 * @since 1.0.0
231 * @param integer $order_id
232 */
233 public static function format_shipping_data_api($order_id)
234 {
235 $order = wc_get_order($order_id);
236
237 if (!$order) return [];
238
239 $tracking_number = self::get_meta_data( $order->get_id(), 'routeapp_shipment_tracking_number');
240 $routeapp_shipment_tracking = new Routeapp_Shipment_Tracking();
241 $courier_id = $routeapp_shipment_tracking->get_shipping_provider_name($order_id);
242 // Same ID resolution as shipment create / webhook order-create.
243 $product_ids = Routeapp_WooCommerce_Common_Tracking_Provider::get_order_products_from_order( $order );
244
245 if (strpos($tracking_number, Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_PIPE) > 0) {
246 $tracking_numbers = explode(Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_PIPE, $tracking_number);
247 } elseif (strpos($tracking_number, Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_COMMA) > 0) {
248 $tracking_numbers = explode(Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_COMMA, $tracking_number);
249 }else{
250 $tracking_numbers = [$tracking_number];
251 }
252
253 $trackings = [];
254
255 foreach ($tracking_numbers as $tracking){
256 if ($tracking!='') {
257 $trackings[] = [
258 'source_order_id' => $order->get_id(),
259 'source_product_ids' => $product_ids,
260 'courier_id' => is_string($courier_id) ? $courier_id : $courier_id[$tracking],
261 'tracking_number' => $tracking,
262 ];
263 }
264 }
265 if(empty($trackings[0])) {
266 //we don't have shipping on our post_meta yet, try to get the shipping data from third party module
267 $trackings = $routeapp_shipment_tracking->get_shipping_info($order_id);
268 }
269
270 return $trackings;
271 }
272
273
274 /**
275 * Periodic Worker Triggering Transaction Validation Jobs on Route
276 *
277 * @return void
278 */
279 function routeapp_validation_worker() {
280 $after = get_option('_routeapp_last_install_date');
281
282 $acceptedOrderStatuses = get_option('routeapp_included_order_statuses') ? get_option('routeapp_included_order_statuses') : [];
283
284 $args = array(
285 'post_type' => 'shop_order',
286 'posts_per_page' => -1,
287 'post_status' => array_merge($acceptedOrderStatuses),
288 'date_created' => '>=' . $after,
289 'meta_query' => array(
290 array(
291 'key' => '_routeapp_order_id',
292 'compare' => 'NOT EXISTS'
293 ),
294 )
295 );
296
297 $orders = wc_get_orders($args);
298
299 $this::routeapp_run_order_reconcile($orders);
300 }
301
302 /**
303 * Update order status meta (_routeapp_included_order_statuses, _routeapp_cancel_order_statuses)
304 * only if they don't exist or are different from current options.
305 *
306 * @param \WC_Order $order The order to update.
307 * @param bool $auto_save If true (default), saves the order when HPOS and meta changed.
308 * Set to false when the caller will save the order (e.g. massiveOrderSave).
309 * @return void
310 */
311 public static function updateOrderStatusMetaIfNeeded( $order, $auto_save = true ) {
312 if ( ! $order ) {
313 return;
314 }
315 $routeapp_public = self::get_route_public_instance();
316 if ( ! $routeapp_public ) {
317 return;
318 }
319 $is_hpos = class_exists( 'Automattic\WooCommerce\Utilities\OrderUtil' )
320 && OrderUtil::custom_orders_table_usage_is_enabled();
321 $acceptedOrderStatuses = get_option( 'routeapp_included_order_statuses' ) ? get_option( 'routeapp_included_order_statuses' ) : array();
322 $acceptedCanceledStatuses = get_option( 'routeapp_cancel_order_statuses' ) ? get_option( 'routeapp_cancel_order_statuses' ) : array();
323
324 $updated = false;
325 $updated |= $routeapp_public->updateRouteOrderMetaData( $order, $is_hpos, '_routeapp_included_order_statuses', $acceptedOrderStatuses );
326 $updated |= $routeapp_public->updateRouteOrderMetaData( $order, $is_hpos, '_routeapp_cancel_order_statuses', $acceptedCanceledStatuses );
327
328 if ( $updated && $is_hpos && $auto_save ) {
329 $order->save();
330 }
331 }
332
333 /**
334 * Update order post_meta with route order data
335 *
336 * @param $order
337 * @param $routeOrder
338 * @return void
339 */
340 public static function updateOrderPostMeta($order, $routeOrder) {
341 //order was found, save route order id in db
342 $routeOrder = json_decode($routeOrder['body'], true);
343 $routeCharge = $routeOrder['insured_status'] == 'insured_selected' ? $routeOrder['paid_to_insure'] : '';
344 $protected = !empty($routeCharge) ? 1 : 0;
345 $acceptedOrderStatuses = get_option( 'routeapp_included_order_statuses' ) ? get_option( 'routeapp_included_order_statuses' ) : array();
346 $acceptedCanceledStatuses = get_option( 'routeapp_cancel_order_statuses' ) ? get_option( 'routeapp_cancel_order_statuses' ) : array();
347
348 if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil')
349 && OrderUtil::custom_orders_table_usage_is_enabled() ) {
350 // HPOS usage is enabled.
351 $order->update_meta_data( '_routeapp_order_id', $routeOrder['id'] );
352 $order->update_meta_data( '_routeapp_route_charge', $routeCharge );
353 $order->update_meta_data( '_routeapp_route_protection', $protected );
354 self::updateOrderStatusMetaIfNeeded( $order, false );
355 $order->save();
356 } else {
357 // Traditional CPT-based orders are in use.
358 update_post_meta( $order->get_id(), '_routeapp_order_id', $routeOrder['id'] );
359 update_post_meta( $order->get_id(), '_routeapp_route_charge', $routeCharge );
360 update_post_meta( $order->get_id(), '_routeapp_route_protection', $protected );
361 self::updateOrderStatusMetaIfNeeded( $order );
362 }
363 }
364
365 public static function routeapp_run_order_reconcile($orders) {
366 $routeapp_client = Routeapp_API_Client::getInstance();
367 if (count($orders) > 0) {
368
369 foreach ($orders as $order) {
370 //check if order exists on Route side
371 $getOrderResponse = $routeapp_client->get_order($order->get_id());
372
373 if (is_wp_error($getOrderResponse) || !in_array($getOrderResponse['response']['code'], [200, 404])) {
374 continue;
375 }
376
377 if ($getOrderResponse['response']['code'] == 200 ) {
378 //order was found, save route order id in db
379 self::updateOrderPostMeta($order, $getOrderResponse);
380 } else {
381 //order not found
382 //create from our side
383 $routeapp_public = self::get_route_public_instance();
384 if (!$routeapp_public->routeapp_is_shipping_method_allowed(false, $order)) continue;
385 $orderData = $routeapp_public->create_order_data_api($order->get_id());
386 $response = $routeapp_client->create_order($orderData);
387 if (!is_wp_error( $response )) {
388 $body = json_decode( $response['body'] );
389 try {
390 if ($response['response']['code'] > 201 && $response['response']['code'] < 409) {
391 $note = 'Route API Error while posting data. Errorcode- '.$response['response']['code'];
392 throw new Exception($note);
393 } else if (!empty($body->id) && !empty($body->order_number) || $response['response']['code'] == 409) {
394
395 $routeCharge = $body->insured_status == 'insured_selected' ? $body->paid_to_insure : '';
396 $protected = !empty($routeCharge) ? 1 : 0;
397 $acceptedOrderStatuses = get_option( 'routeapp_included_order_statuses' ) ? get_option( 'routeapp_included_order_statuses' ) : array();
398 $acceptedCanceledStatuses = get_option( 'routeapp_cancel_order_statuses' ) ? get_option( 'routeapp_cancel_order_statuses' ) : array();
399
400 if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil')
401 && OrderUtil::custom_orders_table_usage_is_enabled() ) {
402 // HPOS usage is enabled.
403 $order->update_meta_data('_routeapp_order_id', $body->id );
404 $order->update_meta_data('_routeapp_route_charge', $routeCharge );
405 $order->update_meta_data('_routeapp_route_protection', $protected );
406 self::updateOrderStatusMetaIfNeeded( $order, false );
407 $order->save();
408 } else {
409 // Traditional CPT-based orders are in use.
410 update_post_meta( $order->get_id(), '_routeapp_order_id', $body->id );
411 update_post_meta( $order->get_id(), '_routeapp_route_charge', $routeCharge );
412 update_post_meta( $order->get_id(), '_routeapp_route_protection', $protected );
413 self::updateOrderStatusMetaIfNeeded( $order );
414 }
415 }
416 } catch(Exception $exception) {
417 $routeapp_public = self::get_route_public_instance();
418 $extraData = array(
419 'params' => $orderData,
420 'method' => 'POST',
421 'endpoint' => 'orders'
422 );
423 $routeapp_public->routeapp_log($exception, $extraData);
424 }
425 }
426 }
427 }
428 }
429 }
430
431 /**
432 * Periodic Worker that checks Route Webhooks validations
433 *
434 *
435 * @return void
436 */
437 function routeapp_webhooks_validator() {
438 $this->routeapp_webhooks->upsert_webhooks();
439 }
440
441 public function routeapp_missing_shipments_worker() {
442 $after = get_option('_routeapp_last_install_date');
443
444 $acceptedOrderStatuses = get_option('routeapp_included_order_statuses') ? get_option('routeapp_included_order_statuses') : [];
445 $acceptedCanceledStatuses = get_option('routeapp_cancel_order_statuses') ? get_option('routeapp_cancel_order_statuses') : [];
446
447 $args = array(
448 'post_type' => 'shop_order',
449 'posts_per_page' => -1,
450 'post_status' => array_merge($acceptedOrderStatuses, $acceptedCanceledStatuses),
451 'date_created' => '>=' . $after,
452 'meta_query' => array(
453 array(
454 'key' => 'routeapp_shipment_tracking_number',
455 'value' => '',
456 'compare' => '!='
457 ),
458 array(
459 'key' => 'routeapp_shipment_cron_api_called',
460 'compare' => 'NOT EXISTS'
461 ),
462 )
463 );
464
465 $orders = wc_get_orders($args);
466
467 $this->routeapp_reconcile_shippings($orders);
468 }
469 }
470