PluginProbe
PostNL for WooCommerce / 4.0.1
PostNL for WooCommerce v4.0.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 / admin / class-wcpn-export-consignments.php

class-wcpn-export-consignments.php in PostNL for WooCommerce 4.0.1, at includes/admin/class-wcpn-export-consignments.php

464 lines 13.6 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\Adapter\DeliveryOptions\AbstractDeliveryOptionsAdapter as DeliveryOptions;
4 use MyParcelNL\Sdk\src\Factory\ConsignmentFactory;
5 use MyParcelNL\Sdk\src\Helper\MyParcelCollection;
6 use MyParcelNL\Sdk\src\Model\Consignment\AbstractConsignment;
7 use MyParcelNL\Sdk\src\Model\MyParcelCustomsItem;
8 use WPO\WC\PostNL\Compatibility\Order as WCX_Order;
9 use WPO\WC\PostNL\Compatibility\Product as WCX_Product;
10
11 if (! defined("ABSPATH")) {
12 exit;
13 } // Exit if accessed directly
14
15 if (class_exists("WCPN_Export_Consignments")) {
16 return;
17 }
18
19 class WCPN_Export_Consignments
20 {
21 /**
22 * @var AbstractConsignment
23 */
24 private $consignment;
25
26 /**
27 * @var DeliveryOptions
28 */
29 private $deliveryOptions;
30
31 /**
32 * @var mixed
33 */
34 private $recipient;
35
36 /**
37 * @var WC_Order
38 */
39 private $order;
40
41 /**
42 * @var string
43 */
44 private $apiKey;
45
46 /**
47 * @var string|null
48 */
49 private $carrier;
50
51 /**
52 * @var PostNLCollection
53 */
54 public $postNLCollection;
55 /**
56 * @var \OrderSettings
57 */
58 private $orderSettings;
59
60 /**
61 * WCPN_Export_Consignments constructor.
62 *
63 * @param WC_Order $order
64 *
65 * @throws ErrorException
66 * @throws Exception
67 */
68 public function __construct(WC_Order $order)
69 {
70 $this->getApiKey();
71
72 $this->order = $order;
73 $this->deliveryOptions = WCPOST_Admin::getDeliveryOptionsFromOrder($order);
74 $this->orderSettings = new OrderSettings($this->deliveryOptions, $order);
75
76 $this->carrier = $this->deliveryOptions->getCarrier() ?? WCPN_Data::DEFAULT_CARRIER;
77
78 $this->postNLCollection = (new MyParcelCollection())->setUserAgents(
79 [
80 'Wordpress' => get_bloginfo('version'),
81 'WooCommerce' => WOOCOMMERCE_VERSION,
82 'PostNL-WooCommerce' => WC_POSTNL_VERSION,
83 ]
84 );
85
86 $this->createConsignment();
87 $this->setConsignmentData();
88 }
89
90 /**
91 * Create a new consignment
92 *
93 * @return void
94 * @throws Exception
95 */
96 public function createConsignment(): void
97 {
98 $this->consignment = ConsignmentFactory::createByCarrierName($this->carrier);
99 }
100
101 /**
102 * Set all the needed data for the consignment.
103 *
104 * @throws Exception
105 */
106 private function setConsignmentData(): void
107 {
108 $this->setBaseData();
109 $this->setRecipient();
110 $this->setShipmentOptions();
111 $this->setPickupLocation();
112 $this->setCustomsDeclaration();
113 $this->setPhysicalProperties();
114 }
115
116 /**
117 * @param string $name
118 *
119 * @return mixed
120 */
121 private function getSetting(string $name)
122 {
123 return WCPOST()->setting_collection->getByName($name);
124 }
125
126 /**
127 * @return int
128 */
129 private function getPackageType(): int
130 {
131 return WCPN_Data::getPackageTypeId($this->orderSettings->getPackageType());
132 }
133
134 /**
135 * @return int
136 */
137 private function getDeliveryType(): int
138 {
139 $deliveryTypeId = WCPN_Data::getDeliveryTypeId($this->deliveryOptions->getDeliveryType());
140
141 return $deliveryTypeId ?? AbstractConsignment::DELIVERY_TYPE_STANDARD;
142 }
143
144 /**
145 * Get date in YYYY-MM-DD HH:MM:SS format
146 *
147 * @return string
148 */
149 public function getDeliveryDate(): string
150 {
151 $date = strtotime($this->deliveryOptions->getDate());
152 $deliveryDateTime = date('Y-m-d H:i:s', $date);
153 $deliveryDate = date("Y-m-d", $date);
154 $dateOfToday = date("Y-m-d", strtotime('now'));
155 $dateOfTomorrow = date('Y-m-d H:i:s', strtotime('now +1 day'));
156
157 if ($deliveryDate <= $dateOfToday) {
158 return $dateOfTomorrow;
159 }
160
161 return $deliveryDateTime;
162 }
163
164 /**
165 * @return void
166 * @throws \MyParcelNL\Sdk\src\Exception\MissingFieldException
167 * @throws \ErrorException
168 */
169 public function setCustomItems(): void
170 {
171 foreach ($this->order->get_items() as $item_id => $item) {
172 $product = $item->get_product();
173 $country = $this->getCountryOfOrigin($product);
174 if (! empty($product)) {
175 // Description
176 $description = $item["name"];
177
178 // GitHub issue https://github.com/postnlnl/woocommerce/issues/190
179 if (strlen($description) >= WCPN_Export::ITEM_DESCRIPTION_MAX_LENGTH) {
180 $description = substr($item["name"], 0, 47) . "...";
181 }
182 // Amount
183 $amount = (int) (isset($item["qty"]) ? $item["qty"] : 1);
184
185 // Weight (total item weight in grams)
186 $weight = (int) round(WCPN_Export::getItemWeightKg($item, $this->order) * 1000);
187 $totalWeight = $this->getTotalWeight($weight);
188
189 $postNLItem = (new MyParcelCustomsItem())
190 ->setDescription($description)
191 ->setAmount($amount)
192 ->setWeight($totalWeight)
193 ->setItemValue((int) round(($item["line_total"] + $item["line_tax"]) * 100))
194 ->setCountry($country)
195 ->setClassification($this->getHsCode($product));
196
197 $this->consignment->addItem($postNLItem);
198 }
199 }
200 }
201
202 /**
203 * @param int $weight
204 *
205 * @return int
206 */
207 private function getTotalWeight(int $weight): int
208 {
209 $parcelWeight = (int) $this->getSetting(WCPOST_Settings::SETTING_EMPTY_PARCEL_WEIGHT);
210
211 return $parcelWeight + $weight;
212 }
213
214 /**
215 * @param WC_Product $product
216 *
217 * @return int
218 * @throws \ErrorException
219 */
220 public function getHsCode(WC_Product $product): int
221 {
222 $defaultHsCode = $this->getSetting(WCPOST_Settings::SETTING_HS_CODE);
223 $productHsCode = WCX_Product::get_meta($product, WCPOST_Admin::META_HS_CODE, true);
224 $variationHsCode = WCX_Product::get_meta($product, WCPOST_Admin::META_HS_CODE_VARIATION, true);
225
226 $hsCode = $productHsCode ? $productHsCode : $defaultHsCode;
227
228 if ($variationHsCode) {
229 $hsCode = $variationHsCode;
230 }
231
232 if (! $hsCode) {
233 throw new ErrorException(__("No HS code found in PostNL settings", "woocommerce-postnl"));
234 }
235
236 return (int) $hsCode;
237 }
238
239 /**
240 * @param WC_Product $product
241 *
242 * @return string
243 */
244 public function getCountryOfOrigin(WC_Product $product): string
245 {
246 $defaultCountryOfOrigin = $this->getSetting(WCPOST_Settings::SETTING_COUNTRY_OF_ORIGIN);
247 $productCountryOfOrigin = WCX_Product::get_meta($product, WCPOST_Admin::META_COUNTRY_OF_ORIGIN, true);
248
249 $countryOfOrigin = $this->getPriorityOrigin($defaultCountryOfOrigin, $productCountryOfOrigin);
250
251 return (string) $countryOfOrigin;
252 }
253
254 /**
255 * @param string|null $defaultCountryOfOrigin
256 * @param string|null $productCountryOfOrigin
257 *
258 * @return string
259 */
260 public function getPriorityOrigin(?string $defaultCountryOfOrigin, ?string $productCountryOfOrigin): string
261 {
262 if ($productCountryOfOrigin) {
263 return $productCountryOfOrigin;
264 }
265
266 if ($defaultCountryOfOrigin) {
267 return $defaultCountryOfOrigin;
268 }
269
270 return WC()->countries->get_base_country() ?? AbstractConsignment::CC_NL;
271 }
272
273 /**
274 * @return AbstractConsignment
275 */
276 public function getConsignment(): AbstractConsignment
277 {
278 return $this->consignment;
279 }
280
281 /**
282 * @return int
283 */
284 private function getContents(): int
285 {
286 return (int) ($this->getSetting("package_contents") ?? AbstractConsignment::PACKAGE_CONTENTS_COMMERCIAL_GOODS);
287 }
288
289 /**
290 * Gets the recipient and puts its data in the consignment.
291 *
292 * @throws Exception
293 */
294 private function setRecipient(): void
295 {
296 $this->recipient = WCPN_Export::getRecipientFromOrder($this->order);
297
298 $this->consignment
299 ->setCountry($this->recipient['cc'])
300 ->setPerson($this->recipient['person'])
301 ->setCompany($this->recipient['company'])
302 ->setStreet($this->recipient['street'])
303 ->setNumber($this->recipient['number'] ?? null)
304 ->setNumberSuffix($this->recipient['number_suffix'] ?? null)
305 ->setStreetAdditionalInfo($this->recipient['street_additional_info'] ?? null)
306 ->setPostalCode($this->recipient['postal_code'])
307 ->setCity($this->recipient['city'])
308 ->setEmail($this->recipient['email'])
309 ->setPhone($this->recipient['phone']);
310 }
311
312 /**
313 * @throws ErrorException
314 */
315 private function getApiKey(): void
316 {
317 $this->apiKey = $this->getSetting(WCPOST_Settings::SETTING_API_KEY);
318
319 if (! $this->apiKey) {
320 throw new ErrorException(__("No API key found in PostNL settings", "woocommerce-postnl"));
321 }
322 }
323
324 /**
325 * Get the label description from OrderSettings and replace any variables in it.
326 *
327 * @return string
328 */
329 private function getFormattedLabelDescription(): string
330 {
331 $productIds = [];
332 $productNames = [];
333 $productSkus = [];
334
335 foreach ($this->order->get_items() as $item) {
336 if (! method_exists($item, 'get_product')) {
337 continue;
338 }
339
340 /** @var WC_Product $product */
341 $product = $item->get_product();
342 $sku = $product->get_sku();
343
344 $productIds[] = $product->get_id();
345 $productNames[] = $product->get_name();
346 $productSkus[] = empty($sku) ? '–' : $sku;
347 }
348
349 $formattedLabelDescription = strtr(
350 $this->orderSettings->getLabelDescription(),
351 [
352 '[DELIVERY_DATE]' => date('d-m-Y', strtotime($this->deliveryOptions->getDate())),
353 '[ORDER_NR]' => $this->order->get_order_number(),
354 '[PRODUCT_ID]' => implode(', ', $productIds),
355 '[PRODUCT_NAME]' => implode(', ', $productNames),
356 '[PRODUCT_QTY]' => count($this->order->get_items()),
357 '[PRODUCT_SKU]' => implode(', ', $productSkus),
358 '[CUSTOMER_NOTE]' => $this->order->get_customer_note(),
359 ]
360 );
361
362 if (strlen($formattedLabelDescription) > WCPN_Export::ORDER_DESCRIPTION_MAX_LENGTH) {
363 return substr($formattedLabelDescription, 0, 42) . "...";
364 }
365
366 return $formattedLabelDescription;
367 }
368
369 /**
370 * Set the pickup location
371 */
372 private function setPickupLocation(): void
373 {
374 if (! $this->deliveryOptions->isPickup()) {
375 return;
376 }
377
378 $pickupLocation = $this->deliveryOptions->getPickupLocation();
379
380 $this->consignment
381 ->setPickupCountry($pickupLocation->getCountry())
382 ->setPickupCity($pickupLocation->getCity())
383 ->setPickupLocationName($pickupLocation->getLocationName())
384 ->setPickupStreet($pickupLocation->getStreet())
385 ->setPickupNumber($pickupLocation->getNumber())
386 ->setPickupPostalCode($pickupLocation->getPostalCode())
387 ->setRetailNetworkId($pickupLocation->getRetailNetworkId())
388 ->setPickupLocationCode($pickupLocation->getLocationCode());
389 }
390
391 /**
392 * Set the shipment options.
393 *
394 * @throws Exception
395 */
396 private function setShipmentOptions(): void
397 {
398 $this->consignment
399 ->setAgeCheck($this->orderSettings->hasAgeCheck())
400 ->setInsurance($this->orderSettings->getInsuranceAmount())
401 ->setOnlyRecipient($this->orderSettings->hasOnlyRecipient())
402 ->setReturn($this->orderSettings->hasReturnShipment())
403 ->setSignature($this->orderSettings->hasSignature())
404 ->setContents($this->getContents())
405 ->setInvoice($this->order->get_id());
406 }
407
408 /**
409 * Sets a customs declaration for the consignment if necessary.
410 *
411 * @throws \Exception
412 */
413 private function setCustomsDeclaration(): void
414 {
415 $shippingCountry = WCX_Order::get_prop($this->order, "shipping_country");
416
417 if (WCPN_Country_Codes::isWorldShipmentCountry($shippingCountry)) {
418 $this->setCustomItems();
419 }
420 }
421
422 /**
423 * Sets a customs declaration for the consignment if necessary.
424 *
425 * @throws \Exception
426 */
427 private function setPhysicalProperties(): void
428 {
429 $extraOptions = WCX_Order::get_meta($this->order, WCPOST_Admin::META_SHIPMENT_OPTIONS_EXTRA);
430 $packageType = $this->getPackageType();
431
432 if (! $extraOptions) {
433 return;
434 }
435
436 $orderWeight = (int) $this->order->get_meta(WCPOST_Admin::META_ORDER_WEIGHT);
437 $totalWeight = $this->getTotalWeight($orderWeight);
438 $weight = $extraOptions['weight'] ?? $this->orderSettings->getWeight();
439
440 if ((float) $orderWeight === $weight) {
441 $totalWeight = (new WCPN_Export())->calculatedKiloWeight($totalWeight * 1000);
442 }
443 $this->consignment->setPhysicalProperties(
444 [
445 "weight" => round($totalWeight)
446 ]
447 );
448 }
449
450 /**
451 * @throws Exception
452 */
453 private function setBaseData(): void
454 {
455 $this->consignment
456 ->setApiKey($this->apiKey)
457 ->setReferenceId((string) $this->order->get_id())
458 ->setPackageType($this->getPackageType())
459 ->setDeliveryDate($this->getDeliveryDate())
460 ->setDeliveryType($this->getDeliveryType())
461 ->setLabelDescription($this->getFormattedLabelDescription());
462 }
463 }
464