vendor/shopware/core/Content/Flow/Dispatching/Action/GenerateDocumentAction.php line 101

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Flow\Dispatching\Action;
  3. use Doctrine\DBAL\Connection;
  4. use Psr\Log\LoggerInterface;
  5. use Shopware\Core\Checkout\Cart\LineItem\LineItem;
  6. use Shopware\Core\Checkout\Document\DocumentConfigurationFactory;
  7. use Shopware\Core\Checkout\Document\DocumentGenerator\CreditNoteGenerator;
  8. use Shopware\Core\Checkout\Document\DocumentGenerator\DeliveryNoteGenerator;
  9. use Shopware\Core\Checkout\Document\DocumentGenerator\InvoiceGenerator;
  10. use Shopware\Core\Checkout\Document\DocumentGenerator\StornoGenerator;
  11. use Shopware\Core\Checkout\Document\DocumentService;
  12. use Shopware\Core\Checkout\Document\FileGenerator\FileTypes;
  13. use Shopware\Core\Checkout\Document\Service\DocumentGenerator;
  14. use Shopware\Core\Checkout\Document\Struct\DocumentGenerateOperation;
  15. use Shopware\Core\Content\Flow\Dispatching\DelayableAction;
  16. use Shopware\Core\Content\Flow\Dispatching\StorableFlow;
  17. use Shopware\Core\Content\Flow\Exception\GenerateDocumentActionException;
  18. use Shopware\Core\Defaults;
  19. use Shopware\Core\Framework\Context;
  20. use Shopware\Core\Framework\Event\FlowEvent;
  21. use Shopware\Core\Framework\Event\MailAware;
  22. use Shopware\Core\Framework\Event\OrderAware;
  23. use Shopware\Core\Framework\Event\SalesChannelAware;
  24. use Shopware\Core\Framework\Feature;
  25. use Shopware\Core\System\NumberRange\ValueGenerator\NumberRangeValueGeneratorInterface;
  26. /**
  27.  * @deprecated tag:v6.5.0 - reason:remove-subscriber - FlowActions won't be executed over the event system anymore,
  28.  * therefore the actions won't implement the EventSubscriberInterface anymore.
  29.  */
  30. class GenerateDocumentAction extends FlowAction implements DelayableAction
  31. {
  32.     /**
  33.      * @deprecated tag:v6.5.0 - Property $documentService will be removed due to unused
  34.      */
  35.     protected DocumentService $documentService;
  36.     /**
  37.      * @deprecated tag:v6.5.0 - Property $connection will be removed due to unused
  38.      */
  39.     protected Connection $connection;
  40.     private DocumentGenerator $documentGenerator;
  41.     /**
  42.      * @deprecated tag:v6.5.0 - Property $connection will be removed due to unused
  43.      */
  44.     private NumberRangeValueGeneratorInterface $valueGenerator;
  45.     private LoggerInterface $logger;
  46.     /**
  47.      * @internal
  48.      */
  49.     public function __construct(
  50.         DocumentService $documentService,
  51.         DocumentGenerator $documentGenerator,
  52.         NumberRangeValueGeneratorInterface $valueGenerator,
  53.         Connection $connection,
  54.         LoggerInterface $logger
  55.     ) {
  56.         $this->documentService $documentService;
  57.         $this->documentGenerator $documentGenerator;
  58.         $this->connection $connection;
  59.         $this->valueGenerator $valueGenerator;
  60.         $this->logger $logger;
  61.     }
  62.     public static function getName(): string
  63.     {
  64.         return 'action.generate.document';
  65.     }
  66.     /**
  67.      * @deprecated tag:v6.5.0 - reason:remove-subscriber - Will be removed
  68.      */
  69.     public static function getSubscribedEvents(): array
  70.     {
  71.         if (Feature::isActive('v6.5.0.0')) {
  72.             return [];
  73.         }
  74.         return [
  75.             self::getName() => 'handle',
  76.         ];
  77.     }
  78.     /**
  79.      * @return array<int, string>
  80.      */
  81.     public function requirements(): array
  82.     {
  83.         return [OrderAware::class];
  84.     }
  85.     /**
  86.      * @deprecated tag:v6.5.0 Will be removed, implement handleFlow instead
  87.      */
  88.     public function handle(FlowEvent $event): void
  89.     {
  90.         Feature::triggerDeprecationOrThrow(
  91.             'v6.5.0.0',
  92.             Feature::deprecatedMethodMessage(__CLASS____METHOD__'v6.5.0.0')
  93.         );
  94.         $baseEvent $event->getEvent();
  95.         if (!$baseEvent instanceof OrderAware || !$baseEvent instanceof SalesChannelAware) {
  96.             return;
  97.         }
  98.         $this->generate($baseEvent->getContext(), $event->getConfig(), $baseEvent->getOrderId(), $baseEvent->getSalesChannelId());
  99.     }
  100.     public function handleFlow(StorableFlow $flow): void
  101.     {
  102.         if (!$flow->hasStore(OrderAware::ORDER_ID) || !$flow->hasStore(MailAware::SALES_CHANNEL_ID)) {
  103.             return;
  104.         }
  105.         $this->generate($flow->getContext(), $flow->getConfig(), $flow->getStore(OrderAware::ORDER_ID), $flow->getStore(MailAware::SALES_CHANNEL_ID));
  106.     }
  107.     /**
  108.      * @param array<string, mixed> $eventConfig
  109.      */
  110.     private function generate(Context $context, array $eventConfigstring $orderIdstring $salesChannelId): void
  111.     {
  112.         if (\array_key_exists('documentType'$eventConfig)) {
  113.             $this->generateDocument($eventConfig$context$orderId$salesChannelId);
  114.             return;
  115.         }
  116.         $documentsConfig $eventConfig['documentTypes'];
  117.         if (!$documentsConfig) {
  118.             return;
  119.         }
  120.         // Invoice document should be created first
  121.         foreach ($documentsConfig as $index => $config) {
  122.             if ($config['documentType'] === InvoiceGenerator::INVOICE) {
  123.                 $this->generateDocument($config$context$orderId$salesChannelId);
  124.                 unset($documentsConfig[$index]);
  125.                 break;
  126.             }
  127.         }
  128.         foreach ($documentsConfig as $config) {
  129.             $this->generateDocument($config$context$orderId$salesChannelId);
  130.         }
  131.     }
  132.     /**
  133.      * @param array<string, mixed> $eventConfig
  134.      */
  135.     private function generateDocument(array $eventConfigContext $contextstring $orderIdstring $salesChannelId): void
  136.     {
  137.         $documentType $eventConfig['documentType'];
  138.         $documentRangerType $eventConfig['documentRangerType'];
  139.         if (!$documentType || !$documentRangerType) {
  140.             return;
  141.         }
  142.         if (Feature::isActive('v6.5.0.0')) {
  143.             $fileType $eventConfig['fileType'] ?? FileTypes::PDF;
  144.             $config $eventConfig['config'] ?? [];
  145.             $static $eventConfig['static'] ?? false;
  146.             $operation = new DocumentGenerateOperation($orderId$fileType$confignull$static);
  147.             $result $this->documentGenerator->generate($documentType, [$orderId => $operation], $context);
  148.             if (!empty($result->getErrors())) {
  149.                 foreach ($result->getErrors() as $error) {
  150.                     $this->logger->error($error->getMessage());
  151.                 }
  152.             }
  153.             return;
  154.         }
  155.         $documentNumber $this->valueGenerator->getValue(
  156.             $documentRangerType,
  157.             $context,
  158.             $salesChannelId
  159.         );
  160.         $now = (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT);
  161.         $eventConfig['documentNumber'] = $documentNumber;
  162.         $eventConfig['documentDate'] = $now;
  163.         $customConfig $this->getEventCustomConfig(
  164.             $documentType,
  165.             $documentNumber,
  166.             $now,
  167.             $orderId
  168.         );
  169.         $eventConfig['custom'] = $customConfig;
  170.         $documentConfig DocumentConfigurationFactory::createConfiguration($eventConfig);
  171.         $this->documentService->create(
  172.             $orderId,
  173.             $documentType,
  174.             $eventConfig['fileType'] ?? FileTypes::PDF,
  175.             $documentConfig,
  176.             $context,
  177.             $customConfig['referencedInvoiceId'] ?? null,
  178.             $eventConfig['static'] ?? false
  179.         );
  180.     }
  181.     /**
  182.      * @return array<string, mixed>
  183.      */
  184.     private function getEventCustomConfig(string $documentTypestring $documentNumberstring $nowstring $orderId): array
  185.     {
  186.         switch ($documentType) {
  187.             case InvoiceGenerator::INVOICE:
  188.                 return ['invoiceNumber' => $documentNumber];
  189.             case DeliveryNoteGenerator::DELIVERY_NOTE:
  190.                 return [
  191.                     'deliveryNoteNumber' => $documentNumber,
  192.                     'deliveryDate' => $now,
  193.                     'deliveryNoteDate' => $now,
  194.                 ];
  195.             case StornoGenerator::STORNO:
  196.             case CreditNoteGenerator::CREDIT_NOTE:
  197.                 return $this->getConfigWithReferenceDoc($documentType$documentNumber$orderId);
  198.             default:
  199.                 return [];
  200.         }
  201.     }
  202.     /**
  203.      * @throws \Doctrine\DBAL\Exception
  204.      *
  205.      * @return array<string, mixed>
  206.      */
  207.     private function getConfigWithReferenceDoc(string $documentTypestring $documentNumberstring $orderId): array
  208.     {
  209.         $referencedInvoiceDocument $this->connection->fetchAssociative(
  210.             'SELECT LOWER (HEX(`document`.`id`)) as `id` , `document`.`config` as `config`
  211.                     FROM `document` JOIN `document_type` ON `document`.`document_type_id` = `document_type`.`id`
  212.                     WHERE `document_type`.`technical_name` = :techName AND hex(`document`.`order_id`) = :orderId
  213.                     ORDER BY `document`.`created_at` DESC LIMIT 1',
  214.             [
  215.                 'techName' => InvoiceGenerator::INVOICE,
  216.                 'orderId' => $orderId,
  217.             ]
  218.         );
  219.         if (empty($referencedInvoiceDocument)) {
  220.             throw new GenerateDocumentActionException('Cannot generate ' $documentType ' document because no invoice document exists. OrderId: ' $orderId);
  221.         }
  222.         if ($documentType === CreditNoteGenerator::CREDIT_NOTE && !$this->hasCreditItem($orderId)) {
  223.             throw new GenerateDocumentActionException('Cannot generate the credit note document because no credit items exist. OrderId: ' $orderId);
  224.         }
  225.         $documentRefer json_decode($referencedInvoiceDocument['config'], true);
  226.         $documentNumberRefer $documentRefer['custom']['invoiceNumber'];
  227.         return array_filter([
  228.             'invoiceNumber' => $documentNumberRefer,
  229.             'stornoNumber' => $documentType === StornoGenerator::STORNO $documentNumber null,
  230.             'creditNoteNumber' => $documentType === CreditNoteGenerator::CREDIT_NOTE $documentNumber null,
  231.             'referencedInvoiceId' => $referencedInvoiceDocument['id'],
  232.         ]);
  233.     }
  234.     private function hasCreditItem(string $orderId): bool
  235.     {
  236.         $lineItem $this->connection->fetchFirstColumn(
  237.             'SELECT 1 FROM `order_line_item` WHERE hex(`order_id`) = :orderId and `type` = :itemType LIMIT 1',
  238.             [
  239.                 'orderId' => $orderId,
  240.                 'itemType' => LineItem::CREDIT_LINE_ITEM_TYPE,
  241.             ]
  242.         );
  243.         return !empty($lineItem);
  244.     }
  245. }