IDEMPIERE-5836: Adding unit test cases for Non stocked expense curren… (#2024)

* IDEMPIERE-5836: Adding unit test cases for Non stocked expense current bhavior

* IDEMPIERE-5836: Refactoring as per code review comments.

* IDEMPIERE-5836: Corrected code comments as per peer review
This commit is contained in:
Deepak Pansheriya 2023-09-29 20:24:23 +05:30 committed by GitHub
parent 34485f97f8
commit cb6c8b636d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 1544 additions and 0 deletions

View File

@ -0,0 +1,770 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
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.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.List;
import org.compiere.acct.Doc;
import org.compiere.acct.DocManager;
import org.compiere.model.MAccount;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MBPartner;
import org.compiere.model.MClient;
import org.compiere.model.MCost;
import org.compiere.model.MFactAcct;
import org.compiere.model.MInOut;
import org.compiere.model.MInOutConfirm;
import org.compiere.model.MInOutLine;
import org.compiere.model.MInvoice;
import org.compiere.model.MInvoiceLine;
import org.compiere.model.MMatchInv;
import org.compiere.model.MMatchPO;
import org.compiere.model.MOrder;
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.MRMA;
import org.compiere.model.MRMALine;
import org.compiere.model.MStorageOnHand;
import org.compiere.model.ProductCost;
import org.compiere.model.Query;
import org.compiere.model.SystemIDs;
import org.compiere.process.DocAction;
import org.compiere.process.DocumentEngine;
import org.compiere.process.ProcessInfo;
import org.compiere.process.ServerProcessCtl;
import org.compiere.util.Env;
import org.compiere.util.TimeUtil;
import org.compiere.util.Util;
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;
/**
* Test cases Stocked Expense type product with Average PO Costing Method.
*
* @author Logilite Technologies
* @since September 15, 2023
*/
@Isolated
public class NonStockedExpTypeAvgPOCostingTest extends AbstractTestCase
{
private static final BigDecimal BD_2 = new BigDecimal("2.00");
private static final BigDecimal BD_5 = new BigDecimal("5.00");
private static final BigDecimal BD_20 = new BigDecimal("20.00");
public NonStockedExpTypeAvgPOCostingTest()
{
}
@Test
public void testMaterialReceipt()
{
//need to create category and product for the every test case because after the first test completed it's rollback
//and also need to delete category and product at the end of the each test case otherwise next test case failed
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
// Create Material Receipt
MInOutLine rLine = createPOAndMRForProduct(product);
MMatchPO[] matchPOs = MMatchPO.get(Env.getCtx(), rLine.getM_InOutLine_ID(), getTrxName());
assertEquals(1, matchPOs.length);
assertEquals(rLine.getC_OrderLine_ID(), matchPOs[0].getC_OrderLine_ID());
assertEquals(rLine.getM_InOutLine_ID(), matchPOs[0].getM_InOutLine_ID());
assertTrue(matchPOs[0].getQty().compareTo(BigDecimal.TEN) == 0);
cost.load(getTrxName());
//Validate that averagePO costing method should not be created in the cost for the expense type product
assertNoAveragePOCost(product.get_ID());
// Testing cost and stock of the product after completing MR
assertEquals(BD_2, cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), cost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "Unexpected current Qty");
//Assert no storage records created for expense type product
MStorageOnHand[] storages = MStorageOnHand.getAll( Env.getCtx(), product.get_ID(),
DictionaryIDs.M_Locator.HQ.id, getTrxName(), false, 0);
assertEquals(0,storages.length,"No storage records should be created for expense type product");
// Testing Accounting For MR
Doc doc = DocManager.getDocument(as, MInOut.Table_ID, rLine.getM_InOut_ID(), getTrxName());
doc.setC_BPartner_ID(rLine.getM_InOut().getC_BPartner_ID());
MAccount acctNIR = doc.getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as);
// get ProductExpense Of the created MR
ProductCost pc = new ProductCost(Env.getCtx(), rLine.getM_Product_ID(), 0, getTrxName());
MAccount productExpense = pc.getAccount(ProductCost.ACCTTYPE_P_Expense, as);
StringBuilder whereClause = new StringBuilder();
whereClause
.append(MFactAcct.COLUMNNAME_AD_Table_ID)
.append("=")
.append(MInOut.Table_ID)
.append(" AND ")
.append(MFactAcct.COLUMNNAME_Record_ID)
.append("=")
.append(rLine.getM_InOut_ID())
.append(" AND ")
.append(MFactAcct.COLUMNNAME_C_AcctSchema_ID)
.append("=")
.append(as.getC_AcctSchema_ID());
int[] ids = MFactAcct.getAllIDs(MFactAcct.Table_Name, whereClause.toString(), getTrxName());
for (int id : ids)
{
// Test Accounting for MR
MFactAcct fa = new MFactAcct(Env.getCtx(), id, getTrxName());
if (fa.getAccount_ID() == acctNIR.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctCr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(new BigDecimal("-10.00"), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
else if (fa.getAccount_ID() == productExpense.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctDr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
}
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
@Test
public void testSOAndShipment()
{
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
createPOAndMRForProduct(product);
cost.load(getTrxName());
assertNoAveragePOCost(product.get_ID());
createSOAndShipmentForProduct(product);
cost.load(getTrxName());
// testing cost and stock of the product after completing Shipment
assertEquals(BD_2, cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), cost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "Unexpected current Qty");
// Assert no storage records created for expense type product
MStorageOnHand[] storages = MStorageOnHand.getAll( Env.getCtx(), product.get_ID(),
DictionaryIDs.M_Locator.HQ.id, getTrxName(), false, 0);
assertEquals(0,storages.length,"No storage records should be created for expense type product");
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
@Test
public void testCustomerRMA()
{
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
createPOAndMRForProduct(product);
cost.load(getTrxName());
assertNoAveragePOCost(product.get_ID());
// Create SO And Shipment
MInOutLine sLine = createSOAndShipmentForProduct(product);
cost.load(getTrxName());
// Create MRA
MRMA rma = new MRMA(Env.getCtx(), 0, getTrxName());
rma.setName("testCustomerReturnMaterial");
rma.setC_DocType_ID(DictionaryIDs.C_DocType.CUSTOMER_RETURN_MATERIAL.id);
rma.setM_RMAType_ID(DictionaryIDs.M_RMAType.DAMAGE_ON_ARRIVAL.id);
rma.setM_InOut_ID(sLine.getM_InOut_ID());
rma.setIsSOTrx(true);
rma.setSalesRep_ID(SystemIDs.USER_SUPERUSER); // SuperUser
rma.saveEx();
// Create MRALine
MRMALine rmaLine = new MRMALine(Env.getCtx(), 0, getTrxName());
rmaLine.setLine(10);
rmaLine.setM_RMA_ID(rma.get_ID());
rmaLine.setQty(new BigDecimal("3"));
rmaLine.setM_InOutLine_ID(sLine.get_ID());
rmaLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(rma, DocAction.ACTION_Complete);
rma.load(getTrxName());
assertFalse(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Completed, rma.getDocStatus());
// Create delivery for Customer Return Material
MInOut delivery = new MInOut(Env.getCtx(), 0, getTrxName());
delivery.setM_RMA_ID(rma.get_ID());
delivery.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.PATIO.id));
delivery.setC_DocType_ID(DictionaryIDs.C_DocType.MM_CUSTOMER_RETURN.id);
delivery.setMovementType(MInOut.MOVEMENTTYPE_CustomerReturns);
delivery.setIsSOTrx(true);
delivery.setDocStatus(DocAction.STATUS_Drafted);
delivery.setDocAction(DocAction.ACTION_Complete);
delivery.setM_Warehouse_ID(DictionaryIDs.M_Warehouse.HQ.id);
delivery.saveEx();
MInOutLine dLine = new MInOutLine(delivery);
dLine.setM_RMALine_ID(rmaLine.get_ID());
dLine.setLine(10);
dLine.setProduct(new MProduct(Env.getCtx(), product.getM_Product_ID(), getTrxName()));
dLine.setM_Locator_ID(sLine.getM_Locator_ID());
dLine.setQty(new BigDecimal("3"));
dLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(delivery, DocAction.ACTION_Complete);
delivery.load(getTrxName());
assertFalse(info.isError(), info.getSummary());
// Complete the MInOutConfirm to complete the delivery
MInOutConfirm[] confirmations = delivery.getConfirmations(true);
assertEquals(1, confirmations.length, "Expected 1 Shipment Confirmation Document");
assertTrue(confirmations[0].getM_InOutConfirm_ID() != delivery.getM_InOut_ID(), "Test should run with M_InOut_ID != M_InOutConfirm_ID");
info = MWorkflow.runDocumentActionWorkflow(confirmations[0], DocAction.ACTION_Complete);
if (info.isError())
{
fail("Failed to complete shipment confirmation" + (Util.isEmpty(info.getSummary()) ? "" : " : " + info.getSummary()));
return;
}
confirmations[0].load(getTrxName());
assertEquals(DocAction.STATUS_Completed, confirmations[0].getDocStatus(), "Expected Completed Status for Shipment Confirmation");
// Complete the delivery
info = MWorkflow.runDocumentActionWorkflow(delivery, DocAction.ACTION_Complete);
delivery.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, delivery.getDocStatus(), "Expected Completed Status for Shipment");
if (!delivery.isPosted())
{
String error = DocumentEngine.postImmediate( Env.getCtx(), delivery.getAD_Client_ID(), MInOut.Table_ID,
delivery.get_ID(), false, getTrxName());
assertNull(error, error);
}
// testing cost and stock of the product after Customer return
cost.load(getTrxName());
assertEquals(BD_2, cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), cost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost Qty");
// Assert no storage records created for expense type product
MStorageOnHand[] storages = MStorageOnHand.getAll( Env.getCtx(), product.get_ID(),
DictionaryIDs.M_Locator.HQ.id, getTrxName(), false, 0);
assertEquals(0,storages.length,"No storage records should be created for expense type product");
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
@Test
public void testInvoice()
{
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
// Create PO And MR
MInOutLine rLine = createPOAndMRForProduct(product);
cost.load(getTrxName());
assertNoAveragePOCost(product.get_ID());
// Create Invoice from the MR
MInOut mInOut = (MInOut) rLine.getM_InOut();
MInvoice invoice = new MInvoice(mInOut, rLine.getM_InOut().getMovementDate());
invoice.setDocStatus(DocAction.STATUS_Drafted);
invoice.setDocAction(DocAction.ACTION_Complete);
invoice.saveEx();
MOrderLine oLine = (MOrderLine) rLine.getC_OrderLine();
MInvoiceLine iLine = new MInvoiceLine(invoice);
iLine.setOrderLine(oLine);
iLine.setLine(10);
iLine.setProduct(new MProduct(Env.getCtx(), product.getM_Product_ID(), getTrxName()));
iLine.setQty(BigDecimal.TEN);
iLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
invoice.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, invoice.getDocStatus());
invoice.getDocsPostProcess().clear();
if (!invoice.isPosted())
{
String error = DocumentEngine.postImmediate( Env.getCtx(), invoice.getAD_Client_ID(),
invoice.get_Table_ID(), invoice.get_ID(), false, getTrxName());
assertNull(error, error);
}
// Testing Accounting for Invoice
// get ProductExpense Of the Invoice
ProductCost pc = new ProductCost(Env.getCtx(), iLine.getM_Product_ID(), 0, getTrxName());
MAccount productExpense = pc.getAccount(ProductCost.ACCTTYPE_P_Expense, as);
// get AccPayable of the Invoice
MAccount acctPT = new MAccount(Env.getCtx(), as.getAcctSchemaDefault().getV_Liability_Acct(), getTrxName());
String whereClause = "AD_Table_ID = " + MInvoice.Table_ID;
whereClause += " AND Record_ID = " + iLine.getC_Invoice_ID();
whereClause += " AND C_AcctSchema_ID = " + as.getC_AcctSchema_ID();
int[] ids = MFactAcct.getAllIDs(MFactAcct.Table_Name, whereClause.toString(), getTrxName());
for (int id : ids)
{
// Test Accounting for Invoice
MFactAcct fa = new MFactAcct(Env.getCtx(), id, getTrxName());
if (fa.getAccount_ID() == acctPT.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctCr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
else if (fa.getAccount_ID() == productExpense.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctDr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
}
// Testing Accounting For MatchInv
MMatchInv[] matchInvoices = MMatchInv.getInOut(Env.getCtx(), rLine.getM_InOut_ID(), getTrxName());
assertEquals(1, matchInvoices.length);
assertEquals(rLine.getM_InOutLine_ID(), matchInvoices[0].getM_InOutLine_ID());
assertTrue( matchInvoices[0].getQty().compareTo(BigDecimal.TEN) == 0,
"Expected 5, get " + matchInvoices[0].getQty());
// get NotInvoicedReceipts for the MatchInv
Doc doc = DocManager.getDocument(as, MMatchInv.Table_ID, matchInvoices[0].get_ID(), getTrxName());
doc.setC_BPartner_ID(matchInvoices[0].getC_InvoiceLine().getC_Invoice().getC_BPartner_ID());
MAccount acctNIR = doc.getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as);
// get Product Expense for the MatchInv
pc = new ProductCost(Env.getCtx(), matchInvoices[0].getM_Product_ID(), 0, getTrxName());
productExpense = pc.getAccount(ProductCost.ACCTTYPE_P_Expense, as);
whereClause = null;
whereClause = "AD_Table_ID = " + MMatchInv.Table_ID;
whereClause += " AND Record_ID = " + matchInvoices[0].get_ID();
whereClause += " AND C_AcctSchema_ID = " + as.getC_AcctSchema_ID();
int[] idsMatchInv = MFactAcct.getAllIDs(MFactAcct.Table_Name, whereClause.toString(), getTrxName());
for (int id : idsMatchInv)
{
// Test Accounting for MatchINV
MFactAcct fa = new MFactAcct(Env.getCtx(), id, getTrxName());
if (fa.getAccount_ID() == acctNIR.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctDr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
else if (fa.getAccount_ID() == productExpense.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctCr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(new BigDecimal("-10.00"), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
}
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
@Test
public void testExpenseProduction()
{
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
MProduct fgProdcut = null;
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
// create PO And MR for the expense type product
createPOAndMRForProduct(product);
cost.load(getTrxName());
assertNoAveragePOCost(product.get_ID());
// Create Product in which isBOM Flag true
fgProdcut = new MProduct(Env.getCtx(), 0, null);
fgProdcut.setName("Test_BOM");
fgProdcut.setIsBOM(true);
fgProdcut.setIsStocked(true);
fgProdcut.setC_UOM_ID(DictionaryIDs.C_UOM.EACH.id);
fgProdcut.setM_Product_Category_ID(DictionaryIDs.M_Product_Category.STANDARD.id);
fgProdcut.setProductType(MProduct.PRODUCTTYPE_Item);
fgProdcut.setM_Product_Category_ID(category.get_ID());
fgProdcut.setC_TaxCategory_ID(DictionaryIDs.C_TaxCategory.STANDARD.id);
fgProdcut.saveEx();
MCost fgCost = MCost.get(fgProdcut, 0, as, 0, DictionaryIDs.M_CostElement.MATERIAL.id, getTrxName());
// create Product BOM
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
bom.setM_Product_ID(fgProdcut.get_ID());
bom.setBOMType(MPPProductBOM.BOMTYPE_CurrentActive);
bom.setBOMUse(MPPProductBOM.BOMUSE_Master);
bom.setName(fgProdcut.getName());
bom.saveEx();
// Add expense type product in the ProductBOMLine
MPPProductBOMLine bomLine = new MPPProductBOMLine(bom);
bomLine.setM_Product_ID(product.getM_Product_ID());
bomLine.setQtyBOM(BD_2);
bomLine.saveEx();
// Create production for the BOM product
MProduction production = new MProduction(Env.getCtx(), 0, getTrxName());
production.setM_Product_ID(fgProdcut.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(BD_2);
production.setPP_Product_BOM_ID(bom.getPP_Product_BOM_ID());
production.saveEx();
// Create ProductionLine for the BOM Product
MProcess process = MProcess.get(Env.getCtx(), 53226); // CreateProductionAndCreateLines
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(2, 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());
fgCost.load(getTrxName());
cost.load(getTrxName());
// Test the Cost and stock for the Expense type's Product
assertEquals(new BigDecimal("6.00"), cost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BD_2, cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "");
// Assert no storage records created for expense type product
MStorageOnHand[] storages = MStorageOnHand.getAll( Env.getCtx(), product.get_ID(),
DictionaryIDs.M_Locator.HQ.id, getTrxName(), false, 0);
assertEquals(0,storages.length,"No stocked should be used for expense type product Usage in Poroduction");
// test Stock and cost of the Product in which BOM is created
assertEquals(BD_2, fgCost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), fgCost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "");
// Assert that OnHand Qty for the Finished goods of item type is created
BigDecimal onHand = MStorageOnHand.getQtyOnHand(fgProdcut.get_ID(),DictionaryIDs.M_Warehouse.HQ.id, 0, getTrxName());
assertEquals(BD_2, onHand.setScale(2, RoundingMode.HALF_UP), "Storge for the Expense Type Product Should not be Created");
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (fgProdcut != null)
fgProdcut.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
/**
* Create Expense Type Product
*
* @param category_ID Standard Costing
* @return
*/
private MProduct createProduct(int category_ID)
{
MProduct product = new MProduct(Env.getCtx(), 0, null);
product.setName("testStandardCosting");
product.setValue("testStandardCosting");
product.setProductType(MProduct.PRODUCTTYPE_ExpenseType);
product.setIsStocked(false);
product.setIsSold(true);
product.setIsPurchased(true);
product.setC_UOM_ID(DictionaryIDs.C_UOM.EACH.id);
product.setM_Product_Category_ID(category_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.saveEx();
MPriceListVersion plvs = MPriceList.get(DictionaryIDs.M_PriceList.STANDARD.id).getPriceListVersion(null);
MProductPrice pps = new MProductPrice(Env.getCtx(), 0, getTrxName());
pps.setM_PriceList_Version_ID(plvs.getM_PriceList_Version_ID());
pps.setM_Product_ID(product.get_ID());
pps.saveEx();
return product;
}
/**
* Create PO And MR for the Expense Type Product
*
* @param product
* @return
*/
private MInOutLine createPOAndMRForProduct(MProduct product)
{
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 oLine = new MOrderLine(order);
oLine.setLine(10);
oLine.setProduct(product);
oLine.setQty(BigDecimal.TEN);
oLine.setDatePromised(today);
oLine.setPrice(BD_2);
oLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
order.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
MInOut receipt = new MInOut(order, DictionaryIDs.C_DocType.MM_RECEIPT.id, order.getDateOrdered());
receipt.setDocStatus(DocAction.STATUS_Drafted);
receipt.setDocAction(DocAction.ACTION_Complete);
receipt.saveEx();
MInOutLine rLine = new MInOutLine(receipt);
rLine.setOrderLine(oLine, 0, BigDecimal.TEN);
rLine.setQty(BigDecimal.TEN);
rLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
receipt.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, receipt.getDocStatus());
if (!receipt.isPosted())
{
String error = DocumentEngine.postImmediate( Env.getCtx(), receipt.getAD_Client_ID(), receipt.get_Table_ID(),
receipt.get_ID(), false, getTrxName());
assertNull(error, error);
}
return rLine;
}
/**
* Create SO And Shipment for the Expense Type Product
*
* @param product
* @return
*/
private MInOutLine createSOAndShipmentForProduct(MProduct product)
{
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.STANDARD_ORDER.id);
order.setIsSOTrx(true);
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 oLine = new MOrderLine(order);
oLine.setLine(10);
oLine.setProduct(product);
oLine.setQty(BD_5);
oLine.setDatePromised(today);
oLine.setPrice(BD_2);
oLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
order.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
MInOut receipt = new MInOut(order, DictionaryIDs.C_DocType.MM_SHIPMENT.id, order.getDateOrdered());
receipt.setDocStatus(DocAction.STATUS_Drafted);
receipt.setDocAction(DocAction.ACTION_Complete);
receipt.setIsSOTrx(true);
receipt.saveEx();
MInOutLine rLine = new MInOutLine(receipt);
rLine.setOrderLine(oLine, 0, BD_5);
rLine.setQty(BD_5);
rLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
receipt.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, receipt.getDocStatus());
if (!receipt.isPosted())
{
String error = DocumentEngine.postImmediate( Env.getCtx(), receipt.getAD_Client_ID(), receipt.get_Table_ID(),
receipt.get_ID(), false, getTrxName());
assertNull(error, error);
}
return rLine;
}
/**
* Get Accounting Schema
*
* @return
*/
private MAcctSchema getAccountingSchema()
{
MClient client = MClient.get(Env.getCtx());
MAcctSchema as = client.getAcctSchema();
return as;
}
private MCost getCost(MAcctSchema as, MProduct product)
{
return MCost.get(product, 0, as, 0, DictionaryIDs.M_CostElement.MATERIAL.id, getTrxName());
}
/**
* Create Product Category "Standard Costing"
*
* @return
*/
private MProductCategory createProductCategory()
{
MProductCategory category = new MProductCategory(Env.getCtx(), 0, null);
category.setName("Average PO");
category.saveEx();
String whereClause = "M_Product_Category_ID=?";
List<MProductCategoryAcct> categoryAccts = new Query(Env.getCtx(), MProductCategoryAcct.Table_Name, whereClause, null)
.setParameters(category.get_ID())
.list();
for (MProductCategoryAcct categoryAcct : categoryAccts)
{
categoryAcct.setCostingMethod(MAcctSchema.COSTINGMETHOD_AveragePO);
categoryAcct.saveEx();
}
return category;
}
private void assertNoAveragePOCost(int productID)
{
List<MCost> costs = new Query(Env.getCtx(), MCost.Table_Name, " M_Product_ID = ? ", null)
.setParameters(productID)
.list();
for (MCost cost : costs)
{
assertFalse(DictionaryIDs.M_CostElement.AVERAGE_PO.id == cost.getM_CostElement_ID(),"Average PO cost should not be created for Expense type product");
}
}
}

View File

@ -0,0 +1,774 @@
/******************************************************************************
* Copyright (C) 2016 Logilite Technologies LLP *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. 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., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
*****************************************************************************/
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 static org.junit.jupiter.api.Assertions.fail;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.List;
import org.compiere.acct.Doc;
import org.compiere.acct.DocManager;
import org.compiere.model.MAccount;
import org.compiere.model.MAcctSchema;
import org.compiere.model.MBPartner;
import org.compiere.model.MClient;
import org.compiere.model.MCost;
import org.compiere.model.MFactAcct;
import org.compiere.model.MInOut;
import org.compiere.model.MInOutConfirm;
import org.compiere.model.MInOutLine;
import org.compiere.model.MInvoice;
import org.compiere.model.MInvoiceLine;
import org.compiere.model.MMatchInv;
import org.compiere.model.MMatchPO;
import org.compiere.model.MOrder;
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.MRMA;
import org.compiere.model.MRMALine;
import org.compiere.model.MStorageOnHand;
import org.compiere.model.ProductCost;
import org.compiere.model.Query;
import org.compiere.model.SystemIDs;
import org.compiere.process.DocAction;
import org.compiere.process.DocumentEngine;
import org.compiere.process.ProcessInfo;
import org.compiere.process.ServerProcessCtl;
import org.compiere.util.Env;
import org.compiere.util.TimeUtil;
import org.compiere.util.Util;
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;
/**
* Test cases Stocked Expense type product with Standard PO Costing Method.
*
* @author Logilite Technologies
* @since September 15, 2023
*/
@Isolated
public class NonStockedExpTypeStdCostingTest extends AbstractTestCase
{
private static final BigDecimal BD_2 = new BigDecimal("2.00");
private static final BigDecimal BD_5 = new BigDecimal("5.00");
private static final BigDecimal BD_20 = new BigDecimal("20.00");
public NonStockedExpTypeStdCostingTest()
{
}
@Test
public void testMaterialReceipt()
{
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
assertNotNull(cost, "No MCost record found");
// Create Material Receipt
MInOutLine rLine = createPOAndMRForProduct(product);
//MatchPO
MMatchPO[] matchPOs = MMatchPO.get(Env.getCtx(), rLine.getM_InOutLine_ID(), getTrxName());
assertEquals(1, matchPOs.length);
assertEquals(rLine.getC_OrderLine_ID(), matchPOs[0].getC_OrderLine_ID());
assertEquals(rLine.getM_InOutLine_ID(), matchPOs[0].getM_InOutLine_ID());
assertTrue(matchPOs[0].getQty().compareTo(BigDecimal.TEN) == 0);
cost.load(getTrxName());
// testing cost and stock of the product after completing MR
assertEquals(BD_2, cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), cost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "Unexpected current Qty");
// Assert no storage records created for expense type product
MStorageOnHand[] storages = MStorageOnHand.getAll( Env.getCtx(), product.get_ID(),
DictionaryIDs.M_Locator.HQ.id, getTrxName(), false, 0);
assertEquals(0,storages.length,"No storage records should be created for expense type product");
// Test Acc. For MR
// get NotInvoicedReceipts Of the created MR
Doc doc = DocManager.getDocument(as, MInOut.Table_ID, rLine.getM_InOut_ID(), getTrxName());
doc.setC_BPartner_ID(rLine.getM_InOut().getC_BPartner_ID());
MAccount acctNIR = doc.getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as);
// get ProductExpense Of the created MR
ProductCost pc = new ProductCost(Env.getCtx(), rLine.getM_Product_ID(), 0, getTrxName());
MAccount productExpense = pc.getAccount(ProductCost.ACCTTYPE_P_Expense, as);
StringBuilder whereClause = new StringBuilder();
whereClause
.append(MFactAcct.COLUMNNAME_AD_Table_ID)
.append("=")
.append(MInOut.Table_ID)
.append(" AND ")
.append(MFactAcct.COLUMNNAME_Record_ID)
.append("=")
.append(rLine.getM_InOut_ID())
.append(" AND ")
.append(MFactAcct.COLUMNNAME_C_AcctSchema_ID)
.append("=")
.append(as.getC_AcctSchema_ID());
int[] ids = MFactAcct.getAllIDs(MFactAcct.Table_Name, whereClause.toString(), getTrxName());
for (int id : ids)
{
// Test Accounting for MR
MFactAcct fa = new MFactAcct(Env.getCtx(), id, getTrxName());
if (fa.getAccount_ID() == acctNIR.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctCr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(new BigDecimal("-10.00"), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
else if (fa.getAccount_ID() == productExpense.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctDr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
}
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
@Test
public void testSOAndShipment()
{
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
assertNotNull(cost, "No MCost record found");
// Create PO And MR
createPOAndMRForProduct(product);
cost.load(getTrxName());
// Create SO And Shipment
createSOAndShipmentForProduct(product);
cost.load(getTrxName());
// testing cost and stock of the product after completing Shipment
assertEquals(BD_2, cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
assertEquals(BD_5, cost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "Unexpected current Qty");
// Assert no storage records created for expense type product
MStorageOnHand[] storages = MStorageOnHand.getAll( Env.getCtx(), product.get_ID(),
DictionaryIDs.M_Locator.HQ.id, getTrxName(), false, 0);
assertEquals(0,storages.length,"No storage records should be created for expense type product");
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
@Test
public void testCustomerRMA()
{
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
assertNotNull(cost, "No MCost record found");
// Create PO And MR
createPOAndMRForProduct(product);
cost.load(getTrxName());
// Create SO And Shipment
MInOutLine sLine = createSOAndShipmentForProduct(product);
cost.load(getTrxName());
// Create MRA
MRMA rma = new MRMA(Env.getCtx(), 0, getTrxName());
rma.setName("testCustomerReturnMaterial");
rma.setC_DocType_ID(DictionaryIDs.C_DocType.CUSTOMER_RETURN_MATERIAL.id);
rma.setM_RMAType_ID(DictionaryIDs.M_RMAType.DAMAGE_ON_ARRIVAL.id);
rma.setM_InOut_ID(sLine.getM_InOut_ID());
rma.setIsSOTrx(true);
rma.setSalesRep_ID(SystemIDs.USER_SUPERUSER);
rma.saveEx();
// Create MRALine
MRMALine rmaLine = new MRMALine(Env.getCtx(), 0, getTrxName());
rmaLine.setLine(10);
rmaLine.setM_RMA_ID(rma.get_ID());
rmaLine.setQty(new BigDecimal("3"));
rmaLine.setM_InOutLine_ID(sLine.get_ID());
rmaLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(rma, DocAction.ACTION_Complete);
rma.load(getTrxName());
assertFalse(info.isError(), info.getSummary());
assertEquals(DocAction.STATUS_Completed, rma.getDocStatus());
// Create shipment for Customer Return Material
MInOut delivery = new MInOut(Env.getCtx(), 0, getTrxName());
delivery.setM_RMA_ID(rma.get_ID());
delivery.setBPartner(MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.PATIO.id));
delivery.setC_DocType_ID(DictionaryIDs.C_DocType.MM_CUSTOMER_RETURN.id);
delivery.setMovementType(MInOut.MOVEMENTTYPE_CustomerReturns);
delivery.setIsSOTrx(true);
delivery.setDocStatus(DocAction.STATUS_Drafted);
delivery.setDocAction(DocAction.ACTION_Complete);
delivery.setM_Warehouse_ID(DictionaryIDs.M_Warehouse.HQ.id);
delivery.saveEx();
MInOutLine dLine = new MInOutLine(delivery);
dLine.setM_RMALine_ID(rmaLine.get_ID());
dLine.setLine(10);
dLine.setProduct(new MProduct(Env.getCtx(), product.getM_Product_ID(), getTrxName()));
dLine.setM_Locator_ID(sLine.getM_Locator_ID());
dLine.setQty(new BigDecimal("3"));
dLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(delivery, DocAction.ACTION_Complete);
delivery.load(getTrxName());
assertFalse(info.isError(), info.getSummary());
// Need to complete the InoutConfirmation to complete the return
// material
MInOutConfirm[] confirmations = delivery.getConfirmations(true);
assertEquals(1, confirmations.length, "Expected 1 Shipment Confirmation Document");
assertTrue(confirmations[0].getM_InOutConfirm_ID() != delivery.getM_InOut_ID(), "Test should run with M_InOut_ID != M_InOutConfirm_ID");
info = MWorkflow.runDocumentActionWorkflow(confirmations[0], DocAction.ACTION_Complete);
if (info.isError())
{
fail("Failed to complete shipment confirmation" + (Util.isEmpty(info.getSummary()) ? "" : " : " + info.getSummary()));
return;
}
confirmations[0].load(getTrxName());
assertEquals(DocAction.STATUS_Completed, confirmations[0].getDocStatus(), "Expected Completed Status for Shipment Confirmation");
info = MWorkflow.runDocumentActionWorkflow(delivery, DocAction.ACTION_Complete);
delivery.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, delivery.getDocStatus(), "Expected Completed Status for Shipment");
if (!delivery.isPosted())
{
String error = DocumentEngine.postImmediate(Env.getCtx(), delivery.getAD_Client_ID(), MInOut.Table_ID, delivery.get_ID(), false, getTrxName());
assertNull(error, error);
}
cost.load(getTrxName());
// Complete the MInOutConfirm to complete the delivery
assertEquals(BD_2, cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost price");
assertEquals(new BigDecimal("8.00"), cost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "Unexpected current cost Qty");
// Assert no storage records created for expense type product
MStorageOnHand[] storages = MStorageOnHand.getAll( Env.getCtx(), product.get_ID(),
DictionaryIDs.M_Locator.HQ.id, getTrxName(), false, 0);
assertEquals(0, storages.length,"No storage records should be created for expense type product");
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
@Test
public void testInvoice()
{
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
assertNotNull(cost, "No MCost record found");
// Create PO And MR
MInOutLine rLine = createPOAndMRForProduct(product);
cost.load(getTrxName());
// Create Invoice from the MR
MInOut mInOut = (MInOut) rLine.getM_InOut();
MInvoice invoice = new MInvoice(mInOut, rLine.getM_InOut().getMovementDate());
invoice.setDocStatus(DocAction.STATUS_Drafted);
invoice.setDocAction(DocAction.ACTION_Complete);
invoice.saveEx();
MOrderLine oLine = (MOrderLine) rLine.getC_OrderLine();
MInvoiceLine iLine = new MInvoiceLine(invoice);
iLine.setOrderLine(oLine);
iLine.setLine(10);
iLine.setProduct(new MProduct(Env.getCtx(), product, getTrxName()));
iLine.setQty(BigDecimal.TEN);
iLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
invoice.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, invoice.getDocStatus());
invoice.getDocsPostProcess().clear();
if (!invoice.isPosted())
{
String error = DocumentEngine.postImmediate( Env.getCtx(), invoice.getAD_Client_ID(),
invoice.get_Table_ID(), invoice.get_ID(), false, getTrxName());
assertNull(error, error);
}
// get ProductExpense Of the Invoice
ProductCost pc = new ProductCost(Env.getCtx(), iLine.getM_Product_ID(), 0, getTrxName());
MAccount productExpense = pc.getAccount(ProductCost.ACCTTYPE_P_Expense, as);
// get AccPayable of the Invoice
MAccount acctPT = new MAccount(Env.getCtx(), as.getAcctSchemaDefault().getV_Liability_Acct(), getTrxName());
StringBuilder whereClause = new StringBuilder();
whereClause
.append(MFactAcct.COLUMNNAME_AD_Table_ID)
.append("=")
.append(MInvoice.Table_ID)
.append(" AND ")
.append(MFactAcct.COLUMNNAME_Record_ID)
.append("=")
.append(iLine.getC_Invoice_ID())
.append(" AND ")
.append(MFactAcct.COLUMNNAME_C_AcctSchema_ID)
.append("=")
.append(as.getC_AcctSchema_ID());
int[] ids = MFactAcct.getAllIDs(MFactAcct.Table_Name, whereClause.toString(), getTrxName());
for (int id : ids)
{
// Test Accounting for Invoice
MFactAcct fa = new MFactAcct(Env.getCtx(), id, getTrxName());
if (fa.getAccount_ID() == acctPT.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctCr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
else if (fa.getAccount_ID() == productExpense.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctDr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
}
//Accounting MatchInv
MMatchInv[] matchInvoices = MMatchInv.getInOut(Env.getCtx(), rLine.getM_InOut_ID(), getTrxName());
assertEquals(1, matchInvoices.length);
assertEquals(rLine.getM_InOutLine_ID(), matchInvoices[0].getM_InOutLine_ID());
assertTrue(matchInvoices[0].getQty().compareTo(BigDecimal.TEN) == 0,
"Expected 5, get " + matchInvoices[0].getQty());
// get NotInvoicedReceipts for the MatchInv
Doc doc = DocManager.getDocument(as, MMatchInv.Table_ID, matchInvoices[0].get_ID(), getTrxName());
doc.setC_BPartner_ID(matchInvoices[0].getC_InvoiceLine().getC_Invoice().getC_BPartner_ID());
MAccount acctNIR = doc.getAccount(Doc.ACCTTYPE_NotInvoicedReceipts, as);
// get Product Expense for the MatchInv
pc = new ProductCost(Env.getCtx(), matchInvoices[0].getM_Product_ID(), 0, getTrxName());
productExpense = pc.getAccount(ProductCost.ACCTTYPE_P_Expense, as);
whereClause = null;
whereClause = new StringBuilder();
whereClause
.append(MFactAcct.COLUMNNAME_AD_Table_ID)
.append("=")
.append(MMatchInv.Table_ID)
.append(" AND ")
.append(MFactAcct.COLUMNNAME_Record_ID)
.append("=")
.append(matchInvoices[0].get_ID())
.append(" AND ")
.append(MFactAcct.COLUMNNAME_C_AcctSchema_ID)
.append("=")
.append(as.getC_AcctSchema_ID());
int[] idsMatchInv = MFactAcct.getAllIDs(MFactAcct.Table_Name, whereClause.toString(), getTrxName());
for (int id : idsMatchInv)
{
// Test Accounting for MatchINV
MFactAcct fa = new MFactAcct(Env.getCtx(), id, getTrxName());
if (fa.getAccount_ID() == acctNIR.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctDr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.TEN.setScale(2, RoundingMode.HALF_UP), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
else if (fa.getAccount_ID() == productExpense.getAccount_ID())
{
assertEquals(BD_20, fa.getAmtAcctCr().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(new BigDecimal("-10.00"), fa.getQty().setScale(2, RoundingMode.HALF_UP), "");
}
}
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
@Test
public void testExpenseProductInBOM()
{
MProductCategory category = createProductCategory();
MProduct product = createProduct(category.get_ID());
MProduct fgProdcut = null;
try
{
MAcctSchema as = getAccountingSchema();
MCost cost = getCost(as, product);
assertNotNull(cost, "No MCost record found");
createPOAndMRForProduct(product);
cost.load(getTrxName());
// create product in which BOM is true
fgProdcut = new MProduct(Env.getCtx(), 0, null);
fgProdcut.setName("Test_BOM");
fgProdcut.setIsBOM(true);
fgProdcut.setIsStocked(true);
fgProdcut.setC_UOM_ID(DictionaryIDs.C_UOM.EACH.id);
fgProdcut.setM_Product_Category_ID(DictionaryIDs.M_Product_Category.STANDARD.id);
fgProdcut.setProductType(MProduct.PRODUCTTYPE_Item);
fgProdcut.setM_Product_Category_ID(category.get_ID());
fgProdcut.setC_TaxCategory_ID(DictionaryIDs.C_TaxCategory.STANDARD.id);
fgProdcut.saveEx();
MCost fgCost = MCost.get(fgProdcut, 0, as, 0, DictionaryIDs.M_CostElement.MATERIAL.id, getTrxName());
// create productBOM
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
bom.setM_Product_ID(fgProdcut.get_ID());
bom.setBOMType(MPPProductBOM.BOMTYPE_CurrentActive);
bom.setBOMUse(MPPProductBOM.BOMUSE_Master);
bom.setName(fgProdcut.getName());
bom.saveEx();
// Add expense type product in the BOM Line
MPPProductBOMLine bomLine = new MPPProductBOMLine(bom);
bomLine.setM_Product_ID(product.getM_Product_ID());
bomLine.setQtyBOM(BD_2);
bomLine.saveEx();
// create production for the BOM Product
MProduction production = new MProduction(Env.getCtx(), 0, getTrxName());
production.setM_Product_ID(fgProdcut.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(BD_2);
production.setPP_Product_BOM_ID(bom.getPP_Product_BOM_ID());
production.saveEx();
// create production lines from the process
MProcess process = MProcess.get(Env.getCtx(), 53226);// CreateProductionAndCreateLines
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(2, 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());
fgCost.load(getTrxName());
cost.load(getTrxName());
// Test the Cost and stock for the Expense type's Product
assertEquals(new BigDecimal("6.00"), cost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BD_2, cost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "");
// Assert no storage records created for expense type product
MStorageOnHand[] storages = MStorageOnHand.getAll( Env.getCtx(), product.get_ID(),
DictionaryIDs.M_Locator.HQ.id, getTrxName(), false, 0);
assertEquals(0, storages.length,"No storage records should be created for expense type product");
// test Stock and cost of the Product in which BOM is created
assertEquals(BD_2, fgCost.getCurrentQty().setScale(2, RoundingMode.HALF_UP), "");
assertEquals(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), fgCost.getCurrentCostPrice().setScale(2, RoundingMode.HALF_UP), "");
// Assert that OnHand Qty for the item type finished good Product created
BigDecimal onHand = MStorageOnHand.getQtyOnHand(fgProdcut.get_ID(),DictionaryIDs.M_Warehouse.HQ.id, 0, getTrxName());
assertEquals(BD_2, onHand.setScale(2, RoundingMode.HALF_UP), "Storge for the Item Type Product Should not be Created");
}
finally
{
rollback();
if (product != null)
product.deleteEx(true);
if (fgProdcut != null)
fgProdcut.deleteEx(true);
if (category != null)
category.deleteEx(true);
}
}
/**
* Create Expense Type Product
*
* @param category_ID Standard Costing
* @return
*/
private MProduct createProduct(int category_ID)
{
MProduct product = new MProduct(Env.getCtx(), 0, null);
product.setName("testStandardCosting");
product.setValue("testStandardCosting");
product.setProductType(MProduct.PRODUCTTYPE_ExpenseType);
product.setIsStocked(false);
product.setIsSold(true);
product.setIsPurchased(true);
product.setC_UOM_ID(DictionaryIDs.C_UOM.EACH.id);
product.setM_Product_Category_ID(category_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.saveEx();
MPriceListVersion plvs = MPriceList.get(DictionaryIDs.M_PriceList.STANDARD.id).getPriceListVersion(null);
MProductPrice pps = new MProductPrice(Env.getCtx(), 0, getTrxName());
pps.setM_PriceList_Version_ID(plvs.getM_PriceList_Version_ID());
pps.setM_Product_ID(product.get_ID());
pps.saveEx();
return product;
}
/**
* Create PO And MR for the expense type product
*
* @param product
* @return
*/
private MInOutLine createPOAndMRForProduct(MProduct product)
{
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 oLine = new MOrderLine(order);
oLine.setLine(10);
oLine.setProduct(product);
oLine.setQty(BigDecimal.TEN);
oLine.setDatePromised(today);
oLine.setPrice(BD_2);
oLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
order.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
MInOut receipt = new MInOut(order, DictionaryIDs.C_DocType.MM_RECEIPT.id, order.getDateOrdered());
receipt.setDocStatus(DocAction.STATUS_Drafted);
receipt.setDocAction(DocAction.ACTION_Complete);
receipt.saveEx();
MInOutLine rLine = new MInOutLine(receipt);
rLine.setOrderLine(oLine, 0, BigDecimal.TEN);
rLine.setQty(BigDecimal.TEN);
rLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
receipt.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, receipt.getDocStatus());
if (!receipt.isPosted())
{
String error = DocumentEngine.postImmediate(Env.getCtx(), receipt.getAD_Client_ID(), receipt.get_Table_ID(),
receipt.get_ID(), false, getTrxName());
assertNull(error, error);
}
return rLine;
}
/**
* Create SO And Shipment for the Expense type Product
*
* @param product
* @return
*/
private MInOutLine createSOAndShipmentForProduct(MProduct product)
{
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.STANDARD_ORDER.id);
order.setIsSOTrx(true);
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 oLine = new MOrderLine(order);
oLine.setLine(10);
oLine.setProduct(product);
oLine.setQty(BD_5);
oLine.setDatePromised(today);
oLine.setPrice(BD_2);
oLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
order.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
MInOut receipt = new MInOut(order, DictionaryIDs.C_DocType.MM_SHIPMENT.id, order.getDateOrdered());
receipt.setDocStatus(DocAction.STATUS_Drafted);
receipt.setDocAction(DocAction.ACTION_Complete);
receipt.setIsSOTrx(true);
receipt.saveEx();
MInOutLine rLine = new MInOutLine(receipt);
rLine.setOrderLine(oLine, 0, BD_5);
rLine.setQty(BD_5);
rLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
receipt.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, receipt.getDocStatus());
if (!receipt.isPosted())
{
String error = DocumentEngine.postImmediate(Env.getCtx(), receipt.getAD_Client_ID(), receipt.get_Table_ID(),
receipt.get_ID(), false, getTrxName());
assertNull(error, error);
}
return rLine;
}
private MCost getCost(MAcctSchema as, MProduct product)
{
return product.getCostingRecord(as, getAD_Org_ID(), 0, MAcctSchema.COSTINGMETHOD_StandardCosting);
}
/**
* Get Accounting Schema
*
* @return
*/
private MAcctSchema getAccountingSchema()
{
MClient client = MClient.get(Env.getCtx());
MAcctSchema as = client.getAcctSchema();
return as;
}
/**
* Create Product Category "Standard Costing"
*
* @return
*/
private MProductCategory createProductCategory()
{
MProductCategory category = new MProductCategory(Env.getCtx(), 0, null);
category.setName("Standard Costing");
category.saveEx();
String whereClause = "M_Product_Category_ID=?";
List<MProductCategoryAcct> categoryAccts = new Query(Env.getCtx(), MProductCategoryAcct.Table_Name, whereClause, null)
.setParameters(category.get_ID())
.list();
for (MProductCategoryAcct categoryAcct : categoryAccts)
{
categoryAcct.setCostingMethod(MAcctSchema.COSTINGMETHOD_StandardCosting);
categoryAcct.saveEx();
}
return category;
}
}