码农刚子 · 六年制造业 ERP / .NET 实战 · 只写生产环境踩过的坑
如果你做过制造业 ERP,应该都见过这种场面:销售下了单,MRP 跑出来要采购的料跟实际差了十万八千里;月底财务对账,成本差异大到说不清是哪张单子带出来的;产线缺一颗螺丝停了线,系统里却显示库存充足。
十次有八次,根子不在 MRP 算法,而在最底层——物料建模。
物料主数据(Material Master)是 ERP 几乎所有模块的公共地基:销售、采购、库存、生产、成本、财务全从这张表取数。地基歪一寸,上层全都跟着歪。这篇文章把制造业 ERP 里最容易翻车的几个建模决策讲清楚,并给出一套能直接落到 EF Core 上的实体模型。
一、物料不是"一张表",是 ERP 的地基
很多团队一开始把物料想简单了:不就是 Material 表加几个字段吗?等上线半年就会发现,物料建模的坑会沿着依赖链一路传导到成本、库存和计划。
典型连锁反应:
- 一物多码 → 同一物料在系统里是两条记录,库存被割裂,MRP 重复算需求;
- 单位混乱 → 采购按"箱"、库存按"个"、BOM 按"千克",MRP 一换算就错位;
- BOM 错配 → 辅料混进主料清单,成本算不清,缺料时系统还以为有货。
所以物料建模不是"建表",是给整座 ERP 打地基。下面三个决策,是地基稳不稳的关键。
二、主料 vs 辅料:别用"重不重"判断,看三个维度
最常见的错误,是按"体积大不大、单价高不高"来分主料辅料。这不对。正确判断要看三个业务属性:
| 维度 | 含义 | 主料典型 | 辅料典型 |
|---|---|---|---|
| ① 是否计入产品成本 BOM | 构成商品实体、随产品出售 | 钢板、芯片 | 焊锡、包装箱 |
| ② 是否独立库存管理 | 有收发、有盘点、有库存账 | 是 | 常常是(也可能是费用领用) |
| ③ 是否参与 MRP 运算 | 需要按订单/计划跑净需求 | 是 | 多数按产量摊或安全库存 |
判定逻辑:
- ①②③ 全"是" → 主料,进成本 BOM、跑 MRP、管库存;
- 只有①是、②③ 否 → 包装/耗材类,可进 BOM 但不跑独立 MRP,按需发料;
- ①②③ 全否 → 费用类 / 虚拟件,不入生产 BOM,走费用或用作结构占位。
行业里这类“辅料漏进 / 漏出 BOM”的反向事故同样常见:据公开行业报道,一家小家电厂电水壶 BOM 只录了壶身、加热管,螺丝、包装盒全没录;工单领料不够就去仓库补领、补领又不进工单,成本归不到产品,年底一算卖得挺好利润却薄得可怜(据 NextERP 公开案例,非本人项目)。另一家模切厂把泡棉、双面胶归工单、手套油料进制造费用后,材料损耗率从 5% 降到 2%,一个月省下好几万原料钱——主料辅料分开管,省的就是真金白银。
关键认知:辅料不是"次要物料",而是"管理口径不同"的物料。把它从主料 BOM 里摘出来单独管,成本和计划才会准。
三、单 BOM 还是主/辅料分 BOM:核心架构决策
这是物料建模里最该想清楚的一步,没有标准答案,但有两种典型模式:
模式 A:单 BOM(主料辅料混一张)
- 优点:结构简单,录单快,小团队好维护。
- 代价:成本、库存、MRP 全混在一起。辅料损耗率、替代料、按工单发料都难管;想看清"这颗螺丝到底耗了多少"基本不可能。
模式 B:主 BOM + 辅料清单(分开管理)
- 主料 BOM 管成本与 MRP(按净需求跑采购/生产);
- 辅料按工单产量分摊或独立发料/安全库存处理。
- 代价:建模复杂一点,需要明确"辅料挂在哪里、怎么摊"。
我的实践结论(离散制造):优先主 BOM + 辅料分摊/独立发料。前期多花半天定结构,后期能省下无数个"成本差异查不清"的加班。流程制造看工艺定,不一定照搬。
混 BOM 的坑见第六节"踩坑1"——辅料塞进主料 BOM,月底成本差 450 万。这个项目后来落地"主 BOM + 辅料分摊":辅料从主料清单摘出,改按工单产量摊、独立发料,成本差异从"说不清"变成可逐单追溯。代价是前期多花时间定"辅料挂在哪、怎么摊",但换来的是年底盘库不再为差异扯皮。
四、物料主数据怎么设计:五个不可省的点
无论主辅料怎么拆,物料主数据本身有五个字段维度不能省:
- 编码规则优于"智能编码" 用分段规则编码(大类 + 中类 + 流水),别用"名字当编码"或塞一堆业务含义进去。规则稳定,系统才好做校验和去重,也才能防住"一物多码"。
反面教材:一张真实物料表,命名维度全乱(脱敏)
下面这组取自某制造业客户刀具类物料的名称(已脱敏:V割刀 泛化为成型刀、隐去企业编码),同行看了都会后背发凉——同一批刀具,命名口径完全没统一:
| 原物料名称(脱敏) | 暴露的命名问题 | 建议标准化写法 |
|---|---|---|
| 成型刀 研磨(100齿 钨钢)(120100T2.0) | ① 双层括号套叠;② 同一条内"齿"与"100T"混用;③ 尺寸顺序/单位无规则 | 成型刀-100齿-钨钢-研磨(规格另存字段) |
| 手动成型刀(5148T2.4 内径25 20度) | ④ "48T"用 T、"20度"用"度",单位写法不一 | 成型刀-48齿-2.4-内径25-20° |
| 成型刀 50齿 30度 | ⑤ 角度又用"度";齿数后置、语序乱 | 成型刀-50齿-30° |
| 成型刀 数控 100齿 | ⑥ 品类与工艺语序倒装("数控"插中间) | 成型刀-数控-100齿 |
| 手动成型刀压簧(红色胶棍) | ⑦ 混入颜色等非规格属性(红) | 成型刀-压簧(颜色移出名称) |
| 380 手动成型刀 | ⑧ 混入电压"380" | 成型刀-手动(电压移出名称) |
同一个品类,齿数 50T / 100T / 50齿 / 100齿 混写,角度 45° / 20度 / 30度 混写,尺寸 120*100T*2.0 / 51*48T*2.4 / 120*25.4*50T 厚度2.0 三套写法,括号单层双层套叠,还把颜色、电压、新旧、工艺全揉进名字。这种命名表必然催生"同物不同码"——两个仓管各录一条,系统当两样东西,库存从此割裂。
对照看同为这张表的 螺丝刀加长 / 油灰刀 / 斜边刀 / 刮刀柄螺丝:只描述功能、不带冗余维度,反而规范。问题从来不是名字长短,而是维度口径有没有统一。
所以"编码规则优于智能编码"落到实操:把品类 / 规格 / 工艺拆成固定段位(如 大类-中类-规格-工艺),名称只做给人看的备注,编码交给规则生成。命名维度一统一,"一物多码"就从源头被掐死。
类型用枚举,不用字符串
MaterialType枚举(原料 / 半成品 / 成品 / 辅料 / 虚拟件 / 费用),下游的成本、MRP、权限逻辑全靠它分流,比自由文本稳得多。单位:基准单位 + 多单位换算 设一个
BaseUom基准单位,采购单位 / 库存单位 / 销售单位都换算回基准。MRP 全程只用基准单位算,避免单位错配。替代料:替代组 + 优先级 + 生效期 主料缺货时系统能自动找替代,靠的就是替代组和优先级。没有这层,缺一颗料就只能停线等采购。
生效期 / 版本 物料和 BOM 都要带版本与生效期,避免"已停产的料还被新订单引用"这类幽灵数据。
五、EF Core 落地:实体建模与四个避坑
下面这套实体模型,能直接撑起上面的结构(.NET 10 / EF Core):
public enum MaterialType { Raw = 1, Semi = 2, Finished = 3, Auxiliary = 4, Phantom = 5, Expense = 6 }
public enum BomType { Main = 1, Auxiliary = 2 }
public class Material
{
public int Id { get; set; }
public string Code { get; set; } = ""; // 分段规则编码
public string Name { get; set; } = "";
public MaterialType Type { get; set; }
public string BaseUom { get; set; } = ""; // 基准单位
public bool IsActive { get; set; } = true; // 软删除标志
public byte[] RowVersion { get; set; } = []; // 并发控制
public ICollection<BomItem> AsComponent { get; set; } = [];
}
public class BomHeader
{
public int Id { get; set; }
public int MaterialId { get; set; } // 成品/半成品
public BomType Type { get; set; } // 主 BOM / 辅料清单
public int Version { get; set; } // 版本
public DateTime EffectiveFrom { get; set; } // 生效期
public ICollection<BomItem> Items { get; set; } = [];
}
public class BomItem
{
public int Id { get; set; }
public int BomHeaderId { get; set; }
public int ComponentMaterialId { get; set; }
public decimal QtyPer { get; set; } // 用量:decimal 防精度丢
public decimal ScrapPct { get; set; } // 损耗率
public int? SubstituteGroup { get; set; } // 替代组
public int? SubstitutePriority { get; set; } // 替代优先级
}
落地时四个高频坑:
坑1:软删除忘了加查询过滤器
IsActive=false 的物料如果查询时不统一过滤,MRP、BOM 展开会把它当活跃料用,污染整批计算结果。用 HasQueryFilter 全局过滤,别靠每个查询手写 Where(x => x.IsActive)。
坑2:并发覆盖
物料主数据、BOM 是多人高频改的对象。不加 RowVersion(乐观并发),两个人先后改同一张 BOM,后提交的会静默覆盖前一个的修改。生产环境这比编译错误难查十倍。
坑3:展开 BOM 树触发 N+1
整棵 BOM 多级展开如果用循环逐层 Find,一次展开就是几十上百次查询。用 Include + AsSplitQuery 一次把层级拉出来,或显式写递归 CTE,别在循环里打库。
坑4:用量用 float / double
QtyPer 一旦用浮点,MRP 多级累计后精度误差会放大,采购量算出来差几个百分点。用量、损耗率一律 decimal,这是制造业系统的底线。
六、四个真实踩坑(发布前请用真实项目经验替换)
下面四个场景是物料建模最常出事故的地方。每个我都留了占位,发布前请用你做过的真实案例填进去——有真实血肉,这篇文章才立得住。
踩坑1:辅料混进主料 BOM,成本算不清
SMT 工厂的物料里,主料(芯片、PCB、元器件)和辅料(锡膏、红胶、钢网、包装载带)本来该分开管。早先为了录单省事,把锡膏、包装耗材全塞进同一张生产 BOM 当主料核算。后果在月底结账时炸了:辅料其实该按产量摊才合理,却跟着主料一起按净需求算成本,一摊下来整月成本差异高达 450 万,财务怎么都平不了账、跟产线互相扯皮。 根因就是第一节那句话——"辅料不是次要物料,是管理口径不同的物料"。把它从主料 BOM 摘出来、单独按工单产量分摊,差异立刻能说清。这 450 万的教训,换来的就是主 BOM + 辅料分摊这套结构。
踩坑2:一物多码,库存被割裂
物料主数据里,"记事本"和"笔记本"是两条记录——其实完全是同一种东西。根因就是早期没定编码规则,录料的人按习惯各写各的,系统当成两样物料。后果很典型:同一个东西在 A 仓有一条库存、B 仓又一条,账面永远对不上;MRP 跑需求时各算各的,明明一边有货一边缺,却重复下了采购单,到货了又变成两边各压一批死库存。月底盘点,财务怎么都凑不出这个数。 这正好印证第二节说的:编码规则优于智能编码——名字能重,编码不能重;同一个物料必须且只有一个主数据。
踩坑3:单位不统一,MRP 算错采购量
油墨采购按"桶"、生产按"kg"领用、库存也按 kg 管,但系统里"桶→kg"的换算关系要么没维护、要么维护错。MRP 跑净需求时只能按单一口径算,换算系数一对不上,结果就两极分化:
- 算多了:该买 6 桶的量,系统算出要买 10 桶,多买的油墨放久了过期报废,白花几万;
- 算少了:反过来算漏,产线要用才发现不够,只能紧急加单等料。 根因正是第三节说的"基准单位 + 多单位换算"没落地——采购单位、库存单位、BOM 用量单位各说各话,MRP 一换算就错位。一次系数错,不是多买报废就是少买待料。
踩坑4:替代料没设优先级,缺料只能停线
最典型的一种:某批订单要用 1.6mm 厚度的覆铜板,系统里查着"有库存",真到产线投料才发现该型号板厚实际已经用完。更糟的是从头到尾没人提前按安全库存补货——等到断料才反应过来。 根因是三层都没兜住:① 同板厚不同型号的覆铜板没建替代料组、也没配优先级,明明库里躺着可替代的料号,系统却给不出替代方案,只能干等原型号到货;② 没设安全库存、MRP 也没跑准,本该提前触发的采购一直没下单;③ 库存账实不符又没低库存预警,"没有及时发现"——等产线喊缺料已经晚了。 一次待料,往往就是几小时到一整天的产能白白空转,交期直接往后拖。这恰恰说明:替代料优先级、安全库存、库存预警,缺一个都会变成停线事故。
这四个坑的共性:都不是算法问题,而是建模阶段没把口径定清楚。地基的账,迟早要还。
七、写在最后
制造业 ERP 里,物料建模是那种"前期嫌麻烦、后期全变事故"的地基工程。主料辅料怎么拆、BOM 单还是分、单位与替代料怎么管——这几个决策定好了,上层的成本、库存、MRP 才站得稳。
完整的物料建模检查表 + 物料主数据字段模板,我已整理进《.NET 生产环境避坑手册》的制造业补充篇。在微信搜索公众号「CSharp精选营」关注后,后台回复「避坑」即可免费领取。
ERP / 制造业垂直踩坑系列,我会持续更——只写生产环境里真踩过的坑,不整虚的。
本文为通用制造业 ERP 物料建模方法论分享,不涉及任何企业专有业务细节。
暂无评论,来抢沙发吧~