Resend specific order state emails e.g. order delivery shipped in shopware 6

Viewed 64

I'm wondering if there is any simpler solution in sending specific order state emails.

Currently i use the following code to send e.g. the "order delivery shipped" email again.

$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('orderNumber', $orderNumber));
$criteria->addAssociation('orderCustomer.salutation');
$criteria->addAssociation('orderCustomer.customer');
$criteria->addAssociation('stateMachineState');
$criteria->addAssociation('deliveries.shippingMethod');
$criteria->addAssociation('deliveries.shippingOrderAddress.country');
$criteria->addAssociation('deliveries.shippingOrderAddress.countryState');
$criteria->addAssociation('salesChannel');
$criteria->addAssociation('language.locale');
$criteria->addAssociation('transactions.paymentMethod');
$criteria->addAssociation('lineItems');
$criteria->addAssociation('currency');
$criteria->addAssociation('addresses.country');
$criteria->addAssociation('addresses.countryState');

/** @var OrderEntity|null $order */
$order = $this->orderRepository->search($criteria, $context)->first();

$context = new Context(new SystemSource());
$salesChannelContext = $this->orderConverter->assembleSalesChannelContext($order, $context)->getContext();
$salesChannelContext->addExtension(
    MailSendSubscriber::MAIL_CONFIG_EXTENSION,
    new MailSendSubscriberConfig(false)
);
$event = new OrderStateMachineStateChangeEvent(
    'state_enter.order_delivery.state.shipped',
    $order,
    $salesChannelContext
);
$flowEvent = new FlowEvent('action.mail.send', new FlowState($event), [
    'recipient' => [
'data' => [],
'type' => 'default',
    ],
    'mailTemplateId' => $mailTemplateId,
]);

$this->sendMailAction->handle($flowEvent);
3 Answers

You can build and send the mail with the Shopware\Core\Content\Mail\Service\MailService by yourself. But is way more code then just triggering it with a state change.


        $criteria = new Criteria();
        $criteria->addFilter(new EqualsFilter('mailTemplateTypeId', self::ORDER_CONFIRMATION_MAIL_TYPE_ID));
        $criteria->addAssociation('media.media');
        $criteria->setLimit(1);

        if ($order->getSalesChannelId()) {
            $criteria->addFilter(new EqualsFilter('mail_template.salesChannels.salesChannel.id', $order->getSalesChannelId()));

            /** @var MailTemplateEntity|null $mailTemplate */
            $mailTemplate = $this->mailTemplateRepository->search($criteria, $this->context)->first();

            // Fallback if no template for the saleschannel is found
            if (null === $mailTemplate) {
                $criteria = new Criteria();
                $criteria->addFilter(new EqualsFilter('mailTemplateTypeId', self::ORDER_CONFIRMATION_MAIL_TYPE_ID));
                $criteria->addAssociation('media.media');
                $criteria->setLimit(1);

                /** @var MailTemplateEntity|null $mailTemplate */
                $mailTemplate = $this->mailTemplateRepository->search($criteria, $this->context)->first();
            }
        } else {
            /** @var MailTemplateEntity|null $mailTemplate */
            $mailTemplate = $this->mailTemplateRepository->search($criteria, $this->context)->first();
        }

        if (null === $mailTemplate) {
            return;
        }

        $data = new DataBag();
        $data->set('recipients', [
            $order->getOrderCustomer()->getEmail() => $order->getOrderCustomer()->getFirstName() . ' ' . $order->getOrderCustomer()->getLastName(),
        ]);
        $data->set('senderName', $mailTemplate->getTranslation('senderName'));
        $data->set('salesChannelId', $order->getSalesChannelId());

        $data->set('templateId', $mailTemplate->getId());
        $data->set('customFields', $mailTemplate->getCustomFields());
        $data->set('contentHtml', $mailTemplate->getTranslation('contentHtml'));
        $data->set('contentPlain', $mailTemplate->getTranslation('contentPlain'));
        $data->set('subject', $mailTemplate->getTranslation('subject'));
        $data->set('mediaIds', []);

        $attachments = [];
        if (null !== $mailTemplate->getMedia()) {
            foreach ($mailTemplate->getMedia() as $mailTemplateMedia) {
                if (null === $mailTemplateMedia->getMedia()) {
                    continue;
                }
                if (null !== $mailTemplateMedia->getLanguageId() && $mailTemplateMedia->getLanguageId() !== $order->getLanguageId()) {
                    continue;
                }

                $attachments[] = $this->mediaService->getAttachment(
                    $mailTemplateMedia->getMedia(),
                    $this->context
                );
            }
        }
        if (!empty($attachments)) {
            $data->set('binAttachments', $attachments);
        }

        try {
            if (
                null === $this->mailService->send(
                    $data->all(),
                    $this->context,
                    $this->getTemplateData($order)
                )
            ) {
                throw new \Exception('Could not render mail template');
            }

            $this->orderMailRepository->upsert([[
                'orderId'          => $order->getId(),
                'confirmationSend' => true,
            ]], $this->context);
        } catch (\Exception $e) {
            $this->logger->error(sprintf(
                'Could not send mail for order %s: %s',
                $order->getOrderNumber(),
                $e->getMessage()
            ));
        }
    }

Given that you don't actually want to change the state, this looks like a good solution.

If you actually needed to programmatically change the state you could use the StateMachineRegistry service.

$transition = new Transition(OrderDeliveryDefinition::ENTITY_NAME, $orderDeliveryId, StateMachineTransitionActions::ACTION_SHIP, 'stateId');
$this->stateMachineRegistry->transition($transition, Context::createDefaultContext());

However if the current state is equal to the state you want to transition to, then an UnnecessaryTransitionException is expected to be thrown. You'd have to change the state back to a previous one before doing this and at that point your code is probably less trouble, if all you want to do is to repeat sending that mail.

Related