乐于分享
好东西不私藏

ERPNext 生产模块分析文档

ERPNext 生产模块分析文档

一、概述

ERPNext 的生产模块实现了完整的制造业务流程,包括生产计划、生产工单、物料清单(BOM)、工票管理等核心功能。核心设计理念是通过 Work Order(生产工单)驱动生产流程,BOM(物料清单)定义产品结构,Job Card(工票)记录工序执行,实现生产过程的精细化管理和成本追踪。

模块定位

┌─────────────────────────────────────────────────────────────┐│                    ERPNext 生产模块                        │├─────────────────────────────────────────────────────────────┤│  基础数据层:BOM(物料清单)、Routing(工艺路线)、Workstation││  生产计划层:Production Plan(生产计划)、Material Request  ││  生产执行层:Work Order(生产工单)、Job Card(工票)        ││  成本核算层:Operating Cost(运营成本)、WIP(在制品)       ││  质量管理层:Quality Inspection(质量检验)                ││  库存集成层:Stock Entry(库存变动)、Serial/Batch Management│└─────────────────────────────────────────────────────────────┘

二、核心数据模型

2.1 BOM(物料清单)

文件:bom.py

表结构(关键字段)

字段
类型
说明
name
VARCHAR(140)
BOM编号(BOM-item-001)
item
Link
成品物料编码
item_name
Data
成品名称
quantity
Float
BOM数量(生产1个成品所需)
is_active
Check
是否激活
is_default
Check
是否默认BOM
is_phantom_bom
Check
是否虚拟BOM(用于多层BOM展开)
has_variants
Check
是否有变体
inspection_required
Check
是否需要质检
operations
Table[BOMOperation]
工序列表
items
Table[BOMItem]
物料清单(投入物)
secondary_items
Table[BOMSecondaryItem]
产出物(联产品/副产品/废品/额外成品)

BOMItem 子表

class BOMItem:    item_code: DF.Link           # 物料编码    item_name: DF.Data           # 物料名称    qty: DF.Float               # 数量    stock_qty: DF.Float         # 库存单位数量    uom: DF.Link                # 计量单位    conversion_factor: DF.Float # 转换因子    bom_no: DF.Link | None      # 子BOM编号(用于子装配)    is_stock_item: DF.Check     # 是否库存物料    is_phantom_item: DF.Check   # 是否虚拟物料    source_warehouse: DF.Link   # 源仓库

BOMOperation 子表(BOM 工序行)

文件:bom_operation.py

class BOMOperation(Document):    # 工序与工位    operation: DF.Link              # 工序    workstation: DF.Link | None     # 具体工位    workstation_type: DF.Link | None # 工位类型(可替代具体工位,排产时再分配)    sequence_id: DF.Int             # 工序顺序号    description: DF.TextEditor | None    image: DF.Attach | None    # 工时与成本    time_in_mins: DF.Float          # 工时(分钟)    hour_rate: DF.Currency          # 小时费率    operating_cost: DF.Currency     # 运营成本 = hour_rate × time_in_mins / 60    base_hour_rate: DF.Currency     # 本位币小时费率    base_operating_cost: DF.Currency    fixed_time: DF.Check            # 是否固定工时(不随数量变化)    set_cost_based_on_bom_qty: DF.Check  # 按BOM数量计算成本    cost_per_unit: DF.Float         # 单位成本    base_cost_per_unit: DF.Float    # 批量与委外    batch_size: DF.Float            # 批量大小    is_subcontracted: DF.Check      # 是否委外    bom_no: DF.Link | None          # 委外子装配BOM    # 产出与仓库    finished_good: DF.Link | None   # 该工序产出的半成品/成品    finished_good_qty: DF.Float    is_final_finished_good: DF.Check    source_warehouse: DF.Link | None    wip_warehouse: DF.Link | None    fg_warehouse: DF.Link | None    # 流转控制    skip_material_transfer: DF.Check        # 跳过物料转移    backflush_from_wip_warehouse: DF.Check  # 从WIP仓库反冲    quality_inspection_required: DF.Check   # 是否需要质检

BOM成本结构

class BOM:    raw_material_cost: DF.Currency      # 原材料成本    operating_cost: DF.Currency         # 运营成本(人工+机器)    secondary_items_cost: DF.Currency   # 产出物成本(联产品/副产品等)    total_cost: DF.Currency             # 总成本    process_loss_percentage: DF.Percent # 工艺损耗百分比

BOMSecondaryItem 子表(产出物/联副产品)

文件:bom_secondary_item.py

BOM 不仅定义投入物(原材料),还能定义产出物(除主成品外的联产品、副产品、废品等),并支持成本分摊:

class BOMSecondaryItem(Document):    item_code: DF.Link           # 产出物料编码    item_name: DF.Data | None    qty: DF.Float                # 产出数量    stock_qty: DF.Float          # 库存单位数量    uom: DF.Link                 # 计量单位    stock_uom: DF.Link | None    conversion_factor: DF.Float    # 产出物类型    secondary_item_type: DF.Literal[        "",        "Co-Product",              # 联产品(主产品之一,分摊成本)        "By-Product",              # 副产品(附带产出,低成本分摊)        "Scrap",                   # 废品(边角料)        "Additional Finished Good", # 额外成品    ]    # 成本与分摊    rate: DF.Currency            # 单价    cost: DF.Currency            # 成本    base_cost: DF.Currency       # 本位币成本    cost_allocation_per: DF.Percent  # 成本分摊比例(联产品按比例分摊主成本)    # 工艺损耗    process_loss_per: DF.Percent  # 工艺损耗百分比    process_loss_qty: DF.Float    # 工艺损耗数量    description: DF.TextEditor | None    image: DF.AttachImage | None    is_legacy: DF.Check           # 是否旧版数据

