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

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