You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shopware 6.5自定义文档类型生成PDF报错:无法找到对应生成器

Shopware 6.5.0.0 自定义文档类型PDF生成报错问题排查

我按照Shopware官方文档在Shopware 6.5.0.0中创建了名为order_confirmation的自定义文档类型,后台订单创建新文档时能识别该类型,但生成PDF时提示错误:Unable to find a document generator with type 'order_confirmation'。我参考官方invoice的服务配置编写了services.xml,也实现了对应的Document Renderer类,且已确认TYPE常量与迁移中定义的文档类型一致,但问题仍未解决。以下是相关代码:

services.xml

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="Dima\OrderConfirmation\Core\Checkout\Document\Renderer\OrderConfirmationRenderer">
            <argument type="service" id="order.repository"/>
            <argument type="service" id="Shopware\Core\Checkout\Document\Service\DocumentConfigLoader"/>
            <argument type="service" id="Shopware\Core\Checkout\Document\Twig\DocumentTemplateRenderer"/>
            <argument type="service" id="Shopware\Core\System\NumberRange\ValueGenerator\NumberRangeValueGeneratorInterface"/>
            <argument>%kernel.project_dir%</argument>
            
            <tag name="document.renderer" />
        </service>
    </services>
</container>

OrderConfirmationRenderer

<?php declare(strict_types=1);

namespace Dima\OrderConfirmation\Core\Checkout\Document\Renderer;

use Shopware\Core\Checkout\Document\Renderer\AbstractDocumentRenderer;
use Shopware\Core\Checkout\Document\Renderer\DocumentRendererConfig;
use Shopware\Core\Checkout\Document\Renderer\RenderedDocument;
use Shopware\Core\Checkout\Document\Renderer\RendererResult;
use Shopware\Core\Checkout\Document\Service\DocumentConfigLoader;
use Shopware\Core\Checkout\Document\Struct\DocumentGenerateOperation;
use Shopware\Core\Checkout\Document\Twig\DocumentTemplateRenderer;
use Shopware\Core\Checkout\Order\OrderEntity;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
use Shopware\Core\System\Locale\LocaleEntity;
use Shopware\Core\System\NumberRange\ValueGenerator\NumberRangeValueGeneratorInterface;

class OrderConfirmationRenderer extends AbstractDocumentRenderer
{
    public const DEFAULT_TEMPLATE = '@DimaOrderConfirmation/documents/order_confirmation.html.twig';

    final public const TYPE = 'order_confirmation';

    /**
     * @internal
     */
    public function __construct(
        private readonly EntityRepository $orderRepository,
        private readonly DocumentConfigLoader $documentConfigLoader,
        private readonly DocumentTemplateRenderer $documentTemplateRenderer,
        private readonly NumberRangeValueGeneratorInterface $numberRangeValueGenerator,
        private readonly string $rootDir,
    ) {
    }

    public function supports(): string
    {
        return self::TYPE;
    }

    /**
     * @param array<DocumentGenerateOperation> $operations
     */
    public function render(array $operations, Context $context, DocumentRendererConfig $rendererConfig): RendererResult
    {
        $ids = \array_map(fn (DocumentGenerateOperation $operation) => $operation->getOrderId(), $operations);

        if (empty($ids)) {
            return new RendererResult();
        }

        $result = new RendererResult();

        $template = self::DEFAULT_TEMPLATE;

        $orders = $this->orderRepository->search(new Criteria($ids), $context)->getEntities();

        foreach ($orders as $order) {
            $orderId = $order->getId();

            try {
                $operation = $operations[$orderId] ?? null;
                if ($operation === null) {
                    continue;
                }

                $config = clone $this->documentConfigLoader->load(self::TYPE, $order->getSalesChannelId(), $context);

                $config->merge($operation->getConfig());

                $number = $config->getDocumentNumber() ?: $this->getNumber($context, $order, $operation);

                $now = (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT);

                $config->merge([
                    'documentDate' => $operation->getConfig()['documentDate'] ?? $now,
                    'documentNumber' => $number,
                    'custom' => [
                        'invoiceNumber' => $number,
                    ],
                ]);

                // If document is uploaded manually
                if ($operation->isStatic()) {
                    $doc = new RenderedDocument('', $number, $config->buildName(), $operation->getFileType(), $config->jsonSerialize());
                    $result->addSuccess($orderId, $doc);

                    continue;
                }

                /** @var LocaleEntity $locale */
                $locale = $order->getLanguage()->getLocale();

                $html = $this->documentTemplateRenderer->render(
                    $template,
                    [
                        'order' => $order,
                        'config' => $config,
                        'rootDir' => $this->rootDir,
                        'context' => $context,
                    ],
                    $context,
                    $order->getSalesChannelId(),
                    $order->getLanguageId(),
                    $locale->getCode()
                );

                $doc = new RenderedDocument(
                    $html,
                    $number,
                    $config->buildName(),
                    $operation->getFileType(),
                    $config->jsonSerialize(),
                );

                $result->addSuccess($orderId, $doc);
            } catch (\Throwable $exception) {
                $result->addError($orderId, $exception);
            }
        }

        return $result;
    }

    public function getDecorated(): AbstractDocumentRenderer
    {
        throw new DecorationPatternException(self::class);
    }

    private function getNumber(Context $context, OrderEntity $order, DocumentGenerateOperation $operation): string
    {
        return $this->numberRangeValueGenerator->getValue(
            'document_' . self::TYPE,
            $context,
            $order->getSalesChannelId(),
            $operation->isPreview()
        );
    }
}

问题解决方法

1. 完善服务标签配置

你的services.xml中document.renderer标签缺少type属性,Shopware无法将Renderer与对应的文档类型关联。修改标签部分:

<tag name="document.renderer" type="order_confirmation" />

这个属性明确告诉系统该Renderer服务负责处理order_confirmation类型的文档。

2. 验证文档类型迁移

确保迁移文件中插入的document_type记录的technical_name与Renderer的TYPE常量完全一致(大小写敏感),示例迁移代码:

$this->connection->insert('document_type', [
    'technical_name' => 'order_confirmation',
    'name' => '订单确认单',
    'created_at' => (new \DateTime())->format(Defaults::STORAGE_DATE_TIME_FORMAT),
]);

3. 清除缓存并重建容器

修改服务配置后执行以下命令:

bin/console cache:clear
bin/console cache:warmup

4. 检查模板路径

确认@DimaOrderConfirmation/documents/order_confirmation.html.twig模板存在于插件的Resources/views/documents/目录下,路径和文件名必须完全匹配。

内容的提问来源于stack exchange,提问作者Darkice997

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 01:57:02