IDEMPIERE-5503 Product Costs > Cost Movement > Field AMOUNT has incon… (#1598)
* IDEMPIERE-5503 Product Costs > Cost Movement > Field AMOUNT has inconsistent +/- signs * IDEMPIERE-5503 Product Costs > Cost Movement > Field AMOUNT has inconsistent +/- signs - refinement and added unit tests * IDEMPIERE-5503 Product Costs > Cost Movement > Field AMOUNT has inconsistent +/- signs - add unit test for lot level costing * IDEMPIERE-5503 Product Costs > Cost Movement > Field AMOUNT has inconsistent +/- signs - fix unit test error. * IDEMPIERE-5503 Product Costs > Cost Movement > Field AMOUNT has inconsistent +/- signs - add unit test for cost adjustment * IDEMPIERE-5503 Product Costs > Cost Movement > Field AMOUNT has inconsistent +/- signs - add unit test for physical inventory * IDEMPIERE-5503 Product Costs > Cost Movement > Field AMOUNT has inconsistent +/- signs - add unit test for PO landed cost
This commit is contained in:
parent
7c0055f797
commit
06b02889f8
|
@ -0,0 +1,10 @@
|
||||||
|
SELECT register_migration_script('202212021150_IDEMPIERE-5503.sql') FROM dual;
|
||||||
|
|
||||||
|
SET SQLBLANKLINES ON
|
||||||
|
SET DEFINE OFF
|
||||||
|
|
||||||
|
UPDATE M_CostDetail SET Amt = Amt * -1 WHERE Qty < 0 AND Amt > 0 AND M_InOutLine_ID IS NOT NULL
|
||||||
|
;
|
||||||
|
|
||||||
|
UPDATE M_CostDetail SET Amt = Amt * -1 WHERE Qty > 0 AND Amt < 0 AND M_InOutLine_ID IS NOT NULL
|
||||||
|
;
|
|
@ -0,0 +1,8 @@
|
||||||
|
SELECT register_migration_script('202212021150_IDEMPIERE-5503.sql') FROM dual;
|
||||||
|
|
||||||
|
UPDATE M_CostDetail SET Amt = Amt * -1 WHERE Qty < 0 AND Amt > 0 AND M_InOutLine_ID IS NOT NULL
|
||||||
|
;
|
||||||
|
|
||||||
|
UPDATE M_CostDetail SET Amt = Amt * -1 WHERE Qty > 0 AND Amt < 0 AND M_InOutLine_ID IS NOT NULL
|
||||||
|
;
|
||||||
|
|
|
@ -1481,7 +1481,7 @@ public abstract class Doc
|
||||||
ResultSet rs = null;
|
ResultSet rs = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pstmt = DB.prepareStatement(sql, null);
|
pstmt = DB.prepareStatement(sql, getTrxName());
|
||||||
if (para_1 == -1) // GL Accounts
|
if (para_1 == -1) // GL Accounts
|
||||||
pstmt.setInt (1, as.getC_AcctSchema_ID());
|
pstmt.setInt (1, as.getC_AcctSchema_ID());
|
||||||
else
|
else
|
||||||
|
|
|
@ -20,7 +20,9 @@ import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import org.compiere.model.I_C_OrderLine;
|
import org.compiere.model.I_C_OrderLine;
|
||||||
|
@ -194,6 +196,7 @@ public class Doc_InOut extends Doc
|
||||||
{
|
{
|
||||||
for (int i = 0; i < p_lines.length; i++)
|
for (int i = 0; i < p_lines.length; i++)
|
||||||
{
|
{
|
||||||
|
Map<String, BigDecimal> batchLotCostMap = null;
|
||||||
DocLine_InOut line = (DocLine_InOut) p_lines[i];
|
DocLine_InOut line = (DocLine_InOut) p_lines[i];
|
||||||
MProduct product = line.getProduct();
|
MProduct product = line.getProduct();
|
||||||
BigDecimal costs = null;
|
BigDecimal costs = null;
|
||||||
|
@ -207,16 +210,19 @@ public class Doc_InOut extends Doc
|
||||||
MInOutLineMA mas[] = MInOutLineMA.get(getCtx(), ioLine.get_ID(), getTrxName());
|
MInOutLineMA mas[] = MInOutLineMA.get(getCtx(), ioLine.get_ID(), getTrxName());
|
||||||
if (mas != null && mas.length > 0 )
|
if (mas != null && mas.length > 0 )
|
||||||
{
|
{
|
||||||
|
batchLotCostMap = new HashMap<>();
|
||||||
costs = BigDecimal.ZERO;
|
costs = BigDecimal.ZERO;
|
||||||
for (int j = 0; j < mas.length; j++)
|
for (int j = 0; j < mas.length; j++)
|
||||||
{
|
{
|
||||||
MInOutLineMA ma = mas[j];
|
MInOutLineMA ma = mas[j];
|
||||||
BigDecimal QtyMA = ma.getMovementQty();
|
BigDecimal QtyMA = ma.getMovementQty();
|
||||||
|
if (QtyMA.signum() != line.getQty().signum())
|
||||||
|
QtyMA = QtyMA.negate();
|
||||||
ProductCost pc = line.getProductCost();
|
ProductCost pc = line.getProductCost();
|
||||||
pc.setQty(QtyMA);
|
pc.setQty(QtyMA);
|
||||||
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
|
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
|
||||||
BigDecimal maCosts = line.getProductCosts(as, line.getAD_Org_ID(), true, "M_InOutLine_ID=?");
|
BigDecimal maCosts = line.getProductCosts(as, line.getAD_Org_ID(), true, "M_InOutLine_ID=?");
|
||||||
|
batchLotCostMap.put(ma.getM_InOutLineMA_UU(), maCosts);
|
||||||
costs = costs.add(maCosts);
|
costs = costs.add(maCosts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,10 +335,31 @@ public class Doc_InOut extends Doc
|
||||||
for (int j = 0; j < mas.length; j++)
|
for (int j = 0; j < mas.length; j++)
|
||||||
{
|
{
|
||||||
MInOutLineMA ma = mas[j];
|
MInOutLineMA ma = mas[j];
|
||||||
|
BigDecimal qty = ma.getMovementQty();
|
||||||
|
if (qty.signum() != line.getQty().signum())
|
||||||
|
qty = qty.negate();
|
||||||
|
BigDecimal amt = batchLotCostMap != null ? batchLotCostMap.get(ma.getM_InOutLineMA_UU()) : null;
|
||||||
|
if (amt == null && isReversal(line))
|
||||||
|
{
|
||||||
|
amt = findReversalCostDetailAmt(as, line.getM_Product_ID(), ma, line.getReversalLine_ID());
|
||||||
|
if (amt != null)
|
||||||
|
amt = amt.negate();
|
||||||
|
}
|
||||||
|
if (amt == null)
|
||||||
|
{
|
||||||
|
amt = costs.divide(line.getProductCost().getQty(), RoundingMode.HALF_UP);
|
||||||
|
amt = amt.multiply(qty);
|
||||||
|
}
|
||||||
|
else if (!isReversal(line) && line.getProductCost().getQty().signum() != line.getQty().signum())
|
||||||
|
{
|
||||||
|
if (amt.signum() != (costs.signum() * -1)) {
|
||||||
|
amt = amt.negate();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
||||||
line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),
|
line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),
|
||||||
line.get_ID(), 0,
|
line.get_ID(), 0,
|
||||||
costs, ma.getMovementQty().negate(),
|
amt, qty,
|
||||||
line.getDescription(), true, getTrxName()))
|
line.getDescription(), true, getTrxName()))
|
||||||
{
|
{
|
||||||
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
||||||
|
@ -346,10 +373,13 @@ public class Doc_InOut extends Doc
|
||||||
//
|
//
|
||||||
if (line.getM_Product_ID() != 0)
|
if (line.getM_Product_ID() != 0)
|
||||||
{
|
{
|
||||||
|
BigDecimal amt = costs;
|
||||||
|
if (line.getProductCost().getQty().signum() != line.getQty().signum() && !isReversal(line))
|
||||||
|
amt = amt.negate();
|
||||||
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
||||||
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
||||||
line.get_ID(), 0,
|
line.get_ID(), 0,
|
||||||
costs, line.getQty(),
|
amt, line.getQty(),
|
||||||
line.getDescription(), true, getTrxName()))
|
line.getDescription(), true, getTrxName()))
|
||||||
{
|
{
|
||||||
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
||||||
|
@ -363,10 +393,13 @@ public class Doc_InOut extends Doc
|
||||||
//
|
//
|
||||||
if (line.getM_Product_ID() != 0)
|
if (line.getM_Product_ID() != 0)
|
||||||
{
|
{
|
||||||
|
BigDecimal amt = costs;
|
||||||
|
if (line.getProductCost().getQty().signum() != line.getQty().signum() && !isReversal(line))
|
||||||
|
amt = amt.negate();
|
||||||
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
||||||
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
||||||
line.get_ID(), 0,
|
line.get_ID(), 0,
|
||||||
costs, line.getQty(),
|
amt, line.getQty(),
|
||||||
line.getDescription(), true, getTrxName()))
|
line.getDescription(), true, getTrxName()))
|
||||||
{
|
{
|
||||||
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
||||||
|
@ -398,6 +431,7 @@ public class Doc_InOut extends Doc
|
||||||
DocLine_InOut line = (DocLine_InOut) p_lines[i];
|
DocLine_InOut line = (DocLine_InOut) p_lines[i];
|
||||||
MProduct product = line.getProduct();
|
MProduct product = line.getProduct();
|
||||||
BigDecimal costs = null;
|
BigDecimal costs = null;
|
||||||
|
Map<String, BigDecimal> batchLotCostMap = null;
|
||||||
if (!isReversal(line))
|
if (!isReversal(line))
|
||||||
{
|
{
|
||||||
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(product.getCostingLevel(as)) )
|
if (MAcctSchema.COSTINGLEVEL_BatchLot.equals(product.getCostingLevel(as)) )
|
||||||
|
@ -409,15 +443,18 @@ public class Doc_InOut extends Doc
|
||||||
costs = BigDecimal.ZERO;
|
costs = BigDecimal.ZERO;
|
||||||
if (mas != null && mas.length > 0 )
|
if (mas != null && mas.length > 0 )
|
||||||
{
|
{
|
||||||
|
batchLotCostMap = new HashMap<>();
|
||||||
for (int j = 0; j < mas.length; j++)
|
for (int j = 0; j < mas.length; j++)
|
||||||
{
|
{
|
||||||
MInOutLineMA ma = mas[j];
|
MInOutLineMA ma = mas[j];
|
||||||
BigDecimal QtyMA = ma.getMovementQty();
|
BigDecimal QtyMA = ma.getMovementQty();
|
||||||
|
if (QtyMA.signum() != line.getQty().signum())
|
||||||
|
QtyMA = QtyMA.negate();
|
||||||
ProductCost pc = line.getProductCost();
|
ProductCost pc = line.getProductCost();
|
||||||
pc.setQty(QtyMA);
|
pc.setQty(QtyMA);
|
||||||
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
|
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
|
||||||
BigDecimal maCosts = line.getProductCosts(as, line.getAD_Org_ID(), true, "M_InOutLine_ID=?");
|
BigDecimal maCosts = line.getProductCosts(as, line.getAD_Org_ID(), true, "M_InOutLine_ID=?");
|
||||||
|
batchLotCostMap.put(ma.getM_InOutLineMA_UU(), maCosts);
|
||||||
costs = costs.add(maCosts);
|
costs = costs.add(maCosts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -490,10 +527,31 @@ public class Doc_InOut extends Doc
|
||||||
for (int j = 0; j < mas.length; j++)
|
for (int j = 0; j < mas.length; j++)
|
||||||
{
|
{
|
||||||
MInOutLineMA ma = mas[j];
|
MInOutLineMA ma = mas[j];
|
||||||
|
BigDecimal qty = ma.getMovementQty();
|
||||||
|
if (qty.signum() != line.getQty().signum())
|
||||||
|
qty = qty.negate();
|
||||||
|
BigDecimal amt = batchLotCostMap != null ? batchLotCostMap.get(ma.getM_InOutLineMA_UU()) : null;
|
||||||
|
if (amt == null && isReversal(line))
|
||||||
|
{
|
||||||
|
amt = findReversalCostDetailAmt(as, line.getM_Product_ID(), ma, line.getReversalLine_ID());
|
||||||
|
if (amt != null)
|
||||||
|
amt = amt.negate();
|
||||||
|
}
|
||||||
|
if (amt == null)
|
||||||
|
{
|
||||||
|
amt = costs.divide(line.getProductCost().getQty(), RoundingMode.HALF_UP);
|
||||||
|
amt = amt.multiply(qty);
|
||||||
|
}
|
||||||
|
else if (!isReversal(line) && line.getProductCost().getQty().signum() != line.getQty().signum())
|
||||||
|
{
|
||||||
|
if (amt.signum() != (costs.signum() * -1)) {
|
||||||
|
amt = amt.negate();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
||||||
line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),
|
line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),
|
||||||
line.get_ID(), 0,
|
line.get_ID(), 0,
|
||||||
costs, ma.getMovementQty(),
|
amt, qty,
|
||||||
line.getDescription(), true, getTrxName()))
|
line.getDescription(), true, getTrxName()))
|
||||||
{
|
{
|
||||||
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
||||||
|
@ -505,10 +563,13 @@ public class Doc_InOut extends Doc
|
||||||
{
|
{
|
||||||
if (line.getM_Product_ID() != 0)
|
if (line.getM_Product_ID() != 0)
|
||||||
{
|
{
|
||||||
|
BigDecimal amt = costs;
|
||||||
|
if (line.getProductCost().getQty().signum() != line.getQty().signum() && !isReversal(line))
|
||||||
|
amt = amt.negate();
|
||||||
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
||||||
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
||||||
line.get_ID(), 0,
|
line.get_ID(), 0,
|
||||||
costs, line.getQty(),
|
amt, line.getQty(),
|
||||||
line.getDescription(), true, getTrxName()))
|
line.getDescription(), true, getTrxName()))
|
||||||
{
|
{
|
||||||
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
||||||
|
@ -521,10 +582,13 @@ public class Doc_InOut extends Doc
|
||||||
//
|
//
|
||||||
if (line.getM_Product_ID() != 0)
|
if (line.getM_Product_ID() != 0)
|
||||||
{
|
{
|
||||||
|
BigDecimal amt = costs;
|
||||||
|
if (line.getProductCost().getQty().signum() != line.getQty().signum() && !isReversal(line))
|
||||||
|
amt = amt.negate();
|
||||||
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
if (!MCostDetail.createShipment(as, line.getAD_Org_ID(),
|
||||||
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
||||||
line.get_ID(), 0,
|
line.get_ID(), 0,
|
||||||
costs, line.getQty(),
|
amt, line.getQty(),
|
||||||
line.getDescription(), true, getTrxName()))
|
line.getDescription(), true, getTrxName()))
|
||||||
{
|
{
|
||||||
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
p_Error = Msg.getMsg(getCtx(),"Failed to create cost detail record");
|
||||||
|
@ -899,6 +963,8 @@ public class Doc_InOut extends Doc
|
||||||
{
|
{
|
||||||
MInOutLineMA ma = mas[j];
|
MInOutLineMA ma = mas[j];
|
||||||
BigDecimal QtyMA = ma.getMovementQty();
|
BigDecimal QtyMA = ma.getMovementQty();
|
||||||
|
if (QtyMA.signum() != line.getQty().signum())
|
||||||
|
QtyMA = QtyMA.negate();
|
||||||
ProductCost pc = line.getProductCost();
|
ProductCost pc = line.getProductCost();
|
||||||
pc.setQty(QtyMA);
|
pc.setQty(QtyMA);
|
||||||
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
|
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
|
||||||
|
@ -1009,6 +1075,17 @@ public class Doc_InOut extends Doc
|
||||||
return facts;
|
return facts;
|
||||||
} // createFact
|
} // createFact
|
||||||
|
|
||||||
|
private BigDecimal findReversalCostDetailAmt(MAcctSchema as, int M_Product_ID, MInOutLineMA ma, int reversalLine_ID) {
|
||||||
|
StringBuilder select = new StringBuilder("SELECT ").append(MCostDetail.COLUMNNAME_Amt)
|
||||||
|
.append(" FROM ").append(MCostDetail.Table_Name).append(" WHERE ")
|
||||||
|
.append(MCostDetail.COLUMNNAME_C_AcctSchema_ID).append("=? ")
|
||||||
|
.append("AND ").append(MCostDetail.COLUMNNAME_M_AttributeSetInstance_ID).append("=? ")
|
||||||
|
.append("AND ").append(MCostDetail.COLUMNNAME_M_InOutLine_ID).append("=? ")
|
||||||
|
.append("AND ").append(MCostDetail.COLUMNNAME_M_Product_ID).append("=? ");
|
||||||
|
BigDecimal amt = DB.getSQLValueBDEx(getTrxName(), select.toString(), as.getC_AcctSchema_ID(), ma.getM_AttributeSetInstance_ID(), reversalLine_ID, M_Product_ID);
|
||||||
|
return amt;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isReversal(DocLine line) {
|
private boolean isReversal(DocLine line) {
|
||||||
return m_Reversal_ID !=0 && line.getReversalLine_ID() != 0;
|
return m_Reversal_ID !=0 && line.getReversalLine_ID() != 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,6 +316,7 @@ public class Doc_Inventory extends Doc
|
||||||
p_Error = "Original Physical Inventory not posted yet";
|
p_Error = "Original Physical Inventory not posted yet";
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
costs = dr.getAcctBalance(); //get original cost
|
||||||
}
|
}
|
||||||
|
|
||||||
// InventoryDiff DR CR
|
// InventoryDiff DR CR
|
||||||
|
@ -357,7 +358,6 @@ public class Doc_Inventory extends Doc
|
||||||
p_Error = "Original Physical Inventory not posted yet";
|
p_Error = "Original Physical Inventory not posted yet";
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
costs = cr.getAcctBalance(); //get original cost
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -386,11 +386,15 @@ public class Doc_Inventory extends Doc
|
||||||
{
|
{
|
||||||
MInventoryLineMA ma = mas[j];
|
MInventoryLineMA ma = mas[j];
|
||||||
BigDecimal maCost = costMap.get(line.get_ID()+ "_"+ ma.getM_AttributeSetInstance_ID());
|
BigDecimal maCost = costMap.get(line.get_ID()+ "_"+ ma.getM_AttributeSetInstance_ID());
|
||||||
|
BigDecimal qty = ma.getMovementQty();
|
||||||
|
if (qty.signum() != line.getQty().signum())
|
||||||
|
qty = qty.negate();
|
||||||
|
if (maCost.signum() != costDetailAmt.signum())
|
||||||
|
maCost = maCost.negate();
|
||||||
if (!MCostDetail.createInventory(as, line.getAD_Org_ID(),
|
if (!MCostDetail.createInventory(as, line.getAD_Org_ID(),
|
||||||
line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),
|
line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),
|
||||||
line.get_ID(), 0,
|
line.get_ID(), 0,
|
||||||
maCost, ma.getMovementQty().negate(),
|
maCost, qty,
|
||||||
line.getDescription(), getTrxName()))
|
line.getDescription(), getTrxName()))
|
||||||
{
|
{
|
||||||
p_Error = "Failed to create cost detail record";
|
p_Error = "Failed to create cost detail record";
|
||||||
|
@ -401,10 +405,11 @@ public class Doc_Inventory extends Doc
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
BigDecimal amt = costDetailAmt;
|
||||||
if (!MCostDetail.createInventory(as, line.getAD_Org_ID(),
|
if (!MCostDetail.createInventory(as, line.getAD_Org_ID(),
|
||||||
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
||||||
line.get_ID(), 0,
|
line.get_ID(), 0,
|
||||||
costDetailAmt, line.getQty(),
|
amt, line.getQty(),
|
||||||
line.getDescription(), getTrxName()))
|
line.getDescription(), getTrxName()))
|
||||||
{
|
{
|
||||||
p_Error = "Failed to create cost detail record";
|
p_Error = "Failed to create cost detail record";
|
||||||
|
@ -415,10 +420,11 @@ public class Doc_Inventory extends Doc
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Cost Detail
|
// Cost Detail
|
||||||
|
BigDecimal amt = costDetailAmt;
|
||||||
if (!MCostDetail.createInventory(as, line.getAD_Org_ID(),
|
if (!MCostDetail.createInventory(as, line.getAD_Org_ID(),
|
||||||
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
||||||
line.get_ID(), 0,
|
line.get_ID(), 0,
|
||||||
costDetailAmt, line.getQty(),
|
amt, line.getQty(),
|
||||||
line.getDescription(), getTrxName()))
|
line.getDescription(), getTrxName()))
|
||||||
{
|
{
|
||||||
p_Error = "Failed to create cost detail record";
|
p_Error = "Failed to create cost detail record";
|
||||||
|
|
|
@ -154,6 +154,8 @@ public class Doc_Movement extends Doc
|
||||||
{
|
{
|
||||||
MMovementLineMA ma = mas[j];
|
MMovementLineMA ma = mas[j];
|
||||||
BigDecimal QtyMA = ma.getMovementQty();
|
BigDecimal QtyMA = ma.getMovementQty();
|
||||||
|
if (QtyMA.signum() != line.getQty().signum())
|
||||||
|
QtyMA = QtyMA.negate();
|
||||||
ProductCost pc = line.getProductCost();
|
ProductCost pc = line.getProductCost();
|
||||||
pc.setQty(QtyMA);
|
pc.setQty(QtyMA);
|
||||||
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
|
pc.setM_M_AttributeSetInstance_ID(ma.getM_AttributeSetInstance_ID());
|
||||||
|
|
|
@ -161,6 +161,8 @@ public class Doc_ProjectIssue extends Doc
|
||||||
if (product != null && product.get_ID() > 0 && !product.isService() && product.isStocked()) {
|
if (product != null && product.get_ID() > 0 && !product.isService() && product.isStocked()) {
|
||||||
BigDecimal costDetailQty = m_line.getQty();
|
BigDecimal costDetailQty = m_line.getQty();
|
||||||
BigDecimal costDetailAmt = cost;
|
BigDecimal costDetailAmt = cost;
|
||||||
|
if (m_line.getQty().signum() != m_line.getProductCost().getQty().signum())
|
||||||
|
costDetailAmt = costDetailAmt.negate();
|
||||||
if (!MCostDetail.createProjectIssue(as, m_line.getAD_Org_ID(),
|
if (!MCostDetail.createProjectIssue(as, m_line.getAD_Org_ID(),
|
||||||
m_line.getM_Product_ID(), m_line.getM_AttributeSetInstance_ID(),
|
m_line.getM_Product_ID(), m_line.getM_AttributeSetInstance_ID(),
|
||||||
m_line.get_ID(), 0,
|
m_line.get_ID(), 0,
|
||||||
|
|
|
@ -663,6 +663,27 @@ public class MCostDetail extends X_M_CostDetail
|
||||||
return retValue;
|
return retValue;
|
||||||
} // get
|
} // get
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
* Get Cost Detail Records
|
||||||
|
* @param ctx context
|
||||||
|
* @param whereClause where clause
|
||||||
|
* @param ID 1st parameter
|
||||||
|
* @param M_AttributeSetInstance_ID ASI
|
||||||
|
* @param trxName trx
|
||||||
|
* @return list of cost detail record
|
||||||
|
*/
|
||||||
|
public static List<MCostDetail> list (Properties ctx, String whereClause,
|
||||||
|
int ID, int M_AttributeSetInstance_ID, int C_AcctSchema_ID, String trxName)
|
||||||
|
{
|
||||||
|
StringBuilder localWhereClause = new StringBuilder(whereClause)
|
||||||
|
.append(" AND M_AttributeSetInstance_ID=?")
|
||||||
|
.append(" AND C_AcctSchema_ID=?");
|
||||||
|
List<MCostDetail> retValue = new Query(ctx,I_M_CostDetail.Table_Name,localWhereClause.toString(),trxName)
|
||||||
|
.setParameters(ID,M_AttributeSetInstance_ID,C_AcctSchema_ID)
|
||||||
|
.list();
|
||||||
|
return retValue;
|
||||||
|
} // get
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process Cost Details for product
|
* Process Cost Details for product
|
||||||
* @param product product
|
* @param product product
|
||||||
|
@ -732,15 +753,15 @@ public class MCostDetail extends X_M_CostDetail
|
||||||
* @param M_Product_ID product
|
* @param M_Product_ID product
|
||||||
* @param M_AttributeSetInstance_ID asi
|
* @param M_AttributeSetInstance_ID asi
|
||||||
* @param M_CostElement_ID optional cost element for Freight
|
* @param M_CostElement_ID optional cost element for Freight
|
||||||
* @param Amt amt
|
* @param amt Amount
|
||||||
* @param Qty qty
|
* @param qty Quantity
|
||||||
* @param Description optional description
|
* @param description optional description
|
||||||
* @param trxName transaction
|
* @param trxName transaction
|
||||||
*/
|
*/
|
||||||
public MCostDetail (MAcctSchema as, int AD_Org_ID,
|
public MCostDetail (MAcctSchema as, int AD_Org_ID,
|
||||||
int M_Product_ID, int M_AttributeSetInstance_ID,
|
int M_Product_ID, int M_AttributeSetInstance_ID,
|
||||||
int M_CostElement_ID, BigDecimal Amt, BigDecimal Qty,
|
int M_CostElement_ID, BigDecimal amt, BigDecimal qty,
|
||||||
String Description, String trxName)
|
String description, String trxName)
|
||||||
{
|
{
|
||||||
this (as.getCtx(), 0, trxName);
|
this (as.getCtx(), 0, trxName);
|
||||||
setClientOrg(as.getAD_Client_ID(), AD_Org_ID);
|
setClientOrg(as.getAD_Client_ID(), AD_Org_ID);
|
||||||
|
@ -749,10 +770,9 @@ public class MCostDetail extends X_M_CostDetail
|
||||||
setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID);
|
setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID);
|
||||||
//
|
//
|
||||||
setM_CostElement_ID(M_CostElement_ID);
|
setM_CostElement_ID(M_CostElement_ID);
|
||||||
//
|
setAmt (amt);
|
||||||
setAmt (Amt);
|
setQty (qty);
|
||||||
setQty (Qty);
|
setDescription(description);
|
||||||
setDescription(Description);
|
|
||||||
} // MCostDetail
|
} // MCostDetail
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -121,7 +121,14 @@ public class ProductCost
|
||||||
m_C_UOM_ID = C_UOM_ID;
|
m_C_UOM_ID = C_UOM_ID;
|
||||||
} // setQty
|
} // setQty
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return qty
|
||||||
|
*/
|
||||||
|
public BigDecimal getQty()
|
||||||
|
{
|
||||||
|
return m_qty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Product Revenue Acct */
|
/** Product Revenue Acct */
|
||||||
|
|
|
@ -381,6 +381,24 @@ public final class DictionaryIDs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum M_CostElement {
|
||||||
|
MATERIAL(100),
|
||||||
|
FREIGHT(101),
|
||||||
|
FIFO(102),
|
||||||
|
AVERAGE_PO(103),
|
||||||
|
AVERAGE_INVOICE(104),
|
||||||
|
LABOR(105),
|
||||||
|
BURDEN(50000),
|
||||||
|
OVERHEAD(50001),
|
||||||
|
OUTSIDE_PROCESSING(50002);
|
||||||
|
|
||||||
|
public final int id;
|
||||||
|
|
||||||
|
private M_CostElement(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum M_DiscountSchema {
|
public enum M_DiscountSchema {
|
||||||
SALES_2001(100),
|
SALES_2001(100),
|
||||||
PURCHASE_2001(101),
|
PURCHASE_2001(101),
|
||||||
|
|
|
@ -0,0 +1,972 @@
|
||||||
|
/***********************************************************************
|
||||||
|
* This file is part of iDempiere ERP Open Source *
|
||||||
|
* http://www.idempiere.org *
|
||||||
|
* *
|
||||||
|
* Copyright (C) Contributors *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License *
|
||||||
|
* as published by the Free Software Foundation; either version 2 *
|
||||||
|
* of the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License *
|
||||||
|
* along with this program; if not, write to the Free Software *
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||||
|
* MA 02110-1301, USA. *
|
||||||
|
* *
|
||||||
|
* Contributors: *
|
||||||
|
* - hengsin *
|
||||||
|
**********************************************************************/
|
||||||
|
package org.idempiere.test.costing;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import org.compiere.model.MAcctSchema;
|
||||||
|
import org.compiere.model.MAttributeSet;
|
||||||
|
import org.compiere.model.MAttributeSetExclude;
|
||||||
|
import org.compiere.model.MAttributeSetInstance;
|
||||||
|
import org.compiere.model.MBPartner;
|
||||||
|
import org.compiere.model.MClient;
|
||||||
|
import org.compiere.model.MCost;
|
||||||
|
import org.compiere.model.MCostDetail;
|
||||||
|
import org.compiere.model.MCostElement;
|
||||||
|
import org.compiere.model.MInOut;
|
||||||
|
import org.compiere.model.MInOutLine;
|
||||||
|
import org.compiere.model.MInOutLineMA;
|
||||||
|
import org.compiere.model.MInventory;
|
||||||
|
import org.compiere.model.MInventoryLine;
|
||||||
|
import org.compiere.model.MOrder;
|
||||||
|
import org.compiere.model.MOrderLandedCost;
|
||||||
|
import org.compiere.model.MOrderLine;
|
||||||
|
import org.compiere.model.MPriceList;
|
||||||
|
import org.compiere.model.MPriceListVersion;
|
||||||
|
import org.compiere.model.MProcess;
|
||||||
|
import org.compiere.model.MProduct;
|
||||||
|
import org.compiere.model.MProductCategory;
|
||||||
|
import org.compiere.model.MProductCategoryAcct;
|
||||||
|
import org.compiere.model.MProductPrice;
|
||||||
|
import org.compiere.model.MProduction;
|
||||||
|
import org.compiere.model.MProductionLine;
|
||||||
|
import org.compiere.model.MProject;
|
||||||
|
import org.compiere.model.MProjectIssue;
|
||||||
|
import org.compiere.model.MStorageOnHand;
|
||||||
|
import org.compiere.process.DocAction;
|
||||||
|
import org.compiere.process.DocumentEngine;
|
||||||
|
import org.compiere.process.ProcessInfo;
|
||||||
|
import org.compiere.process.ServerProcessCtl;
|
||||||
|
import org.compiere.util.DB;
|
||||||
|
import org.compiere.util.Env;
|
||||||
|
import org.compiere.util.TimeUtil;
|
||||||
|
import org.compiere.wf.MWorkflow;
|
||||||
|
import org.eevolution.model.MPPProductBOM;
|
||||||
|
import org.eevolution.model.MPPProductBOMLine;
|
||||||
|
import org.idempiere.test.AbstractTestCase;
|
||||||
|
import org.idempiere.test.DictionaryIDs;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.parallel.Isolated;
|
||||||
|
|
||||||
|
@Isolated
|
||||||
|
public class AveragePOCostingTest extends AbstractTestCase {
|
||||||
|
|
||||||
|
public AveragePOCostingTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMaterialReceipt() {
|
||||||
|
MProduct product = null;
|
||||||
|
MClient client = MClient.get(Env.getCtx());
|
||||||
|
MAcctSchema as = client.getAcctSchema();
|
||||||
|
assertEquals(as.getCostingMethod(), MCostElement.COSTINGMETHOD_AveragePO, "Default costing method not Average PO");
|
||||||
|
|
||||||
|
try {
|
||||||
|
product = new MProduct(Env.getCtx(), 0, null);
|
||||||
|
product.setM_Product_Category_ID(DictionaryIDs.M_Product_Category.CHEMICALS.id);
|
||||||
|
product.setName("testMaterialReceipt");
|
||||||
|
product.setValue("testMaterialReceipt");
|
||||||
|
product.setProductType(MProduct.PRODUCTTYPE_Item);
|
||||||
|
product.setIsStocked(true);
|
||||||
|
product.setIsSold(true);
|
||||||
|
product.setIsPurchased(true);
|
||||||
|
product.setC_UOM_ID(DictionaryIDs.C_UOM.EACH.id);
|
||||||
|
product.setC_TaxCategory_ID(DictionaryIDs.C_TaxCategory.STANDARD.id);
|
||||||
|
product.saveEx();
|
||||||
|
|
||||||
|
MPriceListVersion plv = MPriceList.get(DictionaryIDs.M_PriceList.PURCHASE.id).getPriceListVersion(null);
|
||||||
|
MProductPrice pp = new MProductPrice(Env.getCtx(), 0, getTrxName());
|
||||||
|
pp.setM_PriceList_Version_ID(plv.getM_PriceList_Version_ID());
|
||||||
|
pp.setM_Product_ID(product.get_ID());
|
||||||
|
pp.setPriceStd(new BigDecimal("2"));
|
||||||
|
pp.setPriceList(new BigDecimal("2"));
|
||||||
|
pp.saveEx();
|
||||||
|
|
||||||
|
MInOutLine receiptLine = createPOAndMRForProduct(product.get_ID(), null, null);
|
||||||
|
|
||||||
|
product.set_TrxName(getTrxName());
|
||||||
|
MCost cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
assertNotNull(cost, "No MCost record found");
|
||||||
|
assertEquals(new BigDecimal("2.00"), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
|
||||||
|
|
||||||
|
MCostDetail cd = MCostDetail.get(Env.getCtx(), "C_OrderLine_ID=?", receiptLine.getC_OrderLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for material receipt line");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("2.00"), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
receiptLine = createPOAndMRForProduct(product.get_ID(), null, new BigDecimal("3.00"));
|
||||||
|
cost.load(getTrxName());
|
||||||
|
//(2+3)/2
|
||||||
|
assertEquals(new BigDecimal("2.50"), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "C_OrderLine_ID=?", receiptLine.getC_OrderLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for material receipt line");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("3.00"), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
//reverse MR2
|
||||||
|
MInOut receipt = new MInOut(Env.getCtx(), receiptLine.getM_InOut_ID(), getTrxName());
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
receipt.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, receipt.getDocStatus(), "Unexpected Document Status");
|
||||||
|
MInOut reverse = new MInOut(Env.getCtx(), receipt.getReversal_ID(), getTrxName());
|
||||||
|
if (!reverse.isPosted()) {
|
||||||
|
String error = DocumentEngine.postImmediate(Env.getCtx(), reverse.getAD_Client_ID(), reverse.get_Table_ID(), reverse.get_ID(), false, getTrxName());
|
||||||
|
assertNull(error, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
//back to cost=2 after reversal
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(new BigDecimal("2.00"), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "C_OrderLine_ID=?", receiptLine.getC_OrderLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertEquals(0, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("0.00"), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
} finally {
|
||||||
|
rollback();
|
||||||
|
|
||||||
|
if (product != null) {
|
||||||
|
product.set_TrxName(null);
|
||||||
|
product.deleteEx(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShipment() {
|
||||||
|
MProduct product = new MProduct(Env.getCtx(), DictionaryIDs.M_Product.AZALEA_BUSH.id, getTrxName());
|
||||||
|
MClient client = MClient.get(Env.getCtx());
|
||||||
|
MAcctSchema as = client.getAcctSchema();
|
||||||
|
assertEquals(as.getCostingMethod(), MCostElement.COSTINGMETHOD_AveragePO, "Default costing method not Average PO");
|
||||||
|
MCost cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
if (cost == null || cost.getCurrentCostPrice().signum() == 0) {
|
||||||
|
createPOAndMRForProduct(DictionaryIDs.M_Product.AZALEA_BUSH.id, null, new BigDecimal("5.00"));
|
||||||
|
cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
}
|
||||||
|
BigDecimal currentCost = cost.getCurrentCostPrice();
|
||||||
|
|
||||||
|
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||||
|
order.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id));
|
||||||
|
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
|
||||||
|
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
|
||||||
|
order.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
order.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
|
||||||
|
order.setDatePromised(today);
|
||||||
|
order.saveEx();
|
||||||
|
|
||||||
|
MOrderLine line1 = new MOrderLine(order);
|
||||||
|
line1.setLine(10);
|
||||||
|
line1.setProduct(MProduct.get(Env.getCtx(), DictionaryIDs.M_Product.AZALEA_BUSH.id));
|
||||||
|
line1.setQty(new BigDecimal("1"));
|
||||||
|
line1.setDatePromised(today);
|
||||||
|
line1.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
|
||||||
|
order.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, order.getDocStatus(), "Unexpected Document Status");
|
||||||
|
|
||||||
|
MInOut shipment = new MInOut(order, DictionaryIDs.C_DocType.MM_SHIPMENT.id, order.getDateOrdered());
|
||||||
|
shipment.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
shipment.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
shipment.saveEx();
|
||||||
|
|
||||||
|
MInOutLine shipmentLine = new MInOutLine(shipment);
|
||||||
|
shipmentLine.setOrderLine(line1, 0, new BigDecimal("1"));
|
||||||
|
shipmentLine.setQty(new BigDecimal("1"));
|
||||||
|
shipmentLine.saveEx();
|
||||||
|
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(shipment, DocAction.ACTION_Complete);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
shipment.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, shipment.getDocStatus(), "Unexpected Document Status");
|
||||||
|
|
||||||
|
//cost shouldn't change after shipment
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
|
||||||
|
|
||||||
|
MCostDetail cd = MCostDetail.get(Env.getCtx(), "M_InOutLine_ID=?", shipmentLine.get_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for shipment line");
|
||||||
|
assertEquals(-1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(currentCost.negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
//reverse shipment
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(shipment, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
shipment.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, shipment.getDocStatus(), "Unexpected Document Status");
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
|
||||||
|
|
||||||
|
MInOut reversal = new MInOut(Env.getCtx(), shipment.getReversal_ID(), getTrxName());
|
||||||
|
MInOutLine[] reversalLines = reversal.getLines();
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_InOutLine_ID=?", reversalLines[0].get_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInternalUse() {
|
||||||
|
MClient client = MClient.get(Env.getCtx());
|
||||||
|
MAcctSchema as = client.getAcctSchema();
|
||||||
|
assertEquals(as.getCostingMethod(), MCostElement.COSTINGMETHOD_AveragePO, "Default costing method not Average PO");
|
||||||
|
MProduct product = new MProduct(Env.getCtx(), DictionaryIDs.M_Product.MULCH.id, getTrxName());
|
||||||
|
MCost cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
if (cost == null || cost.getCurrentCostPrice().signum() == 0 || cost.getCurrentQty().signum() == 0) {
|
||||||
|
createPOAndMRForProduct(DictionaryIDs.M_Product.MULCH.id, null, null);
|
||||||
|
cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
}
|
||||||
|
assertNotNull(cost, "No MCost Record");
|
||||||
|
BigDecimal currentCost = cost.getCurrentCostPrice();
|
||||||
|
|
||||||
|
MInventory inventory = new MInventory(Env.getCtx(), 0, getTrxName());
|
||||||
|
inventory.setC_DocType_ID(DictionaryIDs.C_DocType.INTERNAL_USE_INVENTORY.id);
|
||||||
|
inventory.saveEx();
|
||||||
|
|
||||||
|
MInventoryLine line = new MInventoryLine(Env.getCtx(), 0, getTrxName());
|
||||||
|
line.setM_Inventory_ID(inventory.get_ID());
|
||||||
|
line.setM_Product_ID(DictionaryIDs.M_Product.MULCH.id);
|
||||||
|
line.setQtyInternalUse(new BigDecimal("1.00"));
|
||||||
|
line.setC_Charge_ID(DictionaryIDs.C_Charge.COMMISSIONS.id);
|
||||||
|
line.setM_Locator_ID(DictionaryIDs.M_Locator.HQ.id);
|
||||||
|
line.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(inventory, DocAction.ACTION_Complete);
|
||||||
|
inventory.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, inventory.getDocStatus(), "Unexpected Document Status");
|
||||||
|
|
||||||
|
MCostDetail cd = MCostDetail.get(Env.getCtx(), "M_InventoryLine_ID=?", line.getM_InventoryLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for internal use line");
|
||||||
|
assertEquals(-1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(currentCost.negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
|
||||||
|
//reverse internal use
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(inventory, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
inventory.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, inventory.getDocStatus(), "Unexpected Document Status");
|
||||||
|
|
||||||
|
MInventory reversal = new MInventory(Env.getCtx(), inventory.getReversal_ID(), getTrxName());
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_InventoryLine_ID=?", reversal.getLines(false)[0].getM_InventoryLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for internal use line");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProjectIssue() {
|
||||||
|
MClient client = MClient.get(Env.getCtx());
|
||||||
|
MAcctSchema as = client.getAcctSchema();
|
||||||
|
assertEquals(as.getCostingMethod(), MCostElement.COSTINGMETHOD_AveragePO, "Default costing method not Average PO");
|
||||||
|
MProduct product = new MProduct(Env.getCtx(), DictionaryIDs.M_Product.MULCH.id, getTrxName());
|
||||||
|
MCost cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
if (cost == null || cost.getCurrentCostPrice().signum() == 0 || cost.getCurrentQty().signum() == 0) {
|
||||||
|
createPOAndMRForProduct(DictionaryIDs.M_Product.MULCH.id, null, null);
|
||||||
|
cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
}
|
||||||
|
assertNotNull(cost, "No MCost Record");
|
||||||
|
BigDecimal currentCost = cost.getCurrentCostPrice();
|
||||||
|
|
||||||
|
MProject project = new MProject(Env.getCtx(), 0, getTrxName());
|
||||||
|
project.setName("testProjectIssue");
|
||||||
|
project.setC_Currency_ID(DictionaryIDs.C_Currency.USD.id);
|
||||||
|
project.saveEx();
|
||||||
|
|
||||||
|
MProjectIssue issue = new MProjectIssue(project);
|
||||||
|
issue.setM_Product_ID(DictionaryIDs.M_Product.MULCH.id);
|
||||||
|
issue.setLine(10);
|
||||||
|
issue.setM_Locator_ID(DictionaryIDs.M_Locator.HQ.id);
|
||||||
|
issue.setMovementQty(new BigDecimal("1.00"));
|
||||||
|
issue.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(issue, DocAction.ACTION_Complete);
|
||||||
|
issue.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, issue.getDocStatus(), "Unexpected Document Status");
|
||||||
|
if (!issue.isPosted()) {
|
||||||
|
String error = DocumentEngine.postImmediate(Env.getCtx(), issue.getAD_Client_ID(), issue.get_Table_ID(), issue.get_ID(), false, getTrxName());
|
||||||
|
assertNull(error, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
MCostDetail cd = MCostDetail.get(Env.getCtx(), "C_ProjectIssue_ID=?", issue.getC_ProjectIssue_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for project issue line");
|
||||||
|
assertEquals(-1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(currentCost.negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
|
||||||
|
//reverse issue
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(issue, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
issue.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, issue.getDocStatus(), "Unexpected Document Status");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "C_ProjectIssue_ID=?", issue.getReversal_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for project issue line");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProduction() {
|
||||||
|
MProduct mulch = new MProduct(Env.getCtx(), DictionaryIDs.M_Product.MULCH.id, getTrxName());
|
||||||
|
MProduct azb = new MProduct(Env.getCtx(), DictionaryIDs.M_Product.AZALEA_BUSH.id, getTrxName());
|
||||||
|
|
||||||
|
MProduct mulchX = new MProduct(Env.getCtx(), 0, null);
|
||||||
|
mulchX.setName("MulchX2");
|
||||||
|
mulchX.setIsBOM(true);
|
||||||
|
mulchX.setIsStocked(true);
|
||||||
|
mulchX.setC_UOM_ID(mulch.getC_UOM_ID());
|
||||||
|
mulchX.setM_Product_Category_ID(mulch.getM_Product_Category_ID());
|
||||||
|
mulchX.setProductType(mulch.getProductType());
|
||||||
|
mulchX.setM_AttributeSet_ID(mulch.getM_AttributeSet_ID());
|
||||||
|
mulchX.setC_TaxCategory_ID(mulch.getC_TaxCategory_ID());
|
||||||
|
mulchX.saveEx();
|
||||||
|
|
||||||
|
try {
|
||||||
|
MClient client = MClient.get(Env.getCtx());
|
||||||
|
MAcctSchema as = client.getAcctSchema();
|
||||||
|
assertEquals(as.getCostingMethod(), MCostElement.COSTINGMETHOD_AveragePO, "Default costing method not Average PO");
|
||||||
|
MCost cost = mulch.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
if (cost == null || cost.getCurrentCostPrice().signum() == 0 || cost.getCurrentQty().signum() == 0) {
|
||||||
|
createPOAndMRForProduct(DictionaryIDs.M_Product.MULCH.id, null, null);
|
||||||
|
cost = mulch.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
}
|
||||||
|
BigDecimal mulchCost = cost.getCurrentCostPrice();
|
||||||
|
cost = azb.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
if (cost == null || cost.getCurrentCostPrice().signum() == 0 || cost.getCurrentQty().signum() == 0) {
|
||||||
|
createPOAndMRForProduct(DictionaryIDs.M_Product.AZALEA_BUSH.id, null, null);
|
||||||
|
cost = azb.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
}
|
||||||
|
BigDecimal azbCost = cost.getCurrentCostPrice();
|
||||||
|
|
||||||
|
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
|
||||||
|
bom.setM_Product_ID(mulchX.get_ID());
|
||||||
|
bom.setBOMType(MPPProductBOM.BOMTYPE_CurrentActive);
|
||||||
|
bom.setBOMUse(MPPProductBOM.BOMUSE_Master);
|
||||||
|
bom.setName(mulchX.getName());
|
||||||
|
bom.saveEx();
|
||||||
|
|
||||||
|
MPPProductBOMLine line1 = new MPPProductBOMLine(bom);
|
||||||
|
line1.setM_Product_ID(DictionaryIDs.M_Product.MULCH.id);
|
||||||
|
line1.setQtyBOM(new BigDecimal("1"));
|
||||||
|
line1.saveEx();
|
||||||
|
|
||||||
|
MPPProductBOMLine line2 = new MPPProductBOMLine(bom);
|
||||||
|
line2.setM_Product_ID(DictionaryIDs.M_Product.AZALEA_BUSH.id);
|
||||||
|
line2.setQtyBOM(new BigDecimal("1"));
|
||||||
|
line2.saveEx();
|
||||||
|
|
||||||
|
mulchX.load((String)null);
|
||||||
|
mulchX.setIsVerified(true);
|
||||||
|
mulchX.saveEx();
|
||||||
|
|
||||||
|
MProduction production = new MProduction(Env.getCtx(), 0, getTrxName());
|
||||||
|
production.setM_Product_ID(mulchX.get_ID());
|
||||||
|
production.setM_Locator_ID(DictionaryIDs.M_Locator.HQ.id);
|
||||||
|
production.setIsUseProductionPlan(false);
|
||||||
|
production.setMovementDate(getLoginDate());
|
||||||
|
production.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
production.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
production.setIsComplete(false);
|
||||||
|
production.setProductionQty(new BigDecimal("1"));
|
||||||
|
production.setPP_Product_BOM_ID(bom.getPP_Product_BOM_ID());
|
||||||
|
production.saveEx();
|
||||||
|
|
||||||
|
int productionCreate = 53226;
|
||||||
|
MProcess process = MProcess.get(Env.getCtx(), productionCreate);
|
||||||
|
ProcessInfo pi = new ProcessInfo(process.getName(), process.get_ID());
|
||||||
|
pi.setAD_Client_ID(getAD_Client_ID());
|
||||||
|
pi.setAD_User_ID(getAD_User_ID());
|
||||||
|
pi.setRecord_ID(production.get_ID());
|
||||||
|
pi.setTransactionName(getTrxName());
|
||||||
|
ServerProcessCtl.process(pi, getTrx(), false);
|
||||||
|
assertFalse(pi.isError(), pi.getSummary());
|
||||||
|
|
||||||
|
production.load(getTrxName());
|
||||||
|
MProductionLine[] plines = production.getLines();
|
||||||
|
assertEquals("Y", production.getIsCreated(), "MProduction.IsCreated != Y");
|
||||||
|
assertTrue(plines.length > 0, "No Production Lines");
|
||||||
|
assertEquals(3, plines.length, "Unexpected number of production lines");
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(production, DocAction.ACTION_Complete);
|
||||||
|
production.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, production.getDocStatus(), "Production Status="+production.getDocStatus());
|
||||||
|
|
||||||
|
BigDecimal rollup = mulchCost.add(azbCost);
|
||||||
|
MCostDetail cd = MCostDetail.get(Env.getCtx(), "M_ProductionLine_ID=?", plines[0].getM_ProductionLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for project issue line");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(rollup.setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
mulchX.set_TrxName(getTrxName());
|
||||||
|
cost = mulchX.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
assertEquals(rollup.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_ProductionLine_ID=?", plines[1].getM_ProductionLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for project issue line");
|
||||||
|
assertEquals(-1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(mulchCost.negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost = mulch.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
assertEquals(mulchCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_ProductionLine_ID=?", plines[2].getM_ProductionLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for project issue line");
|
||||||
|
assertEquals(-1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(azbCost.negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost = azb.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
assertEquals(azbCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
|
||||||
|
//reverse production
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(production, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
production.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, production.getDocStatus(), "Production Status="+production.getDocStatus());
|
||||||
|
|
||||||
|
MProduction reversal = new MProduction(Env.getCtx(), production.getReversal_ID(), getTrxName());
|
||||||
|
MProductionLine[] reversalLines = reversal.getLines();
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_ProductionLine_ID=?", reversalLines[0].getM_ProductionLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for project issue line");
|
||||||
|
assertEquals(-1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(rollup.negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_ProductionLine_ID=?", reversalLines[1].getM_ProductionLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for project issue line");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(mulchCost.setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost = mulch.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
assertEquals(mulchCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_ProductionLine_ID=?", reversalLines[2].getM_ProductionLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for project issue line");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(azbCost.setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost = azb.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
} finally {
|
||||||
|
rollback();
|
||||||
|
DB.executeUpdateEx("delete from m_cost where m_product_id=?", new Object[] {mulchX.get_ID()}, null);
|
||||||
|
mulchX.set_TrxName(null);
|
||||||
|
mulchX.deleteEx(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMRAndShipmentByLot() {
|
||||||
|
MClient client = MClient.get(Env.getCtx());
|
||||||
|
MAcctSchema as = client.getAcctSchema();
|
||||||
|
|
||||||
|
MProductCategory lotLevel = new MProductCategory(Env.getCtx(), 0, null);
|
||||||
|
lotLevel.setName("testMaterialReceiptLot");
|
||||||
|
lotLevel.saveEx();
|
||||||
|
|
||||||
|
MProduct product = null;
|
||||||
|
MAttributeSetExclude exclude = null;
|
||||||
|
MAttributeSetExclude exclude1 = null;
|
||||||
|
try {
|
||||||
|
MAttributeSet mas = new MAttributeSet(Env.getCtx(), DictionaryIDs.M_AttributeSet.FERTILIZER_LOT.id, getTrxName());
|
||||||
|
mas.setMandatoryType(MAttributeSet.MANDATORYTYPE_NotMandatory);
|
||||||
|
mas.saveEx();
|
||||||
|
|
||||||
|
exclude = new MAttributeSetExclude(Env.getCtx(), 0, null);
|
||||||
|
exclude.setM_AttributeSet_ID(mas.get_ID());
|
||||||
|
exclude.setAD_Table_ID(MOrderLine.Table_ID);
|
||||||
|
exclude.setIsSOTrx(true);
|
||||||
|
exclude.saveEx();
|
||||||
|
|
||||||
|
exclude1 = new MAttributeSetExclude(Env.getCtx(), 0, null);
|
||||||
|
exclude1.setM_AttributeSet_ID(mas.get_ID());
|
||||||
|
exclude1.setAD_Table_ID(MInOutLine.Table_ID);
|
||||||
|
exclude1.setIsSOTrx(true);
|
||||||
|
exclude1.saveEx();
|
||||||
|
|
||||||
|
MProductCategoryAcct lotLevelAcct = MProductCategoryAcct.get(lotLevel.get_ID(), as.get_ID());
|
||||||
|
lotLevelAcct = new MProductCategoryAcct(Env.getCtx(), lotLevelAcct);
|
||||||
|
lotLevelAcct.setCostingLevel(MAcctSchema.COSTINGLEVEL_BatchLot);
|
||||||
|
lotLevelAcct.saveEx();
|
||||||
|
|
||||||
|
product = new MProduct(Env.getCtx(), 0, null);
|
||||||
|
product.setM_Product_Category_ID(lotLevel.get_ID());
|
||||||
|
product.setName("testMaterialReceiptLot");
|
||||||
|
product.setProductType(MProduct.PRODUCTTYPE_Item);
|
||||||
|
product.setIsStocked(true);
|
||||||
|
product.setIsSold(true);
|
||||||
|
product.setIsPurchased(true);
|
||||||
|
product.setC_UOM_ID(DictionaryIDs.C_UOM.EACH.id);
|
||||||
|
product.setC_TaxCategory_ID(DictionaryIDs.C_TaxCategory.STANDARD.id);
|
||||||
|
product.setM_AttributeSet_ID(DictionaryIDs.M_AttributeSet.FERTILIZER_LOT.id);
|
||||||
|
product.saveEx();
|
||||||
|
|
||||||
|
MPriceListVersion plv = MPriceList.get(DictionaryIDs.M_PriceList.PURCHASE.id).getPriceListVersion(null);
|
||||||
|
MProductPrice pp = new MProductPrice(Env.getCtx(), 0, getTrxName());
|
||||||
|
pp.setM_PriceList_Version_ID(plv.getM_PriceList_Version_ID());
|
||||||
|
pp.setM_Product_ID(product.get_ID());
|
||||||
|
pp.setPriceStd(new BigDecimal("2"));
|
||||||
|
pp.setPriceList(new BigDecimal("2"));
|
||||||
|
pp.saveEx();
|
||||||
|
|
||||||
|
MAttributeSetInstance asi1 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||||
|
asi1.setM_AttributeSet_ID(DictionaryIDs.M_AttributeSet.FERTILIZER_LOT.id);
|
||||||
|
asi1.setLot("Lot1");
|
||||||
|
asi1.saveEx();
|
||||||
|
MInOutLine line1 = createPOAndMRForProduct(product.get_ID(), asi1, new BigDecimal("2.00"));
|
||||||
|
|
||||||
|
MAttributeSetInstance asi2 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||||
|
asi2.setM_AttributeSet_ID(DictionaryIDs.M_AttributeSet.FERTILIZER_LOT.id);
|
||||||
|
asi2.setLot("Lot2");
|
||||||
|
asi2.saveEx();
|
||||||
|
MInOutLine line2 = createPOAndMRForProduct(product.get_ID(), asi2, new BigDecimal("3.00"));
|
||||||
|
|
||||||
|
MCostDetail cd = MCostDetail.get(Env.getCtx(), "C_OrderLine_ID=?", line1.getC_OrderLine_ID(), asi1.get_ID(), as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for order line1");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("2").setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "C_OrderLine_ID=?", line2.getC_OrderLine_ID(), asi2.get_ID(), as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for order line1");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("3").setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
product.set_TrxName(getTrxName());
|
||||||
|
MCost cost1 = product.getCostingRecord(as, getAD_Org_ID(), asi1.get_ID(), as.getCostingMethod());
|
||||||
|
assertNotNull(cost1, "MCost record not found");
|
||||||
|
assertEquals(new BigDecimal("2.00"), cost1.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP));
|
||||||
|
|
||||||
|
MCost cost2 = product.getCostingRecord(as, getAD_Org_ID(), asi2.get_ID(), as.getCostingMethod());
|
||||||
|
assertNotNull(cost2, "MCost record not found");
|
||||||
|
assertEquals(new BigDecimal("3.00"), cost2.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP));
|
||||||
|
|
||||||
|
//shipment
|
||||||
|
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||||
|
order.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id));
|
||||||
|
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
|
||||||
|
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
|
||||||
|
order.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
order.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
|
||||||
|
order.setDatePromised(today);
|
||||||
|
order.saveEx();
|
||||||
|
|
||||||
|
MOrderLine ol1 = new MOrderLine(order);
|
||||||
|
ol1.setLine(10);
|
||||||
|
//Azalea Bush
|
||||||
|
ol1.setProduct(product);
|
||||||
|
ol1.setQty(new BigDecimal("2"));
|
||||||
|
ol1.setDatePromised(today);
|
||||||
|
ol1.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
|
||||||
|
order.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, order.getDocStatus(), "Unexpected document status");
|
||||||
|
|
||||||
|
MInOut shipment = new MInOut(order, DictionaryIDs.C_DocType.MM_SHIPMENT.id, order.getDateOrdered());
|
||||||
|
shipment.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
shipment.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
shipment.saveEx();
|
||||||
|
|
||||||
|
MInOutLine shipmentLine = new MInOutLine(shipment);
|
||||||
|
shipmentLine.setOrderLine(ol1, 0, new BigDecimal("2"));
|
||||||
|
shipmentLine.setQty(new BigDecimal("2"));
|
||||||
|
shipmentLine.saveEx();
|
||||||
|
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(shipment, DocAction.ACTION_Complete);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
shipment.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, shipment.getDocStatus(), "Unexpected document status");
|
||||||
|
|
||||||
|
MInOutLineMA[] linema = MInOutLineMA.get(Env.getCtx(), shipmentLine.get_ID(), getTrxName());
|
||||||
|
assertEquals(2, linema.length, "Unexpected number of MInOutLineMA records");
|
||||||
|
assertEquals(linema[0].getM_AttributeSetInstance_ID(), asi1.get_ID(), "Unexpected M_AttributeSetInstance_ID for MInOutLineMA 1");
|
||||||
|
assertEquals(linema[1].getM_AttributeSetInstance_ID(), asi2.get_ID(), "Unexpected M_AttributeSetInstance_ID for MInOutLineMA 2");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_InOutLine_ID=?", shipmentLine.getM_InOutLine_ID(), linema[0].getM_AttributeSetInstance_ID(), as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for order line1");
|
||||||
|
assertEquals(-1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("2").negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_InOutLine_ID=?", shipmentLine.getM_InOutLine_ID(), linema[1].getM_AttributeSetInstance_ID(), as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for order line1");
|
||||||
|
assertEquals(-1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("3").negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
//reverse shipment
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(shipment, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
shipment.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, shipment.getDocStatus(), "Unexpected document status");
|
||||||
|
|
||||||
|
MInOut reversal = new MInOut(Env.getCtx(), shipment.getReversal_ID(), getTrxName());
|
||||||
|
MInOutLine[] reversalLines = reversal.getLines();
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_InOutLine_ID=?", reversalLines[0].getM_InOutLine_ID(), asi1.getM_AttributeSetInstance_ID(), as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for order line1");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("2").setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_InOutLine_ID=?", reversalLines[0].getM_InOutLine_ID(), asi2.getM_AttributeSetInstance_ID(), as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for order line1");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("3").setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
|
||||||
|
cost1 = product.getCostingRecord(as, getAD_Org_ID(), asi1.get_ID(), as.getCostingMethod());
|
||||||
|
assertNotNull(cost1, "MCost record not found");
|
||||||
|
assertEquals(new BigDecimal("2.00"), cost1.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP));
|
||||||
|
|
||||||
|
cost2 = product.getCostingRecord(as, getAD_Org_ID(), asi2.get_ID(), as.getCostingMethod());
|
||||||
|
assertNotNull(cost2, "MCost record not found");
|
||||||
|
assertEquals(new BigDecimal("3.00"), cost2.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP));
|
||||||
|
|
||||||
|
//reverse MR2
|
||||||
|
MInOut mr2 = new MInOut(Env.getCtx(), line2.getM_InOut_ID(), getTrxName());
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(mr2, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
mr2.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, mr2.getDocStatus(), "Unexpected document status");
|
||||||
|
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "C_OrderLine_ID=?", line2.getC_OrderLine_ID(), asi2.getM_AttributeSetInstance_ID(), as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for order line2");
|
||||||
|
assertEquals(0, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("0").setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
} finally {
|
||||||
|
rollback();
|
||||||
|
|
||||||
|
if (exclude != null)
|
||||||
|
exclude.deleteEx(true);
|
||||||
|
|
||||||
|
if (exclude1 != null)
|
||||||
|
exclude1.deleteEx(true);
|
||||||
|
|
||||||
|
if (product != null) {
|
||||||
|
product.set_TrxName(null);
|
||||||
|
product.deleteEx(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
lotLevel.deleteEx(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCostAdjustment() {
|
||||||
|
MClient client = MClient.get(Env.getCtx());
|
||||||
|
MAcctSchema as = client.getAcctSchema();
|
||||||
|
assertEquals(as.getCostingMethod(), MCostElement.COSTINGMETHOD_AveragePO, "Default costing method not Average PO");
|
||||||
|
MProduct product = new MProduct(Env.getCtx(), DictionaryIDs.M_Product.MULCH.id, getTrxName());
|
||||||
|
MCost cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
if (cost == null || cost.getCurrentCostPrice().signum() == 0 || cost.getCurrentQty().signum() == 0) {
|
||||||
|
createPOAndMRForProduct(DictionaryIDs.M_Product.MULCH.id, null, null);
|
||||||
|
cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
}
|
||||||
|
assertNotNull(cost, "No MCost Record");
|
||||||
|
BigDecimal currentCost = cost.getCurrentCostPrice();
|
||||||
|
BigDecimal adjustment = new BigDecimal("0.25");
|
||||||
|
|
||||||
|
MInventory inventory = new MInventory(Env.getCtx(), 0, getTrxName());
|
||||||
|
inventory.setC_DocType_ID(DictionaryIDs.C_DocType.COST_ADJUSTMENT.id);
|
||||||
|
inventory.setC_Currency_ID(as.getC_Currency_ID());
|
||||||
|
inventory.setCostingMethod(as.getCostingMethod());
|
||||||
|
inventory.saveEx();
|
||||||
|
|
||||||
|
MInventoryLine line = new MInventoryLine(Env.getCtx(), 0, getTrxName());
|
||||||
|
line.setM_Inventory_ID(inventory.get_ID());
|
||||||
|
line.setM_Product_ID(DictionaryIDs.M_Product.MULCH.id);
|
||||||
|
line.setC_Charge_ID(DictionaryIDs.C_Charge.COMMISSIONS.id);
|
||||||
|
line.setCurrentCostPrice(currentCost);
|
||||||
|
line.setNewCostPrice(currentCost.add(adjustment));
|
||||||
|
line.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(inventory, DocAction.ACTION_Complete);
|
||||||
|
inventory.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, inventory.getDocStatus(), "Unexpected Document Status");
|
||||||
|
|
||||||
|
MCostDetail cd = MCostDetail.get(Env.getCtx(), "M_InventoryLine_ID=?", line.getM_InventoryLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for internal use line");
|
||||||
|
assertEquals(0, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(adjustment.setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.add(adjustment).setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
|
||||||
|
//reverse cost adjustment
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(inventory, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
inventory.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, inventory.getDocStatus(), "Unexpected Document Status");
|
||||||
|
|
||||||
|
MInventory reversal = new MInventory(Env.getCtx(), inventory.getReversal_ID(), getTrxName());
|
||||||
|
MInventoryLine[] reversalLines = reversal.getLines(true);
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_InventoryLine_ID=?", reversalLines[0].getM_InventoryLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for internal use line");
|
||||||
|
assertEquals(0, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(adjustment.negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPhysicalInventory() {
|
||||||
|
MClient client = MClient.get(Env.getCtx());
|
||||||
|
MAcctSchema as = client.getAcctSchema();
|
||||||
|
assertEquals(as.getCostingMethod(), MCostElement.COSTINGMETHOD_AveragePO, "Default costing method not Average PO");
|
||||||
|
MProduct product = new MProduct(Env.getCtx(), DictionaryIDs.M_Product.MULCH.id, getTrxName());
|
||||||
|
createPOAndMRForProduct(DictionaryIDs.M_Product.MULCH.id, null, null);
|
||||||
|
MCost cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
assertNotNull(cost, "No MCost Record");
|
||||||
|
BigDecimal currentCost = cost.getCurrentCostPrice();
|
||||||
|
|
||||||
|
MInventory inventory = new MInventory(Env.getCtx(), 0, getTrxName());
|
||||||
|
inventory.setC_DocType_ID(DictionaryIDs.C_DocType.MATERIAL_PHYSICAL_INVENTORY.id);
|
||||||
|
inventory.saveEx();
|
||||||
|
|
||||||
|
MInventoryLine line = new MInventoryLine(Env.getCtx(), 0, getTrxName());
|
||||||
|
line.setM_Inventory_ID(inventory.get_ID());
|
||||||
|
line.setM_Product_ID(DictionaryIDs.M_Product.MULCH.id);
|
||||||
|
line.setM_Locator_ID(DictionaryIDs.M_Locator.HQ.id);
|
||||||
|
BigDecimal qtyOnHand = MStorageOnHand.getQtyOnHandForLocator(line.getM_Product_ID(), line.getM_Locator_ID(), 0, getTrxName());
|
||||||
|
line.setQtyBook(qtyOnHand);
|
||||||
|
line.setQtyCount(line.getQtyBook().add(new BigDecimal("1")));
|
||||||
|
line.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(inventory, DocAction.ACTION_Complete);
|
||||||
|
inventory.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, inventory.getDocStatus(), "Unexpected Document Status");
|
||||||
|
|
||||||
|
MCostDetail cd = MCostDetail.get(Env.getCtx(), "M_InventoryLine_ID=?", line.getM_InventoryLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for internal use line");
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
|
||||||
|
//reverse physical inventory
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(inventory, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
inventory.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, inventory.getDocStatus(), "Unexpected Document Status");
|
||||||
|
|
||||||
|
MInventory reversal = new MInventory(Env.getCtx(), inventory.getReversal_ID(), getTrxName());
|
||||||
|
MInventoryLine[] reversalLines = reversal.getLines(true);
|
||||||
|
cd = MCostDetail.get(Env.getCtx(), "M_InventoryLine_ID=?", reversalLines[0].getM_InventoryLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertNotNull(cd, "MCostDetail not found for internal use line");
|
||||||
|
assertEquals(-1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(currentCost.negate().setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
cost.load(getTrxName());
|
||||||
|
assertEquals(currentCost.setScale(2, RoundingMode.HALF_UP), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLandedCostForPO() {
|
||||||
|
MClient client = MClient.get(Env.getCtx());
|
||||||
|
MAcctSchema as = client.getAcctSchema();
|
||||||
|
assertEquals(as.getCostingMethod(), MCostElement.COSTINGMETHOD_AveragePO, "Default costing method not Average PO");
|
||||||
|
|
||||||
|
MProduct product = null;
|
||||||
|
try {
|
||||||
|
product = new MProduct(Env.getCtx(), 0, null);
|
||||||
|
product.setM_Product_Category_ID(DictionaryIDs.M_Product_Category.STANDARD.id);
|
||||||
|
product.setName("testLandedCostForPO");
|
||||||
|
product.setProductType(MProduct.PRODUCTTYPE_Item);
|
||||||
|
product.setIsStocked(true);
|
||||||
|
product.setIsSold(true);
|
||||||
|
product.setIsPurchased(true);
|
||||||
|
product.setC_UOM_ID(DictionaryIDs.C_UOM.EACH.id);
|
||||||
|
product.setC_TaxCategory_ID(DictionaryIDs.C_TaxCategory.STANDARD.id);
|
||||||
|
product.saveEx();
|
||||||
|
|
||||||
|
MPriceListVersion plv = MPriceList.get(DictionaryIDs.M_PriceList.PURCHASE.id).getPriceListVersion(null);
|
||||||
|
MProductPrice pp = new MProductPrice(Env.getCtx(), 0, getTrxName());
|
||||||
|
pp.setM_PriceList_Version_ID(plv.getM_PriceList_Version_ID());
|
||||||
|
pp.setM_Product_ID(product.get_ID());
|
||||||
|
pp.setPriceStd(new BigDecimal("2"));
|
||||||
|
pp.setPriceList(new BigDecimal("2"));
|
||||||
|
pp.saveEx();
|
||||||
|
|
||||||
|
//create order
|
||||||
|
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||||
|
order.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.PATIO.id));
|
||||||
|
order.setC_DocTypeTarget_ID(DictionaryIDs.C_DocType.PURCHASE_ORDER.id);
|
||||||
|
order.setIsSOTrx(false);
|
||||||
|
order.setSalesRep_ID(DictionaryIDs.AD_User.GARDEN_ADMIN.id);
|
||||||
|
order.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
order.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
|
||||||
|
order.setDateOrdered(today);
|
||||||
|
order.setDatePromised(today);
|
||||||
|
order.saveEx();
|
||||||
|
|
||||||
|
MOrderLine line1 = new MOrderLine(order);
|
||||||
|
line1.setLine(10);
|
||||||
|
line1.setProduct(new MProduct(Env.getCtx(), product.get_ID(), getTrxName()));
|
||||||
|
line1.setQty(new BigDecimal("1"));
|
||||||
|
line1.setDatePromised(today);
|
||||||
|
line1.setPrice(new BigDecimal("2"));
|
||||||
|
line1.saveEx();
|
||||||
|
|
||||||
|
MOrderLandedCost landedCost = new MOrderLandedCost(Env.getCtx(), 0, getTrxName());
|
||||||
|
landedCost.setC_Order_ID(order.get_ID());
|
||||||
|
landedCost.setLandedCostDistribution(MOrderLandedCost.LANDEDCOSTDISTRIBUTION_Costs);
|
||||||
|
landedCost.setM_CostElement_ID(DictionaryIDs.M_CostElement.FREIGHT.id);
|
||||||
|
landedCost.setAmt(new BigDecimal("0.30"));
|
||||||
|
landedCost.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
order.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
|
||||||
|
|
||||||
|
MInOut receipt1 = new MInOut(order, DictionaryIDs.C_DocType.MM_RECEIPT.id, order.getDateOrdered());
|
||||||
|
receipt1.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
receipt1.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
receipt1.saveEx();
|
||||||
|
|
||||||
|
MInOutLine receiptLine1 = new MInOutLine(receipt1);
|
||||||
|
receiptLine1.setOrderLine(line1, 0, new BigDecimal("1"));
|
||||||
|
receiptLine1.setQty(new BigDecimal("1"));
|
||||||
|
receiptLine1.saveEx();
|
||||||
|
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(receipt1, DocAction.ACTION_Complete);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
receipt1.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, receipt1.getDocStatus());
|
||||||
|
if (!receipt1.isPosted()) {
|
||||||
|
String error = DocumentEngine.postImmediate(Env.getCtx(), receipt1.getAD_Client_ID(), receipt1.get_Table_ID(), receipt1.get_ID(), false, getTrxName());
|
||||||
|
assertNull(error, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MCostDetail> cds = MCostDetail.list(Env.getCtx(), "C_OrderLine_ID=?", line1.getC_OrderLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertTrue(cds.size() == 2, "MCostDetail not found for order line1");
|
||||||
|
for(MCostDetail cd : cds) {
|
||||||
|
if (cd.getM_CostElement_ID() == 0) {
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("2.00").setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
} else if (cd.getM_CostElement_ID() == DictionaryIDs.M_CostElement.FREIGHT.id ) {
|
||||||
|
assertEquals(1, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("0.30").setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
product.set_TrxName(getTrxName());
|
||||||
|
MCost cost = product.getCostingRecord(as, getAD_Org_ID(), 0, as.getCostingMethod());
|
||||||
|
assertNotNull(cost, "No MCost record found");
|
||||||
|
assertEquals(new BigDecimal("2.30"), cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
|
||||||
|
|
||||||
|
//reverse receipt
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(receipt1, DocAction.ACTION_Reverse_Accrual);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
receipt1.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Reversed, receipt1.getDocStatus());
|
||||||
|
|
||||||
|
cds = MCostDetail.list(Env.getCtx(), "C_OrderLine_ID=?", line1.getC_OrderLine_ID(), 0, as.get_ID(), getTrxName());
|
||||||
|
assertTrue(cds.size() == 2, "MCostDetail not found for order line1");
|
||||||
|
for(MCostDetail cd : cds) {
|
||||||
|
if (cd.getM_CostElement_ID() == 0) {
|
||||||
|
assertEquals(0, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("0").setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
} else if (cd.getM_CostElement_ID() == DictionaryIDs.M_CostElement.FREIGHT.id ) {
|
||||||
|
assertEquals(0, cd.getQty().intValue(), "Unexpected MCostDetail Qty");
|
||||||
|
assertEquals(new BigDecimal("0").setScale(2, RoundingMode.HALF_UP), cd.getAmt().setScale(2, RoundingMode.HALF_UP), "Unexpected MCostDetail Amt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
rollback();
|
||||||
|
|
||||||
|
if (product != null) {
|
||||||
|
product.set_TrxName(null);
|
||||||
|
product.deleteEx(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private MInOutLine createPOAndMRForProduct(int productId, MAttributeSetInstance asi, BigDecimal price) {
|
||||||
|
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||||
|
order.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.PATIO.id));
|
||||||
|
order.setC_DocTypeTarget_ID(DictionaryIDs.C_DocType.PURCHASE_ORDER.id);
|
||||||
|
order.setIsSOTrx(false);
|
||||||
|
order.setSalesRep_ID(DictionaryIDs.AD_User.GARDEN_ADMIN.id);
|
||||||
|
order.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
order.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
|
||||||
|
order.setDateOrdered(today);
|
||||||
|
order.setDatePromised(today);
|
||||||
|
order.saveEx();
|
||||||
|
|
||||||
|
MOrderLine line1 = new MOrderLine(order);
|
||||||
|
line1.setLine(10);
|
||||||
|
line1.setProduct(new MProduct(Env.getCtx(), productId, getTrxName()));
|
||||||
|
line1.setQty(new BigDecimal("1"));
|
||||||
|
line1.setDatePromised(today);
|
||||||
|
if (price != null)
|
||||||
|
line1.setPrice(price);
|
||||||
|
if (asi != null)
|
||||||
|
line1.setM_AttributeSetInstance_ID(asi.getM_AttributeSetInstance_ID());
|
||||||
|
line1.saveEx();
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
order.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
|
||||||
|
|
||||||
|
MInOut receipt1 = new MInOut(order, DictionaryIDs.C_DocType.MM_RECEIPT.id, order.getDateOrdered());
|
||||||
|
receipt1.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
receipt1.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
receipt1.saveEx();
|
||||||
|
|
||||||
|
MInOutLine receiptLine1 = new MInOutLine(receipt1);
|
||||||
|
receiptLine1.setOrderLine(line1, 0, new BigDecimal("1"));
|
||||||
|
receiptLine1.setQty(new BigDecimal("1"));
|
||||||
|
if (asi != null)
|
||||||
|
receiptLine1.setM_AttributeSetInstance_ID(asi.get_ID());
|
||||||
|
receiptLine1.saveEx();
|
||||||
|
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(receipt1, DocAction.ACTION_Complete);
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
receipt1.load(getTrxName());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, receipt1.getDocStatus());
|
||||||
|
if (!receipt1.isPosted()) {
|
||||||
|
String error = DocumentEngine.postImmediate(Env.getCtx(), receipt1.getAD_Client_ID(), receipt1.get_Table_ID(), receipt1.get_ID(), false, getTrxName());
|
||||||
|
assertNull(error, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return receiptLine1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -122,9 +122,7 @@ public class ProductionTest extends AbstractTestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAutoProduce() {
|
public void testAutoProduce() {
|
||||||
int mulchId = 137;
|
MProduct mulch = MProduct.get(DictionaryIDs.M_Product.MULCH.id);
|
||||||
int joeBlock = 118;
|
|
||||||
MProduct mulch = MProduct.get(mulchId);
|
|
||||||
|
|
||||||
MProduct mulchX = new MProduct(Env.getCtx(), 0, null);
|
MProduct mulchX = new MProduct(Env.getCtx(), 0, null);
|
||||||
mulchX.setName("MulchX2");
|
mulchX.setName("MulchX2");
|
||||||
|
@ -139,7 +137,7 @@ public class ProductionTest extends AbstractTestCase {
|
||||||
mulchX.saveEx();
|
mulchX.saveEx();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
createPOAndMRForProduct(mulchId); // create some stock to avoid negative qty average cost exception
|
createPOAndMRForProduct(DictionaryIDs.M_Product.MULCH.id); // create some stock to avoid negative qty average cost exception
|
||||||
|
|
||||||
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
|
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
|
||||||
bom.setM_Product_ID(mulchX.get_ID());
|
bom.setM_Product_ID(mulchX.get_ID());
|
||||||
|
@ -149,7 +147,7 @@ public class ProductionTest extends AbstractTestCase {
|
||||||
bom.saveEx();
|
bom.saveEx();
|
||||||
|
|
||||||
MPPProductBOMLine line = new MPPProductBOMLine(bom);
|
MPPProductBOMLine line = new MPPProductBOMLine(bom);
|
||||||
line.setM_Product_ID(mulchId);
|
line.setM_Product_ID(DictionaryIDs.M_Product.MULCH.id);
|
||||||
line.setQtyBOM(new BigDecimal("2"));
|
line.setQtyBOM(new BigDecimal("2"));
|
||||||
line.saveEx();
|
line.saveEx();
|
||||||
|
|
||||||
|
@ -158,8 +156,7 @@ public class ProductionTest extends AbstractTestCase {
|
||||||
mulchX.saveEx();
|
mulchX.saveEx();
|
||||||
|
|
||||||
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||||
//Joe Block
|
order.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id));
|
||||||
order.setBPartner(MBPartner.get(Env.getCtx(), joeBlock));
|
|
||||||
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
|
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
|
||||||
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
|
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
|
||||||
order.setDocStatus(DocAction.STATUS_Drafted);
|
order.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
@ -218,7 +215,7 @@ public class ProductionTest extends AbstractTestCase {
|
||||||
assertTrue(productionLines.length==2,"Number of production line is not 2 as expected ("+productionLines.length+")");
|
assertTrue(productionLines.length==2,"Number of production line is not 2 as expected ("+productionLines.length+")");
|
||||||
assertTrue(productionLines[0].getM_Product_ID()==shipmentLine.getM_Product_ID(), "Production Line Production <> Shipment Line Product");
|
assertTrue(productionLines[0].getM_Product_ID()==shipmentLine.getM_Product_ID(), "Production Line Production <> Shipment Line Product");
|
||||||
assertTrue(productionLines[0].getMovementQty().equals(shipmentLine.getMovementQty()), "Production Line Qty <> Shipment Line Qty");
|
assertTrue(productionLines[0].getMovementQty().equals(shipmentLine.getMovementQty()), "Production Line Qty <> Shipment Line Qty");
|
||||||
assertTrue(productionLines[1].getM_Product_ID()==mulchId,"Production Line 2 Product is not the expected component product");
|
assertTrue(productionLines[1].getM_Product_ID()==DictionaryIDs.M_Product.MULCH.id,"Production Line 2 Product is not the expected component product");
|
||||||
assertTrue(productionLines[1].getMovementQty().intValue()==-2,"Production Line 2 Qty is not the expected component qty");
|
assertTrue(productionLines[1].getMovementQty().intValue()==-2,"Production Line 2 Qty is not the expected component qty");
|
||||||
} finally {
|
} finally {
|
||||||
rollback();
|
rollback();
|
||||||
|
|
Loading…
Reference in New Issue