ERP多阶BOM物料需求计算全解析:从展开逻辑到常见坑
上个月一位做电力设备的朋友找我救火说他们ERP上线大半年MRP跑出来的采购计划根本没法用某些自制半成品明明有库存系统还在往下游下采购单另一些物料则缺料缺得离谱仓库天天追着采购要货。我把他们产品的BOM展开看了一眼问题几乎全出在同一个地方——多阶BOM的物料需求计算逻辑不对整个产品结构被计划员当成一锅炖库存扣减、时间偏移、层级传递全乱了。这类情况我做ERP实施这些年见了太多次。很多企业所谓的跑MRP实际上只是把BOM做出来了按单阶展开看了看用量根本没有让系统按多阶结构真正逐层计算需求。这篇文章就围绕ERP里的多阶BOM到底怎么算物料需求这个核心问题把展开逻辑、净需求算法、时间偏移、批量规则、系统实现和常见坑一次讲透。适合正在学MRP的顾问、企业的PMC/生产计划员以及被MRP结果搞得一头雾水的IT人员。1. 多阶BOM在ERP里的真实分量物料需求的源头是产品结构1.1 阶到底是什么先建立产品树的概念BOMBill of Materials翻译成物料清单但在实际业务里它的本质不是一张清单而是一棵产品结构树。树的根节点是成品往下分叉是部件、组件再往下是零件、原材料。阶指的就是这棵树上的层级距离。成品本身是第0层成品直接使用的物料算第1阶这些物料再往下展开的子件算第2阶以此类推。比如一个办公椅第0层办公椅成品第1层椅背框、椅座框、扶手、气压杆第2层椅背框下面的钢管、焊接件、螺丝第3层钢管对应的钢材卷料这里的关键认知是物料需求不是直接从成品跳到最底层物料的。生产办公椅先要产出椅背框然后才能组装成椅。每一层都有它自己的加工周期、在制库存、损耗和提前期。这就是多阶的意义——需求是一层一层传导的每一层都需要单独计算净需求和下达时间。1.2 单阶展开、多阶展开、全阶展开三种方式解决三种问题很多刚开始接触ERP的人会把展开BOM当成一个操作实际展开方式至少有三种每种解决的问题完全不同。单阶展开只看某个父项的直接子件。比如查办公椅用到了哪些一级物料。车间领料、成本卷积、组装工位的配料单基本都用单阶视图。多阶展开从父项出发沿着产品结构一层一层往下展开直到最底层的原材料/采购件为止。它把整棵产品树完整呈现出来这是MRP计算的基础视图。全阶展开展开结果会重复显示所有层级每个中间件在父项下出现一次同时也在自己的层级出现一次。这种主要用于查询和追溯比如查某个零件影响哪些成品。MRP算物料需求时用的是多阶展开不是单阶展开。原因很简单单阶展开只知道成品直接用了哪些物料但那些物料自己在生产过程中还要消耗什么单阶视图完全看不到。换句话说单阶展开回答的是组装这个成品第一层需要领什么料多阶展开回答的是为了做出这个成品整条供应链每一层分别要在什么时候备好什么料。1.3 为什么多阶BOM和时区天然绑在一起多阶BOM还有一个容易忽略的维度时间。产品结构不只是空间上的层级关系每一阶还对应一个提前期。成品有装配提前期半成品有加工提前期采购件有采购提前期。多阶展开计算物料需求本质上是把一张静态的产品结构图变成一张动态的时间轴上的需求计划第6周要交付成品AA装配需要1周那么A的投入时间在第5周第5周需要半成品BB加工需要2周那么B的开工时间在第3周第3周需要零件DD采购需要2周那么D的采购单要在第1周下达。这个从成品需求出发沿着BOM逐层减去提前期、倒推出每一层的需求日期和下单日期的过程就是MRP最核心的时间偏移Lead Time Offset。很多ERP新手一上来就埋头算数量把时间维度丢掉结果需求数量对了但该第1周下的采购单变成了第3周才下缺料没跑了。2. 从毛需求到净需求多阶展开前必须想清楚的几个扣减项2.1 毛需求是怎么来的独立需求加相关需求多阶BOM展开后某一层物料的需求来源通常有两个独立需求来自销售预测、客户订单、维修备件需求等不由上层BOM传递产生。这类需求在MRP中作为主需求存在常常从主生产计划MPS流入。相关需求由上层父项的计划订单按BOM用量展开而来。比如成品A计划生产100件BOM里1个A用2个D那么D从A这里继承的相关需求就是200件。在完整的MRP计算里一个物料最终的毛需求 分配给它的独立需求 所有上层父项传下来的相关需求。多阶BOM展开的核心工作之一就是把这些来自不同父项、不同需求日期的相关需求按物料和时间合并起来。不合并就会出现同一个物料被重复采购、重复生产的情况。2.2 毛需求到净需求库存、在途、在制、安全库存怎么扣毛需求代表理论上需要多少但真正要下达采购或生产计划的是净需求。净需求的计算公式可以写成净需求 毛需求 安全库存 - 现有可用库存 - 已分配数量 - 在途订单量 - 在制量现有可用库存这里的可用很关键。ERP里库存量不等于可用量质检中、冻结库存、寄售库存通常不能直接参与MRP扣减。已分配数量已经被其他订单占用、预留的库存。如果不扣掉会把同一批货分给两个订单。在途订单量已经下达但还没到货的采购单、委外单。这批货会覆盖未来的需求。在制量车间正在加工、还没完工入库的数量。安全库存比较特殊它在公式里是加项意味着净需求要额外多算出一部分让期末库存始终保底。有一个非常容易出问题的细节这些扣减项是有时间属性的。在途订单不是一次性都能用来覆盖所有时间的需求它有一个预计到货日期在制量也有完工日期。MRP计算时会按时间分桶先看这个时段的期初可用库存再扣这个时段的毛需求逐期滚动。所以现有库存30毛需求100净需求就是70这种算法只有在所有需求都在同一个时间点时才成立。跨周、跨月的需求必须分段算否则会把未来库存提前占用或者漏掉中间的缺口。2.3 批量规则和时间偏移为什么计划不能只算够不够净需求算出来只是一个缺口数量的参考值真正下达采购/生产数量时还要过批量规则这一关。常见的批量规则有直接批量AS-IS需要多少就订多少净需求计划订单量。适合高价值、低频采购的特殊件。固定批量每次订单量固定为某个值比如一次采购200件净需求120时订单量会取200。最小批量净需求低于批量下限时按下限下达超过下限则按净需求或批量倍数取整。周期批量把一段时间比如一周或一个月内的需求合并成一张订单。对降低采购频次、减少换线有帮助但会增加库存。批量规则直接影响下游的BOM展开——因为下层物料的需求量来源于上层计划订单量而不是上层净需求量。举个例子上层净需求是120件固定批量200系统生成了200件的计划订单那么这个上层物料对应的子件需求就会按200件展开多出来的80件会成为下层物料的额外需求。这个连锁效应在多阶BOM中会被放大所以设定批量规则时一定要想清楚千万别在每一层都设大固定批量。时间偏移刚才提过了这里补充一个实操细节很多ERP系统在净需求计算界面上会显示订单下达日期和订单完工日期两个日期。下达日期 完工日期 - 提前期。采购件的提前期是采购提前期自制件的提前期是加工提前期委外件则要考虑运输和加工两端时间。真正下发给计划员/采购员的日期永远是下达日期。看MRP报表时不要只盯着需求日期否则会误判还早得很。3. 拿一个两阶产品手把手推演需求是怎么一层一层算出来的3.1 先搭建一个典型的产品结构与初始条件纸上谈兵没有用我们直接把一个典型的两阶产品从头算到尾。设产品A为成品其BOM结构如下A由1个B自制半成品和3个C外购件组成B由2个D外购件和1个E外购件组成。相关参数如下物料类型提前期期初库存安全库存批量规则A成品1周00直接批量B自制半成品2周3010直接批量C外购件3周6020固定批量200D外购件2周400固定批量200E外购件1周100直接批量主生产计划MPS给出两个独立需求A产品第6周需要100件第8周需要50件。这就是一个典型的两阶展开实战场景。接下来我们就按多阶BOM的逻辑一层一层算先把所有需求归到时间轴上。3.2 第一层展开A的净需求变成B和C的相关需求时间反推先做起来A提前期1周所以第6周要交100件A装配投入时间在第5周第8周要交50件A装配投入时间在第7周。A没有期初库存、没有安全库存净需求就是毛需求直接生成计划订单第5周开始装配的100件A第7周开始装配的50件A。A的计划订单量随即作为上层需求按BOM用量展开到第1层B的相关需求第5周100件第7周50件。 C的相关需求第5周300件100×3第7周150件50×3。注意B和C的需求日期是跟随A的装配开工时间的不是成品交付时间。这就是提前期偏移在实际计算中的体现。很多新手在这里直接把第6周当作B和C的需求日得出的采购计划全部提前期失真。3.3 第二层展开B的净需求继续向下C冻结为采购建议先看B。B是自制件期初库存30安全库存10可用库存为2030-10期间没有其他入库。第5周毛需求100净需求 100 10 - 30 80件。按直接批量规则B在第5周投产80件。B的提前期是2周所以B的生产开工时间在第3周。冰点时刻到了接下来D和E的需求量不是按B的毛需求100件算的而是按B的净需求80件算的。这是多阶BOM计算里最容易引起争议、也最值得记住的一点。为什么因为MRP计算到B这一层时已经确认了只需要生产80件B来满足净需求。如果还按100件展开D和E那20件多出来的下层需求没有任何意义只会造成过剩采购。所以D的相关需求 80 × 2 160件需求日期在第3周。 E的相关需求 80 × 1 80件需求日期在第3周。然后继续往时间轴反推D采购提前期2周第3周要160件净需求 160 - 40 120件固定批量200 → 下达采购200件下单日期在第1周E采购提前期1周第3周要80件净需求 80 - 10 70件直接批量 → 下达采购70件下单日期在第2周。因为固定批量200D这一层实际上多采购了80件会形成库存结转到下一期。这是批量规则的正常代价后面在合并汇总时会看到它如何影响第8周那次需求。再看C。C在第5周毛需求300件期初库存60安全库存20可用库存40。净需求 300 20 - 60 260件。但C的固定批量是200且要求是200的整数倍且不小于净需求所以260取整为400件采购提前期3周C的采购单下达日期是第2周。3.4 合并汇总同一物料出现在不同父项时需求先按时间合并再算净需求上面只推演了第6周的100件A。别忘了还有第8周的50件A以及由它引发的一系列下层需求。为了避免错误我们等第二波需求也用同样逻辑展开完再统一合并同物料的跨期需求。第二波展开结果B第7周毛需求50件第5周已经消耗了期初库存并补足了安全库存第7周期初可用库存10安全库存净需求50件投产日期第5周C第7周毛需求150件第5周采购了400件但只领用300件剩余100件结转到第7周所以第7周净需求15020-10070件按固定批量200取整再采购200件下单日期第4周D第5周需要100件50件B × 2第1周采购200件到货后第3周领用160件剩余库存40件所以第5周净需求100-4060件固定批量200 → 采购200件下单日期第3周E第5周需要50件第2周采购70件第3周领用完毕净需求50件直接采购50件下单日期第4周。把所有需求汇总就是一个完整的MRP输出物料计划订单下达/开工日期说明B80件投产第3周第3周满足A第6周需求B50件投产第5周第5周满足A第8周需求C400件采购单第2周下单第2周第5周领用300剩100结转到第7周C200件采购单第4周下单第4周第7周领用150批量结余D200件采购单第1周下单第1周第3周领用160剩余40结转到第5周D200件采购单第3周下单第3周第5周领用100批量结余E70件采购单第2周下单第2周第3周领用E50件采购单第4周下单第4周第5周领用这套结果要是靠人工在Excel里做极容易在C第7周库存还剩多少这种跨期结转上算错。而ERP里的MRP模块就是为了把这个过程自动化并且固化规则。3.5 低位码的作用为什么系统坚持先层后料而不是先料后层上面的手工推演我们是按产品树逐层展开来做的。但如果把同一个例子交给系统去算还不能这么简单粗暴。原因在于同一物料可能出现在多个父层级中。假设物料D既是B的零件又是A的备用直购件即第0层直接使用同时也出现在第1层B下面那么计算顺序就乱了。如果不先分层次系统一碰到D就立刻算净需求但是D从A那个直购分支拿到的需求还没展开结果必然是先算了一部分、漏了一部分最后做MRP合并时数据全是脏的。这就是**低层码Low Level Code, LLC**存在的意义。低层码是指某个物料在整棵产品结构树里所处的最低层级。计算规则是取该物料出现在所有BOM关系中的最大层级号成品为0级它的直接子件若还出现在更下层就按更深的那层计。MRP逐层计算时严格按低层码从0、1、2……逐级处理先把所有0层成品的净需求算完再展开出1层物料的毛需求所有1层物料的需求汇总完再算净需求、生成计划订单再展开出2层需求。这样每个物料在整个计算周期内只被完整地处理一次不会出现需求还没全部汇总就被提前计算的情况。所有主流ERP系统都遵循这个原则。4. 系统实现层面的两条路递归展开与层级迭代怎么选4.1 递归展开思路直观但性能和重复计算都是问题自己写过程序的人看到多阶BOM展开第一反应往往是递归从当前父项出发读取它的子项列表对每个子项继续调用同一个函数往下钻直到所有分支到达采购件/原材料为止。递归的优点是代码短、看起来符合人的思维习惯。物料树有多深它就递归多少层逻辑上很自然。但放到真实ERP环境里递归展开有两个明显问题第一重复访问。如果同一个物料在多个父项下出现或者被多个上层产品共用纯递归方式可能多次展开同一个子结构。比如一个通用螺丝出现在10种部件下面递归就会把螺丝的需求计算逻辑跑10遍。数据量小时无所谓企业完整BOM一展开往往几万条记录性能会很快崩掉。第二与净需求计算时机冲突。MRP的正确节奏是全部同类层级算完再进下一层而递归是从一个分支钻到底再回头。两者天然矛盾。所以即便用了递归也需要在递归外层做好按层汇总、缓存中间结果实际上还是绕回到层级迭代。4.2 层级迭代加低位码主流ERP的成熟做法现在主流ERPSAP、Oracle、用友、金蝶等的MRP逻辑本质上都是按低层码层级迭代读取已确认的主生产计划/独立需求计算所有物料的低层码如果系统能动态维护的话从低层码0开始处理所有成品物料汇总毛需求、扣减库存/在途/在制、考虑批量、生成计划订单拿这些计划订单量回写生成下一层级物料的毛需求并将所有同层物料的需求汇总低层码1重复第3和第4步直到处理完全部物料层级。这个流程的好处有两个一是需求汇总完整不会出现漏算二是每个物料只做一次库存扣减和净需求计算计算量可控。实际开发MRP增强功能时很多资深AP顾问会直接用标准功能只在计划订单生成后用BAdI/插件做二次校验。这很值得推荐——不要自己在外部重复造MRP轮子边界情况太多了比如订单取消、需求日期修改、BOM版本切换这些标准逻辑处理了很多年才稳定。4.3 防死循环与数据准确性保障BOM结构理论上不允许出现循环比如A用到BB又用到A这在物理世界中是不可能的拿自己做原料。但ERP实施中BOM数据维护错误导致循环结构并不罕见尤其是多个工程师同时维护物料表的时候。实际做系统开发或排错时一定要有循环保护遍历已访问的物料节点如果在这一条分支路径上再次遇到同一个物料立即报错并终止计算。只靠总访问次数超过N就退出这类兜底逻辑也可以但最好在第一轮BOM数据校验时就预先检查。上ERP之前让IT定期跑一遍BOM循环检测能省掉后面大量莫名其妙的MRP错误。另外准确计算永远建立在BOM版本和生效日期正确的基础上。多阶展开时如果某个父项BOM已经换版但系统引用的是旧版本展开结果几乎是全错的。建议所有的MRP跑批都绑定计算日期只使用截至该日期已生效的BOM版本而不是当前最新版本。5. 实施中最容易翻车的几个场景阶码、损耗率、替代料与库存占用5.1 阶码维护错误最常见也最隐蔽很多ERP系统允许用户手工维护物料的默认低层码LLC用来优化MRP性能。这本身没什么问题但一旦BOM结构变更后没有同步刷新低层码MRP计算顺序就会出错。典型的症状是某个物料明明处于较低层级系统却因为老的低层码把它提前计算了导致它后面从其他父项传来的需求根本没被汇总进去或者反过来A的低层码改大了它下面的子件跟着被推迟到下一轮计算需求日期全部错位。解决思路只有一个不要手工维护低层码让系统基于当前BOM结构自动重算。每次MRP跑批前确保低层码刷新程序执行完毕。如果ERP不支持自动重算那就要在BOM变更流程里强制加入低层码重算步骤并纳入上线检查清单。5.2 损耗率该放在哪一层避免需求被重复放大制造过程通常有报废率、损耗率、良品率这些参数。很多企业把这些参数同时维护在父项和子项上以为多扣一点更保险结果在多阶BOM里造成需求成倍放大。正确的做法是把损耗率定义在真正发生损耗的那道工序所在的父项层级或者根据MRP引擎的设计规则统一放在子项需求放大上。二选一不要两头都算。比如说B加工过程中报废率5%应该是生产100件B需要投料105件这种逻辑由B产品的计划订单放大来实现如果同时又给B的子件D加了一个损耗率D的需求就会被5%再次放大实际多采购的物料就远远超过所需了。在系统参数设置里务必要搞清楚损耗率影响的是父项投入数量还是子项需求量不同ERP处理方式不同。上线前做一次多阶BOM手工推演和系统输出对比一下是最有效的校准手段。5.3 替代料多数ERP算不了自动最优选择有些行业对替代料的需求非常强比如电子行业电容、电阻的通用替代或者原材料紧张时的同等级替用。但在标准的多阶BOM MRP计算逻辑里替代料通常不参与自动计算。系统默认按主BOM展开需求替代料关系大多是作为计划员手工调整的参考。如果企业确实需要自动替代一般靠三种方案一是BOM中把替代料按比例写死比如可用A或B替代按70%/30%用量展开这种最稳定二是通过高级ATP可用量承诺或MRP增强功能在净需求不足时自动检索替代料库存三是让计划员跑完MRP后在计划订单上手工替换物料。我的建议是简单场景直接写死在BOM里复杂场景先跑主计划再人工干预。不要为了全自动替代去改MRP底层逻辑跨物料的优先级和有效期判断非常复杂强行自动很容易产生超量采购。5.4 库存分配与硬预留的冲突需求没算错库却被锁死多阶BOM计算是多层级的库存扣减也是逐层进行的。这就带来一个执行层面的问题同一个库存物料可能同时被上层多个父项订单抢占。比如某个共用件X既被A订单需要也被B订单需要。MRP第一次计算时若已把X的库存120件全部预留给了A订单那么B订单在下一轮计算时看到X的可用库存可能是0直接就生成了采购建议。如果计划员没有仔细核对已分配量就会看到系统明明报了缺料但仓库其实还有120件X躺在账上被锁住。这类问题通常不是算法错而是预留策略太粗。调整的方向有两个一是在MRP参数中设置可承诺量预留比例不要把库存一次性100%锁给第一个订单二是给不同订单类型设置优先级系统按优先级逐级分配库存。还有一个更实用的习惯跑MRP之前先清理掉无效的销售订单预留和过期的工单预留。5.5 多组织/多工厂场景跨工厂需求别让BOM就地掐断如果企业有多个工厂、多个组织BOM是按组织维护的MRP计算也可以在单一组织内进行。但一旦存在跨工厂调拨需求比如A工厂产出的半成品要调到B工厂去组装就没法只按一棵树往下展开了。半成品在A工厂的库存、在制、计划订单必须作为B工厂的相关需求来源。很多中大型企业栽在多阶BOM在单个组织内看起来没问题但跨工厂需求完全没有集成到下一层。正确的做法是在MRP需求传递配置中把跨组织需求映射为计划调拨订单让它进入对方组织后参与库存扣减。这一块在实施时最好有专门的流程设计不能指望标准MRP自动处理所有组织间关系。结尾的个人体会这几年帮企业调MRP我最大的体会是多阶BOM算物料需求从来不是一个纯软件功能问题而是一个规则设计数据治理流程纪律的组合问题。算法再标准只要BOM有循环、低层码没刷新、损耗率重复设置算出来的结果就能让整个计划团队失去信任。如果你现在正被MRP结果折磨建议从一个小范围产品做起手工推演一次多阶展开再和系统结果比对。只要有一次算实了、对齐了后面所有需求数据都有了可信的锚点。这比对着报表熬夜排查有效得多。