简介这是一份面向生产管理、供应链及工业工程方向学习者的物料需求计划MRP教学课件适合需要理解MRP原理、掌握库存控制与生产计划逻辑的高校学生及企业从业者。资源包内含1个PPT文件压缩包约458KB以幻灯片形式系统梳理MRP知识框架。课件从传统库存管理的盲目性、高库存与低服务水平矛盾切入逐步展开MRP基本原理、三大关键输入主生产计划MPS、物料清单BOM、库存记录及逻辑流程图并详细讲解粗需求量、预计入库量、现有库存量、净需求量等计算项目与计划订货量、计划投入量的推导过程。内容还涵盖BOM分层编码方法、方桌结构案例、电表零件需求计划案例及MRP逻辑处理流程帮助读者建立从市场预测到采购生产指令的完整认知。目前已有665人学习适合作为课程复习或企业内训的参考材料。1. 物料需求计划(MRP)为什么你的生产计划总在救火上周跟一位做电子组装的朋友吃饭他说工厂最魔幻的场景是仓库里堆着够用三个月的电容产线却因为缺一颗两毛钱的连接器停了半天。采购追着供应商骂供应商说上周才补过货一查系统原来那颗料被另一个订单的BOM吃掉了而那个订单的交期还在两个月后。这不是人的问题是物料需求计划(MRP)没跑对。MRP要解决的核心就一句话在正确的时间把正确数量的正确物料推到正确的位置。它上承主生产计划(MPS)下接采购与车间作业中间靠BOM、库存、提前期三根柱子撑着。适合谁看如果你正在用Excel排料、被缺料和呆滞两头夹击或者刚接手一套ERP但MRP运算结果总对不上账这篇就是写给你的。我会从计算逻辑讲到跑批参数再到那些只有踩过才知道的坑尽量让你看完能自己搭一版可复现的MRP运算。2. MRP的净需求计算从毛需求到计划订单的完整推演MRP不是玄学它是一套确定性的时间轴推演。很多人跑出来的结果不对不是软件坏了是输入没对齐。这一章把计算链路拆开每一步都给出可验证的算法和参数含义。2.1 毛需求、净需求、计划订单接收的递推关系MRP的核心递推发生在每个物料的时间桶(bucket)上。毛需求来自上层物料的计划订单投放加上独立需求备件、样品。净需求 毛需求 安全库存 - 预计入库 - 可用库存。如果净需求大于零就按批量规则生成计划订单接收再按提前期往前推变成计划订单投放成为下一层物料的毛需求。这个递推里最容易翻车的是「预计入库」的口径。已下达采购订单、已下达生产订单、在途调拨这三类都算预计入库但很多系统默认只抓采购在途把车间已下达的工单漏了结果净需求虚高采购被重复建议。我一般会在跑批前先对一遍预计入库的取数范围确认它覆盖了所有已确认的供应来源。下面是一段用Python实现的单层净需求递推逻辑清晰方便你对照自己系统的结果排查。# 单层MRP净需求递推按时间桶逐期计算 def mrp_netting(gross_req, sched_receipts, on_hand, safety_stock, lot_size_rule, lead_time, periods): gross_req: 每期毛需求 list sched_receipts: 每期预计入库 list on_hand: 期初可用库存 safety_stock: 安全库存 lot_size_rule: 批量规则LFL按需或固定倍数 lead_time: 提前期单位期数 periods: 总期数 projected_available on_hand planned_order_receipt [0] * periods planned_order_release [0] * periods for t in range(periods): # 预计可用 上期可用 预计入库 - 毛需求 projected_available (projected_available sched_receipts[t] - gross_req[t]) # 净需求低于安全库存的部分需要补 net_req safety_stock - projected_available if net_req 0: if lot_size_rule LFL: order_qty net_req else: # 固定批量向上取整到倍数 multiple lot_size_rule order_qty ((net_req multiple - 1) // multiple) * multiple planned_order_receipt[t] order_qty projected_available order_qty # 按提前期前推投放期 release_period t - lead_time if release_period 0: planned_order_release[release_period] order_qty return planned_order_receipt, planned_order_release这段代码里projected_available是逐期滚动的预计可用量net_req为负说明库存够用不生成订单。批量规则用LFLLot-for-Lot按需批量时订多少补多少库存最省但订单最碎用固定倍数时订单整齐但可能产生余量。提前期lead_time决定投放期如果算出来是负数说明交期已经来不及系统一般会抛异常或建议提前这个信号必须接住不能忽略。参数上安全库存不是拍脑袋定的它跟需求波动和供应波动挂钩。需求标准差大、供应商交期不稳安全库存就得抬。但安全库存设太高MRP会频繁触发补货呆滞风险跟着涨。我一般建议先按历史消耗的变异系数估一版跑三个月再调。2.2 低层码与BOM展开顺序为什么运算顺序错了全盘皆错MRP展开BOM时必须按低层码(low-level code)从低到高运算。低层码是某个物料在所有BOM中出现在的最低层级。比如一颗电阻既用在成品A第1层又用在半成品B第2层那它的低层码就是2。运算时必须等第1层和第2层的需求都汇总完才能算这颗电阻的净需求。如果顺序反了先算电阻再算半成品BB产生的电阻需求就漏了结果就是缺料。常见做法是跑批前先重算低层码尤其是工程变更(ECN)之后。BOM结构一改低层码可能变不重算就会用旧的展开顺序。我见过一次因为新增了一个共用件低层码从3变成2但系统没刷新导致该物料的需求少算了一大截产线停了两天。低层码的计算可以用一遍BFS从成品往下遍历记录每个物料出现的最大深度。下面是一个简化实现。from collections import deque def compute_low_level_code(bom_dict, finished_goods): bom_dict: {父件: [子件列表]} finished_goods: 成品列表 返回 {物料: 低层码} llc {} # BFS逐层展开记录每个物料出现的最大层级 queue deque([(fg, 0) for fg in finished_goods]) while queue: item, level queue.popleft() # 低层码取出现过的最大层级 if item not in llc or level llc[item]: llc[item] level for child in bom_dict.get(item, []): queue.append((child, level 1)) return llcbom_dict是父件到子件的映射finished_goods是独立需求的成品。BFS保证按层级推进llc记录每个物料出现过的最大深度。实际系统里还要处理环状BOM自己引用自己得加访问标记防死循环。算完低层码后按llc升序排列物料再逐个跑净需求才能保证上层需求全部汇总到位。2.3 提前期偏移与计划订单投放时间轴对齐的实操提前期偏移是MRP里最容易被低估的环节。采购提前期、生产提前期、检验提前期每一段都要算进去。很多系统只填一个采购提前期把收货检验、上架的时间漏了结果物料到了但不能用产线照样停。我一般会把提前期拆成三段下单到供应商发货、运输在途、到货检验入库。三段相加才是真正的可用提前期。生产件同理要拆成备料、加工、检验。这些数据不一定要很精确但必须完整漏一段就是坑。计划订单投放的日期 计划订单接收日期 - 总提前期。如果投放日期落在过去说明已经来不及系统应该给出例外信息。跑MRP时一定要看例外消息不能只看计划订单列表。例外消息里的「提前期不足」「交期已过」才是真正要处理的。时间桶的粒度也要注意。按天跑和按周跑结果可能差很多。交期紧的行业建议按天甚至按班次。桶太粗提前期偏移会失真该今天下的单被推到下周缺料就来了。3. 用Python搭一版可复现的MRP运算数据结构与跑批流程理解了计算逻辑下一步是把它跑起来。这一章给出一套最小可用的MRP运算框架包含数据结构定义、BOM展开、逐层净需求和例外信息输出。你可以直接拿去改接自己的数据源。3.1 物料主数据与BOM的数据结构设计MRP的输入就三块物料主数据、BOM、库存与在途。物料主数据里关键字段是提前期、安全库存、批量规则、低层码。BOM是父子关系加用量。库存分现有量和预计入库。用Python的dataclass定义清晰且好扩展。from dataclasses import dataclass, field from typing import List, Dict dataclass class Material: code: str lead_time: int # 总提前期单位天 safety_stock: float lot_size_rule: str # LFL 或固定倍数 low_level_code: int 0 dataclass class BomLine: parent: str child: str qty_per: float # 单位用量 dataclass class Inventory: code: str on_hand: float scheduled_receipts: Dict[int, float] field(default_factorydict) # 键为期数值为该期预计入库量Material里的lead_time是总提前期建议在数据准备阶段就把采购、运输、检验加好不要留到运算时再拼。BomLine的qty_per是单位用量注意损耗率要提前乘进去比如实际用量是1.02就填1.02不要等到运算时再算。Inventory的scheduled_receipts用字典按期间存方便按桶取数。这套结构的好处是跟数据库表能一一对应物料主数据一张表BOM一张表库存一张表跑批时全量加载到内存运算快且好调试。3.2 逐层展开与净需求运算的跑批脚本有了数据结构跑批流程分四步算低层码、按低层码排序、逐层算净需求、汇总计划订单。下面是一个完整的最小实现。def run_mrp(materials: Dict[str, Material], bom_lines: List[BomLine], inventory: Dict[str, Inventory], independent_demand: Dict[str, Dict[int, float]], periods: int): materials: 物料主数据字典 bom_lines: BOM行列表 inventory: 库存字典 independent_demand: {物料: {期数: 需求量}} periods: 总期数 # 构建BOM父子映射 bom_dict {} for line in bom_lines: bom_dict.setdefault(line.parent, []).append(line) # 第一步算低层码 finished list(independent_demand.keys()) llc compute_low_level_code( {p: [l.child for l in lines] for p, lines in bom_dict.items()}, finished ) for code, level in llc.items(): if code in materials: materials[code].low_level_code level # 第二步按低层码升序排列 sorted_codes sorted(llc.keys(), keylambda c: llc[c]) # 第三步逐层算净需求 gross_req {code: {} for code in sorted_codes} planned_release {code: {} for code in sorted_codes} for code in sorted_codes: mat materials[code] inv inventory.get(code, Inventory(code, 0)) # 汇总毛需求独立需求 上层投放 req {} for t, qty in independent_demand.get(code, {}).items(): req[t] req.get(t, 0) qty for t, qty in gross_req.get(code, {}).items(): req[t] req.get(t, 0) qty gross_list [req.get(t, 0) for t in range(periods)] receipt_list [inv.scheduled_receipts.get(t, 0) for t in range(periods)] por, pol mrp_netting( gross_list, receipt_list, inv.on_hand, mat.safety_stock, mat.lot_size_rule, mat.lead_time, periods ) planned_release[code] {t: q for t, q in enumerate(pol) if q 0} # 把本层投放传递给子件作为子件毛需求 for line in bom_dict.get(code, []): child_req gross_req.setdefault(line.child, {}) for t, qty in planned_release[code].items(): child_req[t] child_req.get(t, 0) qty * line.qty_per return planned_release跑批的核心在最后那个循环算完当前物料的计划订单投放后立刻按BOM用量乘给子件累加到子件的毛需求里。因为已经按低层码排序子件一定在父件之后被处理需求不会漏。independent_demand是成品的独立需求备件和样品也放这里。periods是总期数按天跑就填天数。这段代码没处理环状BOM和负库存实际用的时候要加校验。另外mrp_netting里提前期为负的情况要单独抛出来作为例外信息。3.3 例外信息与计划订单的验证方法跑完MRP别急着把计划订单推给采购。先看例外信息。例外信息一般分几类提前期不足、交期已过、负库存、BOM环状引用、无供应商。每一类都要有处理动作。验证计划订单是否合理我常用两个办法。一是反查随便挑一个计划订单手工从成品需求往下推看能不能对上。二是总量校验所有物料的计划订单投放按BOM展开后的总需求应该跟独立需求的总量在数量级上一致差太多说明某层漏了或重了。下面是一个简单的例外信息检查。def check_exceptions(planned_release, materials, periods): exceptions [] for code, releases in planned_release.items(): mat materials[code] for t, qty in releases.items(): if t 0: exceptions.append({ type: 提前期不足, material: code, period: t, qty: qty, msg: f{code} 需在第{t}期投放已过期 }) if t 0 and mat.lead_time 0: exceptions.append({ type: 交期紧张, material: code, period: t, qty: qty, msg: f{code} 需立即投放提前期{mat.lead_time}天 }) return exceptionsplanned_release里键为负数的说明投放期已经过了必须人工干预。键为0的说明今天就得下单也是紧急信号。这些例外信息要每天跑完MRP后第一时间看不能等。验证时还要注意单位一致。BOM用量、库存、需求单位必须统一。我见过因为BOM用「千个」而库存用「个」导致需求放大一千倍的翻车现场。跑批前先做单位校验能省很多事。4. MRP跑批的避坑清单那些让计划对不上账的细节MRP的坑大多不在算法在数据和参数。这一章列几条我踩过的每条按现象、原因、解决写你对照排查。4.1 预计入库口径不一致导致净需求虚高现象MRP建议的采购量比实际需要多出一大截仓库明明有货系统还在催采购。原因预计入库只取了采购在途漏了已下达的生产工单和调拨在途。净需求 毛需求 安全库存 - 预计入库 - 可用库存预计入库少算净需求就虚高。解决跑批前核对预计入库的取数范围确认覆盖采购在途、生产在途、调拨在途三类。最好在数据准备层就把它们合并成一张供应表MRP只读这张表避免口径分散。4.2 低层码未刷新导致共用件需求漏算现象某个共用件总是缺料但它的独立需求看起来不大查BOM发现它被多个半成品使用。原因工程变更后BOM结构变了低层码没重算MRP按旧的展开顺序运算共用件在低层级的需求没汇总进来。解决每次ECN生效后强制重算低层码跑MRP前加一道校验对比低层码是否有变化。有变化就重新排序再跑。4.3 提前期只填采购段导致到货即缺料现象采购说货已经到了产线还是停一问还在检验。原因提前期只填了下单到供应商发货运输和检验没算进去。计划订单投放日期偏晚物料到了但不能用。解决提前期拆成下单、运输、检验三段相加作为总提前期。检验时间不稳定的按历史均值加安全余量。4.4 批量规则选错导致库存越跑越高现象MRP跑着跑着呆滞库存越来越多但缺料也没少。原因批量规则用了固定大批量需求小的时候也按大批量补余量累积成呆滞。或者安全库存设太高频繁触发补货。解决需求平稳的物料用LFL需求碎但供应有最小起订量的用固定倍数并定期复核倍数是否合理。安全库存按变异系数估每季度调一次。4.5 时间桶粒度太粗导致交期失真现象按周跑MRP计划订单投放日期总是差几天紧急单来不及。原因时间桶按周提前期偏移以周为单位精度不够。交期紧的物料被粗桶抹平了。解决交期紧的行业按天跑甚至按班次。桶粒度至少要比最短提前期细一个数量级。5. MRP运算结果的验证与调优从能跑到跑得准跑通MRP只是第一步跑得准才是目标。这一章讲怎么验证结果、怎么调参数以及一个我常用的对账技巧。5.1 用反查法验证单条计划订单挑一条计划订单从成品需求手工往下推。比如成品A第10期需要100台BOM里用到半成品B两件、外壳一件。半成品B又用到某芯片三颗。手工算一遍芯片在第10期的毛需求再减去库存和在途看净需求跟MRP结果是否一致。不一致就顺着BOM一层层查看哪一层的用量、损耗率、提前期对不上。这个方法笨但有效新上线MRP时我一般会抽十条不同层级的订单反查。5.2 关键参数的敏感度测试MRP结果对几个参数特别敏感安全库存、批量倍数、提前期。调参前先做敏感度测试固定其他参数只动一个看计划订单总量和缺料率怎么变。参数测试范围观察指标调优方向安全库存0.5倍到2倍历史均值缺料次数、呆滞金额缺料多则抬呆滞多则降批量倍数最小起订量的1到3倍订单数、库存周转订单碎则抬库存高则降提前期历史均值上下20%例外信息数量例外多则复核实际提前期测试时一次只动一个参数跑完对比计划订单列表和例外信息。敏感度高的参数要重点管敏感度低的可以粗放一点。5.3 一个对账习惯总量守恒校验我每次跑完MRP会做一个总量守恒校验把所有成品的独立需求按BOM展开算出每种物料的毛需求总量再跟MRP输出的毛需求总量对比。理论上应该一致差太多说明某层漏算或重算。这个校验能快速发现BOM错误、低层码错误、单位错误。具体做法是写一个独立的展开脚本不依赖MRP的运算逻辑纯按BOM递归展开然后跟MRP结果对。两个独立实现的结果一致才敢把计划订单推出去。这个习惯帮我抓过好几次问题有一次是BOM里某个物料的损耗率被误填成10而不是1.1展开后需求差了近十倍总量校验直接暴露了。MRP这东西算法不难难在数据和参数的一致性。把校验做在前面比事后救火省心得多。希望帮到你。本文还有配套的精品资源点击获取