PluginProbe
Route ‑ Shipping Protection / 2.2.6
Route ‑ Shipping Protection v2.2.6
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 / trunk / includes / class-routeapp-cron-schedules.php

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

440 lines 16.4 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_weekly_missing_shipments', array($this, 'routeapp_weekly_missing_shipments_worker'));
30 add_action('routeapp_check_for_missing_shipments', array($this, 'routeapp_missing_shipments_worker'));
31 add_action('routeapp_check_for_invalid_webhooks', array($this, 'routeapp_webhooks_validator'));
32 $this->init();
33 }
34
35 /**
36 * Add wp_cron Minutely and Biminutely Interval Schedules if not is scheduled
37 *
38 * @since 1.0.3
39 * @return void
40 */
41 public function init() {
42 $this->set_schedule_event('routeapp_daily', 'routeapp_check_for_missing_orders');
43 $this->set_schedule_event('routeapp_5_hours', 'routeapp_check_for_missing_shipments');
44 $this->set_schedule_event('routeapp_daily', 'routeapp_check_for_weekly_missing_shipments');
45 $this->set_schedule_event('routeapp_5_hours', 'routeapp_check_for_invalid_webhooks');
46 }
47
48 public function set_schedule_event($event, $action) {
49 if (!wp_next_scheduled($action)) {
50 wp_schedule_event(time(), $event, $action);
51 };
52 }
53
54 /**
55 * @param $routeapp_client
56 * @param array $trackingPayload
57 * @param $order
58 * @param $params
59 * @return bool
60 */
61 public static function resend_shipment(array $trackingPayload)
62 {
63 $routeapp_client = Routeapp_API_Client::getInstance();
64
65 try {
66 $response = $routeapp_client->create_shipment($trackingPayload['tracking_number'], $trackingPayload);
67
68 if (!is_wp_error($response) &&
69 (wp_remote_retrieve_response_code($response) == 409 ||
70 wp_remote_retrieve_response_code($response) == 201)
71 ) {
72 return true;
73 }
74
75 return false;
76
77 } catch (Exception $exception) {
78 self::get_route_public_instance()->routeapp_log($exception, [
79 'endpoint' => 'shipments',
80 'params' => $trackingPayload,
81 'method' => 'POST'
82 ]);
83 return false;
84 }
85 }
86
87
88 /**
89 * Add wp_cron Daily, Daily Interval Schedules
90 *
91 * @since 1.0.3
92 * @param array $schedules
93 * @return array $schedules
94 */
95
96 public function wpcron_schedule($schedules) {
97
98 $schedules['routeapp_daily'] = array(
99 'interval' => 60 * 60 * 24,
100 'display' => __('Once Daily', 'routeapp')
101 );
102
103 $schedules['routeapp_5_hours'] = array(
104 'interval' => 60 * 60 * 5,
105 'display' => __('Every 5 hours', 'routeapp')
106 );
107
108 return $schedules;
109 }
110
111 /**
112 *
113 * Check if the order is created on Route API.
114 *
115 */
116 public static function has_route_order_id( $order_id ) {
117 return get_post_meta( $order_id, '_routeapp_order_id', true );
118 }
119
120 /**
121 *
122 * Get the Route Public class instance
123 *
124 */
125 public static function get_route_public_instance() {
126 global $routeapp_public;
127 return $routeapp_public;
128 }
129
130 /**
131 *
132 * Set the 'routeapp_shipment_cron_api_called' as a flag to avoid multiple requests to Route
133 *
134 */
135 public static function has_reconcile_shipping_triggered( $order_id ) {
136 return get_post_meta( $order_id, 'routeapp_shipment_cron_api_called', true ) == 'success';
137 }
138
139 /**
140 *
141 * Get the 'routeapp_shipment_cron_api_called' as a flag see if the order has already be sent to Route
142 *
143 */
144 public static function set_reconcile_shipping_triggered( $order_id ) {
145 update_post_meta( $order_id, 'routeapp_shipment_cron_api_called', 'success' );
146 }
147
148 /**
149 * Periodic Worker Triggering Missing Shippings on Route
150 *
151 * @return void
152 */
153 public static function routeapp_reconcile_shippings($date_query)
154 {
155 $args = array(
156 'post_type' => 'shop_order',
157 'posts_per_page' => -1,
158 'post_status' => array_keys(wc_get_order_statuses()),
159 'date_query' => $date_query,
160 'meta_query' => array(
161 'relation' => 'AND',
162 array(
163 'key' => 'routeapp_shipment_tracking_number',
164 'value' => '',
165 'compare' => '!='
166 )
167 )
168 );
169
170 $query = new WP_Query($args);
171 $orders = $query->get_posts();
172 $routeapp_client = Routeapp_API_Client::getInstance();
173
174 if (count($orders) > 0) {
175 foreach ($orders as $order) {
176
177 $order = wc_get_order($order->ID);
178
179 if (!$order || self::has_reconcile_shipping_triggered($order->get_id()) || !self::has_route_order_id($order->get_id())) {
180 continue;
181 }
182
183 try {
184
185 $trackingPayloads = self::format_shipping_data_api($order->get_id());
186 $tracking_numbers = array();
187 foreach ($trackingPayloads as $trackingPayload){
188 $shipmentResponse = $routeapp_client->get_shipment($trackingPayload['tracking_number'], $order->get_id());
189 $tracking_numbers[] = $trackingPayload['tracking_number'];
190 if (is_wp_error($shipmentResponse) || (isset($shipmentResponse['response']['code']) && $shipmentResponse['response']['code'] == 200)) {
191 continue;
192 }
193
194 self::resend_shipment($trackingPayload);
195 }
196
197 //save on post_meta the tracking ids
198 if (!empty($tracking_numbers)) {
199 $tracking_numbers = implode(Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_PIPE, $tracking_numbers);
200 update_post_meta( $order->get_id(), 'routeapp_shipment_tracking_number', $tracking_numbers );
201 }
202
203 self::set_reconcile_shipping_triggered($order->get_id());
204
205 } catch (Exception $exception) {
206 continue;
207 }
208 }
209 }
210 }
211
212
213 /**
214 * create shipping data sending for API
215 * @since 1.0.0
216 * @param integer $order_id
217 */
218 public static function format_shipping_data_api($order_id)
219 {
220 $order = wc_get_order($order_id);
221
222 if (!$order) return [];
223
224 $tracking_number = get_post_meta($order->get_id(), 'routeapp_shipment_tracking_number', true);
225 $routeapp_shipment_tracking = new Routeapp_Shipment_Tracking();
226 $courier_id = $routeapp_shipment_tracking->get_shipping_provider_name($order_id);
227 $product_ids = array();
228
229 foreach ($order->get_items() as $order_item) {
230 for ($i = 1; $i <= $order_item->get_quantity(); $i++) {
231 if ($order_item->get_product() instanceof WC_Product) {
232 array_push($product_ids, $order_item->get_product()->get_id());
233 }
234 }
235 }
236
237 if (strpos($tracking_number, Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_PIPE) > 0) {
238 $tracking_numbers = explode(Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_PIPE, $tracking_number);
239 } elseif (strpos($tracking_number, Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_COMMA) > 0) {
240 $tracking_numbers = explode(Routeapp_WooCommerce_Tracking_Provider::SEPARATOR_COMMA, $tracking_number);
241 }else{
242 $tracking_numbers = [$tracking_number];
243 }
244
245 $trackings = [];
246
247 foreach ($tracking_numbers as $tracking){
248 if ($tracking!='') {
249 $trackings[] = [
250 'source_order_id' => $order->get_id(),
251 'source_product_ids' => $product_ids,
252 'courier_id' => is_string($courier_id) ? $courier_id : $courier_id[$tracking],
253 'tracking_number' => $tracking,
254 ];
255 }
256 }
257 if(empty($trackings[0])) {
258 //we don't have shipping on our post_meta yet, try to get the shipping data from third party module
259 $trackings = $routeapp_shipment_tracking->get_shipping_info($order_id);
260 }
261
262 return $trackings;
263 }
264
265
266 /**
267 * Periodic Worker Triggering Transaction Validation Jobs on Route
268 *
269 * @return void
270 */
271 function routeapp_validation_worker() {
272 $after = get_option('_routeapp_last_install_date');
273 $date_query = array(
274 'column' => 'post_modified_gmt',
275 'before' => '-5 hour',
276 'after' => $after,
277 'inclusive' => true,
278 );
279
280 $args = array(
281 'post_type' => 'shop_order',
282 'posts_per_page' => -1,
283 'post_status' => array_keys( wc_get_order_statuses() ),
284 'date_query' => $date_query,
285 'meta_query' => array(
286 array(
287 'key' => '_routeapp_order_id',
288 'compare' => 'NOT EXISTS'
289 ),
290 )
291 );
292
293 $query = new WP_Query( $args );
294
295 $this::routeapp_run_order_reconcile($query);
296 }
297
298 /**
299 * Update order post_meta with route order data
300 *
301 * @param $order
302 * @param $routeOrder
303 * @return void
304 */
305 public static function updateOrderPostMeta($order, $routeOrder) {
306 //order was found, save route order id in db
307 $routeOrder = json_decode($routeOrder['body'], true);
308 $routeCharge = $routeOrder['insured_status'] == 'insured_selected' ? $routeOrder['paid_to_insure'] : '';
309 $protected = !empty($routeCharge) ? 1 : 0;
310 if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil')
311 && OrderUtil::custom_orders_table_usage_is_enabled() ) {
312 // HPOS usage is enabled.
313 $order->update_meta_data('_routeapp_order_id', $routeOrder['id'] );
314 $order->update_meta_data('_routeapp_route_charge', $routeCharge );
315 $order->update_meta_data('_routeapp_route_protection', $protected );
316
317 $order->save();
318 } else {
319 // Traditional CPT-based orders are in use.
320 update_post_meta( $order->get_id(), '_routeapp_order_id', $routeOrder['id'] );
321 update_post_meta( $order->get_id(), '_routeapp_route_charge', $routeCharge );
322 update_post_meta( $order->get_id(), '_routeapp_route_protection', $protected );
323 }
324 }
325
326 public static function routeapp_run_order_reconcile($query) {
327 if (count($query->get_posts()) > 0) {
328 foreach ($query->get_posts() as $post) {
329 $order = wc_get_order( $post->ID );
330
331 //check if order exists on Route side
332 $getOrderResponse = Routeapp_API_Client::getInstance()->get_order($post->ID);
333
334 if (is_wp_error($getOrderResponse) || !in_array($getOrderResponse['response']['code'], [200, 404])) {
335 continue;
336 }
337
338 if ($getOrderResponse['response']['code'] == 200 ) {
339 //order was found, save route order id in db
340 self::updateOrderPostMeta($order, $getOrderResponse);
341 } else {
342 //order not found
343 //check if we need to create the order based on the selected statuses
344 $orderStatus = get_post_status($post->ID);
345 $includedOrderStatuses = get_post_meta( $post->ID, '_routeapp_included_order_statuses', true );
346 if ($includedOrderStatuses) {
347 $includedOrderStatuses = json_decode($includedOrderStatuses);
348 }
349
350 $cancelOrderStatuses = get_post_meta( $post->ID, '_routeapp_cancel_order_statuses', true );
351 if ($cancelOrderStatuses) {
352 $cancelOrderStatuses = json_decode($cancelOrderStatuses);
353 }
354 if (!empty($includedOrderStatuses) || !empty($cancelOrderStatuses)) {
355 if (in_array($orderStatus, $cancelOrderStatuses) || !in_array($orderStatus, $includedOrderStatuses)) {
356 //this order needs to be either canceled or not created, so we won't do anything
357 continue;
358 }
359 }
360
361 //create from our side
362 $routeapp_public = self::get_route_public_instance();
363 if (!$routeapp_public->routeapp_is_shipping_method_allowed(false, $order)) continue;
364 $orderData = $routeapp_public->create_order_data_api($post->ID);
365 $response = Routeapp_API_Client::getInstance()->create_order($orderData);
366 if (!is_wp_error( $response )) {
367 $body = json_decode( $response['body'] );
368 try {
369 if ($response['response']['code'] > 201 && $response['response']['code'] < 409) {
370 $note = 'Route API Error while posting data. Errorcode- '.$response['response']['code'];
371 throw new Exception($note);
372 } else if (!empty($body->id) && !empty($body->order_number) || $response['response']['code'] == 409) {
373
374 $routeCharge = $body->insured_status == 'insured_selected' ? $body->paid_to_insure : '';
375 $protected = !empty($routeCharge) ? 1 : 0;
376 if ( class_exists('Automattic\WooCommerce\Utilities\OrderUtil')
377 && OrderUtil::custom_orders_table_usage_is_enabled() ) {
378 // HPOS usage is enabled.
379 $order->update_meta_data('_routeapp_order_id', $body->id );
380 $order->update_meta_data('_routeapp_route_charge', $routeCharge );
381 $order->update_meta_data('_routeapp_route_protection', $protected );
382
383 $order->save();
384 } else {
385 // Traditional CPT-based orders are in use.
386 update_post_meta( $order->get_id(), '_routeapp_order_id', $body->id );
387 update_post_meta( $order->get_id(), '_routeapp_route_charge', $routeCharge );
388 update_post_meta( $order->get_id(), '_routeapp_route_protection', $protected );
389 }
390 }
391 } catch(Exception $exception) {
392 $routeapp_public = self::get_route_public_instance();
393 $extraData = array(
394 'params' => $orderData,
395 'method' => 'POST',
396 'endpoint' => 'orders'
397 );
398 $routeapp_public->routeapp_log($exception, $extraData);
399 }
400 }
401 }
402 }
403 }
404 }
405
406 /**
407 * Periodic Worker that checks Route Webhooks validations
408 *
409 *
410 * @return void
411 */
412 function routeapp_webhooks_validator() {
413 $this->routeapp_webhooks->upsert_webhooks();
414 }
415
416 public function routeapp_missing_shipments_worker() {
417 $after = get_option('_routeapp_last_install_date');
418 $args = array(
419 'column' => 'post_modified_gmt',
420 'before' => '-5 hour',
421 'after' => $after,
422 'inclusive' => true,
423 );
424
425 $this->routeapp_reconcile_shippings($args);
426 }
427
428 public function routeapp_weekly_missing_shipments_worker() {
429 $after = get_option('_routeapp_last_install_date');
430 $args = array(
431 'column' => 'post_modified_gmt',
432 'before' => '-1 week',
433 'after' => $after,
434 'inclusive' => true,
435 );
436
437 $this->routeapp_reconcile_shippings($args);
438 }
439 }
440