
简介这份文档面向企业ERP顾问、供应链与库存管理人员以及正在学习SAP物料主数据配置的技术人员系统讲解基于库存管理的MPN制造商零件编号应用帮助解决同一物料在不同制造商之间编号不统一、买卖双方数据难以无损对接的问题。资源包共1个doc文件约566KB内容围绕Inventory-Managed MPN展开涵盖概述、架构、系统配置与实例等模块重点说明部物料号、制造商编号、外部制造商编号、FFF类物料及替换关系的建立逻辑。读者可从中掌握物料管理、采购管理、库存管理与MPN管理四大模块的协作方式并通过创建制造商XK01、供应商XK01、部物料MM01、关联物料MM01、维护替换关系PIC01以及采购订单ME21N、收货MIGO、库存查询MMBE、采购替换件ME22N等完整实例理解替换件库存共享与替换作业的实现路径。目前已有223人学习适合需要提升供应链数据对接与库存管理效率的从业者参考。1. 从一份“基于库存管理的MPN应用.doc”说起制造业物料主数据到底该怎么落地如果你在制造业信息化岗位待过大概率见过这样的场景采购催着要料仓库说系统里查不到这个型号工程师翻出一份 Word 文档里面密密麻麻列着物料编码、厂商型号、替代关系文件名就叫“基于库存管理的MPN应用.doc”。这份文档往往就是整个工厂物料主数据的“黑匣子”——谁都在用谁都不敢改改完还没人知道对不对。MPN 是 Manufacturer Part Number制造商零件编号。它和内部物料编码Internal Part Number最大的区别在于内部编码是企业自己编的MPN 是原厂给的。库存管理里最头疼的问题之一就是同一个物料可能对应多个 MPN同一个 MPN 也可能因为封装、批次、版本差异对应多个内部编码。这份文档要解决的就是把这层多对多关系管起来让采购、仓库、计划、工程四个角色看到同一套数据。适合谁看适合正在做 ERP、MES、WMS 物料主数据模块的开发和实施人员也适合被 Excel 和 Word 折磨了很久、想把这套东西系统化的工厂 IT。2. MPN 与库存管理的映射逻辑先搞清楚一对多、多对一和多对多2.1 为什么不能直接把 MPN 当物料编码用很多小厂起步阶段图省事直接把厂商型号当物料编码录进系统。短期看没问题时间一长就翻车。原因有三个第一同一颗料不同厂商的 MPN 完全不同但功能可以互换采购会按价格切换供应商库存账就对不上第二原厂会改版本号比如从 A 版改到 B 版MPN 只差一个后缀但封装或电气参数变了仓库如果按字符串匹配就会把两批料混在一起第三客户 BOM 里写的是 MPN内部生产领料用的是内部编码中间没有映射表计划排产时就得靠人工翻译。常见做法是建三层结构内部物料编码唯一→ MPN 映射表一对多→ 厂商主数据厂商代码、厂商 MPN、封装、生命周期状态。库存扣账永远走内部编码采购下单和来料检验走 MPN工程变更走映射关系。这样即使采购换供应商只要新 MPN 挂到同一个内部编码下库存和计划就不受影响。2.2 用一张映射表把 MPN 和内部编码串起来下面这张表是我在多个项目里用过的最小可用结构字段不多但能覆盖 80% 的库存管理场景。注意internal_pn和mpn的组合要加唯一约束否则同一颗料重复录入会导致库存重复扣减。字段名类型说明idbigint自增主键internal_pnvarchar(64)内部物料编码唯一标识一颗料mpnvarchar(128)厂商零件编号区分大小写manufacturervarchar(64)厂商名称或代码packagevarchar(32)封装形式如 0603、QFNlifecyclevarchar(16)生命周期Active/NRND/EOLpriorityint替代优先级数字越小越优先created_atdatetime创建时间updated_atdatetime更新时间建表 SQL 如下注意UNIQUE KEY那行它防止同一内部编码下重复挂同一个 MPNCREATE TABLE mpn_mapping ( id BIGINT AUTO_INCREMENT PRIMARY KEY, internal_pn VARCHAR(64) NOT NULL, mpn VARCHAR(128) NOT NULL, manufacturer VARCHAR(64) NOT NULL, package VARCHAR(32) DEFAULT NULL, lifecycle VARCHAR(16) DEFAULT Active, priority INT DEFAULT 100, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_internal_mpn (internal_pn, mpn), KEY idx_mpn (mpn), KEY idx_internal (internal_pn) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;逻辑说明internal_pn和mpn的联合唯一索引是核心它保证同一颗料不会因为重复导入而出现两条相同映射。idx_mpn用于采购按厂商型号反查内部编码idx_internal用于仓库按内部编码查所有可用 MPN。priority字段在替代料场景下决定先用哪个 MPN 的库存数字小的优先出库。参数说明lifecycle建议用枚举值而不是自由文本否则 EOL 和 NRND 会被写成各种拼写。package字段在电子料场景下必须填因为同一 MPN 不同封装可能对应不同内部编码。manufacturer如果你们有厂商主数据表这里存厂商代码而不是全称避免“TI”和“Texas Instruments”被当成两个厂商。2.3 库存扣减时怎么按优先级选 MPN有了映射表库存扣减的逻辑就清晰了。假设仓库里同一个内部编码下有三个 MPN 都有库存系统应该按priority从小到大扣而不是随机选。下面这段伪代码展示了核心逻辑用 Python 写出来方便理解def deduct_stock(internal_pn, required_qty, cursor): # 按优先级升序取出该内部编码下所有有库存的 MPN cursor.execute( SELECT m.mpn, s.qty, s.location FROM mpn_mapping m JOIN stock s ON s.mpn m.mpn WHERE m.internal_pn %s AND s.qty 0 ORDER BY m.priority ASC, s.qty DESC , (internal_pn,)) rows cursor.fetchall() remaining required_qty for mpn, qty, location in rows: if remaining 0: break take min(qty, remaining) # 执行扣减并记录扣的是哪个 MPN cursor.execute( UPDATE stock SET qty qty - %s WHERE mpn %s AND location %s , (take, mpn, location)) remaining - take if remaining 0: raise Exception(f库存不足缺口 {remaining})逻辑说明先按priority排序再按库存量降序目的是优先消耗优先级高且库存多的 MPN减少拆批次数。每次扣减都记录具体 MPN这样后续追溯时能知道哪批货发给了哪个客户。如果扣完还有缺口直接抛异常让上层计划去处理而不是静默扣成负数。参数说明required_qty是本次需求数量cursor是数据库游标。实际项目中建议把这段逻辑放在存储过程或事务里避免并发扣减导致超卖。ORDER BY里的s.qty DESC是经验值如果你们仓库有先进先出要求应该改成按入库时间排序。3. 从 Word 文档到数据库MPN 数据清洗与导入的完整步骤3.1 先解析 .doc 里的表格结构别急着写库“基于库存管理的MPN应用.doc”这类文档通常是人手工维护的格式极其不统一。有的用表格有的用段落有的把 MPN 写在括号里。直接读文本会得到一堆噪声。我一般先用 Python 的python-docx把文档里所有表格抽出来按行转成二维列表再人工确认列含义。from docx import Document def extract_tables(doc_path): doc Document(doc_path) all_rows [] for table in doc.tables: for row in table.rows: cells [cell.text.strip() for cell in row.cells] # 跳过空行和表头 if not any(cells): continue if cells[0] in (内部编码, 物料编码, Internal PN): continue all_rows.append(cells) return all_rows rows extract_tables(基于库存管理的MPN应用.doc) for r in rows[:5]: print(r)逻辑说明doc.tables只抓表格内容段落里的文字不处理因为段落格式太自由强行解析反而容易出错。跳过表头那行是为了后续直接映射字段。打印前 5 行用于人工核对列顺序确认哪一列是内部编码、哪一列是 MPN。参数说明doc_path是文件路径cells[0]假设第一列是编码列如果实际文档第一列是序号需要调整索引。strip()去掉单元格里的空格和换行否则后续匹配会失败。3.2 用正则清洗 MPN 字段里的脏数据从 Word 里抽出来的 MPN 经常带各种后缀比如“停产”“RoHS”“无铅”或者全角括号、多余空格。这些不洗干净导入数据库后采购按 MPN 查不到料。下面这个清洗函数处理了最常见的几种情况import re def clean_mpn(raw): if not raw: return # 去掉全角括号及其中内容 s re.sub(r[^]*, , raw) # 去掉半角括号及其中内容 s re.sub(r\([^)]*\), , s) # 去掉常见状态词 for word in [停产, NRND, EOL, RoHS, 无铅, 环保]: s s.replace(word, ) # 去掉多余空格和不可见字符 s re.sub(r\s, , s) return s.strip().upper() print(clean_mpn(STM32F103C8T6停产 RoHS)) # 输出 STM32F103C8T6逻辑说明先处理全角括号再处理半角括号因为中文文档里全角更常见。状态词用循环替换而不是正则方便后续增删。最后统一转大写因为 MPN 通常不区分大小写但数据库里如果混用会导致唯一索引失效。参数说明raw是原始字符串返回清洗后的 MPN。注意upper()对某些厂商型号可能不合适比如有些型号区分大小写如果你们有这种情况去掉upper()即可。3.3 导入前去重和冲突检测的 SQL 写法清洗完的数据不能直接INSERT必须先查一遍库里有没有冲突。最常见的冲突是同一个 MPN 挂到了两个不同的内部编码下或者同一个内部编码下同一个 MPN 出现了两次。下面这条 SQL 用来检测第一种冲突SELECT mpn, COUNT(DISTINCT internal_pn) AS cnt FROM mpn_mapping GROUP BY mpn HAVING cnt 1;逻辑说明按 MPN 分组统计它关联了多少个不同的内部编码。如果结果大于 1说明这个 MPN 被多个内部编码共用需要人工确认是替代关系还是录入错误。第二种冲突用联合唯一索引就能挡住导入时用INSERT IGNORE或ON DUPLICATE KEY UPDATE处理。参数说明COUNT(DISTINCT internal_pn)比COUNT(*)更准确因为同一内部编码下重复挂同一个 MPN 不算冲突。HAVING cnt 1只返回有问题的行方便逐条排查。4. 避坑MPN 库存管理里最容易翻车的五个地方4.1 现象库存扣减时提示“库存足够”实际发料却不够原因映射表里同一个内部编码挂了多个 MPN但库存表是按 MPN 记的扣减逻辑只查了其中一个 MPN 的库存没有汇总所有替代 MPN 的可用量。解决扣减前先按内部编码汇总所有关联 MPN 的库存确认总量足够再执行扣减。上面 2.3 的代码已经按优先级逐个扣但前提是查询时要把所有有库存的 MPN 都取出来不能只取优先级最高的那个。4.2 现象采购按 MPN 下单收货时系统提示“物料不存在”原因来料检验环节只认内部编码而采购订单上写的是 MPN收货时没有做 MPN 到内部编码的转换。解决在收货界面加一个 MPN 反查功能输入厂商型号自动带出内部编码。如果查不到允许收货员手动挂接但必须走审批流防止随意创建新内部编码。4.3 现象工程变更后旧 MPN 的库存变成呆滞料原因ECN工程变更通知只改了 BOM 里的 MPN没有同步更新映射表里的lifecycle字段系统仍然认为旧 MPN 是 Active 状态计划继续按旧料排产。解决ECN 流程里强制增加一步“更新 MPN 映射表”把旧 MPN 的lifecycle改成 EOL 或 NRND并设置一个消耗截止日期。库存扣减逻辑里对 EOL 的 MPN 要给出警告但不阻止出库直到截止日期过后才禁用。4.4 现象同一个 MPN 在不同仓库的库存被重复计算原因库存表的主键是(mpn, location)但映射表里同一个 MPN 可能因为历史原因挂了两个内部编码导致按内部编码汇总时把两个仓库的库存加了两遍。解决在映射表上加唯一约束之前先做一次数据清洗把重复的 MPN 合并到正确的内部编码下。合并时注意保留priority最小的那条记录其余删除。4.5 现象导入 Excel 时 MPN 列里的前导零丢失原因Excel 默认把纯数字的 MPN 当成数值处理比如0012345变成12345。解决导入前把 MPN 列格式设成文本或者在 Python 里用str(cell.value)强制转字符串。如果已经导入错了用LPAD补零只能救回固定长度的不固定长度的必须重新导入。血泪经验让工程师在 Excel 里录 MPN 时前面加一个单引号虽然丑但不会丢零。5. 进阶用 MPN 映射表做替代料推荐和库存健康度检查5.1 替代料推荐根据库存和优先级自动给出建议当计划员发现某个内部编码库存不足时系统应该自动推荐可用的替代 MPN。推荐逻辑不复杂查映射表里同一内部编码下所有lifecycle Active且库存大于 0 的 MPN按priority排序再结合采购在途量给出建议。下面这段 SQL 直接可用SELECT m.mpn, m.manufacturer, m.priority, COALESCE(s.qty, 0) AS stock_qty, COALESCE(p.on_order, 0) AS on_order_qty FROM mpn_mapping m LEFT JOIN ( SELECT mpn, SUM(qty) AS qty FROM stock GROUP BY mpn ) s ON s.mpn m.mpn LEFT JOIN ( SELECT mpn, SUM(qty) AS on_order FROM purchase_order WHERE status open GROUP BY mpn ) p ON p.mpn m.mpn WHERE m.internal_pn 你的内部编码 AND m.lifecycle Active ORDER BY m.priority ASC, stock_qty DESC;逻辑说明用两个子查询分别汇总库存和在途量再左连接到映射表保证没有库存的 MPN 也能显示出来。COALESCE把 NULL 转成 0避免前端显示空白。排序先按优先级再按库存量计划员一眼就能看出该用哪个。参数说明internal_pn替换成实际编码。purchase_order表名和status字段根据你们系统调整。如果你们有安全库存字段可以在ORDER BY里加一列safety_stock优先推荐低于安全库存的 MPN。5.2 库存健康度检查找出挂载过多 MPN 的内部编码一个内部编码挂 3 到 5 个 MPN 是正常的挂 20 个以上就有问题了要么是数据录入错误要么是工程变更没清理。下面这条 SQL 用来找出这些“重灾区”SELECT internal_pn, COUNT(*) AS mpn_count FROM mpn_mapping WHERE lifecycle Active GROUP BY internal_pn HAVING mpn_count 10 ORDER BY mpn_count DESC;逻辑说明只统计 Active 状态的 MPN因为 EOL 的不算活跃替代料。HAVING mpn_count 10是经验阈值你们可以根据物料类型调整电子料可以放宽到 15结构件建议设 5。结果按数量降序优先处理挂载最多的。参数说明如果你们有物料分类字段可以在WHERE里加AND category 电子料避免结构件和电子料用同一个阈值。查出来的结果建议导出给工程部门确认该合并的合并该停用的停用。5.3 一个我坚持了多年的习惯每次上线新的 MPN 映射数据之前我一定会做两件事第一用 3.3 的冲突检测 SQL 跑一遍确认没有 MPN 挂到多个内部编码第二随机抽 10 条记录手工去厂商官网核对 MPN 和封装是否匹配。这两件事花不了半小时但能挡住 90% 的脏数据。很多项目上线后库存对不上根源就是导入时没做这两步后面再查就得翻几个月的流水后悔药都没地方买。希望帮到你。本文还有配套的精品资源点击获取