Advanced Production & BOM Management
Overview
- What you’ll learn:
- How Production Plans (MProductionPlan) enable multi-product production runs under a single MProduction header
- How lot and serial tracking works through MProductionLineMA and Attribute Set Instances during production
- How BOM Verification (BOMVerify) validates structure integrity, detects circular references, and ensures master BOMs exist
- How phantom BOM expansion recursively replaces non-stocked assemblies with their component parts during line creation
- How ProductionCreate and ProductionProcess drive the production document lifecycle
- Prerequisites: Lessons 24–25 — Production Overview and Inventory & Warehouse Management
- Estimated reading time: 22 minutes
Introduction
In earlier lessons you learned the fundamentals of iDempiere production — creating a simple production document, defining Bills of Materials, and completing a basic production run. Those basics cover straightforward scenarios where one finished product consumes a fixed set of components in a single operation.
Real-world manufacturing is rarely that simple. A single production run may yield multiple finished products simultaneously. Component materials must be tracked by lot or serial number for quality traceability. BOMs grow complex with phantom sub-assemblies that exist only as logical groupings and never occupy warehouse shelves. Before any production runs, the BOM structure itself needs validation to catch circular references or missing components.
This lesson covers the advanced production and BOM management capabilities in iDempiere’s core production module.
Production Plans: Multi-Product Production Runs
A standard MProduction document produces a single finished product. But many manufacturing environments produce multiple items in a single run — for example, a cutting operation that yields several different parts from the same raw material.
iDempiere handles this through Production Plans. When IsUseProductionPlan is set to true on the production header, the system shifts from a single-product model to a multi-product model using MProductionPlan records.
The MProductionPlan Structure
| Field | Description |
|---|---|
| M_Production_ID | Reference to the parent production header |
| M_Product_ID | The finished product this plan will produce |
| ProductionQty | Quantity to produce for this specific product |
| M_Locator_ID | Warehouse locator for finished product receipt |
| Line | Sequence number within the production document |
// Multi-product production with production plans
MProduction production = new MProduction(ctx, 0, trxName);
production.setAD_Org_ID(orgId);
production.setM_Product_ID(finishedProductId);
production.setMovementDate(new Timestamp(System.currentTimeMillis()));
production.setIsUseProductionPlan(true);
production.saveEx();
// Plan 1: Product A — 50 units
MProductionPlan plan1 = new MProductionPlan(production);
plan1.setM_Product_ID(productA_ID);
plan1.setProductionQty(new BigDecimal("50"));
plan1.setM_Locator_ID(locatorId);
plan1.saveEx();
// Plan 2: Product B — 30 units
MProductionPlan plan2 = new MProductionPlan(production);
plan2.setM_Product_ID(productB_ID);
plan2.setProductionQty(new BigDecimal("30"));
plan2.setM_Locator_ID(locatorId);
plan2.saveEx();
// Generate lines for each plan independently
plan1.createLines(true); // true = mustBeStocked
plan2.createLines(true);
// Each plan manages its own production lines
MProductionLine[] plan1Lines = plan1.getLines();
MProductionLine[] plan2Lines = plan2.getLines();
System.out.println("Plan 1: " + plan1Lines.length + " lines");
System.out.println("Plan 2: " + plan2Lines.length + " lines");
Lot and Serial Tracking with MProductionLineMA
Traceability is critical in regulated industries. iDempiere’s production module achieves lot-level tracking through Material Allocation records stored in MProductionLineMA.
What Is Material Allocation?
A MProductionLineMA record links a production line to a specific Attribute Set Instance (ASI). When a production line consumes 100 units of a component, those units may come from multiple lots — 60 from Lot A and 40 from Lot B. Each allocation is recorded separately.
| Field | Description |
|---|---|
| M_ProductionLine_ID | The production line this allocation belongs to |
| M_AttributeSetInstance_ID | The specific lot/serial/attribute combination |
| MovementQty | Quantity allocated from this specific ASI |
| DateMaterialPolicy | Date used for FIFO/LIFO cost layer selection |
// During production completion, MA records drive inventory transactions
MProductionLine line = ...; // a component consumption line
MProduct product = line.getM_Product();
if (product.getM_AttributeSet_ID() != 0) {
// Product requires attribute tracking
MProductionLineMA[] mas = MProductionLineMA.get(ctx,
line.getM_ProductionLine_ID(), trxName);
for (MProductionLineMA ma : mas) {
BigDecimal qty = ma.getMovementQty();
int asiId = ma.getM_AttributeSetInstance_ID();
Timestamp datePolicy = ma.getDateMaterialPolicy();
// Each MA record generates a separate inventory transaction
// with its own ASI for lot-level tracking
System.out.println("Consuming " + qty + " units from ASI=" + asiId
+ " with policy date " + datePolicy);
}
}
FIFO/LIFO and DateMaterialPolicy
The DateMaterialPolicy field is essential for FIFO or LIFO costing. When components are consumed, the system selects cost layers based on this date. For FIFO, the oldest records are consumed first. For LIFO, the newest are consumed first.
Auto-Generating Lot Numbers
When finished products require lot tracking, the production completion process auto-generates new lot numbers for the output. A new M_AttributeSetInstance is created and populated based on the attribute set’s lot control settings.
BOM Verification with BOMVerify
Before running production, you need confidence that your BOM is structurally sound. The BOMVerify process catches problems before they disrupt production.
What BOMVerify Checks
- Master BOM existence: Verifies the product has a BOM flagged as master (default BOM requires
BOMUse = 'A'ANDBOMType = 'A'— this is whatMPPProductBOM.getDefault()looks for) - Circular reference detection: Walks the BOM tree recursively to ensure no product appears as both ancestor and descendant
- Component validity: Checks that every BOM line references a valid, active product
- Stocking validation: Confirms non-phantom components are stocked items
- IsVerified flag: Updates the product’s
IsVerifiedflag upon success
BOMVerify Parameters
| Parameter | Description |
|---|---|
| M_Product_ID | Verify a single product’s BOM |
| M_Product_Category_ID | Batch verify all products in a category |
| IsReValidate | Re-verify previously verified products |
// Running BOM verification programmatically
MPInstance instance = new MPInstance(ctx, bomVerifyProcessId, 0);
instance.saveEx();
// Single product verification
MPInstancePara para = new MPInstancePara(instance, 10);
para.setParameter("M_Product_ID", productId);
para.saveEx();
// Re-validate previously verified products
MPInstancePara paraRevalidate = new MPInstancePara(instance, 20);
paraRevalidate.setParameter("IsReValidate", "Y");
paraRevalidate.saveEx();
ProcessInfo pi = new ProcessInfo("BOM Verify", bomVerifyProcessId);
pi.setAD_PInstance_ID(instance.getAD_PInstance_ID());
BOMVerify process = new BOMVerify();
process.startProcess(ctx, pi, null);
if (pi.isError()) {
System.err.println("BOM verification failed: " + pi.getSummary());
} else {
System.out.println("BOM verified: " + pi.getSummary());
}
Phantom BOM Expansion in Detail
A phantom assembly exists in the BOM for organizational purposes but is never physically stocked. During createLines(), phantom components are recursively expanded into their leaf components.
The ComponentType field on MPPProductBOMLine controls how each line behaves during production. The full set of values:
- Component — Standard part consumed during production
- Phantom — Recursively expanded; never stocked separately
- By-Product — Secondary output received into inventory (e.g., scrap metal from cutting)
- Co-Product — Joint output of equal importance (e.g., chemical process yielding multiple products)
- Packing — Packaging material consumed
- Tools — Tooling required but not consumed
- Variant — Alternative component for product variants
- Option — Optional component (configurable BOMs)
- Planning — Used for MRP planning only, not production
How Phantoms Work
A product is treated as a phantom when:
- The BOM line has
ComponentType = 'PH'(checked viaMPPProductBOMLine.COMPONENTTYPE_Phantom.equals(bomLine.getComponentType())) - The component product itself is marked
isPhantom() = trueon theM_Productrecord - The component product has its own BOM (
isBOM() = true) - The product is typically configured with
isStocked() = false
Phantom vs. Regular Sub-Assembly
| Characteristic | Phantom Assembly | Regular Sub-Assembly |
|---|---|---|
| Stocked in inventory | No | Yes |
| Has its own BOM | Yes | Yes |
| Production line created | No — expanded into components | Yes — consumed as single item |
| Requires separate production | No | Yes — must be produced first |
// Conceptual illustration of phantom expansion during createLines()
// This logic is internal to MProduction/MProductionPlan
private void expandBOM(MPPProductBOM bom, BigDecimal parentQty,
int locatorId) {
MPPProductBOMLine[] bomLines = bom.getLines();
for (MPPProductBOMLine bomLine : bomLines) {
MProduct component = MProduct.get(ctx, bomLine.getM_Product_ID());
BigDecimal componentQty = bomLine.getQtyBOM().multiply(parentQty);
if (MPPProductBOMLine.COMPONENTTYPE_Phantom.equals(bomLine.getComponentType())
&& component.isBOM()) {
// PHANTOM: Recursively expand — no line created for phantom itself
MPPProductBOM subBOM = MPPProductBOM.getDefault(component, trxName);
expandBOM(subBOM, componentQty, locatorId);
} else {
// REGULAR: Create consumption line
MProductionLine line = new MProductionLine(plan);
line.setM_Product_ID(component.getM_Product_ID());
line.setMovementQty(componentQty.negate()); // negative = consume
line.setM_Locator_ID(locatorId);
line.saveEx();
}
}
}
ProductionCreate and ProductionProcess
Two key processes drive the production lifecycle:
ProductionCreate — Generate Lines from BOM
| Parameter | Description |
|---|---|
| PP_Product_BOM_ID | Specific BOM to use (optional — defaults to master BOM) |
| ProductionQty | Override production quantity |
| Recreate | Delete existing lines before creating new ones |
ProductionProcess — Execute Document Workflow
ProductionProcess uses MWorkflow.runDocumentActionWorkflow() to ensure workflow conditions, approvals, and transitions are properly executed. When completeIt() runs:
- Iterates through all production lines (or plans)
- Creates
MTransactionrecords for inventory movements - Processes
MProductionLineMArecords for lot tracking - Updates
MStorageOnHandquantities - Posts accounting entries
Note: M_Production_Run is @Deprecated(since="13", forRemoval=true). Always use ProductionProcess instead.
// Complete production workflow
MProduction production = new MProduction(ctx, productionId, trxName);
production.setDocAction(DocAction.ACTION_Complete);
production.saveEx();
// Process via document action
if (!production.processIt(DocAction.ACTION_Complete)) {
throw new AdempiereException("Production failed: "
+ production.getProcessMsg());
}
production.saveEx();
System.out.println("Production completed: " + production.getDocumentNo());
Key Takeaways
- Production Plans (
MProductionPlan) enable multi-product production whenIsUseProductionPlan=true. Each plan manages its own product, BOM, and lines independently. - Material Allocation (
MProductionLineMA) provides lot-level traceability. TheDateMaterialPolicyfield ensures correct FIFO/LIFO cost layer selection. - BOM Verification (
BOMVerify) is critical pre-production validation — checks for circular references, missing components, and master BOM existence. - Phantom Expansion recursively replaces non-stocked assemblies with leaf components during
createLines(). Phantom products are never consumed or produced directly. - ProductionCreate generates lines from BOM; ProductionProcess executes the DocAction workflow.
M_Production_Runis deprecated.
What’s Next
The next lesson — Production Costing & Analysis — covers how iDempiere tracks and calculates production costs, including costing methods, the MCost and MCostDetail APIs, and multi-level BOM cost rollup using IndentedBOM.
繁體中文
概述
- 您將學到:
- 生產計劃(MProductionPlan)如何在單一 MProduction 表頭下實現多產品生產運行
- 在生產過程中,批號與序號追蹤如何透過 MProductionLineMA 與屬性集實例運作
- BOM 驗證(BOMVerify)如何驗證結構完整性、偵測循環參照,並確保主要 BOM 存在
- 虛設組件 BOM 展開如何在建立生產明細時,遞迴地將非庫存組合件替換為其組成零件
- ProductionCreate 和 ProductionProcess 如何驅動生產單據生命週期
- 先決條件:第 24–25 課 — 生產概述與庫存及倉庫管理
- 預估閱讀時間:22 分鐘
介紹
在之前的課程中,您學習了 iDempiere 生產的基礎知識 — 建立簡單的生產單據、定義物料清單(BOM),以及完成基本的生產運行。這些基礎涵蓋了簡單的情境,即一個成品在單一操作中消耗一組固定的組件。
現實世界的製造很少如此簡單。單次生產運行可能同時產出多個成品。組件物料必須按批號或序號進行追蹤,以確保品質可追溯性。BOM 會因虛設子組合件而變得複雜,這些子組合件僅作為邏輯分組存在,從不佔用倉庫貨架。在任何生產運行之前,BOM 結構本身需要驗證,以捕捉循環參照或缺少的組件。
本課涵蓋 iDempiere 核心生產模組中的進階生產與 BOM 管理功能。
生產計劃:多產品生產運行
標準的 MProduction 單據生產單一成品。但許多製造環境會在單次運行中生產多個品項 — 例如,一次裁切操作從相同原材料中產出數個不同的零件。
iDempiere 透過生產計劃來處理此情境。當生產表頭上的 IsUseProductionPlan 設為 true 時,系統從單一產品模式切換為使用 MProductionPlan 記錄的多產品模式。
MProductionPlan 結構
| 欄位 | 說明 |
|---|---|
| M_Production_ID | 參照至父級生產表頭 |
| M_Product_ID | 此計劃將生產的成品 |
| ProductionQty | 此特定產品的生產數量 |
| M_Locator_ID | 成品入庫的倉庫儲位 |
| Line | 在生產單據中的序號 |
// Multi-product production with production plans
MProduction production = new MProduction(ctx, 0, trxName);
production.setAD_Org_ID(orgId);
production.setM_Product_ID(finishedProductId);
production.setMovementDate(new Timestamp(System.currentTimeMillis()));
production.setIsUseProductionPlan(true);
production.saveEx();
// Plan 1: Product A — 50 units
MProductionPlan plan1 = new MProductionPlan(production);
plan1.setM_Product_ID(productA_ID);
plan1.setProductionQty(new BigDecimal("50"));
plan1.setM_Locator_ID(locatorId);
plan1.saveEx();
// Plan 2: Product B — 30 units
MProductionPlan plan2 = new MProductionPlan(production);
plan2.setM_Product_ID(productB_ID);
plan2.setProductionQty(new BigDecimal("30"));
plan2.setM_Locator_ID(locatorId);
plan2.saveEx();
// Generate lines for each plan independently
plan1.createLines(true); // true = mustBeStocked
plan2.createLines(true);
// Each plan manages its own production lines
MProductionLine[] plan1Lines = plan1.getLines();
MProductionLine[] plan2Lines = plan2.getLines();
System.out.println("Plan 1: " + plan1Lines.length + " lines");
System.out.println("Plan 2: " + plan2Lines.length + " lines");
使用 MProductionLineMA 進行批號與序號追蹤
在受監管的行業中,可追溯性至關重要。iDempiere 的生產模組透過儲存在 MProductionLineMA 中的物料分配記錄來實現批號層級的追蹤。
什麼是物料分配?
MProductionLineMA 記錄將生產明細連結到特定的屬性集實例(ASI)。當一條生產明細消耗 100 單位的組件時,這些單位可能來自多個批號 — 60 個來自批號 A,40 個來自批號 B。每次分配都會分別記錄。
| 欄位 | 說明 |
|---|---|
| M_ProductionLine_ID | 此分配所屬的生產明細 |
| M_AttributeSetInstance_ID | 特定的批號/序號/屬性組合 |
| MovementQty | 從此特定 ASI 分配的數量 |
| DateMaterialPolicy | 用於 FIFO/LIFO 成本層選擇的日期 |
// During production completion, MA records drive inventory transactions
MProductionLine line = ...; // a component consumption line
MProduct product = line.getM_Product();
if (product.getM_AttributeSet_ID() != 0) {
// Product requires attribute tracking
MProductionLineMA[] mas = MProductionLineMA.get(ctx,
line.getM_ProductionLine_ID(), trxName);
for (MProductionLineMA ma : mas) {
BigDecimal qty = ma.getMovementQty();
int asiId = ma.getM_AttributeSetInstance_ID();
Timestamp datePolicy = ma.getDateMaterialPolicy();
// Each MA record generates a separate inventory transaction
// with its own ASI for lot-level tracking
System.out.println("Consuming " + qty + " units from ASI=" + asiId
+ " with policy date " + datePolicy);
}
}
FIFO/LIFO 與 DateMaterialPolicy
DateMaterialPolicy 欄位對於 FIFO 或 LIFO 成本計算至關重要。當組件被消耗時,系統根據此日期選擇成本層。對於 FIFO,最早的記錄優先被消耗。對於 LIFO,最新的記錄優先被消耗。
自動產生批號
當成品需要批號追蹤時,生產完成流程會自動為產出品產生新的批號。系統會建立新的 M_AttributeSetInstance,並根據屬性集的批號控制設定進行填入。
使用 BOMVerify 進行 BOM 驗證
在執行生產之前,您需要確信您的 BOM 結構是健全的。BOMVerify 流程在問題影響生產之前將其捕捉。
BOMVerify 檢查項目
- 主要 BOM 存在性:驗證產品是否有 BOM 同時滿足
BOMUse = 'A'且BOMType = 'A'(即MPPProductBOM.getDefault()的判定邏輯) - 循環參照偵測:遞迴走訪 BOM 樹狀結構,確保沒有產品同時出現在祖先和後代中
- 組件有效性:檢查每條 BOM 明細是否參照有效且啟用的產品
- 庫存驗證:確認非虛設組件為庫存品項
- IsVerified 旗標:驗證成功後更新產品的
IsVerified旗標
BOMVerify 參數
| 參數 | 說明 |
|---|---|
| M_Product_ID | 驗證單一產品的 BOM |
| M_Product_Category_ID | 批次驗證類別中的所有產品 |
| IsReValidate | 重新驗證已驗證過的產品 |
// Running BOM verification programmatically
MPInstance instance = new MPInstance(ctx, bomVerifyProcessId, 0);
instance.saveEx();
// Single product verification
MPInstancePara para = new MPInstancePara(instance, 10);
para.setParameter("M_Product_ID", productId);
para.saveEx();
// Re-validate previously verified products
MPInstancePara paraRevalidate = new MPInstancePara(instance, 20);
paraRevalidate.setParameter("IsReValidate", "Y");
paraRevalidate.saveEx();
ProcessInfo pi = new ProcessInfo("BOM Verify", bomVerifyProcessId);
pi.setAD_PInstance_ID(instance.getAD_PInstance_ID());
BOMVerify process = new BOMVerify();
process.startProcess(ctx, pi, null);
if (pi.isError()) {
System.err.println("BOM verification failed: " + pi.getSummary());
} else {
System.out.println("BOM verified: " + pi.getSummary());
}
虛設組件 BOM 展開詳解
虛設組合件在 BOM 中基於組織目的而存在,但從不實際入庫。在 createLines() 期間,虛設組件會被遞迴展開為其末端組件。
MPPProductBOMLine 的 ComponentType 欄位控制每個明細行在生產中的行為。完整值列表:
- Component — 生產中消耗的標準元件
- Phantom — 遞迴展開;不單獨入庫
- By-Product — 生產過程的次要產出(如切削廢料)
- Co-Product — 同等重要的聯合產出(如化學製程產生的多種產品)
- Packing — 消耗的包裝材料
- Tools — 需要但不消耗的工具
- Variant — 產品變體的替代元件
- Option — 選配元件(可配置 BOM)
- Planning — 僅用於 MRP 計劃,不用於生產
虛設組件的運作方式
當符合以下條件時,產品被視為虛設組件:
- BOM 明細的
ComponentType = 'PH'(透過MPPProductBOMLine.COMPONENTTYPE_Phantom.equals(bomLine.getComponentType())判斷) - 元件產品本身在
M_Product記錄上標記為isPhantom() = true - 組件產品擁有自己的 BOM(
isBOM() = true) - 產品通常設定為
isStocked() = false
虛設組件與一般子組合件的比較
| 特性 | 虛設組合件 | 一般子組合件 |
|---|---|---|
| 在庫存中存放 | 否 | 是 |
| 擁有自己的 BOM | 是 | 是 |
| 建立生產明細 | 否 — 展開為組件 | 是 — 作為單一品項消耗 |
| 需要單獨生產 | 否 | 是 — 必須先行生產 |
// Conceptual illustration of phantom expansion during createLines()
// This logic is internal to MProduction/MProductionPlan
private void expandBOM(MPPProductBOM bom, BigDecimal parentQty,
int locatorId) {
MPPProductBOMLine[] bomLines = bom.getLines();
for (MPPProductBOMLine bomLine : bomLines) {
MProduct component = MProduct.get(ctx, bomLine.getM_Product_ID());
BigDecimal componentQty = bomLine.getQtyBOM().multiply(parentQty);
if (MPPProductBOMLine.COMPONENTTYPE_Phantom.equals(bomLine.getComponentType())
&& component.isBOM()) {
// PHANTOM: Recursively expand — no line created for phantom itself
MPPProductBOM subBOM = MPPProductBOM.getDefault(component, trxName);
expandBOM(subBOM, componentQty, locatorId);
} else {
// REGULAR: Create consumption line
MProductionLine line = new MProductionLine(plan);
line.setM_Product_ID(component.getM_Product_ID());
line.setMovementQty(componentQty.negate()); // negative = consume
line.setM_Locator_ID(locatorId);
line.saveEx();
}
}
}
ProductionCreate 與 ProductionProcess
兩個關鍵流程驅動生產生命週期:
ProductionCreate — 從 BOM 產生明細
| 參數 | 說明 |
|---|---|
| PP_Product_BOM_ID | 指定使用的 BOM(選填 — 預設為主要 BOM) |
| ProductionQty | 覆寫生產數量 |
| Recreate | 在建立新明細之前刪除現有明細 |
ProductionProcess — 執行單據工作流
ProductionProcess 使用 MWorkflow.runDocumentActionWorkflow() 來確保工作流條件、核准和狀態轉換被正確執行。當 completeIt() 執行時:
- 遍歷所有生產明細(或計劃)
- 建立
MTransaction記錄以進行庫存異動 - 處理
MProductionLineMA記錄以進行批號追蹤 - 更新
MStorageOnHand數量 - 過帳會計分錄
注意:M_Production_Run 已被標記為 @Deprecated(since="13", forRemoval=true)。請一律使用 ProductionProcess 代替。
// Complete production workflow
MProduction production = new MProduction(ctx, productionId, trxName);
production.setDocAction(DocAction.ACTION_Complete);
production.saveEx();
// Process via document action
if (!production.processIt(DocAction.ACTION_Complete)) {
throw new AdempiereException("Production failed: "
+ production.getProcessMsg());
}
production.saveEx();
System.out.println("Production completed: " + production.getDocumentNo());
重點摘要
- 生產計劃(
MProductionPlan)在IsUseProductionPlan=true時啟用多產品生產。每個計劃獨立管理自己的產品、BOM 和明細。 - 物料分配(
MProductionLineMA)提供批號層級的可追溯性。DateMaterialPolicy欄位確保正確的 FIFO/LIFO 成本層選擇。 - BOM 驗證(
BOMVerify)是關鍵的生產前驗證 — 檢查循環參照、缺少的組件以及主要 BOM 的存在性。 - 虛設組件展開在
createLines()期間遞迴地將非庫存組合件替換為末端組件。虛設產品從不直接被消耗或生產。 - ProductionCreate 從 BOM 產生明細;ProductionProcess 執行 DocAction 工作流。
M_Production_Run已棄用。
下一步
下一課 — 生產成本計算與分析 — 涵蓋 iDempiere 如何追蹤和計算生產成本,包括成本計算方法、MCost 和 MCostDetail API,以及使用 IndentedBOM 進行多階 BOM 成本彙總。
日本語
概要
- 学習内容:
- 生産計画(MProductionPlan)が単一の MProduction ヘッダーの下で複数製品の生産実行をどのように実現するか
- 生産中に MProductionLineMA と属性セットインスタンスを通じてロットおよびシリアル追跡がどのように機能するか
- BOM検証(BOMVerify)が構造の整合性を検証し、循環参照を検出し、マスターBOMの存在を確認する方法
- ファントムBOM展開が、明細作成時に非在庫アセンブリをその構成部品に再帰的に置き換える方法
- ProductionCreate と ProductionProcess が生産伝票のライフサイクルをどのように駆動するか
- 前提条件:レッスン24–25 — 生産概要および在庫・倉庫管理
- 推定読了時間:22分
はじめに
以前のレッスンでは、iDempiere の生産の基礎を学びました — シンプルな生産伝票の作成、部品表(BOM)の定義、基本的な生産実行の完了です。これらの基礎は、1つの完成品が単一の操作で固定のコンポーネントセットを消費する単純なシナリオをカバーしています。
実際の製造はそれほど単純ではありません。単一の生産実行で複数の完成品を同時に生産する場合があります。コンポーネント材料は、品質のトレーサビリティのためにロット番号またはシリアル番号で追跡する必要があります。BOMは、論理的なグループとしてのみ存在し、倉庫の棚を占有しないファントムサブアセンブリにより複雑になります。生産実行の前に、BOM構造自体を検証して、循環参照や欠落コンポーネントを検出する必要があります。
このレッスンでは、iDempiere のコア生産モジュールにおける高度な生産およびBOM管理機能を扱います。
生産計画:複数製品の生産実行
標準的な MProduction 伝票は単一の完成品を生産します。しかし、多くの製造環境では単一の実行で複数のアイテムを生産します — 例えば、同じ原材料から複数の異なる部品を生み出す切断作業などです。
iDempiere はこれを生産計画で処理します。生産ヘッダーで IsUseProductionPlan が true に設定されると、システムは単一製品モデルから MProductionPlan レコードを使用した複数製品モデルに切り替わります。
MProductionPlan の構造
| フィールド | 説明 |
|---|---|
| M_Production_ID | 親の生産ヘッダーへの参照 |
| M_Product_ID | この計画で生産する完成品 |
| ProductionQty | この特定の製品の生産数量 |
| M_Locator_ID | 完成品受入のための倉庫ロケーター |
| Line | 生産伝票内の順序番号 |
// Multi-product production with production plans
MProduction production = new MProduction(ctx, 0, trxName);
production.setAD_Org_ID(orgId);
production.setM_Product_ID(finishedProductId);
production.setMovementDate(new Timestamp(System.currentTimeMillis()));
production.setIsUseProductionPlan(true);
production.saveEx();
// Plan 1: Product A — 50 units
MProductionPlan plan1 = new MProductionPlan(production);
plan1.setM_Product_ID(productA_ID);
plan1.setProductionQty(new BigDecimal("50"));
plan1.setM_Locator_ID(locatorId);
plan1.saveEx();
// Plan 2: Product B — 30 units
MProductionPlan plan2 = new MProductionPlan(production);
plan2.setM_Product_ID(productB_ID);
plan2.setProductionQty(new BigDecimal("30"));
plan2.setM_Locator_ID(locatorId);
plan2.saveEx();
// Generate lines for each plan independently
plan1.createLines(true); // true = mustBeStocked
plan2.createLines(true);
// Each plan manages its own production lines
MProductionLine[] plan1Lines = plan1.getLines();
MProductionLine[] plan2Lines = plan2.getLines();
System.out.println("Plan 1: " + plan1Lines.length + " lines");
System.out.println("Plan 2: " + plan2Lines.length + " lines");
MProductionLineMA によるロットおよびシリアル追跡
規制産業においてトレーサビリティは不可欠です。iDempiere の生産モジュールは、MProductionLineMA に格納される材料割当レコードを通じて、ロットレベルの追跡を実現します。
材料割当とは?
MProductionLineMA レコードは、生産明細を特定の属性セットインスタンス(ASI)にリンクします。生産明細がコンポーネントを100単位消費する場合、それらの単位は複数のロットから供給される可能性があります — ロットAから60、ロットBから40など。各割当は個別に記録されます。
| フィールド | 説明 |
|---|---|
| M_ProductionLine_ID | この割当が属する生産明細 |
| M_AttributeSetInstance_ID | 特定のロット/シリアル/属性の組み合わせ |
| MovementQty | この特定のASIから割り当てられた数量 |
| DateMaterialPolicy | FIFO/LIFOコスト層の選択に使用される日付 |
// During production completion, MA records drive inventory transactions
MProductionLine line = ...; // a component consumption line
MProduct product = line.getM_Product();
if (product.getM_AttributeSet_ID() != 0) {
// Product requires attribute tracking
MProductionLineMA[] mas = MProductionLineMA.get(ctx,
line.getM_ProductionLine_ID(), trxName);
for (MProductionLineMA ma : mas) {
BigDecimal qty = ma.getMovementQty();
int asiId = ma.getM_AttributeSetInstance_ID();
Timestamp datePolicy = ma.getDateMaterialPolicy();
// Each MA record generates a separate inventory transaction
// with its own ASI for lot-level tracking
System.out.println("Consuming " + qty + " units from ASI=" + asiId
+ " with policy date " + datePolicy);
}
}
FIFO/LIFO と DateMaterialPolicy
DateMaterialPolicy フィールドは、FIFOまたはLIFO原価計算に不可欠です。コンポーネントが消費される際、システムはこの日付に基づいてコスト層を選択します。FIFOでは最も古いレコードが最初に消費されます。LIFOでは最も新しいレコードが最初に消費されます。
ロット番号の自動生成
完成品にロット追跡が必要な場合、生産完了プロセスは出力に対して新しいロット番号を自動生成します。新しい M_AttributeSetInstance が作成され、属性セットのロット管理設定に基づいて値が設定されます。
BOMVerify によるBOM検証
生産を実行する前に、BOMの構造が健全であることを確認する必要があります。BOMVerify プロセスは、生産を中断させる前に問題を検出します。
BOMVerify の検証項目
- マスターBOMの存在確認:製品に
BOMUse = 'A'かつBOMType = 'A'を同時に満たすBOMがあることを検証します(MPPProductBOM.getDefault()の判定ロジック) - 循環参照の検出:BOMツリーを再帰的に走査し、同じ製品が祖先と子孫の両方に表示されないことを確認します
- コンポーネントの有効性:すべてのBOM明細が有効でアクティブな製品を参照していることを確認します
- 在庫検証:非ファントムコンポーネントが在庫品目であることを確認します
- IsVerified フラグ:検証成功時に製品の
IsVerifiedフラグを更新します
BOMVerify のパラメーター
| パラメーター | 説明 |
|---|---|
| M_Product_ID | 単一製品のBOMを検証 |
| M_Product_Category_ID | カテゴリ内のすべての製品を一括検証 |
| IsReValidate | 検証済み製品を再検証 |
// Running BOM verification programmatically
MPInstance instance = new MPInstance(ctx, bomVerifyProcessId, 0);
instance.saveEx();
// Single product verification
MPInstancePara para = new MPInstancePara(instance, 10);
para.setParameter("M_Product_ID", productId);
para.saveEx();
// Re-validate previously verified products
MPInstancePara paraRevalidate = new MPInstancePara(instance, 20);
paraRevalidate.setParameter("IsReValidate", "Y");
paraRevalidate.saveEx();
ProcessInfo pi = new ProcessInfo("BOM Verify", bomVerifyProcessId);
pi.setAD_PInstance_ID(instance.getAD_PInstance_ID());
BOMVerify process = new BOMVerify();
process.startProcess(ctx, pi, null);
if (pi.isError()) {
System.err.println("BOM verification failed: " + pi.getSummary());
} else {
System.out.println("BOM verified: " + pi.getSummary());
}
ファントムBOM展開の詳細
ファントムアセンブリは、組織的な目的でBOM内に存在しますが、実際に在庫として保管されることはありません。createLines() の実行時に、ファントムコンポーネントはそのリーフコンポーネントに再帰的に展開されます。
MPPProductBOMLine の ComponentType フィールドは、各明細行が生産中にどのように動作するかを制御します。完全な値リスト:
- Component — 生産で消費される標準部品
- Phantom — 再帰的に展開;個別に在庫保管しない
- By-Product — 生産プロセスの二次的な産出物(例:切断時のスクラップ金属)
- Co-Product — 同等の重要性を持つ共同産出物(例:化学プロセスで生成される複数の製品)
- Packing — 消費される包装材料
- Tools — 必要だが消費されない工具
- Variant — 製品バリアントの代替部品
- Option — オプション部品(構成可能なBOM)
- Planning — MRP計画のみに使用、生産には不使用
ファントムの仕組み
以下の条件を満たす場合、製品はファントムとして扱われます:
- BOM明細の
ComponentType = 'PH'(MPPProductBOMLine.COMPONENTTYPE_Phantom.equals(bomLine.getComponentType())で判定) - 部品の
M_Productレコード自体がisPhantom() = trueとしてマークされている - コンポーネント製品が独自のBOMを持つ(
isBOM() = true) - 製品は通常
isStocked() = falseに設定される
ファントムと通常のサブアセンブリの比較
| 特性 | ファントムアセンブリ | 通常のサブアセンブリ |
|---|---|---|
| 在庫として保管 | いいえ | はい |
| 独自のBOMを持つ | はい | はい |
| 生産明細の作成 | いいえ — コンポーネントに展開 | はい — 単一品目として消費 |
| 個別の生産が必要 | いいえ | はい — 先に生産が必要 |
// Conceptual illustration of phantom expansion during createLines()
// This logic is internal to MProduction/MProductionPlan
private void expandBOM(MPPProductBOM bom, BigDecimal parentQty,
int locatorId) {
MPPProductBOMLine[] bomLines = bom.getLines();
for (MPPProductBOMLine bomLine : bomLines) {
MProduct component = MProduct.get(ctx, bomLine.getM_Product_ID());
BigDecimal componentQty = bomLine.getQtyBOM().multiply(parentQty);
if (MPPProductBOMLine.COMPONENTTYPE_Phantom.equals(bomLine.getComponentType())
&& component.isBOM()) {
// PHANTOM: Recursively expand — no line created for phantom itself
MPPProductBOM subBOM = MPPProductBOM.getDefault(component, trxName);
expandBOM(subBOM, componentQty, locatorId);
} else {
// REGULAR: Create consumption line
MProductionLine line = new MProductionLine(plan);
line.setM_Product_ID(component.getM_Product_ID());
line.setMovementQty(componentQty.negate()); // negative = consume
line.setM_Locator_ID(locatorId);
line.saveEx();
}
}
}
ProductionCreate と ProductionProcess
2つの主要プロセスが生産ライフサイクルを駆動します:
ProductionCreate — BOMからの明細生成
| パラメーター | 説明 |
|---|---|
| PP_Product_BOM_ID | 使用する特定のBOM(任意 — デフォルトはマスターBOM) |
| ProductionQty | 生産数量の上書き |
| Recreate | 新しい明細を作成する前に既存の明細を削除 |
ProductionProcess — 伝票ワークフローの実行
ProductionProcess は MWorkflow.runDocumentActionWorkflow() を使用して、ワークフロー条件、承認、および遷移が適切に実行されることを保証します。completeIt() の実行時:
- すべての生産明細(または計画)を反復処理
- 在庫移動のための
MTransactionレコードを作成 - ロット追跡のための
MProductionLineMAレコードを処理 MStorageOnHandの数量を更新- 会計仕訳を転記
注意:M_Production_Run は @Deprecated(since="13", forRemoval=true) です。常に ProductionProcess を使用してください。
// Complete production workflow
MProduction production = new MProduction(ctx, productionId, trxName);
production.setDocAction(DocAction.ACTION_Complete);
production.saveEx();
// Process via document action
if (!production.processIt(DocAction.ACTION_Complete)) {
throw new AdempiereException("Production failed: "
+ production.getProcessMsg());
}
production.saveEx();
System.out.println("Production completed: " + production.getDocumentNo());
重要ポイント
- 生産計画(
MProductionPlan)は、IsUseProductionPlan=trueの場合に複数製品の生産を可能にします。各計画は独自の製品、BOM、および明細を独立して管理します。 - 材料割当(
MProductionLineMA)はロットレベルのトレーサビリティを提供します。DateMaterialPolicyフィールドにより、正確なFIFO/LIFOコスト層の選択が保証されます。 - BOM検証(
BOMVerify)は、生産前の重要な検証です — 循環参照、欠落コンポーネント、およびマスターBOMの存在を確認します。 - ファントム展開は、
createLines()の実行時に非在庫アセンブリをリーフコンポーネントに再帰的に置き換えます。ファントム製品は直接消費または生産されることはありません。 - ProductionCreate はBOMから明細を生成し、ProductionProcess はDocActionワークフローを実行します。
M_Production_Runは非推奨です。
次のステップ
次のレッスン — 生産原価計算と分析 — では、iDempiere が生産コストをどのように追跡・計算するかを扱います。原価計算方法、MCost および MCostDetail API、IndentedBOM を使用した多階層BOMコストロールアップが含まれます。