Skip to content

Repository files navigation

可配置采购三单匹配工具 ⚙️

以配置文件驱动的通用数据匹配工具包:SQL 解析、数据缓存、匹配聚合、Excel 导出,支撑按配置执行的采购三单匹配与分类审计底稿。

Language License Domain

📌 项目简介

本仓库是一套可配置的通用数据匹配工具包,将三单匹配所需的 SQL 解析、编码检测、数据缓存、匹配键构建、差异计算、分类与 Excel 导出抽象为独立模块,由 config.json 集中驱动匹配阈值、聚合方式、导出与筛选规则。相比固定脚本,它通过配置适配不同项目的数据源与口径,减少重复改码。

✨ 功能特性

  • SQL 解析(sql_parser.py):解析 INSERT INTO ... VALUES 格式 SQL,自动提取列名,处理 NULL 与字符串引号,支持合并多个 SQL 文件(parse_values_line / parse_sql_values / extract_column_names / merge_sql_dataframes)。
  • 文件工具(file_utils.py):get_encoding() 自动检测编码、ensure_dir() 目录管理、read_excel_safe() 安全读取 Excel。
  • 数据缓存(data_cache.py):DataCache 基于 PKL 缓存,load_or_compute() 命中缓存或重算,支持强制重算。
  • 数据匹配(data_matcher.py):normalize_code() 去除 .0 后缀、create_match_key() 构建匹配键、aggregate_dataframe() 聚合、calculate_differences() 差异列、classify_matches() 分类、filter_by_field() 字段筛选。
  • Excel 导出(excel_exporter.py):export_with_classification() 按分类拆分到独立 sheet、create_summary_table() 汇总表、format_excel_accounting() 会计格式、自动处理 Excel 行数上限(EXCEL_MAX_ROWS)。
  • 配置驱动(config.py + config.example.json):Config 类与 get_config(),支持分层键(如 matching.amount_threshold)与配置文件加载。

📂 目录结构

purchase-three-match-configurable/
├── README.md
├── LICENSE
├── .gitignore
├── __init__.py                 # 包入口,重导出各子模块(以 ToolKit 名义导入)
├── config.py                   # Config 类与默认配置
├── config.example.json         # 配置示例(复制为 config.json 使用)
├── sql_parser.py               # SQL 解析
├── file_utils.py               # 编码检测 / 安全读 Excel
├── data_cache.py               # PKL 数据缓存
├── data_matcher.py             # 匹配键 / 聚合 / 差异 / 分类
├── excel_exporter.py           # 分类导出与格式化
├── example_usage.py            # 使用示例
├── config_usage_example.py     # 配置使用示例
├── USAGE_GUIDE.md              # 使用指南
├── CONFIG_REFERENCE.md         # 配置参考
├── PROJECT_SUMMARY.md          # 项目总结
└── requirements.txt            # pandas / numpy / openpyxl / chardet

🔧 环境要求

  • Python ≥ 3.8。
  • 依赖(见 requirements.txt):pandas>=2.0、numpy>=1.24、openpyxl>=3.1、chardet>=5.0。

🚀 安装

git clone https://github.com/Gvmeakiss/purchase-three-match-configurable.git
cd purchase-three-match-configurable
pip install -r requirements.txt

💡 快速开始 / 使用示例

  1. 复制示例配置并修改:
cp config.example.json config.json
  1. 在代码中以 ToolKit 名义调用(见 __init__.py 重导出):
from ToolKit import Config, get_config, sql_parser, data_matcher, excel_exporter

config = Config('config.json')            # 或从 get_config() 取默认配置
threshold = config.get('matching.amount_threshold')
output_dir = config.get_output_dir()

# 解析 SQL
expected_cols = ['order_no', 'item_code', 'quantity', 'amount']
df, encoding = sql_parser.parse_sql_values('data.sql', expected_cols)

# 构建匹配键并聚合
df['match_key'] = data_matcher.create_match_key(
    df, key_cols=['order_no', 'item_code'],
    normalize_func=data_matcher.normalize_code)
  1. 运行内置示例:python example_usage.py / python config_usage_example.py。

🧠 核心逻辑(方法论)

  • 配置驱动匹配:config.example.json 定义 matching(金额阈值 amount_threshold=1.0、数量阈值 quantity_threshold=0.01、匹配键分隔符、编码规范化)、aggregation(numeric_agg=sum / text_agg=first)、excel、paths、data_sources、classification、filtering、logging。
  • 匹配键与差异:create_match_key() 按 key_cols 拼接(可选 normalize_code 去除 .0),calculate_differences() 生成差异列,classify_matches() 依据 classification.categories 将记录分为「全部数据 / 2.Not test / 1.1完全匹配 / 1.2金额不一致 / 1.3数量不一致 / 1.4均不一致」。
  • 分类导出:export_with_classification() 把各分类拆到独立 sheet 并自动创建汇总表,单 sheet 超过 max_rows_per_sheet(默认 1,048,575)时自动分 sheet;format_excel_accounting() 套用会计规范格式(冻结表头、NA 显示为 N/A)。
  • 性能与一致性:DataCache 以 PKL 缓存中间结果,删除 PKL 即强制重算;get_encoding() 自动检测文件编码避免乱码。

📋 输入与输出

  • 输入:INSERT INTO ... VALUES 格式 SQL 文件或 Excel 文件;字段列由调用方在 parse_sql_values() / read_excel_safe() 中指定。
  • 输出:按分类拆分的多 sheet Excel 工作簿 + 汇总表,口径与分类来自 config.json。

