PluginProbe
Packeta / trunk
Packeta vtrunk
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
packeta / src / Packetery / Module / Order / PacketSubmitter.php

PacketSubmitter.php in Packeta trunk, at src/Packetery/Module/Order/PacketSubmitter.php

500 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare( strict_types=1 );
4
5 namespace Packetery\Module\Order;
6
7 use Packetery\Core\Api\InvalidRequestException;
8 use Packetery\Core\Api\Soap;
9 use Packetery\Core\Api\Soap\CreatePacketMapper;
10 use Packetery\Core\CoreHelper;
11 use Packetery\Core\Entity;
12 use Packetery\Core\Log;
13 use Packetery\Core\Validator;
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;
20 use WC_Order;
21
22 class PacketSubmitter {
23 const HOOK_PACKET_STATUS_SYNC = 'packetery_packet_status_sync_hook';
24
25 /**
26 * @var Soap\Client
27 */
28 private $soapApiClient;
29
30 /**
31 * @var Validator\Order
32 */
33 private $orderValidator;
34
35 /**
36 * @var Log\ILogger
37 */
38 private $logger;
39
40 /**
41 * @var Repository
42 */
43 private $orderRepository;
44
45 /**
46 * @var CreatePacketMapper
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
95 public function __construct(
96 Soap\Client $soapApiClient,
97 OrderValidatorFactory $orderValidatorFactory,
98 Log\ILogger $logger,
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
110 ) {
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;
125 }
126
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 /**
196 * Submits packet data to Packeta API.
197 *
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
203 */
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();
215
216 return $submissionResult;
217 }
218
219 $orderData = $wcOrder->get_data();
220 $shippingMethods = $wcOrder->get_shipping_methods();
221 $shippingMethod = reset( $shippingMethods );
222
223 $shippingMethodData = $shippingMethod->get_data();
224 $shippingMethodId = $shippingMethodData['method_id'];
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
299 try {
300 $createPacketData = $this->preparePacketData( $order );
301 } catch ( InvalidRequestException $e ) {
302 $record = new Log\Record();
303 $record->action = Log\Record::ACTION_PACKET_SENDING;
304 $record->status = Log\Record::STATUS_ERROR;
305 $record->title = __( 'Packet could not be created.', 'packeta' );
306 $record->params = [
307 'orderId' => $orderData['id'],
308 'errorMessages' => $e->getMessages(),
309 ];
310 $record->orderId = $order->getNumber();
311 $this->logger->add( $record );
312
313 $submissionResult->increaseLogsCount();
314 $submissionResult->increaseErrorsCount();
315
316 return $submissionResult;
317 }
318
319 $shouldScheduleConsignPasswordFetch = false;
320 $consignPasswordFetchImmediate = false;
321
322 $response = $this->soapApiClient->createPacket( $createPacketData );
323 if ( $response->hasFault() ) {
324 $record = new Log\Record();
325 $record->action = Log\Record::ACTION_PACKET_SENDING;
326 $record->status = Log\Record::STATUS_ERROR;
327 $record->title = __( 'Packet could not be created.', 'packeta' );
328 $record->params = [
329 'request' => $createPacketData,
330 'errorMessage' => $response->getErrorsAsString(),
331 ];
332 $record->orderId = $order->getNumber();
333 $errorMessage = $response->getErrorsAsString( false );
334
335 $submissionResult->increaseErrorsCount();
336 } else {
337 $order->setIsExported( true );
338 $order->setPacketId( $response->getId() );
339 $order->setPacketTrackingUrl( $this->coreHelper->getTrackingUrl( $response->getId() ) );
340
341 $record = new Log\Record();
342 $record->action = Log\Record::ACTION_PACKET_SENDING;
343 $record->status = Log\Record::STATUS_SUCCESS;
344 $record->title = __( 'Packet was successfully created.', 'packeta' );
345 $record->params = [
346 'request' => $createPacketData,
347 'packetId' => $response->getId(),
348 ];
349 $record->orderId = $order->getNumber();
350 $errorMessage = null;
351
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;
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 }
381 } else {
382 $submissionResult->increaseIgnoredCount();
383 }
384
385 return $submissionResult;
386 }
387
388 /**
389 * Prepares packet attributes.
390 *
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.
415 *
416 * @param array<string, int> $submissionResult Submission result.
417 * @param int|null $orderId Order ID.
418 *
419 * @return array<string, null|string>
420 */
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 }
433 }
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 }
468
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
497 );
498 }
499 }
500