针对 SAP SD 模块导出的销售订单 / 交货单 / 发票数据,按订单号(AUBEL/AUPOS)优先做三单匹配与差异分析(13 类场景)。
本工具处理 KPMG 数据提取工具导出的 SAP SD TXT(分隔符 #|#),覆盖表 VBAK / VBAP / LIKP / LIPS / VBRK / VBRP。它先将按 BUKRS/VKORG 拆分的每家公司生成“销售订单 / 交货单 / 发票”三张清单,再以订单号为主键完成三单匹配,并依据数量差异与金额差异归类为 13 个场景,最终汇总为全公司差异分析,支撑销售收入审计的勾稽与异常识别。
split_by_vkorg:按 BUKRS(默认)或 VKORG 将原始 TXT 拆分到InPut/<公司代码>/,每文件夹含 6 张表。three_lists:生成销售订单(VBAK+VBAP)、交货单(LIKP+LIPS)、发票(VBRK+VBRP)三张清单,支持--invoice-only。sales_three_match:三单匹配;关联键优先AUBEL/AUPOS(订单号),为空时回退VBELV/POSNV(交货号),并以SHKZG决定借贷正负。difference_analysis:按“订单-发票数量差异 / 订单-发票金额差异”归类 13 场景(含场景 7 缺失发票),分片处理避免 OOM。aggregate_difference_summary:合并各公司差异分析为OutPut/汇总_差异分析_全公司.xlsx(全公司汇总 / 各公司 / 分析报告 sheet)。run_by_company/run_all_by_company:按公司批量执行(清单 → 匹配 → 差异分析),支持--workers并行、--skip、--match-diff-only。clean_cache_and_output:清理 pickle 缓存与输出,便于重跑。utils/:DylanTools(#|# 解析、编码检测、大文件分块、pickle 缓存)、path_utils。
sap-sd-three-match/
├── config.py # 配置(路径、日期范围、售达方排除、订单类型排除等)
├── split_by_vkorg.py # 按 BUKRS/VKORG 拆分原始数据
├── three_lists.py # 生成销售订单、交货单、发票三张清单
├── sales_three_match.py # 三单匹配(核心)
├── difference_analysis.py # 差异分析(13 场景分类)
├── run_by_company.py # 按公司生成三单清单
├── run_all_by_company.py # 按公司执行:清单→匹配→差异分析
├── aggregate_difference_summary.py # 汇总各公司差异分析
├── clean_cache_and_output.py # 清理缓存与输出
├── utils/
│ ├── path_utils.py # 路径与目录创建
│ └── DylanTools.py # TXT 解析、编码检测、分块读取
├── requirements.txt
└── README.md
- Python >= 3.8(
requirements.txt:pandas>=1.5.0、numpy>=1.21.0、openpyxl>=3.0.0、chardet>=4.0.0)。
git clone https://github.com/Gvmeakiss/sap-sd-three-match.git
cd sap-sd-three-match
pip install -r requirements.txt# 1. 拆分原始数据(默认按 BUKRS)
python split_by_vkorg.py --list
python split_by_vkorg.py --code 4390
# 2. 单公司处理(通过环境变量指定数据目录与公司代码)
export SALES_DATA_FOLDER=InPut/4010
export SALES_COMPANY_CODE=4010
python three_lists.py
python sales_three_match.py
python difference_analysis.py
# 3. 按公司批量处理(4 进程并行,跳过 4390)
python run_all_by_company.py --workers 4 --skip 4390
# 4. 汇总全公司差异分析
python aggregate_difference_summary.py- 三单清单:
three_lists读 VBAK+VBAP→订单清单;LIKP+LIPS→交货清单(SHKZGS/H/X 映射数量正负,VBELV/POSNV关联订单);VBRK+VBRP→发票清单。 - 匹配主键:
sales_three_match中VBRP.AUBEL/AUPOS为订单号优先;为空时用VBELV/POSNV(交货号)回退——避免用交货号去匹配订单导致订单数量/金额为 0。 - 差异分类:
difference_analysis.assign_scenario_per_row按“订单-发票数量差异(qty)”与“订单-发票金额差异(amt,容差TOL=0.01)”分 13 场景;四类归并:无差异 / 金额差异 / 数量差异 / 均有差异;场景 7 = 缺失发票。 - 全公司汇总:
aggregate_difference_summary读取各公司差异分析详细文件,回填“订单-发票金额差异”,输出全公司 sheet、各公司 sheet 与分析报告(完全匹配分层统计)。
- 输入:SAP SD 导出 TXT(
#|#),表 VBAK/VBAP/LIKP/LIPS/VBRK/VBRP,按公司代码命名子目录(如InPut/4010/)。 - 输出(
OutPut/):销售三单清单/<公司>/(订单、交货、发票 Excel)SalesThreeMatchResult_<公司>_<期间>.xlsxSalesThreeMatchResult_Untested_<公司>_<期间>.xlsxSalesThreeMatchResult_差异分析_<公司>.xlsx及详细文件OutPut/汇总_差异分析_全公司.xlsx
config.py 关键项:
SALES_DATA_ROOT/SALES_DATA_FOLDER:数据根目录(后者优先)。INVOICE_EXCLUDE_SOLD_TO_CODES:需排除的售达方代码(关联交易等),默认空。ORDER_YEAR/MONTH_START/MONTH_END、DELIVERY_YEAR/...、INVOICE_YEAR/...:日期范围(默认 2025 全年)。ORDER_TYPE_EXCLUDE:AB/ZTR/ZFT/ZRE/ZCR/ZDR/ZFD;MATNR_MAX:物料号上限。MEMORY_SAVE_MODE与分块阈值:大表内存优化;文件匹配*_[0-9]*.TXT;分隔符#|#。
- 数据脱敏:不含真实客户业务数据,示例为脱敏/合成数据。
- pickle 缓存按公司代码命名,避免多公司混用;大文件分块与结果分片以避免 OOM。
- 口径说明:匹配与差异分类口径以代码与配置为准。
可复用的 SAP SD 三单勾稽方法:以发票行为基准,把「销售订单 → 交货 → 发票」拉平到同一订单行粒度,再用数量 × 金额两个正交维度切出 13 个互斥场景。 A reusable SAP SD three-way match method: flatten order → delivery → invoice onto the same order-line grain, then classify by quantity × amount into 13 mutually exclusive scenarios.
split_by_vkorg 按 BUKRS(默认;LIKP 无 BUKRS 时由 VBAK/VBRK 构建 VKORG→BUKRS 映射)拆分原始 TXT → three_lists 生成三单清单(VBAK+VBAP / LIKP+LIPS / VBRK+VBRP,均按 MANDT+VBELN 做表头-行项合并)→ sales_three_match 以发票为基础左连接交货与订单(连接键 VKORG+VBELN+POSNR)→ difference_analysis 逐行判场景 → aggregate_difference_summary 汇总全公司。
Split by company code → build three lists → invoice-anchored left join → per-row scenario → cross-company roll-up.
- 发票侧:
VBRP.AUBEL/AUPOS(销售订单号/行)优先,为空或为 0 时才回退VBELV/POSNV(交货号/行)——实际取数中VBELV常为空,若直接用交货号去匹配订单,订单数量与金额会整列变 0。 - 交货侧:
LIPS.VBELV/POSNV为源订单号/行,为空时用VGBEL/VGPOS(参考单据号/行)补齐。 - 订单行若因多物料出现重复键,先按订单行聚合,避免合并时发票金额被重复计算。
- Order number takes priority, delivery number is only a fallback — a wrong key silently collapses order qty/amount to zero.
SHKZG统一映射{S: +1, H: -1, X: -1}(缺省视为 S),订单/交货/发票三侧同一规则,退货与冲销天然取负值,因此下游差异计算不再按发票类型二次取负。- 数量统一折算到基本单位:订单优先
KLMENG,否则KWMENG × UMVKZ/UMVKN;发票优先FKLMG,否则FKIMG × UMVKN/UMVKZ;交货取LFIMG。金额取NETWR,本币 = 外币 ×WKURS。 - One sign rule across all three documents; all quantities normalized to base UoM before comparison.
容差 TOL = 0.01,两个维度正交组合:
- 数量维度(4 态):订单=交货=发票 / 订单=发票≠交货 / 订单=交货≠发票 / 三者互不相等。
- 金额维度(3 态):订单金额 = 发票金额 / 订单金额 < 发票金额 / 订单金额 > 发票金额(差异 = 订单金额 − 发票金额)。
- 4 × 3 = 12 个互斥场景(编号 1–6、8–13,其中 场景 10 = 完全无差异),再加 场景 7 = 缺失发票(有订单/交货但无发票、无开票金额,取自 Untested 的「仅订单」「仅订单及发货单」sheet)= 13 场景。
- 兜底修正:订单与发票数量、金额均一致而交货数量约为订单数量的整数倍时,判定为单位换算遗留并修正交货数量,使其由场景 3 归入场景 10。
- Quantity pattern (4) × amount relation (3) + missing invoice = 13; scenario 10 = fully matched, scenario 7 = no invoice at all.
- 解析层:
#|#分隔的 TXT,chardet采样自动探测编码(置信度 > 0.7,否则按 gbk / gb2312 / utf-8 等逐个试读兜底);文件 > 50 MB 或强制模式走分块读取(默认 15 万行/块),块内先按日期过滤再合并,单据号/物料号等字段强制为字符串以保留前导零;结果按「表名_公司代码_期间」落 pickle 缓存并校验 TXT 修改时间,重跑秒级命中。 - 执行层:
run_all_by_company.py --workers N以ProcessPoolExecutor按公司并行(pickle 按公司命名,多进程互不串数);差异分析在分片 ≥ 3 时切换分块模式,逐分片读取 → 累加场景统计 → 滚动写出明细(单文件行数上限约 98 万),配合gc.collect()压低内存峰值。 - Chunked TXT parsing + per-company pickle cache + process-level parallelism; sharded accumulation keeps multi-GB extracts out of OOM.
- sap-abap-data-extraction — ECC6 取数 XML 配置与手册
- sap-fi-2026h1 — 2026 H1 FI 凭证/余额处理与勾稽
- test-tools — 采购三单匹配测试与诊断工具集
MIT
Disclaimer: Personal project and personal views. Not affiliated with or endorsed by KPMG or any client.
本仓库为个人项目与个人观点,与任何前/现雇主及客户无关。