成本分摊逻辑:联产品(Co-Product)通过 cost_allocation_per 按比例分摊生产总成本;副产品(By-Product)和废品(Scrap)通常按固定单价扣减主成品成本。

BOM树结构

class BOMTree:    """BOM的树形表示,支持多层展开"""    # 示例:成品A的BOM结构    # BOM-A    # ├── SubAssembly-1(子装配,有自己的BOM)    # │   ├── item1    # │   └── item2    # ├── SubAssembly-2(子装配,有自己的BOM)    # │   └── item3    # └── item4    # 展开方法    def level_order_traversal(self):        """层次遍历,返回展开后的所有物料"""        # 用于物料需求计算

2.2 Work Order(生产工单)

文件:work_order.py

表结构(关键字段)

字段
类型
说明
name
VARCHAR(140)
工单编号(MFG-WO-.YYYY.-)
production_item
Link
成品物料编码
item_name
Data
成品名称
qty
Float
计划生产数量
bom_no
Link
使用的BOM编号
company
Link
所属公司

状态字段

status: DF.Literal[    "",    "Draft",                # 草稿    "Submitted",            # 已提交    "Not Started",          # 未开始    "In Process",           # 进行中    "Stock Reserved",       # 库存已预留    "Stock Partially Reserved",  # 库存部分预留    "Completed",            # 已完成    "Stopped",              # 已停止    "Closed",               # 已关闭    "Cancelled",            # 已取消]

仓库字段

字段
类型
说明
fg_warehouse
Link
成品入库仓库
source_warehouse
Link
原材料仓库
wip_warehouse
Link
在制品仓库
scrap_warehouse
Link
废品仓库

数量字段

字段
类型
说明
produced_qty
Float
已生产数量
material_transferred_for_manufacturing
Float
已转移到生产的数量
process_loss_qty
Float
工艺损耗数量
disassembled_qty
Float
已拆解数量

关联字段

字段
类型
说明
sales_order
Link
关联销售订单
sales_order_item
Data
销售订单行
material_request
Link
关联物料请求
production_plan
Link
关联生产计划
project
Link
关联项目

WorkOrderItem 子表(所需物料)

class WorkOrderItem:    item_code: DF.Link          # 物料编码    item_name: DF.Data          # 物料名称    stock_qty: DF.Float        # 所需数量    transferred_qty: DF.Float  # 已转移数量    consumed_qty: DF.Float     # 已消耗数量    source_warehouse: DF.Link  # 源仓库    bom_no: DF.Link | None     # 子BOM编号

WorkOrderAdditionalItem 子表(额外物料/费用)

文件:work_order_additional_item.py

class WorkOrderAdditionalItem(Document):    item_code: DF.Link | None       # 物料编码(可为非库存物料,如外包服务)    uom: DF.Link | None             # 计量单位    qty: DF.Float                   # 数量    rate: DF.Currency               # 单价    amount: DF.Currency             # 金额    secondary_item_type: DF.Data | None  # 关联产出物类型

记录工单执行过程中计划外的额外物料或费用(如外包加工费、额外辅料),非 BOM 定义的标准物料。

WorkOrderOperation 子表(工单工序行)

文件:work_order_operation.py

在 BOM 工序基础上增加计划/实际执行追踪字段:

class WorkOrderOperation(Document):    # 工序与工位    operation: DF.Link                  # 工序    workstation: DF.Link | None         # 工位    workstation_type: DF.Link | None    # 工位类型    sequence_id: DF.Int                 # 顺序号    description: DF.TextEditor | None    batch_size: DF.Float    # 工时与费率    time_in_mins: DF.Float              # 计划工时    hour_rate: DF.Float                 # 小时费率    # 计划数据    planned_start_time: DF.Datetime | None    planned_end_time: DF.Datetime | None    planned_operating_cost: DF.Currency    # 实际数据    actual_start_time: DF.Datetime | None    actual_end_time: DF.Datetime | None    actual_operation_time: DF.Float     # 实际工时    actual_operating_cost: DF.Currency    # 进度    completed_qty: DF.Float             # 已完成数量    pending_qty: DF.Float               # 待完成数量    process_loss_qty: DF.Float          # 工艺损耗数量    status: DF.Literal["Pending", "Work in Progress", "Completed"]    # 委外与产出    is_subcontracted: DF.Check    bom: DF.Link | None    bom_no: DF.Link | None    finished_good: DF.Link | None    quality_inspection_required: DF.Check    # 仓库    source_warehouse: DF.Link | None    wip_warehouse: DF.Link | None    fg_warehouse: DF.Link | None    # 流转控制    skip_material_transfer: DF.Check    backflush_from_wip_warehouse: DF.Check

2.3 Job Card(工票)

文件:job_card.py

表结构(关键字段)

字段
类型
说明
name
VARCHAR(140)
工票编号(MFG-JC-.YYYY.-)
work_order
Link
关联生产工单
production_item
Link
成品物料
bom_no
Link
BOM编号
company
Link
公司

工序字段

字段
类型
说明
operation
Link
工序名称
workstation
Link
工作中心
hour_rate
Currency
小时费率

数量字段

字段
类型
说明
for_quantity
Float
计划生产数量
completed_qty
Float
已完成数量
pending_qty
Float
待完成数量

时间字段

字段
类型
说明
expected_start_date
Datetime
预计开始时间
expected_end_date
Datetime
预计结束时间
actual_start_date
Datetime
实际开始时间
actual_end_date
Datetime
实际结束时间

JobCardItem 子表(物料消耗)

class JobCardItem:    item_code: DF.Link          # 物料编码    stock_qty: DF.Float        # 计划消耗数量    consumed_qty: DF.Float     # 实际消耗数量    source_warehouse: DF.Link  # 源仓库

JobCardTimeLog 子表(工时记录)

class JobCardTimeLog:    employee: DF.Link          # 员工    time_in_mins: DF.Float     # 工作时长(分钟)    start_time: DF.Datetime    # 开始时间    end_time: DF.Datetime      # 结束时间

JobCardOperation 子表(工序执行记录)

文件:job_card_operation.py

class JobCardOperation(Document):    sub_operation: DF.Link | None   # 子工序(工步细分)    status: DF.Literal["Complete", "Pause", "Pending", "Work In Progress"]    completed_qty: DF.Float         # 已完成数量    completed_time: DF.Data | None  # 已完成工时

JobCardScheduledTime 子表(工票排程)

文件:job_card_scheduled_time.py

class JobCardScheduledTime(Document):    from_time: DF.Datetime | None   # 排程开始时间    to_time: DF.Datetime | None     # 排程结束时间    time_in_mins: DF.Float          # 排程工时

用于工位排产,记录工票在工位上的计划占用时段。

JobCardSecondaryItem 子表(工票产出物)

文件:job_card_secondary_item.py

class JobCardSecondaryItem(Document):    item_code: DF.Link              # 产出物料编码    item_name: DF.Data | None    stock_qty: DF.Float             # 实际产出数量    stock_uom: DF.Link | None    bom_secondary_item: DF.Data | None  # 关联BOM产出物定义    secondary_item_type: DF.Literal[        "Co-Product",              # 联产品        "By-Product",              # 副产品        "Scrap",                   # 废品        "Additional Finished Good", # 额外成品    ]    description: DF.SmallText | None

记录工票执行时实际产出的联产品/副产品/废品,与 BOM 的 secondary_items 定义对应。


2.4 Production Plan(生产计划)

文件:production_plan.py

表结构(关键字段)

字段
类型
说明
name
VARCHAR(140)
计划编号(MFG-PP-.YYYY.-)
company
Link
公司
get_items_from
Select
数据来源(Sales Order/Material Request)

计划选项

字段
类型
说明
combine_items
Check
是否合并相同物料
combine_sub_items
Check
是否合并子装配物料
consider_minimum_order_qty
Check
是否考虑最小订购量
include_non_stock_items
Check
是否包含非库存物料
include_safety_stock
Check
是否包含安全库存
include_subcontracted_items
Check
是否包含委外加工物料

2.5 工位(Workstation)体系

ERPNext 用一套 DocType 管理"工位"(机器/工位资源),支持工位类型、工作时间、成本组件、所属车间等。

核心表

DocType
说明
Workstation
工位主表(机器/工位资源)
Workstation Type
工位类型(如:数控车床、注塑机),定义成本模板
Workstation Working Hour
工位工作时间(班次)
Workstation Cost
工位成本组件(电费/人工/折旧等)
Plant Floor
车间(工位所属物理区域,支持看板)

Workstation 关键字段

workstation.py#L39

class Workstation(Document):    workstation_name: DF.Data               # 工位名称    workstation_type: DF.Link | None        # 工位类型    plant_floor: DF.Link | None             # 所属车间    warehouse: DF.Link | None               # 关联仓库(WIP)    hour_rate: DF.Currency                  # 小时费率(自动汇总成本组件)    production_capacity: DF.Int             # 产能(同时可处理数量)    total_working_hours: DF.Float           # 总工作小时    status: DF.Literal[        "Production",     # 生产中        "Off",            # 关闭        "Idle",           # 空闲        "Problem",        # 故障        "Maintenance",    # 维护        "Setup",          # 换型    ]    holiday_list: DF.Link | None            # 节假日表    working_hours: DF.Table[WorkstationWorkingHour]    # 工作时间    workstation_costs: DF.Table[WorkstationCost]       # 成本组件

工位成本计算

工位的 hour_rate 由多个成本组件汇总而来(workstation.py#L110):

def set_hour_rate(self):    self.hour_rate = 0.0    for row in self.workstation_costs:        if row.operating_cost:            self.hour_rate += flt(row.operating_cost)

成本组件示例:电费、人工、机器折旧、维护费等。可通过 Workstation Type 统一模板,批量应用到同类型工位(workstation.py#L121 的 _set_data_based_on_workstation_type)。

Workstation Type(工位类型)

workstation_type.py#L10

class WorkstationType(Document):    workstation_type: DF.Data                       # 类型名称    description: DF.SmallText | None    hour_rate: DF.Currency                          # 汇总小时费率    workstation_costs: DF.Table[WorkstationCost]    # 成本组件模板

工位类型作为成本模板,新建工位时选择类型会自动带入成本组件清单。

Workstation Working Hour(工作时间/班次)

workstation_working_hour.py#L8

class WorkstationWorkingHour(Document):    enabled: DF.Check    start_time: DF.Time    end_time: DF.Time

用于校验工序是否在工位工作时间内完成(workstation.py#L437 的 is_within_operating_hours),可通过 Manufacturing Settings 的 allow_overtime 放开限制。

Workstation Cost(成本组件)

workstation_cost.py#L8

class WorkstationCost(Document):    operating_component: DF.Link     # 运营组件(电费/人工/折旧等)    operating_cost: DF.Currency      # 该组件的小时成本

Plant Floor(车间看板)

plant_floor.py#L10

class PlantFloor(Document):    company: DF.Link | None    floor_name: DF.Data | None    warehouse: DF.Link | None

车间关联工位,工位状态变化时通过 publish_workstation_status(workstation.py#L150)实时推送到车间看板,状态颜色映射(workstation.py#L534):

def get_color_map():    return {        "Production": "green",    # 生产中        "Off": "gray",            # 关闭        "Idle": "gray",           # 空闲        "Problem": "red",         # 故障        "Maintenance": "yellow",  # 维护        "Setup": "blue",          # 换型    }

工位与工单/工票的联动

工位提供 start_job 和 complete_job 方法(workstation.py#L226),支持从工位直接开工/完工工票:

@frappe.whitelist(methods=["POST"])def start_job(self, job_card: str, from_time, employee: str):    doc = frappe.get_doc("Job Card", job_card)    doc.append("time_logs", {"from_time": from_time, "employee": employee})    doc.save()@frappe.whitelist(methods=["POST"])def complete_job(self, job_card: str, qty: float, to_time):    # 填写结束时间并提交工票

工位变更时还会同步更新关联 BOM 工序的小时费率(workstation.py#L194 的 update_bom_operation)。


2.6 工步(Operation)体系

ERPNext 中"工步"对应 Operation(工序),并支持子工序(Sub Operation)细分,工序通过 Routing(工艺路线)有序组合。

核心表

DocType
说明
Operation
工序主表(可含子工序)
Sub Operation
子工序(工步细分)
Routing
工艺路线(工序有序集合,可被多个BOM复用)

Operation 关键字段

operation.py#L10

class Operation(Document):    workstation: DF.Link | None                         # 默认工位    batch_size: DF.Int                                  # 批量大小    create_job_card_based_on_batch_size: DF.Check       # 是否按批量生成工票    is_corrective_operation: DF.Check                   # 是否返工工序    quality_inspection_template: DF.Link | None         # 质检模板    sub_operations: DF.Table[SubOperation]              # 子工序列表    total_operation_time: DF.Float                      # 总工时(子工序汇总)

Sub Operation(子工序/工步细分)

sub_operation.py#L9

class SubOperation(Document):    operation: DF.Link        # 引用的工序    time_in_mins: DF.Float    # 子工序工时    description: DF.SmallText

工序总工时自动汇总子工序(operation.py#L53):

def set_total_time(self):    self.total_operation_time = 0.0    for row in self.sub_operations:        self.total_operation_time += row.time_in_mins

校验子工序不能重复、不能自引用(operation.py#L38 的 duplicate_sub_operation)。

Routing(工艺路线)

routing.py#L11

class Routing(Document):    routing_name: DF.Data | None    operations: DF.Table[BOMOperation]   # 工序列表    disabled: DF.Check

工艺路线把多个工序按 sequence_id 排序组合,自动计算每道工序的运营成本(routing.py#L34):

def calculate_operating_cost(self):    for operation in self.operations:        if not operation.hour_rate:            operation.hour_rate = frappe.db.get_value("Workstation", operation.workstation, "hour_rate")        operation.operating_cost = flt(operation.hour_rate) * flt(operation.time_in_mins) / 60

sequence_id 校验保证工序顺序递增(routing.py#L43 的 set_routing_id)。

工位 + 工步整体数据流

工位定义层:  Workstation Type ──┐                     ├──> Workstation ──> Plant Floor(车间看板)  Workstation Cost ──┘     │(hour_rate)                           │工步定义层:                │  Sub Operation ──> Operation(工序)                           │  Operation ──> Routing(工艺路线,按 sequence_id 排序)                           │业务传递层:                │  Routing ──> BOM Operation(BOM 工序行)                  │                  ├──> Work Order Operation(工单工序,含计划/实际)                  │       │                  │       └──> Job Card(工票,每道工序一张)                  │               ├──> Job Card Operation(执行记录,含子工序)                  │               └──> Job Card Time Log(工时记录)                  │                  └──> 运营成本计入 BOM 总成本 / Work Order 实际成本

工位与工步的关键设计要点

  1. 工位与工位类型分离:‎Workstation Type 定义成本模板,‎Workstation 是具体资源,便于批量管理同类设备
  2. 工序可嵌套:‎Operation 通过 ‎Sub Operation 支持工步细分
  3. 工艺路线可复用:‎Routing 独立于 BOM,多个 BOM 可共用同一条工艺路线
  4. 工位类型可替代具体工位:BOM 工序行可只指定 ‎workstation_type,排产时再分配具体工位
  5. 工序级别仓库控制:每道工序可独立配置 ‎source_warehouse/‎wip_warehouse/‎fg_warehouse,支持多工序分阶段入库
  6. 工序级别委外:单道工序可标记 ‎is_subcontracted,支持工序级委外加工
  7. 返工工序:‎is_corrective_operation 标记返工工序,用于不合格品返工流程
  8. 工位状态实时发布:通过 ‎publish_workstation_status 推送到 Plant Floor 看板,支持车间可视化
  9. 批量化工票:‎create_job_card_based_on_batch_size 支持按批量自动拆分工票
  10. 工时校验:工位工作时间与节假日表联动,校验工序是否可排产

2.7 停机记录(Downtime Entry)

文件:downtime_entry.py

记录工位停机事件,用于车间设备效率(OEE)分析和停机成本核算。

class DowntimeEntry(Document):    naming_series: DF.Literal["DT-"]    workstation: DF.Link              # 停机工位    operator: DF.Link                 # 操作员(员工)    from_time: DF.Datetime            # 停机开始时间    to_time: DF.Datetime              # 停机结束时间    downtime: DF.Float                # 停机时长(分钟,自动计算)    stop_reason: DF.Literal[        "",        "Excessive machine set up time",  # 设备调试时间过长        "Unplanned machine maintenance",   # 非计划维护        "On-machine press checks",         # 在机检查        "Machine operator errors",         # 操作员失误        "Machine malfunction",             # 设备故障        "Electricity down",                # 停电        "Other",                           # 其他    ]    remarks: DF.Text | None

停机时长自动计算(downtime_entry.py#L37):

def validate(self):    if self.from_time and self.to_time:        self.downtime = time_diff_in_hours(self.to_time, self.from_time) * 60

用途:结合工位工作时间,计算机器可用率(Availability)、性能率(Performance)、质量率(Quality),得出 OEE。


2.8 主生产计划(MPS)与销售预测

ERPNext 的生产计划体系分两层:

  • Sales Forecast(销售预测):基于历史/预期生成未来需求
  • Master Production Schedule(MPS,主生产计划):整合销售订单、物料请求、销售预测,计算累积提前期,生成 MRP
  • Production Plan(生产计划):基于 MPS 或独立的需求来源,展开 BOM 生成工单和采购请求

Sales Forecast(销售预测)

文件:sales_forecast.py

class SalesForecast(Document):    company: DF.Link    from_date: DF.Date    frequency: DF.Literal["Weekly", "Monthly"]  # 预测频率    demand_number: DF.Int                       # 需求期数    parent_warehouse: DF.Link    items: DF.Table[SalesForecastItem]          # 预测明细    selected_items: DF.TableMultiSelect[SalesForecastItem]    status: DF.Literal["Planned", "MPS Generated", "Cancelled"]    posting_date: DF.Date | None    amended_from: DF.Link | None

需求生成逻辑(sales_forecast.py#L37):按 frequency(周/月)和 demand_number 自动生成多个交付日期的需求行。

转化路径Sales Forecast → create_mps() → Master Production Schedule(sales_forecast.py#L70)。

Master Production Schedule(主生产计划)

文件:master_production_schedule.py

class MasterProductionSchedule(Document):    company: DF.Link    from_date: DF.Date    to_date: DF.Date | None    posting_date: DF.Date    sales_forecast: DF.Link | None       # 关联销售预测    sales_orders: DF.Table[ProductionPlanSalesOrder]      # 销售订单来源    material_requests: DF.Table[ProductionPlanMaterialRequest]  # 物料请求来源    select_items: DF.TableMultiSelect[MasterProductionScheduleItem]    items: DF.Table[MasterProductionScheduleItem]         # 计划明细    parent_warehouse: DF.Link | None

核心逻辑

  1. 需求汇总(master_production_schedule.py#L164):合并销售订单、物料请求、销售预测数据,按 ‎(item_code, delivery_date) 聚合数量。

  2. 累积提前期计算(master_production_schedule.py#L145):递归遍历 BOM 各层级物料的提前期(采购时间+制造时间+缓冲时间),得到成品的总提前期。

def get_cumulative_lead_time(self, item_code, bom_no, time_in_days=0):    time_in_days = get_item_lead_time(item_code)  # 当前物料提前期    bom_materials = frappe.get_all("BOM Item", filters={"parent": bom_no})    for row in bom_materials:        if row.bom_no:            time_in_days += self.get_cumulative_lead_time(row.item_code, row.bom_no)  # 递归子装配        else:            time_in_days += get_item_lead_time(row.item_code)  # 外购物料提前期    return time_in_days
  1. 下达日期计算:‎order_release_date = delivery_date - cumulative_lead_time(master_production_schedule.py#L297)。

  2. MRP 生成:提交后异步生成 MRP 日志(master_production_schedule.py#L438)。

MPS 与 Production Plan 的区别

维度
Master Production Schedule
Production Plan
定位
主生产计划(成品层)
物料需求计划(展开 BOM)
需求来源
销售订单 + 物料请求 + 销售预测
销售订单 + 物料请求 + BOM 展开
提前期
计算累积提前期,确定下达日期
基于 BOM 展开计算物料需求
产出
MRP 日志
工单 + 物料请求
层级
成品级(独立需求)
全层级(相关需求)

2.9 BOM 批量更新工具

用于批量替换 BOM 中的子 BOM 或批量更新 BOM 成本,避免手工逐个修改。

BOM Update Tool

文件:bom_update_tool.py

class BOMUpdateTool(Document):    current_bom: DF.Link    # 当前BOM    new_bom: DF.Link        # 新BOM

提供两种批量操作(通过异步队列执行):

  1. 替换 BOM(‎enqueue_replace_bom):将所有引用 ‎current_bom 的地方替换为 ‎new_bom,用于 BOM 版本切换。

  2. 更新成本(‎enqueue_update_cost):重新计算所有 BOM 的原材料成本和运营成本,用于价格变动后的成本刷新。

BOM Update Log

记录每次批量更新的执行日志,状态流转:Queued → In Progress → Completed / Failed。

自动更新

通过 Manufacturing Settings 的 update_bom_costs_automatically 开启自动更新(bom_update_tool.py#L48):系统定期检查并自动刷新 BOM 成本(超过 10 天的日志会触发重新计算)。


三、核心业务流程

3.1 生产计划流程

ERPNext 的生产计划体系包含三个层次,从需求预测到物料需求展开:

Sales Forecast → Master Production Schedule → Production Plan → Work Order   (销售预测)        (主生产计划/MPS)           (生产计划)        (工单)                         ↑              Sales Order / Material Request                 (销售订单/物料请求)

流程一:销售预测 → MPS(预测驱动)

1. 创建 Sales Forecast   ├── 选择预测频率(Weekly/Monthly)   ├── 设置需求期数(demand_number)   ├── 选择预测物料   └── 系统自动生成多期需求行2. 从销售预测创建 MPS   ├── create_mps() 映射   ├── MPS 获取销售订单 + 物料请求 + 销售预测数据   ├── 计算各物料的累积提前期(递归BOM)   ├── 计算下达日期 = 交付日期 - 累积提前期   └── 提交后异步生成 MRP 日志

流程二:Production Plan(订单驱动)

Sales Order / Material Request → Production Plan → Work Order1. 创建 Production Plan   ├── 选择数据来源(销售订单或物料请求)   ├── 设置计划选项(合并物料、考虑安全库存等)   ├── 系统自动展开BOM,计算所需原材料   └── 生成生产计划明细2. 提交 Production Plan   ├── 预留库存(可选)   ├── 生成 Material Request(如果原材料不足)   └── 创建 Work Order3. 生产计划展开逻辑   ├── 多层BOM展开(BOMTree.level_order_traversal)   ├── 计算子装配需求   ├── 考虑现有库存和在途数量   └── 生成物料需求清单

BOM展开示例

# 成品A的BOM结构# BOM-A: 生产1个A需要 1个SubAssembly-1 + 1个SubAssembly-2 + 2个item4# BOM-SubAssembly-1: 生产1个SubAssembly-1需要 2个item1 + 3个item2# BOM-SubAssembly-2: 生产1个SubAssembly-2需要 1个item3# 计划生产10个A,展开后:# item1: 10 × 2 = 20# item2: 10 × 3 = 30# item3: 10 × 1 = 10# item4: 10 × 2 = 20

3.2 生产工单流程

Work Order → Stock Entry(物料转移)→ Job Card(工序执行)→ Stock Entry(成品入库)1. 创建 Work Order   ├── 选择成品物料和BOM   ├── 输入计划生产数量   ├── 指定仓库(成品库、原材料库、WIP库)   ├── 系统自动从BOM获取所需物料列表(required_items)   └── 系统自动获取工序列表(operations)2. 提交 Work Order   ├── 验证BOM有效性   ├── 设置默认仓库   ├── 计算运营成本   ├── 更新状态为 "Submitted" 或 "Not Started"   └── 如果启用库存预留,创建库存预留记录3. 物料转移到生产   ├── 创建 Stock Entry(Material Transfer)   ├── 将原材料从 source_warehouse 转移到 wip_warehouse   ├── 更新 WorkOrderItem.transferred_qty   └── 创建 SLE 和 GL Entry(如果仓库关联不同科目)4. 工序执行(Job Card)   ├── 从 Work Order 创建 Job Card(按工序)   ├── 记录工时(JobCardTimeLog)   ├── 记录物料消耗(JobCardItem)   ├── 更新工序状态   └── 更新运营成本5. 成品入库   ├── 创建 Stock Entry(Manufacture)   ├── 从 wip_warehouse 转移成品到 fg_warehouse   ├── 更新 WorkOrder.produced_qty   ├── 创建 SLE 和 GL Entry:   │   ├── 借: Stock In Hand(成品仓库)   │   ├── 贷: Stock In Hand(原材料)   │   └── 贷: WIP(在制品差异)   └── 更新序列号/批次6. 工单完成   ├── 所有工序完成   ├── produced_qty >= qty   ├── 更新状态为 "Completed"   └── 关闭工单

Work Order 验证逻辑

def validate(self):    self.validate_production_item()    if self.bom_no:        validate_bom_no(self.production_item, self.bom_no)    self.validate_sales_order()    self.set_default_warehouse()    self.set_operation_warehouses()    self.validate_warehouse_belongs_to_company()    self.check_wip_warehouse_skip()    self.calculate_operating_cost()    self.validate_qty()    self.validate_transfer_against()    self.validate_operations()    self.status = self.get_status()    self.validate_workstation_type()

3.3 工票流程

Job Card 创建 → 开工 → 记录工时 → 记录物料消耗 → 完成1. 创建 Job Card   ├── 从 Work Order 创建   ├── 选择工序和工作中心   ├── 指定计划生产数量(for_quantity)   └── 系统自动获取物料消耗清单2. 开工   ├── 设置 actual_start_date   ├── 更新状态为 "WIP"   └── 如果启用,创建序列号/批次3. 记录工时   ├── 添加 JobCardTimeLog   ├── 记录员工、开始时间、结束时间   └── 计算工作时长4. 记录物料消耗   ├── 更新 JobCardItem.consumed_qty   └── 如果启用 Backflush,自动生成物料消耗5. 完成工票   ├── 设置 actual_end_date   ├── 更新 completed_qty   ├── 更新 WorkOrderOperation.status   └── 更新运营成本6. 关闭工票   ├── completed_qty >= for_quantity   ├── 更新状态为 "Completed"   └── 更新 Work Order 状态

Backflush(反冲)机制

# Backflush 基于:# - BOM:根据成品入库数量自动计算物料消耗# - Material Transferred for Manufacture:根据转移数量计算消耗# 在 Stock Entry(Manufacture)提交时:# 自动计算并创建物料消耗记录

3.4 物料转移与消耗

物料转移流程

# 从 Work Order 创建 Stock Entry(Material Transfer)# Stock Entry 业务类型:Material Transfer# 源仓库:source_warehouse → 目标仓库:wip_warehouse# 提交时:- 创建 SLE(原材料出库)- 更新 Bin(原材料仓库减少,WIP仓库增加)- 创建 GL Entry(如果两个仓库关联不同科目)- 更新 WorkOrderItem.transferred_qty

物料消耗流程

# 物料消耗有两种方式:# 方式1:手动消耗(通过 Job Card)# 在 Job Card 中记录实际消耗数量# 更新 JobCardItem.consumed_qty# 更新 WorkOrderItem.consumed_qty# 方式2:Backflush(自动消耗)# 根据成品入库数量,按BOM比例自动计算消耗# 在 Stock Entry(Manufacture)提交时触发# 消耗的会计分录:# 借: WIP(在制品科目)# 贷: Stock In Hand(原材料仓库)

四、成本核算

4.1 BOM成本计算

# BOM成本组成total_cost = raw_material_cost + operating_cost + secondary_items_cost# 原材料成本计算raw_material_cost = Σ(item.qty × item.rate)# 运营成本计算(基于工序)operating_cost = Σ(operation.time_in_mins × workstation.hour_rate / 60)# 次要物料成本secondary_items_cost = Σ(secondary_item.qty × secondary_item.rate)

BOMCostingService

class BOMCostingService:    """BOM成本计算服务"""    def calculate_cost(self):        # 计算原材料成本        self.calculate_raw_material_cost()        # 计算运营成本        self.calculate_operating_cost()        # 计算次要物料成本        self.calculate_secondary_items_cost()        # 计算总成本        self.calculate_total_cost()

4.2 Work Order成本追踪

# Work Order 成本字段actual_operating_cost: DF.Currency      # 实际运营成本planned_operating_cost: DF.Currency     # 计划运营成本total_operating_cost: DF.Currency       # 总运营成本additional_operating_cost: DF.Currency  # 额外运营成本corrective_operation_cost: DF.Currency # 矫正运营成本# 成本来源:# 1. Job Card 的工时记录 → 人工成本# 2. 物料消耗记录 → 原材料成本# 3. 额外费用 → 其他成本

4.3 WIP(在制品)管理

# WIP仓库用于管理生产过程中的物料# 物料从原材料仓库转移到WIP仓库# 成品从WIP仓库转移到成品仓库# WIP会计科目# 在 Company DocType 中配置# WIP成本计算# WIP价值 = 转移到WIP的原材料价值 + 运营成本 - 已完成成品价值

五、质量检验

5.1 检验触发

# 检验触发条件:# 1. BOM 设置 inspection_required = True# 2. 物料设置 inspection_required = True# 检验时机:# 1. 物料转移到WIP仓库前# 2. 成品入库前# 检验流程:# 1. 创建 Quality Inspection# 2. 执行检验(合格/不合格)# 3. 如果不合格,创建返工工单或报废

5.2 返工流程

# 不合格品处理:# 方式1:返工# 创建 corrective Job Card# 重新执行工序# 更新成本# 方式2:报废# 创建 Stock Entry(Material Issue)到废品仓库# 会计分录:# 借: Scrap Expense(废品科目)# 贷: Stock In Hand(WIP仓库)

六、与其他模块的集成

6.1 库存模块

# 物料转移:Stock Entry(Material Transfer)# 成品入库:Stock Entry(Manufacture)# 物料消耗:Stock Entry(Material Issue)# 废品处理:Stock Entry(Material Issue)# 集成点:# - Work Order → Stock Entry(一键创建)# - Job Card → Stock Entry(物料消耗)# - SLE 和 Bin 更新# - GL Entry 生成(永续盘存)

6.2 销售模块

# Sales Order → Work Order# 根据销售订单创建生产工单# 集成点:# - 销售订单库存预留# - 成品入库后自动关联销售订单# - 发货单(Delivery Note)关联 Work Order

6.3 采购模块

# Material Request → Purchase Order# 根据物料需求创建采购订单# 集成点:# - Production Plan 生成 Material Request# - Material Request 触发采购流程# - 采购收货后更新库存预留

6.4 会计模块

# 成本结转:# - 原材料消耗 → 借 WIP,贷 Stock In Hand# - 成品入库 → 借 Stock In Hand(成品),贷 WIP# - 运营成本 → 借 WIP,贷相应费用科目# 集成点:# - Stock Entry 创建 GL Entry# - 成本中心关联# - 项目关联

七、代码文件结构

erpnext/manufacturing/├── doctype/│   ├── bom/│   │   ├── bom.py                     # BOM定义和验证│   │   ├── bom.json                   # BOM表结构│   │   ├── mapper.py                  # BOM映射器│   │   └── services/│   │       ├── costing.py             # BOM成本计算│   │       ├── exploded_items.py      # BOM展开│   │       └── operations_cost.py     # 工序成本│   ├── work_order/│   │   ├── work_order.py              # 生产工单定义│   │   ├── work_order.json            # 工单表结构│   │   ├── mapper.py                  # 工单映射器│   │   └── services/│   │       ├── operations.py          # 工序服务│   │       ├── required_items.py      # 所需物料服务│   │       ├── reservation.py         # 库存预留服务│   │       └── status.py              # 状态服务│   ├── job_card/│   │   ├── job_card.py                # 工票定义│   │   ├── job_card.json              # 工票表结构│   │   └── mapper.py                  # 工票映射器│   ├── production_plan/│   │   ├── production_plan.py         # 生产计划定义│   │   ├── production_plan.json       # 计划表结构│   │   ├── mapper.py                  # 计划映射器│   │   └── services/│   │       ├── material_request.py    # 物料请求服务│   │       ├── reservation.py         # 预留服务│   │       ├── sales_order_planning.py # 销售订单计划│   │       ├── sub_assembly.py        # 子装配服务│   │       └── work_order_planning.py # 工单计划│   ├── workstation/│   │   ├── workstation.py             # 工位(机器/工位资源)│   │   └── workstation_dashboard.py│   ├── workstation_type/│   │   └── workstation_type.py        # 工位类型(成本模板)│   ├── workstation_working_hour/│   │   └── workstation_working_hour.py # 工位工作时间(班次)│   ├── workstation_cost/│   │   └── workstation_cost.py        # 工位成本组件│   ├── workstation_operating_component/│   │   └── workstation_operating_component.py  # 工位运营组件│   ├── plant_floor/│   │   └── plant_floor.py             # 车间(看板)│   ├── operation/│   │   └── operation.py               # 工序(工步)│   ├── sub_operation/│   │   └── sub_operation.py           # 子工序(工步细分)│   ├── routing/│   │   └── routing.py                 # 工艺路线(工序有序集合)│   ├── bom_operation/│   │   └── bom_operation.py           # BOM 工序行│   ├── work_order_operation/│   │   └── work_order_operation.py    # 工单工序行│   ├── job_card_operation/│   │   └── job_card_operation.py      # 工票工序执行记录│   ├── job_card_time_log/│   │   └── job_card_time_log.py       # 工票工时记录│   ├── downtime_entry/│   │   └── downtime_entry.py          # 停机记录│   ├── bom_update_tool/│   │   └── bom_update_tool.py         # BOM 批量更新工具│   ├── bom_update_log/│   │   ├── bom_update_log.py          # BOM 更新日志│   │   └── bom_updation_utils.py│   ├── bom_update_batch/│   │   └── bom_update_batch.py        # BOM 更新批次│   ├── bom_creator/│   │   └── bom_creator.py             # BOM 创建器(简化流程)│   ├── sales_forecast/│   │   └── sales_forecast.py          # 销售预测│   ├── master_production_schedule/│   │   └── master_production_schedule.py  # 主生产计划(MPS)│   ├── blanket_order/│   │   └── blanket_order.py           # 框架协议│   └── ...├── controllers/│   └── manufacturing_controller.py    # 生产控制器基类└── utils.py                           # 工具函数

八、核心设计原则

8.1 BOM驱动生产

产品结构由BOM定义生产工单基于BOM创建物料需求从BOM展开计算成本从BOM核算

8.2 多层BOM支持

支持子装配(SubAssembly)支持虚拟BOM(Phantom BOM)支持BOMTree层次遍历

8.3 工序精细化管理

每个工序有独立的Job Card支持工时记录和成本追踪支持工作中心分配

8.4 库存预留机制

生产工单可以预留原材料确保生产所需物料不被其他订单占用预留数量在物料消耗后释放

8.5 Backflush(反冲)

根据成品入库自动计算物料消耗减少手动录入工作量支持基于BOM或转移数量的反冲

8.6 实时成本追踪

实际成本 vs 计划成本支持成本差异分析支持项目级成本归集

九、配置要点

9.1 Manufacturing Settings

class ManufacturingSettings:    # 工单设置    allow_editing_of_items_and_quantities_in_work_order: DF.Check    material_consumption: DF.Select    backflush_raw_materials_based_on: DF.Select    overproduction_percentage_for_work_order: DF.Percent    transfer_extra_materials_percentage: DF.Percent    # 工票设置    track_operation_completion_in_job_card: DF.Check    track_time_in_job_card: DF.Check    # 成本设置    add_operating_cost_in_valuation: DF.Check

9.2 仓库配置

# 需要配置的仓库类型:# 1. FG Warehouse(成品仓库)# 2. Source Warehouse(原材料仓库)# 3. WIP Warehouse(在制品仓库)# 4. Scrap Warehouse(废品仓库)# 每个仓库关联对应的会计科目

9.3 工位配置

详见 2.5 工位体系。

class Workstation:    workstation_name: DF.Data          # 工位名称    workstation_type: DF.Link          # 工位类型(成本模板来源)    plant_floor: DF.Link               # 所属车间    warehouse: DF.Link                 # 关联WIP仓库    hour_rate: DF.Currency             # 小时费率(成本组件汇总)    production_capacity: DF.Int        # 产能    status: DF.Literal                 # 状态(Production/Off/Idle/Problem/Maintenance/Setup)    working_hours: DF.Table            # 工作时间(班次)    workstation_costs: DF.Table        # 成本组件    holiday_list: DF.Link              # 节假日表

十、报表体系

10.1 主要报表

报表
数据来源
说明
Work Order Summary
Work Order
工单汇总表
Production Plan Report
Production Plan
生产计划报表
Job Card Summary
Job Card
工票汇总表
BOM Analysis
BOM
BOM成本分析
WIP Summary
Work Order + Bin
在制品汇总表
Production Cost Report
Work Order + Stock Entry
生产成本报表
Operation Wise Production
Job Card
工序产量报表

十一、总结

生产模块的核心逻辑

  1. BOM管理:定义产品结构(投入物)和产出物(联产品/副产品/废品),支持多层展开和成本分摊
  2. 生产计划:三层计划体系——销售预测 → 主生产计划(MPS,计算累积提前期)→ 生产计划(展开 BOM 生成工单和采购请求)
  3. 生产工单:驱动生产流程,管理物料转移、工序执行和成品入库
  4. 工票管理:记录工序执行细节,追踪工时、物料消耗和产出物(含联副产品)
  5. 工位与工步:工位(Workstation)管理设备资源和成本,工步(Operation/Sub Operation)定义工序,Routing 组织工艺路线
  6. 车间管理:Plant Floor 看板实时展示工位状态,Downtime Entry 记录停机事件用于 OEE 分析
  7. 成本核算:实时追踪生产成本,支持差异分析和联副产品成本分摊
  8. 库存集成:与库存模块无缝集成,自动生成库存变动和会计分录
  9. BOM 运维:BOM Update Tool 支持批量替换 BOM 和刷新成本

核心文件

  • work_order.py — 生产工单
  • bom.py — 物料清单
  • job_card.py — 工票
  • production_plan.py — 生产计划
  • workstation.py — 工位
  • operation.py — 工序
  • routing.py — 工艺路线
  • master_production_schedule.py — 主生产计划
  • downtime_entry.py — 停机记录

业务流程

Sales Forecast → Master Production Schedule ─┐                                              ↓Sales Order / Material Request → Production Plan → Work Order → Job Card → Stock Entry(成品入库)                                                       ↓                                                 Material Request → Purchase Order → Purchase Receipt