PluginProbe
Packeta / 2.3.2
Packeta v2.3.2
2.3.2 2.3.1 trunk 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3.0 1.3.1 1.3.2 1.4 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 All 56 releases
← All changes | src/Packetery/Module/Order/PacketSubmitter.php +401 -94 1.42.3.2 View file →
@@ -1,10 +1,5 @@
1 1 <?php
2 -/**
3 - * Class PacketSubmitter
4 - *
5 - * @package Packetery\Api
6 - */
7 2
8 3 declare( strict_types=1 );
9 4
10 5 namespace Packetery\Module\Order;
@@ -9,94 +4,301 @@
9 4
10 5 namespace Packetery\Module\Order;
11 6
12 7 use Packetery\Core\Api\InvalidRequestException;
13 -use Packetery\Core\Api\Soap\Client;
14 -use Packetery\Core\Api\Soap\Request\CreatePacket;
8 +use Packetery\Core\Api\Soap;
9 +use Packetery\Core\Api\Soap\CreatePacketMapper;
10 +use Packetery\Core\CoreHelper;
15 11 use Packetery\Core\Entity;
16 12 use Packetery\Core\Log;
17 13 use Packetery\Core\Validator;
18 -use Packetery\Module\ShippingMethod;
14 +use Packetery\Module;
15 +use Packetery\Module\CustomsDeclaration;
16 +use Packetery\Module\MessageManager;
17 +use Packetery\Module\ModuleHelper;
18 +use Packetery\Module\Shipping\ShippingProvider;
19 +use Packetery\Nette\Http\Request;
19 20 use WC_Order;
20 21
21 -/**
22 - * Class PacketSubmitter
23 - *
24 - * @package Packetery\Api
25 - */
26 22 class PacketSubmitter {
23 + const HOOK_PACKET_STATUS_SYNC = 'packetery_packet_status_sync_hook';
27 24
28 25 /**
29 - * SOAP API Client.
30 - *
31 - * @var Client SOAP API Client.
26 + * @var Soap\Client
32 27 */
33 28 private $soapApiClient;
34 29
35 30 /**
36 - * Order validator.
37 - *
38 31 * @var Validator\Order
39 32 */
40 33 private $orderValidator;
41 34
42 35 /**
43 - * ILogger.
44 - *
45 36 * @var Log\ILogger
46 37 */
47 38 private $logger;
48 39
49 40 /**
50 - * Order repository.
51 - *
52 41 * @var Repository
53 42 */
54 43 private $orderRepository;
55 44
56 45 /**
57 - * OrderApi constructor.
58 - *
59 - * @param Client $soapApiClient SOAP API Client.
60 - * @param Validator\Order $orderValidator Order validator.
61 - * @param Log\ILogger $logger Logger.
62 - * @param Repository $orderRepository Order repository.
46 + * @var CreatePacketMapper
63 47 */
48 + private $createPacketMapper;
49 +
50 + /**
51 + * @var Request
52 + */
53 + private $request;
54 +
55 + /**
56 + * @var MessageManager
57 + */
58 + private $messageManager;
59 +
60 + /**
61 + * @var Module\Log\Page
62 + */
63 + private $logPage;
64 +
65 + /**
66 + * @var PacketActionsCommonLogic
67 + */
68 + private $commonLogic;
69 +
70 + /**
71 + * @var CustomsDeclaration\Repository
72 + */
73 + private $customsDeclarationRepository;
74 +
75 + /**
76 + * @var PacketSynchronizer
77 + */
78 + private $packetSynchronizer;
79 +
80 + /**
81 + * @var ModuleHelper
82 + */
83 + private $moduleHelper;
84 +
85 + /**
86 + * @var CoreHelper
87 + */
88 + private $coreHelper;
89 +
90 + /**
91 + * @var ConsignPasswordSynchronizer
92 + */
93 + private $consignPasswordSynchronizer;
94 +
64 95 public function __construct(
65 - Client $soapApiClient,
66 - Validator\Order $orderValidator,
96 + Soap\Client $soapApiClient,
97 + OrderValidatorFactory $orderValidatorFactory,
67 98 Log\ILogger $logger,
68 - Repository $orderRepository
99 + Repository $orderRepository,
100 + CreatePacketMapper $createPacketMapper,
101 + Request $request,
102 + MessageManager $messageManager,
103 + Module\Log\Page $logPage,
104 + PacketActionsCommonLogic $commonLogic,
105 + CustomsDeclaration\Repository $customsDeclarationRepository,
106 + PacketSynchronizer $packetSynchronizer,
107 + ModuleHelper $moduleHelper,
108 + CoreHelper $coreHelper,
109 + ConsignPasswordSynchronizer $consignPasswordSynchronizer
69 110 ) {
70 - $this->soapApiClient = $soapApiClient;
71 - $this->orderValidator = $orderValidator;
72 - $this->logger = $logger;
73 - $this->orderRepository = $orderRepository;
111 + $this->soapApiClient = $soapApiClient;
112 + $this->orderValidator = $orderValidatorFactory->create();
113 + $this->logger = $logger;
114 + $this->orderRepository = $orderRepository;
115 + $this->createPacketMapper = $createPacketMapper;
116 + $this->request = $request;
117 + $this->messageManager = $messageManager;
118 + $this->logPage = $logPage;
119 + $this->commonLogic = $commonLogic;
120 + $this->customsDeclarationRepository = $customsDeclarationRepository;
121 + $this->packetSynchronizer = $packetSynchronizer;
122 + $this->moduleHelper = $moduleHelper;
123 + $this->coreHelper = $coreHelper;
124 + $this->consignPasswordSynchronizer = $consignPasswordSynchronizer;
74 125 }
75 126
76 127 /**
128 + * Process action
129 + *
130 + * @return void
131 + */
132 + public function processAction(): void {
133 + $order = $this->commonLogic->getOrder();
134 + $redirectTo = $this->request->getQuery( PacketActionsCommonLogic::PARAM_REDIRECT_TO );
135 +
136 + if ( $order === null ) {
137 + $record = new Log\Record();
138 + $record->action = Log\Record::ACTION_PACKET_SENDING;
139 + $record->status = Log\Record::STATUS_ERROR;
140 + $record->orderId = null;
141 + $record->title = __( 'Packet submission error', 'packeta' );
142 + $record->params = [
143 + 'referer' => (string) $this->request->getReferer(),
144 + 'errorMessage' => 'Order not found',
145 + ];
146 +
147 + $this->logger->add( $record );
148 +
149 + $this->messageManager->flash_message( __( 'Order not found', 'packeta' ), MessageManager::TYPE_ERROR );
150 + $this->commonLogic->redirectTo( $redirectTo, $order );
151 +
152 + return;
153 + }
154 +
155 + $this->commonLogic->checkAction( PacketActionsCommonLogic::ACTION_SUBMIT_PACKET, $order );
156 +
157 + $submissionResult = $this->submitPacket(
158 + $this->orderRepository->getWcOrderById( (int) $order->getNumber() ),
159 + $order,
160 + true
161 + );
162 + $resultsCounter = $submissionResult->getCounter();
163 + $submissionResultMessages = $this->getTranslatedSubmissionMessages( $resultsCounter, (int) $order->getNumber() );
164 +
165 + if ( $resultsCounter['success'] > 0 ) {
166 + $this->messageManager->flashMessageObject(
167 + Module\Message::create()
168 + ->setText( $submissionResultMessages['success'] )
169 + ->setEscape( false )
170 + );
171 + }
172 +
173 + if ( $resultsCounter['ignored'] > 0 ) {
174 + $this->messageManager->flashMessageObject(
175 + Module\Message::create()
176 + ->setText( $submissionResultMessages['ignored'] )
177 + ->setEscape( false )
178 + ->setType( MessageManager::TYPE_INFO )
179 + );
180 + }
181 +
182 + if ( $resultsCounter['errors'] > 0 ) {
183 + $this->messageManager->flashMessageObject(
184 + Module\Message::create()
185 + ->setText( $submissionResultMessages['errors'] )
186 + ->setEscape( false )
187 + ->setType( MessageManager::TYPE_ERROR )
188 + );
189 + }
190 +
191 + $redirectTo = $this->request->getQuery( PacketActionsCommonLogic::PARAM_REDIRECT_TO );
192 + $this->commonLogic->redirectTo( $redirectTo, $order );
193 + }
194 +
195 + /**
77 196 * Submits packet data to Packeta API.
78 197 *
79 - * @param WC_Order $order WC order.
80 - * @param array $resultsCounter Array with results.
198 + * @param WC_Order $wcOrder WC order.
199 + * @param Entity\Order|null $order Order.
200 + * @param bool $immediatePacketStatusCheck Whether to sync status immediately.
201 + *
202 + * @return PacketSubmissionResult
81 203 */
82 - public function submitPacket( WC_Order $order, array &$resultsCounter ): void {
83 - $commonEntity = $this->orderRepository->getByWcOrder( $order );
84 - if ( null === $commonEntity ) {
85 - $resultsCounter['ignored'] ++;
204 + public function submitPacket(
205 + WC_Order $wcOrder,
206 + ?Entity\Order $order = null,
207 + bool $immediatePacketStatusCheck = false
208 + ): PacketSubmissionResult {
209 + $submissionResult = new PacketSubmissionResult();
210 + if ( $order === null ) {
211 + $order = $this->orderRepository->getByWcOrderWithValidCarrier( $wcOrder );
212 + }
213 + if ( $order === null ) {
214 + $submissionResult->increaseIgnoredCount();
86 215
87 - return;
216 + return $submissionResult;
88 217 }
89 218
90 - $orderData = $order->get_data();
91 - $shippingMethods = $order->get_shipping_methods();
219 + $orderData = $wcOrder->get_data();
220 + $shippingMethods = $wcOrder->get_shipping_methods();
92 221 $shippingMethod = reset( $shippingMethods );
93 222
94 223 $shippingMethodData = $shippingMethod->get_data();
95 224 $shippingMethodId = $shippingMethodData['method_id'];
96 - if ( ShippingMethod::PACKETERY_METHOD_ID === $shippingMethodId && ! $commonEntity->isExported() ) {
225 + if ( ShippingProvider::isPacketaMethod( $shippingMethodId ) && ! $order->isExported() ) {
226 + $customsDeclaration = $order->getCustomsDeclaration();
227 + if (
228 + $customsDeclaration !== null &&
229 + $customsDeclaration->getInvoiceFileId() === null &&
230 + $customsDeclaration->hasInvoiceFileContent()
231 + ) {
232 + $invoiceFileResponse = $this->soapApiClient->createStorageFile(
233 + new Soap\Request\CreateStorageFile(
234 + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
235 + base64_encode( $customsDeclaration->getInvoiceFile() ),
236 + sprintf( 'invoice_%s.pdf', $customsDeclaration->getId() )
237 + )
238 + );
239 +
240 + if ( $invoiceFileResponse->hasFault() ) {
241 + $record = new Log\Record();
242 + $record->action = Log\Record::ACTION_PACKET_SENDING;
243 + $record->status = Log\Record::STATUS_ERROR;
244 + $record->title = __( 'Packet invoice file could not be created.', 'packeta' );
245 + $record->params = [
246 + 'errorMessage' => $invoiceFileResponse->getFaultString(),
247 + ];
248 + $record->orderId = $order->getNumber();
249 + $this->logger->add( $record );
250 + $submissionResult->increaseLogsCount();
251 +
252 + $submissionResult->increaseErrorsCount();
253 + $order->updateApiErrorMessage( $invoiceFileResponse->getFaultString() );
254 + $this->orderRepository->save( $order );
255 +
256 + return $submissionResult;
257 + }
258 +
259 + $customsDeclaration->setInvoiceFileId( $invoiceFileResponse->getId() );
260 + $this->customsDeclarationRepository->save( $customsDeclaration );
261 + }
262 +
263 + if (
264 + $customsDeclaration !== null &&
265 + $customsDeclaration->getEadFileId() === null &&
266 + $customsDeclaration->hasEadFileContent()
267 + ) {
268 + $eadFileResponse = $this->soapApiClient->createStorageFile(
269 + new Soap\Request\CreateStorageFile(
270 + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
271 + base64_encode( $customsDeclaration->getEadFile() ),
272 + sprintf( 'ead_%s.pdf', $customsDeclaration->getId() )
273 + )
274 + );
275 +
276 + if ( $eadFileResponse->hasFault() ) {
277 + $record = new Log\Record();
278 + $record->action = Log\Record::ACTION_PACKET_SENDING;
279 + $record->status = Log\Record::STATUS_ERROR;
280 + $record->title = __( 'Packet ead file could not be created.', 'packeta' );
281 + $record->params = [
282 + 'errorMessage' => $eadFileResponse->getFaultString(),
283 + ];
284 + $record->orderId = $order->getNumber();
285 + $this->logger->add( $record );
286 + $submissionResult->increaseLogsCount();
287 +
288 + $submissionResult->increaseErrorsCount();
289 + $order->updateApiErrorMessage( $eadFileResponse->getFaultString() );
290 + $this->orderRepository->save( $order );
291 +
292 + return $submissionResult;
293 + }
294 +
295 + $customsDeclaration->setEadFileId( $eadFileResponse->getId() );
296 + $this->customsDeclarationRepository->save( $customsDeclaration );
297 + }
298 +
97 299 try {
98 - $createPacketRequest = $this->preparePacketRequest( $commonEntity );
300 + $createPacketData = $this->preparePacketData( $order );
99 301 } catch ( InvalidRequestException $e ) {
100 302 $record = new Log\Record();
101 303 $record->action = Log\Record::ACTION_PACKET_SENDING;
102 304 $record->status = Log\Record::STATUS_ERROR;
@@ -101,21 +303,24 @@
101 303 $record->action = Log\Record::ACTION_PACKET_SENDING;
102 304 $record->status = Log\Record::STATUS_ERROR;
103 305 $record->title = __( 'Packet could not be created.', 'packeta' );
104 306 $record->params = [
105 - 'orderId' => $orderData['id'],
106 - 'errorMessage' => $e->getMessage(),
307 + 'orderId' => $orderData['id'],
308 + 'errorMessages' => $e->getMessages(),
107 309 ];
108 - $record->orderId = $commonEntity->getNumber();
310 + $record->orderId = $order->getNumber();
109 311 $this->logger->add( $record );
110 312
111 - $resultsCounter['logs'] ++;
112 - $resultsCounter['errors'] ++;
313 + $submissionResult->increaseLogsCount();
314 + $submissionResult->increaseErrorsCount();
113 315
114 - return;
316 + return $submissionResult;
115 317 }
116 318
117 - $response = $this->soapApiClient->createPacket( $createPacketRequest );
319 + $shouldScheduleConsignPasswordFetch = false;
320 + $consignPasswordFetchImmediate = false;
321 +
322 + $response = $this->soapApiClient->createPacket( $createPacketData );
118 323 if ( $response->hasFault() ) {
119 324 $record = new Log\Record();
120 325 $record->action = Log\Record::ACTION_PACKET_SENDING;
121 326 $record->status = Log\Record::STATUS_ERROR;
@@ -120,39 +325,65 @@
120 325 $record->action = Log\Record::ACTION_PACKET_SENDING;
121 326 $record->status = Log\Record::STATUS_ERROR;
122 327 $record->title = __( 'Packet could not be created.', 'packeta' );
123 328 $record->params = [
124 - 'request' => $createPacketRequest->getSubmittableData(),
329 + 'request' => $createPacketData,
125 330 'errorMessage' => $response->getErrorsAsString(),
126 331 ];
127 - $record->orderId = $commonEntity->getNumber();
128 - $this->logger->add( $record );
332 + $record->orderId = $order->getNumber();
333 + $errorMessage = $response->getErrorsAsString( false );
129 334
130 - $resultsCounter['logs'] ++;
131 - $resultsCounter['errors'] ++;
335 + $submissionResult->increaseErrorsCount();
132 336 } else {
133 - $commonEntity->setIsExported( true );
134 - $commonEntity->setPacketId( (string) $response->getId() );
135 - $this->orderRepository->save( $commonEntity );
337 + $order->setIsExported( true );
338 + $order->setPacketId( $response->getId() );
339 + $order->setPacketTrackingUrl( $this->coreHelper->getTrackingUrl( $response->getId() ) );
136 340
137 - $resultsCounter['success'] ++;
138 -
139 341 $record = new Log\Record();
140 342 $record->action = Log\Record::ACTION_PACKET_SENDING;
141 343 $record->status = Log\Record::STATUS_SUCCESS;
142 - $record->title = __( 'Packet was sucessfully created.', 'packeta' );
344 + $record->title = __( 'Packet was successfully created.', 'packeta' );
143 345 $record->params = [
144 - 'request' => $createPacketRequest->getSubmittableData(),
346 + 'request' => $createPacketData,
145 347 'packetId' => $response->getId(),
146 348 ];
147 - $record->orderId = $commonEntity->getNumber();
148 - $this->logger->add( $record );
349 + $record->orderId = $order->getNumber();
350 + $errorMessage = null;
149 351
150 - $resultsCounter['logs'] ++;
352 + $submissionResult->increaseSuccessCount();
353 +
354 + $wcOrder->add_order_note(
355 + sprintf(
356 + // translators: %s represents a packet tracking link.
357 + __( 'Packeta: Packet %s has been created', 'packeta' ),
358 + $this->moduleHelper->createHtmlLink( $order->getPacketTrackingUrl(), $order->getPacketBarcode() )
359 + )
360 + );
361 + $wcOrder->save();
362 +
363 + if ( $immediatePacketStatusCheck || ! function_exists( 'as_enqueue_async_action' ) ) {
364 + $this->packetSynchronizer->syncStatus( $order );
365 + } else {
366 + as_enqueue_async_action( self::HOOK_PACKET_STATUS_SYNC, [ $order->getNumber() ] );
367 + }
368 +
369 + $shouldScheduleConsignPasswordFetch = true;
370 + $consignPasswordFetchImmediate = $immediatePacketStatusCheck;
151 371 }
372 +
373 + $submissionResult->increaseLogsCount();
374 + $this->logger->add( $record );
375 + $order->updateApiErrorMessage( $errorMessage );
376 + $this->orderRepository->save( $order );
377 +
378 + if ( $shouldScheduleConsignPasswordFetch ) {
379 + $this->consignPasswordSynchronizer->schedule( $order, $consignPasswordFetchImmediate );
380 + }
152 381 } else {
153 - $resultsCounter['ignored'] ++;
382 + $submissionResult->increaseIgnoredCount();
154 383 }
384 +
385 + return $submissionResult;
155 386 }
156 387
157 388 /**
158 389 * Prepares packet attributes.
@@ -157,36 +388,112 @@
157 388 /**
158 389 * Prepares packet attributes.
159 390 *
160 391 * @param Entity\Order $order Order entity.
392 + * @return array<string, string|null|bool>
393 + * @throws InvalidRequestException For the case request is not eligible to be sent to API.
394 + */
395 + private function preparePacketData( Entity\Order $order ): array {
396 + $validationErrors = $this->orderValidator->validate( $order );
397 + if ( count( $validationErrors ) > 0 ) {
398 + throw new InvalidRequestException( 'All required order attributes are not set.', $validationErrors );
399 + }
400 +
401 + $createPacketData = $this->createPacketMapper->fromOrderToArray( $order );
402 +
403 + /**
404 + * Allows to update CreatePacket request data.
405 + *
406 + * @since 1.4
407 + *
408 + * @param array $createPacketData CreatePacket request data.
409 + */
410 + return (array) apply_filters( 'packeta_create_packet', $createPacketData );
411 + }
412 +
413 + /**
414 + * Gets translated messages by submission result.
161 415 *
162 - * @return CreatePacket
163 - * @throws InvalidRequestException For the case request is not eligible to be sent to API.
416 + * @param array<string, int> $submissionResult Submission result.
417 + * @param int|null $orderId Order ID.
418 + *
419 + * @return array<string, null|string>
164 420 */
165 - private function preparePacketRequest( Entity\Order $order ): CreatePacket {
166 - /*
167 - TODO: extend validator to return specific errors.
168 - if ( ! $this->orderValidator->validate( $commonEntity ) ) {
169 - throw new InvalidRequestException( 'All required order attributes are not set.' );
421 + public function getTranslatedSubmissionMessages( array $submissionResult, ?int $orderId ): array {
422 + $success = null;
423 + if ( is_numeric( $submissionResult['success'] ) && $submissionResult['success'] > 0 ) {
424 + if ( $submissionResult['logs'] > 0 ) {
425 + $success = sprintf( // translators: 1: link start 2: link end.
426 + esc_html__( 'Shipments were submitted successfully. %1$sShow logs%2$s', 'packeta' ),
427 + '<a href="' . $this->logPage->createLogListUrl( $orderId ) . '">',
428 + '</a>'
429 + );
430 + } else {
431 + $success = esc_html__( 'Shipments were submitted successfully.', 'packeta' );
432 + }
170 433 }
171 - */
434 + $ignored = null;
435 + if ( is_numeric( $submissionResult['ignored'] ) && $submissionResult['ignored'] > 0 ) {
436 + if ( $submissionResult['logs'] > 0 ) {
437 + $ignored = sprintf( // translators: 1: total number of shipments 2: link start 3: link end.
438 + esc_html__( 'Some shipments (%1$s in total) were not submitted (these were submitted already or are not Packeta orders). %2$sShow logs%3$s', 'packeta' ),
439 + $submissionResult['ignored'],
440 + '<a href="' . $this->logPage->createLogListUrl( $orderId ) . '">',
441 + '</a>'
442 + );
443 + } else {
444 + $ignored = sprintf( // translators: %s is count.
445 + esc_html__( 'Some shipments (%s in total) were not submitted (these were submitted already or are not Packeta orders).', 'packeta' ),
446 + $submissionResult['ignored']
447 + );
448 + }
449 + }
450 + $errors = null;
451 + if ( is_numeric( $submissionResult['errors'] ) && $submissionResult['errors'] > 0 ) {
452 + if ( $submissionResult['logs'] > 0 ) {
453 + $errors = sprintf( // translators: 1: total number of shipments 2: link start 3: link end.
454 + esc_html__( 'Some shipments (%1$s in total) failed to be submitted to Packeta. %2$sShow logs%3$s', 'packeta' ),
455 + $submissionResult['errors'],
456 + '<a href="' . $this->logPage->createLogListUrl( $orderId ) . '">',
457 + '</a>'
458 + );
459 + } else {
460 + $errors = sprintf( // translators: %s is count.
461 + esc_html__( 'Some shipments (%s in total) failed to be submitted to Packeta.', 'packeta' ),
462 + $submissionResult['errors']
463 + );
464 + }
465 + } elseif ( isset( $submissionResult['errors'] ) ) {
466 + $errors = esc_html( (string) $submissionResult['errors'] );
467 + }
172 468
173 - return new CreatePacket(
174 - /**
175 - * Filters the input order for CreatePacket request.
176 - *
177 - * @since 1.4
178 - *
179 - * @param Entity\Order $order Packeta core order. DO NOT USE THIS STRICT TYPE IN YOUR METHOD SIGNATURE!
180 - */
181 - apply_filters(
182 - 'packeta_create_packet',
183 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
184 - unserialize(
185 - // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
186 - serialize( $order )
187 - )
188 - )
469 + if ( is_numeric( $submissionResult['statusUnchanged'] ) && $submissionResult['statusUnchanged'] > 0 ) {
470 + $errors = esc_html__( 'Some order statuses have not been automatically changed.', 'packeta' );
471 + }
472 +
473 + return [
474 + 'success' => $success,
475 + 'ignored' => $ignored,
476 + 'errors' => $errors,
477 + ];
478 + }
479 +
480 + /**
481 + * Registers action for cron.
482 + *
483 + * @return void
484 + */
485 + public function registerCronAction(): void {
486 + add_action(
487 + self::HOOK_PACKET_STATUS_SYNC,
488 + function ( string $orderId ): void {
489 + $order = $this->orderRepository->getByIdWithValidCarrier( (int) $orderId );
490 + if ( $order === null ) {
491 + return;
492 + }
493 + $this->packetSynchronizer->syncStatus( $order );
494 + },
495 + 10,
496 + 1
189 497 );
190 498 }
191 -
192 499 }