PluginProbe
PostNL for WooCommerce / 2.5.1
PostNL for WooCommerce v2.5.1
5.9.12 5.9.11 5.9.10 5.9.9 5.9.8 5.9.7 5.9.6 trunk 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 3.1.4 3.1.5 3.1.6 3.1.7 4.0.0 4.0.1 4.0.2 4.3.2 4.3.3 4.4.0 4.4.1 All 72 releases
woo-postnl / includes / class-wcmp-export.php

class-wcmp-export.php in PostNL for WooCommerce 2.5.1, at includes/class-wcmp-export.php

1,377 lines 61.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 use WPO\WC\PostNL\Compatibility\WC_Core as WCX;
3 use WPO\WC\PostNL\Compatibility\Order as WCX_Order;
4 use WPO\WC\PostNL\Compatibility\Product as WCX_Product;
5
6 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
7
8 if ( !class_exists( 'WooCommerce_PostNL_Export' ) ) :
9
10 class WooCommerce_PostNL_Export {
11 public $order_id;
12 public $success;
13 public $errors;
14
15 /**
16 * Construct.
17 */
18
19 public function __construct() {
20 $this->success = array();
21 $this->errors = array();
22
23 include( 'class-wcmp-rest.php' );
24 include( 'class-wcmp-api.php' );
25
26 add_action( 'admin_notices', array( $this, 'admin_notices' ) );
27 add_action( 'wp_ajax_wc_postnl', array($this, 'export' ));
28 add_action( 'wp_ajax_wc_postnl_frontend', array($this, 'frontend_api_request' ));
29 add_action( 'wp_ajax_nopriv_wc_postnl_frontend', array($this, 'frontend_api_request' ));
30 }
31
32 public function admin_notices () {
33 if ( isset($_GET['postnl_done']) ) { // only do this when for the user that initiated this
34 $action_return = get_option( 'wcpostnl_admin_notices' );
35 $print_queue = get_option( 'wcpostnl_print_queue', array() );
36 if (!empty($action_return)) {
37 foreach ($action_return as $type => $message) {
38 if (in_array($type, array('success','error'))) {
39 if ( $type == 'success' && !empty($print_queue) ) {
40 $print_queue_store = sprintf('<input type="hidden" value="%s" id="wcmp_printqueue">', json_encode(array_keys($print_queue['order_ids'])));
41 $print_queue_offset_store = sprintf('<input type="hidden" value="%s" id="wcmp_printqueue_offset">', $print_queue['offset']);
42 // dequeue
43 delete_option( 'wcpostnl_print_queue' );
44 }
45 printf('<div class="postnl_notice notice notice-%s"><p>%s</p>%s</div>', $type, $message, isset($print_queue_store)?$print_queue_store.$print_queue_offset_store:'');
46 }
47 }
48 // destroy after reading
49 delete_option( 'wcpostnl_admin_notices' );
50 wp_cache_delete( 'wcpostnl_admin_notices','options' );
51 }
52 }
53
54 if (isset($_GET['postnl'])) {
55 switch ($_GET['postnl']) {
56 case 'no_consignments':
57 $message = __('You have to export the orders to PostNL before you can print the labels!', 'woocommerce-postnl');
58 printf('<div class="postnl_notice notice notice-error"><p>%s</p></div>', $message);
59 break;
60 default:
61 break;
62 }
63 }
64 }
65
66 /**
67 * Export selected orders
68 *
69 * @access public
70 * @return void
71 */
72 public function export() {
73 // Check the nonce
74 check_ajax_referer( 'wc_postnl', 'security' );
75
76 if( ! is_user_logged_in() ) {
77 wp_die( __( 'You do not have sufficient permissions to access this page.', 'woocommerce-postnl' ) );
78 }
79
80 $return = array();
81
82 // Check the user privileges (maybe use order ids for filter?)
83 if( apply_filters( 'wc_postnl_check_privs', !current_user_can( 'manage_woocommerce_orders' ) && !current_user_can( 'edit_shop_orders' ) ) ) {
84 $return['error'] = __( 'You do not have sufficient permissions to access this page.', 'woocommerce-postnl' );
85 $json = json_encode( $return );
86 echo $json;
87 die();
88 }
89
90 extract($_REQUEST); // $request, $order_ids, ...
91 // make sure $order_ids is a proper array
92 $order_ids = !empty($order_ids) ? $this->sanitize_posted_array($order_ids) : array();
93
94 switch($request) {
95 case 'add_shipments':
96
97 // filter out non-postnl destinations
98 $order_ids = $this->filter_postnl_destination_orders( $order_ids );
99
100 if ( empty($order_ids) ) {
101 $this->errors[] = __( 'You have not selected any orders!', 'woocommerce-postnl' );
102 break;
103 }
104
105 // if we're going to print directly, we need to process the orders first, regardless of the settings
106 $process = (isset($print) && $print == 'yes') ? true : false;
107 $return = $this->add_shipments( $order_ids );
108 break;
109
110 case 'get_labels':
111
112 $offset = !empty($offset) && is_numeric($offset) ? $offset % 4 : 0;
113
114 if ( empty($order_ids) && empty($shipment_ids)) {
115 $this->errors[] = __( 'You have not selected any orders!', 'woocommerce-postnl' );
116 break;
117 }
118 $label_response_type = isset($label_response_type) ? $label_response_type : NULL;
119
120 if (!empty($shipment_ids)) {
121 $order_ids = !empty($order_ids) ? $this->sanitize_posted_array($order_ids) : array();
122 $shipment_ids = $this->sanitize_posted_array($shipment_ids);
123 $return = $this->get_shipment_labels( $shipment_ids, $order_ids, $label_response_type, $offset );
124 } else {
125 $order_ids = $this->filter_postnl_destination_orders( $order_ids );
126 $return = $this->get_labels( $order_ids, $label_response_type, $offset );
127
128 }
129 break;
130 case 'modal_dialog':
131 if ( empty($order_ids) ) {
132 $errors[] = __( 'You have not selected any orders!', 'woocommerce-postnl' );
133 break;
134 }
135 $order_ids = $this->filter_postnl_destination_orders( $order_ids );
136 $this->modal_dialog( $order_ids, $dialog );
137 break;
138 }
139
140 // display errors directly if PDF requested or modal
141 if ( in_array($request, array('get_labels','modal_dialog')) && !empty($this->errors) ) {
142 echo $this->parse_errors( $this->errors );
143 die();
144 }
145
146 // format errors for html output
147 if (!empty($this->errors)) {
148 $return['error'] = $this->parse_errors( $this->errors );
149 }
150
151 // When adding shipments, store $return for use in admin_notice
152 // This way we can refresh the page (JS) to show all new buttons
153 if ($request == 'add_shipments' && !empty($print) && ($print == 'no'|| $print == 'after_reload')) {
154 update_option( 'wcpostnl_admin_notices', $return );
155 if ($print == 'after_reload') {
156 $print_queue = array(
157 'order_ids' => $return['success_ids'],
158 'offset' => isset($offset) && is_numeric($offset) ? $offset % 4 : 0,
159 );
160 update_option( 'wcpostnl_print_queue', $print_queue );
161 }
162 }
163
164 // if we're directed here from modal, show proper result page
165 if (isset($modal)) {
166 $this->modal_success_page( $request, $return );
167 } else {
168 // return JSON response
169 echo json_encode( $return );
170 }
171
172 die();
173 }
174
175 public function sanitize_posted_array($array) {
176 // check for JSON
177 if (is_string($array) && strpos($array, '[') !== false ) {
178 $array = json_decode(stripslashes($array));
179 }
180
181 // cast as array for single exports
182 $array = (array) $array;
183
184 return $array;
185 }
186
187 public function add_shipments( $order_ids, $process = false ) {
188 $return = array();
189
190 $this->log("*** Creating shipments started ***");
191
192 foreach ($order_ids as $order_id) {
193 $created_shipments = array();
194 $order = WCX::get_order( $order_id );
195 $shipments = $this->get_order_shipment_data( (array) $order_id );
196 $shipments = $this->validate_shipments( $shipments );
197 if (empty($shipments)) {
198 $this->log("Export for order {$order_id} skipped (missing or invalidated shipment data)");
199 continue;
200 }
201
202 $this->log("Shipment data for order {$order_id}:\n".var_export($shipments, true));
203
204 // check colli amount
205 $extra_params = WCX_Order::get_meta( $order, '_postnl_shipment_options_extra' );
206 $colli_amount = isset($extra_params['colli_amount']) ? $extra_params['colli_amount'] : 1;
207
208 for ($i=0; $i < intval($colli_amount); $i++) {
209 try {
210 $api = $this->init_api();
211 $response = $api->add_shipments( $shipments );
212 $this->log("API response (order {$order_id}):\n".var_export($response, true));
213 // echo '<pre>';var_dump($response);echo '</pre>';die();
214 if (isset($response['body']['data']['ids'])) {
215 $ids = array_shift($response['body']['data']['ids']);
216 $shipment_id = $ids['id'];
217
218 $this->success[$order_id] = $shipment_id;
219
220 $created_shipments[] = $shipment_id;
221 $shipment = array (
222 'shipment_id' => $shipment_id,
223 );
224
225 // save shipment data in order meta
226 $this->save_shipment_data( $order, $shipment );
227
228 // process directly setting
229 if ( isset(WooCommerce_PostNL()->general_settings['process_directly']) || $process === true ) {
230 // flush cache until WC issue #13439 is fixed https://github.com/woocommerce/woocommerce/issues/13439
231 if (method_exists($order, 'save')) {
232 $order->save();
233 }
234 $this->get_labels( (array) $order_id, 'url' );
235 $this->get_shipment_data( $shipment_id, $order );
236 }
237
238 // status automation
239 if ( isset(WooCommerce_PostNL()->general_settings['order_status_automation']) && !empty(WooCommerce_PostNL()->general_settings['automatic_order_status']) ) {
240 $order->update_status( WooCommerce_PostNL()->general_settings['automatic_order_status'], __( 'PostNL shipment created:', 'woocommerce-postnl' ) );
241 }
242 } else {
243 $this->errors[$order_id] = __( 'Unknown error', 'woocommerce-postnl' );
244 }
245 } catch (Exception $e) {
246 $this->errors[$order_id] = $e->getMessage();
247 }
248 }
249
250 // store shipment ids from this export
251 if (!empty($created_shipments)) {
252 WCX_Order::update_meta_data( $order, '_postnl_last_shipment_ids', $created_shipments );
253 }
254 }
255 // echo '<pre>';var_dump($this->success);echo '</pre>';die();
256 if (!empty($this->success)) {
257 $return['success'] = sprintf(__( '%s shipments successfully exported to PostNL', 'woocommerce-postnl' ), count($this->success));
258 $return['success_ids'] = $this->success;
259 }
260
261 return $return;
262 }
263
264 public function get_shipment_labels( $shipment_ids, $order_ids = array(), $label_response_type = NULL, $offset = 0 ) {
265 $return = array();
266 $this->log("*** Label request started ***");
267 $this->log("Shipment ID's: ".implode(', ', $shipment_ids));
268 try {
269 $api = $this->init_api();
270 $params = array();
271
272 if(WooCommerce_PostNL()->general_settings['label_format'] == 'A4'){
273
274 if (!empty($offset) && is_numeric ($offset)) {
275 $portrait_positions = array( 2, 4, 1, 3 ); // positions are defined on landscape, but paper is filled portrait-wise
276 $params['positions'] = implode( ';', array_slice($portrait_positions,$offset) );
277 }
278 $params['format'] = 'A4';
279 }else{
280
281 $params['format'] = 'A6';
282 }
283
284 if (isset($label_response_type) && $label_response_type == 'url') {
285 $response = $api->get_shipment_labels( $shipment_ids, $params, 'link' );
286 $this->log("API response:\n".var_export($response, true));
287 // var_dump( $response );
288 if (isset($response['body']['data']['pdfs']['url'])) {
289 $url = untrailingslashit( $api->APIURL ) . $response['body']['data']['pdfs']['url'];
290 $return['url'] = $url;
291 } else {
292 $this->errors[] = __( 'Unknown error', 'woocommerce-postnl' );
293 }
294 } else {
295 $response = $api->get_shipment_labels( $shipment_ids, $params, 'pdf' );
296
297 if (isset($response['body'])) {
298 $this->log("PDF data received");
299 $pdf_data = $response['body'];
300 $output_mode = isset(WooCommerce_PostNL()->general_settings['download_display'])?WooCommerce_PostNL()->general_settings['download_display']:'';
301 if ( $output_mode == 'display' ) {
302 $this->stream_pdf( $pdf_data, $order_ids );
303 } else {
304 $this->download_pdf( $pdf_data, $order_ids );
305 }
306 } else {
307 $this->log("Unknown error, API response:\n".var_export($response, true));
308 $this->errors[] = __( 'Unknown error', 'woocommerce-postnl' );
309 }
310
311 // echo '<pre>';var_dump($response);echo '</pre>';die();
312 }
313 } catch (Exception $e) {
314 $this->errors[] = $e->getMessage();
315 }
316
317 return $return;
318 }
319
320 public function get_labels( $order_ids, $label_response_type = NULL, $offset = 0 ) {
321 $shipment_ids = $this->get_shipment_ids( $order_ids, array( 'only_last' => true ) );
322
323 if ( empty($shipment_ids) ) {
324 $this->log("*** Failed label request (not exported yet) ***");
325 $this->errors[] = __( 'The selected orders have not been exported to PostNL yet!', 'woocommerce-postnl' );
326 return array();
327 }
328
329 return $this->get_shipment_labels( $shipment_ids, $order_ids, $label_response_type, $offset );
330 }
331
332 public function modal_dialog( $order_ids, $dialog ) {
333 // check for JSON
334 if (is_string($order_ids) && strpos($order_ids, '[') !== false ) {
335 $order_ids = json_decode(stripslashes($order_ids));
336 }
337
338 // cast as array for single exports
339 $order_ids = (array) $order_ids;
340
341 include('views/wcmp-bulk-options-form.php');
342 die();
343 }
344
345 public function modal_success_page( $request, $result ) {
346 include('views/wcmp-modal-result-page.php');
347 die();
348 }
349
350 public function frontend_api_request() {
351 // TODO: check nonce
352 $params = $_REQUEST;
353
354 // filter non API params
355 $api_params = array(
356 'cc' => '',
357 'postal_code' => '',
358 'number' => '',
359 'carrier' => '',
360 'delivery_time' => '',
361 'delivery_date' => '',
362 'cutoff_time' => '',
363 'dropoff_days' => '',
364 'dropoff_delay' => '',
365 'deliverydays_window' => '',
366 'exclude_delivery_type' => '',
367 );
368 $params = array_intersect_key($params, $api_params);
369
370 $api = $this->init_api();
371
372 try {
373 $response = $api->get_delivery_options( $params, true );
374
375 @header('Content-type: application/json; charset=utf-8');
376
377 echo $response['body'];
378 } catch (Exception $e) {
379 @header("HTTP/1.1 503 service unavailable");
380 }
381 die();
382 }
383
384 public function init_api () {
385 // $user = WooCommerce_PostNL()->general_settings['api_username'];
386 if ( !isset(WooCommerce_PostNL()->general_settings['api_key']) ) {
387 return false;
388 }
389
390 $key = WooCommerce_PostNL()->general_settings['api_key'];
391 $api = new WC_PostNL_API( $key );
392
393 return $api;
394 }
395
396 public function get_order_shipment_data( $order_ids, $type = 'standard' ) {
397 foreach( $order_ids as $order_id ) {
398 // get order
399 $order = WCX::get_order( $order_id );
400
401 $shipment = array(
402 'recipient' => $this->get_recipient( $order ),
403 'options' => $this->get_options( $order ),
404 'carrier' => 1, // default to POSTNL for now
405 );
406
407 if ( $pickup = $this->is_pickup( $order ) ) {
408 // $pickup_time = array_shift($pickup['time']); // take first element in time array
409 $shipment['pickup'] = array(
410 'postal_code' => $pickup['postal_code'],
411 'street' => $pickup['street'],
412 'city' => $pickup['city'],
413 'number' => $pickup['number'],
414 'location_name' => $pickup['location'],
415 );
416 }
417
418 $shipping_country = WCX_Order::get_prop( $order, 'shipping_country' );
419 if ( $this->is_world_shipment_country( $shipping_country ) ) {
420 $customs_declaration = $this->get_customs_declaration( $order );
421 $shipment['customs_declaration'] = $customs_declaration;
422 $shipment['physical_properties'] = array(
423 'weight' => $customs_declaration['weight'],
424 );
425 }
426
427 /* disabled for now
428 $concept_shipments = $this->get_shipment_ids( (array) $order_id, array( 'only_concepts' => true, 'only_last' => true ) );
429 if ( !empty($concept_shipments) ) {
430 $shipment['id'] = array_pop($concept_shipments);
431 }
432 */
433
434 $shipments[] = $shipment;
435 }
436
437 return $shipments;
438 }
439
440 public function get_recipient( $order ) {
441 $shipping_name = method_exists($order, 'get_formatted_shipping_full_name') ? $order->get_formatted_shipping_full_name() : trim( $order->shipping_first_name . ' ' . $order->shipping_last_name );
442 $address = array(
443 'cc' => (string) WCX_Order::get_prop( $order, 'shipping_country' ),
444 'city' => (string) WCX_Order::get_prop( $order, 'shipping_city' ),
445 'person' => $shipping_name,
446 'company' => (string) WCX_Order::get_prop( $order, 'shipping_company' ),
447 'email' => (string) WCX_Order::get_prop( $order, 'billing_email' ),
448 'phone' => isset(WooCommerce_PostNL()->export_defaults['connect_phone']) ? WCX_Order::get_prop( $order, 'billing_phone' ) : '',
449 );
450
451
452 $shipping_country = WCX_Order::get_prop( $order, 'shipping_country' );
453 if ( $shipping_country == 'NL' ) {
454 // use billing address if old 'pakjegemak' (1.5.6 and older)
455 if ( $pgaddress = WCX_Order::get_meta( $order, '_postnl_pgaddress' ) ) {
456 $billing_name = method_exists($order, 'get_formatted_billing_full_name') ? $order->get_formatted_billing_full_name() : trim( $order->billing_first_name . ' ' . $order->billing_last_name );
457 $address_intl = array(
458 'city' => (string) WCX_Order::get_prop( $order, 'billing_city' ),
459 'person' => $billing_name,
460 'company' => (string) WCX_Order::get_prop( $order, 'billing_company' ),
461 'street' => (string) WCX_Order::get_meta( $order, '_billing_street_name' ),
462 'number' => (string) WCX_Order::get_meta( $order, '_billing_house_number' ),
463 'number_suffix' => (string) WCX_Order::get_meta( $order, '_billing_house_number_suffix' ),
464 'postal_code' => (string) WCX_Order::get_prop( $order, 'billing_postcode' ),
465 );
466 } else {
467 $address_intl = array(
468 'street' => (string) WCX_Order::get_meta( $order, '_shipping_street_name' ),
469 'number' => (string) WCX_Order::get_meta( $order, '_shipping_house_number' ),
470 'number_suffix' => (string) WCX_Order::get_meta( $order, '_shipping_house_number_suffix' ),
471 'postal_code' => (string) WCX_Order::get_prop( $order, 'shipping_postcode' ),
472 );
473 }
474 } else {
475 $address_intl = array(
476 'postal_code' => (string) WCX_Order::get_prop( $order, 'shipping_postcode' ),
477 'street' => (string) WCX_Order::get_prop( $order, 'shipping_address_1' ),
478 'street_additional_info' => (string) WCX_Order::get_prop( $order, 'shipping_address_2' ),
479 'region' => (string) WCX_Order::get_prop( $order, 'shipping_state' ),
480 );
481 }
482
483 $address = array_merge( $address, $address_intl);
484
485 return apply_filters( 'wc_postnl_recipient', $address, $order );
486 }
487
488 public function get_options( $order ) {
489 // parse description
490 if (isset(WooCommerce_PostNL()->export_defaults['label_description'])) {
491 $description = $this->replace_shortcodes( WooCommerce_PostNL()->export_defaults['label_description'], $order );
492 } else {
493 $description = '';
494 }
495
496 // use shipment options from order when available
497 $shipment_options = WCX_Order::get_meta( $order, '_postnl_shipment_options' );
498 if (!empty($shipment_options)) {
499 $emty_defaults = array(
500 'package_type' => 1,
501 'only_recipient' => 0,
502 'signature' => 0,
503 'return' => 0,
504 'label_description' => '',
505 'insured_amount' => 0,
506 );
507 $options = array_merge($emty_defaults, $shipment_options);
508 } else {
509 if (isset(WooCommerce_PostNL()->export_defaults['insured']) && WooCommerce_PostNL()->export_defaults['insured_amount'] == '' && isset(WooCommerce_PostNL()->export_defaults['insured_amount_custom'])) {
510 $insured_amount = WooCommerce_PostNL()->export_defaults['insured_amount_custom'];
511 } elseif (isset(WooCommerce_PostNL()->export_defaults['insured']) && isset(WooCommerce_PostNL()->export_defaults['insured_amount'])) {
512 $insured_amount = WooCommerce_PostNL()->export_defaults['insured_amount'];
513 } else {
514 $insured_amount = 0;
515 }
516
517 $options = array(
518 'package_type' => $this->get_package_type_for_order( $order ),
519 'only_recipient' => (isset(WooCommerce_PostNL()->export_defaults['only_recipient'])) ? 1 : 0,
520 'signature' => (isset(WooCommerce_PostNL()->export_defaults['signature'])) ? 1 : 0,
521 'return' => (isset(WooCommerce_PostNL()->export_defaults['return'])) ? 1 : 0,
522 'label_description' => $description,
523 'insured_amount' => $insured_amount,
524 );
525 }
526
527 // convert insurance option
528 if ( !isset($options['insurance']) && isset($options['insured_amount']) ) {
529 if ($options['insured_amount'] > 0) {
530 $options['insurance'] = array(
531 'amount' => (int) $options['insured_amount'] * 100,
532 'currency' => 'EUR',
533 );
534 }
535
536 unset($options['insured_amount']);
537 unset($options['insured']);
538 }
539
540 // set insurance amount to int if already set
541 if (isset($options['insurance'])) {
542 $options['insurance']['amount'] = (int) $options['insurance']['amount'];
543 }
544
545 // remove frontend insurance option values
546 if (isset($options['insured_amount'])) {
547 unset($options['insured_amount']);
548 }
549 if (isset($options['insured'])) {
550 unset($options['insured']);
551 }
552
553 // load delivery options
554 $postnl_delivery_options = WCX_Order::get_meta( $order, '_postnl_delivery_options' );
555
556 // set delivery type
557 $options['delivery_type'] = $this->get_delivery_type( $order, $postnl_delivery_options );
558
559 // Options for Pickup and Pickup express delivery types:
560 // always enable signature on receipt
561 if ( $this->is_pickup( $order, $postnl_delivery_options ) ) {
562 $options['signature'] = 1;
563 }
564
565 // delivery date (postponed delivery & pickup)
566 if ($delivery_date = $this->get_delivery_date( $order, $postnl_delivery_options ) ) {
567 $date_time = explode(' ', $delivery_date); // split date and time
568 // only add if date is in the future
569 $timestamp = strtotime($date_time[0]);
570
571 if ( $timestamp < time() ) {
572 $new_timestamp= $this->get_next_delivery_day($timestamp);
573 $delivery_date = date( 'Y-m-d h:i:s', $new_timestamp );
574 }
575
576 $options['delivery_date'] = $delivery_date;
577 }
578
579 // options signed & recipient only
580 $postnl_signed = WCX_Order::get_meta( $order, '_postnl_signed' );
581 if (!empty($postnl__signed)) {
582 $options['signature'] = 1;
583 }
584 $postnl_only_recipient = WCX_Order::get_meta( $order, '_postnl_only_recipient' );
585 if (!empty($postnl_only_recipient)) {
586 $options['only_recipient'] = 1;
587 }
588
589 // allow prefiltering consignment data
590 $options = apply_filters( 'wc_postnl_order_shipment_options', $options, $order );
591
592 // PREVENT ILLEGAL SETTINGS
593 // convert numeric strings to int
594 $int_options = array( 'package_type', 'delivery_type', 'only_recipient', 'signature', 'return' );
595 foreach ($options as $key => &$value) {
596 if ( in_array($key, $int_options) ) {
597 $value = (int) $value;
598 }
599 }
600
601 // disable options for mailbox package and unpaid letter
602 // echo '<pre>';var_dump($package_type);echo '</pre>';die();
603 if ( $options['package_type'] != 1 ) {
604 $illegal_options = array( 'delivery_type', 'only_recipient', 'signature', 'return', 'insurance', 'delivery_date' );
605 foreach ($options as $key => $option) {
606 if (in_array($key, $illegal_options)) {
607 unset($options[$key]);
608 }
609 }
610 }
611
612 return $options;
613
614 }
615
616 /**
617 * @param int $timestamp
618 *
619 * @return false|string
620 */
621 private function get_next_delivery_day($timestamp) {
622 $weekDay = date('w', $timestamp);
623 $new_timestamp = strtotime( '+1 day', $timestamp );
624
625 if ($weekDay == 0 || $weekDay == 1 || $new_timestamp < time() ) {
626 $new_timestamp = $this->get_next_delivery_day( $new_timestamp );
627 }
628
629 return $new_timestamp;
630 }
631
632 public function get_customs_declaration( $order ) {
633 $weight = (int) round( $this->get_parcel_weight( $order ) * 1000 );
634 $invoice = $this->get_invoice_number( $order );
635 $contents = (int) ( (isset(WooCommerce_PostNL()->export_defaults['package_contents'])) ? WooCommerce_PostNL()->export_defaults['package_contents'] : 1 );
636
637 // Item defaults:
638 // Classification
639 $default_hs_code = (isset(WooCommerce_PostNL()->export_defaults['hs_code'])) ? WooCommerce_PostNL()->export_defaults['hs_code'] : '';
640 // Country (=shop base)
641 $country = WC()->countries->get_base_country();
642
643 $items = array();
644 foreach ( $order->get_items() as $item_id => $item ) {
645 $product = $order->get_product_from_item( $item );
646 if ( !empty( $product )) {
647 // Description
648 $description = $item['name'];
649 // Amount
650 $amount = (int) ( isset($item['qty']) ? $item['qty'] : 1 );
651 // Weight (total item weight in grams)
652 $weight = (int) round( $this->get_item_weight_kg ( $item, $order ) * 1000 );
653 // Item value (in cents)
654 $item_value = array(
655 'amount' => (int) round( ( $item['line_total'] + $item['line_tax'] ) * 100 ),
656 'currency' => WCX_Order::get_prop( $order, 'currency' ),
657 );
658 // Classification / HS Code
659 $classification = WCX_Product::get_meta( $product, '_postnl_hs_code', true );
660 if (empty($classification)) {
661 $classification = $default_hs_code;
662 }
663
664 // add item to item list
665 $items[] = compact( 'description', 'amount', 'weight', 'item_value', 'classification', 'country' );
666 }
667 }
668
669 return compact( 'weight', 'invoice', 'contents', 'items' );
670 }
671
672 public function validate_shipments( $shipments, $output_errors = true ) {
673 $missing_hs_codes = 0;
674 foreach ($shipments as $key => $shipment) {
675 // check customs declaration for HS codes
676 if (isset($shipment['customs_declaration']) && !empty($shipment['customs_declaration']['items'])) {
677 foreach ($shipment['customs_declaration']['items'] as $key => $item) {
678 if (empty($item['classification'])) {
679 unset($shipments[$key]);
680 $missing_hs_codes++;
681 break;
682 }
683 }
684 }
685 if ($output_errors === true && $missing_hs_codes > 0) {
686 $this->errors[] = sprintf( __( '%d shipments missing HS codes - not exported.', 'woocommerce-postnl' ), $missing_hs_codes);
687 }
688 }
689
690 return $shipments;
691 }
692
693 public function get_shipment_ids( $order_ids, $args ) {
694 $shipment_ids = array();
695 foreach ($order_ids as $order_id) {
696 $order = WCX::get_order( $order_id );
697 $order_shipments = WCX_Order::get_meta( $order, '_postnl_shipments' );
698 if (!empty($order_shipments)) {
699 $order_shipment_ids = array();
700 // exclude concepts or only concepts
701 foreach ( $order_shipments as $key => $shipment) {
702 if (isset($args['exclude_concepts']) && empty($shipment['tracktrace'])) {
703 continue;
704 }
705 if (isset($args['only_concepts']) && !empty($shipment['tracktrace'])) {
706 continue;
707 }
708
709 $order_shipment_ids[] = $shipment['shipment_id'];
710 }
711
712 if (isset($args['only_last'])) {
713 $last_shipment_ids = WCX_Order::get_meta( $order, '_postnl_last_shipment_ids' );
714 if ( !empty( $last_shipment_ids ) && is_array( $last_shipment_ids ) ) {
715 foreach ($order_shipment_ids as $order_shipment_id) {
716 if ( in_array($order_shipment_id, $last_shipment_ids ) ) {
717 $shipment_ids[] = $order_shipment_id;
718 }
719 }
720 } else {
721 $shipment_ids[] = array_pop( $order_shipment_ids );
722 }
723 } else {
724 $shipment_ids[] = array_merge( $shipment_ids, $order_shipment_ids );
725 }
726 }
727 }
728
729 return $shipment_ids;
730 }
731
732 public function save_shipment_data ( $order, $shipment ) {
733 if ( empty($shipment) ) {
734 return false;
735 }
736
737 $new_shipments = array();
738 $new_shipments[$shipment['shipment_id']] = $shipment;
739 // don't store full shipment data
740 // if (isset($shipment['shipment'])) {
741 // unset($shipment['shipment']);
742 // }
743
744 if ( isset(WooCommerce_PostNL()->general_settings['keep_shipments']) ) {
745 if ( $old_shipments = WCX_Order::get_meta( $order, '_postnl_shipments' ) ) {
746 $shipments = $old_shipments;
747 foreach ($new_shipments as $shipment_id => $shipment) {
748 $shipments[$shipment_id] = $shipment;
749 }
750 }
751 }
752
753 $shipments = isset($shipments) ? $shipments : $new_shipments;
754
755 WCX_Order::update_meta_data( $order, '_postnl_shipments', $shipments );
756
757 return;
758 }
759
760 public function get_package_type_from_shipping_method( $shipping_method, $shipping_class, $shipping_country ) {
761 $package_type = 1;
762 if (isset(WooCommerce_PostNL()->export_defaults['shipping_methods_package_types'])) {
763 if ( strpos($shipping_method, "table_rate:") === 0 && class_exists('WC_Table_Rate_Shipping') ) {
764 // Automattic / WooCommerce table rate
765 // use full method = method_id:instance_id:rate_id
766 $shipping_method_id = $shipping_method;
767 } else { // non table rates
768
769 if ( strpos($shipping_method, ':') !== false ) {
770 // means we have method_id:instance_id
771 $shipping_method = explode(':', $shipping_method);
772 $shipping_method_id = $shipping_method[0];
773 $shipping_method_instance = $shipping_method[1];
774 } else {
775 $shipping_method_id = $shipping_method;
776 }
777
778 // add class if we have one
779 if (!empty($shipping_class)) {
780 $shipping_method_id_class = "{$shipping_method_id}:{$shipping_class}";
781 }
782 }
783
784 foreach (WooCommerce_PostNL()->export_defaults['shipping_methods_package_types'] as $package_type_key => $package_type_shipping_methods ) {
785 // check if we have a match with the predefined methods
786 // fallback to bare method (without class) (if bare method also defined in settings)
787 if (in_array($shipping_method_id, $package_type_shipping_methods) || (!empty($shipping_method_id_class) && in_array($shipping_method_id_class, $package_type_shipping_methods))) {
788 $package_type = $package_type_key;
789 break;
790 }
791 }
792 }
793
794 // disable mailbox package outside NL
795 if ($shipping_country != 'NL' && $package_type == 2 ) {
796 $package_type = 1;
797 }
798
799 return $package_type;
800 }
801
802 // determine appropriate package type for this order
803 public function get_package_type_for_order( $order ) {
804 $shipping_country = WCX_Order::get_prop( $order, 'shipping_country' );
805
806 // get shipping methods from order
807 $order_shipping_methods = $order->get_items('shipping');
808
809 if ( !empty( $order_shipping_methods ) ) {
810 // we're taking the first (we're not handling multiple shipping methods as of yet)
811 $order_shipping_method = array_shift($order_shipping_methods);
812 $order_shipping_method = $order_shipping_method['method_id'];
813
814 $order_shipping_class = WCX_Order::get_meta( $order, '_postnl_highest_shipping_class' );
815 if (empty($order_shipping_class)) {
816 $order_shipping_class = $this->get_order_shipping_class( $order, $order_shipping_method );
817 }
818
819 $package_type = $this->get_package_type_from_shipping_method( $order_shipping_method, $order_shipping_class, $shipping_country );
820 }
821
822 // fallbacks if no match from previous
823 if (!isset($package_type)) {
824 if ((isset(WooCommerce_PostNL()->export_defaults['package_type']))) {
825 $package_type = WooCommerce_PostNL()->export_defaults['package_type'];
826 } else {
827 $package_type = 1; // 1. package | 2. mailbox package | 3. letter
828 }
829 }
830
831 // always parcel for Pickup and Pickup express delivery types.
832 if ( $this->is_pickup( $order ) ) {
833 $package_type = 1;
834 }
835
836 return $package_type;
837 }
838
839 public function get_package_types( $shipment_type = 'shipment' ) {
840 $package_types = array(
841 1 => __( 'Parcel' , 'woocommerce-postnl' ),
842 2 => __( 'Mailbox package' , 'woocommerce-postnl' ),
843 3 => __( 'Unpaid letter' , 'woocommerce-postnl' ),
844 );
845 if ( $shipment_type == 'return' ) {
846 unset($package_types[2]);
847 unset($package_types[3]);
848 }
849
850 return $package_types;
851 }
852
853 public function get_package_name( $package_type ) {
854 $package_types = $this->get_package_types();
855 $package_name = isset($package_types[$package_type]) ? $package_types[$package_type] : __( 'Unknown' , 'woocommerce-postnl' );
856 return $package_name;
857 }
858
859 public function parse_errors( $errors ) {
860 $parsed_errors = array();
861 foreach ($errors as $key => $error) {
862 // check if we have an order_id
863 if ($key > 10) {
864 $parsed_errors[] = sprintf("<strong>%s %s:</strong> %s", __( 'Order', 'woocommerce-postnl' ), $key, $error );
865 } else {
866 $parsed_errors[] = $error;
867 }
868 }
869
870 if (count($parsed_errors) == 1) {
871 $html = array_shift($parsed_errors);
872 } else {
873 foreach ($parsed_errors as &$parsed_error) {
874 $parsed_error = "<li>{$parsed_error}</li>";
875 }
876 $html = sprintf("<ul>%s</ul>", implode("\n",$parsed_errors));
877 }
878
879 return $html;
880 }
881
882 public function stream_pdf ( $pdf_data, $order_ids ) {
883 header('Content-type: application/pdf');
884 header('Content-Disposition: inline; filename="'.$this->get_filename( $order_ids ).'"');
885 echo $pdf_data;
886 die();
887 }
888 public function download_pdf ( $pdf_data, $order_ids ) {
889 header('Content-Description: File Transfer');
890 header('Content-Type: application/octet-stream');
891 header('Content-Disposition: attachment; filename="'.$this->get_filename( $order_ids ).'"');
892 header('Content-Transfer-Encoding: binary');
893 header('Connection: Keep-Alive');
894 header('Expires: 0');
895 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
896 header('Pragma: public');
897 echo $pdf_data;
898 die();
899 }
900
901 public function get_filename ( $order_ids ) {
902 $filename = 'PostNL';
903 $filename .= '-' . date('Y-m-d') . '.pdf';
904
905 return apply_filters( 'wcpostnl_filename', $filename, $order_ids );
906 }
907
908 public function get_shipment_status_name( $status_code ) {
909 $shipment_statuses = array(
910 1 => __('pending - concept', 'woocommerce-postnl'),
911 2 => __('pending - registered', 'woocommerce-postnl'),
912 3 => __('enroute - handed to carrier', 'woocommerce-postnl'),
913 4 => __('enroute - sorting', 'woocommerce-postnl'),
914 5 => __('enroute - distribution', 'woocommerce-postnl'),
915 6 => __('enroute - customs', 'woocommerce-postnl'),
916 7 => __('delivered - at recipient', 'woocommerce-postnl'),
917 8 => __('delivered - ready for pickup', 'woocommerce-postnl'),
918 9 => __('delivered - package picked up', 'woocommerce-postnl'),
919 30 => __('inactive - concept', 'woocommerce-postnl'),
920 31 => __('inactive - registered', 'woocommerce-postnl'),
921 32 => __('inactive - enroute - handed to carrier', 'woocommerce-postnl'),
922 33 => __('inactive - enroute - sorting', 'woocommerce-postnl'),
923 34 => __('inactive - enroute - distribution', 'woocommerce-postnl'),
924 35 => __('inactive - enroute - customs', 'woocommerce-postnl'),
925 36 => __('inactive - delivered - at recipient', 'woocommerce-postnl'),
926 37 => __('inactive - delivered - ready for pickup', 'woocommerce-postnl'),
927 38 => __('inactive - delivered - package picked up', 'woocommerce-postnl'),
928 99 => __('inactive - unknown', 'woocommerce-postnl'),
929 );
930
931 if (isset($shipment_statuses[$status_code])) {
932 return $shipment_statuses[$status_code];
933 } else {
934 return __('Unknown status', 'woocommerce-postnl');
935 }
936 }
937
938 public function get_shipment_data( $id, $order ) {
939 try {
940 $api = $this->init_api();
941 $response = $api->get_shipments( $id );
942
943 if (!empty($response['body']['data']['shipments'])) {
944 $shipments = $response['body']['data']['shipments'];
945 $shipment = array_shift($shipments);
946
947 // if shipment id matches and status is not concept, get tracktrace barcode and status name
948 if ( isset($shipment['id']) && $shipment['id'] == $id && $shipment['status'] >= 2 ) {
949 $status = $this->get_shipment_status_name( $shipment['status']);
950 $tracktrace = $shipment['barcode'];
951 $shipment_id = $id;
952 $shipment_data = compact( 'shipment_id', 'status', 'tracktrace', 'shipment');
953 $this->save_shipment_data( $order, $shipment_data );
954 return $shipment_data;
955 } else {
956 return false;
957 }
958
959 } else {
960 // No shipments found with this ID
961 return false;
962 }
963
964
965 } catch (Exception $e) {
966 // echo $e->getMessage();
967 return false;
968 }
969 }
970
971 public function replace_shortcodes( $description, $order ) {
972 $postnl_delivery_options = WCX_Order::get_meta( $order, '_postnl_delivery_options' );
973 $replacements = array(
974 '[ORDER_NR]' => $order->get_order_number(),
975 '[DELIVERY_DATE]' => isset($postnl_delivery_options) && isset($postnl_delivery_options['date']) ? $postnl_delivery_options['date'] : '',
976 );
977
978 $description = str_replace(array_keys($replacements), array_values($replacements), $description);
979
980 return $description;
981 }
982
983 public function get_item_display_name ( $item, $order ) {
984 // set base name
985 $name = $item['name'];
986
987 // add variation name if available
988 $product = $order->get_product_from_item( $item );
989 if( $product && isset( $item['variation_id'] ) && $item['variation_id'] > 0 && method_exists($product, 'get_variation_attributes')) {
990 $name .= woocommerce_get_formatted_variation( $product->get_variation_attributes() );
991 }
992
993 return $name;
994 }
995
996 public function get_parcel_weight ( $order ) {
997 $parcel_weight = (isset(WooCommerce_PostNL()->general_settings['empty_parcel_weight'])) ? preg_replace("/\D/","",WooCommerce_PostNL()->general_settings['empty_parcel_weight'])/1000 : 0;
998
999 $items = $order->get_items();
1000 foreach ( $items as $item_id => $item ) {
1001 $parcel_weight += $this->get_item_weight_kg( $item, $order );
1002 }
1003
1004 return $parcel_weight;
1005 }
1006
1007 public function get_item_weight_kg ( $item, $order ) {
1008 $product = $order->get_product_from_item( $item );
1009
1010 if (empty($product)) {
1011 return 0;
1012 }
1013
1014 $weight = $product->get_weight();
1015 $weight_unit = get_option( 'woocommerce_weight_unit' );
1016 switch ($weight_unit) {
1017 case 'kg':
1018 $product_weight = $weight;
1019 break;
1020 case 'g':
1021 $product_weight = $weight / 1000;
1022 break;
1023 case 'lbs':
1024 $product_weight = $weight * 0.45359237;
1025 break;
1026 case 'oz':
1027 $product_weight = $weight * 0.0283495231;
1028 break;
1029 default:
1030 $product_weight = $weight;
1031 break;
1032 }
1033
1034 $item_weight = (float) $product_weight * (int) $item['qty'];
1035
1036 return $item_weight;
1037 }
1038
1039 public function is_pickup( $order, $postnl_delivery_options = '' ) {
1040 if (empty($postnl_delivery_options)) {
1041 $postnl_delivery_options = WCX_Order::get_meta( $order, '_postnl_delivery_options' );
1042 }
1043
1044 $pickup_types = array( 'retail', 'retailexpress' );
1045 if ( !empty($postnl_delivery_options['price_comment']) && in_array($postnl_delivery_options['price_comment'], $pickup_types) ) {
1046 return $postnl_delivery_options;
1047 }
1048
1049 // Backwards compatibility for pakjegemak data
1050 $pgaddress = WCX_Order::get_meta( $order, '_postnl_pgaddress' );
1051 if ( !empty( $pgaddress ) && !empty( $pgaddress['postcode'] ) ) {
1052 $pickup = array(
1053 'postal_code' => $pgaddress['postcode'],
1054 'street' => $pgaddress['street'],
1055 'city' => $pgaddress['town'],
1056 'number' => $pgaddress['house_number'],
1057 'location' => $pgaddress['name'],
1058 'price_comment' => 'retail',
1059 );
1060
1061 return $pickup;
1062 }
1063
1064 // no pickup
1065 return false;
1066 }
1067
1068 public function get_delivery_type( $order, $postnl_delivery_options = '' ) {
1069 // delivery types
1070 $delivery_types = array(
1071 'morning' => 1,
1072 'standard' => 2, // 'default in JS API'
1073 'night' => 3,
1074 'retail' => 4, // 'pickup'
1075 'retailexpress' => 5, // 'pickup_express'
1076 );
1077
1078 if (empty($postnl_delivery_options)) {
1079 $postnl_delivery_options = WCX_Order::get_meta( $order, '_postnl_delivery_options' );
1080 }
1081
1082 // standard = default, overwrite if otpions found
1083 $delivery_type = 'standard';
1084 if (!empty($postnl_delivery_options)) {
1085 // pickup & pickupexpress store the delivery type in the delivery options,
1086 // morning & night store it in the time data (...)
1087 if ( empty($postnl_delivery_options['price_comment']) && !empty($postnl_delivery_options['time']) ) {
1088 // check if we have a price_comment in the time option
1089 $delivery_time = array_shift($postnl_delivery_options['time']); // take first element in time array
1090 if (isset($delivery_time['price_comment'])) {
1091 $delivery_type = $delivery_time['price_comment'];
1092 }
1093 } else {
1094 $delivery_type = $postnl_delivery_options['price_comment'];
1095 }
1096 }
1097
1098 // backwards compatibility for pakjegemak
1099 if ( $pgaddress = WCX_Order::get_meta( $order, '_postnl_pgaddress' ) ) {
1100 $delivery_type = 'retail';
1101 }
1102
1103 // convert to int (default to 2 = standard for unknown types)
1104 $delivery_type = isset($delivery_types[$delivery_type]) ? $delivery_types[$delivery_type] : 2;
1105
1106 return $delivery_type;
1107 }
1108
1109 public function get_delivery_date( $order, $postnl_delivery_options = '' ) {
1110 if (empty($postnl_delivery_options)) {
1111 $postnl_delivery_options = WCX_Order::get_meta( $order, '_postnl_delivery_options' );
1112 }
1113
1114
1115 if ( !empty($postnl_delivery_options) && !empty($postnl_delivery_options['date']) ) {
1116 $delivery_date = $postnl_delivery_options['date'];
1117
1118 $delivery_type = $this->get_delivery_type( $order, $postnl_delivery_options );
1119 if ( in_array($delivery_type, array(1,3)) && !empty($postnl_delivery_options['time']) ) {
1120 $delivery_time_options = array_shift($postnl_delivery_options['time']); // take first element in time array
1121 $delivery_time = $delivery_time_options['start'];
1122 } else {
1123 $delivery_time = '00:00:00';
1124 }
1125 $delivery_date = "{$delivery_date} {$delivery_time}";
1126 return $delivery_date;
1127 } else {
1128 return false;
1129 }
1130
1131 }
1132
1133 public function get_order_shipping_class($order, $shipping_method_id = '') {
1134 if (empty($shipping_method_id)) {
1135 $order_shipping_methods = $order->get_items('shipping');
1136
1137 if ( !empty( $order_shipping_methods ) ) {
1138 // we're taking the first (we're not handling multiple shipping methods as of yet)
1139 $order_shipping_method = array_shift($order_shipping_methods);
1140 $shipping_method_id = $order_shipping_method['method_id'];
1141 } else {
1142 return false;
1143 }
1144 }
1145
1146 $shipping_method = $this->get_shipping_method( $shipping_method_id );
1147 if (empty($shipping_method)) {
1148 return false;
1149 }
1150
1151 // get shipping classes from order
1152 $found_shipping_classes = $this->find_order_shipping_classes( $order );
1153
1154 $highest_class = $this->get_shipping_class( $shipping_method, $found_shipping_classes );
1155 return $highest_class;
1156
1157 }
1158
1159 public function get_shipping_method($chosen_method) {
1160 if ( version_compare( WOOCOMMERCE_VERSION, '2.6', '>=' ) && $chosen_method !== 'legacy_flat_rate' ) {
1161 $chosen_method = explode( ':', $chosen_method ); // slug:instance
1162 // only for flat rate
1163 if ( $chosen_method[0] !== 'flat_rate' ) {
1164 return false;
1165 }
1166 if ( empty( $chosen_method[1] ) ) {
1167 return false; // no instance known (=probably manual order)
1168 }
1169
1170 $method_slug = $chosen_method[0];
1171 $method_instance = $chosen_method[1];
1172
1173 $shipping_method = WC_Shipping_Zones::get_shipping_method( $method_instance );
1174 } else {
1175 // only for flat rate or legacy flat rate
1176 if ( !in_array($chosen_method, array('flat_rate','legacy_flat_rate') ) ) {
1177 return false;
1178 }
1179 $shipping_methods = WC()->shipping->load_shipping_methods( $package );
1180
1181 if (!isset($shipping_methods[$chosen_method])) {
1182 return false;
1183 }
1184 $shipping_method = $shipping_methods[$chosen_method];
1185 }
1186
1187 return $shipping_method;
1188 }
1189
1190 public function get_shipping_class($shipping_method, $found_shipping_classes) {
1191 // get most expensive class
1192 // adapted from $shipping_method->calculate_shipping()
1193 $highest_class_cost = 0;
1194 $highest_class = false;
1195 foreach ( $found_shipping_classes as $shipping_class => $products ) {
1196 // Also handles BW compatibility when slugs were used instead of ids
1197 $shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' );
1198 $class_cost_string = $shipping_class_term && $shipping_class_term->term_id ? $shipping_method->get_option( 'class_cost_' . $shipping_class_term->term_id, $shipping_method->get_option( 'class_cost_' . $shipping_class, '' ) ) : $shipping_method->get_option( 'no_class_cost', '' );
1199
1200 if ( $class_cost_string === '' ) {
1201 continue;
1202 }
1203
1204 $has_costs = true;
1205 $class_cost = $this->wc_flat_rate_evaluate_cost( $class_cost_string, array(
1206 'qty' => array_sum( wp_list_pluck( $products, 'quantity' ) ),
1207 'cost' => array_sum( wp_list_pluck( $products, 'line_total' ) )
1208 ), $shipping_method );
1209 if ( $class_cost > $highest_class_cost && !empty($shipping_class_term->term_id) ) {
1210 $highest_class_cost = $class_cost;
1211 $highest_class = $shipping_class_term->term_id;
1212 }
1213 }
1214
1215 return $highest_class;
1216 }
1217
1218 public function find_order_shipping_classes($order) {
1219 $found_shipping_classes = array();
1220 $order_items = $order->get_items();
1221 foreach ( $order_items as $item_id => $item ) {
1222 $product = $order->get_product_from_item( $item );
1223 if ( $product && $product->needs_shipping() ) {
1224 $found_class = $product->get_shipping_class();
1225
1226 if ( ! isset( $found_shipping_classes[ $found_class ] ) ) {
1227 $found_shipping_classes[ $found_class ] = array();
1228 }
1229 // normally this should pass the $product object, but only in the checkout this contains
1230 // quantity & line_total (which is all we need), so we pass data from the $item instead
1231 $item_product = new stdClass;
1232 $item_product->quantity = $item['qty'];
1233 $item_product->line_total = $item['line_total'];
1234 $found_shipping_classes[ $found_class ][ $item_id ] = $item_product;
1235 }
1236 }
1237
1238 return $found_shipping_classes;
1239 }
1240
1241 /**
1242 * Adapted from WC_Shipping_Flat_Rate - Protected method
1243 * Evaluate a cost from a sum/string.
1244 * @param string $sum
1245 * @param array $args
1246 * @return string
1247 */
1248 public function wc_flat_rate_evaluate_cost($sum, $args = array(), $flat_rate_method) {
1249 if ( version_compare( WOOCOMMERCE_VERSION, '2.6', '>=' ) ) {
1250 include_once( WC()->plugin_path() . '/includes/libraries/class-wc-eval-math.php' );
1251 } else {
1252 include_once( WC()->plugin_path() . '/includes/shipping/flat-rate/includes/class-wc-eval-math.php' );
1253 }
1254
1255 // Allow 3rd parties to process shipping cost arguments
1256 $args = apply_filters( 'woocommerce_evaluate_shipping_cost_args', $args, $sum, $flat_rate_method );
1257 $locale = localeconv();
1258 $decimals = array( wc_get_price_decimal_separator(), $locale['decimal_point'], $locale['mon_decimal_point'], ',' );
1259 $this->fee_cost = $args['cost'];
1260
1261 // Expand shortcodes
1262 add_shortcode( 'fee', array( $this, 'wc_flat_rate_fee' ) );
1263
1264 $sum = do_shortcode( str_replace(
1265 array(
1266 '[qty]',
1267 '[cost]'
1268 ),
1269 array(
1270 $args['qty'],
1271 $args['cost']
1272 ),
1273 $sum
1274 ) );
1275
1276 remove_shortcode( 'fee', array( $this, 'wc_flat_rate_fee' ) );
1277
1278 // Remove whitespace from string
1279 $sum = preg_replace( '/\s+/', '', $sum );
1280
1281 // Remove locale from string
1282 $sum = str_replace( $decimals, '.', $sum );
1283
1284 // Trim invalid start/end characters
1285 $sum = rtrim( ltrim( $sum, "\t\n\r\0\x0B+*/" ), "\t\n\r\0\x0B+-*/" );
1286
1287 // Do the math
1288 return $sum ? WC_Eval_Math::evaluate( $sum ) : 0;
1289 }
1290
1291 /**
1292 * Adapted from WC_Shipping_Flat_Rate - Protected method
1293 * Work out fee (shortcode).
1294 * @param array $atts
1295 * @return string
1296 */
1297 public function wc_flat_rate_fee( $atts ) {
1298 $atts = shortcode_atts( array(
1299 'percent' => '',
1300 'min_fee' => '',
1301 'max_fee' => '',
1302 ), $atts );
1303
1304 $calculated_fee = 0;
1305
1306 if ( $atts['percent'] ) {
1307 $calculated_fee = $this->fee_cost * ( floatval( $atts['percent'] ) / 100 );
1308 }
1309
1310 if ( $atts['min_fee'] && $calculated_fee < $atts['min_fee'] ) {
1311 $calculated_fee = $atts['min_fee'];
1312 }
1313
1314 if ( $atts['max_fee'] && $calculated_fee > $atts['max_fee'] ) {
1315 $calculated_fee = $atts['max_fee'];
1316 }
1317
1318 return $calculated_fee;
1319 }
1320
1321 public function filter_postnl_destination_orders( $order_ids ) {
1322 foreach ($order_ids as $key => $order_id) {
1323 $order = WCX::get_order( $order_id );
1324 $shipping_country = WCX_Order::get_prop( $order, 'shipping_country' );
1325 // skip non-postnl destination orders
1326 if ( !$this->is_postnl_destination( $shipping_country ) ) {
1327 unset($order_ids[$key]);
1328 }
1329 }
1330 return $order_ids;
1331 }
1332
1333 public function is_postnl_destination( $country_code ) {
1334 return ( $country_code == 'NL' || $this->is_eu_country( $country_code ) || $this->is_world_shipment_country( $country_code ) );
1335 }
1336
1337 public function is_eu_country($country_code) {
1338 // $eu_countries = array( 'GB', 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'EL', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE' );
1339 $euro_countries = array( 'AT','BE','BG','CZ','DK','EE','FI','FR','DE','GB','GR','HU','IE','IT','LV','LT','LU','PL','PT','RO','SK','SI','ES','SE','MC','AL','AD','BA','IC','FO','GI','GL','GG','IS','JE','HR','LI','MK','MD','ME','UA','SM','RS','VA','BY' );
1340
1341 return in_array( $country_code, $euro_countries);
1342 }
1343
1344 public function is_world_shipment_country( $country_code ) {
1345 $world_shipment_countries = array( 'AF','AQ','DZ','VI','AO','AG','AR','AM','AW','AU','AZ','BS','BH','BD','BB','BZ','BJ','BM','BT','BO','BW','BR','VG','BN','BF','BI','KH','CA','KY','CF','CL','CN','CO','KM','CG','CD','CR','CU','DJ','DM','DO','EC','EG','SV','GQ','ER','ET','FK','FJ','PH','GF','PF','GA','GM','GE','GH','GD','GP','GT','GN','GW','GY','HT','HN','HK','IN','ID','IQ','IR','IL','CI','JM','JP','YE','JO','CV','CM','KZ','KE','KG','KI','KW','LA','LS','LB','LR','LY','MO','MG','MW','MV','MY','ML','MA','MQ','MR','MU','MX','MN','MS','MZ','MM','NA','NR','NP','NI','NC','NZ','NE','NG','KP','UZ','OM','TL','PK','PA','PG','PY','PE','PN','PR','QA','RE','RU','RW','KN','LC','VC','PM','WS','ST','SA','SN','SC','SL','SG','SO','LK','SD','SR','SZ','SY','TJ','TW','TZ','TH','TG','TO','TT','TD','TN','TM','TC','TV','UG','UY','VU','VE','AE','US','VN','ZM','ZW','ZA','KR','AN','BQ','CW','SX','XK','IM','MT','CY','CH','TR','NO' );
1346 return in_array( $country_code, $world_shipment_countries);
1347 }
1348
1349 public function get_invoice_number( $order ) {
1350 return (string) apply_filters( 'wc_postnl_invoice_number', $order->get_order_number() );
1351 }
1352
1353 public function log( $message ) {
1354 if (isset(WooCommerce_PostNL()->general_settings['error_logging'])) {
1355 if( class_exists('WC_Logger') ) {
1356 $wc_logger = function_exists('wc_get_logger') ? wc_get_logger() : new WC_Logger();
1357 $wc_logger->add('wc-postnl', $message );
1358 } else {
1359 // Old WC versions didn't have a logger
1360 // log file in upload folder - wp-content/uploads
1361 $upload_dir = wp_upload_dir();
1362 $upload_base = trailingslashit( $upload_dir['basedir'] );
1363 $log_file = $upload_base.'postnl_log.txt';
1364
1365 $current_date_time = date("Y-m-d H:i:s");
1366 $message = $current_date_time .' ' .$message ."\n";
1367
1368 file_put_contents($log_file, $message, FILE_APPEND);
1369 }
1370 }
1371 }
1372 }
1373
1374 endif; // class_exists
1375
1376 return new WooCommerce_PostNL_Export();
1377