一、概述
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 | ||
item | ||
item_name | ||
quantity | ||
is_active | ||
is_default | ||
is_phantom_bom | ||
has_variants | ||
inspection_required | ||
operations | ||
items | ||
secondary_items |
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 | ||
production_item | ||
item_name | ||
qty | ||
bom_no | ||
company |
状态字段
status: DF.Literal[ "", "Draft", # 草稿 "Submitted", # 已提交 "Not Started", # 未开始 "In Process", # 进行中 "Stock Reserved", # 库存已预留 "Stock Partially Reserved", # 库存部分预留 "Completed", # 已完成 "Stopped", # 已停止 "Closed", # 已关闭 "Cancelled", # 已取消]仓库字段
fg_warehouse | ||
source_warehouse | ||
wip_warehouse | ||
scrap_warehouse |
数量字段
produced_qty | ||
material_transferred_for_manufacturing | ||
process_loss_qty | ||
disassembled_qty |
关联字段
sales_order | ||
sales_order_item | ||
material_request | ||
production_plan | ||
project |
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.Check2.3 Job Card(工票)
文件:job_card.py
表结构(关键字段)
name | ||
work_order | ||
production_item | ||
bom_no | ||
company |
工序字段
operation | ||
workstation | ||
hour_rate |
数量字段
for_quantity | ||
completed_qty | ||
pending_qty |
时间字段
expected_start_date | ||
expected_end_date | ||
actual_start_date | ||
actual_end_date |
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 | ||
company | ||
get_items_from |
计划选项
combine_items | ||
combine_sub_items | ||
consider_minimum_order_qty | ||
include_non_stock_items | ||
include_safety_stock | ||
include_subcontracted_items |
2.5 工位(Workstation)体系
ERPNext 用一套 DocType 管理"工位"(机器/工位资源),支持工位类型、工作时间、成本组件、所属车间等。
核心表
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(工艺路线)有序组合。
核心表
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) / 60sequence_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 实际成本工位与工步的关键设计要点
工位与工位类型分离: Workstation Type定义成本模板,Workstation是具体资源,便于批量管理同类设备工序可嵌套: Operation通过 Sub Operation支持工步细分工艺路线可复用: Routing独立于 BOM,多个 BOM 可共用同一条工艺路线工位类型可替代具体工位:BOM 工序行可只指定 workstation_type,排产时再分配具体工位工序级别仓库控制:每道工序可独立配置 source_warehouse/wip_warehouse/fg_warehouse,支持多工序分阶段入库工序级别委外:单道工序可标记 is_subcontracted,支持工序级委外加工返工工序: is_corrective_operation标记返工工序,用于不合格品返工流程工位状态实时发布:通过 publish_workstation_status推送到 Plant Floor 看板,支持车间可视化批量化工票: create_job_card_based_on_batch_size支持按批量自动拆分工票工时校验:工位工作时间与节假日表联动,校验工序是否可排产
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核心逻辑:
需求汇总(master_production_schedule.py#L164):合并销售订单、物料请求、销售预测数据,按
(item_code, delivery_date)聚合数量。累积提前期计算(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下达日期计算:
order_release_date = delivery_date - cumulative_lead_time(master_production_schedule.py#L297)。MRP 生成:提交后异步生成 MRP 日志(master_production_schedule.py#L438)。
MPS 与 Production Plan 的区别
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提供两种批量操作(通过异步队列执行):
替换 BOM(
enqueue_replace_bom):将所有引用 current_bom的地方替换为 new_bom,用于 BOM 版本切换。更新成本(
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 = 203.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 Order6.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.Check9.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 主要报表
十一、总结
生产模块的核心逻辑:
BOM管理:定义产品结构(投入物)和产出物(联产品/副产品/废品),支持多层展开和成本分摊 生产计划:三层计划体系——销售预测 → 主生产计划(MPS,计算累积提前期)→ 生产计划(展开 BOM 生成工单和采购请求) 生产工单:驱动生产流程,管理物料转移、工序执行和成品入库 工票管理:记录工序执行细节,追踪工时、物料消耗和产出物(含联副产品) 工位与工步:工位(Workstation)管理设备资源和成本,工步(Operation/Sub Operation)定义工序,Routing 组织工艺路线 车间管理:Plant Floor 看板实时展示工位状态,Downtime Entry 记录停机事件用于 OEE 分析 成本核算:实时追踪生产成本,支持差异分析和联副产品成本分摊 库存集成:与库存模块无缝集成,自动生成库存变动和会计分录 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
夜雨聆风