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

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