PluginProbe
PostNL for WooCommerce / 5.9.12
PostNL for WooCommerce v5.9.12
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 / src / Rest_API / V4 / Label / Eligibility.php

Eligibility.php in PostNL for WooCommerce 5.9.12, at src/Rest_API/V4/Label/Eligibility.php

157 lines 6.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Rest_API\V4\Label\Eligibility file.
4 *
5 * @package PostNLWooCommerce\Rest_API\V4\Label
6 */
7
8 declare( strict_types = 1 );
9
10 namespace PostNLWooCommerce\Rest_API\V4\Label;
11
12 use PostNLWooCommerce\Helper\Product_Mapper\V4_Mapper;
13 use PostNLWooCommerce\Utils;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 /**
20 * Pure decision logic for whether an order is a shipment the V4 label service
21 * handles — a domestic NL parcel, a domestic NL letterbox (mailbox parcel), or
22 * an EU/ROW international parcel from NL/BE. Kept free of WooCommerce and
23 * Order\Base so the gate — the highest-risk part of the flow — can be asserted
24 * in isolation.
25 *
26 * @since 6.0.0
27 * @package PostNLWooCommerce\Rest_API\V4\Label
28 */
29 class Eligibility {
30
31 /**
32 * Resolve the V4 mapper result for an order's product combination.
33 *
34 * The selected options are passed through so a service-bearing combination
35 * that keeps product 3085 (e.g. insured) resolves to a services row (or an
36 * unknown combination) and is rejected by is_eligible(), rather than
37 * silently masquerading as the base parcel.
38 *
39 * @param string $origin Origin country (store base).
40 * @param string $destination Shipping zone (NL|BE|EU|ROW).
41 * @param bool $is_pickup Whether a pickup point was selected.
42 * @param array $backend_raw Raw backend feature flags ('yes' strings).
43 * @param string $product_code Legacy resolved product code.
44 * @return array V4_Mapper::map() result.
45 */
46 public static function resolve_mapped( string $origin, string $destination, bool $is_pickup, array $backend_raw, string $product_code ): array {
47 return V4_Mapper::map(
48 array(
49 'origin' => $origin,
50 'destination' => $destination,
51 'flow' => $is_pickup ? 'pickup_points' : 'delivery_day',
52 'options' => array_keys( Utils::get_selected_label_features( $backend_raw ) ),
53 'legacy_product_code' => $product_code,
54 )
55 );
56 }
57
58 /**
59 * Decide whether the collected signals describe a shipment the V4 service
60 * handles — a domestic NL or EU/ROW international parcel (single- or
61 * multi-collo), or a domestic NL letterbox (mailbox parcel 2928).
62 *
63 * @param array $signals {
64 * Signal set assembled by Service::gather_signals().
65 *
66 * @type int $num_labels Collo count (>= 1).
67 * @type bool $is_delivery_day A delivery-day option was selected.
68 * @type bool $is_pickup A pickup point was selected.
69 * @type bool $has_return A return label/barcode is involved.
70 * @type string $delivery_type 'Standard' or 'Evening'.
71 * @type string $origin Origin country.
72 * @type string $destination Shipping zone.
73 * @type array $mapped V4_Mapper::map() result.
74 * }
75 * @return bool
76 */
77 public static function is_eligible( array $signals ): bool {
78 // Multi-collo (num_labels > 1) is supported via multiple request items;
79 // only a missing/invalid collo count is rejected here.
80 if ( (int) ( $signals['num_labels'] ?? 1 ) < 1 ) {
81 return false;
82 }
83
84 if ( ! empty( $signals['is_delivery_day'] ) || ! empty( $signals['is_pickup'] ) ) {
85 return false;
86 }
87
88 if ( ! empty( $signals['has_return'] ) ) {
89 return false;
90 }
91
92 if ( 'Standard' !== ( $signals['delivery_type'] ?? 'Standard' ) ) {
93 return false;
94 }
95
96 $origin = (string) ( $signals['origin'] ?? '' );
97 $destination = (string) ( $signals['destination'] ?? '' );
98
99 // Domestic NL (tasks 18-19) or an EU/ROW international parcel from NL/BE
100 // (task 20). NL<->BE cross-border stays on legacy for now.
101 $is_domestic = ( 'NL' === $origin && 'NL' === $destination );
102 $is_international = in_array( $origin, array( 'NL', 'BE' ), true )
103 && in_array( $destination, array( 'EU', 'ROW' ), true );
104
105 if ( ! $is_domestic && ! $is_international ) {
106 return false;
107 }
108
109 // The mapper is authoritative: for a combination it marks as having a V4
110 // equivalent, its services array is the full V4 representation of every
111 // selected option, so no separate product-option gate is needed. Only a
112 // pickup DeliveryLocation is excluded here — that variant lands separately.
113 $mapped = $signals['mapped'] ?? array();
114 $shipment_type = (string) ( $mapped['shipmentType'] ?? '' );
115
116 if ( empty( $mapped['has_v4_equivalent'] ) || ! empty( $mapped['deliveryLocation'] ) ) {
117 return false;
118 }
119
120 // Letterbox (mailbox parcel 2928) is a domestic-NL-only variant. The 48h
121 // variant (2948) never reaches here: it collapses onto the same 'letterbox'
122 // option key but keeps product code 2948, so the mapper's product-code
123 // mismatch guard drops it to has_v4_equivalent = false above. A parcel may
124 // be domestic or EU/ROW international.
125 if ( 'letterbox' === $shipment_type ) {
126 return $is_domestic;
127 }
128
129 return 'parcel' === $shipment_type;
130 }
131
132 /**
133 * Resolve the mapper's service placeholders into concrete request values.
134 *
135 * The matrix stores insuredValue as the '<order_total>' placeholder — a misnomer
136 * kept for now to match V4_Mapper; the value substituted is the item subtotal, not
137 * the order total. It is replaced here with the order's insured amount. All other
138 * flags (deliveryConfirmation, statedAddressOnly, returnWhenNotHome) pass through
139 * unchanged. minimalAgeCheck would too, but no matrix row emits it yet.
140 *
141 * The insured amount must remain the order item subtotal (WC_Order::get_subtotal),
142 * matching the value the legacy Shipping\Client puts in the Amounts block. Do not
143 * switch it to the order total — that would add tax/shipping and diverge from V1.
144 *
145 * @param array $mapped_services Services array from V4_Mapper::map().
146 * @param float $insured_value Amount to insure (order item subtotal).
147 * @return array Concrete service flags for Request_Builder.
148 */
149 public static function resolve_services( array $mapped_services, float $insured_value ): array {
150 if ( array_key_exists( 'insuredValue', $mapped_services ) ) {
151 $mapped_services['insuredValue'] = $insured_value;
152 }
153
154 return $mapped_services;
155 }
156 }
157