Advanced Production & BOM Management

Level: Advanced Module: Production 13 min read Lesson 35 of 47

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 with one flagged as the master (default)
  • 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 IsVerified flag 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.

How Phantoms Work

A product is treated as a phantom when:

  • The BOM line has isPhantom() = true
  • 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 (bomLine.isPhantom() && 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 MTransaction records for inventory movements
  • Processes MProductionLineMA records for lot tracking
  • Updates MStorageOnHand quantities
  • 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 when IsUseProductionPlan=true. Each plan manages its own product, BOM, and lines independently.
  • Material Allocation (MProductionLineMA) provides lot-level traceability. The DateMaterialPolicy field 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_Run is 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,且其中一個被標記為主要(預設)BOM
  • 循環參照偵測:遞迴走訪 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() 期間,虛設組件會被遞迴展開為其末端組件。

虛設組件的運作方式

當符合以下條件時,產品被視為虛設組件:

  • BOM 明細的 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 (bomLine.isPhantom() && 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 はこれを生産計画で処理します。生産ヘッダーで IsUseProductionPlantrue に設定されると、システムは単一製品モデルから 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の存在確認:製品にBOMがあり、その中の1つがマスター(デフォルト)としてフラグ付けされていることを検証します
  • 循環参照の検出: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() の実行時に、ファントムコンポーネントはそのリーフコンポーネントに再帰的に展開されます。

ファントムの仕組み

以下の条件を満たす場合、製品はファントムとして扱われます:

  • BOM明細の 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 (bomLine.isPhantom() && 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 — 伝票ワークフローの実行

ProductionProcessMWorkflow.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コストロールアップが含まれます。

You Missed