PluginProbe
Packeta / 2.0.9
Packeta v2.0.9
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 / LabelPrint.php

LabelPrint.php in Packeta 2.0.9, at src/Packetery/Module/Order/LabelPrint.php

601 lines 18.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class LabelPrint.
4 *
5 * @package Packetery\Order
6 */
7
8 declare( strict_types=1 );
9
10 namespace Packetery\Module\Order;
11
12 use Packetery\Core\Api\Soap\Client;
13 use Packetery\Core\Api\Soap\ILabelResponse;
14 use Packetery\Core\Api\Soap\Request;
15 use Packetery\Core\Api\Soap\Response;
16 use Packetery\Core\Entity\Order;
17 use Packetery\Core\Log;
18 use Packetery\Latte\Engine;
19 use Packetery\Module\FormFactory;
20 use Packetery\Module\MessageManager;
21 use Packetery\Module\ModuleHelper;
22 use Packetery\Module\Options\OptionsProvider;
23 use Packetery\Module\Plugin;
24 use Packetery\Nette\Forms\Form;
25 use Packetery\Nette\Http;
26
27 /**
28 * Class LabelPrint.
29 *
30 * @package Packetery\Order
31 */
32 class LabelPrint {
33 public const ACTION_PACKETA_LABELS = 'print_packeta_labels';
34 public const ACTION_CARRIER_LABELS = 'print_carrier_labels';
35 public const LABEL_TYPE_PARAM = 'label_type';
36 public const MENU_SLUG = 'label-print';
37
38 /**
39 * PacketeryLatte Engine.
40 *
41 * @var Engine PacketeryLatte engine.
42 */
43 private $latteEngine;
44
45 /**
46 * Options Provider
47 *
48 * @var OptionsProvider
49 */
50 private $optionsProvider;
51
52 /**
53 * Form factory.
54 *
55 * @var FormFactory Form factory.
56 */
57 private $formFactory;
58
59 /**
60 * Http Request.
61 *
62 * @var Http\Request
63 */
64 private $httpRequest;
65
66 /**
67 * SOAP API Client.
68 *
69 * @var Client SOAP API Client.
70 */
71 private $soapApiClient;
72
73 /**
74 * Message Manager.
75 *
76 * @var MessageManager
77 */
78 private $messageManager;
79
80 /**
81 * Logger.
82 *
83 * @var Log\ILogger
84 */
85 private $logger;
86
87 /**
88 * Order repository.
89 *
90 * @var Repository
91 */
92 private $orderRepository;
93
94 /**
95 * Packet actions common logic.
96 *
97 * @var PacketActionsCommonLogic
98 */
99 private $packetActionsCommonLogic;
100
101 /**
102 * ModuleHelper.
103 *
104 * @var ModuleHelper
105 */
106 private $moduleHelper;
107
108 /**
109 * LabelPrint constructor.
110 *
111 * @param Engine $latteEngine Latte Engine.
112 * @param OptionsProvider $optionsProvider Options provider.
113 * @param FormFactory $formFactory Form factory.
114 * @param Http\Request $httpRequest Http Request.
115 * @param Client $soapApiClient SOAP API Client.
116 * @param MessageManager $messageManager Message Manager.
117 * @param Log\ILogger $logger Logger.
118 * @param Repository $orderRepository Order repository.
119 * @param PacketActionsCommonLogic $packetActionsCommonLogic Packet actions common logic.
120 * @param ModuleHelper $moduleHelper ModuleHelper.
121 */
122 public function __construct(
123 Engine $latteEngine,
124 OptionsProvider $optionsProvider,
125 FormFactory $formFactory,
126 Http\Request $httpRequest,
127 Client $soapApiClient,
128 MessageManager $messageManager,
129 Log\ILogger $logger,
130 Repository $orderRepository,
131 PacketActionsCommonLogic $packetActionsCommonLogic,
132 ModuleHelper $moduleHelper
133 ) {
134 $this->latteEngine = $latteEngine;
135 $this->optionsProvider = $optionsProvider;
136 $this->formFactory = $formFactory;
137 $this->httpRequest = $httpRequest;
138 $this->soapApiClient = $soapApiClient;
139 $this->messageManager = $messageManager;
140 $this->logger = $logger;
141 $this->orderRepository = $orderRepository;
142 $this->packetActionsCommonLogic = $packetActionsCommonLogic;
143 $this->moduleHelper = $moduleHelper;
144 }
145
146 /**
147 * Generates id of transient to store order ids.
148 *
149 * @return string
150 */
151 public static function getOrderIdsTransientName(): string {
152 return 'packetery_label_print_order_ids_' . wp_get_session_token();
153 }
154
155 /**
156 * Generates id of transient to store back link.
157 *
158 * @return string
159 */
160 public static function getBackLinkTransientName(): string {
161 return 'packetery_label_print_back_link_' . wp_get_session_token();
162 }
163
164 /**
165 * Gets label type.
166 *
167 * @param Order $order Order.
168 *
169 * @return string
170 */
171 public function getLabelFormatByOrder( Order $order ): string {
172 if ( $order->isExternalCarrier() ) {
173 return $this->optionsProvider->get_carrier_label_format();
174 }
175
176 return $this->optionsProvider->get_packeta_label_format();
177 }
178
179 /**
180 * Prepares form and renders template.
181 */
182 public function render(): void {
183 $form = $this->createForm( $this->optionsProvider->getLabelMaxOffset( $this->getLabelFormat() ) );
184
185 $count = 0;
186 $isCarrierLabels = ( $this->httpRequest->getQuery( self::LABEL_TYPE_PARAM ) === self::ACTION_CARRIER_LABELS );
187
188 $orderIdsTransient = $this->getOrderIdsTransient();
189 if ( $orderIdsTransient !== null ) {
190 $orderIds = $this->getPacketIdsFromTransient( $orderIdsTransient, $isCarrierLabels );
191 $count = count( $orderIds );
192 } else {
193 $this->messageManager->flash_message( __( 'No orders were selected', 'packeta' ), MessageManager::TYPE_INFO, MessageManager::RENDERER_PACKETERY, self::MENU_SLUG );
194 }
195
196 $this->latteEngine->render(
197 PACKETERY_PLUGIN_DIR . '/template/order/label-print.latte',
198 [
199 'form' => $form,
200 'count' => $count,
201 'backLink' => get_transient( self::getBackLinkTransientName() ),
202 'flashMessages' => $this->messageManager->renderToString( MessageManager::RENDERER_PACKETERY, self::MENU_SLUG ),
203 'translations' => [
204 'packeta' => __( 'Packeta', 'packeta' ),
205 'labelPrinting' => __( 'Print labels', 'packeta' ),
206 // translators: %s is count.
207 'numberOfLabelsToPrint' => __( 'Number of labels to print: %s', 'packeta' ),
208
209 'back' => __( 'Back', 'packeta' ),
210 'printLabels' => __( 'Print labels', 'packeta' ),
211 ],
212 ]
213 );
214 }
215
216 /**
217 * Gets label format for current job.
218 *
219 * @return string
220 */
221 private function getLabelFormat(): string {
222 $packetaLabelFormat = $this->optionsProvider->get_packeta_label_format();
223 $carrierLabelFormat = $this->optionsProvider->get_carrier_label_format();
224
225 return ( $this->httpRequest->getQuery( self::LABEL_TYPE_PARAM ) === self::ACTION_CARRIER_LABELS ? $carrierLabelFormat : $packetaLabelFormat );
226 }
227
228 /**
229 * Outputs pdf.
230 */
231 public function outputLabelsPdf(): void {
232 if ( $this->httpRequest->getQuery( 'page' ) !== self::MENU_SLUG ) {
233 return;
234 }
235
236 $fallbackToPacketaLabel = false;
237 $isCarrierLabels = ( $this->httpRequest->getQuery( self::LABEL_TYPE_PARAM ) === self::ACTION_CARRIER_LABELS );
238 $idParam = $this->httpRequest->getQuery( 'id' ) !== null ? (int) $this->httpRequest->getQuery( 'id' ) : null;
239 $packetIdParam = $this->httpRequest->getQuery( 'packet_id' );
240 if ( $idParam !== null && $packetIdParam !== null ) {
241 $fallbackToPacketaLabel = true;
242 $packetIds = [ $idParam => $packetIdParam ];
243 } else {
244 $orderIdsTransient = $this->getOrderIdsTransient();
245 if ( $orderIdsTransient === null ) {
246 return;
247 }
248
249 $packetIds = $this->getPacketIdsFromTransient( $orderIdsTransient, $isCarrierLabels );
250 }
251 if ( count( $packetIds ) === 0 ) {
252 $this->messageManager->flash_message( __( 'No suitable orders were selected', 'packeta' ), 'info' );
253 $this->packetActionsCommonLogic->redirectTo( PacketActionsCommonLogic::REDIRECT_TO_ORDER_GRID );
254
255 return;
256 }
257
258 $maxOffset = $this->optionsProvider->getLabelMaxOffset( $this->getLabelFormat() );
259 $form = $this->createForm( $maxOffset );
260 $offsetParam = $this->httpRequest->getQuery( 'offset' );
261 if ( $maxOffset === 0 ) {
262 $offset = 0;
263 } elseif ( $offsetParam !== null ) {
264 $offset = (int) $offsetParam;
265 } elseif ( $form->isSubmitted() ) {
266 $data = $form->getValues( 'array' );
267 $offset = $data['offset'];
268 } else {
269 return;
270 }
271
272 if ( $isCarrierLabels ) {
273 $response = $this->requestCarrierLabels( $offset, $packetIds );
274 if ( $fallbackToPacketaLabel && $response->hasFault() ) {
275 $response = $this->requestPacketaLabels( $offset, $packetIds );
276 }
277 } else {
278 $response = $this->requestPacketaLabels( $offset, $packetIds );
279 }
280 if ( $response->hasFault() ) {
281 $message = __( 'Label printing failed, you can find more information in the Packeta log.', 'packeta' );
282 $this->messageManager->flash_message( $message, MessageManager::TYPE_ERROR );
283
284 $redirectTo = $this->httpRequest->getQuery( PacketActionsCommonLogic::PARAM_REDIRECT_TO );
285 if ( $redirectTo === PacketActionsCommonLogic::REDIRECT_TO_ORDER_DETAIL && $idParam !== null ) {
286 $this->packetActionsCommonLogic->redirectTo( $redirectTo, $this->orderRepository->findById( $idParam ) );
287 }
288 $this->packetActionsCommonLogic->redirectTo( PacketActionsCommonLogic::REDIRECT_TO_ORDER_GRID );
289
290 return;
291 }
292
293 foreach ( $packetIds as $orderId => $packetId ) {
294 $this->addLabelCreationInfoToWcOrderNote( $orderId, $packetId, $response );
295 }
296
297 header( 'Content-Type: application/pdf' );
298 header( 'Content-Transfer-Encoding: Binary' );
299 header( 'Content-Length: ' . strlen( $response->getPdfContents() ) );
300 header( 'Content-Disposition: attachment; filename="' . $this->getFilename() . '"' );
301 // @codingStandardsIgnoreStart
302 echo $response->getPdfContents();
303 // @codingStandardsIgnoreEnd
304 exit;
305 }
306
307 /**
308 * Creates offset setting form.
309 *
310 * @param int $maxOffset Maximal offset.
311 * @param string|null $name Form name.
312 *
313 * @return Form
314 */
315 public function createForm( int $maxOffset, ?string $name = null ): Form {
316 $form = $this->formFactory->create( $name );
317
318 $availableOffsets = [];
319 for ( $i = 0; $i <= $maxOffset; $i++ ) {
320 $availableOffsets[ $i ] = ( $i === 0 ?
321 __( "don't skip any field on a print sheet", 'packeta' ) :
322 // translators: %s is offset.
323 sprintf( __( 'skip %s fields on first sheet', 'packeta' ), $i )
324 );
325 }
326 $form->addSelect(
327 'offset',
328 __( 'Skip fields', 'packeta' ),
329 $availableOffsets
330 )->checkDefaultValue( false );
331
332 return $form;
333 }
334
335 /**
336 * Registers submenu item.
337 */
338 public function register(): void {
339 add_submenu_page(
340 \Packetery\Module\Options\Page::SLUG,
341 __( 'Print labels', 'packeta' ),
342 __( 'Print labels', 'packeta' ),
343 'manage_woocommerce',
344 self::MENU_SLUG,
345 array(
346 $this,
347 'render',
348 ),
349 20
350 );
351 }
352
353 /**
354 * Hides submenu item.
355 */
356 public function hideFromMenus(): void {
357 Plugin::hideSubmenuItem( self::MENU_SLUG );
358 }
359
360 /**
361 * Prepares labels.
362 *
363 * @param int $offset Offset value.
364 * @param string[] $packetIds Packet ids.
365 *
366 * @return Response\PacketsLabelsPdf
367 */
368 private function requestPacketaLabels( int $offset, array $packetIds ): Response\PacketsLabelsPdf {
369 $request = new Request\PacketsLabelsPdf( array_values( $packetIds ), $this->getLabelFormat(), $offset );
370 $response = $this->soapApiClient->packetsLabelsPdf( $request );
371 // TODO: is possible to merge following part of requestPacketaLabels and requestCarrierLabels?
372
373 foreach ( $packetIds as $orderId => $packetId ) {
374 $record = new Log\Record();
375 $record->action = Log\Record::ACTION_LABEL_PRINT;
376 $record->orderId = $orderId;
377 $order = $this->orderRepository->getByIdWithValidCarrier( $orderId );
378
379 if ( ! $response->hasFault() ) {
380 if ( $order !== null ) {
381 $order->setIsLabelPrinted( true );
382 }
383
384 $record->status = Log\Record::STATUS_SUCCESS;
385 $record->title = __( 'Label has been printed successfully.', 'packeta' );
386 } else {
387 $record->status = Log\Record::STATUS_ERROR;
388 $record->title = __( 'Label could not be printed.', 'packeta' );
389 $record->params = [
390 'packetId' => $packetId,
391 'isPacketIdInvalid' => $response->hasInvalidPacketId( (string) $packetId ),
392 'request' => [
393 'packetIds' => $request->getPacketIds(),
394 'format' => $request->getFormat(),
395 'offset' => $request->getOffset(),
396 ],
397 'errorMessage' => $response->getFaultString(),
398 ];
399 }
400
401 $this->logger->add( $record );
402
403 if ( $order !== null ) {
404 $order->updateApiErrorMessage( $response->getFaultString() );
405 $this->orderRepository->save( $order );
406 }
407 }
408
409 return $response;
410 }
411
412 /**
413 * Prepares carrier labels.
414 *
415 * @param int $offset Offset value.
416 * @param string[] $packetIds Packet ids.
417 *
418 * @return Response\PacketsCourierLabelsPdf
419 */
420 private function requestCarrierLabels( int $offset, array $packetIds ): Response\PacketsCourierLabelsPdf {
421 $packetIdsWithCourierNumbers = $this->getPacketIdsWithCourierNumbers( $packetIds );
422 $request = new Request\PacketsCourierLabelsPdf( array_values( $packetIdsWithCourierNumbers ), $this->getLabelFormat(), $offset );
423 $response = $this->soapApiClient->packetsCarrierLabelsPdf( $request );
424
425 foreach ( $packetIdsWithCourierNumbers as $orderId => $pairItem ) {
426 $record = new Log\Record();
427 $record->action = Log\Record::ACTION_CARRIER_LABEL_PRINT;
428 $record->orderId = $orderId;
429 $order = $this->orderRepository->getByIdWithValidCarrier( $orderId );
430
431 if ( ! $response->hasFault() ) {
432 if ( $order !== null ) {
433 $order->setIsLabelPrinted( true );
434 $order->setCarrierNumber( $pairItem['courierNumber'] );
435 }
436
437 $record->status = Log\Record::STATUS_SUCCESS;
438 $record->title = __( 'Carrier label has been printed successfully.', 'packeta' );
439 } else {
440 $record->status = Log\Record::STATUS_ERROR;
441 $record->title = __( 'Carrier label could not be printed.', 'packeta' );
442 $record->params = [
443 'packetId' => $pairItem['packetId'],
444 'courierNumber' => $pairItem['courierNumber'],
445 'isPacketIdInvalid' => $response->hasInvalidPacketId( (string) $pairItem['packetId'] ),
446 'isCourierNumberInvalid' => $response->hasInvalidCourierNumber( (string) $pairItem['courierNumber'] ),
447 'request' => [
448 'packetIdsWithCourierNumbers' => $request->getPacketIdsWithCourierNumbers(),
449 'format' => $request->getFormat(),
450 'offset' => $request->getOffset(),
451 ],
452 'errorMessage' => $response->getFaultString(),
453 ];
454 }
455 $this->logger->add( $record );
456
457 if ( $order !== null ) {
458 $order->updateApiErrorMessage( $response->getFaultString() );
459 $this->orderRepository->save( $order );
460 }
461 }
462
463 return $response;
464 }
465
466 /**
467 * Gets filename for label pdf.
468 *
469 * @return string
470 */
471 private function getFilename(): string {
472 return 'packeta_labels_' . strtolower( str_replace( ' ', '_', $this->getLabelFormat() ) ) . '.pdf';
473 }
474
475 /**
476 * Gets saved packet ids.
477 *
478 * @param string[] $orderIds Order IDs from transient.
479 * @param bool $isCarrierLabels Are carrier labels requested?
480 *
481 * @return string[]
482 */
483 private function getPacketIdsFromTransient( array $orderIds, bool $isCarrierLabels ): array {
484 $orders = $this->orderRepository->getByIds( $orderIds );
485 $packetIds = [];
486 foreach ( $orders as $order ) {
487 if ( $order->getPacketId() === null ) {
488 continue;
489 }
490 if ( ! $isCarrierLabels || $order->isExternalCarrier() ) {
491 $packetIds[ $order->getNumber() ] = $order->getPacketId();
492 }
493 }
494
495 return $packetIds;
496 }
497
498 /**
499 * Gets order IDs transient.
500 *
501 * @return string[]|null
502 */
503 private function getOrderIdsTransient(): ?array {
504 $orderIds = get_transient( self::getOrderIdsTransientName() );
505 if ( is_array( $orderIds ) ) {
506 return $orderIds;
507 }
508
509 return null;
510 }
511
512 /**
513 * Gets carrier packet numbers from API.
514 *
515 * @param string[] $packetIds List of packet ids.
516 *
517 * @return array<int, array{packetId: string, courierNumber: string}>
518 */
519 private function getPacketIdsWithCourierNumbers( array $packetIds ): array {
520 $pairs = [];
521 foreach ( $packetIds as $orderId => $packetId ) {
522 $request = new Request\PacketCourierNumber( $packetId );
523 $response = $this->soapApiClient->packetCourierNumber( $request );
524 if ( $response->hasFault() ) {
525 if ( $response->hasWrongPassword() ) {
526 $this->messageManager->flash_message( __( 'Please set a proper API password.', 'packeta' ), MessageManager::TYPE_ERROR );
527
528 return [];
529 }
530
531 $record = new Log\Record();
532 $record->action = Log\Record::ACTION_CARRIER_NUMBER_RETRIEVING;
533 $record->status = Log\Record::STATUS_ERROR;
534 $record->title = __( 'Carrier number could not be retrieved.', 'packeta' );
535 $record->params = [
536 'packetId' => $request->getPacketId(),
537 'errorMessage' => $response->getFaultString(),
538 ];
539 $record->orderId = $orderId;
540 $this->logger->add( $record );
541 $order = $this->orderRepository->getByIdWithValidCarrier( $orderId );
542 if ( $order !== null ) {
543 $order->updateApiErrorMessage( $response->getFaultString() );
544 $this->orderRepository->save( $order );
545 }
546
547 continue;
548 }
549 $pairs[ $orderId ] = [
550 'packetId' => $packetId,
551 'courierNumber' => $response->getNumber(),
552 ];
553 }
554
555 return $pairs;
556 }
557
558 /**
559 * Saves information about the creation of the label in the order note.
560 *
561 * @param int $orderId Order id.
562 * @param string $packetId Packet.
563 * @param ILabelResponse $response Response.
564 *
565 * @return void
566 */
567 private function addLabelCreationInfoToWcOrderNote( int $orderId, string $packetId, ILabelResponse $response ): void {
568 $order = $this->orderRepository->findById( $orderId );
569 $wcOrder = $this->orderRepository->getWcOrderById( $orderId );
570 if ( $wcOrder === null || $order === null ) {
571 return;
572 }
573
574 $linkText = $order->getPacketBarcode();
575 $trackingUrl = $order->getPacketTrackingUrl();
576 if ( $response instanceof Response\PacketsLabelsPdf ) {
577 if ( $order->isPacketClaim( $packetId ) ) {
578 // translators: %s represents a packet tracking link.
579 $message = __( 'Packeta: Label for packet claim %s has been created', 'packeta' );
580 $linkText = $order->getPacketClaimBarcode();
581 $trackingUrl = $order->getPacketClaimTrackingUrl();
582 } else {
583 // translators: %s represents a packet tracking link.
584 $message = __( 'Packeta: Label for packet %s has been created', 'packeta' );
585 }
586 } else {
587 // Response is of type Response\PacketsCourierLabelsPdf.
588 // translators: %s represents a packet tracking link.
589 $message = __( 'Packeta: Carrier label for packet %s has been created', 'packeta' );
590 }
591
592 $wcOrder->add_order_note(
593 sprintf(
594 $message,
595 $this->moduleHelper->createHtmlLink( $trackingUrl, $linkText )
596 )
597 );
598 $wcOrder->save();
599 }
600 }
601