⚙️ 配置说明

以 config.example.json 为准,关键分组:

分组 关键项 默认值
matching amount_threshold / quantity_threshold / normalize_code 1.0 / 0.01 / true
aggregation numeric_agg / text_agg sum / first
excel max_rows_per_sheet / sheet_name_max_length / auto_format / freeze_header 1048575 / 31 / true / true
paths cache_dir / output_dir / log_dir PKL / output / logs
classification categories(6 类) 见上
filtering sales_org_field / sales_org_values / case_sensitive 销售组织 / [1240,1250,1260] / false

⚠️ 注意事项

  • 数据脱敏:不含真实客户业务数据;示例为脱敏/合成数据(如 NewHope、AQPP 等化名场景)。
  • Excel 限制:单 sheet 最大 1,048,575 行,超出自动分 sheet;sheet 名最多 31 字符。
  • 编码与缓存:用 get_encoding() 自动检测编码;处理大文件注意内存,可删除 PKL 强制重算。

🧠 核心方法论 / Core Methodology

本仓库的可复用资产不是"某一次三单匹配",而是把匹配过程抽象成一条配置驱动的通用数据匹配流水线:口径变了改配置,代码不动。 The reusable asset is a config-driven matching pipeline, not a one-off script.

五阶段流水线 / Five-stage pipeline

阶段 实现 关键约定
① 解析 Parse sql_parser.parse_sql_values() / extract_column_names() chardet 先探测编码再逐行扫描;parse_values_line() 用引号状态机切分,字符串内的逗号不误切、NULL → None、外层单引号剥离;列数不足的行丢弃、超出的列截断到 expected_cols;merge_sql_dataframes() 取列名并集、缺列补 NaN 后纵向拼接
② 缓存 Cache data_cache.DataCache.load_or_compute() 幂等约定:cache_name 命中 PKL 直接复用,未命中才执行 compute_func 并回写;force_reload=True 或删除 PKL 即强制重算,让重跑成本趋近于零
③ 匹配键 Key data_matcher.create_match_key() + normalize_code() 多列按 key_cols 顺序转字符串后拼成单列键;normalize_code() 统一 NaN → '' 并剥掉数值转文本产生的 .0 尾巴(混源数据错配的头号原因);键列不存在即 ValueError 快速失败
④ 聚合 Aggregate aggregate_dataframe() groupby(key, as_index=False).agg(agg_dict) 把明细压成"一键一行",后续才能做 1:1 三方比对;数值列默认 sum、文本列默认 first;分组列若混进聚合字典直接报错
⑤ 差异与分类 Diff & Classify calculate_differences() → classify_matches() 见下方两条契约

差异—分类契约 / Diff & classification contract

  • 差异列:base_cols 与 compare_cols 按位配对,两侧 pd.to_numeric(errors='coerce') 后相减并 round(2),列名固定为 差异-<基准列>;任一列缺失则跳过该对而不抛错。
  • 分类维度:先按 key_cols 是否含空值标出 2.Not test(不可测样本),再以 abs(差异) 与阈值比较产出四个互斥布尔列 1.1完全匹配 / 1.2金额不一致 / 1.3数量不一致 / 1.4均不一致,且全部与 ~2.Not test 相与——不可测的行不进入任何匹配结论。
  • 容差而非等值:金额阈值 matching.amount_threshold(默认 1.0)、数量阈值 matching.quantity_threshold(默认 0.01),规避浮点与尾差噪音;无法转成数值的差异被填为哨兵值 999,即默认判为不匹配(保守口径,宁可多报不可漏报)。

导出契约 / Export contract

  • export_with_classification():第一个 sheet 恒为「汇总表」,其后每个分类一个 sheet;未显式传 categories 时按上述布尔列自动拆分,空分类不出 sheet;sheet 名截断至 excel.sheet_name_max_length,单分类超 excel.max_rows_per_sheet 时自动切为 _P1/_P2...;空数据集直接跳过导出。
  • create_summary_table():输出「分类 / 记录数 / 金额」并追加「总计」行,金额列由 classification.summary_amount_col 指定或按列名含「金额 / amount」自动识别——先给结论、再给底稿。
  • format_excel_accounting():表头填色加粗、冻结首行、全表细边框、数字右对齐文本居中、列宽自适应(上限 50 字符),NaN 统一显示为 N/A,导出即可直接进入复核流程。

为什么配置驱动优于固定脚本 / Why config-driven beats hard-coded scripts

config.py 以「内置默认配置 + JSON 递归合并 + 点号路径读取」(Config.get('matching.amount_threshold'))把阈值、聚合方式、目录、分类名、筛选口径与 Excel 行为全部外置,get_config() 单例保证同一次运行内口径一致。换项目或换数据源时只改 config.json,避免为每个场景 fork 一份脚本而导致口径漂移与逻辑分叉;filter_by_field() 的字段筛选同样把两侧统一转字符串比较,规避"数字型编码 vs 文本型编码"这一经典陷阱。 Thresholds, aggregation, classification names and export behaviour live in config — one engine, many engagements.

🔗 相关仓库

📄 License

MIT


Disclaimer: Personal project and personal views. Not affiliated with or endorsed by KPMG or any client.
本仓库为个人项目与个人观点,与任何前/现雇主及客户无关。

About

Configurable purchase three-way matching toolkit · 可配置采购三单匹配工具

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages