Magento 2:如何为每个商品生成独立发票?
Magento 2 自动生成单个商品独立发票并合并PDF发送解决方案
核心实现步骤
1. 监听订单完成事件
通过sales_order_save_after事件触发发票生成逻辑,创建自定义模块的events.xml配置文件:
<!-- app/code/YourVendor/YourModule/etc/events.xml --> <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> <event name="sales_order_save_after"> <observer name="yourmodule_order_create_item_invoices" instance="YourVendor\YourModule\Observer\CreateItemInvoices" /> </event> </config>
2. 编写Observer生成单个商品发票
遍历订单内的每个有效订单项,为每个商品单独生成发票:
// app/code/YourVendor/YourModule/Observer/CreateItemInvoices.php <?php namespace YourVendor\YourModule\Observer; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Sales\Model\Order\InvoiceFactory; use Magento\Sales\Model\Order\Item as OrderItem; use Magento\Framework\Exception\LocalizedException; use Magento\Sales\Model\Order; class CreateItemInvoices implements ObserverInterface { protected $invoiceFactory; public function __construct(InvoiceFactory $invoiceFactory) { $this->invoiceFactory = $invoiceFactory; } public function execute(Observer $observer) { /** @var Order $order */ $order = $observer->getOrder(); // 仅处理可开票且处于处理状态的订单,可按需调整状态判断 if (!$order->canInvoice() || $order->getState() !== Order::STATE_PROCESSING) { return; } $invoices = []; foreach ($order->getAllItems() as $item) { /** @var OrderItem $item */ if (!$item->getQtyToInvoice() || $item->isDummy()) { continue; } try { $invoice = $this->invoiceFactory->create(); $invoice->setOrder($order); $invoice->addItem($item, $item->getQtyToInvoice()); $invoice->collectTotals(); $invoice->setState(\Magento\Sales\Model\Order\Invoice::STATE_PAID); $invoice->save(); $invoices[] = $invoice; } catch (LocalizedException $e) { // 异常处理:可添加日志记录逻辑 continue; } } if (!empty($invoices)) { $this->mergeInvoicesToPdfAndSend($order, $invoices); } } protected function mergeInvoicesToPdfAndSend(Order $order, array $invoices) { $pdfMerger = \Magento\Framework\App\ObjectManager::getInstance()->get(\YourVendor\YourModule\Model\PdfMerger::class); $pdfContent = $pdfMerger->mergeInvoices($invoices); $mailSender = \Magento\Framework\App\ObjectManager::getInstance()->get(\YourVendor\YourModule\Model\InvoiceMailSender::class); $mailSender->sendMergedInvoiceEmail($order, $pdfContent); } }
3. 实现PDF合并逻辑
将多个独立发票的PDF内容合并为单个文件:
// app/code/YourVendor/YourModule/Model/PdfMerger.php <?php namespace YourVendor\YourModule\Model; use Magento\Sales\Model\Order\Pdf\Invoice as InvoicePdf; use Zend_Pdf; class PdfMerger { protected $invoicePdf; public function __construct(InvoicePdf $invoicePdf) { $this->invoicePdf = $invoicePdf; } public function mergeInvoices(array $invoices) { $mergedPdf = new Zend_Pdf(); foreach ($invoices as $invoice) { $invoicePdf = $this->invoicePdf->getPdf([$invoice]); foreach ($invoicePdf->pages as $page) { $mergedPdf->pages[] = $page; } } return $mergedPdf->render(); } }
4. 发送包含合并PDF的邮件
构建邮件并添加合并后的PDF附件发送给客户:
// app/code/YourVendor/YourModule/Model/InvoiceMailSender.php <?php namespace YourVendor\YourModule\Model; use Magento\Sales\Model\Order; use Magento\Framework\Mail\Template\TransportBuilder; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Store\Model\ScopeInterface; class InvoiceMailSender { protected $transportBuilder; protected $scopeConfig; public function __construct( TransportBuilder $transportBuilder, ScopeConfigInterface $scopeConfig ) { $this->transportBuilder = $transportBuilder; $this->scopeConfig = $scopeConfig; } public function sendMergedInvoiceEmail(Order $order, $pdfContent) { $storeId = $order->getStoreId(); $templateId = 'sales_email_invoice_template'; // 可替换为自定义邮件模板ID $this->transportBuilder ->setTemplateIdentifier($templateId) ->setTemplateOptions([ 'area' => \Magento\Framework\App\Area::AREA_FRONTEND, 'store' => $storeId ]) ->setTemplateVars([ 'order' => $order, 'billing' => $order->getBillingAddress(), 'comment' => '' ]) ->setFrom([ 'name' => $this->scopeConfig->getValue('trans_email/ident_general/name', ScopeInterface::SCOPE_STORE, $storeId), 'email' => $this->scopeConfig->getValue('trans_email/ident_general/email', ScopeInterface::SCOPE_STORE, $storeId) ]) ->addTo($order->getCustomerEmail(), $order->getCustomerName()); $transport = $this->transportBuilder->getTransport(); $attachment = new \Zend_Mime_Part($pdfContent); $attachment->type = 'application/pdf'; $attachment->filename = 'merged_invoices_' . $order->getIncrementId() . '.pdf'; $attachment->disposition = \Zend_Mime::DISPOSITION_ATTACHMENT; $attachment->encoding = \Zend_Mime::ENCODING_BASE64; $message = $transport->getMessage(); $message->addAttachment($attachment); $transport->sendMessage(); } }
注意事项
- 替换代码中的
YourVendor\YourModule为实际的厂商名和模块名,按Magento规范创建自定义模块。 - 可根据业务需求调整订单状态判断逻辑,确保仅在订单支付完成等合适时机触发发票生成。
- 异常处理部分可补充日志记录,便于排查问题。
- 可在后台
Marketing > Communications > Email Templates中创建自定义邮件模板,替换上述代码中的templateId。 - 若订单包含大量商品,需考虑性能优化,比如异步处理发票生成和PDF合并。
内容的提问来源于stack exchange,提问作者Lokesh Yadav
相关产品推荐
相关产品推荐

