PluginProbe
PostNL for WooCommerce / 4.4.0
PostNL for WooCommerce v4.4.0
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 / frontend / class-wcpn-checkout.php

class-wcpn-checkout.php in PostNL for WooCommerce 4.4.0, at includes/frontend/class-wcpn-checkout.php

562 lines 21.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use MyParcelNL\Sdk\src\Model\Consignment\AbstractConsignment;
4 use MyParcelNL\Sdk\src\Model\Consignment\PostNLConsignment;
5 use MyParcelNL\Sdk\src\Support\Arr;
6 use WPO\WC\PostNL\Compatibility\Order as WCX_Order;
7 use WPO\WC\PostNL\Compatibility\WC_Core as WCX;
8
9 if (! defined('ABSPATH')) {
10 exit;
11 } // Exit if accessed directly
12
13 if (class_exists('WCPN_Checkout')) {
14 return new WCPN_Checkout();
15 }
16
17 /**
18 * Frontend views
19 */
20 class WCPN_Checkout
21 {
22 private const DELIVERY_OPTIONS_KEY_MAP = [
23 'deliveryType' => 'delivery_type',
24 'isPickup' => 'is_pickup',
25 'labelDescription' => 'label_description',
26 'pickupLocation' => 'pickup_location',
27 'packageType' => 'package_type',
28 'shipmentOptions' => 'shipment_options',
29 'shipmentOptions.ageCheck' => 'shipment_options.age_check',
30 'shipmentOptions.insuredAmount' => 'shipment_options.insured_amount',
31 'shipmentOptions.largeFormat' => 'shipment_options.large_format',
32 'shipmentOptions.onlyRecipient' => 'shipment_options.only_recipient',
33 'shipmentOptions.returnShipment' => 'shipment_options.return_shipment',
34 ];
35
36 /**
37 * WCPN_Checkout constructor.
38 */
39 public function __construct()
40 {
41 add_action("wp_enqueue_scripts", [$this, "enqueue_frontend_scripts"], 100);
42
43 // Save delivery options data
44 add_action("woocommerce_checkout_update_order_meta", [$this, "save_delivery_options"], 10, 2);
45
46 add_action("wp_ajax_wcpn_get_delivery_options_config", [$this, "getDeliveryOptionsConfigAjax"]);
47 }
48
49 /**
50 * Load styles & scripts on the checkout page.
51 *
52 * @throws \Exception
53 */
54 public function enqueue_frontend_scripts(): void
55 {
56 // The order received page has the same page id as the checkout so `is_checkout()` returns true on both...
57 if (! is_checkout() || is_order_received_page()) {
58 return;
59 }
60
61 // if using split address fields
62 $useSplitAddressFields = WCPOST()->setting_collection->isEnabled(WCPOST_Settings::SETTING_USE_SPLIT_ADDRESS_FIELDS);
63 if ($useSplitAddressFields) {
64 wp_enqueue_script(
65 "wcpn-checkout-fields",
66 WCPOST()->plugin_url() . "/assets/js/wcpn-checkout-fields.js",
67 ["wc-checkout"],
68 WC_POSTNL_VERSION,
69 true
70 );
71 }
72
73 // Don"t load the delivery options scripts if it"s disabled
74 if (! WCPOST()->setting_collection->isEnabled(WCPOST_Settings::SETTING_DELIVERY_OPTIONS_ENABLED)) {
75 return;
76 }
77
78 /**
79 * JS dependencies array
80 */
81 $deps = ["wc-checkout"];
82
83 /**
84 * If split address fields are enabled add the checkout fields script as an additional dependency.
85 */
86 if ($useSplitAddressFields) {
87 $deps[] = "wcpn-checkout-fields";
88 }
89
90 /*
91 * Show delivery options also for shipments on backorder
92 */
93 if (! $this->shouldShowDeliveryOptions()) {
94 return;
95 }
96
97 wp_enqueue_script(
98 "wc-postnl",
99 WCPOST()->plugin_url() . "/assets/js/myparcel.js",
100 $deps,
101 WC_POSTNL_VERSION,
102 true
103 );
104
105 wp_enqueue_script(
106 "wc-postnl-frontend",
107 WCPOST()->plugin_url() . "/assets/js/wcpn-frontend.js",
108 array_merge($deps, ["wc-postnl", "jquery"]),
109 WC_POSTNL_VERSION,
110 true
111 );
112
113 $this->inject_delivery_options_variables();
114 }
115
116 /**
117 * Localize variables into the delivery options scripts.
118 *
119 * @throws Exception
120 */
121 public function inject_delivery_options_variables(): void
122 {
123 wp_localize_script(
124 'wc-postnl-frontend',
125 'wcpn',
126 [
127 "ajax_url" => admin_url("admin-ajax.php"),
128 ]
129 );
130
131 wp_localize_script(
132 "wc-postnl-frontend",
133 "PostNLDisplaySettings",
134 [
135 // Convert true/false to int for JavaScript
136 "isUsingSplitAddressFields" => (int) WCPOST()->setting_collection->isEnabled(
137 WCPOST_Settings::SETTING_USE_SPLIT_ADDRESS_FIELDS
138 ),
139 "splitAddressFieldsCountries" => WCPN_NL_Postcode_Fields::COUNTRIES_WITH_SPLIT_ADDRESS_FIELDS,
140 ]
141 );
142
143 wp_localize_script(
144 "wc-postnl",
145 "PostNLDeliveryOptions",
146 [
147 "allowedShippingMethods" => json_encode($this->getShippingMethodsAllowingDeliveryOptions()),
148 "disallowedShippingMethods" => json_encode(WCPN_Export::DISALLOWED_SHIPPING_METHODS),
149 "alwaysShow" => $this->alwaysDisplayDeliveryOptions(),
150 "hiddenInputName" => WCPOST_Admin::META_DELIVERY_OPTIONS,
151 ]
152 );
153
154 wp_localize_script(
155 'wc-postnl',
156 'MyParcelConfig',
157 $this->getDeliveryOptionsConfig()
158 );
159
160 // Load the checkout template.
161 add_action(
162 apply_filters(
163 'wc_wcpn_delivery_options_location',
164 WCPOST()->setting_collection->getByName(WCPOST_Settings::SETTING_DELIVERY_OPTIONS_POSITION)
165 ),
166 [$this, 'output_delivery_options'],
167 10
168 );
169 }
170
171 /**
172 * @return string
173 */
174 public function get_delivery_options_shipping_methods()
175 {
176 $packageTypes = WCPOST()->setting_collection->getByName(WCPOST_Settings::SETTING_SHIPPING_METHODS_PACKAGE_TYPES);
177
178 if (! is_array($packageTypes)) {
179 $packageTypes = [];
180 }
181
182 $shipping_methods = [];
183
184 if (array_key_exists(AbstractConsignment::PACKAGE_TYPE_PACKAGE, $packageTypes ?? [])) {
185 // settings_checkout_display_for_selected_methods = enable delivery options
186 $shipping_methods = $packageTypes[AbstractConsignment::PACKAGE_TYPE_PACKAGE];
187 }
188
189 return json_encode($shipping_methods);
190 }
191
192 /**
193 * Get the delivery options config in JSON for passing to JavaScript.
194 *
195 * @return array
196 */
197 public function getDeliveryOptionsConfig(): array
198 {
199 $settings = WCPOST()->setting_collection;
200 $carriers = $this->getCarriers();
201 $cartTotals = WC()->session->get('cart_totals');
202 $chosenShippingMethodPrice = (float) $cartTotals['shipping_total'];
203 $displayIncludingTax = WC()->cart->display_prices_including_tax();
204 $priceFormat = self::getDeliveryOptionsTitle(WCPOST_Settings::SETTING_DELIVERY_OPTIONS_PRICE_FORMAT);
205
206 if ($displayIncludingTax) {
207 $chosenShippingMethodPrice += (float) $cartTotals['shipping_tax'];
208 }
209
210 $postNLConfig = [
211 "config" => [
212 "currency" => get_woocommerce_currency(),
213 "locale" => "nl-NL",
214 "platform" => "myparcel",
215 "basePrice" => $chosenShippingMethodPrice,
216 "showPriceSurcharge" => WCPN_Settings_Data::DISPLAY_SURCHARGE_PRICE === $priceFormat,
217 "pickupLocationsDefaultView" => self::getPickupLocationsDefaultView(),
218 ],
219 "strings" => [
220 "addressNotFound" => __("Address details are not entered", "woocommerce-postnl"),
221 "city" => __("City", "woocommerce-postnl"),
222 "closed" => __("Closed", "woocommerce-postnl"),
223 "deliveryEveningTitle" => self::getDeliveryOptionsTitle(WCPOST_Settings::SETTING_EVENING_DELIVERY_TITLE),
224 "deliveryMorningTitle" => self::getDeliveryOptionsTitle(WCPOST_Settings::SETTING_MORNING_DELIVERY_TITLE),
225 "deliveryStandardTitle" => self::getDeliveryOptionsTitle(WCPOST_Settings::SETTING_STANDARD_TITLE),
226 "deliveryTitle" => self::getDeliveryOptionsTitle(WCPOST_Settings::SETTING_DELIVERY_TITLE),
227 "headerDeliveryOptions" => self::getDeliveryOptionsTitle(WCPOST_Settings::SETTING_HEADER_DELIVERY_OPTIONS_TITLE),
228 "houseNumber" => __("House number", "woocommerce-postnl"),
229 "onlyRecipientTitle" => self::getDeliveryOptionsTitle(WCPOST_Settings::SETTING_ONLY_RECIPIENT_TITLE),
230 "openingHours" => __("Opening hours", "woocommerce-postnl"),
231 "pickUpFrom" => __("Pick up from", "woocommerce-postnl"),
232 "pickupTitle" => self::getDeliveryOptionsTitle(WCPOST_Settings::SETTING_PICKUP_TITLE),
233 "postcode" => __("Postcode", "woocommerce-postnl"),
234 "retry" => __("Retry", "woocommerce-postnl"),
235 "signatureTitle" => self::getDeliveryOptionsTitle(WCPOST_Settings::SETTING_SIGNATURE_TITLE),
236 "wrongHouseNumberCity" => __("Postcode/city combination unknown", "woocommerce-postnl"),
237 ],
238 ];
239
240 foreach ($carriers as $carrier) {
241 foreach (self::getDeliveryOptionsConfigMap($carrier) as $key => $setting) {
242 [$settingName, $function, $addBasePrice] = $setting;
243
244 $value = $settings->{$function}($carrier . '_' . $settingName);
245
246 if (is_numeric($value) && $this->useTotalPrice() && $addBasePrice) {
247 $value += $chosenShippingMethodPrice;
248 }
249
250 Arr::set($postNLConfig, 'config.' . $key, $value);
251 }
252 }
253
254 $postNLConfig['config']['priceStandardDelivery'] = $this->useTotalPrice() ? $chosenShippingMethodPrice : null;
255
256 return $postNLConfig;
257 }
258
259 /**
260 * Echoes the delivery options config as a JSON string for use with AJAX.
261 */
262 public function getDeliveryOptionsConfigAjax(): void
263 {
264 echo json_encode($this->getDeliveryOptionsConfig(), JSON_UNESCAPED_SLASHES);
265 die();
266 }
267
268 /**
269 * @return bool
270 */
271 public function useTotalPrice(): bool
272 {
273 $priceFormat = WCPOST()->setting_collection->getByName(WCPOST_Settings::SETTING_DELIVERY_OPTIONS_PRICE_FORMAT);
274
275 if (! isset($priceFormat) || WCPN_Settings_Data::DISPLAY_TOTAL_PRICE === $priceFormat){
276 return true;
277 }
278
279 return false;
280 }
281
282 /**
283 * @param string $title
284 *
285 * @return string
286 */
287 public static function getDeliveryOptionsTitle(string $title): string
288 {
289 $settings = WCPOST()->setting_collection;
290
291 return __(strip_tags($settings->getStringByName($title)), "woocommerce-postnl");
292 }
293
294 /**
295 * @return string
296 */
297 public static function getPickupLocationsDefaultView(): string
298 {
299 $settings = WCPOST()->setting_collection;
300
301 return $settings->getStringByName(WCPOST_Settings::SETTING_PICKUP_LOCATIONS_DEFAULT_VIEW);
302 }
303
304 /**
305 * Output the delivery options template.
306 */
307 public function output_delivery_options(): void
308 {
309 do_action('woocommerce_postnl_before_delivery_options');
310 require_once(WCPOST()->includes . '/views/html-delivery-options-template.php');
311 do_action('woocommerce_postnl_after_delivery_options');
312 }
313
314 /**
315 * Get the array of enabled carriers by checking if they have either delivery or pickup enabled.
316 *
317 * @return array
318 */
319 private function getCarriers(): array
320 {
321 $settings = WCPOST()->setting_collection;
322 $carriers = [];
323
324 foreach ([PostNLConsignment::CARRIER_NAME] as $carrier) {
325 if ($settings->getByName("{$carrier}_" . WCPOST_Settings::SETTING_CARRIER_PICKUP_ENABLED)
326 || $settings->getByName(
327 "{$carrier}_" . WCPOST_Settings::SETTING_CARRIER_DELIVERY_ENABLED
328 )) {
329 $carriers[] = $carrier;
330 }
331 }
332
333 return $carriers;
334 }
335
336 /**
337 * Save delivery options to order when used
338 *
339 * @param int $order_id
340 * @param array $posted
341 *
342 * @return void
343 * @throws Exception
344 */
345 public static function save_delivery_options($order_id)
346 {
347 $order = WCX::get_order($order_id);
348
349 $shippingMethod = Arr::get($_POST, "shipping_method");
350 $highestShippingClass = Arr::get($_POST, "postnl_highest_shipping_class") ?? $shippingMethod[0];
351
352 /**
353 * Save the current version of our plugin to the order.
354 */
355 WCX_Order::update_meta_data(
356 $order,
357 WCPOST_Admin::META_ORDER_VERSION,
358 WCPOST()->version
359 );
360
361 WCX_Order::update_meta_data(
362 $order,
363 WCPOST_Admin::META_SHIPMENT_OPTIONS_EXTRA,
364 [
365 'collo_amount' => 1,
366 'weight' => WC()->cart->get_cart_contents_weight(),
367 ]
368 );
369
370 if ($highestShippingClass) {
371 WCX_Order::update_meta_data(
372 $order,
373 WCPOST_Admin::META_HIGHEST_SHIPPING_CLASS,
374 $highestShippingClass
375 );
376 }
377
378 $deliveryOptionsFromPost = Arr::get($_POST, WCPOST_Admin::META_DELIVERY_OPTIONS);
379 $deliveryOptionsFromShippingClass = $highestShippingClass
380 ? [
381 'packageType' => WCPN_Export::getPackageTypeFromShippingMethod(
382 $shippingMethod[0],
383 $highestShippingClass
384 ),
385 ]
386 : null;
387
388 $deliveryOptions = empty($deliveryOptionsFromPost)
389 ? $deliveryOptionsFromShippingClass
390 : stripslashes($deliveryOptionsFromPost);
391
392 if ($deliveryOptions) {
393 if (! is_array($deliveryOptions)) {
394 $deliveryOptions = json_decode($deliveryOptions, true);
395 }
396 $deliveryOptions = self::convertDeliveryOptionsForAdapter($deliveryOptions);
397 $deliveryOptions = WCPOST_Admin::removeDisallowedDeliveryOptions(
398 $deliveryOptions,
399 $order->get_shipping_country()
400 );
401
402 /*
403 * Create a new DeliveryOptions class from the data.
404 */
405 $deliveryOptions = new WCPN_DeliveryOptionsFromOrderAdapter(null, $deliveryOptions);
406
407 /*
408 * Store it in the meta data.
409 */
410 WCX_Order::update_meta_data(
411 $order,
412 WCPOST_Admin::META_DELIVERY_OPTIONS,
413 $deliveryOptions->toArray()
414 );
415 }
416 }
417
418 /**
419 * Return the names of shipping methods that will show delivery options. If DISPLAY_FOR_ALL_METHODS is enabled it'll
420 * return an empty array and the frontend will allow any shipping except any that are specifically disallowed.
421 *
422 * @return string[]
423 * @throws Exception
424 * @see WCPN_Export::DISALLOWED_SHIPPING_METHODS
425 */
426 private function getShippingMethodsAllowingDeliveryOptions(): array
427 {
428 $allowedMethods = [];
429 $displayFor = WCPOST()->setting_collection->getByName(WCPOST_Settings::SETTING_DELIVERY_OPTIONS_DISPLAY);
430 $shippingMethodsByPackageType = WCPOST()->setting_collection->getByName(WCPOST_Settings::SETTING_SHIPPING_METHODS_PACKAGE_TYPES);
431
432 if (WCPN_Settings_Data::DISPLAY_FOR_ALL_METHODS === $displayFor || ! $shippingMethodsByPackageType) {
433 return $allowedMethods;
434 }
435
436 $shippingMethodsForPackage = $shippingMethodsByPackageType[AbstractConsignment::PACKAGE_TYPE_PACKAGE_NAME];
437
438 foreach ($shippingMethodsForPackage as $shippingMethod) {
439 [$methodId] = self::splitShippingMethodString($shippingMethod);
440
441 if (! in_array($methodId, WCPN_Export::DISALLOWED_SHIPPING_METHODS)) {
442 $allowedMethods[] = $shippingMethod;
443 }
444 }
445
446 return $allowedMethods;
447 }
448
449 /**
450 * @return bool
451 */
452 private function alwaysDisplayDeliveryOptions(): bool
453 {
454 $display = WCPOST()->setting_collection->getByName(WCPOST_Settings::SETTING_DELIVERY_OPTIONS_DISPLAY);
455
456 return $display === WCPN_Settings_Data::DISPLAY_FOR_ALL_METHODS;
457 }
458
459 /**
460 * Split a <rateId>:<instanceId> string into an array. If there is no instanceId, the second array element will be
461 * null.
462 *
463 * @param $shippingMethod
464 *
465 * @return array
466 */
467 public static function splitShippingMethodString(string $shippingMethod): array
468 {
469 $split = explode(':', $shippingMethod, 2);
470
471 if (count($split) === 1) {
472 $split[] = null;
473 }
474
475 return $split;
476 }
477
478 /**
479 * Map keys from the delivery options to the keys used in the adapters.
480 *
481 * @param array $deliveryOptions
482 *
483 * @return array
484 */
485 private static function convertDeliveryOptionsForAdapter(array $deliveryOptions): array
486 {
487 foreach (self::DELIVERY_OPTIONS_KEY_MAP as $camel => $snake) {
488 $value = Arr::get($deliveryOptions, $camel);
489 if (isset($value)) {
490 Arr::set($deliveryOptions, $snake, $value);
491 Arr::forget($deliveryOptions, $camel);
492 }
493 }
494
495 return $deliveryOptions;
496 }
497
498 /**
499 * @param string $carrier
500 *
501 * @return array[]
502 */
503 private static function getDeliveryOptionsConfigMap(string $carrier): array
504 {
505 return [
506 "carrierSettings.$carrier.allowDeliveryOptions" => [WCPOST_Settings::SETTING_CARRIER_DELIVERY_ENABLED, 'isEnabled', false],
507 "carrierSettings.$carrier.allowEveningDelivery" => [WCPOST_Settings::SETTING_CARRIER_DELIVERY_EVENING_ENABLED, 'isEnabled', false],
508 "carrierSettings.$carrier.allowMondayDelivery" => [WCPOST_Settings::SETTING_CARRIER_MONDAY_DELIVERY_ENABLED, 'isEnabled', false],
509 "carrierSettings.$carrier.allowMorningDelivery" => [WCPOST_Settings::SETTING_CARRIER_DELIVERY_MORNING_ENABLED, 'isEnabled', false],
510 "carrierSettings.$carrier.allowOnlyRecipient" => [WCPOST_Settings::SETTING_CARRIER_ONLY_RECIPIENT_ENABLED, 'isEnabled', false],
511 "carrierSettings.$carrier.allowPickupLocations" => [WCPOST_Settings::SETTING_CARRIER_PICKUP_ENABLED, 'isEnabled', false],
512 "carrierSettings.$carrier.allowSaturdayDelivery" => [WCPOST_Settings::SETTING_CARRIER_SATURDAY_DELIVERY_ENABLED, 'isEnabled', false],
513 "carrierSettings.$carrier.allowSignature" => [WCPOST_Settings::SETTING_CARRIER_SIGNATURE_ENABLED, 'isEnabled', false],
514 "carrierSettings.$carrier.priceEveningDelivery" => [WCPOST_Settings::SETTING_CARRIER_DELIVERY_EVENING_FEE, 'getPriceByName', true],
515 "carrierSettings.$carrier.priceMondayDelivery" => [WCPOST_Settings::SETTING_CARRIER_MONDAY_DELIVERY_FEE, 'getPriceByName', true],
516 "carrierSettings.$carrier.priceMorningDelivery" => [WCPOST_Settings::SETTING_CARRIER_DELIVERY_MORNING_FEE, 'getPriceByName', true],
517 "carrierSettings.$carrier.priceOnlyRecipient" => [WCPOST_Settings::SETTING_CARRIER_ONLY_RECIPIENT_FEE, 'getPriceByName', false],
518 "carrierSettings.$carrier.pricePickup" => [WCPOST_Settings::SETTING_CARRIER_PICKUP_FEE, 'getPriceByName', true],
519 "carrierSettings.$carrier.priceSaturdayDelivery" => [WCPOST_Settings::SETTING_CARRIER_SATURDAY_DELIVERY_FEE, 'getPriceByName', true],
520 "carrierSettings.$carrier.priceSignature" => [WCPOST_Settings::SETTING_CARRIER_SIGNATURE_FEE, 'getPriceByName', false],
521 "cutoffTime" => [WCPOST_Settings::SETTING_CARRIER_CUTOFF_TIME, 'getStringByName', false],
522 "deliveryDaysWindow" => [WCPOST_Settings::SETTING_CARRIER_DELIVERY_DAYS_WINDOW, 'getIntegerByName', false],
523 "dropOffDays" => [WCPOST_Settings::SETTING_CARRIER_DROP_OFF_DAYS, 'getByName', false],
524 "dropOffDelay" => [WCPOST_Settings::SETTING_CARRIER_DROP_OFF_DELAY, 'getIntegerByName', false],
525 "fridayCutoffTime" => [WCPOST_Settings::SETTING_CARRIER_FRIDAY_CUTOFF_TIME, 'getStringByName', false],
526 "saturdayCutoffTime" => [WCPOST_Settings::SETTING_CARRIER_SATURDAY_CUTOFF_TIME, 'getStringByName', false],
527 ];
528 }
529
530 /**
531 * Show delivery options also for shipments on backorder
532 * @return bool
533 */
534 private function shouldShowDeliveryOptions(): bool
535 {
536 // $backorderDeliveryOptions causes the options to be displayed also when product is in backorder
537 $backorderDeliveryOptions = WCPOST()->setting_collection->isEnabled(WCPOST_Settings::SETTINGS_SHOW_DELIVERY_OPTIONS_FOR_BACKORDERS);
538 $show = true;
539
540 if ($backorderDeliveryOptions) {
541 return $show;
542 }
543
544 foreach (WC()->cart->get_cart() as $cartItem) {
545 /**
546 * @var WC_Product $product
547 */
548 $product = $cartItem['data'];
549 $isOnBackorder = $product->is_on_backorder($cartItem['quantity']);
550
551 if ($isOnBackorder) {
552 $show = false;
553 break;
554 }
555 }
556
557 return $show;
558 }
559 }
560
561 return new WCPN_Checkout();
562