> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kasoftware.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 将 MySQL 输出映射为 EDI 810 发票

> 从 MySQL 数据库检索数据，并在知行之桥中将其映射为 X12 810 EDI 发票文档。

export const companyName = "知行软件";

export const siteNameShort = "知行之桥";

export const siteName = "知行之桥";

## 概述

{siteName} 包含强大的工具，可以从后端数据库中检索目标数据，并将这些数据映射为出站 EDI 文档（如 X12、EDIFACT 等）。本指南逐步介绍创建 MySQL 数据库到 EDI 工作流的过程，重点是生成 X12 810 发票文档。示例发票遵循 Amazon 所需的语法。

本文中描述的方法和原理也适用于生成其他 EDI 文档，但从数据库中提取的特定数据以及映射值之间的关系可能存在差异。

本指南从如何在 {siteNameShort} 中完成映射的概述开始，然后逐步完成工作流的每一步。请参阅 [示例数据](#示例数据) 查看本文中使用的数据库结构、输出文件以及生成的 810 文档。

## {siteName} 中的 EDI 映射

### 将 MySQL 数据提取为 XML

{siteNameShort} 使用 XML 作为通用格式，以完成数据转换和操作。所有 EDI 映射工作流都涉及将源数据（本例中为数据库数据）和目标数据（本例中为 EDI 文件）转换为 XML。

所有从数据库（或类数据库应用程序）提取数据的 {siteNameShort} 端口都会自动将输出数据格式化为 XML 文件。因此，从数据库中提取的数据可以立即进行映射或转换。本指南使用 [MySQL 端口](/26.2/self-hosted/zh/connectors/mysql/mysql) 与 MySQL 数据库交互。

### 提取 810 发票相关的数据

通常会根据采购订单 (PO) 向交易伙伴发送发票。当制造公司收到请求一组制成品的采购订单时，制造公司会以发票响应，以便收取所请求货物的款项。

因此，用来生成发票的数据通常来自之前接收到的采购订单。本文假设传入的采购订单数据会自动插入到数据库中，所以采购订单数据库表保存着与 810 发票相关的数据。为此工作流配置 MySQL 端口时，MySQL 端口将指向保存采购订单数据的表。

采购订单可以包含多个订单行，并且典型的数据库设置会将订单行数据存储在与 `PurchaseOrders` 表分开的 `LineItems` 表中。`LineItems` 表具有指向 `PurchaseOrders` 表的外键，以便识别哪些订单行与哪个采购订单关联，但必须从两个表中提取数据才能检索填充 810 发票所需的全部信息。

[定义 SELECT 操作](#定义-select-操作) 包含同时从 `PurchaseOrders` 表和 `LineItems` 表（具有合适的外键关系）提取数据所需的步骤。

### 将 EDI 文档表示为 XML

为了将从数据库中提取的 XML 数据映射为 EDI 文档，EDI 文档也必须表示为 XML。{siteNameShort} 包括许多可以在 EDI 和 XML 格式（X12、EDIFACT 等）之间相互转换的端口。本指南重点介绍映射 X12 文档，因此使用 [X12 端口](/26.2/self-hosted/zh/connectors/x12) 生成目标 810 文档的 XML 表示。

在创建映射工作流之前，准备一个示例输出文档（例如示例 810）非常重要。该示例文档用于为映射步骤创建 XML 模型。您可以上传自己的示例文档，也可以从内置模板列表中选择。

### 转换 XML 结构

一旦源格式和目标格式都表示为 XML，就可以使用强大的 [XML Map 端口](/26.2/self-hosted/zh/connectors/xml-map/xml-map) 将一个 XML 结构转换为另一个。

XML Map 端口需要示例 **源** 和 **目标** 模板文件，分别代表起始和结束的 XML 结构。从数据库中提取的 XML 输出是源文件，而 X12 810 文档的 XML 表示（通过将示例 810 传入 X12 端口生成）是目标文件。

一旦指定源结构和目标结构，XML Map 端口的可视化设计器将填充这两个结构。源结构中的元素可以被拖拽到目标结构的元素上，以在两者之间创建映射关系。此映射步骤需要理解 EDI 结构，这样合适的元素才能包含在输出文件中。

XML Map 端口包括值格式化器、条件逻辑，甚至是自定义脚本来在映射过程中操作和计算数据。有关这些功能的更多信息，请参阅 [映射](/26.2/self-hosted/zh/mapping/mapping)。

一旦映射关系创建，XML Map 端口可以自动将匹配源 XML 结构的文件转换为目标 XML 结构。在本例中，从数据库中提取的数据会自动转换为 X12 810 文档的 XML 模型。最后一步是采用 810 XML 文件的 XML 模型并将其转换为实际的 810 文档，通过 X12 端口可以轻松完成。

## 创建映射工作流

本节包含在 {siteNameShort} 中创建工作流的每一步，以检索数据库输出并将其映射到 810 发票。

### 第 1 步：MySQL 端口

映射工作流的第一步是配置 [MySQL 端口](/26.2/self-hosted/zh/connectors/mysql/mysql)，以从 MySQL 数据库中提取相关数据。

#### 建立连接

点击导航栏上的 **工作流**，然后将一个 MySQL 端口实例添加到工作区画布（参见 [设计工作流](/26.2/self-hosted/zh/flows/designing-a-flow) 了解说明）。对于生产环境的工作流，MySQL 端口实例名称应包含目标 MySQL 数据库，以及关于正在从数据库提取或推送的数据的上下文信息（例如 MySQL\_Invoice\_tradingPartner）。在此工作流中，为简单起见，端口实例名称为 **MySQL\_Output**。

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mysql_edi_1.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=f1d7a3dcca6f96b68d4a82eb6573a963" alt="MySQL connector on the workspace canvas" width="800" data-path="public/images/mysql_edi_1.png" />

从下拉列表中选择一个 **连接**，或单击 **创建** 按钮以创建新连接。下图显示了 **添加连接** 页面。有关每个字段的详细信息，请参阅 [建立连接](/26.2/self-hosted/zh/connectors/mysql/mysql#establish-a-connection)。

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mysql_edi_connection_config.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=c4fd3c376a2f2c3362f34c3520e8f9e0" alt="MySQL Add Connection page" width="600" data-path="public/images/mysql_edi_connection_config.png" />

#### 定义 SELECT 操作

建立连接后，使用 [Select](/26.2/self-hosted/zh/connectors/mysql/mysql-select) 操作从数据库中提取相关数据。如 [提取 810 发票相关的数据](#提取-810-发票相关的数据) 中所述，数据是从父 `PurchaseOrders` 表和子 `LineItems` 表中提取的。

在 MySQL 端口 **系统设置** 页面上，将 **操作** 设置为 `Select`。在 **Select 配置** 部分中，单击 **添加** 以配置操作。这将显示数据库中的表列表。选择父表（包含 PO 数据）。稍后将添加子表（包含行项目数据）。在此示例中，PO 表为 `test_purchase_orders`，选择此表将填充配置部分的 **列** 区域。

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_db_output_mapping.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=e13e1379a74acf6eeb2a1a706efc6997" alt="MySQL Select configuration with test_purchase_orders table" width="800" data-path="public/images/mapmysql-810_db_output_mapping.png" />

使用编辑器选择要检索表中的哪些列；本指南在输出 XML 中包含所有列。

表中的一列为 `Processed`，如果记录尚未处理，则保存值 `0`，如果已处理，则保存值 `1`。当采购订单被插入表中时，预期 `Processed` 列会设置为 `0`。Select 配置应仅在 `Processed` 列为 `0` 时提取记录，并在成功提取记录后将该列更新为 `1`。

要完成这一步，请应用过滤器，仅提取 `Processed` = `0` 的记录。在编辑器的 **过滤器** 部分，增加一条新规则：

* 将左侧下拉列表设置为要检查的列；本例中为 `Processed`
* 将中间下拉列表设置为逻辑运算符；本例中为 `Equals`
* 将右侧输入设置为要与该列比较的值；本例中为 `0`

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_db_output_mapping_filtered.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=fa699b8a2ca1c2b29a319a862b11b7ef" alt="MySQL Select configuration with Processed filter" width="800" data-path="public/images/mapmysql-810_db_output_mapping_filtered.png" />

除了根据 `Processed` 列来过滤记录，该列还需要被更新，这样记录不会被重复提取。编辑器中的 **高级** 部分允许指定当成功从数据库中提取行时应更新的列。

在这种情况下，`Processed` 列应被更新为值 `1`。

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_db_output_mapping_filtered_updated.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=d822f8e6eecb61b1ef181120b81b576a" alt="MySQL Select configuration with update value for Processed column" width="700" data-path="public/images/mapmysql-810_db_output_mapping_filtered_updated.png" />

现在将从 `test_purchase_orders` 表中提取相应的记录，您必须配置 **Select 配置** 部分，以提取与每个采购订单相关的行项目。在此示例中，行项目数据保存在 `test_po_line_items` 表中。

在 **Select 配置** 中，单击 **添加** 以添加新的子表。选择 `test_po_line_items` 表。编辑器会自动更新左侧面板中的表树结构。与第一个表一样，您可以从 `test_po_line_items` 中选择特定列进行输出；本指南包含所有列。

当 MySQL 端口从 `test_purchase_orders` 表中提取一个采购订单记录时，它还会尝试从 `test_po_line_items` 表中提取记录。只有与当前采购订单关联的订单行才应被提取，所以必须在子表上应用 `WHERE` 约束。

在本例中，`test_po_line_items` 表中的 `PONumber` 列对应 `test_purchase_orders` 表中的 `PONumber` 列。只有订单行中的 `PONumber` 与采购订单中的 `PONumber` 匹配时，记录才应包含在采购订单记录的输出中。

要完成该步骤，请在 `test_po_line_items` 配置的 **过滤器** 部分添加一条新规则：

* 将左侧下拉列表设置为子表中需要检查的列；本例中为 `PONumber`
* 将中间下拉列表设置为 `Equals`
* 将右侧输入设置为引用父表：单击下拉箭头并选择 `REF`（出现 `$` 符号表示引用）
* 将最后一个输入设置为父表中要比较的列；本例中同样为 `PONumber`

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_db_output_mapping_full.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=cd8a0ecee4871569e81402fd48141fc3" alt="MySQL Select configuration with full parent-child relationship" width="800" data-path="public/images/mapmysql-810_db_output_mapping_full.png" />

配置现在已经完成，因此应在 MySQL 端口中保存它。

配置保存后，可以通过两种方式从数据库中提取数据。如果 MySQL 端口的 [自动化页面](/26.2/self-hosted/zh/connectors/database/database#automation-tab) 上启用了 **接收自动化**，端口会根据接收间隔自动尝试提取未处理的采购订单（以及关联的订单行）。或者，您也可以导航到 **交易** 页面并点击 **接收** 来手动提取记录。在这两种情况下，端口都会针对已配置的表运行 SELECT 查询，并生成包含所选数据的输出消息。

#### SELECT 操作输出为 XML

定义 Select 操作后，MySQL 端口将输出表示为 XML。要查看输出的 XML 模型，请点击 **\</> 代码** 按钮：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_output_mapping_code.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=c6ecc86f892c4e1343eb8183a42af953" alt="MySQL connector code view showing XML output structure" width="800" data-path="public/images/mapmysql-810_output_mapping_code.png" />

当 MySQL 端口从数据库中提取数据时，它会使用检索到的值填充具有此结构的 XML 文档。XML 文件中的子结构可以重复。例如，如果为单个采购订单提取多个订单行，则 `test_po_line_items` 表对应的 XML 子部分会针对每个订单行重复。

现在，该 XML 结构必须映射为表示 X12 810 发票的 XML 结构。然后，生成的映射 XML 可以直接转换为 EDI 文档。

<Note>本指南中使用的映射位于 [数据库 XML 结构](#数据库-xml-结构)。</Note>

### 第 2 步：X12 端口

X12 端口在映射工作流中充当两个角色。

1. 它用于生成 810 发票的 XML 表示。这可以通过在 X12 端口的 **交易** 页面上传示例文件，或使用随 X12 端口提供的内置模板文件来完成。在 X12 端口连接到 XML Map 端口后，上传的模板文件和内置模板文件会显示在 XML Map 端口的 **目标** 字段中（以 `connector://` 为前缀）。
2. 当 X12 端口将 XML Map 端口生成的 XML 转换为 X12 文档时，它将执行工作流中的最后一步。

将 X12 端口实例添加到画布。X12 端口实例名称通常应包括发送 X12 文档的交易伙伴名称。在此工作流中，为简单起见，实例名称为 **XML\_To\_X12**。

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mysql_edi_2.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=23fcf486994aaa150fe82e63e721e6f7" alt="X12 connector added to the canvas" width="800" data-path="public/images/mysql_edi_2.png" />

#### 上传测试文件

如果已经有示例 810 发票文件，**上传测试文件** 功能可简化为目标 EDI 文档生成 XML 模型的过程。打开 X12 端口 **交易** 页面，然后单击 **更多 > 上传文件**：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_upload_test_file.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=b85f2ed3de3b12f71b1924312b74ebe4" alt="Upload Files option on the X12 connector Transactions tab" width="800" data-path="public/images/mapmysql-810_upload_test_file.png" />

浏览并上传示例 810 发票。X12 端口会在内部生成该文档的 XML 模型，XML Map 端口可以自动检测到该模型。由于模型是内部存储的，端口的外观和配置不会发生变化。

如果没有示例 810 文件，则当您将 XML Map 端口连接到 X12 端口时，XML Map 端口 **系统设置** 页面上的 **目标文件** 字段会填充可供选择的内置模板文件列表。有关更多信息，请参阅 [选择内置模板文件](/26.2/self-hosted/zh/connectors/xml-map/xml-map-template-files#choose-a-built-in-template-file)。

<Note>本指南中使用的示例 810 文档位于 [X12 810](#x12-810)。</Note>

#### 配置 X12 端口

除了创建一个示例 XML 模型，X12 端口还用于将 XML Map 端口映射的 XML 转换为输出 EDI 文件。它还会根据端口中配置的设置向文档添加交换和功能组数据。因此，必须根据将接收出站 X12 810 文档的交易伙伴的具体设置来配置 X12 端口。

在 X12 端口的 **系统设置** 页面上，将 **转换类型** 属性设置为 `XML-to-X12`。在此模式下运行时，X12 端口的输入应是结构化为 EDI 文档的 XML。使用前一步中 XML Map 端口的 **创建测试文件** 选项，可确保输入 XML 与此特定结构匹配。

默认情况下，勾选了 **动态处理伙伴**。这意味着端口会自动从 EDI 交易中识别并跟踪交易伙伴关系。在这种情况下，不需要手动配置伙伴，因为端口会在许可限制内动态管理所有伙伴。本指南使用默认设置。请参阅 [X12 端口设置](/26.2/self-hosted/zh/connectors/x12#connector-configuration) 了解取消勾选该项后需要执行的操作。

端口还会根据配置的设置填充交换和功能组值。但是，交易伙伴可能需要进一步的交换 (ISA) 和功能组 (GS) 值才能成功接收 810。请与交易伙伴明确沟通需要哪些头部元素。有关更多信息，请参阅 [X12 端口](/26.2/self-hosted/zh/connectors/x12) 文档。

### 第 3 步：XML Map 端口

现在 MySQL 端口和 X12 端口已经分别生成了输入和输出数据的 XML 模型，使用 XML Map 端口将一个 XML 结构转换为另一个。

将 XML Map 端口添加到工作流，然后将 MySQL 端口连接到 XML Map 端口，将 XML Map 端口连接到 X12 端口。

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mysql_edi_3.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=cf0a785f6eeecb32eb281ec69b7da92c" alt="Full flow with MySQL, XML Map, and X12 connectors" width="800" data-path="public/images/mysql_edi_3.png" />

#### 配置 XML Map 端口

在 XML Map 端口的 **XML 映射** 页面上，找到 **源** 和 **目标** 模板文件字段。端口会将 MySQL 端口 [Select 配置](#定义-select-操作) 中的 XML 模型检测为可用的源模板文件，并将 X12 端口测试文件中的 XML 模型检测为可用的目标模板文件。如果任一选项不可用，请验证测试文件和配置是否已正确保存，以及端口是否已在画布上连接。

指定模板文件后，XML Map 可视化设计器会自动填充 MySQL Select 配置和示例 X12 810 文件的 XML 结构。

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_designer.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=fa95b4146ae922d079bd9e6faa80ed58" alt="XML Map connector visual designer with MySQL and X12 structures" width="800" data-path="public/images/mapmysql-810_xml_map_designer.png" />

<Tip>如果目标结构中的 *Interchange* 和 *FunctionalGroup* 元素包含 *Meta* 元素，则可以安全地删除它们。当将 XML 转换为 EDI 时，X12 端口会将相关的元数据应用于 ISA 和 GS 段。</Tip>

#### 理解 XML 结构

要在 XML Map 端口中创建正确的映射关系，需要理解源结构和目标结构如何以 XML 表示。

结构中有两种类型的 XML 元素（节点）。*父* 节点和 *叶* 节点定义了 XML 数据的层级结构，其中父节点对相关元素进行分组，叶节点代表要映射的实际数据值。使用父节点旁边的展开和收起按钮可以显示或隐藏其叶节点。有关更多信息，请参阅 [映射节点](/26.2/self-hosted/zh/mapping/mapping-config#mapping-nodes)。

在 MySQL XML 中，每个父节点代表一张正在提取数据的表，每个叶节点代表该表中的一列。在 810 XML 中，每个 EDI *循环* 和 *段* 都是父节点，每个单独的 EDI *元素* 都是叶节点。

## 创建映射

一旦 XML Map 端口配置了源结构和目标结构，就可以在设计器中拖拽节点来创建映射。

映射主要通过两个步骤创建：

1. 在父节点之间创建 `Foreach` 循环关系
2. 在已建立的 `Foreach` 循环中映射叶节点之间的值

### 创建 Foreach 循环

`Foreach` 关系意味着每当源中出现一个元素时，就会在目标中创建一个新的 XML 结构。例如，如果源中的 *ElementA* 映射到目标中的 *ElementB*，输入文件中每次出现 *ElementA* 都会在输出文件中产生一个 *ElementB* 实例（以及所有 *ElementB* 的子元素）。

在这种情况下，从 PO 表 (`test_purchase_orders`) 中提取的每条记录都会在生成的 EDI 中产生一个新的交易 (TX)，而从订单项表 (`test_po_line_items`) 中提取的每条记录都会产生一个新的 IT1Loop。将 *test\_purchase\_orders* 元素拖到 *TX-00401-810* 元素上，如下图所示：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_foreach_1.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=82edcc7034f05aff3a2656bc2d9897c6" alt="将 test_purchase_orders 拖到 TX-00401-810 以创建 Foreach 循环" width="897" height="503" data-path="public/images/mapmysql-810_xml_map_foreach_1.png" />

现在将 *test\_po\_line\_items* 拖到 *IT1Loop1* 元素上，如下所示：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_foreach_2.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=8aa99010560ec9e2bb3bf58ece354fe5" alt="将 test_po_line_items 拖到 IT1Loop1" width="897" height="501" data-path="public/images/mapmysql-810_xml_map_foreach_2.png" />

<Note>用来创建目标结构的示例 X12 810 文档可能在映射设计器中显示多个 *IT1Loop1* 元素。您可以删除除一个之外的所有这些元素（右键单击节点并选择 **删除节点**），因为 *Foreach* 关系会确保生成正确数量的 *IT1Loop1* 元素。</Note>

### 映射叶节点

建立 `Foreach` 关系后，可以映射循环中叶节点的值。映射叶节点决定了填充由 `Foreach` 循环创建的 XML 结构的值。

一些叶节点映射很简单，另一些则需要使用 [节点值编辑器](/26.2/self-hosted/zh/mapping/mapping-node-value-editor) 或 [代码脚本编辑器](/26.2/self-hosted/zh/mapping/mapping-config#code-script)。以下各节介绍 EDI 文档中常见的叶节点映射类型。有关更多信息，请参阅 [映射](/26.2/self-hosted/zh/mapping/mapping)。

#### 简单叶节点映射

当源文件中的值应直接插入生成的输出中时，只需将叶节点从源拖到目标中的叶节点上，即可映射这些值。此过程需要理解 EDI 目标格式，以及生成的 X12 文档中需要包含哪些值。

例如，X12 810 中的 *BIG04* 元素保存了发票所对应的采购订单编号。它对应数据库输出中的 *PONumber* 元素。将 *PONumber* 节点拖拽到 *BIG/BIG04* 节点上，以建立该关系。

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_one_leaf.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=4bdededee23bf6e26b4bb7f6d36bd1d4" alt="在 XML Map 端口中将 PONumber 映射到 BIG04" width="886" height="502" data-path="public/images/mapmysql-810_xml_map_one_leaf.png" />

映射目标节点右侧的 xpath 表明映射值的来源（上图中为 `PONumber`）。叶节点 xpath 与父节点中的 *Foreach* xpath（`/Items/test_purchase_orders`）相关。可以将所有父节点的 xpath 与叶节点中的 xpath 串联起来，以在源 XML 中找到映射值的完整 xpath：`/Items/test_purchase_orders/PONumber`。

在本例中，其他直接的叶节点映射值包括以下内容：

* PODate > BIG/BIG03
* CurrencyCode > CUR/CUR02
* ItemID > IT1Loop1/IT1/IT107
* Quantity > IT1Loop1/IT1/IT102
* PricePerUnit > IT1Loop1/IT1/IT104
* PONumber（再次） > IT1Loop1/IT1/IT111

可以将这些源值逐个拖放到目标 EDI 文档中，无需修改或条件逻辑。

#### 上下文映射

一些叶节点映射取决于目标中的其他元素。例如，目标 XML 中的 *N1Loop1* 循环是表示 EDI 交易中的参与方或实体（例如开票方、收货方、采购方等）的元素集合。*N1Loop1/N1/N101* 元素指示参与方的特定角色，而参与方的角色决定了应从源 XML 中映射哪些值。

在本指南中，源数据包括收货方和收款方的详细信息。为了在 EDI 输出中包含这些数据，需要两个独立的 *N1Loop1* 结构，一个将 *N1Loop1/N1/N101* 设置为 `ST`（ShipTo 的值），另一个设置为 `RI`（RemitTo 的值）。然后可以通过从源中拖放 *ShipToID* 和 *RemitToID*，映射每个相应循环的 *N1Loop1/N1/N102* 值：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_n1_leaf.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=c599ca8ccbd5d3f3939f9c056b92efcc" alt="将 ShipToID 和 RemitToID 映射到 N1Loop1 结构" width="898" height="502" data-path="public/images/mapmysql-810_xml_map_n1_leaf.png" />

将其他收货方数据（*ShipToAddr*、*ShipToZip* 等）映射到 *N101* 设置为 `ST` 的 *N1Loop1* 结构中的 *N2*、*N3* 和 *N4* 元素。同样，将其他收款方数据（*RemitToAddr*、*RemitToZip* 等）映射到 *N101* 设置为 `RI` 的 *N1Loop1* 结构中的适当元素。

<Note>一些 EDI 循环在 **目标** 映射中需要多个不同的节点（例如本节中的 *N1Loop1* 循环），而其他循环在目标映射中只需要一个节点（例如上一节中的 *IT1Loop1* 循环）。差异取决于 *Foreach* 关系是否会自动生成正确数量的输出节点：由于 *N1Loop1* 节点没有与其关联的 *Foreach* 关系，因此目标映射需要显式包含正确数量的 *N1Loop1* 节点。</Note>

#### 简单计算

一些值不是直接从源 XML 中提取的，而是使用源中的一个或多个值计算得出。例如，每个 *IT1Loop1* 循环（即每个订单行）都有一个 *TXI/TXI02* 元素，代表适用于该订单行的税额。源数据包含计算适用于购买的税额所需的所有信息：

* 单价
* 数量
* 税率

然而，计算得到的税额本身不存在于源中，因此必须在 XML Map 端口中计算。

[节点值编辑器](/26.2/self-hosted/zh/mapping/mapping-node-value-editor) 中的 [格式化器](/26.2/self-hosted/zh/scripting/value-formatters/formatters) 可用于在映射期间处理数据。在本例中，需要使用 [multiply](/26.2/self-hosted/zh/scripting/value-formatters/number-formatters#multiply-value) 格式化器将源数据转换为目标值。

从将 *PricePerUnit* 源节点映射到 *IT1Loop1/TXI/TXI02* 目标节点开始。这是生成合适数学表达式的良好起点，因为单价是表达式的输入变量之一。然后，点击目标节点右侧的平板和铅笔 <img src="https://mintcdn.com/qiao/BoZrJ8_ctEN6h_9z/public/images/xmlmap_expressioneditor_icon.png?fit=max&auto=format&n=BoZrJ8_ctEN6h_9z&q=85&s=7ead9906fb7ee170d403e2536c8a545f" alt="tablet and pencil icon" noZoom style={{ display: "inline-block", verticalAlign: "middle", height: "0.9em", margin: 0 }} width="28" height="26" data-path="public/images/xmlmap_expressioneditor_icon.png" /> 图标，打开下图所示的节点值编辑器：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_tax_expression_1.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=d15898f3775e4d007d2db57055566991" alt="Node Value Editor showing the initial tax expression" width="800" data-path="public/images/mapmysql-810_xml_map_tax_expression_1.png" />

将价格参数与数量相乘，得到该订单行的总成本；使用竖线字符 (`|`) 将 *PricePerUnit* 值传给 `multiply` 格式化器，然后将 `multiply` 格式化器的参数设置为 *Quantity* 源元素中的值：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_tax_expression_2.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=fd5bc74af91deaad48533ee4634d74ce" alt="Tax expression with multiply formatter for quantity" width="800" data-path="public/images/mapmysql-810_xml_map_tax_expression_2.png" />

<Note>*Quantity* 的 `xpath` 值必须用方括号括起来，才能解释为动态值而不是字符串文字。</Note>

此总成本值必须再乘以 *TaxPercent*，并乘以 .01 以从百分比转换为货币值。完整表达式如下图所示：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_tax_expression_3.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=e6725eebbcb25bf396b87f67bfd853b9" alt="Complete tax expression with TaxPercent and .01 multiplier" width="800" data-path="public/images/mapmysql-810_xml_map_tax_expression_3.png" />

保存表达式后，映射会根据指定的动态输入自动计算合适的值。

<Note>如前面的图像所示，使用节点值编辑器时，可以轻松搜索格式化器和源文档中的数据。</Note>

#### 累加计算

输出的 810 文档有一个 *TDS* 段，代表发票的总金额。汇总每个订单行的成本需要一个可在每个订单行循环中更新的持久变量。

需要脚本将值存储在变量中，并在之后的映射中检索这些值。在 {siteNameShort}Script 中，值存储为 *属性*，*项目* 是可以具有多个属性的对象。项目属性使用以下语法引用：`item_name.attribute_name`

`_map` 项是一个特殊项，会在整个文档映射过程中保留属性值。换句话说，大多数属性（变量）只会为单个节点映射保留，而 `_map` 项的属性会在映射中的每个节点上保持不变。

为了汇总每个订单行的成本，请在订单行的 *Foreach* 循环中（即 *IT1Loop1* 循环）设置 `_map` 项的一个属性。由于该脚本没有与 *IT1Loop1* 循环中的任何特定元素相关联，因此应将专用的 [代码脚本](/26.2/self-hosted/zh/mapping/mapping-config#code-script) 元素添加到目标映射。该元素不会出现在生成的 EDI 文档中，但表示正在执行脚本逻辑，以用于映射中的其他用途。

首先，右键单击 *IT1Loop1* 元素并选择 **添加节点 > 添加代码脚本** 来添加一个新的代码脚本元素（可以通过单击并拖动来重新定位该元素）：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_tax_summation_1.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=15a96939770a9efa2f0616f668fdc5bb" alt="Adding a code script element to IT1Loop1" width="800" data-path="public/images/mapmysql-810_xml_map_tax_summation_1.png" />

此脚本需要计算订单行的完整成本，可通过以下公式得到：

`(PricePerUnit * Quantity) + (PricePerUnit * Quantity * TaxPercent * .01)`

编辑脚本元素，用 {siteNameShort}Script 表示以下公式。命名脚本元素，然后点击 **添加脚本**。

```xml theme={null}
<arc:set attr="lineItemCost" value="[xpath(PricePerUnit) | multiply([xpath(Quantity)])]" />
<arc:set attr="lineItemTax" value="[lineItemCost | multiply([xpath(TaxPercent)]) | multiply(.01) | round(2)]" />
<arc:set attr="_map.costSum" value="[_map.costSum | def(0) | add([lineItemCost]) | add([lineItemTax])]" />
```

在每个订单行循环中执行完该脚本之后，*\_map.costSum* 属性会保存所有订单行的总成本（含税）。要在 *TDS/TDS01* 元素中引用此值，请单击 *TDS01* 元素右侧的 `</>` 图标并将 *result.text* 值设置为先前的属性：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_tax_summation_after.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=3b9f010baebefd6d6b15230d104f4f26" alt="TDS01 element with _map.costSum reference" width="600" data-path="public/images/mapmysql-810_xml_map_tax_summation_after.png" />

#### 条件映射

一些节点需要条件逻辑来决定它们是否应出现在输出中，以及其中应设置什么值。例如，*ITD* 段包含有关发票条款的数据，而且只有在指定了条款时，才需要在输出的 810 中包含该段。

可以使用目标映射中 *ITD* 元素旁边的漏斗图标来应用条件，决定该元素（以及它的子元素）是否出现在输出文档中。单击该图标并在映射条件编辑器中添加一条规则；在本例中，采购订单条款具有一个前缀，用来表明是否必须在发票中指定条款：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_xml_map_condition.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=1b8aa3ac8ee9ef76593454bfea0c85ba" alt="Mapping condition editor for the ITD element" width="700" data-path="public/images/mapmysql-810_xml_map_condition.png" />

现在 *ITD* 元素只会在指定了特殊条款时出现，*ITD01* 元素需要保存对应条款的合适代码。在本例中，以下值应用于关联的条款：

* 02 - 加急付款
* 03 - 延迟付款
* 11 - 替代付款模式

输入数据中 *Terms* 元素的值不能使用格式化器转换为 *ITD01* 值，因此需要自定义脚本来决定 *ITD01* 映射。编辑 *ITD01* 元素以打开 **节点值编辑器**，然后使用 **脚本模式** 切换项提供自定义脚本。以下脚本可完成上述模式：

```xml theme={null}
<arc:set attr="termsCode" value="" />
<arc:set attr="termsInput" value="[xpath(Terms)]" />

<arc:select value="[termsInput]">
 <arc:case value="Expedited Payment">
  <arc:set attr="termsCode" value="02" />
 </arc:case>
 <arc:case value="Delayed Payment">
  <arc:set attr="termsCode" value="03" />
 </arc:case>
 <arc:case value="Alternative Payment Profile">
  <arc:set attr="termsCode" value="11" />
 </arc:case>
</arc:select>

<arc:set attr='result.text'>[termsCode]</arc:set>
```

上述示例使用了 [arc:select](/26.2/self-hosted/zh/scripting/keyword-reference/op-arc-select) 关键字，但也可以使用一系列 [arc:if](/26.2/self-hosted/zh/scripting/keyword-reference/op-arc-if) 和 [arc:else](/26.2/self-hosted/zh/scripting/keyword-reference/op-arc-else) 语句得到相同结果。

#### 索引映射

目标 XML 中的一些元素需要对 *Foreach* 关系中的循环次数进行计数。例如，*IT1Loop1/IT1/IT101* 元素保存订单行编号，因此需要为采购订单中的每个订单行递增。同样，*Meta/ST02* 元素需要为文档中的每个采购订单递增。

`_index` 属性是一个特殊变量，保存最局部的 *Foreach* 循环关系的索引（对于嵌套的 *Foreach* 循环，`_index` 指最内层循环）。只要 *Foreach* 关系正确建立，就可以将 *Meta/ST02* 和 *IT1Loop1/IT1/IT101* 都设置为以下值，以正确统计采购订单和订单行：

`[_index]`

如果 *Foreach* 循环中的当前索引与更高级的映射逻辑相关，则此特殊属性在任何自定义脚本环境中也可用。

#### 在映射中生成日期

目标 XML 中的一些元素完全不引用源数据。*BIG/BIG01* 元素代表发票日期，该值直到当前映射步骤才可用。

可以使用特殊的 [now](/26.2/self-hosted/zh/scripting/value-formatters/date-formatters#now-outputformat) 日期格式化器为当前日期时间生成新的日期值。像所有格式化器一样，它可在任何节点映射的节点值编辑器中使用。

所有格式化器都需要一个输入属性来格式化。通常，在 XML 映射期间，输入属性是使用 `xpath` 格式化器检索的值（也就是从源 XML 解析的值）。在本例中，传给 `now` 格式化器的属性并不重要，因为它始终返回当前日期时间。因此，可以将默认 `_` 项作为占位符传递：

<img src="https://mintcdn.com/qiao/3lMD3uYuGJqErRlJ/public/images/mapmysql-810_now_date.png?fit=max&auto=format&n=3lMD3uYuGJqErRlJ&q=85&s=bd3ee692f54065b4245dd820ba8e27d0" alt="Node Value Editor showing the now() date formatter" width="700" data-path="public/images/mapmysql-810_now_date.png" />

<Note>传递给 `now` 格式化器的参数定义了日期的输出格式。</Note>

#### 计算订单行和总成本

*CTT* 段包含用于计算订单行总数的元素，以及表示项目总成本的哈希值。计算这些值的方法与 [累加计算](#累加计算) 中描述的方法类似。

相关的值必须在可以使用订单行数据的映射部分（即 *IT1Loop1* 部分）中进行计算，然后在之后的 *CTT* 映射中引用。这需要一个自定义脚本，将值存储在特殊的 `_map` 项中。

在 *IT1Loop1* 循环中（使用 *Foreach* 关系映射到 `test_po_line_items` 记录），右键单击并选择 **添加节点 > 添加代码脚本** 来添加新节点。脚本会在每次映射循环时执行；换句话说，它会为每个订单行执行一次。为了计算值，需要汇总行数和总成本，然后存储在 `_map` 项的属性中。

订单行数量通过一个简单的计数器计算，而成本可以使用 [累加计算](#累加计算) 中使用的相同公式计算。

```xml theme={null}
<arc:set attr="_map.lineItemCount" value="[_map.lineItemCount | def(0) | add(1)]">

<arc:set attr="currentCost" value="[xpath(PricePerUnit) | multiply([xpath(Quantity)])]" />
<arc:set attr="currentTax" value="[currentCost | multiply([xpath(TaxPercent)]) | multiply(.01) | round(2)]" />

<arc:set attr="_map.totalCost" value="[_map.totalCost | def(0) | add([lineItemCost]) | add([lineItemTax])]" />
```

*CTT01* 元素可以直接映射到 *\_map.lineItemCount*。打开脚本编辑器并引用先前的属性：

```xml theme={null}
<arc:set attr="result.text">[_map.lineItemCount]</arc:set>
```

*CTT02* 元素可以映射到 *\_map.totalCost*。但是，*CTT* 哈希要求忽略小数点，这里等同于将成本乘以 100：

```xml theme={null}
<arc:set attr="result.text">[_map.lineItemCount | multiply(100)]</arc:set>
```

## 工作流总结

MySQL 端口从后端系统中生成采购订单数据的 XML，随后 XML Map 端口将该 XML 映射为 EDI 格式。然后将 EDI XML 传给 X12 端口，以添加交换头并将 XML 转换为 X12 格式。

此工作流中最复杂的步骤是创建 XML 映射。映射需要在父节点之间建立 `Foreach` 关系，然后结合拖放操作、使用节点值编辑器、添加条件和编写自定义脚本来映射单个叶节点。

## 示例数据

### 数据库 XML 结构

```xml theme={null}
<Items>
  <test_purchase_orders selectQuery="SELECT * FROM `test_purchase_orders` WHERE `Processed` = 0">
    <PONumber key="true" />
    <CurrencyCode />
    <CustomerID />
    <PODate type="datetime" />
    <Processed type="int" updateValue="1" />
    <RemitToAddr />
    <RemitToCountry />
    <RemitToID />
    <RemitToState />
    <RemitToZip />
    <ShipToAddr />
    <ShipToCountry />
    <ShipToID />
    <ShipToState />
    <ShipToZip />
    <Terms />
    <test_po_line_items selectQuery="SELECT * FROM `test_po_line_items` WHERE `PONumber` = ${PONumber}">
      <ItemID key="true" />
      <PONumber />
      <PricePerUnit type="double" />
      <Quantity type="int" />
      <TaxPercent type="double" />
    </test_po_line_items>
  </test_purchase_orders>
</Items>
```

### X12 810

```text theme={null}
ISA*00*          *00*          *ZZ*VENDOR         *ZZ*AMAZON         *090322*1001*U*00400*000001631*0*T*>~
GS*IN*VENDOR*AMAZON*20090322*1001*480*X*004010~
ST*810*0001~
BIG*20090320*5002841638*20090311*Q6515853~
CUR*BT*CAD~
N1*RI*VENDORNAME~
N3*PO BOX 1234~
N4*TORONTO*ON*M5W 5M5*CA~
N1*ST*AMAZON CANADA~
N3*500 MCCARTHY DR*FAIRVIEW BUSINESS PARK~
N4*MISSISSAUGA*ON*L5R 3W5*CA~
ITD*01*3*****60~
IT1*1*4*EA*12.6*NT*UP*786932788990*VN*1012380100000*PO*Q2515852~
TXI*GS*2.52*5~
IT1*2*30*EA*29.5*NT*UP*786936725951*VN*1000570100000*PO*Q2515852~
TXI*GS*44.25*5~
IT1*3*90*EA*22.5*NT*UP*782936735321*VN*0543900100000*PO*Q2515852~
TXI*GS*101.25*5~
IT1*4*5*EA*29.5*NT*UP*786936782268*VN*1000570200000*PO*Q2515852~
TXI*GS*7.38*5~
TDS*326330~
TXI*GS*155.4*5*****3108*954090650~
CTT*4*129~
SE*22*0001~
GE*1*480~
IEA*1*000001631~
```
