IDEMPIERE-4849 Nonsensical code in MProduction.createLines() (#1116)
This commit is contained in:
parent
ccdb4868f1
commit
916a5a9233
|
@ -263,8 +263,6 @@ public class MProduction extends X_M_Production implements DocAction {
|
||||||
|
|
||||||
int M_Warehouse_ID = finishedLocator.getM_Warehouse_ID();
|
int M_Warehouse_ID = finishedLocator.getM_Warehouse_ID();
|
||||||
|
|
||||||
int asi = 0;
|
|
||||||
|
|
||||||
// products used in production
|
// products used in production
|
||||||
String sql = " SELECT bl.M_Product_ID, bl.QtyBOM" + " FROM PP_Product_BOMLine bl"
|
String sql = " SELECT bl.M_Product_ID, bl.QtyBOM" + " FROM PP_Product_BOMLine bl"
|
||||||
+ " JOIN PP_Product_BOM b ON b.PP_Product_BOM_ID = bl.PP_Product_BOM_ID "
|
+ " JOIN PP_Product_BOM b ON b.PP_Product_BOM_ID = bl.PP_Product_BOM_ID "
|
||||||
|
@ -354,7 +352,6 @@ public class MProduction extends X_M_Production implements DocAction {
|
||||||
|
|
||||||
MProductionLine BOMLine = null;
|
MProductionLine BOMLine = null;
|
||||||
int prevLoc = -1;
|
int prevLoc = -1;
|
||||||
int previousAttribSet = -1;
|
|
||||||
// Create lines from storage until qty is reached
|
// Create lines from storage until qty is reached
|
||||||
for (int sl = 0; sl < storages.length; sl++) {
|
for (int sl = 0; sl < storages.length; sl++) {
|
||||||
|
|
||||||
|
@ -365,13 +362,9 @@ public class MProduction extends X_M_Production implements DocAction {
|
||||||
|
|
||||||
|
|
||||||
int loc = storages[sl].getM_Locator_ID();
|
int loc = storages[sl].getM_Locator_ID();
|
||||||
int slASI = storages[sl].getM_AttributeSetInstance_ID();
|
|
||||||
int locAttribSet = new MAttributeSetInstance(getCtx(), asi,
|
|
||||||
get_TrxName()).getM_AttributeSet_ID();
|
|
||||||
|
|
||||||
// roll up costing attributes if in the same locator
|
// same locator
|
||||||
if (locAttribSet == 0 && previousAttribSet == 0
|
if (prevLoc == loc) {
|
||||||
&& prevLoc == loc) {
|
|
||||||
BOMLine.setQtyUsed(BOMLine.getQtyUsed()
|
BOMLine.setQtyUsed(BOMLine.getQtyUsed()
|
||||||
.add(lineQty));
|
.add(lineQty));
|
||||||
BOMLine.setPlannedQty(BOMLine.getQtyUsed());
|
BOMLine.setPlannedQty(BOMLine.getQtyUsed());
|
||||||
|
@ -386,15 +379,12 @@ public class MProduction extends X_M_Production implements DocAction {
|
||||||
BOMLine.setM_Locator_ID( loc );
|
BOMLine.setM_Locator_ID( loc );
|
||||||
BOMLine.setQtyUsed( lineQty);
|
BOMLine.setQtyUsed( lineQty);
|
||||||
BOMLine.setPlannedQty( lineQty);
|
BOMLine.setPlannedQty( lineQty);
|
||||||
if ( slASI != 0 && locAttribSet != 0 ) // ie non costing attribute
|
|
||||||
BOMLine.setM_AttributeSetInstance_ID(slASI);
|
|
||||||
BOMLine.saveEx(get_TrxName());
|
BOMLine.saveEx(get_TrxName());
|
||||||
|
|
||||||
lineno = lineno + 10;
|
lineno = lineno + 10;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
prevLoc = loc;
|
prevLoc = loc;
|
||||||
previousAttribSet = locAttribSet;
|
|
||||||
// enough ?
|
// enough ?
|
||||||
BOMMovementQty = BOMMovementQty.subtract(lineQty);
|
BOMMovementQty = BOMMovementQty.subtract(lineQty);
|
||||||
if (BOMMovementQty.signum() == 0)
|
if (BOMMovementQty.signum() == 0)
|
||||||
|
@ -407,9 +397,8 @@ public class MProduction extends X_M_Production implements DocAction {
|
||||||
if (!mustBeStocked)
|
if (!mustBeStocked)
|
||||||
{
|
{
|
||||||
|
|
||||||
// roll up costing attributes if in the same locator
|
// same locator
|
||||||
if ( previousAttribSet == 0
|
if (prevLoc == defaultLocator) {
|
||||||
&& prevLoc == defaultLocator) {
|
|
||||||
BOMLine.setQtyUsed(BOMLine.getQtyUsed()
|
BOMLine.setQtyUsed(BOMLine.getQtyUsed()
|
||||||
.add(BOMMovementQty));
|
.add(BOMMovementQty));
|
||||||
BOMLine.setPlannedQty(BOMLine.getQtyUsed());
|
BOMLine.setPlannedQty(BOMLine.getQtyUsed());
|
||||||
|
|
|
@ -95,11 +95,11 @@ public class MProductionLine extends X_M_ProductionLine {
|
||||||
StringBuilder errorString = new StringBuilder();
|
StringBuilder errorString = new StringBuilder();
|
||||||
|
|
||||||
MAttributeSetInstance asi = new MAttributeSetInstance(getCtx(), getM_AttributeSetInstance_ID(), get_TrxName());
|
MAttributeSetInstance asi = new MAttributeSetInstance(getCtx(), getM_AttributeSetInstance_ID(), get_TrxName());
|
||||||
I_M_AttributeSet attributeset = prod.getM_AttributeSet();
|
I_M_AttributeSet attributeset = prod.getM_AttributeSet_ID() > 0 ? MAttributeSet.get(prod.getM_AttributeSet_ID()) : null;
|
||||||
boolean isAutoGenerateLot = false;
|
boolean isAutoGenerateLot = false;
|
||||||
if (attributeset != null)
|
if (attributeset != null)
|
||||||
isAutoGenerateLot = attributeset.isAutoGenerateLot();
|
isAutoGenerateLot = attributeset.isAutoGenerateLot();
|
||||||
String asiString = asi.getDescription();
|
String asiString = asi.get_ID() > 0 ? asi.getDescription() : "";
|
||||||
if ( asiString == null )
|
if ( asiString == null )
|
||||||
asiString = "";
|
asiString = "";
|
||||||
|
|
||||||
|
@ -165,16 +165,15 @@ public class MProductionLine extends X_M_ProductionLine {
|
||||||
if (lineQty.compareTo(qtyToMove ) > 0)
|
if (lineQty.compareTo(qtyToMove ) > 0)
|
||||||
lineQty = qtyToMove;
|
lineQty = qtyToMove;
|
||||||
|
|
||||||
MAttributeSetInstance slASI = new MAttributeSetInstance(getCtx(),
|
MAttributeSetInstance slASI = storages[sl].getM_AttributeSetInstance_ID() > 0 ? new MAttributeSetInstance(getCtx(),
|
||||||
storages[sl].getM_AttributeSetInstance_ID(),get_TrxName());
|
storages[sl].getM_AttributeSetInstance_ID(),get_TrxName()) : null;
|
||||||
String slASIString = slASI.getDescription();
|
String slASIString = slASI != null ? slASI.getDescription() : "";
|
||||||
if (slASIString == null)
|
if (slASIString == null)
|
||||||
slASIString = "";
|
slASIString = "";
|
||||||
|
|
||||||
if (log.isLoggable(Level.FINEST))log.log(Level.FINEST,"slASI-Description =" + slASIString);
|
if (log.isLoggable(Level.FINEST))log.log(Level.FINEST,"slASI-Description =" + slASIString);
|
||||||
|
|
||||||
if ( slASIString.compareTo(asiString) == 0
|
if (asi.getM_AttributeSet_ID() == 0 || slASIString.equals(asiString))
|
||||||
|| asi.getM_AttributeSet_ID() == 0 )
|
|
||||||
//storage matches specified ASI or is a costing asi (inc. 0)
|
//storage matches specified ASI or is a costing asi (inc. 0)
|
||||||
// This process will move negative stock on hand quantities
|
// This process will move negative stock on hand quantities
|
||||||
{
|
{
|
||||||
|
@ -227,7 +226,7 @@ public class MProductionLine extends X_M_ProductionLine {
|
||||||
{
|
{
|
||||||
setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID());
|
setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID());
|
||||||
asi = new MAttributeSetInstance(getCtx(), storage.getM_AttributeSetInstance_ID(), get_TrxName());
|
asi = new MAttributeSetInstance(getCtx(), storage.getM_AttributeSetInstance_ID(), get_TrxName());
|
||||||
asiString = asi.getDescription();
|
asiString = asi.get_ID() > 0 ? asi.getDescription() : "";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -273,16 +272,15 @@ public class MProductionLine extends X_M_ProductionLine {
|
||||||
asi.get_ID(), date, get_TrxName(), true);
|
asi.get_ID(), date, get_TrxName(), true);
|
||||||
|
|
||||||
BigDecimal lineQty = qtyToMove;
|
BigDecimal lineQty = qtyToMove;
|
||||||
MAttributeSetInstance slASI = new MAttributeSetInstance(getCtx(),
|
MAttributeSetInstance slASI = storage.getM_AttributeSetInstance_ID() > 0
|
||||||
storage.getM_AttributeSetInstance_ID(),get_TrxName());
|
? new MAttributeSetInstance(getCtx(), storage.getM_AttributeSetInstance_ID(),get_TrxName()) : null;
|
||||||
String slASIString = slASI.getDescription();
|
String slASIString = slASI != null ? slASI.getDescription() : "";
|
||||||
if (slASIString == null)
|
if (slASIString == null)
|
||||||
slASIString = "";
|
slASIString = "";
|
||||||
|
|
||||||
if (log.isLoggable(Level.FINEST))log.log(Level.FINEST,"slASI-Description =" + slASIString);
|
if (log.isLoggable(Level.FINEST))log.log(Level.FINEST,"slASI-Description =" + slASIString);
|
||||||
|
|
||||||
if ( slASIString.compareTo(asiString) == 0
|
if (asi.getM_AttributeSet_ID() == 0 || slASIString.compareTo(asiString) == 0)
|
||||||
|| asi.getM_AttributeSet_ID() == 0 )
|
|
||||||
//storage matches specified ASI or is a costing asi (inc. 0)
|
//storage matches specified ASI or is a costing asi (inc. 0)
|
||||||
// This process will move negative stock on hand quantities
|
// This process will move negative stock on hand quantities
|
||||||
{
|
{
|
||||||
|
|
|
@ -122,8 +122,6 @@ public class MProductionPlan extends X_M_ProductionPlan {
|
||||||
|
|
||||||
int M_Warehouse_ID = finishedLocator.getM_Warehouse_ID();
|
int M_Warehouse_ID = finishedLocator.getM_Warehouse_ID();
|
||||||
|
|
||||||
int asi = 0;
|
|
||||||
|
|
||||||
// products used in production
|
// products used in production
|
||||||
String sql = " SELECT bl.M_Product_ID, bl.QtyBOM" + " FROM PP_Product_BOMLine bl"
|
String sql = " SELECT bl.M_Product_ID, bl.QtyBOM" + " FROM PP_Product_BOMLine bl"
|
||||||
+ " JOIN PP_Product_BOM b ON b.PP_Product_BOM_ID = bl.PP_Product_BOM_ID "
|
+ " JOIN PP_Product_BOM b ON b.PP_Product_BOM_ID = bl.PP_Product_BOM_ID "
|
||||||
|
@ -209,7 +207,6 @@ public class MProductionPlan extends X_M_ProductionPlan {
|
||||||
|
|
||||||
MProductionLine BOMLine = null;
|
MProductionLine BOMLine = null;
|
||||||
int prevLoc = -1;
|
int prevLoc = -1;
|
||||||
int previousAttribSet = -1;
|
|
||||||
// Create lines from storage until qty is reached
|
// Create lines from storage until qty is reached
|
||||||
for (int sl = 0; sl < storages.length; sl++) {
|
for (int sl = 0; sl < storages.length; sl++) {
|
||||||
|
|
||||||
|
@ -218,15 +215,9 @@ public class MProductionPlan extends X_M_ProductionPlan {
|
||||||
if (lineQty.compareTo(BOMMovementQty) > 0)
|
if (lineQty.compareTo(BOMMovementQty) > 0)
|
||||||
lineQty = BOMMovementQty;
|
lineQty = BOMMovementQty;
|
||||||
|
|
||||||
|
|
||||||
int loc = storages[sl].getM_Locator_ID();
|
int loc = storages[sl].getM_Locator_ID();
|
||||||
int slASI = storages[sl].getM_AttributeSetInstance_ID();
|
// same locator
|
||||||
int locAttribSet = new MAttributeSetInstance(getCtx(), asi,
|
if (prevLoc == loc) {
|
||||||
get_TrxName()).getM_AttributeSet_ID();
|
|
||||||
|
|
||||||
// roll up costing attributes if in the same locator
|
|
||||||
if (locAttribSet == 0 && previousAttribSet == 0
|
|
||||||
&& prevLoc == loc) {
|
|
||||||
BOMLine.setQtyUsed(BOMLine.getQtyUsed()
|
BOMLine.setQtyUsed(BOMLine.getQtyUsed()
|
||||||
.add(lineQty));
|
.add(lineQty));
|
||||||
BOMLine.setPlannedQty(BOMLine.getQtyUsed());
|
BOMLine.setPlannedQty(BOMLine.getQtyUsed());
|
||||||
|
@ -241,15 +232,12 @@ public class MProductionPlan extends X_M_ProductionPlan {
|
||||||
BOMLine.setM_Locator_ID( loc );
|
BOMLine.setM_Locator_ID( loc );
|
||||||
BOMLine.setQtyUsed( lineQty);
|
BOMLine.setQtyUsed( lineQty);
|
||||||
BOMLine.setPlannedQty( lineQty);
|
BOMLine.setPlannedQty( lineQty);
|
||||||
if ( slASI != 0 && locAttribSet != 0 ) // ie non costing attribute
|
|
||||||
BOMLine.setM_AttributeSetInstance_ID(slASI);
|
|
||||||
BOMLine.saveEx(get_TrxName());
|
BOMLine.saveEx(get_TrxName());
|
||||||
|
|
||||||
lineno = lineno + 10;
|
lineno = lineno + 10;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
prevLoc = loc;
|
prevLoc = loc;
|
||||||
previousAttribSet = locAttribSet;
|
|
||||||
// enough ?
|
// enough ?
|
||||||
BOMMovementQty = BOMMovementQty.subtract(lineQty);
|
BOMMovementQty = BOMMovementQty.subtract(lineQty);
|
||||||
if (BOMMovementQty.signum() == 0)
|
if (BOMMovementQty.signum() == 0)
|
||||||
|
@ -261,10 +249,8 @@ public class MProductionPlan extends X_M_ProductionPlan {
|
||||||
if (BOMMovementQty.signum() != 0 ) {
|
if (BOMMovementQty.signum() != 0 ) {
|
||||||
if (!mustBeStocked)
|
if (!mustBeStocked)
|
||||||
{
|
{
|
||||||
|
//same locator
|
||||||
// roll up costing attributes if in the same locator
|
if (prevLoc == defaultLocator) {
|
||||||
if ( previousAttribSet == 0
|
|
||||||
&& prevLoc == defaultLocator) {
|
|
||||||
BOMLine.setQtyUsed(BOMLine.getQtyUsed()
|
BOMLine.setQtyUsed(BOMLine.getQtyUsed()
|
||||||
.add(BOMMovementQty));
|
.add(BOMMovementQty));
|
||||||
BOMLine.setPlannedQty(BOMLine.getQtyUsed());
|
BOMLine.setPlannedQty(BOMLine.getQtyUsed());
|
||||||
|
@ -273,7 +259,6 @@ public class MProductionPlan extends X_M_ProductionPlan {
|
||||||
}
|
}
|
||||||
// otherwise create new line
|
// otherwise create new line
|
||||||
else {
|
else {
|
||||||
|
|
||||||
BOMLine = new MProductionLine( this );
|
BOMLine = new MProductionLine( this );
|
||||||
BOMLine.setLine( lineno );
|
BOMLine.setLine( lineno );
|
||||||
BOMLine.setM_Product_ID( BOMProduct_ID );
|
BOMLine.setM_Product_ID( BOMProduct_ID );
|
||||||
|
@ -285,7 +270,6 @@ public class MProductionPlan extends X_M_ProductionPlan {
|
||||||
lineno = lineno + 10;
|
lineno = lineno + 10;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,7 @@ import java.util.List;
|
||||||
|
|
||||||
import org.compiere.model.MAccount;
|
import org.compiere.model.MAccount;
|
||||||
import org.compiere.model.MAcctSchema;
|
import org.compiere.model.MAcctSchema;
|
||||||
|
import org.compiere.model.MAttributeSetInstance;
|
||||||
import org.compiere.model.MBPartner;
|
import org.compiere.model.MBPartner;
|
||||||
import org.compiere.model.MClient;
|
import org.compiere.model.MClient;
|
||||||
import org.compiere.model.MCost;
|
import org.compiere.model.MCost;
|
||||||
|
@ -85,6 +86,10 @@ public class ProductionTest extends AbstractTestCase {
|
||||||
private static final int DOCTYPE_PO = 126;
|
private static final int DOCTYPE_PO = 126;
|
||||||
private static final int DOCTYPE_RECEIPT = 122;
|
private static final int DOCTYPE_RECEIPT = 122;
|
||||||
private static final int USER_GARDENADMIN = 101;
|
private static final int USER_GARDENADMIN = 101;
|
||||||
|
private static final int FERTILIZER_LOT_ATTRIBUTESET_ID = 101;
|
||||||
|
private static final int UOM_EACH_ID = 100;
|
||||||
|
private static final int TAX_CATEGORY_STANDARD_ID = 107;
|
||||||
|
private static final int HQ_LOCATOR_ID = 101;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAverageCostingProduction() {
|
public void testAverageCostingProduction() {
|
||||||
|
@ -546,4 +551,371 @@ public class ProductionTest extends AbstractTestCase {
|
||||||
mulchX.deleteEx(true);
|
mulchX.deleteEx(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleASI() {
|
||||||
|
//use standard costing only to avoid negative qty exception
|
||||||
|
DB.executeUpdateEx("UPDATE M_CostElement SET IsActive = 'N' WHERE AD_Client_ID=? AND CostingMethod IS NOT NULL AND CostingMethod != ?",
|
||||||
|
new Object[] {getAD_Client_ID(), MCostElement.COSTINGMETHOD_StandardCosting}, getTrxName());
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
//storageonhand api doesn't use trx to retrieve product
|
||||||
|
MProduct component = new MProduct(Env.getCtx(), 0, null);
|
||||||
|
component.setName("testMultipleASI_Child");
|
||||||
|
component.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||||
|
component.setIsStocked(true);
|
||||||
|
component.setProductType(MProduct.PRODUCTTYPE_Item);
|
||||||
|
component.setC_UOM_ID(UOM_EACH_ID);
|
||||||
|
component.setM_Product_Category_ID(category.get_ID());
|
||||||
|
component.setC_TaxCategory_ID(TAX_CATEGORY_STANDARD_ID);
|
||||||
|
component.saveEx();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Timestamp today = TimeUtil.getDay(null);
|
||||||
|
MProduct parent = new MProduct(Env.getCtx(), 0, getTrxName());
|
||||||
|
parent.setName("testMultipleASI_Parent");
|
||||||
|
parent.setIsBOM(true);
|
||||||
|
parent.setIsStocked(true);
|
||||||
|
parent.setC_UOM_ID(component.getC_UOM_ID());
|
||||||
|
parent.setM_Product_Category_ID(component.getM_Product_Category_ID());
|
||||||
|
parent.setProductType(component.getProductType());
|
||||||
|
parent.setC_TaxCategory_ID(component.getC_TaxCategory_ID());
|
||||||
|
parent.saveEx();
|
||||||
|
BigDecimal endProductOnHand1 = MStorageOnHand.getQtyOnHand(parent.get_ID(), getM_Warehouse_ID(), 0, getTrxName());
|
||||||
|
assertEquals(0, endProductOnHand1.intValue(), "On hand of new product is not zero");
|
||||||
|
|
||||||
|
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
|
||||||
|
bom.setM_Product_ID(parent.get_ID());
|
||||||
|
bom.setBOMType(MPPProductBOM.BOMTYPE_CurrentActive);
|
||||||
|
bom.setBOMUse(MPPProductBOM.BOMUSE_Master);
|
||||||
|
bom.setName(parent.getName());
|
||||||
|
bom.saveEx();
|
||||||
|
|
||||||
|
MPPProductBOMLine line = new MPPProductBOMLine(bom);
|
||||||
|
line.setM_Product_ID(component.get_ID());
|
||||||
|
line.setQtyBOM(new BigDecimal("2"));
|
||||||
|
line.saveEx();
|
||||||
|
|
||||||
|
parent.load(getTrxName());
|
||||||
|
parent.setIsVerified(true);
|
||||||
|
parent.saveEx();
|
||||||
|
|
||||||
|
MAttributeSetInstance asi1 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||||
|
asi1.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||||
|
asi1.setLot("Lot1");
|
||||||
|
asi1.saveEx();
|
||||||
|
MStorageOnHand.add(Env.getCtx(), HQ_LOCATOR_ID, component.get_ID(), asi1.get_ID(), new BigDecimal("1"), today, getTrxName());
|
||||||
|
|
||||||
|
MAttributeSetInstance asi2 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||||
|
asi2.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||||
|
asi2.setLot("Lot2");
|
||||||
|
asi2.saveEx();
|
||||||
|
MStorageOnHand.add(Env.getCtx(), HQ_LOCATOR_ID, component.get_ID(), asi2.get_ID(), new BigDecimal("1"), today, getTrxName());
|
||||||
|
|
||||||
|
MProduction production = new MProduction(Env.getCtx(), 0, getTrxName());
|
||||||
|
production.setM_Product_ID(parent.get_ID());
|
||||||
|
production.setM_Locator_ID(HQ_LOCATOR_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());
|
||||||
|
assertEquals("Y", production.getIsCreated(), "MProduction.IsCreated != Y");
|
||||||
|
assertTrue(production.getLines().length > 0, "No Production Lines");
|
||||||
|
assertEquals(2, production.getLines().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 endProductOnHand2 = MStorageOnHand.getQtyOnHand(parent.get_ID(), getM_Warehouse_ID(), 0, getTrxName());
|
||||||
|
|
||||||
|
assertEquals(1, endProductOnHand2.intValue(), "On hand of end product doesn't increase as expected");
|
||||||
|
} finally {
|
||||||
|
getTrx().rollback();
|
||||||
|
component.deleteEx(true);
|
||||||
|
category.deleteEx(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleDateMPolicy() {
|
||||||
|
//use standard costing only to avoid negative qty exception
|
||||||
|
DB.executeUpdateEx("UPDATE M_CostElement SET IsActive = 'N' WHERE AD_Client_ID=? AND CostingMethod IS NOT NULL AND CostingMethod != ?",
|
||||||
|
new Object[] {getAD_Client_ID(), MCostElement.COSTINGMETHOD_StandardCosting}, getTrxName());
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
//storageonhand api doesn't use trx to retrieve product
|
||||||
|
MProduct component = new MProduct(Env.getCtx(), 0, null);
|
||||||
|
component.setName("testMultipleDateMPolicy_Child");
|
||||||
|
component.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||||
|
component.setIsStocked(true);
|
||||||
|
component.setProductType(MProduct.PRODUCTTYPE_Item);
|
||||||
|
component.setC_UOM_ID(UOM_EACH_ID);
|
||||||
|
component.setM_Product_Category_ID(category.get_ID());
|
||||||
|
component.setC_TaxCategory_ID(TAX_CATEGORY_STANDARD_ID);
|
||||||
|
component.saveEx();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Timestamp today = TimeUtil.getDay(null);
|
||||||
|
MProduct parent = new MProduct(Env.getCtx(), 0, getTrxName());
|
||||||
|
parent.setName("testMultipleDateMPolicy_Parent");
|
||||||
|
parent.setIsBOM(true);
|
||||||
|
parent.setIsStocked(true);
|
||||||
|
parent.setC_UOM_ID(component.getC_UOM_ID());
|
||||||
|
parent.setM_Product_Category_ID(component.getM_Product_Category_ID());
|
||||||
|
parent.setProductType(component.getProductType());
|
||||||
|
parent.setC_TaxCategory_ID(component.getC_TaxCategory_ID());
|
||||||
|
parent.saveEx();
|
||||||
|
BigDecimal endProductOnHand1 = MStorageOnHand.getQtyOnHand(parent.get_ID(), getM_Warehouse_ID(), 0, getTrxName());
|
||||||
|
assertEquals(0, endProductOnHand1.intValue(), "On hand of new product is not zero");
|
||||||
|
|
||||||
|
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
|
||||||
|
bom.setM_Product_ID(parent.get_ID());
|
||||||
|
bom.setBOMType(MPPProductBOM.BOMTYPE_CurrentActive);
|
||||||
|
bom.setBOMUse(MPPProductBOM.BOMUSE_Master);
|
||||||
|
bom.setName(parent.getName());
|
||||||
|
bom.saveEx();
|
||||||
|
|
||||||
|
MPPProductBOMLine line = new MPPProductBOMLine(bom);
|
||||||
|
line.setM_Product_ID(component.get_ID());
|
||||||
|
line.setQtyBOM(new BigDecimal("2"));
|
||||||
|
line.saveEx();
|
||||||
|
|
||||||
|
parent.load(getTrxName());
|
||||||
|
parent.setIsVerified(true);
|
||||||
|
parent.saveEx();
|
||||||
|
|
||||||
|
MAttributeSetInstance asi1 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||||
|
asi1.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||||
|
asi1.setLot("Lot1");
|
||||||
|
asi1.saveEx();
|
||||||
|
MStorageOnHand.add(Env.getCtx(), HQ_LOCATOR_ID, component.get_ID(), asi1.get_ID(), new BigDecimal("1"), TimeUtil.addDays(today, -1), getTrxName());
|
||||||
|
MStorageOnHand.add(Env.getCtx(), HQ_LOCATOR_ID, component.get_ID(), asi1.get_ID(), new BigDecimal("1"), today, getTrxName());
|
||||||
|
|
||||||
|
MProduction production = new MProduction(Env.getCtx(), 0, getTrxName());
|
||||||
|
production.setM_Product_ID(parent.get_ID());
|
||||||
|
production.setM_Locator_ID(HQ_LOCATOR_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());
|
||||||
|
assertEquals("Y", production.getIsCreated(), "MProduction.IsCreated != Y");
|
||||||
|
assertTrue(production.getLines().length > 0, "No Production Lines");
|
||||||
|
assertEquals(2, production.getLines().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 endProductOnHand2 = MStorageOnHand.getQtyOnHand(parent.get_ID(), getM_Warehouse_ID(), 0, getTrxName());
|
||||||
|
|
||||||
|
assertEquals(1, endProductOnHand2.intValue(), "On hand of end product doesn't increase as expected");
|
||||||
|
} finally {
|
||||||
|
getTrx().rollback();
|
||||||
|
component.deleteEx(true);
|
||||||
|
category.deleteEx(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleInProgressProduction() {
|
||||||
|
//use standard costing only to avoid negative qty exception
|
||||||
|
DB.executeUpdateEx("UPDATE M_CostElement SET IsActive = 'N' WHERE AD_Client_ID=? AND CostingMethod IS NOT NULL AND CostingMethod != ?",
|
||||||
|
new Object[] {getAD_Client_ID(), MCostElement.COSTINGMETHOD_StandardCosting}, getTrxName());
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
//storageonhand api doesn't use trx to retrieve product
|
||||||
|
MProduct component = new MProduct(Env.getCtx(), 0, null);
|
||||||
|
component.setName("testMultipleDateMPolicy_Child");
|
||||||
|
component.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||||
|
component.setIsStocked(true);
|
||||||
|
component.setProductType(MProduct.PRODUCTTYPE_Item);
|
||||||
|
component.setC_UOM_ID(UOM_EACH_ID);
|
||||||
|
component.setM_Product_Category_ID(category.get_ID());
|
||||||
|
component.setC_TaxCategory_ID(TAX_CATEGORY_STANDARD_ID);
|
||||||
|
component.saveEx();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Timestamp today = TimeUtil.getDay(null);
|
||||||
|
MProduct parent = new MProduct(Env.getCtx(), 0, getTrxName());
|
||||||
|
parent.setName("testMultipleDateMPolicy_Parent");
|
||||||
|
parent.setIsBOM(true);
|
||||||
|
parent.setIsStocked(true);
|
||||||
|
parent.setC_UOM_ID(component.getC_UOM_ID());
|
||||||
|
parent.setM_Product_Category_ID(component.getM_Product_Category_ID());
|
||||||
|
parent.setProductType(component.getProductType());
|
||||||
|
parent.setC_TaxCategory_ID(component.getC_TaxCategory_ID());
|
||||||
|
parent.saveEx();
|
||||||
|
BigDecimal endProductOnHand1 = MStorageOnHand.getQtyOnHand(parent.get_ID(), getM_Warehouse_ID(), 0, getTrxName());
|
||||||
|
assertEquals(0, endProductOnHand1.intValue(), "On hand of new product is not zero");
|
||||||
|
|
||||||
|
MPPProductBOM bom = new MPPProductBOM(Env.getCtx(), 0, getTrxName());
|
||||||
|
bom.setM_Product_ID(parent.get_ID());
|
||||||
|
bom.setBOMType(MPPProductBOM.BOMTYPE_CurrentActive);
|
||||||
|
bom.setBOMUse(MPPProductBOM.BOMUSE_Master);
|
||||||
|
bom.setName(parent.getName());
|
||||||
|
bom.saveEx();
|
||||||
|
|
||||||
|
MPPProductBOMLine line = new MPPProductBOMLine(bom);
|
||||||
|
line.setM_Product_ID(component.get_ID());
|
||||||
|
line.setQtyBOM(new BigDecimal("2"));
|
||||||
|
line.saveEx();
|
||||||
|
|
||||||
|
parent.load(getTrxName());
|
||||||
|
parent.setIsVerified(true);
|
||||||
|
parent.saveEx();
|
||||||
|
|
||||||
|
MAttributeSetInstance asi1 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||||
|
asi1.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||||
|
asi1.setLot("Lot1");
|
||||||
|
asi1.saveEx();
|
||||||
|
MStorageOnHand.add(Env.getCtx(), HQ_LOCATOR_ID, component.get_ID(), asi1.get_ID(), new BigDecimal("2"), today, getTrxName());
|
||||||
|
|
||||||
|
MAttributeSetInstance asi2 = new MAttributeSetInstance(Env.getCtx(), 0, getTrxName());
|
||||||
|
asi2.setM_AttributeSet_ID(FERTILIZER_LOT_ATTRIBUTESET_ID);
|
||||||
|
asi2.setLot("Lot2");
|
||||||
|
asi2.saveEx();
|
||||||
|
MStorageOnHand.add(Env.getCtx(), HQ_LOCATOR_ID, component.get_ID(), asi2.get_ID(), new BigDecimal("2"), today, getTrxName());
|
||||||
|
|
||||||
|
MProduction production1 = new MProduction(Env.getCtx(), 0, getTrxName());
|
||||||
|
production1.setM_Product_ID(parent.get_ID());
|
||||||
|
production1.setM_Locator_ID(HQ_LOCATOR_ID);
|
||||||
|
production1.setIsUseProductionPlan(false);
|
||||||
|
production1.setMovementDate(getLoginDate());
|
||||||
|
production1.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
production1.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
production1.setIsComplete(false);
|
||||||
|
production1.setProductionQty(new BigDecimal("1"));
|
||||||
|
production1.setPP_Product_BOM_ID(bom.getPP_Product_BOM_ID());
|
||||||
|
production1.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(production1.get_ID());
|
||||||
|
pi.setTransactionName(getTrxName());
|
||||||
|
ServerProcessCtl.process(pi, getTrx(), false);
|
||||||
|
assertFalse(pi.isError(), pi.getSummary());
|
||||||
|
|
||||||
|
production1.load(getTrxName());
|
||||||
|
assertEquals("Y", production1.getIsCreated(), "MProduction.IsCreated != Y");
|
||||||
|
assertTrue(production1.getLines().length > 0, "No Production Lines");
|
||||||
|
assertEquals(2, production1.getLines().length, "Unexpected number of production lines");
|
||||||
|
|
||||||
|
MProduction production2 = new MProduction(Env.getCtx(), 0, getTrxName());
|
||||||
|
production2.setM_Product_ID(parent.get_ID());
|
||||||
|
production2.setM_Locator_ID(HQ_LOCATOR_ID);
|
||||||
|
production2.setIsUseProductionPlan(false);
|
||||||
|
production2.setMovementDate(getLoginDate());
|
||||||
|
production2.setDocAction(DocAction.ACTION_Complete);
|
||||||
|
production2.setDocStatus(DocAction.STATUS_Drafted);
|
||||||
|
production2.setIsComplete(false);
|
||||||
|
production2.setProductionQty(new BigDecimal("1"));
|
||||||
|
production2.setPP_Product_BOM_ID(bom.getPP_Product_BOM_ID());
|
||||||
|
production2.saveEx();
|
||||||
|
|
||||||
|
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(production2.get_ID());
|
||||||
|
pi.setTransactionName(getTrxName());
|
||||||
|
ServerProcessCtl.process(pi, getTrx(), false);
|
||||||
|
assertFalse(pi.isError(), pi.getSummary());
|
||||||
|
|
||||||
|
production2.load(getTrxName());
|
||||||
|
assertEquals("Y", production2.getIsCreated(), "MProduction.IsCreated != Y");
|
||||||
|
assertTrue(production2.getLines().length > 0, "No Production Lines");
|
||||||
|
assertEquals(2, production2.getLines().length, "Unexpected number of production lines");
|
||||||
|
|
||||||
|
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(production1, DocAction.ACTION_Complete);
|
||||||
|
production1.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, production1.getDocStatus(), "Production Status="+production1.getDocStatus());
|
||||||
|
BigDecimal endProductOnHand2 = MStorageOnHand.getQtyOnHand(parent.get_ID(), getM_Warehouse_ID(), 0, getTrxName());
|
||||||
|
assertEquals(1, endProductOnHand2.intValue(), "On hand of end product doesn't increase as expected");
|
||||||
|
|
||||||
|
info = MWorkflow.runDocumentActionWorkflow(production2, DocAction.ACTION_Complete);
|
||||||
|
production2.load(getTrxName());
|
||||||
|
assertFalse(info.isError(), info.getSummary());
|
||||||
|
assertEquals(DocAction.STATUS_Completed, production2.getDocStatus(), "Production Status="+production2.getDocStatus());
|
||||||
|
endProductOnHand2 = MStorageOnHand.getQtyOnHand(parent.get_ID(), getM_Warehouse_ID(), 0, getTrxName());
|
||||||
|
assertEquals(2, endProductOnHand2.intValue(), "On hand of end product doesn't increase as expected");
|
||||||
|
} finally {
|
||||||
|
getTrx().rollback();
|
||||||
|
component.deleteEx(true);
|
||||||
|
category.deleteEx(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue