IDEMPIERE-5159 Remove hard coded rounding in Doc_Production (#1137)

- plus multiple improvements to production unit tests
This commit is contained in:
Tony Snook 2022-01-19 14:09:03 +11:00 committed by GitHub
parent 0d7f775f1a
commit c8c0ae317e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 32 deletions

View File

@ -273,6 +273,7 @@ public class Doc_Production extends Doc
}
int stdPrecision = as.getStdPrecision();
BigDecimal bomCost = Env.ZERO;
BigDecimal qtyProduced = null;
if (line.isProductionBOM())
@ -320,7 +321,7 @@ public class Doc_Production extends Doc
costMap.put(line0.get_ID()+ "_"+ ma.getM_AttributeSetInstance_ID(),maCost);
costs0 = costs0.add(maCost);
}
bomCost = bomCost.add(costs0.setScale(2,RoundingMode.HALF_UP));
bomCost = bomCost.add(costs0);
}
else
p_Error = "Failed to post - No Attribute Set for line";
@ -341,7 +342,7 @@ public class Doc_Production extends Doc
costs0 = line0.getProductCosts(as, line0.getAD_Org_ID(), false);
}
costMap.put(line0.get_ID()+ "_"+ line0.getM_AttributeSetInstance_ID(),costs0);
bomCost = bomCost.add(costs0.setScale(2,RoundingMode.HALF_UP));
bomCost = bomCost.add(costs0);
}
}
@ -360,7 +361,7 @@ public class Doc_Production extends Doc
costs0 = line0.getProductCosts(as, line0.getAD_Org_ID(), false);
}
costMap.put(line0.get_ID()+ "_"+ line0.getM_AttributeSetInstance_ID(),costs0);
bomCost = bomCost.add(costs0.setScale(2,RoundingMode.HALF_UP));
bomCost = bomCost.add(costs0);
}
}
}
@ -369,7 +370,7 @@ public class Doc_Production extends Doc
if (line.getQty().compareTo(qtyProduced) != 0)
{
BigDecimal factor = line.getQty().divide(qtyProduced, 12, RoundingMode.HALF_UP);
bomCost = bomCost.multiply(factor).setScale(2,RoundingMode.HALF_UP);
bomCost = bomCost.multiply(factor);
}
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(CostingLevel))
@ -377,7 +378,7 @@ public class Doc_Production extends Doc
//post roll-up
fl = fact.createLine(line,
line.getAccount(ProductCost.ACCTTYPE_P_Asset, as),
as.getC_Currency_ID(), bomCost.negate());
as.getC_Currency_ID(), bomCost.negate().setScale(stdPrecision, RoundingMode.HALF_UP));
if (fl == null)
{
p_Error = "Couldn't post roll-up " + line.getLine() + " - " + line;
@ -387,8 +388,7 @@ public class Doc_Production extends Doc
}
else if (MAcctSchema.COSTINGMETHOD_StandardCosting.equals(costingMethod))
{
int precision = as.getStdPrecision();
BigDecimal variance = (costs.setScale(precision, RoundingMode.HALF_UP)).subtract(bomCost.negate());
BigDecimal variance = costs.subtract(bomCost.negate()).setScale(stdPrecision, RoundingMode.HALF_UP);
// only post variance if it's not zero
if (variance.signum() != 0)
{
@ -417,7 +417,7 @@ public class Doc_Production extends Doc
}
fl = fact.createLine(line,
line.getAccount(ProductCost.ACCTTYPE_P_Asset, as),
as.getC_Currency_ID(), factLineAmt);
as.getC_Currency_ID(), factLineAmt.setScale(stdPrecision, RoundingMode.HALF_UP));
if (fl == null)
{
p_Error = "No Costs for Line " + line.getLine() + " - " + line;

View File

@ -31,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.List;
@ -164,8 +165,10 @@ public class ProductionTest extends AbstractTestCase {
BigDecimal componentOnHand2 = MStorageOnHand.getQtyOnHand(mulchId, getM_Warehouse_ID(), 0, getTrxName());
BigDecimal endProductOnHand2 = MStorageOnHand.getQtyOnHand(mulchX.get_ID(), getM_Warehouse_ID(), 0, getTrxName());
assertTrue(componentOnHand2.compareTo(componentOnHand1) < 0, "On hand of component doesn't reduce as expected");
assertTrue(endProductOnHand2.compareTo(endProductOnHand1) > 0, "On hand of end product doesn't increase as expected");
BigDecimal componentChange = componentOnHand2.subtract(componentOnHand1).setScale(0);
BigDecimal endProductChange = endProductOnHand2.subtract(endProductOnHand1).setScale(0);
assertEquals(new BigDecimal("-1"), componentChange, "On hand of component doesn't reduce as expected");
assertEquals(new BigDecimal("1"), endProductChange, "On hand of end product doesn't increase as expected");
if (!production.isPosted()) {
String msg = DocumentEngine.postImmediate(Env.getCtx(), getAD_Client_ID(), MProduction.Table_ID, production.get_ID(), false, getTrxName());
@ -173,9 +176,13 @@ public class ProductionTest extends AbstractTestCase {
}
BigDecimal endProductCost = MCost.getCurrentCost(mulchX, 0, getTrxName());
assertTrue(endProductCost.equals(componentCost), "Cost not roll up correctly");
MAcctSchema as = MClient.get(getAD_Client_ID()).getAcctSchema();
componentCost = componentCost.setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
endProductCost = endProductCost.setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
assertEquals(componentCost, endProductCost, "Cost not roll up correctly");
}
// creates an order and material receipt for qty 25 at special price of 2.60 each
private void createPOAndMRForProduct(int mulchId) {
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
order.setBPartner(MBPartner.get(Env.getCtx(), BP_PATIO));
@ -192,7 +199,8 @@ public class ProductionTest extends AbstractTestCase {
MOrderLine line1 = new MOrderLine(order);
line1.setLine(10);
line1.setProduct(MProduct.get(Env.getCtx(), mulchId));
line1.setQty(new BigDecimal("1"));
line1.setQty(new BigDecimal("25"));
line1.setPrice(new BigDecimal("2.60"));
line1.setDatePromised(today);
line1.saveEx();
@ -207,8 +215,8 @@ public class ProductionTest extends AbstractTestCase {
receipt1.saveEx();
MInOutLine receiptLine1 = new MInOutLine(receipt1);
receiptLine1.setOrderLine(line1, 0, new BigDecimal("1"));
receiptLine1.setQty(new BigDecimal("1"));
receiptLine1.setOrderLine(line1, 0, new BigDecimal("25"));
receiptLine1.setQty(new BigDecimal("25"));
receiptLine1.saveEx();
info = MWorkflow.runDocumentActionWorkflow(receipt1, DocAction.ACTION_Complete);
@ -235,14 +243,17 @@ public class ProductionTest extends AbstractTestCase {
categoryAcct.setCostingMethod(MAcctSchema.COSTINGMETHOD_StandardCosting);
categoryAcct.saveEx();
}
// ProductCost api doesn't use trx to retrieve product
int mulchId = 137;
MProduct mulch = new MProduct(Env.getCtx(), mulchId, null);
int categorySaveId = mulch.getM_Product_Category_ID();
mulch.setM_Product_Category_ID(category.get_ID());
mulch.saveEx();
try {
int mulchId = 137;
int hqLocator = 101;
createPOAndMRForProduct(mulchId);
MProduct mulch = MProduct.get(mulchId);
BigDecimal componentOnHand1 = MStorageOnHand.getQtyOnHand(mulchId, getM_Warehouse_ID(), 0, getTrxName());
BigDecimal componentCost = MCost.getCurrentCost(mulch, 0, getTrxName());
@ -267,7 +278,8 @@ public class ProductionTest extends AbstractTestCase {
inventory.setDocAction(DocAction.ACTION_Complete);
inventory.saveEx();
BigDecimal endProductCost = new BigDecimal("2.50");
MAcctSchema as = MClient.get(getAD_Client_ID()).getAcctSchema();
BigDecimal endProductCost = new BigDecimal("2.50").setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
MInventoryLine il = new MInventoryLine(Env.getCtx(), 0, getTrxName());
il.setM_Inventory_ID(inventory.get_ID());
il.setM_Locator_ID(hqLocator);
@ -285,8 +297,8 @@ public class ProductionTest extends AbstractTestCase {
String msg = DocumentEngine.postImmediate(Env.getCtx(), getAD_Client_ID(), MInventory.Table_ID, inventory.get_ID(), false, getTrxName());
assertNull(msg, msg);
}
BigDecimal adjusted = MCost.getCurrentCost(mulchX, 0, getTrxName());
assertTrue(adjusted.equals(endProductCost), "Cost not adjusted: " + adjusted.toPlainString());
BigDecimal adjusted = MCost.getCurrentCost(mulchX, 0, getTrxName()).setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
assertEquals(endProductCost, adjusted, "Cost not adjusted: " + adjusted.toPlainString());
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
bom.setM_Product_ID(mulchX.get_ID());
@ -337,24 +349,26 @@ public class ProductionTest extends AbstractTestCase {
BigDecimal componentOnHand2 = MStorageOnHand.getQtyOnHand(mulchId, getM_Warehouse_ID(), 0, getTrxName());
BigDecimal endProductOnHand2 = MStorageOnHand.getQtyOnHand(mulchX.get_ID(), getM_Warehouse_ID(), 0, getTrxName());
BigDecimal componentChange = componentOnHand2.subtract(componentOnHand1).setScale(0);
BigDecimal endProductChange = endProductOnHand2.subtract(endProductOnHand1).setScale(0);
assertTrue(componentOnHand2.compareTo(componentOnHand1) < 0, "On hand of component doesn't reduce as expected");
assertTrue(endProductOnHand2.compareTo(endProductOnHand1) > 0, "On hand of end product doesn't increase as expected");
assertEquals(componentChange, new BigDecimal("-1"), "On hand of component doesn't reduce as expected");
assertEquals(endProductChange, new BigDecimal("1"), "On hand of end product doesn't increase as expected");
if (!production.isPosted()) {
String msg = DocumentEngine.postImmediate(Env.getCtx(), getAD_Client_ID(), MProduction.Table_ID, production.get_ID(), false, getTrxName());
assertNull(msg, msg);
}
BigDecimal endProductCost1 = MCost.getCurrentCost(mulchX, 0, getTrxName());
assertTrue(endProductCost1.equals(endProductCost), "Standard Cost Changed");
BigDecimal endProductCost1 = MCost.getCurrentCost(mulchX, 0, getTrxName()).setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
assertEquals(endProductCost, endProductCost1, "Standard Cost Changed");
ProductCost pc = new ProductCost (Env.getCtx(), mulchX.getM_Product_ID(), 0, getTrxName());
MAccount acctVariance = pc.getAccount(ProductCost.ACCTTYPE_P_RateVariance, MClient.get(getAD_Client_ID()).getAcctSchema());
MAccount acctVariance = pc.getAccount(ProductCost.ACCTTYPE_P_RateVariance, as);
whereClause = MFactAcct.COLUMNNAME_AD_Table_ID + "=" + MProduction.Table_ID
+ " AND " + MFactAcct.COLUMNNAME_Record_ID + "=" + production.get_ID()
+ " AND " + MFactAcct.COLUMNNAME_C_AcctSchema_ID + "=" + MClient.get(getAD_Client_ID()).getAcctSchema().getC_AcctSchema_ID()
+ " AND " + MFactAcct.COLUMNNAME_C_AcctSchema_ID + "=" + as.getC_AcctSchema_ID()
+ " AND " + MFactAcct.COLUMNNAME_Account_ID + "=" + acctVariance.getAccount_ID();
int[] ids = MFactAcct.getAllIDs(MFactAcct.Table_Name, whereClause, getTrxName());
BigDecimal variance = BigDecimal.ZERO;
@ -363,9 +377,12 @@ public class ProductionTest extends AbstractTestCase {
variance = fa.getAmtAcctDr().subtract(fa.getAmtAcctCr());
break;
}
assertEquals(variance.doubleValue(), componentCost.subtract(endProductCost).doubleValue(), "Variance not posted correctly.");
BigDecimal varianceExpected = componentCost.subtract(endProductCost).setScale(as.getStdPrecision(), RoundingMode.HALF_UP);
assertEquals(varianceExpected, variance, "Variance not posted correctly.");
} finally {
getTrx().rollback();
mulch.setM_Product_Category_ID(categorySaveId);
mulch.saveEx();
category.deleteEx(true);
}
}
@ -392,7 +409,8 @@ public class ProductionTest extends AbstractTestCase {
mulch.setM_Product_Category_ID(category.get_ID());
mulch.saveEx();
BigDecimal componentCost = MCost.getCurrentCost(mulch, 0, getTrxName());
MAcctSchema as = MClient.get(getAD_Client_ID()).getAcctSchema();
BigDecimal componentCost = MCost.getCurrentCost(mulch, 0, getTrxName()).setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);
MProduct mulchX = new MProduct(Env.getCtx(), 0, getTrxName());
mulchX.setName("Mulch X");
@ -439,8 +457,8 @@ public class ProductionTest extends AbstractTestCase {
ServerProcessCtl.process(info, getTrx(), false);
assertFalse(info.isError(), info.getSummary());
BigDecimal endProductCost = MCost.getCurrentCost(mulchX, 0, getTrxName());
assertEquals(componentCost.doubleValue(), endProductCost.doubleValue(), "BOM Cost not roll up.");
BigDecimal endProductCost = MCost.getCurrentCost(mulchX, 0, getTrxName()).setScale(as.getCostingPrecision(), RoundingMode.HALF_UP);;
assertEquals(componentCost, endProductCost, "BOM Cost not roll up.");
} finally {
getTrx().rollback();
category.deleteEx(true);
@ -466,6 +484,8 @@ public class ProductionTest extends AbstractTestCase {
mulchX.saveEx();
try {
createPOAndMRForProduct(mulchId); // create some stock to avoid negative qty average cost exception
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
bom.setM_Product_ID(mulchX.get_ID());
bom.setBOMType(MPPProductBOM.BOMTYPE_CurrentActive);