feat: Enhance production line handling and inventory management
- Added a new callout in MID_CalloutProductionLine to set locator based on production details and product type. - Updated MID_DocInvoice to handle service products correctly during invoice processing. - Introduced MID_MStorageOnHand to manage storage on hand with additional methods for creating and retrieving storage records. - Created MID_Production and MID_ProductionLine classes to extend production functionalities, including transaction handling and validation for end products. - Implemented SQL query for invoice details with comprehensive data retrieval for reporting purposes.
This commit is contained in:
parent
0fd0590bc5
commit
014b14c47b
|
|
@ -6,6 +6,7 @@ import org.adempiere.base.IColumnCallout;
|
|||
import org.compiere.model.CalloutEngine;
|
||||
import org.compiere.model.GridField;
|
||||
import org.compiere.model.GridTab;
|
||||
import org.compiere.model.MDocType;
|
||||
import org.compiere.model.MInOut;
|
||||
import org.compiere.model.MOrder;
|
||||
|
||||
|
|
@ -19,13 +20,91 @@ public class MID_CalloutInOut extends CalloutEngine implements IColumnCallout{
|
|||
if(mField.getColumnName().equals(MInOut.COLUMNNAME_MovementDate)) {
|
||||
mTab.setValue(MInOut.COLUMNNAME_DateAcct, value);
|
||||
}
|
||||
|
||||
if(mField.getColumnName().equals(MInOut.COLUMNNAME_C_Order_ID)) {
|
||||
order(ctx, WindowNo, mTab, mField, value);
|
||||
}
|
||||
// if(mField.getColumnName().equals(MInOut.COLUMNNAME_C_Order_ID)) {
|
||||
// setAJU(ctx, WindowNo, mTab, mField, value, oldValue);
|
||||
// }
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* C_Order - Order Defaults.
|
||||
* @param ctx
|
||||
* @param WindowNo
|
||||
* @param mTab
|
||||
* @param mField
|
||||
* @param value
|
||||
* @return error message or ""
|
||||
*/
|
||||
public String order (Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value)
|
||||
{
|
||||
Integer C_Order_ID = (Integer)value;
|
||||
if (C_Order_ID == null || C_Order_ID.intValue() == 0)
|
||||
return "";
|
||||
// No Callout Active to fire dependent values
|
||||
if (isCalloutActive()) // prevent recursive
|
||||
return "";
|
||||
|
||||
// Get Details
|
||||
MOrder order = new MOrder (ctx, C_Order_ID.intValue(), null);
|
||||
if (order.get_ID() != 0)
|
||||
{
|
||||
mTab.setValue("DateOrdered", order.getDateOrdered());
|
||||
mTab.setValue("POReference", order.getPOReference());
|
||||
mTab.setValue("AD_Org_ID", Integer.valueOf(order.getAD_Org_ID()));
|
||||
mTab.setValue("AD_OrgTrx_ID", Integer.valueOf(order.getAD_OrgTrx_ID()));
|
||||
mTab.setValue("C_Activity_ID", Integer.valueOf(order.getC_Activity_ID()));
|
||||
mTab.setValue("C_Campaign_ID", Integer.valueOf(order.getC_Campaign_ID()));
|
||||
mTab.setValue("C_Project_ID", Integer.valueOf(order.getC_Project_ID()));
|
||||
mTab.setValue("User1_ID", Integer.valueOf(order.getUser1_ID()));
|
||||
mTab.setValue("User2_ID", Integer.valueOf(order.getUser2_ID()));
|
||||
mTab.setValue("M_Warehouse_ID", Integer.valueOf(order.getM_Warehouse_ID()));
|
||||
//
|
||||
mTab.setValue("DeliveryRule", order.getDeliveryRule());
|
||||
mTab.setValue("DeliveryViaRule", order.getDeliveryViaRule());
|
||||
mTab.setValue("M_Shipper_ID", Integer.valueOf(order.getM_Shipper_ID()));
|
||||
mTab.setValue("FreightCostRule", order.getFreightCostRule());
|
||||
mTab.setValue("FreightAmt", order.getFreightAmt());
|
||||
|
||||
mTab.setValue("C_BPartner_ID", Integer.valueOf(order.getC_BPartner_ID()));
|
||||
mTab.setValue("SalesRep_ID", Integer.valueOf(order.getSalesRep_ID()));
|
||||
|
||||
//[ 1867464 ]
|
||||
mTab.setValue("C_BPartner_Location_ID", Integer.valueOf(order.getC_BPartner_Location_ID()));
|
||||
|
||||
if (order.getAD_User_ID() > 0)
|
||||
mTab.setValue("AD_User_ID", Integer.valueOf(order.getAD_User_ID()));
|
||||
else
|
||||
mTab.setValue("AD_User_ID", null);
|
||||
|
||||
if (order.isDropShip()) {
|
||||
mTab.setValue(MInOut.COLUMNNAME_IsDropShip, order.isDropShip());
|
||||
mTab.setValue(MInOut.COLUMNNAME_DropShip_BPartner_ID, order.getDropShip_BPartner_ID());
|
||||
mTab.setValue(MInOut.COLUMNNAME_DropShip_Location_ID, order.getDropShip_Location_ID());
|
||||
mTab.setValue(MInOut.COLUMNNAME_DropShip_User_ID, order.getDropShip_User_ID());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Modification: set corresponding document type
|
||||
*/
|
||||
if (mTab.getValue("C_DocType_ID") != null)
|
||||
{
|
||||
mTab.setValue("C_DocType_ID", mTab.getValue("C_DocType_ID"));
|
||||
}
|
||||
// int docTypeId = order.getC_DocType_ID();
|
||||
// int relatedDocTypeId = 0;
|
||||
//
|
||||
// if (docTypeId == 0)
|
||||
// {
|
||||
// docTypeId = order.getC_DocTypeTarget_ID();
|
||||
// }
|
||||
//
|
||||
// relatedDocTypeId = MDocType.getShipmentReceiptDocType(docTypeId);
|
||||
//
|
||||
// mTab.setValue("C_DocType_ID", relatedDocTypeId);
|
||||
return "";
|
||||
} // order
|
||||
public String setAJU(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value, Object oldValue) {
|
||||
MOrder order = new MOrder(ctx, (int)value, null);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,13 @@ import org.adempiere.base.IColumnCallout;
|
|||
import org.compiere.model.CalloutEngine;
|
||||
import org.compiere.model.GridField;
|
||||
import org.compiere.model.GridTab;
|
||||
import org.compiere.model.MInOut;
|
||||
import org.compiere.model.MInOutLine;
|
||||
import org.compiere.model.MLocator;
|
||||
import org.compiere.model.MOrderLine;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.MProduction;
|
||||
import org.compiere.model.Query;
|
||||
|
||||
public class MID_CalloutInOutLine extends CalloutEngine implements IColumnCallout{
|
||||
|
||||
|
|
@ -19,10 +24,37 @@ public class MID_CalloutInOutLine extends CalloutEngine implements IColumnCallou
|
|||
if(mField.getColumnName().equals(MInOutLine.COLUMNNAME_C_OrderLine_ID)) {
|
||||
this.setDescription(ctx, WindowNo, mTab, mField, value, oldValue);
|
||||
}
|
||||
|
||||
if(mField.getColumnName().equals(MInOutLine.COLUMNNAME_M_Product_ID)) {
|
||||
setLocator(ctx, WindowNo, mTab, mField, value, oldValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String setLocator(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value,
|
||||
Object oldValue) {
|
||||
int MProduct_ID = (int) mTab.getValue("M_Product_ID");
|
||||
int InOutID = (int) mTab.getValue("M_InOut_ID");
|
||||
MInOut inout = new MInOut(ctx, InOutID, null);
|
||||
MProduct product = new MProduct(ctx, MProduct_ID, null);
|
||||
boolean isService = (boolean) product.get_Value("IsService");
|
||||
if (isService) {
|
||||
if (inout.getM_Warehouse_ID() > 0) {
|
||||
int Warehouse_ID = inout.getM_Warehouse_ID();
|
||||
// get locator in same warehouse IsEnableNegative
|
||||
int M_Location_ID = new Query(ctx, MLocator.Table_Name, "IsEnableNegative = 'Y' and M_Warehouse_ID=?",
|
||||
null)
|
||||
.setParameters(Warehouse_ID)
|
||||
.setOnlyActiveRecords(true)
|
||||
.firstId();
|
||||
if (M_Location_ID > 0) {
|
||||
mTab.setValue("M_Locator_ID", M_Location_ID);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
public String setDescription(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value, Object oldValue) {
|
||||
MOrderLine ol = new MOrderLine(ctx, (int)value, null);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import org.adempiere.base.IColumnCallout;
|
|||
import org.compiere.model.CalloutEngine;
|
||||
import org.compiere.model.GridField;
|
||||
import org.compiere.model.GridTab;
|
||||
import org.compiere.model.MInventory;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.Query;
|
||||
import org.compiere.model.X_M_InventoryLine;
|
||||
import org.compiere.util.DB;
|
||||
|
||||
|
|
@ -16,9 +19,50 @@ public class MID_CalloutInventoryLine extends CalloutEngine implements IColumnCa
|
|||
public String start(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value, Object oldValue) {
|
||||
if (mField.getColumnName().equals("QtyEntered"))
|
||||
return setInternalUseQty(ctx, WindowNo, mTab, mField, value, oldValue);
|
||||
if( mField.getColumnName().equals("M_Product_ID"))
|
||||
return setUOMByProduct(ctx, WindowNo, mTab, mField, value, oldValue);
|
||||
if(mField.getColumnName().equals("C_Charge_ID")) {
|
||||
if(value == null) {
|
||||
// get parent Inventory
|
||||
log.info("C_Charge_ID is null, trying to set C_Charge_ID from M_Inventory_ID");
|
||||
int M_Inventory_ID = (int) mTab.getValue("M_Inventory_ID");
|
||||
if(M_Inventory_ID > 0) {
|
||||
MInventory inventory = new MInventory(ctx, M_Inventory_ID, null);
|
||||
int DocumentType = inventory.getC_DocType_ID();
|
||||
//
|
||||
|
||||
int chargeID = DB.getSQLValueEx(null, " SELECT cc.C_Charge_ID FROM C_ChargeType_DocType cctd JOIN C_Charge cc ON cc.C_ChargeType_ID = cctd.C_ChargeType_ID WHERE cctd.C_DocType_ID = ? LIMIT 1 ", DocumentType);
|
||||
|
||||
if (chargeID > 0) {
|
||||
log.info("C_Charge_ID: " + chargeID);
|
||||
mTab.setValue("C_Charge_ID", chargeID);
|
||||
} else {
|
||||
mTab.setValue("C_Charge_ID", null);
|
||||
}
|
||||
|
||||
}
|
||||
} else if(value instanceof BigDecimal) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String setUOMByProduct(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value, Object oldValue) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
int M_Product_ID = (int) value;
|
||||
if (M_Product_ID <= 0) {
|
||||
return "";
|
||||
}
|
||||
MProduct product = MProduct.get(ctx, M_Product_ID);
|
||||
int C_UOM_ID = product.getC_UOM_ID();
|
||||
if (C_UOM_ID > 0) {
|
||||
mTab.setValue("C_UOM_ID", C_UOM_ID);
|
||||
} else {
|
||||
mTab.setValue("C_UOM_ID", null);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
public String setInternalUseQty(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value, Object oldValue) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ import org.adempiere.base.IColumnCallout;
|
|||
import org.compiere.model.CalloutEngine;
|
||||
import org.compiere.model.GridField;
|
||||
import org.compiere.model.GridTab;
|
||||
import org.compiere.model.MLocator;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.MProduction;
|
||||
import org.compiere.model.Query;
|
||||
import org.compiere.util.Env;
|
||||
|
||||
public class MID_CalloutProductionLine extends CalloutEngine implements IColumnCallout {
|
||||
|
|
@ -22,10 +25,37 @@ public class MID_CalloutProductionLine extends CalloutEngine implements IColumnC
|
|||
if(value==null) return "";
|
||||
MProduct pro = new MProduct(ctx, (int)value, null);
|
||||
mTab.setValue("C_UOM_ID", pro.getC_UOM_ID());
|
||||
setLocator(ctx, WindowNo, mTab, mField, value, oldValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public String setLocator(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value,
|
||||
Object oldValue) {
|
||||
int MProduct_ID = (int) mTab.getValue("M_Product_ID");
|
||||
int ProductionID = (int) mTab.getValue("M_Production_ID");
|
||||
MProduction production = new MProduction(ctx, ProductionID, null);
|
||||
MProduct product = new MProduct(ctx, MProduct_ID, null);
|
||||
boolean isService = (boolean) product.get_Value("IsService");
|
||||
if (isService) {
|
||||
if (production.getM_Locator_ID() > 0) {
|
||||
MLocator locator = new MLocator(ctx, production.getM_Locator_ID(), null);
|
||||
int Warehouse_ID = locator.getM_Warehouse_ID();
|
||||
// get locator in same warehouse IsEnableNegative
|
||||
int M_Location_ID = new Query(ctx, MLocator.Table_Name, "IsEnableNegative = 'Y' and M_Warehouse_ID=?",
|
||||
null)
|
||||
.setParameters(Warehouse_ID)
|
||||
.setOnlyActiveRecords(true)
|
||||
.firstId();
|
||||
if (M_Location_ID > 0) {
|
||||
mTab.setValue("M_Locator_ID", M_Location_ID);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
public String setConversion(Properties ctx, int WindowNo, GridTab mTab, GridField mField, Object value,
|
||||
Object oldValue) {
|
||||
if (value == null || mTab.getValue("M_Product_ID") == null) {
|
||||
|
|
|
|||
|
|
@ -607,13 +607,26 @@ public class MID_DocInvoice extends Doc
|
|||
serviceAmt = serviceAmt.add(amt);
|
||||
}
|
||||
//
|
||||
|
||||
if (line.getM_Product_ID() != 0
|
||||
&& line.getProduct().isService()) // otherwise Inv Matching
|
||||
&& line.getProduct().isService()) { // otherwise Inv Matching
|
||||
MCostDetail.createInvoice(as, line.getAD_Org_ID(),
|
||||
line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
||||
line.get_ID(), 0, // No Cost Element
|
||||
line.getAmtSource(), line.getQty(),
|
||||
line.getDescription(), getTrxName());
|
||||
}
|
||||
// lanjut nanti
|
||||
// else if(line.getM_Product_ID() != 0
|
||||
// && !line.getProduct().isStocked()
|
||||
// && line.getProduct().get_ValueAsBoolean("AllowNegativeCosting")) {
|
||||
// MCostDetail.createInvoice(as, line.getAD_Org_ID(),
|
||||
// line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),
|
||||
// line.get_ID(), 0, // No Cost Element
|
||||
// line.getAmtSource(), line.getQty(),
|
||||
// line.getDescription(), getTrxName());
|
||||
//
|
||||
// }
|
||||
}
|
||||
}
|
||||
// Set Locations
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import balinusa.midsuit.model.MID_MRequisitionTrxLine;
|
|||
import balinusa.midsuit.model.MID_MRequsitionTax;
|
||||
import balinusa.midsuit.model.MID_PPO;
|
||||
import balinusa.midsuit.model.MID_PPOLine;
|
||||
import balinusa.midsuit.model.MID_Production;
|
||||
import balinusa.midsuit.model.MID_UnrealizedRate;
|
||||
import balinusa.midsuit.model.MID_UnrealizedRateLine;
|
||||
import balinusa.midsuit.model.MWarehouse;
|
||||
|
|
@ -100,6 +101,8 @@ public class MID_ModelFactory implements IModelFactory{
|
|||
mapTableModels.put(MWarehouse.Table_Name, "balinusa.midsuit.model.MWarehouse");
|
||||
mapTableModels.put(MID_MInvoice.Table_Name, "balinusa.midsuit.model.MID_MInvoice");
|
||||
mapTableModels.put(MID_MOrderLine.Table_Name, "balinusa.midsuit.model.MID_MOrderLine");
|
||||
mapTableModels.put(MID_Production.Table_Name, "balinusa.midsuit.model.MID_Production");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ public class MID_MInventory extends MInventory {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("Check 1: qtyDiff " + qtyDiff);
|
||||
// If Quantity Count minus Quantity Book = Zero, then no change in Inventory
|
||||
if (qtyDiff.signum() == 0)
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,248 @@
|
|||
package balinusa.midsuit.model;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.adempiere.exceptions.AdempiereException;
|
||||
import org.adempiere.exceptions.NegativeInventoryDisallowedException;
|
||||
import org.compiere.model.I_M_AttributeSet;
|
||||
import org.compiere.model.I_M_Cost;
|
||||
import org.compiere.model.I_M_ProductionPlan;
|
||||
import org.compiere.model.MAccount;
|
||||
import org.compiere.model.MAcctSchema;
|
||||
import org.compiere.model.MAttributeSet;
|
||||
import org.compiere.model.MAttributeSetInstance;
|
||||
import org.compiere.model.MClient;
|
||||
import org.compiere.model.MClientInfo;
|
||||
import org.compiere.model.MCost;
|
||||
import org.compiere.model.MCostElement;
|
||||
import org.compiere.model.MLocator;
|
||||
import org.compiere.model.MOrderLine;
|
||||
import org.compiere.model.MOrg;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.MProductCategory;
|
||||
import org.compiere.model.MProduction;
|
||||
import org.compiere.model.MProductionLine;
|
||||
import org.compiere.model.MProductionLineMA;
|
||||
import org.compiere.model.MProductionPlan;
|
||||
import org.compiere.model.MRequisitionLine;
|
||||
import org.compiere.model.MSequence;
|
||||
import org.compiere.model.MStorageOnHand;
|
||||
import org.compiere.model.MStorageReservation;
|
||||
import org.compiere.model.MTable;
|
||||
import org.compiere.model.MTransaction;
|
||||
import org.compiere.model.MTree;
|
||||
import org.compiere.model.MTreeNode;
|
||||
import org.compiere.model.MTree_Base;
|
||||
import org.compiere.model.MWarehouse;
|
||||
import org.compiere.model.ModelValidationEngine;
|
||||
import org.compiere.model.ModelValidator;
|
||||
import org.compiere.model.Query;
|
||||
import org.compiere.model.X_AD_Tree;
|
||||
import org.compiere.model.X_M_CostElement;
|
||||
import org.compiere.model.X_M_Product;
|
||||
import org.compiere.process.DocAction;
|
||||
import org.compiere.util.CLogger;
|
||||
import org.compiere.util.DB;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.Msg;
|
||||
import org.compiere.util.Util;
|
||||
import org.eevolution.model.MPPProductBOM;
|
||||
import org.eevolution.model.MPPProductBOMLine;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
public class MID_MStorageOnHand extends MStorageOnHand{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
private static final long serialVersionUID = 1563913040977233621L;
|
||||
private boolean isValueEmptyBeforeSave = false;
|
||||
private static CLogger s_log = CLogger.getCLogger (MCost.class);
|
||||
|
||||
public MID_MStorageOnHand(Properties ctx, String M_StorageOnHand_UU, String trxName) {
|
||||
super(ctx, M_StorageOnHand_UU, trxName);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
/**
|
||||
* @param ctx
|
||||
* @param M_Locator_ID
|
||||
* @param M_Product_ID
|
||||
* @param M_AttributeSetInstance_ID
|
||||
* @param trxName
|
||||
* @deprecated
|
||||
* @return MStorageOnHand
|
||||
*/
|
||||
@Deprecated
|
||||
public static MID_MStorageOnHand get (Properties ctx, int M_Locator_ID,
|
||||
int M_Product_ID, int M_AttributeSetInstance_ID, String trxName) {
|
||||
return get (ctx, M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID, null, trxName);
|
||||
}
|
||||
/**
|
||||
* NEW MStorageOnHand Constructor
|
||||
* @param locator (parent) locator
|
||||
* @param M_Product_ID product
|
||||
* @param M_AttributeSetInstance_ID attribute
|
||||
* @param dateMPolicy
|
||||
*/
|
||||
private MID_MStorageOnHand (MLocator locator, int M_Product_ID, int M_AttributeSetInstance_ID, Timestamp dateMPolicy)
|
||||
{
|
||||
super(locator.getCtx(), 0, locator.get_TrxName());
|
||||
setClientOrg(locator);
|
||||
setM_Locator_ID (locator.getM_Locator_ID());
|
||||
setM_Product_ID (M_Product_ID);
|
||||
setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID);
|
||||
dateMPolicy = Util.removeTime(dateMPolicy);
|
||||
setDateMaterialPolicy(dateMPolicy);
|
||||
} // MStorageOnHand
|
||||
/**
|
||||
* Create or Get On Hand Storage
|
||||
* @param ctx context
|
||||
* @param M_Locator_ID locator
|
||||
* @param M_Product_ID product
|
||||
* @param M_AttributeSetInstance_ID instance
|
||||
* @param dateMPolicy optional DateMaterialPolicy filter
|
||||
* @param trxName transaction
|
||||
* @param forUpdate true to acquire DB lock with FOR UPDATE clause
|
||||
* @return existing or new MStorageOnHand
|
||||
*/
|
||||
public static MID_MStorageOnHand getCreate (Properties ctx, int M_Locator_ID,
|
||||
int M_Product_ID, int M_AttributeSetInstance_ID,Timestamp dateMPolicy, String trxName, boolean forUpdate)
|
||||
{
|
||||
return getCreate(ctx, M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID, dateMPolicy, trxName, forUpdate, 0);
|
||||
}
|
||||
/**
|
||||
* Create or Get On Hand Storage
|
||||
* @param ctx context
|
||||
* @param M_Locator_ID locator
|
||||
* @param M_Product_ID product
|
||||
* @param M_AttributeSetInstance_ID instance
|
||||
* @param dateMPolicy optional DateMaterialPolicy filter
|
||||
* @param trxName transaction
|
||||
* @param forUpdate true to acquire DB lock with FOR UPDATE clause
|
||||
* @param timeout if forUpdate is true, value for query timeout (0 for no timeout).
|
||||
* @return existing or new MStorageOnHand
|
||||
*/
|
||||
public static MID_MStorageOnHand getCreate (Properties ctx, int M_Locator_ID,
|
||||
int M_Product_ID, int M_AttributeSetInstance_ID,Timestamp dateMPolicy, String trxName, boolean forUpdate, int timeout)
|
||||
{
|
||||
// Retrieve or create the base MStorageOnHand object
|
||||
MStorageOnHand so = MStorageOnHand.getCreate(ctx, M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID, dateMPolicy, trxName, false);
|
||||
|
||||
// Ensure the returned object is of type MID_MStorageOnHand
|
||||
if (so instanceof MID_MStorageOnHand) {
|
||||
return (MID_MStorageOnHand) so;
|
||||
} else {
|
||||
// Create a new MID_MStorageOnHand instance if necessary
|
||||
MID_MStorageOnHand mid_so = new MID_MStorageOnHand(ctx, so.getM_StorageOnHand_UU(), trxName);
|
||||
mid_so.setM_Locator_ID(so.getM_Locator_ID());
|
||||
mid_so.setM_Product_ID(so.getM_Product_ID());
|
||||
mid_so.setM_AttributeSetInstance_ID(so.getM_AttributeSetInstance_ID());
|
||||
mid_so.setDateMaterialPolicy(so.getDateMaterialPolicy());
|
||||
mid_so.setQtyOnHand(so.getQtyOnHand());
|
||||
return mid_so;
|
||||
}
|
||||
} // getCreate
|
||||
/**
|
||||
* Get On Hand Storage
|
||||
* @param ctx context
|
||||
* @param M_Locator_ID locator
|
||||
* @param M_Product_ID product
|
||||
* @param M_AttributeSetInstance_ID instance
|
||||
* @param dateMPolicy optional DateMaterialPolicy filter
|
||||
* @param trxName transaction
|
||||
* @return existing MStorageOnHand or null
|
||||
*/
|
||||
public static MID_MStorageOnHand get (Properties ctx, int M_Locator_ID,
|
||||
int M_Product_ID, int M_AttributeSetInstance_ID,Timestamp dateMPolicy, String trxName)
|
||||
{
|
||||
String sqlWhere = "M_Locator_ID=? AND M_Product_ID=? AND ";
|
||||
if (M_AttributeSetInstance_ID == 0)
|
||||
sqlWhere += "(M_AttributeSetInstance_ID=? OR M_AttributeSetInstance_ID IS NULL)";
|
||||
else
|
||||
sqlWhere += "M_AttributeSetInstance_ID=?";
|
||||
|
||||
if (dateMPolicy != null)
|
||||
sqlWhere += " AND DateMaterialPolicy=trunc(cast(? as date))";
|
||||
|
||||
Query query = new Query(ctx, MID_MStorageOnHand.Table_Name, sqlWhere, trxName);
|
||||
if (dateMPolicy != null)
|
||||
query.setParameters(M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID, dateMPolicy);
|
||||
else
|
||||
query.setParameters(M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID);
|
||||
MID_MStorageOnHand retValue = query.first();
|
||||
|
||||
if (retValue == null) {
|
||||
if (s_log.isLoggable(Level.FINE)) s_log.fine("Not Found - M_Locator_ID=" + M_Locator_ID
|
||||
+ ", M_Product_ID=" + M_Product_ID + ", M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID);
|
||||
} else {
|
||||
if (s_log.isLoggable(Level.FINE)) s_log.fine("M_Locator_ID=" + M_Locator_ID
|
||||
+ ", M_Product_ID=" + M_Product_ID + ", M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID);
|
||||
}
|
||||
return retValue;
|
||||
} // get
|
||||
/**
|
||||
* Create or Get On Hand Storage
|
||||
* @param ctx context
|
||||
* @param M_Locator_ID locator
|
||||
* @param M_Product_ID product
|
||||
* @param M_AttributeSetInstance_ID instance
|
||||
* @param trxName transaction
|
||||
* @return existing or new MStorageOnHand
|
||||
*/
|
||||
public static MID_MStorageOnHand getCreate(Properties ctx, int M_Locator_ID,
|
||||
int M_Product_ID, int M_AttributeSetInstance_ID, Timestamp dateMPolicy, String trxName) {
|
||||
// Retrieve or create the base MStorageOnHand object
|
||||
MStorageOnHand so = MStorageOnHand.getCreate(ctx, M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID, dateMPolicy, trxName, false);
|
||||
|
||||
// Ensure the returned object is of type MID_MStorageOnHand
|
||||
if (so instanceof MID_MStorageOnHand) {
|
||||
return (MID_MStorageOnHand) so;
|
||||
} else {
|
||||
// Create a new MID_MStorageOnHand instance if necessary
|
||||
MID_MStorageOnHand mid_so = new MID_MStorageOnHand(ctx, so.getM_StorageOnHand_UU(), trxName);
|
||||
mid_so.setM_Locator_ID(so.getM_Locator_ID());
|
||||
mid_so.setM_Product_ID(so.getM_Product_ID());
|
||||
mid_so.setM_AttributeSetInstance_ID(so.getM_AttributeSetInstance_ID());
|
||||
mid_so.setDateMaterialPolicy(so.getDateMaterialPolicy());
|
||||
mid_so.setQtyOnHand(so.getQtyOnHand());
|
||||
return mid_so;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add addition to quantity on hand with direct SQL - not using cached value - solving IDEMPIERE-2629
|
||||
* @param addition
|
||||
*/
|
||||
public void addQtyOnHand(BigDecimal addition) {
|
||||
final String sql = "UPDATE M_StorageOnHand SET QtyOnHand=QtyOnHand+?, Updated=getDate(), UpdatedBy=? " +
|
||||
"WHERE M_Product_ID=? AND M_Locator_ID=? AND M_AttributeSetInstance_ID=? AND DateMaterialPolicy=?";
|
||||
DB.executeUpdateEx(sql,
|
||||
new Object[] {addition, Env.getAD_User_ID(Env.getCtx()), getM_Product_ID(), getM_Locator_ID(), getM_AttributeSetInstance_ID(), getDateMaterialPolicy()},
|
||||
get_TrxName());
|
||||
load(get_TrxName());
|
||||
if (getQtyOnHand().signum() == -1) {
|
||||
MWarehouse wh = MWarehouse.get(Env.getCtx(), getM_Warehouse_ID());
|
||||
if (wh.isDisallowNegativeInv()) {
|
||||
MLocator loc = MLocator.get(Env.getCtx(), getM_Locator_ID());
|
||||
// boolean isEnableNegative "Y" / "N" in M_Locator
|
||||
boolean isEnableNegative = (boolean) loc.get_Value("IsEnableNegative");
|
||||
log.info("addQtyOnHand - Locator=" + loc.get_ValueAsString("Value") + ", QtyOnHand=" + getQtyOnHand() + ", addition=" + addition
|
||||
+ ", isEnableNegative=" + isEnableNegative + "Value of M_Locator.IsEnableNegative is " + loc.get_Value("IsEnableNegative"));
|
||||
if (!isEnableNegative) {
|
||||
throw new NegativeInventoryDisallowedException(getCtx(), getM_Warehouse_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(), getM_Locator_ID(),
|
||||
getQtyOnHand().subtract(addition), addition.negate());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
package balinusa.midsuit.model;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.adempiere.exceptions.AdempiereException;
|
||||
import org.compiere.model.I_M_ProductionPlan;
|
||||
import org.compiere.model.MAccount;
|
||||
import org.compiere.model.MAcctSchema;
|
||||
import org.compiere.model.MAttributeSetInstance;
|
||||
import org.compiere.model.MClient;
|
||||
import org.compiere.model.MClientInfo;
|
||||
import org.compiere.model.MCost;
|
||||
import org.compiere.model.MCostElement;
|
||||
import org.compiere.model.MOrderLine;
|
||||
import org.compiere.model.MOrg;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.MProductCategory;
|
||||
import org.compiere.model.MProduction;
|
||||
import org.compiere.model.MProductionLine;
|
||||
import org.compiere.model.MProductionPlan;
|
||||
import org.compiere.model.MRequisitionLine;
|
||||
import org.compiere.model.MSequence;
|
||||
import org.compiere.model.MStorageOnHand;
|
||||
import org.compiere.model.MStorageReservation;
|
||||
import org.compiere.model.MTable;
|
||||
import org.compiere.model.MTree;
|
||||
import org.compiere.model.MTreeNode;
|
||||
import org.compiere.model.MTree_Base;
|
||||
import org.compiere.model.ModelValidationEngine;
|
||||
import org.compiere.model.ModelValidator;
|
||||
import org.compiere.model.Query;
|
||||
import org.compiere.model.X_AD_Tree;
|
||||
import org.compiere.model.X_M_CostElement;
|
||||
import org.compiere.model.X_M_Product;
|
||||
import org.compiere.process.DocAction;
|
||||
import org.compiere.util.CLogger;
|
||||
import org.compiere.util.DB;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.Msg;
|
||||
import org.compiere.util.Util;
|
||||
import org.eevolution.model.MPPProductBOM;
|
||||
import org.eevolution.model.MPPProductBOMLine;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
public class MID_Production extends MProduction{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
private static final long serialVersionUID = 1563913040977233621L;
|
||||
private boolean isValueEmptyBeforeSave = false;
|
||||
private static CLogger s_log = CLogger.getCLogger (MCost.class);
|
||||
|
||||
public MID_Production(Properties ctx, int M_Production_ID, String trxName) {
|
||||
super(ctx, M_Production_ID, trxName);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
/**
|
||||
* @param lines
|
||||
* @return true if one of the production is an end product line (IsEndProduct=Y).
|
||||
*/
|
||||
private boolean isHaveEndProduct(MProductionLine[] lines) {
|
||||
|
||||
for(MProductionLine line : lines) {
|
||||
if(line.isEndProduct())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process production lines - create material transaction
|
||||
* @param lines
|
||||
* @return error message or empty string
|
||||
*/
|
||||
protected Object processLines(MProductionLine[] lines) {
|
||||
StringBuilder errors = new StringBuilder();
|
||||
for ( int i = 0; i<lines.length; i++) {
|
||||
// convert MProductionLine to MID_ProductionLine
|
||||
MID_ProductionLine productionline = new MID_ProductionLine(getCtx(), lines[i].getM_ProductionLine_ID(), get_TrxName());
|
||||
String error = productionline.createTransactions(getMovementDate(), false);
|
||||
if (!Util.isEmpty(error)) {
|
||||
errors.append(error);
|
||||
} else {
|
||||
lines[i].setProcessed( true );
|
||||
lines[i].saveEx(get_TrxName());
|
||||
}
|
||||
}
|
||||
|
||||
return errors.toString();
|
||||
}
|
||||
@Override
|
||||
public String completeIt()
|
||||
{
|
||||
// Re-Check
|
||||
if (!m_justPrepared)
|
||||
{
|
||||
String status = prepareIt();
|
||||
m_justPrepared = false;
|
||||
if (!DocAction.STATUS_InProgress.equals(status))
|
||||
return status;
|
||||
}
|
||||
|
||||
// Set the definite document number after completed (if needed)
|
||||
setDefiniteDocumentNo();
|
||||
|
||||
m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE);
|
||||
if (m_processMsg != null)
|
||||
return DocAction.STATUS_Invalid;
|
||||
|
||||
StringBuilder errors = new StringBuilder();
|
||||
int processed = 0;
|
||||
|
||||
if (!isUseProductionPlan()) {
|
||||
MProductionLine[] lines = getLines();
|
||||
//IDEMPIERE-3107 Check if End Product in Production Lines exist
|
||||
if(!isHaveEndProduct(lines)) {
|
||||
m_processMsg = "Production does not contain End Product";
|
||||
return DocAction.STATUS_Invalid;
|
||||
}
|
||||
errors.append(processLines(lines));
|
||||
if (errors.length() > 0) {
|
||||
m_processMsg = errors.toString();
|
||||
return DocAction.STATUS_Invalid;
|
||||
}
|
||||
processed = processed + lines.length;
|
||||
} else {
|
||||
Query planQuery = new Query(Env.getCtx(), I_M_ProductionPlan.Table_Name, "M_ProductionPlan.M_Production_ID=?", get_TrxName());
|
||||
List<MProductionPlan> plans = planQuery.setParameters(getM_Production_ID()).list();
|
||||
for(MProductionPlan plan : plans) {
|
||||
MProductionLine[] lines = plan.getLines();
|
||||
|
||||
//IDEMPIERE-3107 Check if End Product in Production Lines exist
|
||||
if(!isHaveEndProduct(lines)) {
|
||||
m_processMsg = String.format("Production plan (line %1$d id %2$d) does not contain End Product", plan.getLine(), plan.get_ID());
|
||||
return DocAction.STATUS_Invalid;
|
||||
}
|
||||
|
||||
if (lines.length > 0) {
|
||||
errors.append(processLines(lines));
|
||||
if (errors.length() > 0) {
|
||||
m_processMsg = errors.toString();
|
||||
return DocAction.STATUS_Invalid;
|
||||
}
|
||||
processed = processed + lines.length;
|
||||
}
|
||||
plan.setProcessed(true);
|
||||
plan.saveEx();
|
||||
}
|
||||
}
|
||||
|
||||
// User Validation
|
||||
String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE);
|
||||
if (valid != null)
|
||||
{
|
||||
m_processMsg = valid;
|
||||
return DocAction.STATUS_Invalid;
|
||||
}
|
||||
|
||||
setProcessed(true);
|
||||
setDocAction(DOCACTION_Close);
|
||||
return DocAction.STATUS_Completed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
package balinusa.midsuit.model;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.adempiere.exceptions.AdempiereException;
|
||||
import org.compiere.model.I_M_AttributeSet;
|
||||
import org.compiere.model.I_M_Cost;
|
||||
import org.compiere.model.I_M_ProductionPlan;
|
||||
import org.compiere.model.MAccount;
|
||||
import org.compiere.model.MAcctSchema;
|
||||
import org.compiere.model.MAttributeSet;
|
||||
import org.compiere.model.MAttributeSetInstance;
|
||||
import org.compiere.model.MClient;
|
||||
import org.compiere.model.MClientInfo;
|
||||
import org.compiere.model.MCost;
|
||||
import org.compiere.model.MCostElement;
|
||||
import org.compiere.model.MLocator;
|
||||
import org.compiere.model.MOrderLine;
|
||||
import org.compiere.model.MOrg;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.MProductCategory;
|
||||
import org.compiere.model.MProduction;
|
||||
import org.compiere.model.MProductionLine;
|
||||
import org.compiere.model.MProductionLineMA;
|
||||
import org.compiere.model.MProductionPlan;
|
||||
import org.compiere.model.MRequisitionLine;
|
||||
import org.compiere.model.MSequence;
|
||||
import org.compiere.model.MStorageOnHand;
|
||||
import org.compiere.model.MStorageReservation;
|
||||
import org.compiere.model.MTable;
|
||||
import org.compiere.model.MTransaction;
|
||||
import org.compiere.model.MTree;
|
||||
import org.compiere.model.MTreeNode;
|
||||
import org.compiere.model.MTree_Base;
|
||||
import org.compiere.model.ModelValidationEngine;
|
||||
import org.compiere.model.ModelValidator;
|
||||
import org.compiere.model.Query;
|
||||
import org.compiere.model.X_AD_Tree;
|
||||
import org.compiere.model.X_M_CostElement;
|
||||
import org.compiere.model.X_M_Product;
|
||||
import org.compiere.process.DocAction;
|
||||
import org.compiere.util.CLogger;
|
||||
import org.compiere.util.DB;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.Msg;
|
||||
import org.compiere.util.Util;
|
||||
import org.eevolution.model.MPPProductBOM;
|
||||
import org.eevolution.model.MPPProductBOMLine;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
|
||||
public class MID_ProductionLine extends MProductionLine{
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
private static final long serialVersionUID = 1563913040977233621L;
|
||||
private boolean isValueEmptyBeforeSave = false;
|
||||
private static CLogger s_log = CLogger.getCLogger (MCost.class);
|
||||
|
||||
public MID_ProductionLine(Properties ctx, int M_ProductionLine_ID, String trxName) {
|
||||
super(ctx, M_ProductionLine_ID, trxName, (String[]) null);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
/**
|
||||
* Create material transactions
|
||||
* @param date
|
||||
* @param mustBeStocked true to verify on hand quantity
|
||||
* @return "" for success, error message if failed
|
||||
*/
|
||||
public String createTransactions(Timestamp date, boolean mustBeStocked) {
|
||||
int reversalId = getProductionReversalId ();
|
||||
if (reversalId <= 0 )
|
||||
{
|
||||
// delete existing ASI records
|
||||
int deleted = deleteMA();
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, "Deleted " + deleted + " attribute records ");
|
||||
}
|
||||
MProduct prod = new MProduct(getCtx(), getM_Product_ID(), get_TrxName());
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE,"Loaded Product " + prod.toString());
|
||||
|
||||
if ( !prod.isStocked() || prod.getProductType().compareTo(MProduct.PRODUCTTYPE_Item ) != 0 ) {
|
||||
// no need to do any movements
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, "Production Line " + getLine() + " does not require stock movement");
|
||||
return "";
|
||||
}
|
||||
StringBuilder errorString = new StringBuilder();
|
||||
|
||||
MAttributeSetInstance asi = new MAttributeSetInstance(getCtx(), getM_AttributeSetInstance_ID(), get_TrxName());
|
||||
I_M_AttributeSet attributeset = prod.getM_AttributeSet_ID() > 0 ? MAttributeSet.get(prod.getM_AttributeSet_ID()) : null;
|
||||
boolean isAutoGenerateLot = false;
|
||||
if (attributeset != null)
|
||||
isAutoGenerateLot = attributeset.isAutoGenerateLot();
|
||||
String asiString = asi.get_ID() > 0 ? asi.getDescription() : "";
|
||||
if ( asiString == null )
|
||||
asiString = "";
|
||||
|
||||
if (log.isLoggable(Level.FINEST)) log.log(Level.FINEST, "asi Description is: " + asiString);
|
||||
// create transactions for finished goods
|
||||
if ( getM_Product_ID() == getEndProduct_ID()) {
|
||||
if (reversalId <= 0 && isAutoGenerateLot && getM_AttributeSetInstance_ID() == 0)
|
||||
{
|
||||
asi = MAttributeSetInstance.generateLot(getCtx(), (MProduct)getM_Product(), get_TrxName());
|
||||
setM_AttributeSetInstance_ID(asi.getM_AttributeSetInstance_ID());
|
||||
}
|
||||
Timestamp dateMPolicy = date;
|
||||
if(getM_AttributeSetInstance_ID()>0){
|
||||
Timestamp t = MStorageOnHand.getDateMaterialPolicy(getM_Product_ID(), getM_AttributeSetInstance_ID(), getM_Locator_ID(), get_TrxName());
|
||||
if (t != null)
|
||||
dateMPolicy = t;
|
||||
}
|
||||
|
||||
dateMPolicy = Util.removeTime(dateMPolicy);
|
||||
//for reversal, keep the ma copy from original trx
|
||||
if (reversalId <= 0 )
|
||||
{
|
||||
MProductionLineMA lineMA = new MProductionLineMA( this,
|
||||
asi.get_ID(), getMovementQty(),dateMPolicy);
|
||||
if ( !lineMA.save(get_TrxName()) ) {
|
||||
log.log(Level.SEVERE, "Could not save MA for " + toString());
|
||||
errorString.append("Could not save MA for " + toString() + "\n" );
|
||||
}
|
||||
}
|
||||
MTransaction matTrx = new MTransaction (getCtx(), getAD_Org_ID(),
|
||||
"P+",
|
||||
getM_Locator_ID(), getM_Product_ID(), asi.get_ID(),
|
||||
getMovementQty(), date, get_TrxName());
|
||||
matTrx.setM_ProductionLine_ID(get_ID());
|
||||
if ( !matTrx.save(get_TrxName()) ) {
|
||||
log.log(Level.SEVERE, "Could not save transaction for " + toString());
|
||||
errorString.append("Could not save transaction for " + toString() + "\n");
|
||||
}
|
||||
MID_MStorageOnHand storage = MID_MStorageOnHand.getCreate(getCtx(), getM_Locator_ID(),
|
||||
getM_Product_ID(), asi.get_ID(),dateMPolicy, get_TrxName());
|
||||
storage.addQtyOnHand(getMovementQty());
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, "Created finished goods line " + getLine());
|
||||
|
||||
return errorString.toString();
|
||||
}
|
||||
|
||||
// create transactions and update stock used in production
|
||||
MStorageOnHand[] storages = MStorageOnHand.getAll( getCtx(), getM_Product_ID(),
|
||||
getM_Locator_ID(), get_TrxName(), false, 0);
|
||||
|
||||
MProductionLineMA lineMA = null;
|
||||
MTransaction matTrx = null;
|
||||
BigDecimal qtyToMove = getMovementQty().negate();
|
||||
|
||||
if (qtyToMove.signum() > 0) {
|
||||
for (int sl = 0; sl < storages.length; sl++) {
|
||||
|
||||
BigDecimal lineQty = storages[sl].getQtyOnHand();
|
||||
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, "QtyAvailable " + lineQty );
|
||||
if (lineQty.signum() > 0)
|
||||
{
|
||||
if (lineQty.compareTo(qtyToMove ) > 0)
|
||||
lineQty = qtyToMove;
|
||||
|
||||
MAttributeSetInstance slASI = storages[sl].getM_AttributeSetInstance_ID() > 0 ? new MAttributeSetInstance(getCtx(),
|
||||
storages[sl].getM_AttributeSetInstance_ID(),get_TrxName()) : null;
|
||||
String slASIString = slASI != null ? slASI.getDescription() : "";
|
||||
if (slASIString == null)
|
||||
slASIString = "";
|
||||
|
||||
if (log.isLoggable(Level.FINEST))log.log(Level.FINEST,"slASI-Description =" + slASIString);
|
||||
|
||||
if (asi.getM_AttributeSet_ID() == 0 || slASIString.equals(asiString))
|
||||
//storage matches specified ASI or is a costing asi (inc. 0)
|
||||
// This process will move negative stock on hand quantities
|
||||
{
|
||||
lineMA = MProductionLineMA.get(this,storages[sl].getM_AttributeSetInstance_ID(),storages[sl].getDateMaterialPolicy());
|
||||
lineMA.setMovementQty(lineMA.getMovementQty().add(lineQty.negate()));
|
||||
if ( !lineMA.save(get_TrxName()) ) {
|
||||
log.log(Level.SEVERE, "Could not save MA for " + toString());
|
||||
errorString.append("Could not save MA for " + toString() + "\n" );
|
||||
} else {
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, "Saved MA for " + toString());
|
||||
}
|
||||
matTrx = new MTransaction (getCtx(), getAD_Org_ID(),
|
||||
"P-",
|
||||
getM_Locator_ID(), getM_Product_ID(), lineMA.getM_AttributeSetInstance_ID(),
|
||||
lineQty.negate(), date, get_TrxName());
|
||||
matTrx.setM_ProductionLine_ID(get_ID());
|
||||
if ( !matTrx.save(get_TrxName()) ) {
|
||||
log.log(Level.SEVERE, "Could not save transaction for " + toString());
|
||||
errorString.append("Could not save transaction for " + toString() + "\n");
|
||||
} else {
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, "Saved transaction for " + toString());
|
||||
}
|
||||
DB.getDatabase().forUpdate(storages[sl], 120);
|
||||
storages[sl].addQtyOnHand(lineQty.negate());
|
||||
qtyToMove = qtyToMove.subtract(lineQty);
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, getLine() + " Qty moved = " + lineQty + ", Remaining = " + qtyToMove );
|
||||
}
|
||||
}
|
||||
|
||||
if ( qtyToMove.signum() == 0 )
|
||||
break;
|
||||
|
||||
} // for available storages
|
||||
}
|
||||
else if (qtyToMove.signum() < 0 )
|
||||
{
|
||||
|
||||
MClientInfo m_clientInfo = MClientInfo.get(getCtx(), getAD_Client_ID(), get_TrxName());
|
||||
MAcctSchema acctSchema = new MAcctSchema(getCtx(), m_clientInfo.getC_AcctSchema1_ID(), get_TrxName());
|
||||
if (asi.get_ID() == 0 && MAcctSchema.COSTINGLEVEL_BatchLot.equals(prod.getCostingLevel(acctSchema)) )
|
||||
{
|
||||
//add quantity to last attributesetinstance
|
||||
String sqlWhere = "M_Product_ID=? AND M_Locator_ID=? AND M_AttributeSetInstance_ID > 0 ";
|
||||
MStorageOnHand storage = new Query(getCtx(), MStorageOnHand.Table_Name, sqlWhere, get_TrxName())
|
||||
.setParameters(getM_Product_ID(), getM_Locator_ID())
|
||||
.setOrderBy(MStorageOnHand.COLUMNNAME_DateMaterialPolicy+" DESC,"+ MStorageOnHand.COLUMNNAME_M_AttributeSetInstance_ID +" DESC")
|
||||
.first();
|
||||
|
||||
if (storage != null)
|
||||
{
|
||||
setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID());
|
||||
asi = new MAttributeSetInstance(getCtx(), storage.getM_AttributeSetInstance_ID(), get_TrxName());
|
||||
asiString = asi.get_ID() > 0 ? asi.getDescription() : "";
|
||||
}
|
||||
else
|
||||
{
|
||||
String costingMethod = prod.getCostingMethod(acctSchema);
|
||||
StringBuilder localWhereClause = new StringBuilder("M_Product_ID =?" )
|
||||
.append(" AND C_AcctSchema_ID=?")
|
||||
.append(" AND ce.CostingMethod = ? ")
|
||||
.append(" AND CurrentCostPrice <> 0 ");
|
||||
MCost cost = new Query(getCtx(),I_M_Cost.Table_Name,localWhereClause.toString(),get_TrxName())
|
||||
.setParameters(getM_Product_ID(), acctSchema.get_ID(), costingMethod)
|
||||
.addJoinClause(" INNER JOIN M_CostElement ce ON (M_Cost.M_CostElement_ID =ce.M_CostElement_ID ) ")
|
||||
.setOrderBy("Updated DESC")
|
||||
.first();
|
||||
if (cost != null)
|
||||
{
|
||||
setM_AttributeSetInstance_ID(cost.getM_AttributeSetInstance_ID());
|
||||
asi = new MAttributeSetInstance(getCtx(), cost.getM_AttributeSetInstance_ID(), get_TrxName());
|
||||
asiString = asi.getDescription();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
log.log(Level.SEVERE, "Cannot retrieve cost of Product r " + prod.toString());
|
||||
errorString.append( "Cannot retrieve cost of Product " +prod.toString() ) ;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if ( !( qtyToMove.signum() == 0) ) {
|
||||
if (mustBeStocked && qtyToMove.signum() > 0)
|
||||
{
|
||||
MLocator loc = new MLocator(getCtx(), getM_Locator_ID(), get_TrxName());
|
||||
errorString.append( "Insufficient qty on hand of " + prod.toString() + " at "
|
||||
+ loc.toString() + "\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
MID_MStorageOnHand storage = MID_MStorageOnHand.getCreate(Env.getCtx(), getM_Locator_ID(), getM_Product_ID(),
|
||||
asi.get_ID(), date, get_TrxName(), true);
|
||||
|
||||
BigDecimal lineQty = qtyToMove;
|
||||
MAttributeSetInstance slASI = storage.getM_AttributeSetInstance_ID() > 0
|
||||
? new MAttributeSetInstance(getCtx(), storage.getM_AttributeSetInstance_ID(),get_TrxName()) : null;
|
||||
String slASIString = slASI != null ? slASI.getDescription() : "";
|
||||
if (slASIString == null)
|
||||
slASIString = "";
|
||||
|
||||
if (log.isLoggable(Level.FINEST))log.log(Level.FINEST,"slASI-Description =" + slASIString);
|
||||
|
||||
if (asi.getM_AttributeSet_ID() == 0 || slASIString.compareTo(asiString) == 0)
|
||||
//storage matches specified ASI or is a costing asi (inc. 0)
|
||||
// This process will move negative stock on hand quantities
|
||||
{
|
||||
lineMA = MProductionLineMA.get(this,storage.getM_AttributeSetInstance_ID(),storage.getDateMaterialPolicy());
|
||||
lineMA.setMovementQty(lineMA.getMovementQty().add(lineQty.negate()));
|
||||
|
||||
if ( !lineMA.save(get_TrxName()) ) {
|
||||
log.log(Level.SEVERE, "Could not save MA for " + toString());
|
||||
errorString.append("Could not save MA for " + toString() + "\n" );
|
||||
} else {
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, "Saved MA for " + toString());
|
||||
}
|
||||
matTrx = new MTransaction (getCtx(), getAD_Org_ID(),
|
||||
"P-",
|
||||
getM_Locator_ID(), getM_Product_ID(), asi.get_ID(),
|
||||
lineQty.negate(), date, get_TrxName());
|
||||
matTrx.setM_ProductionLine_ID(get_ID());
|
||||
if ( !matTrx.save(get_TrxName()) ) {
|
||||
log.log(Level.SEVERE, "Could not save transaction for " + toString());
|
||||
errorString.append("Could not save transaction for " + toString() + "\n");
|
||||
} else {
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, "Saved transaction for " + toString());
|
||||
}
|
||||
storage.addQtyOnHand(lineQty.negate());
|
||||
qtyToMove = qtyToMove.subtract(lineQty);
|
||||
if (log.isLoggable(Level.FINE))log.log(Level.FINE, getLine() + " Qty moved = " + lineQty + ", Remaining = " + qtyToMove );
|
||||
} else {
|
||||
errorString.append( "Storage doesn't match ASI " + prod.toString() + " / "
|
||||
+ slASIString + " vs. " + asiString + "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return errorString.toString();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
select
|
||||
inv.ad_client_id,
|
||||
inv.ad_org_id,
|
||||
bp.name as toko,
|
||||
inv.documentdate as tanggal,
|
||||
dt.name as doctype,
|
||||
row_number() over() as no,
|
||||
case
|
||||
when invl.c_charge_id is null then prod.name
|
||||
else ch.name
|
||||
end as product,
|
||||
inv.description as keterangan,
|
||||
inv.documentno,
|
||||
inv.dateinvoiced,
|
||||
pt.name as payterm,
|
||||
case
|
||||
when inv.paymentrule = 'P' then 'On Credit'
|
||||
else inv.paymentrule
|
||||
end as payrule,
|
||||
co.documentno as pono,
|
||||
co.dateordered as podate,
|
||||
io.documentno as shipno,
|
||||
asi.lot,
|
||||
inv.totallines,
|
||||
uom.uomsymbol as uom,
|
||||
cur.iso_code as cur,
|
||||
case
|
||||
when inv.docstatus = 'CO' then 'Completed'
|
||||
when inv.docstatus = 'VO' then 'Void'
|
||||
when inv.docstatus = 'DR' then 'Draft'
|
||||
when inv.docstatus = 'RE' then 'Reversed'
|
||||
when inv.docstatus = 'IN' then 'Invalid'
|
||||
when inv.docstatus = 'CL' then 'Closed'
|
||||
end as docstatus,
|
||||
co.grandtotal,
|
||||
(
|
||||
select
|
||||
sum(co.grandtotal) as grandtotalorder
|
||||
from
|
||||
(
|
||||
select
|
||||
co.c_order_id,
|
||||
co.grandtotal
|
||||
from
|
||||
c_order co
|
||||
left join c_orderline col on col.c_order_id = co.c_order_id
|
||||
left join c_invoiceline cin on cin.c_orderline_id = col.c_orderline_id
|
||||
where
|
||||
cin.c_invoice_id = 1000231
|
||||
group by
|
||||
co.c_order_id,
|
||||
co.grandtotal
|
||||
) co
|
||||
) as grandtotalorder,
|
||||
(
|
||||
select
|
||||
address1 || ' ' || COALESCE(address2, '')
|
||||
from
|
||||
c_location
|
||||
where
|
||||
c_location_id = bloc.c_location_id
|
||||
) as alamat,
|
||||
-- (
|
||||
-- select
|
||||
-- sum(taxamt)
|
||||
-- from
|
||||
-- c_invoiceline
|
||||
-- where
|
||||
-- c_invoice_id = 1000231
|
||||
-- ) as pajak,
|
||||
coalesce(org.description, ' ') as orgdesc,
|
||||
coalesce(lf.address1, ' ') as o1,
|
||||
coalesce(lf.address2, ' ') as o2,
|
||||
coalesce(lf.address3, ' ') as o3,
|
||||
coalesce(lf.address4, ' ') as o4,
|
||||
coalesce(lf.city, ' ') as city,
|
||||
coalesce(f.phone, ' ') as phone,
|
||||
coalesce(f.fax, ' ') as fax
|
||||
from
|
||||
c_invoice inv
|
||||
left join c_invoiceline invl on invl.c_invoice_id = inv.c_invoice_id
|
||||
left join c_orderline col on col.c_orderline_id = invl.c_orderline_id
|
||||
left join c_order co on co.c_order_id = col.c_order_id
|
||||
left join c_bpartner bp on bp.c_bpartner_id = inv.c_bpartner_id
|
||||
left join c_paymentterm pt on pt.c_paymentterm_id = inv.c_paymentterm_id
|
||||
left join M_AttributeSetInstance asi on asi.M_AttributeSetInstance_id = invl.M_AttributeSetInstance_id
|
||||
left join m_product prod on prod.m_product_id = invl.m_product_id
|
||||
left join c_doctype dt on dt.c_doctype_id = inv.c_doctypetarget_id
|
||||
left join c_bpartner_location bloc on bloc.c_bpartner_location_id = inv.c_bpartner_location_id
|
||||
left join c_uom uom on uom.c_uom_id = invl.c_uom_id
|
||||
left join c_currency cur on cur.c_currency_id = inv.c_currency_id
|
||||
left join c_charge ch on ch.c_charge_id = invl.c_charge_id
|
||||
left join c_chargetype cht on cht.c_chargetype_id = ch.c_chargetype_id
|
||||
left join ad_org org on org.ad_org_id = inv.ad_org_id
|
||||
left join ad_orginfo f on f.ad_org_id = org.ad_org_id
|
||||
left join c_location lf on lf.c_location_id = f.c_location_id
|
||||
left join m_inout io on io.m_inout_id = inv.m_inout_id
|
||||
where
|
||||
inv.c_invoice_id = 1000231
|
||||
Loading…
Reference in New Issue