简介:这份文档面向制造业信息化人员、物料管理岗及数据标准化建设者,系统梳理物料分类编码的规则体系,帮助解决物料标识混乱、编码缺乏统一规范的问题。内容以12位码结构为主线,逐位拆解产品编号、一级物料属性码、二级功能性能类别码、三级特征区分码与四位序列码的含义,并延伸至文档编码,给出标准件、外购件、电子元器件、自制件等类别对应的一级编码取值,便于读者对照搭建企业编码表。资源包共1个docx文件,约18KB,篇幅精炼,适合作为编码规则制定的参考底稿或内部培训材料。目前已有2374人学习,说明其在物料主数据管理场景中具有一定参考价值。读者可从中获得编码结构设计思路、码位划分方法与类别编码示例,用于优化采购、仓储、生产等环节的物料检索与库存管理,并为后续编码体系扩展预留空间。
1. 物料编码不是编号,是给 ERP 留的接口
很多团队第一次做物料编码,都是先拉一张 Excel,把现有物料按“看起来差不多”分堆,然后从 001 开始往下排。三个月后新物料进来,发现中间空号不够用,或者同一个规格被两个人编成了两个码,采购和仓库各拿一套表对不上,返工成本比当初建表还高。这份《制造业物料分类编码规则.docx》解决的正是这个起点问题:它给出一套 12 位定长结构,把产品代号、物料属性、功能类别、特征区分和序列号拆成固定码位,让编码从“人脑记忆”变成“机器可解析”。
它适合两类人:一类是正在给 ERP、MES 或 WMS 做物料主数据初始化的实施人员,需要一份能直接落库的码位定义;另一类是制造业信息化负责人,手里已经有编码规则但结构松散,想对照一套成熟模板做收敛。文档本身是规则说明,不是软件,价值在于把“编码结构”这件事讲成可执行的字段约束,而不是停留在“分类要科学”这种正确但没法落地的层面。
2. 拆解 12 位码:每一段到底约束了什么
2.1 码位结构与字段映射
文档给出的结构是XXX X XX XX XXXX,合计 12 位。注意这里的第一段“产品编号”在正文里写的是 1 位,但结构示意画了 3 个 X,这是这类文档最常见的表述不一致,落地时必须以码位说明为准:一级编码 1 位、二级 2 位、三级 2 位、序列码 4 位,加起来是 9 位,剩下 3 位属于产品代号段。我在实际项目里一般按“产品代号 3 位 + 属性 1 位 + 功能类别 2 位 + 特征 2 位 + 序列 4 位”来建表,和结构图对齐,避免下游系统按图解析时对不上。
| 码段 | 位数 | 含义 | 典型取值来源 |
|---|---|---|---|
| 产品代号 | 3 | 产品线或产品系列 | 产品主数据表 |
| 一级编码 | 1 | 物料属性码 | 标准件/外购件/电子元器件/自制件 |
| 二级编码 | 2 | 功能、性能类别 | 类别字典 |
| 三级编码 | 2 | 特征区分 | 特征字典 |
| 序列码 | 4 | 排列序号 | 按类别自增 |
这张表就是落库时的字段定义。很多翻车都出在“位数对不上”:文档写 1 位,结构图画 3 位,开发按图建了 varchar(3),结果业务按 1 位填,后面全错位。我的做法是先把码段长度写成配置,不让它散落在代码和文档两处。
2.2 一级编码的取值边界
文档明确了一级编码的四个取值:标准件 4、外购件 5、电子元器件 6、自制件 7。这四个值不是随便定的,它们对应的是物料来源属性,直接决定后续采购流程走哪条线。标准件和外购件走采购申请,电子元器件往往有单独的供应商准入,自制件走生产工单。所以一级编码一旦定错,影响的不只是分类报表,而是整条业务流程的路由。
落地时建议把这一位做成枚举,而不是自由文本。下面是一段建字典表的参考写法,字段名按常见 ERP 习惯来:
-- 物料一级属性字典,固定四个取值,禁止业务随意扩展 CREATE TABLE md_material_attr ( attr_code CHAR(1) PRIMARY KEY, attr_name VARCHAR(32) NOT NULL, source_type VARCHAR(16) NOT NULL -- 采购/生产/委外 ); INSERT INTO md_material_attr VALUES ('4', '标准件', 'PURCHASE'), ('5', '外购件', 'PURCHASE'), ('6', '电子元器件', 'PURCHASE'), ('7', '自制件', 'PRODUCTION');这段 SQL 的关键在于source_type这一列。它把编码和流程路由绑在一起,后面做采购申请或工单创建时,直接拿一级编码去查来源类型,不用再写一堆 if-else。参数上attr_code用 CHAR(1) 而不是 VARCHAR,是为了在拼接 12 位码时强制长度,数据库层面就能挡住多填一位的低级错误。
2.3 二三级编码的字典化
二级编码是功能、性能类别,三级编码是特征区分,各 2 位,意味着每级最多 99 个取值(从 01 开始,00 一般留作“未分类”)。这个容量对大多数中型制造企业够用,但前提是字典要收得住。我见过最典型的失控场景是:二级编码被业务当成“备注”用,今天加一个“特殊件”,明天加一个“临时用”,两年下来字典膨胀到 80 多个值,编码规则名存实亡。
控制办法是把二三级编码做成受控字典,新增必须走审批,并且和一级编码做组合约束。比如“电子元器件”下面不应该出现“结构件”这种二级类别。可以用一张组合校验表来管:
# 二三级编码组合校验:新增物料编码前先过一遍 VALID_COMBOS = { ("6", "01"): ["01", "02", "03"], # 电子元器件-电阻类-贴片/插件/排阻 ("6", "02"): ["01", "02"], # 电子元器件-电容类-陶瓷/电解 ("7", "01"): ["01", "02", "03"], # 自制件-机加件-车/铣/磨 } def check_combo(attr_code, cat2, cat3): allowed = VALID_COMBOS.get((attr_code, cat2)) if allowed is None: return False, f"二级类别 {cat2} 未在属性 {attr_code} 下注册" if cat3 not in allowed: return False, f"三级特征 {cat3} 不属于类别 {cat2}" return True, "ok"这段校验的逻辑很直白:先按一级属性找到允许的二级类别,再在二级类别下找允许的三级特征。VALID_COMBOS就是业务规则的代码化,新增组合改这里,不用改校验函数。实际项目里我会把这张表放到数据库而不是硬编码,但先用字典跑通流程,再迁到配置表,是更稳的推进节奏。
2.4 序列码的生成与并发
序列码 4 位,按类别自增,最大 9999。单看容量不小,但要注意它是“按类别”还是“全局”自增。文档没有写死,落地时必须自己定。按类别自增的好处是编码可读性强,同一类物料连号;坏处是并发插入时容易撞号,尤其是多个人同时建物料的时候。
常见做法是用一张序列表加行锁,或者直接用数据库序列。下面是一个按“一级+二级+三级”组合取号的写法:
-- 序列号池:按码段前缀分组自增 CREATE TABLE md_seq_pool ( prefix CHAR(5) PRIMARY KEY, -- 一级1位+二级2位+三级2位 cur_val INT NOT NULL DEFAULT 0 ); -- 取号:先锁行再自增,避免并发撞号 BEGIN; INSERT INTO md_seq_pool(prefix, cur_val) VALUES ('60101', 0) ON CONFLICT (prefix) DO NOTHING; UPDATE md_seq_pool SET cur_val = cur_val + 1 WHERE prefix = '60101' RETURNING cur_val; COMMIT;prefix用 5 位拼前缀,正好对应一级 1 位加二三级各 2 位。ON CONFLICT DO NOTHING保证前缀第一次出现时能初始化,UPDATE ... RETURNING在同一个事务里完成取号和读值,行锁天然挡住并发。取到的值再格式化成 4 位,不足补零,拼到 12 位码的最后一段。这里有个边界要盯住:cur_val超过 9999 时必须报错而不是截断,否则会生成重复码,这种错误在数据量上来之后极难排查。
3. 从规则到系统:编码生成与校验的落地路径
3.1 编码生成服务的接口设计
规则文档只是定义,真正要用起来得有一个生成入口。我的习惯是做一个独立的编码服务,输入是业务字段(产品代号、属性、类别、特征),输出是 12 位码,调用方不关心序列怎么取。接口大致长这样:
def generate_material_code(product_code: str, attr: str, cat2: str, cat3: str) -> str: # 1. 长度与枚举校验 assert len(product_code) == 3, "产品代号必须 3 位" assert attr in {"4", "5", "6", "7"}, "一级属性非法" assert len(cat2) == 2 and len(cat3) == 2, "二三级编码必须 2 位" # 2. 组合合法性校验 ok, msg = check_combo(attr, cat2, cat3) if not ok: raise ValueError(msg) # 3. 取序列号并格式化 prefix = f"{attr}{cat2}{cat3}" seq = next_seq(prefix) # 内部走行锁取号 if seq > 9999: raise OverflowError(f"前缀 {prefix} 序列号耗尽") return f"{product_code}{prefix}{seq:04d}"三个校验步骤对应三类翻车:长度错、枚举错、组合错。next_seq是上一节的取号逻辑封装,seq:04d负责补零。这个函数返回的字符串就是最终落库的物料编码,调用方拿到后直接写主数据表,不再二次拼接。把拼接集中在一个地方,是避免“有人手工补零、有人不补”这类不一致的最有效手段。
3.2 存量数据迁移的映射表
新规则上线时,老物料还在用旧编码,直接切换会让历史单据全部断链。稳妥的做法是建一张新旧映射表,旧码作为别名保留,新码作为主键,查询时两边都能命中。
| 字段 | 说明 | 示例 |
|---|---|---|
| old_code | 旧系统物料号 | A-1023 |
| new_code | 12 位新码 | P01 6 01 01 0001 |
| migrate_flag | 迁移状态 | DONE / PENDING |
| migrate_time | 迁移时间 | 时间戳 |
迁移脚本按批次跑,每批处理完把migrate_flag置为 DONE,失败的回滚到 PENDING 重跑。这里不要追求一次全量迁完,分批次、可重入比“一把梭”安全得多。映射表本身也是后悔药:新码生成错了,还能顺着 old_code 找回去。
3.3 与 ERP 物料主数据的对接
编码最终要落到物料主数据表。对接时最容易忽略的是“编码之外的字段一致性”:同一个物料,编码对了,但计量单位、物料组、采购类型没跟着对齐,照样会在 MRP 运算时出问题。我的做法是把编码服务和主数据创建放在同一个事务边界内,编码生成成功但主数据写入失败时,序列号要能回退,否则会留下空号。
空号本身不致命,但连续空号会让业务怀疑系统有问题。所以取号逻辑里要留一个补偿入口:主数据写入失败时,把刚取的序列号标记为作废,下次取号跳过。这个机制不复杂,但要在设计阶段就留好字段,事后补会很别扭。
4. 避坑清单:编码规则落地时最容易翻车的五件事
4.1 码位长度文档与结构图不一致
现象:开发按结构图的 3 位建了产品代号字段,业务按正文的 1 位填写,拼出来的码整体错位,下游解析全乱。 原因:规则文档里“1 位”和“XXX”两种表述并存,没有明确以哪个为准。 解决:落地前先和业务确认一份“码位定义表”,把每段位数、取值来源、是否允许空写死,作为唯一依据,文档里的示意图只作参考。
4.2 序列号并发撞号
现象:两个人同时新建同一类物料,生成了两个相同的 12 位码,主数据表唯一索引报错,或者更糟——没建唯一索引,重复码悄悄入库。 原因:取号逻辑是“先查最大值再加一”,查询和写入之间没有锁。 解决:改用行锁或数据库序列取号,并在物料主数据表上对编码字段建唯一约束,双保险。
4.3 二三级字典无序膨胀
现象:上线半年,二级编码从 10 个涨到 60 多个,很多类别只被用过一次,编码失去分类意义。 原因:新增字典没有审批,业务把编码当备注用。 解决:字典新增走审批流,并做一级属性与二三级组合的合法性校验,非法组合直接拒绝。
4.4 旧码直接废弃导致历史单据断链
现象:切换新编码后,旧采购订单、库存记录按旧码查不到物料,报表对不上。 原因:没有建新旧映射,旧码被直接覆盖。 解决:保留旧码作为别名字段,建映射表,查询层做双码兼容,等历史数据自然消化后再考虑下线。
4.5 序列号耗尽没有预警
现象:某个高频类别的 4 位序列码用到 9999,下一条直接报错或截断成重复码。 原因:取号逻辑没有上限检查,也没有容量监控。 解决:取号时判断上限并抛错,同时对使用率超过 80% 的前缀做定期巡检,提前规划扩位或拆分。
5. 把编码规则变成可验证的约束:一个校验脚本的写法
规则文档最大的问题是“写完就锁进抽屉”,没人知道线上数据到底符不符合。我的习惯是配一个巡检脚本,定期扫物料主数据表,把不合规的编码捞出来。下面这段按 12 位结构做逐段校验,输出问题清单:
import re PATTERN = re.compile(r"^[A-Z0-9]{3}[4-7]\d{2}\d{2}\d{4}$") def audit_codes(rows): issues = [] for code, attr, cat2, cat3 in rows: if not PATTERN.match(code): issues.append((code, "结构不符 12 位规则")) continue if code[3] != attr: issues.append((code, "一级属性与主数据不一致")) if code[4:6] != cat2 or code[6:8] != cat3: issues.append((code, "二三级编码与字典不一致")) if code[8:] == "0000": issues.append((code, "序列号未分配")) return issuesPATTERN把结构约束写成正则:前 3 位字母数字、第 4 位限定 4 到 7、后面各段定长。逐段比对主数据字段,能抓出“编码本身合法但和业务字段对不上”的隐性错误,这类问题比格式错误更隐蔽。0000序列号的检查是防手工造码,正常流程取号从 1 开始,出现全零基本可以判定是人工拼的。
巡检频率我一般设成每周一次,结果推到数据治理群里,不追求一次清零,但要求新增问题为零。跑上三个月,编码质量会明显收敛。从那以后我每次接手物料主数据项目,都强制先跑一遍结构巡检再谈清洗,不然清洗完没有基线,改没改好全靠感觉。希望帮到你。
本文还有配套的精品资源,点击获取