A reusable service class that recursively calculates the total cost of a Bill of Materials (BOM) including all sub-assemblies. It traverses the BOM tree, summing up component costs at each level. Useful for production planning and cost estimation in iDempiere manufacturing workflows.
Code
public class BOMCostService {
/**
* Calculate total BOM cost recursively.
* @param productId M_Product_ID of the finished good
* @return total cost including sub-assemblies
*/
public BigDecimal calculateBOMCost(int productId) {
BigDecimal totalCost = BigDecimal.ZERO;
String sql = "SELECT bl.M_Product_ID, bl.QtyBOM, p.ProductType "
+ "FROM PP_Product_BOMLine bl "
+ "JOIN M_Product p ON p.M_Product_ID = bl.M_Product_ID "
+ "WHERE bl.PP_Product_BOM_ID IN "
+ "(SELECT PP_Product_BOM_ID FROM PP_Product_BOM WHERE M_Product_ID = ?)";
PreparedStatement pstmt = DB.prepareStatement(sql, null);
pstmt.setInt(1, productId);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
int compId = rs.getInt("M_Product_ID");
BigDecimal qty = rs.getBigDecimal("QtyBOM");
BigDecimal unitCost = getStandardCost(compId);
totalCost = totalCost.add(qty.multiply(unitCost));
}
DB.close(rs, pstmt);
return totalCost;
}
private BigDecimal getStandardCost(int productId) {
return DB.getSQLValueBD(null,
"SELECT CurrentCostPrice FROM M_Cost "
+ "WHERE M_Product_ID = ? AND AD_Client_ID = ? "
+ "AND M_CostElement_ID = (SELECT M_CostElement_ID "
+ "FROM M_CostElement WHERE CostingMethod = 'S' AND AD_Client_ID = ?)",
productId, Env.getAD_Client_ID(Env.getCtx()),
Env.getAD_Client_ID(Env.getCtx()));
}
}