文 / 老汪(汽车零部件财务与商业智能实战派)
专注制造业财务中台构建、Power Query/Power BI 自动化建模与业财融合落地
01 老汪开篇·痛点暴击:质检异常备注里的“索赔暗礁”
在汽车零部件制造企业(Tier 1)的供应链财务对账中,月末最让应付会计和成本会计头疼的,莫过于“供应商质量索赔与让步接收扣款”。
每一批来料入厂,质量部(IQC)都会在 ERP 或 MES 系统中录入检验台账。质检员在填写“检验异常与技术协议描述”时,几乎是自由文本录入:
– 有的写:“荧光探伤发现轮齿根部微细裂纹,判定NG严重不合格”;
– 有的写:“尺寸超差且带有ng标识,复核不合格”;
– 有的写:“压铸件局部密集气孔超标,强度测试不达标,建议报废”;
– 有的写:“法兰连接螺孔内壁轻微氧化,尺寸合格,允许让步接收”;
– 有的写:“包装箱轻微破损,外箱标签打码模糊,实物检验OK”。
按照主机厂与供应链质量协议:
1. 致命与严重缺陷(如裂纹、气孔、报废、明确判定 NG):必须整批退货,并按采购总额的 20% 计提质量违约索赔款,当期货款一律冻结不付!
2. 一般工艺与加工缺陷(如超差、氧化、划伤、滑丝):经工程评审特采让步接收,按 5% 扣减结算款;
3. 轻微标识与包装缺陷(如打码模糊、外箱破损):让步接收,按 1% 扣减管理费;
4. 合格检验:全额放行付款。
很多财务人员怎么做?
要么在 Excel 里用肉眼一条条读备注筛选,要么在单元格里套一层又一层的 IF(ISNUMBER(FIND(...)))。但很快就会遇到两颗“致命暗礁”:
– 暗礁一:大小写敏感导致漏判! 质检员随手打的小写 "ng",用大写 "NG" 去找直接返回错误或找不到,导致几十万的严重质量缺陷批次被当作合格放行付款,给公司带来不可挽回的资金损失!
– 暗礁二:多层条件逻辑臃肿不堪! 包含“裂纹”或“气孔”或“报废”,公式写得如同天书,一旦来料几千批,Excel 瞬间卡死。
今天老汪带大家拆解 M 语言中最为核心的文本查找函数——Text.Contains,结合第三参数比较器 Comparer.OrdinalIgnoreCase,打造一套工业级、免疫大小写、毫秒级智能打标与索赔扣款自动稽核模型!
02 函数档案·语法拆解
2.1 函数签名与参数说明
在 Power Query M 语言中,Text.Contains 的官方函数签名如下:
Text.Contains(
string as nullable text,
substring as text,
optional comparer as nullable function
) as nullable logical
| 参数位置 | 参数名 | 数据类型 | 业务含义与输入要求 |
|---|---|---|---|
| 第 1 参数 | string | nullable text | 被查找的原始文本(如质检异常描述字段、摘要字段、物料型号等)。允许为 null。 |
| 第 2 参数 | substring | text | 需要搜索的目标子文本(如 “裂纹”、”NG”、”气孔” 等关键字)。 |
| 第 3 参数 | optional comparer | nullable function | 【核心精髓】可选的比较器函数。如果不填,默认按二进制字符序严格区分大小写;若传入 Comparer.OrdinalIgnoreCase,则不区分英文字母大小写! |
2.2 核心比较器机制:大小写敏感的“天坑”与破局之道
孙老师在逐字稿中特别强调:默认情况下,Text.Contains 是区分大小写的!
我们通过一组直观对比感受其底层差异:
// 场景 1:不填第三参数(默认区分大小写)
Text.Contains("零件尺寸超差(ng)", "NG")
// 结果:false ❌(因为原串是小写 ng,目标是大写 NG,直接漏判!)
// 场景 2:显式指定忽略大小写比较器
Text.Contains("零件尺寸超差(ng)", "NG", Comparer.OrdinalIgnoreCase)
// 结果:true ✅(成功命中,杜绝漏网之鱼!)
老汪划重点:
在工业级制造业数据清洗中,现场质检员、仓管员在移动端 PDA 或车间终端录入英文代码(如NG、OK、SUS、Ra、ppm)时,输入法经常随机处于小写或大写状态。凡是涉及英文标识的模糊匹配,第三参数必须无条件加上Comparer.OrdinalIgnoreCase!
03 业务场景重构:某汽车零部件制造厂来料质检与索赔台账
我们以国内某头部新能源汽车 Tier 1 零部件制造企业为例。企业当月采购入厂了 10 批次核心零部件,包含变速箱齿轮、副车架、制动卡钳、前挡风玻璃等。
3.1 原始入厂质检业务底表
| 送检批次号 | 供应商名称 | 零部件名称与图号 | 送货数量(件) | 批次采购总额(元) | IQC检验异常与技术协议描述 |
|---|---|---|---|---|---|
| IQC-2026-001 | 精锻精密齿轮 | 变速箱二轴从动齿轮(G-208) | 1,200 | ¥360,000.00 | 荧光探伤发现轮齿根部微细裂纹,判定NG严重不合格 |
| IQC-2026-002 | 拓普底盘科技 | 铝合金一体副车架(SF-04) | 450 | ¥405,000.00 | 压铸件局部密集气孔超标,强度测试不达标,建议报废 |
| IQC-2026-003 | 万向传动轴 | 双万向节传动总成(DS-99) | 600 | ¥240,000.00 | 法兰连接螺孔内壁轻微氧化,尺寸合格,允许让步接收 |
| IQC-2026-004 | 伯特利制动 | 电子驻车EPB制动卡钳(BC-12) | 800 | ¥320,000.00 | 密封面存在0.2mm轻微划伤,未伤及油封槽 |
| IQC-2026-005 | 华阳车用连接 | 高压线束接插件端子(HT-08) | 5,000 | ¥150,000.00 | 抽检发现5件内螺纹滑丝超差,尺寸超差需特采返工 |
| IQC-2026-006 | 敏实汽车饰件 | 车窗镀铬装饰亮条(TR-55) | 1,500 | ¥180,000.00 | 包装箱轻微破损,外箱标签打码模糊,实物检验OK |
| IQC-2026-007 | 保隆传感器 | 胎压监测高频传感器(TP-03) | 2,000 | ¥220,000.00 | 各项电性能检测合格,外观整洁无异常 |
| IQC-2026-008 | 福耀汽车玻璃 | 双层夹胶前挡风玻璃(WS-88) | 300 | ¥210,000.00 | 玻璃边缘微小崩边,尺寸超差且带有ng标识,复核不合格 |
| IQC-2026-009 | 三花智控 | 新能源膨胀热力阀(EX-02) | 1,000 | ¥280,000.00 | 气密性实验微漏,内腔含砂眼气孔,严重不合格 |
| IQC-2026-010 | 常春汽车内饰 | 副仪表板中央通道饰件(CP-66) | 800 | ¥160,000.00 | 表面皮纹轻微划痕与划伤,试装配合良好,特采使用 |
04 M 语言核心代码深度剖析
💡 老汪敲黑板(免改路径直接刷新秘籍):
本讲义配套的随堂 Excel 课件包内置了动态自适应路径公式。下载解压后无需手动修改文件绝对路径,点击全部刷新即可直接复现全部清洗步骤!实操指南点此篇文章:《实操指南》
针对上述业务需求,我们在 Power Query 中建立一条完整的端到端自动化流水线。
// =========================================================================
// 案例:EP048 Text.Contains 文本包含与智能质检打标(Q001 动态相对路径标准)
// 业务背景:汽车零部件制造企业(Tier 1)入厂质检(IQC)与供应商索赔扣款对账台账
// 核心函数:Text.Contains(string, substring, optional comparer) 与 Comparer.OrdinalIgnoreCase
// 作者:老汪(汽车零部件财务与商业智能实战派)
// =========================================================================
let
// 1. 动态获取当前工作簿所在文件夹相对路径(免疫文件移动与跨电脑拷贝路径报错)
当前工作簿 = Excel.CurrentWorkbook(),
配置表 = 当前工作簿{[Name="配置"]}[Content],
当前文件夹路径 = 配置表{0}[当前值],
// 2. 动态拼接并加载【原始业务数据表.xlsx】
目标数据文件 = 当前文件夹路径 & "原始业务数据表.xlsx",
文件二进制 = File.Contents(目标数据文件),
打开Excel = Excel.Workbook(文件二进制, null, true),
质检台账原始表 = 打开Excel{[Item="供应商零部件入厂质检台账", Kind="Sheet"]}[Data],
// 3. 提升首行作为标题并规范数据类型
提升标题 = Table.PromoteHeaders(质检台账原始表, [PromoteAllScalars=true]),
规范基础类型 = Table.TransformColumnTypes(提升标题, {
{"送检批次号", type text},
{"供应商名称", type text},
{"零部件名称与图号", type text},
{"送货数量(件)", Int64.Type},
{"批次采购总额(元)", type number},
{"IQC检验异常与技术协议描述", type text},
{"检验员", type text}
}),
// 4. 核心步骤:运用 Text.Contains 与 Comparer.OrdinalIgnoreCase 智能判定质量缺陷等级
// 避坑要点:M 函数默认区分大小写!如果不加 Comparer.OrdinalIgnoreCase,
// 质检员填写的 "ng" 或 "NG" 就会发生漏判,导致巨额质量索赔款流失!
添加质量缺陷等级 = Table.AddColumn(规范基础类型, "质量缺陷等级", each
let
desc = [IQC检验异常与技术协议描述]
in
if desc = null then "检验合格"
// 第一层:致命/严重质量缺陷(裂纹、气孔、报废、或包含不区分大小写的 ng / NG)
else if Text.Contains(desc, "裂纹")
or Text.Contains(desc, "气孔")
or Text.Contains(desc, "报废")
or Text.Contains(desc, "ng", Comparer.OrdinalIgnoreCase) then "严重质量缺陷"
// 第二层:一般工艺与加工缺陷(超差、氧化、划伤、划痕、滑丝)
else if Text.Contains(desc, "超差")
or Text.Contains(desc, "氧化")
or Text.Contains(desc, "划伤")
or Text.Contains(desc, "划痕")
or Text.Contains(desc, "滑丝") then "一般工艺缺陷"
// 第三层:轻微包装与标识缺陷(打码、标签、包装)
else if Text.Contains(desc, "打码")
or Text.Contains(desc, "标签")
or Text.Contains(desc, "包装") then "轻微标识缺陷"
// 第四层:合格放行
else "检验合格",
type text
),
// 5. 根据质量缺陷等级,判定处理结论与索赔扣款比例
添加处理结论与比例 = Table.AddColumn(添加质量缺陷等级, "处置与扣款方案", each
if [质量缺陷等级] = "严重质量缺陷" then [结论="退货并索赔违约金20%", 扣款比例=0.20, 实付退货=true]
else if [质量缺陷等级] = "一般工艺缺陷" then [结论="特采让步接收扣款5%", 扣款比例=0.05, 实付退货=false]
else if [质量缺陷等级] = "轻微标识缺陷" then [结论="让步接收扣款1%", 扣款比例=0.01, 实付退货=false]
else [结论="正常全额放行付款", 扣款比例=0.00, 实付退货=false],
type record
),
展开处置方案 = Table.ExpandRecordColumn(添加处理结论与比例, "处置与扣款方案",
{"结论", "扣款比例", "实付退货"},
{"处理结论", "索赔扣款比例", "是否退货"}
),
// 6. 精确计算质量索赔扣款金额与最终应付结算净额
添加索赔扣款金额 = Table.AddColumn(展开处置方案, "质量索赔扣款金额(元)", each
[批次采购总额(元)] * [索赔扣款比例],
type number
),
添加应付结算净额 = Table.AddColumn(添加索赔扣款金额, "应付结算净额(元)", each
if [是否退货] then 0.0 // 严重缺陷整批退货,货款不付,另行扣收索赔款
else [批次采购总额(元)] - [质量索赔扣款金额(元)],
type number
),
// 7. 整理最终列并移除中间临时列
移除临时列 = Table.RemoveColumns(添加应付结算净额, {"是否退货", "检验员"}),
// 8. 规范最终报表字段类型
规范最终类型 = Table.TransformColumnTypes(移除临时列, {
{"索赔扣款比例", Percentage.Type},
{"质量索赔扣款金额(元)", type number},
{"应付结算净额(元)", type number}
})
in
规范最终类型
05 最终清洗与索赔稽核成果
通过上述查询刷新后,瞬间呈现出条理极其清晰的业财合规稽核报表:
| 送检批次号 | 供应商名称 | 批次采购总额 | 质量缺陷等级 | 处理结论 | 索赔比例 | 索赔扣款金额 | 应付结算净额 |
|---|---|---|---|---|---|---|---|
| IQC-2026-001 | 精锻精密齿轮 | ¥360,000.00 | 严重质量缺陷 | 退货并索赔违约金20% | 20.0% | ¥72,000.00 | ¥0.00 |
| IQC-2026-002 | 拓普底盘科技 | ¥405,000.00 | 严重质量缺陷 | 退货并索赔违约金20% | 20.0% | ¥81,000.00 | ¥0.00 |
| IQC-2026-003 | 万向传动轴 | ¥240,000.00 | 一般工艺缺陷 | 特采让步接收扣款5% | 5.0% | ¥12,000.00 | ¥228,000.00 |
| IQC-2026-004 | 伯特利制动 | ¥320,000.00 | 一般工艺缺陷 | 特采让步接收扣款5% | 5.0% | ¥16,000.00 | ¥304,000.00 |
| IQC-2026-005 | 华阳车用连接 | ¥150,000.00 | 一般工艺缺陷 | 特采让步接收扣款5% | 5.0% | ¥7,500.00 | ¥142,500.00 |
| IQC-2026-006 | 敏实汽车饰件 | ¥180,000.00 | 轻微标识缺陷 | 让步接收扣款1% | 1.0% | ¥1,800.00 | ¥178,200.00 |
| IQC-2026-007 | 保隆传感器 | ¥220,000.00 | 检验合格 | 正常全额放行付款 | 0.0% | ¥0.00 | ¥220,000.00 |
| IQC-2026-008 | 福耀汽车玻璃 | ¥210,000.00 | 严重质量缺陷 | 退货并索赔违约金20% | 20.0% | ¥42,000.00 | ¥0.00 |
| IQC-2026-009 | 三花智控 | ¥280,000.00 | 严重质量缺陷 | 退货并索赔违约金20% | 20.0% | ¥56,000.00 | ¥0.00 |
| IQC-2026-010 | 常春汽车内饰 | ¥160,000.00 | 一般工艺缺陷 | 特采让步接收扣款5% | 5.0% | ¥8,000.00 | ¥152,000.00 |
06 财务 BP 管理洞察·避坑指南
1. 为什么福耀玻璃批次(IQC-2026-008)能被精准识别?
在该批次中,质检员写的是:“玻璃边缘微小崩边,尺寸超差且带有ng标识,复核不合格”。
注意两点:
– 它同时包含了“超差”和“ng”。如果把“超差”的判断放在第一层,就会被错误归入“一般工艺缺陷”(仅扣款 5%);
– 但因为我们在 M 语言规则中优先判决致命的“严重缺陷”,且使用了 Comparer.OrdinalIgnoreCase,即使质检员随手打的是小写 "ng",模型也敏锐抓取并判定为 严重质量缺陷(退货并索赔 20%,扣款 4.2 万元)!业务优先级的层级判定与忽略大小写比较器缺一不可!
2. 遭遇 null 值的健壮性防空处理
在实际业务中,许多完全合格的批次,质检员根本不会在异常描述栏填写任何字(即单元格为空 null)。
在 M 语言中:
Text.Contains(null, "裂纹") // 返回 null,而不是 false!
如果直接写 if Text.Contains(...) then ...,当遇到 null 时,虽然不走 then,但在复合逻辑 or 运算中极易引发逻辑歧义。老汪的黄金写法:在最前端加上 if desc = null then "检验合格",防患于未然!
07 随堂练习与课件下载
请各位财务小伙伴下载随堂课件包,打开 最终完成版_带PQ查询.xlsx,观察【配置】表中当前的相对路径动态抓取,并尝试:
1. 修改原始表中某一条记录的描述为 "探伤OK,外包装标签破损",点击全部刷新,观察其是否自动打标为“轻微标识缺陷”;
2. 如果你的企业有数十个敏感词库,如何结合 List.AnyTrue 与 Text.Contains 实现动态多词库批量匹配?(我们将在后续进阶课程中深入揭秘)。














暂无评论内容