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

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\Exception\GenerateDocumentActionException;
  16. use Shopware\Core\Defaults;
  17. use Shopware\Core\Framework\Event\DelayAware;
  18. use Shopware\Core\Framework\Event\FlowEvent;
  19. use Shopware\Core\Framework\Event\OrderAware;
  20. use Shopware\Core\Framework\Event\SalesChannelAware;
  21. use Shopware\Core\Framework\Feature;
  22. use Shopware\Core\System\NumberRange\ValueGenerator\NumberRangeValueGeneratorInterface;
  23. class GenerateDocumentAction extends FlowAction
  24. {
  25.     /**
  26.      * @deprecated tag:v6.5.0 - Property $documentService will be removed due to unused
  27.      */
  28.     protected DocumentService $documentService;
  29.     /**
  30.      * @deprecated tag:v6.5.0 - Property $connection will be removed due to unused
  31.      */
  32.     protected Connection $connection;
  33.     private DocumentGenerator $documentGenerator;
  34.     /**
  35.      * @deprecated tag:v6.5.0 - Property $connection will be removed due to unused
  36.      */
  37.     private NumberRangeValueGeneratorInterface $valueGenerator;
  38.     private LoggerInterface $logger;
  39.     /**
  40.      * @internal
  41.      */
  42.     public function __construct(
  43.         DocumentService $documentService,
  44.         DocumentGenerator $documentGenerator,
  45.         NumberRangeValueGeneratorInterface $valueGenerator,
  46.         Connection $connection,
  47.         LoggerInterface $logger
  48.     ) {
  49.         $this->documentService $documentService;
  50.         $this->documentGenerator $documentGenerator;
  51.         $this->connection $connection;
  52.         $this->valueGenerator $valueGenerator;
  53.         $this->logger $logger;
  54.     }
  55.     public static function getName(): string
  56.     {
  57.         return 'action.generate.document';
  58.     }
  59.     public static function getSubscribedEvents(): array
  60.     {
  61.         return [
  62.             self::getName() => 'handle',
  63.         ];
  64.     }
  65.     public function requirements(): array
  66.     {
  67.         return [OrderAware::class, DelayAware::class];
  68.     }
  69.     public function handle(FlowEvent $event): void
  70.     {
  71.         $baseEvent $event->getEvent();
  72.         $eventConfig $event->getConfig();
  73.         if (!$baseEvent instanceof OrderAware || !$baseEvent instanceof SalesChannelAware) {
  74.             return;
  75.         }
  76.         if (\array_key_exists('documentType'$eventConfig)) {
  77.             $this->generateDocument($eventConfig$baseEvent);
  78.             return;
  79.         }
  80.         $documentsConfig $eventConfig['documentTypes'];
  81.         if (!$documentsConfig) {
  82.             return;
  83.         }
  84.         // Invoice document should be created first
  85.         foreach ($documentsConfig as $index => $config) {
  86.             if ($config['documentType'] === InvoiceGenerator::INVOICE) {
  87.                 $this->generateDocument($config$baseEvent);
  88.                 unset($documentsConfig[$index]);
  89.                 break;
  90.             }
  91.         }
  92.         foreach ($documentsConfig as $config) {
  93.             $this->generateDocument($config$baseEvent);
  94.         }
  95.     }
  96.     /**
  97.      * @param OrderAware&SalesChannelAware $baseEvent
  98.      */
  99.     private function generateDocument(array $eventConfig$baseEvent): void
  100.     {
  101.         $documentType $eventConfig['documentType'];
  102.         $documentRangerType $eventConfig['documentRangerType'];
  103.         if (!$documentType || !$documentRangerType) {
  104.             return;
  105.         }
  106.         if (Feature::isActive('v6.5.0.0')) {
  107.             $fileType $eventConfig['fileType'] ?? FileTypes::PDF;
  108.             $config $eventConfig['config'] ?? [];
  109.             $static $eventConfig['static'] ?? false;
  110.             $operation = new DocumentGenerateOperation($baseEvent->getOrderId(), $fileType$confignull$static);
  111.             $result $this->documentGenerator->generate($documentType, [$baseEvent->getOrderId() => $operation], $baseEvent->getContext());
  112.             if (!empty($result->getErrors())) {
  113.                 foreach ($result->getErrors() as $error) {
  114.                     $this->logger->error($error->getMessage());
  115.                 }
  116.             }
  117.             return;
  118.         }
  119.         $documentNumber $this->valueGenerator->getValue(
  120.             $documentRangerType,
  121.             $baseEvent->getContext(),
  122.             $baseEvent->getSalesChannelId()
  123.         );
  124.         $now = (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT);
  125.         $eventConfig['documentNumber'] = $documentNumber;
  126.         $eventConfig['documentDate'] = $now;
  127.         $customConfig $this->getEventCustomConfig(
  128.             $documentType,
  129.             $documentNumber,
  130.             $now,
  131.             $baseEvent->getOrderId()
  132.         );
  133.         $eventConfig['custom'] = $customConfig;
  134.         $documentConfig DocumentConfigurationFactory::createConfiguration($eventConfig);
  135.         $this->documentService->create(
  136.             $baseEvent->getOrderId(),
  137.             $documentType,
  138.             $eventConfig['fileType'] ?? FileTypes::PDF,
  139.             $documentConfig,
  140.             $baseEvent->getContext(),
  141.             $customConfig['referencedInvoiceId'] ?? null,
  142.             $eventConfig['static'] ?? false
  143.         );
  144.     }
  145.     private function getEventCustomConfig(string $documentTypestring $documentNumberstring $nowstring $orderId): array
  146.     {
  147.         switch ($documentType) {
  148.             case InvoiceGenerator::INVOICE:
  149.                 return ['invoiceNumber' => $documentNumber];
  150.             case DeliveryNoteGenerator::DELIVERY_NOTE:
  151.                 return [
  152.                     'deliveryNoteNumber' => $documentNumber,
  153.                     'deliveryDate' => $now,
  154.                     'deliveryNoteDate' => $now,
  155.                 ];
  156.             case StornoGenerator::STORNO:
  157.             case CreditNoteGenerator::CREDIT_NOTE:
  158.                 return $this->getConfigWithReferenceDoc($documentType$documentNumber$orderId);
  159.             default:
  160.                 return [];
  161.         }
  162.     }
  163.     private function getConfigWithReferenceDoc(string $documentTypestring $documentNumberstring $orderId): array
  164.     {
  165.         $referencedInvoiceDocument $this->connection->fetchAssociative(
  166.             'SELECT LOWER (HEX(`document`.`id`)) as `id` , `document`.`config` as `config`
  167.                     FROM `document` JOIN `document_type` ON `document`.`document_type_id` = `document_type`.`id`
  168.                     WHERE `document_type`.`technical_name` = :techName AND hex(`document`.`order_id`) = :orderId
  169.                     ORDER BY `document`.`created_at` DESC LIMIT 1',
  170.             [
  171.                 'techName' => InvoiceGenerator::INVOICE,
  172.                 'orderId' => $orderId,
  173.             ]
  174.         );
  175.         if (empty($referencedInvoiceDocument)) {
  176.             throw new GenerateDocumentActionException('Cannot generate ' $documentType ' document because no invoice document exists. OrderId: ' $orderId);
  177.         }
  178.         if ($documentType === CreditNoteGenerator::CREDIT_NOTE && !$this->hasCreditItem($orderId)) {
  179.             throw new GenerateDocumentActionException('Cannot generate the credit note document because no credit items exist. OrderId: ' $orderId);
  180.         }
  181.         $documentRefer json_decode($referencedInvoiceDocument['config'], true);
  182.         $documentNumberRefer $documentRefer['custom']['invoiceNumber'];
  183.         return array_filter([
  184.             'invoiceNumber' => $documentNumberRefer,
  185.             'stornoNumber' => $documentType === StornoGenerator::STORNO $documentNumber null,
  186.             'creditNoteNumber' => $documentType === CreditNoteGenerator::CREDIT_NOTE $documentNumber null,
  187.             'referencedInvoiceId' => $referencedInvoiceDocument['id'],
  188.         ]);
  189.     }
  190.     private function hasCreditItem(string $orderId): bool
  191.     {
  192.         $lineItem $this->connection->fetchFirstColumn(
  193.             'SELECT 1 FROM `order_line_item` WHERE hex(`order_id`) = :orderId and `type` = :itemType LIMIT 1',
  194.             [
  195.                 'orderId' => $orderId,
  196.                 'itemType' => LineItem::CREDIT_LINE_ITEM_TYPE,
  197.             ]
  198.         );
  199.         return !empty($lineItem);
  200.     }
  201. }