基于XSLT实现按供应商自定义ERP发票描述字段的方案问询
实现方案:基于供应商配置动态生成ERP发票Description字段
1. 定义供应商配置文件(vendors.xml)
给每个供应商定义专属的Description模板,用占位符对应Peppol发票里的XML节点,同时支持固定文本拼接。会计只需修改这个XML就能自定义内容和顺序,无需改动XSLT代码。
示例配置:
<vendors> <vendor> <vendor-id>VEND001</vendor-id> <!-- 模板:固定文本 + 占位符组合 --> <description-template>Invoice {invoice-number} from {supplier-name} (Issue Date: {invoice-date})</description-template> </vendor> <vendor> <vendor-id>VEND002</vendor-id> <description-template>#{invoice-number} | Total: {total-amount} EUR | Tax ID: {supplier-tax-id}</description-template> </vendor> <!-- 可选扩展:支持自定义占位符映射,适配更多Peppol节点 --> <vendor> <vendor-id>VEND003</vendor-id> <description-template>PO {po-number} - Invoice {invoice-number}</description-template> <placeholder-mappings> <mapping> <placeholder>{po-number}</placeholder> <xpath>./OrderReference/ID</xpath> <!-- 对应Peppol发票中采购订单ID的路径 --> </mapping> </placeholder-mappings> </vendor> </vendors>
2. XSLT核心实现逻辑
在Peppol转ERP格式的XSLT中,引入vendors.xml配置,根据当前供应商ID匹配模板,再将占位符替换为实际发票数据。
基础版(XSLT 2.0)
适合固定占位符场景,无需频繁扩展新字段:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 加载供应商配置文件 --> <xsl:variable name="vendors-config" select="document('vendors.xml')/vendors"/> <!-- 匹配Peppol发票根节点 --> <xsl:template match="/Invoice"> <!-- 获取当前供应商ID(路径需匹配你的Peppol发票结构) --> <xsl:variable name="current-vendor-id" select="./AccountingSupplierParty/Party/PartyIdentification/ID"/> <!-- 匹配当前供应商的模板,无配置则用默认模板 --> <xsl:variable name="desc-template" select="$vendors-config/vendor[vendor-id = $current-vendor-id]/description-template"/> <xsl:variable name="default-template" select="'Invoice {invoice-number} - {supplier-name}'"/> <xsl:variable name="target-template" select="if ($desc-template) then $desc-template else $default-template"/> <!-- 生成ERP发票结构 --> <ERP-Invoice> <Description> <xsl:call-template name="replace-placeholders"> <xsl:with-param name="template" select="$target-template"/> <xsl:with-param name="invoice" select="."/> </xsl:call-template> </Description> <!-- 其他ERP字段转换逻辑... --> </ERP-Invoice> </xsl:template> <!-- 占位符替换模板 --> <xsl:template name="replace-placeholders"> <xsl:param name="template"/> <xsl:param name="invoice"/> <xsl:choose> <!-- 替换发票编号 --> <xsl:when test="contains($template, '{invoice-number}')"> <xsl:call-template name="replace-placeholders"> <xsl:with-param name="template" select="replace($template, '\{invoice-number\}', $invoice/ID)"/> <xsl:with-param name="invoice" select="$invoice"/> </xsl:call-template> </xsl:when> <!-- 替换供应商名称 --> <xsl:when test="contains($template, '{supplier-name}')"> <xsl:call-template name="replace-placeholders"> <xsl:with-param name="template" select="replace($template, '\{supplier-name\}', $invoice/AccountingSupplierParty/Party/PartyName/Name)"/> <xsl:with-param name="invoice" select="$invoice"/> </xsl:call-template> </xsl:when> <!-- 替换发票日期 --> <xsl:when test="contains($template, '{invoice-date}')"> <xsl:call-template name="replace-placeholders"> <xsl:with-param name="template" select="replace($template, '\{invoice-date\}', $invoice/IssueDate)"/> <xsl:with-param name="invoice" select="$invoice"/> </xsl:call-template> </xsl:when> <!-- 替换总金额 --> <xsl:when test="contains($template, '{total-amount}')"> <xsl:call-template name="replace-placeholders"> <xsl:with-param name="template" select="replace($template, '\{total-amount\}', $invoice/LegalMonetaryTotal/PayableAmount)"/> <xsl:with-param name="invoice" select="$invoice"/> </xsl:call-template> </xsl:when> <!-- 替换供应商税号 --> <xsl:when test="contains($template, '{supplier-tax-id}')"> <xsl:call-template name="replace-placeholders"> <xsl:with-param name="template" select="replace($template, '\{supplier-tax-id\}', $invoice/AccountingSupplierParty/Party/TaxRegistration/TaxSchemeID)"/> <xsl:with-param name="invoice" select="$invoice"/> </xsl:call-template> </xsl:when> <!-- 无占位符时输出最终文本 --> <xsl:otherwise> <xsl:value-of select="$template"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
进阶版(XSLT 3.0)
支持动态占位符映射,会计可自行添加新的Peppol节点映射,无需修改XSLT:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:variable name="vendors-config" select="document('vendors.xml')/vendors"/> <xsl:template match="/Invoice"> <xsl:variable name="current-vendor-id" select="./AccountingSupplierParty/Party/PartyIdentification/ID"/> <xsl:variable name="vendor-config" select="$vendors-config/vendor[vendor-id = $current-vendor-id]"/> <xsl:variable name="desc-template" select="$vendor-config/description-template"/> <xsl:variable name="default-template" select="'Invoice {invoice-number} - {supplier-name}'"/> <xsl:variable name="target-template" select="if ($desc-template) then $desc-template else $default-template"/> <ERP-Invoice> <Description> <!-- 先替换预定义占位符 --> <xsl:variable name="pre-replaced"> <xsl:call-template name="replace-placeholders"> <xsl:with-param name="template" select="$target-template"/> <xsl:with-param name="invoice" select="."/> </xsl:call-template> </xsl:variable> <!-- 再替换自定义占位符 --> <xsl:call-template name="replace-custom-placeholders"> <xsl:with-param name="template" select="$pre-replaced"/> <xsl:with-param name="invoice" select="."/> <xsl:with-param name="mappings" select="$vendor-config/placeholder-mappings/mapping"/> </xsl:call-template> </Description> <!-- 其他ERP字段... --> </ERP-Invoice> </xsl:template> <!-- 保留基础版的预定义占位符替换模板 --> <xsl:template name="replace-placeholders"> <!-- 同基础版代码 --> </xsl:template> <!-- 自定义占位符替换模板 --> <xsl:template name="replace-custom-placeholders"> <xsl:param name="template"/> <xsl:param name="invoice"/> <xsl:param name="mappings"/> <xsl:if test="$mappings"> <xsl:variable name="current-mapping" select="$mappings[1]"/> <!-- 执行XPath获取节点值,不存在则显示[N/A] --> <xsl:variable name="field-value" select="xsl:evaluate($current-mapping/xpath, $invoice)"/> <xsl:variable name="display-value" select="if ($field-value) then $field-value else '[N/A]'"/> <!-- 替换占位符 --> <xsl:variable name="updated-template" select="replace($template, escape-html-uri($current-mapping/placeholder), $display-value)"/> <!-- 递归处理剩余映射 --> <xsl:call-template name="replace-custom-placeholders"> <xsl:with-param name="template" select="$updated-template"/> <xsl:with-param name="invoice" select="$invoice"/> <xsl:with-param name="mappings" select="$mappings[position() > 1]"/> </xsl:call-template> </xsl:if> <xsl:if test="not($mappings)"> <xsl:value-of select="$template"/> </xsl:if> </xsl:template> </xsl:stylesheet>
3. 优化与落地建议
- 错误处理:在占位符替换时,若对应Peppol节点不存在,输出
[N/A]或自定义默认值,避免空值影响ERP导入。 - 配置简化:给会计提供Excel模板(列:供应商ID、模板文本、占位符、XPath),通过VBA或脚本自动生成vendors.xml,降低配置门槛。
- 测试验证:针对每个供应商配置,生成测试Peppol发票,转换后检查Description字段是否符合预期,覆盖无配置、节点缺失等边界场景。
内容的提问来源于stack exchange,提问作者Kees Netelvrees
相关产品推荐
相关产品推荐

