From bfd3b422d464b82feea8022dcfef5dd172e4ecfa Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Mon, 11 Sep 2017 15:31:27 +0800 Subject: [PATCH 01/21] IDEMPIERE-3468 Lookup Record and field validation rules --- .../adempiere/webui/window/FindWindow.java | 140 +++++++++++++++--- 1 file changed, 122 insertions(+), 18 deletions(-) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/FindWindow.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/FindWindow.java index 854521b354..da77b8e3c2 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/FindWindow.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/FindWindow.java @@ -76,6 +76,7 @@ import org.adempiere.webui.util.ZKUpdateUtil; import org.compiere.model.GridField; import org.compiere.model.GridFieldVO; import org.compiere.model.GridTab; +import org.compiere.model.Lookup; import org.compiere.model.MColumn; import org.compiere.model.MLookup; import org.compiere.model.MLookupFactory; @@ -92,9 +93,11 @@ import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.SecureEngine; +import org.compiere.util.Util; import org.compiere.util.ValueNamePair; import org.zkoss.zk.au.out.AuFocus; import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.Components; import org.zkoss.zk.ui.HtmlBasedComponent; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; @@ -122,10 +125,14 @@ import org.zkoss.zul.Vlayout; */ public class FindWindow extends Window implements EventListener, ValueChangeListener, DialogEvents { + private static final String FIND_ROW_EDITOR = "find.row.editor"; + + private static final String FIND_ROW_EDITOR_TO = "find.row.editor.to"; + /** * */ - private static final long serialVersionUID = -5747652133096022993L; + private static final long serialVersionUID = -4461202150492732658L; // values and label for history combo private static final String HISTORY_DAY_ALL = "All"; @@ -221,7 +228,8 @@ public class FindWindow extends Window implements EventListener, ValueCha private Combobox historyCombo = new Combobox(); - private Properties m_findCtx; + private Properties m_simpleCtx; + private Properties m_advanceCtx; private static final String ON_POST_VISIBLE_ATTR = "onPostVisible.Event.Posted"; @@ -251,7 +259,8 @@ public class FindWindow extends Window implements EventListener, ValueCha m_minRecords = minRecords; m_isCancel = true; // - m_findCtx = new Properties(Env.getCtx()); + m_simpleCtx = new Properties(Env.getCtx()); + m_advanceCtx = new Properties(Env.getCtx()); this.setBorder("normal"); this.setShadow(false); @@ -634,7 +643,7 @@ public class FindWindow extends Window implements EventListener, ValueCha if (mField.getVO().displayType == DisplayType.YesNo) { // Make Yes-No searchable as list GridFieldVO vo = mField.getVO(); - GridFieldVO ynvo = vo.clone(vo.ctx, vo.WindowNo, vo.TabNo, vo.AD_Window_ID, vo.AD_Tab_ID, vo.tabReadOnly); + GridFieldVO ynvo = vo.clone(m_simpleCtx, vo.WindowNo, vo.TabNo, vo.AD_Window_ID, vo.AD_Tab_ID, vo.tabReadOnly); ynvo.IsDisplayed = true; ynvo.displayType = DisplayType.List; ynvo.AD_Reference_Value_ID = REFERENCE_YESNO; @@ -642,6 +651,7 @@ public class FindWindow extends Window implements EventListener, ValueCha ynvo.lookupInfo = MLookupFactory.getLookupInfo (ynvo.ctx, ynvo.WindowNo, ynvo.AD_Column_ID, ynvo.displayType, Env.getLanguage(ynvo.ctx), ynvo.ColumnName, ynvo.AD_Reference_Value_ID, ynvo.IsParent, ynvo.ValidationCode); + ynvo.lookupInfo.tabNo = TABNO; GridField ynfield = new GridField(ynvo); @@ -653,13 +663,14 @@ public class FindWindow extends Window implements EventListener, ValueCha GridFieldVO vo = mField.getVO(); if ( vo.AD_Reference_Value_ID > 0 ) { - GridFieldVO postedvo = vo.clone(vo.ctx, vo.WindowNo, vo.TabNo, vo.AD_Window_ID, vo.AD_Tab_ID, vo.tabReadOnly); + GridFieldVO postedvo = vo.clone(m_simpleCtx, vo.WindowNo, vo.TabNo, vo.AD_Window_ID, vo.AD_Tab_ID, vo.tabReadOnly); postedvo.IsDisplayed = true; postedvo.displayType = DisplayType.List; postedvo.lookupInfo = MLookupFactory.getLookupInfo (postedvo.ctx, postedvo.WindowNo, postedvo.AD_Column_ID, postedvo.displayType, Env.getLanguage(postedvo.ctx), postedvo.ColumnName, postedvo.AD_Reference_Value_ID, postedvo.IsParent, postedvo.ValidationCode); + postedvo.lookupInfo.tabNo = TABNO; GridField postedfield = new GridField(postedvo); @@ -669,7 +680,15 @@ public class FindWindow extends Window implements EventListener, ValueCha } } else { // clone the field and clean gridtab - IDEMPIERE-1105 - GridField findField = (GridField) mField.clone(m_findCtx); + GridField findField = (GridField) mField.clone(m_simpleCtx); + if (findField.isLookup()) { + Lookup lookup = findField.getLookup(); + if (lookup != null && lookup instanceof MLookup) { + MLookup mLookup = (MLookup) lookup; + mLookup.getLookupInfo().ctx = m_simpleCtx; + mLookup.getLookupInfo().tabNo = TABNO; + } + } findField.setGridTab(null); m_findFields[i] = findField; mField = findField; @@ -1058,6 +1077,7 @@ public class FindWindow extends Window implements EventListener, ValueCha editor.setMandatory(false); editor.setReadWrite(true); editor.dynamicDisplay(); + editor.addValueChangeListener(this); Label label = editor.getLabel(); Component fieldEditor = editor.getComponent(); @@ -1075,6 +1095,7 @@ public class FindWindow extends Window implements EventListener, ValueCha editorTo.setMandatory(false); editorTo.setReadWrite(true); editorTo.dynamicDisplay(); + editorTo.addValueChangeListener(this); // if (displayLength > 0) // set it back mField.setDisplayLength(displayLength); @@ -1916,7 +1937,7 @@ public class FindWindow extends Window implements EventListener, ValueCha GridField field = getTargetMField(columnName); if(field == null) return new Label(""); - GridField findField = (GridField) field.clone(m_findCtx); + GridField findField = (GridField) field.clone(m_advanceCtx); findField.setGridTab(null); WEditor editor = null; if (findField.isKey() @@ -1928,6 +1949,7 @@ public class FindWindow extends Window implements EventListener, ValueCha { if (findField.getAD_Reference_Value_ID() > 0) { MLookupInfo info = MLookupFactory.getLookup_List(Env.getLanguage(Env.getCtx()), findField.getAD_Reference_Value_ID()); + info.tabNo = TABNO; MLookup mLookup = new MLookup(info, 0); editor = new WTableDirEditor(columnName, false,false, true, mLookup); findField.addPropertyChangeListener(editor); @@ -1945,7 +1967,13 @@ public class FindWindow extends Window implements EventListener, ValueCha //reload lookupinfo for find window if (DisplayType.isLookup(findField.getDisplayType()) ) { - findField.loadLookupNoValidate(); + findField.loadLookupNoValidate(); + Lookup lookup = findField.getLookup(); + if (lookup != null && lookup instanceof MLookup) + { + MLookup mLookup = (MLookup) lookup; + mLookup.getLookupInfo().tabNo = TABNO; + } editor = WebEditorFactory.getEditor(findField, true); findField.addPropertyChangeListener(editor); } @@ -1970,6 +1998,10 @@ public class FindWindow extends Window implements EventListener, ValueCha ((WPaymentEditor)editor).getComponent().setEnabled(true, false); } // + if (to) + row.setAttribute(FIND_ROW_EDITOR_TO, editor); + else + row.setAttribute(FIND_ROW_EDITOR, editor); return editor.getComponent(); } // getTableCellEditorComponent @@ -2358,20 +2390,92 @@ public class FindWindow extends Window implements EventListener, ValueCha { WEditor editor = (WEditor)evt.getSource(); // Editor component - Component component = editor.getComponent(); - ListCell listcell = (ListCell)component.getParent(); - listcell.setAttribute("value", evt.getNewValue()); - if (evt.getNewValue() == null) - Env.setContext(m_findCtx, m_targetWindowNo, editor.getColumnName(), ""); - else if (evt.getNewValue() instanceof Boolean) - Env.setContext(m_findCtx, m_targetWindowNo, editor.getColumnName(), (Boolean)evt.getNewValue()); - else if (evt.getNewValue() instanceof Timestamp) - Env.setContext(m_findCtx, m_targetWindowNo, editor.getColumnName(), (Timestamp)evt.getNewValue()); + ListCell listcell = null; + Properties ctx = null; + if (winMain.getComponent().getSelectedIndex() == 1) + { + Component component = editor.getComponent(); + listcell = (ListCell)component.getParent(); + listcell.setAttribute("value", evt.getNewValue()); + ctx = m_advanceCtx; + } else - Env.setContext(m_findCtx, m_targetWindowNo, editor.getColumnName(), evt.getNewValue().toString()); + { + ctx = m_simpleCtx; + } + if (evt.getNewValue() == null) + { + Env.setContext(ctx, m_targetWindowNo, editor.getColumnName(), ""); + Env.setContext(ctx, m_targetWindowNo, TABNO, editor.getColumnName(), ""); + } + else if (evt.getNewValue() instanceof Boolean) + { + Env.setContext(ctx, m_targetWindowNo, editor.getColumnName(), (Boolean)evt.getNewValue()); + Env.setContext(ctx, m_targetWindowNo, TABNO, editor.getColumnName(), (Boolean)evt.getNewValue()); + } + else if (evt.getNewValue() instanceof Timestamp) + { + Env.setContext(ctx, m_targetWindowNo, editor.getColumnName(), (Timestamp)evt.getNewValue()); + Env.setContext(ctx, m_targetWindowNo, TABNO + "|" + editor.getColumnName(), (Timestamp)evt.getNewValue()); + } + else + { + Env.setContext(ctx, m_targetWindowNo, editor.getColumnName(), evt.getNewValue().toString()); + Env.setContext(ctx, m_targetWindowNo, TABNO, editor.getColumnName(), evt.getNewValue().toString()); + } + + dynamicDisplay(editor, listcell); } } + private void dynamicDisplay(WEditor editor, ListCell listcell) { + if (winMain.getComponent().getSelectedIndex() == 1) + { + List rowList = advancedPanel.getChildren(); + + for (int rowIndex = 1; rowIndex < rowList.size() ; rowIndex++) + { + // Column + ListItem row = (ListItem)rowList.get(rowIndex); + if (Components.isAncestor(row, listcell)) + continue; + WEditor other = (WEditor) row.getAttribute(FIND_ROW_EDITOR); + if (other != null && other.getGridField() != null && other.getGridField().isLookup()) + { + Lookup lookup = other.getGridField().getLookup(); + if (!Util.isEmpty(lookup.getValidation())) + { + other.dynamicDisplay(); + other = (WEditor) row.getAttribute(FIND_ROW_EDITOR_TO); + if (other != null) + other.dynamicDisplay(); + } + } + } + } + else + { + for (int i = 0; i < m_sEditors.size(); i++) + { + WEditor wed = (WEditor)m_sEditors.get(i); + if (wed == editor) + continue; + if (wed.getGridField() != null && wed.getGridField().isLookup()) + { + Lookup lookup = wed.getGridField().getLookup(); + if (!Util.isEmpty(lookup.getValidation())) + { + wed.dynamicDisplay(); + wed = m_sEditorsTo.get(i); + if (wed != null && wed != editor) + wed.dynamicDisplay(); + } + } + + } + } + } + public void OnPostVisible() { removeAttribute(ON_POST_VISIBLE_ATTR); if (m_sEditors.size() > 0) From a0d891f419d8c81056e283e2708e48e9c04e7d12 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Tue, 12 Sep 2017 18:07:01 +0800 Subject: [PATCH 02/21] IDEMPIERE-3431 Posting error for Matched PO if invoice posted while MR is not posted --- .../src/org/compiere/acct/Doc.java | 13 +++++++++ .../src/org/compiere/acct/Doc_MatchPO.java | 28 ++++++++++++++++++- .../org/compiere/server/AcctProcessor.java | 13 ++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/acct/Doc.java b/org.adempiere.base/src/org/compiere/acct/Doc.java index 6364029fe1..1bc38d49ce 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc.java @@ -555,6 +555,12 @@ public abstract class Doc p_Error = loadDocumentDetails(); if (p_Error != null) return p_Error; + if (isDeferPosting()) + { + unlock(); + p_Status = STATUS_NotPosted; + return null; + } Trx trx = Trx.get(getTrxName(), true); // Delete existing Accounting @@ -2304,4 +2310,11 @@ public abstract class Doc public ArrayList getFacts() { return m_fact; } + + /** + * Return document whether need to defer posting or not + */ + public boolean isDeferPosting() { + return false; + } } // Doc diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_MatchPO.java b/org.adempiere.base/src/org/compiere/acct/Doc_MatchPO.java index 1d271a6b48..87a48da28b 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_MatchPO.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_MatchPO.java @@ -77,6 +77,7 @@ public class Doc_MatchPO extends Doc private ProductCost m_pc; private int m_M_AttributeSetInstance_ID = 0; private MMatchPO m_matchPO; + private boolean m_deferPosting = false; /** * Load Specific Document Details @@ -102,7 +103,25 @@ public class Doc_MatchPO extends Doc // m_pc = new ProductCost (Env.getCtx(), getM_Product_ID(), m_M_AttributeSetInstance_ID, getTrxName()); - m_pc.setQty(getQty()); + m_pc.setQty(getQty()); + + if (m_M_InOutLine_ID == 0) + { + MMatchPO[] matchPOs = MMatchPO.getOrderLine(getCtx(), m_oLine.getC_OrderLine_ID(), getTrxName()); + for (MMatchPO matchPO : matchPOs) + { + if (matchPO.getM_InOutLine_ID() > 0 && matchPO.getC_InvoiceLine_ID() == 0) + { + m_M_InOutLine_ID = matchPO.getM_InOutLine_ID(); + break; + } + } + } + + if (m_M_InOutLine_ID == 0) // Defer posting if not matched to Shipment + { + m_deferPosting = true; + } return null; } // loadDocumentDetails @@ -487,4 +506,11 @@ public class Doc_MatchPO extends Doc return null; } + + @Override + public boolean isDeferPosting() { + return m_deferPosting; + } + + } // Doc_MatchPO diff --git a/org.adempiere.server/src/main/server/org/compiere/server/AcctProcessor.java b/org.adempiere.server/src/main/server/org/compiere/server/AcctProcessor.java index c1c4b22876..09b27b5238 100644 --- a/org.adempiere.server/src/main/server/org/compiere/server/AcctProcessor.java +++ b/org.adempiere.server/src/main/server/org/compiere/server/AcctProcessor.java @@ -34,6 +34,7 @@ import org.compiere.model.MClient; import org.compiere.model.MCost; import org.compiere.model.MOrgInfo; import org.compiere.model.MRole; +import org.compiere.model.MTable; import org.compiere.model.MUser; import org.compiere.util.DB; import org.compiere.util.Env; @@ -224,7 +225,6 @@ public class AcctProcessor extends AdempiereServer rs = pstmt.executeQuery(); while (!isInterrupted() && rs.next()) { - count[i]++; boolean ok = true; try { @@ -238,6 +238,17 @@ public class AcctProcessor extends AdempiereServer } if (!ok) countError[i]++; + else // only count the posted record. + { + MTable table = MTable.get(Env.getCtx(), AD_Table_ID); + int Record_ID = rs.getInt(table.getKeyColumns()[0]); + sql = new StringBuffer("SELECT COUNT(*) FROM ").append(table.getTableName()); + sql.append(" WHERE Posted='Y' AND ").append(table.getTableName()).append("_ID=").append(Record_ID); + int no = DB.getSQLValue(null, sql.toString()); + if (no > 0 ) + count[i]++; + + } } } catch (Exception e) From 4336b27e6ef1f4b3835218eb52a269fa0b8bc8c8 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Wed, 13 Sep 2017 14:49:12 +0800 Subject: [PATCH 03/21] IDEMPIERE-3483 Issue to Project from Material Receipt => Error: Posts Unit Cost instead of Extended Cost --- org.adempiere.base/src/org/compiere/acct/Doc_ProjectIssue.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_ProjectIssue.java b/org.adempiere.base/src/org/compiere/acct/Doc_ProjectIssue.java index 31752cda01..96238d43a5 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_ProjectIssue.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_ProjectIssue.java @@ -203,6 +203,8 @@ public class Doc_ProjectIssue extends Doc DB.close(rs, pstmt); pstmt = null; rs = null; } + if (retValue != null) + retValue = retValue.multiply(m_line.getQty()); return retValue; } // getPOCost(); From 2d8ab00c56fa46697ace9894dcae17b012e0c91a Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Wed, 13 Sep 2017 14:56:54 +0800 Subject: [PATCH 04/21] IDEMPIERE-3484 ProjectIssue should create M_CostDetail record --- .../org/compiere/acct/Doc_ProjectIssue.java | 15 +++++ .../src/org/compiere/model/MCostDetail.java | 64 +++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/org.adempiere.base/src/org/compiere/acct/Doc_ProjectIssue.java b/org.adempiere.base/src/org/compiere/acct/Doc_ProjectIssue.java index 96238d43a5..512a8d120e 100644 --- a/org.adempiere.base/src/org/compiere/acct/Doc_ProjectIssue.java +++ b/org.adempiere.base/src/org/compiere/acct/Doc_ProjectIssue.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.logging.Level; import org.compiere.model.MAcctSchema; +import org.compiere.model.MCostDetail; import org.compiere.model.MProduct; import org.compiere.model.MProject; import org.compiere.model.MProjectIssue; @@ -157,6 +158,20 @@ public class Doc_ProjectIssue extends Doc cr.setM_Locator_ID(m_line.getM_Locator_ID()); cr.setLocationFromLocator(m_line.getM_Locator_ID(), true); // from Loc // + if (product != null && product.get_ID() > 0 && !product.isService() && product.isStocked()) { + BigDecimal costDetailQty = m_line.getQty(); + BigDecimal costDetailAmt = cost; + if (!MCostDetail.createProjectIssue(as, m_line.getAD_Org_ID(), + m_line.getM_Product_ID(), m_line.getM_AttributeSetInstance_ID(), + m_line.get_ID(), 0, + costDetailAmt, costDetailQty, + m_line.getDescription(), getTrxName())) + { + p_Error = "Failed to create cost detail record"; + return null; + } + } + // ArrayList facts = new ArrayList(); facts.add(fact); return facts; diff --git a/org.adempiere.base/src/org/compiere/model/MCostDetail.java b/org.adempiere.base/src/org/compiere/model/MCostDetail.java index 4cc2aa662c..6333bec9ec 100644 --- a/org.adempiere.base/src/org/compiere/model/MCostDetail.java +++ b/org.adempiere.base/src/org/compiere/model/MCostDetail.java @@ -529,6 +529,70 @@ public class MCostDetail extends X_M_CostDetail return ok; } // createMatchInvoice + /** + * Create Cost Detail for Project Issue. + * Called from Doc_ProjectIssue + * @param as accounting schema + * @param AD_Org_ID org + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @param C_ProjectIssue_ID project issue line + * @param M_CostElement_ID optional cost element + * @param Amt amt total amount + * @param Qty qty + * @param Description optional description + * @param trxName transaction + * @return true if no error + */ + public static boolean createProjectIssue(MAcctSchema as, int AD_Org_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, + int C_ProjectIssue_ID, int M_CostElement_ID, + BigDecimal Amt, BigDecimal Qty, + String Description, String trxName) + { + MCostDetail cd = get (as.getCtx(), "C_ProjectIssue_ID=? AND Coalesce(M_CostElement_ID,0)="+M_CostElement_ID, + C_ProjectIssue_ID, M_AttributeSetInstance_ID, as.getC_AcctSchema_ID(), trxName); + // + if (cd == null) // createNew + { + cd = new MCostDetail (as, AD_Org_ID, + M_Product_ID, M_AttributeSetInstance_ID, + M_CostElement_ID, + Amt, Qty, Description, trxName); + cd.setC_ProjectIssue_ID(C_ProjectIssue_ID); + } + else + { + if (cd.isProcessed()) + { + cd.setDeltaAmt(Amt.subtract(cd.getAmt())); + cd.setDeltaQty(Qty.subtract(cd.getQty())); + } + else + { + cd.setDeltaAmt(BigDecimal.ZERO); + cd.setDeltaQty(BigDecimal.ZERO); + cd.setAmt(Amt); + cd.setQty(Qty); + } + if (cd.isDelta()) + { + cd.setProcessed(false); + cd.setAmt(Amt); + cd.setQty(Qty); + } + else if (cd.isProcessed()) + return true; // nothing to do + } + boolean ok = cd.save(); + if (ok && !cd.isProcessed()) + { + ok = cd.process(); + } + if (s_log.isLoggable(Level.CONFIG)) s_log.config("(" + ok + ") " + cd); + return ok; + } // createProjectIssue + /************************************************************************** * Get Cost Detail * @param ctx context From d4765a29e41d6594abeeb6c6ded42a74c07188a9 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Wed, 13 Sep 2017 15:01:34 +0800 Subject: [PATCH 05/21] IDEMPIERE-3361 Cost details inherit amt/qty from wrong history cost element --- .../src/org/compiere/model/MCostDetail.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/MCostDetail.java b/org.adempiere.base/src/org/compiere/model/MCostDetail.java index 6333bec9ec..5c0f126e4d 100644 --- a/org.adempiere.base/src/org/compiere/model/MCostDetail.java +++ b/org.adempiere.base/src/org/compiere/model/MCostDetail.java @@ -1383,10 +1383,14 @@ public class MCostDetail extends X_M_CostDetail log.warning("Unknown Type: " + toString()); return false; } - setCurrentCostPrice(cost.getCurrentCostPrice()); - setCurrentQty(cost.getCurrentQty()); - setCumulatedAmt(cost.getCumulatedAmt()); - setCumulatedQty(cost.getCumulatedQty()); + + //Should only update cost detail with value from as costing method + if(as.getCostingMethod().equals(ce.getCostingMethod())) { + setCurrentCostPrice(cost.getCurrentCostPrice()); + setCurrentQty(cost.getCurrentQty()); + setCumulatedAmt(cost.getCumulatedAmt()); + setCumulatedQty(cost.getCumulatedQty()); + } //update history history.setNewQty(cost.getCurrentQty()); From 731ae053a882ae45838cdf364eec6c301c341b2d Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Wed, 13 Sep 2017 16:35:03 +0800 Subject: [PATCH 06/21] IDEMPIERE-3485 Report/Process: Unique constraint exception after save parameter --- .../src/org/compiere/model/MPInstancePara.java | 15 +++++++++++++++ .../webui/apps/ProcessParameterPanel.java | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/org.adempiere.base/src/org/compiere/model/MPInstancePara.java b/org.adempiere.base/src/org/compiere/model/MPInstancePara.java index 2510e73c8a..08f3b1199b 100644 --- a/org.adempiere.base/src/org/compiere/model/MPInstancePara.java +++ b/org.adempiere.base/src/org/compiere/model/MPInstancePara.java @@ -232,4 +232,19 @@ public class MPInstancePara extends X_AD_PInstance_Para } return -1; } + + /** + * Get existing AD_PInstance_Para record or create a new one if not found + * @param ctx + * @param AD_PInstance_ID + * @param SeqNo + */ + public static MPInstancePara getOrCreate(Properties ctx, int AD_PInstance_ID, int SeqNo) + { + Query query = new Query(ctx, Table_Name, "AD_PInstance_ID=? AND SeqNo=?", null); + MPInstancePara para = query.setParameters(AD_PInstance_ID, SeqNo).first(); + if (para == null) + para = new MPInstancePara(ctx, AD_PInstance_ID, SeqNo); + return para; + } } // MPInstance_Para diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessParameterPanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessParameterPanel.java index a12dfaa695..f5da8c2809 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessParameterPanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessParameterPanel.java @@ -520,7 +520,7 @@ public class ProcessParameterPanel extends Panel implements result2 = editor2.getValue(); // Create Parameter - MPInstancePara para = new MPInstancePara(Env.getCtx(), + MPInstancePara para = MPInstancePara.getOrCreate(Env.getCtx(), m_processInfo.getAD_PInstance_ID(), i); GridField mField = (GridField) m_mFields.get(i); para.setParameterName(mField.getColumnName()); From 8f0c18514136b8f7004f6315a9fed87187d08066 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Wed, 13 Sep 2017 21:51:03 +0800 Subject: [PATCH 07/21] IDEMPIERE-3485 Report/Process: Unique constraint exception after save parameter --- .../src/org/adempiere/webui/apps/AbstractProcessDialog.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AbstractProcessDialog.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AbstractProcessDialog.java index 40b1e6db52..99dea34eb2 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AbstractProcessDialog.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AbstractProcessDialog.java @@ -644,6 +644,8 @@ public abstract class AbstractProcessDialog extends Window implements IProcessUI saveReportOptionToInstance(savedParams.get(i)); savedParams.get(i).saveEx(); + + getProcessInfo().setAD_PInstance_ID(0); } } } From e3961c7d613b02a148a537b18cf19d5cebd9bfcd Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Fri, 15 Sep 2017 11:15:10 +0800 Subject: [PATCH 08/21] IDEMPIERE-3488 Improvement to Error Message for Negative Inventory Disallow exception --- .../oracle/201709151000_IDEMPIERE-3488.sql | 11 + .../201709151000_IDEMPIERE-3488.sql | 8 + .../NegativeInventoryDisallowedException.java | 88 +++ .../src/org/compiere/model/MInOut.java | 610 +++++++++--------- .../src/org/compiere/model/MInventory.java | 298 +++++---- .../src/org/compiere/model/MMovement.java | 276 ++++---- .../src/org/compiere/model/MProjectIssue.java | 37 +- .../org/compiere/model/MStorageOnHand.java | 12 +- .../src/org/eevolution/model/MDDOrder.java | 62 +- 9 files changed, 795 insertions(+), 607 deletions(-) create mode 100644 migration/i4.1/oracle/201709151000_IDEMPIERE-3488.sql create mode 100644 migration/i4.1/postgresql/201709151000_IDEMPIERE-3488.sql create mode 100644 org.adempiere.base/src/org/adempiere/exceptions/NegativeInventoryDisallowedException.java diff --git a/migration/i4.1/oracle/201709151000_IDEMPIERE-3488.sql b/migration/i4.1/oracle/201709151000_IDEMPIERE-3488.sql new file mode 100644 index 0000000000..06b9126c82 --- /dev/null +++ b/migration/i4.1/oracle/201709151000_IDEMPIERE-3488.sql @@ -0,0 +1,11 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-3488 Improvement to Error Message for Negative Inventory Disallow exception +-- Sep 14, 2017 6:28:55 PM GMT+08:00 +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','The {0} warehouse does not allow negative inventory for Product = {1}, ASI = {2}, Locator = {3} (Shortage of {4})',0,0,'Y',TO_DATE('2017-09-14 18:28:54','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2017-09-14 18:28:54','YYYY-MM-DD HH24:MI:SS'),100,200431,'NegativeInventoryDisallowedInfo','D','1a686715-09f5-4437-9885-882719423bd1') +; + +SELECT register_migration_script('201709151000_IDEMPIERE-3488.sql') FROM dual +; + diff --git a/migration/i4.1/postgresql/201709151000_IDEMPIERE-3488.sql b/migration/i4.1/postgresql/201709151000_IDEMPIERE-3488.sql new file mode 100644 index 0000000000..d585048143 --- /dev/null +++ b/migration/i4.1/postgresql/201709151000_IDEMPIERE-3488.sql @@ -0,0 +1,8 @@ +-- IDEMPIERE-3488 Improvement to Error Message for Negative Inventory Disallow exception +-- Sep 14, 2017 6:28:55 PM GMT+08:00 +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('E','The {0} warehouse does not allow negative inventory for Product = {1}, ASI = {2}, Locator = {3} (Shortage of {4})',0,0,'Y',TO_TIMESTAMP('2017-09-14 18:28:54','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2017-09-14 18:28:54','YYYY-MM-DD HH24:MI:SS'),100,200431,'NegativeInventoryDisallowedInfo','D','1a686715-09f5-4437-9885-882719423bd1') +; + +SELECT register_migration_script('201709151000_IDEMPIERE-3488.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/adempiere/exceptions/NegativeInventoryDisallowedException.java b/org.adempiere.base/src/org/adempiere/exceptions/NegativeInventoryDisallowedException.java new file mode 100644 index 0000000000..da49059d85 --- /dev/null +++ b/org.adempiere.base/src/org/adempiere/exceptions/NegativeInventoryDisallowedException.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (C) 2017 Trek Global Inc. * + * Copyright (C) 2017 Low Heng Sin * + * 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.adempiere.exceptions; + +import java.math.BigDecimal; +import java.util.Properties; + +import org.compiere.model.MAttributeSetInstance; +import org.compiere.model.MLocator; +import org.compiere.model.MProduct; +import org.compiere.model.MSysConfig; +import org.compiere.model.MWarehouse; +import org.compiere.util.Env; +import org.compiere.util.Msg; + +/** + * + * @author hengsin + * + */ +public class NegativeInventoryDisallowedException extends AdempiereException +{ + /** + * + */ + private static final long serialVersionUID = 253224414462489886L; + + private int M_Warehouse_ID; + private int M_Product_ID; + private int M_AttributeSetInstance_ID; + private int M_Locator_ID; + private BigDecimal QtyOnHand; + private BigDecimal MovementQty; + + public NegativeInventoryDisallowedException(Properties ctx, int M_Warehouse_ID, int M_Product_ID, int M_AttributeSetInstance_ID, int M_Locator_ID, + BigDecimal QtyOnHand, BigDecimal MovementQty) + { + super(Msg.getMsg(ctx, "NegativeInventoryDisallowedInfo", new Object[] { + MWarehouse.get(ctx, M_Warehouse_ID).getName(), + MProduct.get(ctx, M_Product_ID).getValue() + MSysConfig.getValue(MSysConfig.IDENTIFIER_SEPARATOR, "_", Env.getAD_Client_ID(ctx)) + MProduct.get(ctx, M_Product_ID).getName(), + M_AttributeSetInstance_ID > 0 ? MAttributeSetInstance.get(ctx, M_AttributeSetInstance_ID, M_Product_ID).getDescription() : "0", + M_Locator_ID > 0 ? MLocator.get(ctx, M_Locator_ID).getValue() : "0", MovementQty.subtract(QtyOnHand) + + })); + + this.M_Warehouse_ID = M_Warehouse_ID; + this.M_Product_ID = M_Product_ID; + this.M_AttributeSetInstance_ID = M_AttributeSetInstance_ID; + this.M_Locator_ID = M_Locator_ID; + this.QtyOnHand = QtyOnHand; + this.MovementQty = MovementQty; + } + + public int getM_Warehouse_ID() { + return M_Warehouse_ID; + } + + public int getM_Product_ID() { + return M_Product_ID; + } + + public int getM_AttributeSetInstance_ID() { + return M_AttributeSetInstance_ID; + } + + public int getM_Locator_ID() { + return M_Locator_ID; + } + + public BigDecimal getQtyOnHand() { + return QtyOnHand; + } + + public BigDecimal getMovementQty() { + return MovementQty; + } +} diff --git a/org.adempiere.base/src/org/compiere/model/MInOut.java b/org.adempiere.base/src/org/compiere/model/MInOut.java index d8c0627596..35768187e1 100644 --- a/org.adempiere.base/src/org/compiere/model/MInOut.java +++ b/org.adempiere.base/src/org/compiere/model/MInOut.java @@ -26,6 +26,7 @@ import java.util.Properties; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.adempiere.exceptions.PeriodClosedException; import org.compiere.print.MPrintFormat; import org.compiere.print.ReportEngine; @@ -1286,6 +1287,7 @@ public class MInOut extends X_M_InOut implements DocAction if (log.isLoggable(Level.INFO)) log.info(toString()); StringBuilder info = new StringBuilder(); + StringBuilder errors = new StringBuilder(); // For all lines MInOutLine[] lines = getLines(false); for (int lineIndex = 0; lineIndex < lines.length; lineIndex++) @@ -1293,353 +1295,326 @@ public class MInOut extends X_M_InOut implements DocAction MInOutLine sLine = lines[lineIndex]; MProduct product = sLine.getProduct(); - // Qty & Type - String MovementType = getMovementType(); - BigDecimal Qty = sLine.getMovementQty(); - if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return - Qty = Qty.negate(); - - // Update Order Line - MOrderLine oLine = null; - if (sLine.getC_OrderLine_ID() != 0) + try { - oLine = new MOrderLine (getCtx(), sLine.getC_OrderLine_ID(), get_TrxName()); - if (log.isLoggable(Level.FINE)) log.fine("OrderLine - Reserved=" + oLine.getQtyReserved() - + ", Delivered=" + oLine.getQtyDelivered()); - } - - - // Load RMA Line - MRMALine rmaLine = null; - - if (sLine.getM_RMALine_ID() != 0) - { - rmaLine = new MRMALine(getCtx(), sLine.getM_RMALine_ID(), get_TrxName()); - } - - if (log.isLoggable(Level.INFO)) log.info("Line=" + sLine.getLine() + " - Qty=" + sLine.getMovementQty()); - - // Stock Movement - Counterpart MOrder.reserveStock - if (product != null - && product.isStocked() ) - { - //Ignore the Material Policy when is Reverse Correction - if(!isReversal()) + // Qty & Type + String MovementType = getMovementType(); + BigDecimal Qty = sLine.getMovementQty(); + if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return + Qty = Qty.negate(); + + // Update Order Line + MOrderLine oLine = null; + if (sLine.getC_OrderLine_ID() != 0) { - BigDecimal movementQty = sLine.getMovementQty(); - BigDecimal qtyOnLineMA = MInOutLineMA.getManualQty(sLine.getM_InOutLine_ID(), get_TrxName()); - - if ( (movementQty.signum() != 0 && qtyOnLineMA.signum() != 0 && movementQty.signum() != qtyOnLineMA.signum()) // must have same sign - || (qtyOnLineMA.abs().compareTo(movementQty.abs())>0)) { // compare absolute values - // More then line qty on attribute tab for line 10 - m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + sLine.getLine(); - return DOCSTATUS_Invalid; - } - - checkMaterialPolicy(sLine,movementQty.subtract(qtyOnLineMA)); + oLine = new MOrderLine (getCtx(), sLine.getC_OrderLine_ID(), get_TrxName()); + if (log.isLoggable(Level.FINE)) log.fine("OrderLine - Reserved=" + oLine.getQtyReserved() + + ", Delivered=" + oLine.getQtyDelivered()); } - - log.fine("Material Transaction"); - MTransaction mtrx = null; - - // - BigDecimal overReceipt = BigDecimal.ZERO; - if (!isReversal()) + + + // Load RMA Line + MRMALine rmaLine = null; + + if (sLine.getM_RMALine_ID() != 0) + { + rmaLine = new MRMALine(getCtx(), sLine.getM_RMALine_ID(), get_TrxName()); + } + + if (log.isLoggable(Level.INFO)) log.info("Line=" + sLine.getLine() + " - Qty=" + sLine.getMovementQty()); + + // Stock Movement - Counterpart MOrder.reserveStock + if (product != null + && product.isStocked() ) { - if (oLine != null) + //Ignore the Material Policy when is Reverse Correction + if(!isReversal()) { - BigDecimal toDelivered = oLine.getQtyOrdered() - .subtract(oLine.getQtyDelivered()); - if (toDelivered.signum() < 0) // IDEMPIERE-2889 - toDelivered = Env.ZERO; - if (sLine.getMovementQty().compareTo(toDelivered) > 0) - overReceipt = sLine.getMovementQty().subtract( - toDelivered); - if (overReceipt.signum() != 0) - { - sLine.setQtyOverReceipt(overReceipt); - sLine.saveEx(); + BigDecimal movementQty = sLine.getMovementQty(); + BigDecimal qtyOnLineMA = MInOutLineMA.getManualQty(sLine.getM_InOutLine_ID(), get_TrxName()); + + if ( (movementQty.signum() != 0 && qtyOnLineMA.signum() != 0 && movementQty.signum() != qtyOnLineMA.signum()) // must have same sign + || (qtyOnLineMA.abs().compareTo(movementQty.abs())>0)) { // compare absolute values + // More then line qty on attribute tab for line 10 + m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + sLine.getLine(); + return DOCSTATUS_Invalid; } + + checkMaterialPolicy(sLine,movementQty.subtract(qtyOnLineMA)); } - } - else - { - overReceipt = sLine.getQtyOverReceipt(); - } - BigDecimal orderedQtyToUpdate = sLine.getMovementQty().subtract(overReceipt); - // - if (sLine.getM_AttributeSetInstance_ID() == 0) - { - MInOutLineMA mas[] = MInOutLineMA.get(getCtx(), - sLine.getM_InOutLine_ID(), get_TrxName()); - for (int j = 0; j < mas.length; j++) + + log.fine("Material Transaction"); + MTransaction mtrx = null; + + // + BigDecimal overReceipt = BigDecimal.ZERO; + if (!isReversal()) { - MInOutLineMA ma = mas[j]; - BigDecimal QtyMA = ma.getMovementQty(); - if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return - QtyMA = QtyMA.negate(); - - // Update Storage - see also VMatch.createMatchRecord + if (oLine != null) + { + BigDecimal toDelivered = oLine.getQtyOrdered() + .subtract(oLine.getQtyDelivered()); + if (toDelivered.signum() < 0) // IDEMPIERE-2889 + toDelivered = Env.ZERO; + if (sLine.getMovementQty().compareTo(toDelivered) > 0) + overReceipt = sLine.getMovementQty().subtract( + toDelivered); + if (overReceipt.signum() != 0) + { + sLine.setQtyOverReceipt(overReceipt); + sLine.saveEx(); + } + } + } + else + { + overReceipt = sLine.getQtyOverReceipt(); + } + BigDecimal orderedQtyToUpdate = sLine.getMovementQty().subtract(overReceipt); + // + if (sLine.getM_AttributeSetInstance_ID() == 0) + { + MInOutLineMA mas[] = MInOutLineMA.get(getCtx(), + sLine.getM_InOutLine_ID(), get_TrxName()); + for (int j = 0; j < mas.length; j++) + { + MInOutLineMA ma = mas[j]; + BigDecimal QtyMA = ma.getMovementQty(); + if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return + QtyMA = QtyMA.negate(); + + // Update Storage - see also VMatch.createMatchRecord + if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), + sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), + ma.getM_AttributeSetInstance_ID(), + QtyMA,ma.getDateMaterialPolicy(), + get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory OnHand (MA) [" + product.getValue() + "] - " + lastError; + return DocAction.STATUS_Invalid; + } + + // Create Transaction + mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), + MovementType, sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + QtyMA, getMovementDate(), get_TrxName()); + mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); + if (!mtrx.save()) + { + m_processMsg = "Could not create Material Transaction (MA) [" + product.getValue() + "]"; + return DocAction.STATUS_Invalid; + } + } + + if (oLine!=null && mtrx!=null && oLine.getQtyOrdered().signum() > 0) + { + if (sLine.getC_OrderLine_ID() != 0) + { + if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(), + sLine.getM_Product_ID(), + oLine.getM_AttributeSetInstance_ID(), + orderedQtyToUpdate.negate(), + isSOTrx(), + get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory " + (isSOTrx()? "Reserved" : "Ordered") + " (MA) - [" + product.getValue() + "] - " + lastError; + return DocAction.STATUS_Invalid; + } + } + } + + } + // sLine.getM_AttributeSetInstance_ID() != 0 + if (mtrx == null) + { + Timestamp dateMPolicy= null; + MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(), 0, + sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), null, + MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, + sLine.getM_Locator_ID(), get_TrxName()); + for (MStorageOnHand storage : storages) { + if (storage.getQtyOnHand().compareTo(sLine.getMovementQty()) >= 0) { + dateMPolicy = storage.getDateMaterialPolicy(); + break; + } + } + + if (dateMPolicy == null && storages.length > 0) + dateMPolicy = storages[0].getDateMaterialPolicy(); + + if(dateMPolicy==null) + dateMPolicy = getMovementDate(); + + // Fallback: Update Storage - see also VMatch.createMatchRecord if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), sLine.getM_Locator_ID(), sLine.getM_Product_ID(), - ma.getM_AttributeSetInstance_ID(), - QtyMA,ma.getDateMaterialPolicy(), - get_TrxName())) + sLine.getM_AttributeSetInstance_ID(), + Qty,dateMPolicy,get_TrxName())) { String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand (MA) [" + product.getValue() + "] - " + lastError; - return DocAction.STATUS_Invalid; - } - - // Create Transaction - mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), - MovementType, sLine.getM_Locator_ID(), - sLine.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), - QtyMA, getMovementDate(), get_TrxName()); - mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); - if (!mtrx.save()) - { - m_processMsg = "Could not create Material Transaction (MA) [" + product.getValue() + "]"; + m_processMsg = "Cannot correct Inventory OnHand [" + product.getValue() + "] - " + lastError; return DocAction.STATUS_Invalid; } - } - - if (oLine!=null && mtrx!=null && oLine.getQtyOrdered().signum() > 0) - { - if (sLine.getC_OrderLine_ID() != 0) + if (oLine!=null && oLine.getQtyOrdered().signum() > 0) { if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(), sLine.getM_Product_ID(), oLine.getM_AttributeSetInstance_ID(), - orderedQtyToUpdate.negate(), - isSOTrx(), - get_TrxName())) + orderedQtyToUpdate.negate(), isSOTrx(), get_TrxName())) { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory " + (isSOTrx()? "Reserved" : "Ordered") + " (MA) - [" + product.getValue() + "] - " + lastError; + m_processMsg = "Cannot correct Inventory Reserved " + (isSOTrx()? "Reserved [" :"Ordered [") + product.getValue() + "]"; return DocAction.STATUS_Invalid; } } - } - - } - // sLine.getM_AttributeSetInstance_ID() != 0 - if (mtrx == null) - { - Timestamp dateMPolicy= null; - MStorageOnHand[] storages = MStorageOnHand.getWarehouse(getCtx(), 0, - sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), null, - MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, - sLine.getM_Locator_ID(), get_TrxName()); - for (MStorageOnHand storage : storages) { - if (storage.getQtyOnHand().compareTo(sLine.getMovementQty()) >= 0) { - dateMPolicy = storage.getDateMaterialPolicy(); - break; - } - } - - if (dateMPolicy == null && storages.length > 0) - dateMPolicy = storages[0].getDateMaterialPolicy(); - - if(dateMPolicy==null) - dateMPolicy = getMovementDate(); - - // Fallback: Update Storage - see also VMatch.createMatchRecord - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), - sLine.getM_Locator_ID(), - sLine.getM_Product_ID(), - sLine.getM_AttributeSetInstance_ID(), - Qty,dateMPolicy,get_TrxName())) - { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand [" + product.getValue() + "] - " + lastError; - return DocAction.STATUS_Invalid; - } - if (oLine!=null && oLine.getQtyOrdered().signum() > 0) - { - if (!MStorageReservation.add(getCtx(), oLine.getM_Warehouse_ID(), - sLine.getM_Product_ID(), - oLine.getM_AttributeSetInstance_ID(), - orderedQtyToUpdate.negate(), isSOTrx(), get_TrxName())) + + // FallBack: Create Transaction + mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), + MovementType, sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), + Qty, getMovementDate(), get_TrxName()); + mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); + if (!mtrx.save()) { - m_processMsg = "Cannot correct Inventory Reserved " + (isSOTrx()? "Reserved [" :"Ordered [") + product.getValue() + "]"; + m_processMsg = CLogger.retrieveErrorString("Could not create Material Transaction [" + product.getValue() + "]"); return DocAction.STATUS_Invalid; } } - - // FallBack: Create Transaction - mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), - MovementType, sLine.getM_Locator_ID(), - sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), - Qty, getMovementDate(), get_TrxName()); - mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); - if (!mtrx.save()) + } // stock movement + + // Correct Order Line + if (product != null && oLine != null) // other in VMatch.createMatchRecord + { + oLine.setQtyReserved(oLine.getQtyReserved().subtract(sLine.getMovementQty().subtract(sLine.getQtyOverReceipt()))); + } + + // Update Sales Order Line + if (oLine != null) + { + if (isSOTrx() // PO is done by Matching + || sLine.getM_Product_ID() == 0) // PO Charges, empty lines { - m_processMsg = CLogger.retrieveErrorString("Could not create Material Transaction [" + product.getValue() + "]"); + if (isSOTrx()) + oLine.setQtyDelivered(oLine.getQtyDelivered().subtract(Qty)); + else + oLine.setQtyDelivered(oLine.getQtyDelivered().add(Qty)); + oLine.setDateDelivered(getMovementDate()); // overwrite=last + } + if (!oLine.save()) + { + m_processMsg = "Could not update Order Line"; return DocAction.STATUS_Invalid; } - } - } // stock movement - - // Correct Order Line - if (product != null && oLine != null) // other in VMatch.createMatchRecord - { - oLine.setQtyReserved(oLine.getQtyReserved().subtract(sLine.getMovementQty().subtract(sLine.getQtyOverReceipt()))); - } - - // Update Sales Order Line - if (oLine != null) - { - if (isSOTrx() // PO is done by Matching - || sLine.getM_Product_ID() == 0) // PO Charges, empty lines - { - if (isSOTrx()) - oLine.setQtyDelivered(oLine.getQtyDelivered().subtract(Qty)); else - oLine.setQtyDelivered(oLine.getQtyDelivered().add(Qty)); - oLine.setDateDelivered(getMovementDate()); // overwrite=last + if (log.isLoggable(Level.FINE)) log.fine("OrderLine -> Reserved=" + oLine.getQtyReserved() + + ", Delivered=" + oLine.getQtyReserved()); } - if (!oLine.save()) + // Update RMA Line Qty Delivered + else if (rmaLine != null) + { + if (isSOTrx()) + { + rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().add(Qty)); + } + else + { + rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().subtract(Qty)); + } + if (!rmaLine.save()) + { + m_processMsg = "Could not update RMA Line"; + return DocAction.STATUS_Invalid; + } + } + + // Create Asset for SO + if (product != null + && isSOTrx() + && product.isCreateAsset() + && !product.getM_Product_Category().getA_Asset_Group().isFixedAsset() + && sLine.getMovementQty().signum() > 0 + && !isReversal()) { - m_processMsg = "Could not update Order Line"; - return DocAction.STATUS_Invalid; - } - else - if (log.isLoggable(Level.FINE)) log.fine("OrderLine -> Reserved=" + oLine.getQtyReserved() - + ", Delivered=" + oLine.getQtyReserved()); - } - // Update RMA Line Qty Delivered - else if (rmaLine != null) - { - if (isSOTrx()) - { - rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().add(Qty)); - } - else - { - rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().subtract(Qty)); - } - if (!rmaLine.save()) - { - m_processMsg = "Could not update RMA Line"; - return DocAction.STATUS_Invalid; - } - } - - // Create Asset for SO - if (product != null - && isSOTrx() - && product.isCreateAsset() - && !product.getM_Product_Category().getA_Asset_Group().isFixedAsset() - && sLine.getMovementQty().signum() > 0 - && !isReversal()) - { - log.fine("Asset"); - info.append("@A_Asset_ID@: "); - int noAssets = sLine.getMovementQty().intValue(); - if (!product.isOneAssetPerUOM()) - noAssets = 1; - for (int i = 0; i < noAssets; i++) - { - if (i > 0) - info.append(" - "); - int deliveryCount = i+1; + log.fine("Asset"); + info.append("@A_Asset_ID@: "); + int noAssets = sLine.getMovementQty().intValue(); if (!product.isOneAssetPerUOM()) - deliveryCount = 0; - MAsset asset = new MAsset (this, sLine, deliveryCount); - if (!asset.save(get_TrxName())) + noAssets = 1; + for (int i = 0; i < noAssets; i++) { - m_processMsg = "Could not create Asset"; - return DocAction.STATUS_Invalid; - } - info.append(asset.getValue()); - } - } // Asset - - - // Matching - if (!isSOTrx() - && sLine.getM_Product_ID() != 0 - && !isReversal()) - { - BigDecimal matchQty = sLine.getMovementQty(); - // Invoice - Receipt Match (requires Product) - MInvoiceLine iLine = MInvoiceLine.getOfInOutLine (sLine); - if (iLine != null && iLine.getM_Product_ID() != 0) - { - if (matchQty.compareTo(iLine.getQtyInvoiced())>0) - matchQty = iLine.getQtyInvoiced(); - - MMatchInv[] matches = MMatchInv.get(getCtx(), - sLine.getM_InOutLine_ID(), iLine.getC_InvoiceLine_ID(), get_TrxName()); - if (matches == null || matches.length == 0) - { - MMatchInv inv = new MMatchInv (iLine, getMovementDate(), matchQty); - if (sLine.getM_AttributeSetInstance_ID() != iLine.getM_AttributeSetInstance_ID()) + if (i > 0) + info.append(" - "); + int deliveryCount = i+1; + if (!product.isOneAssetPerUOM()) + deliveryCount = 0; + MAsset asset = new MAsset (this, sLine, deliveryCount); + if (!asset.save(get_TrxName())) { - iLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); - iLine.saveEx(); // update matched invoice with ASI - inv.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); - } - if (!inv.save(get_TrxName())) - { - m_processMsg = CLogger.retrieveErrorString("Could not create Inv Matching"); + m_processMsg = "Could not create Asset"; return DocAction.STATUS_Invalid; } - addDocsPostProcess(inv); + info.append(asset.getValue()); } - } - - // Link to Order - if (sLine.getC_OrderLine_ID() != 0) + } // Asset + + + // Matching + if (!isSOTrx() + && sLine.getM_Product_ID() != 0 + && !isReversal()) { - log.fine("PO Matching"); - // Ship - PO - MMatchPO po = MMatchPO.create (null, sLine, getMovementDate(), matchQty); - if (po != null) { - if (!po.save(get_TrxName())) + BigDecimal matchQty = sLine.getMovementQty(); + // Invoice - Receipt Match (requires Product) + MInvoiceLine iLine = MInvoiceLine.getOfInOutLine (sLine); + if (iLine != null && iLine.getM_Product_ID() != 0) + { + if (matchQty.compareTo(iLine.getQtyInvoiced())>0) + matchQty = iLine.getQtyInvoiced(); + + MMatchInv[] matches = MMatchInv.get(getCtx(), + sLine.getM_InOutLine_ID(), iLine.getC_InvoiceLine_ID(), get_TrxName()); + if (matches == null || matches.length == 0) { - m_processMsg = "Could not create PO Matching"; - return DocAction.STATUS_Invalid; - } - if (!po.isPosted()) - addDocsPostProcess(po); - MMatchInv matchInvCreated = po.getMatchInvCreated(); - if (matchInvCreated != null) { - addDocsPostProcess(matchInvCreated); + MMatchInv inv = new MMatchInv (iLine, getMovementDate(), matchQty); + if (sLine.getM_AttributeSetInstance_ID() != iLine.getM_AttributeSetInstance_ID()) + { + iLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + iLine.saveEx(); // update matched invoice with ASI + inv.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + } + if (!inv.save(get_TrxName())) + { + m_processMsg = CLogger.retrieveErrorString("Could not create Inv Matching"); + return DocAction.STATUS_Invalid; + } + addDocsPostProcess(inv); } } - // Update PO with ASI - if ( oLine != null && oLine.getM_AttributeSetInstance_ID() == 0 - && sLine.getMovementQty().compareTo(oLine.getQtyOrdered()) == 0) // just if full match [ 1876965 ] + + // Link to Order + if (sLine.getC_OrderLine_ID() != 0) { - oLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); - oLine.saveEx(get_TrxName()); - } - } - else // No Order - Try finding links via Invoice - { - // Invoice has an Order Link - if (iLine != null && iLine.getC_OrderLine_ID() != 0) - { - // Invoice is created before Shipment - log.fine("PO(Inv) Matching"); - // Ship - Invoice - MMatchPO po = MMatchPO.create (iLine, sLine, - getMovementDate(), matchQty); + log.fine("PO Matching"); + // Ship - PO + MMatchPO po = MMatchPO.create (null, sLine, getMovementDate(), matchQty); if (po != null) { if (!po.save(get_TrxName())) { - m_processMsg = "Could not create PO(Inv) Matching"; + m_processMsg = "Could not create PO Matching"; return DocAction.STATUS_Invalid; } if (!po.isPosted()) addDocsPostProcess(po); + MMatchInv matchInvCreated = po.getMatchInvCreated(); + if (matchInvCreated != null) { + addDocsPostProcess(matchInvCreated); + } } - // Update PO with ASI - oLine = new MOrderLine (getCtx(), iLine.getC_OrderLine_ID(), get_TrxName()); if ( oLine != null && oLine.getM_AttributeSetInstance_ID() == 0 && sLine.getMovementQty().compareTo(oLine.getQtyOrdered()) == 0) // just if full match [ 1876965 ] { @@ -1647,11 +1622,52 @@ public class MInOut extends X_M_InOut implements DocAction oLine.saveEx(get_TrxName()); } } - } // No Order - } // PO Matching - + else // No Order - Try finding links via Invoice + { + // Invoice has an Order Link + if (iLine != null && iLine.getC_OrderLine_ID() != 0) + { + // Invoice is created before Shipment + log.fine("PO(Inv) Matching"); + // Ship - Invoice + MMatchPO po = MMatchPO.create (iLine, sLine, + getMovementDate(), matchQty); + if (po != null) { + if (!po.save(get_TrxName())) + { + m_processMsg = "Could not create PO(Inv) Matching"; + return DocAction.STATUS_Invalid; + } + if (!po.isPosted()) + addDocsPostProcess(po); + } + + // Update PO with ASI + oLine = new MOrderLine (getCtx(), iLine.getC_OrderLine_ID(), get_TrxName()); + if ( oLine != null && oLine.getM_AttributeSetInstance_ID() == 0 + && sLine.getMovementQty().compareTo(oLine.getQtyOrdered()) == 0) // just if full match [ 1876965 ] + { + oLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + oLine.saveEx(get_TrxName()); + } + } + } // No Order + } // PO Matching + } + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + errors.append(Msg.getElement(getCtx(), "Line")).append(" ").append(sLine.getLine()).append(": "); + errors.append(e.getMessage()).append("\n"); + } } // for all lines + if (errors.toString().length() > 0) + { + m_processMsg = errors.toString(); + return DocAction.STATUS_Invalid; + } + // Counter Documents MInOut counter = createCounterDoc(); if (counter != null) diff --git a/org.adempiere.base/src/org/compiere/model/MInventory.java b/org.adempiere.base/src/org/compiere/model/MInventory.java index 2d360d8155..4cd75b68e0 100644 --- a/org.adempiere.base/src/org/compiere/model/MInventory.java +++ b/org.adempiere.base/src/org/compiere/model/MInventory.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.adempiere.exceptions.PeriodClosedException; import org.compiere.process.DocAction; import org.compiere.process.DocumentEngine; @@ -435,6 +436,7 @@ public class MInventory extends X_M_Inventory implements DocAction approveIt(); if (log.isLoggable(Level.INFO)) log.info(toString()); + StringBuilder errors = new StringBuilder(); MInventoryLine[] lines = getLines(false); for (MInventoryLine line : lines) { @@ -442,98 +444,159 @@ public class MInventory extends X_M_Inventory implements DocAction continue; MProduct product = line.getProduct(); - - BigDecimal qtyDiff = Env.ZERO; - if (MDocType.DOCSUBTYPEINV_InternalUseInventory.equals(docSubTypeInv)) - qtyDiff = line.getQtyInternalUse().negate(); - else if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) - qtyDiff = line.getQtyCount().subtract(line.getQtyBook()); - else if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(docSubTypeInv)) + try { - if (!isReversal()) + BigDecimal qtyDiff = Env.ZERO; + if (MDocType.DOCSUBTYPEINV_InternalUseInventory.equals(docSubTypeInv)) + qtyDiff = line.getQtyInternalUse().negate(); + else if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) + qtyDiff = line.getQtyCount().subtract(line.getQtyBook()); + else if (MDocType.DOCSUBTYPEINV_CostAdjustment.equals(docSubTypeInv)) { - BigDecimal currentCost = line.getCurrentCostPrice(); - MClient client = MClient.get(getCtx(), getAD_Client_ID()); - MAcctSchema as = client.getAcctSchema(); - MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(getCtx(), client.get_ID()); - - if (as.getC_Currency_ID() != getC_Currency_ID()) + if (!isReversal()) { - for (int i = 0; i < ass.length ; i ++) + BigDecimal currentCost = line.getCurrentCostPrice(); + MClient client = MClient.get(getCtx(), getAD_Client_ID()); + MAcctSchema as = client.getAcctSchema(); + MAcctSchema[] ass = MAcctSchema.getClientAcctSchema(getCtx(), client.get_ID()); + + if (as.getC_Currency_ID() != getC_Currency_ID()) { - MAcctSchema a = ass[i]; - if (a.getC_Currency_ID() == getC_Currency_ID()) - as = a ; + for (int i = 0; i < ass.length ; i ++) + { + MAcctSchema a = ass[i]; + if (a.getC_Currency_ID() == getC_Currency_ID()) + as = a ; + } + } + + MCost cost = product.getCostingRecord(as, getAD_Org_ID(), line.getM_AttributeSetInstance_ID(), getCostingMethod()); + if (cost != null && cost.getCurrentCostPrice().compareTo(currentCost) != 0) + { + m_processMsg = "Current Cost for Line " + line.getLine() + " have changed."; + return DocAction.STATUS_Invalid; } } - - MCost cost = product.getCostingRecord(as, getAD_Org_ID(), line.getM_AttributeSetInstance_ID(), getCostingMethod()); - if (cost != null && cost.getCurrentCostPrice().compareTo(currentCost) != 0) - { - m_processMsg = "Current Cost for Line " + line.getLine() + " have changed."; - return DocAction.STATUS_Invalid; - } } - } - - //If Quantity Count minus Quantity Book = Zero, then no change in Inventory - if (qtyDiff.signum() == 0) - continue; - - //Ignore the Material Policy when is Reverse Correction - if(!isReversal()){ - BigDecimal qtyOnLineMA = MInventoryLineMA.getManualQty(line.getM_InventoryLine_ID(), get_TrxName()); - - if(qtyDiff.signum()<0){ - if(qtyOnLineMA.compareTo(qtyDiff)<0){ - m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); - return DOCSTATUS_Invalid; - } - }else{ - if(qtyOnLineMA.compareTo(qtyDiff)>0){ - m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); - return DOCSTATUS_Invalid; + + //If Quantity Count minus Quantity Book = Zero, then no change in Inventory + if (qtyDiff.signum() == 0) + continue; + + //Ignore the Material Policy when is Reverse Correction + if(!isReversal()){ + BigDecimal qtyOnLineMA = MInventoryLineMA.getManualQty(line.getM_InventoryLine_ID(), get_TrxName()); + + if(qtyDiff.signum()<0){ + if(qtyOnLineMA.compareTo(qtyDiff)<0){ + m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); + return DOCSTATUS_Invalid; + } + }else{ + if(qtyOnLineMA.compareTo(qtyDiff)>0){ + m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); + return DOCSTATUS_Invalid; + } } + checkMaterialPolicy(line, qtyDiff.subtract(qtyOnLineMA)); } - checkMaterialPolicy(line, qtyDiff.subtract(qtyOnLineMA)); - } - // Stock Movement - Counterpart MOrder.reserveStock - if (product != null - && product.isStocked() ) - { - log.fine("Material Transaction"); - MTransaction mtrx = null; - - //If AttributeSetInstance = Zero then create new AttributeSetInstance use Inventory Line MA else use current AttributeSetInstance - if (line.getM_AttributeSetInstance_ID() == 0 || qtyDiff.compareTo(Env.ZERO) == 0) + // Stock Movement - Counterpart MOrder.reserveStock + if (product != null + && product.isStocked() ) { - MInventoryLineMA mas[] = MInventoryLineMA.get(getCtx(), - line.getM_InventoryLine_ID(), get_TrxName()); - - for (int j = 0; j < mas.length; j++) + log.fine("Material Transaction"); + MTransaction mtrx = null; + + //If AttributeSetInstance = Zero then create new AttributeSetInstance use Inventory Line MA else use current AttributeSetInstance + if (line.getM_AttributeSetInstance_ID() == 0 || qtyDiff.compareTo(Env.ZERO) == 0) { - MInventoryLineMA ma = mas[j]; - BigDecimal QtyMA = ma.getMovementQty(); - BigDecimal QtyNew = QtyMA.add(qtyDiff); - if (log.isLoggable(Level.FINE)) log.fine("Diff=" + qtyDiff - + " - Instance OnHand=" + QtyMA + "->" + QtyNew); - + MInventoryLineMA mas[] = MInventoryLineMA.get(getCtx(), + line.getM_InventoryLine_ID(), get_TrxName()); + + for (int j = 0; j < mas.length; j++) + { + MInventoryLineMA ma = mas[j]; + BigDecimal QtyMA = ma.getMovementQty(); + BigDecimal QtyNew = QtyMA.add(qtyDiff); + if (log.isLoggable(Level.FINE)) log.fine("Diff=" + qtyDiff + + " - Instance OnHand=" + QtyMA + "->" + QtyNew); + + if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), + line.getM_Locator_ID(), + line.getM_Product_ID(), + ma.getM_AttributeSetInstance_ID(), + QtyMA.negate(),ma.getDateMaterialPolicy(), get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory (MA) - " + lastError; + return DocAction.STATUS_Invalid; + } + + // Only Update Date Last Inventory if is a Physical Inventory + if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) + { + MStorageOnHand storage = MStorageOnHand.get(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),ma.getDateMaterialPolicy(),get_TrxName()); + storage.setDateLastInventory(getMovementDate()); + if (!storage.save(get_TrxName())) + { + m_processMsg = "Storage not updated(2)"; + return DocAction.STATUS_Invalid; + } + } + + String m_MovementType =null; + if(QtyMA.negate().compareTo(Env.ZERO) > 0 ) + m_MovementType = MTransaction.MOVEMENTTYPE_InventoryIn; + else + m_MovementType = MTransaction.MOVEMENTTYPE_InventoryOut; + // Transaction + mtrx = new MTransaction (getCtx(), line.getAD_Org_ID(), m_MovementType, + line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + QtyMA.negate(), getMovementDate(), get_TrxName()); + + mtrx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); + if (!mtrx.save()) + { + m_processMsg = "Transaction not inserted(2)"; + return DocAction.STATUS_Invalid; + } + + qtyDiff = QtyNew; + + } + } + + //sLine.getM_AttributeSetInstance_ID() != 0 + // Fallback + if (mtrx == null) + { + Timestamp dateMPolicy= qtyDiff.signum() > 0 ? getMovementDate() : null; + if (line.getM_AttributeSetInstance_ID() > 0) + { + Timestamp t = MStorageOnHand.getDateMaterialPolicy(line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), line.get_TrxName()); + if (t != null) + dateMPolicy = t; + } + + //Fallback: Update Storage - see also VMatch.createMatchRecord if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), line.getM_Locator_ID(), line.getM_Product_ID(), - ma.getM_AttributeSetInstance_ID(), - QtyMA.negate(),ma.getDateMaterialPolicy(), get_TrxName())) + line.getM_AttributeSetInstance_ID(), + qtyDiff,dateMPolicy,get_TrxName())) { String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory (MA) - " + lastError; + m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; return DocAction.STATUS_Invalid; } - + // Only Update Date Last Inventory if is a Physical Inventory if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) { MStorageOnHand storage = MStorageOnHand.get(getCtx(), line.getM_Locator_ID(), - line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(),ma.getDateMaterialPolicy(),get_TrxName()); + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),dateMPolicy, get_TrxName()); + storage.setDateLastInventory(getMovementDate()); if (!storage.save(get_TrxName())) { @@ -541,87 +604,40 @@ public class MInventory extends X_M_Inventory implements DocAction return DocAction.STATUS_Invalid; } } - - String m_MovementType =null; - if(QtyMA.negate().compareTo(Env.ZERO) > 0 ) + + String m_MovementType = null; + if(qtyDiff.compareTo(Env.ZERO) > 0 ) m_MovementType = MTransaction.MOVEMENTTYPE_InventoryIn; else m_MovementType = MTransaction.MOVEMENTTYPE_InventoryOut; // Transaction mtrx = new MTransaction (getCtx(), line.getAD_Org_ID(), m_MovementType, - line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), - QtyMA.negate(), getMovementDate(), get_TrxName()); - - mtrx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); - if (!mtrx.save()) - { - m_processMsg = "Transaction not inserted(2)"; - return DocAction.STATUS_Invalid; - } - - qtyDiff = QtyNew; - - } - } - - //sLine.getM_AttributeSetInstance_ID() != 0 - // Fallback - if (mtrx == null) - { - Timestamp dateMPolicy= qtyDiff.signum() > 0 ? getMovementDate() : null; - if (line.getM_AttributeSetInstance_ID() > 0) - { - Timestamp t = MStorageOnHand.getDateMaterialPolicy(line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), line.get_TrxName()); - if (t != null) - dateMPolicy = t; - } - - //Fallback: Update Storage - see also VMatch.createMatchRecord - if (!MStorageOnHand.add(getCtx(), getM_Warehouse_ID(), - line.getM_Locator_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstance_ID(), - qtyDiff,dateMPolicy,get_TrxName())) - { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; - return DocAction.STATUS_Invalid; - } - - // Only Update Date Last Inventory if is a Physical Inventory - if (MDocType.DOCSUBTYPEINV_PhysicalInventory.equals(docSubTypeInv)) - { - MStorageOnHand storage = MStorageOnHand.get(getCtx(), line.getM_Locator_ID(), - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(),dateMPolicy, get_TrxName()); - - storage.setDateLastInventory(getMovementDate()); - if (!storage.save(get_TrxName())) + line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + qtyDiff, getMovementDate(), get_TrxName()); + mtrx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); + if (!mtrx.save()) { - m_processMsg = "Storage not updated(2)"; + m_processMsg = "Transaction not inserted(2)"; return DocAction.STATUS_Invalid; - } - } - - String m_MovementType = null; - if(qtyDiff.compareTo(Env.ZERO) > 0 ) - m_MovementType = MTransaction.MOVEMENTTYPE_InventoryIn; - else - m_MovementType = MTransaction.MOVEMENTTYPE_InventoryOut; - // Transaction - mtrx = new MTransaction (getCtx(), line.getAD_Org_ID(), m_MovementType, - line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - qtyDiff, getMovementDate(), get_TrxName()); - mtrx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); - if (!mtrx.save()) - { - m_processMsg = "Transaction not inserted(2)"; - return DocAction.STATUS_Invalid; - } - } // Fallback - } // stock movement + } + } // Fallback + } // stock movement + } + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + errors.append(Msg.getElement(getCtx(), "Line")).append(" ").append(line.getLine()).append(": "); + errors.append(e.getMessage()).append("\n"); + } } // for all lines + if (errors.toString().length() > 0) + { + m_processMsg = errors.toString(); + return DocAction.STATUS_Invalid; + } + // User Validation String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); if (valid != null) diff --git a/org.adempiere.base/src/org/compiere/model/MMovement.java b/org.adempiere.base/src/org/compiere/model/MMovement.java index a56cfd7268..d84c0d717f 100644 --- a/org.adempiere.base/src/org/compiere/model/MMovement.java +++ b/org.adempiere.base/src/org/compiere/model/MMovement.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.adempiere.exceptions.PeriodClosedException; import org.compiere.process.DocAction; import org.compiere.process.DocumentEngine; @@ -416,6 +417,7 @@ public class MMovement extends X_M_Movement implements DocAction approveIt(); if (log.isLoggable(Level.INFO)) log.info(toString()); + StringBuilder errors = new StringBuilder(); // MMovementLine[] lines = getLines(false); for (int i = 0; i < lines.length; i++) @@ -425,174 +427,190 @@ public class MMovement extends X_M_Movement implements DocAction //Stock Movement - Counterpart MOrder.reserveStock MProduct product = line.getProduct(); - if (product != null - && product.isStocked() ) + try { - //Ignore the Material Policy when is Reverse Correction - if(!isReversal()){ - BigDecimal qtyOnLineMA = MMovementLineMA.getManualQty(line.getM_MovementLine_ID(), get_TrxName()); - BigDecimal movementQty = line.getMovementQty(); - - if(qtyOnLineMA.compareTo(movementQty)>0) - { - // More then line qty on attribute tab for line 10 - m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); - return DOCSTATUS_Invalid; - } - - checkMaterialPolicy(line,movementQty.subtract(qtyOnLineMA)); - } - - - if (line.getM_AttributeSetInstance_ID() == 0) + if (product != null + && product.isStocked() ) { - MMovementLineMA mas[] = MMovementLineMA.get(getCtx(), - line.getM_MovementLine_ID(), get_TrxName()); - for (int j = 0; j < mas.length; j++) + //Ignore the Material Policy when is Reverse Correction + if(!isReversal()){ + BigDecimal qtyOnLineMA = MMovementLineMA.getManualQty(line.getM_MovementLine_ID(), get_TrxName()); + BigDecimal movementQty = line.getMovementQty(); + + if(qtyOnLineMA.compareTo(movementQty)>0) + { + // More then line qty on attribute tab for line 10 + m_processMsg = "@Over_Qty_On_Attribute_Tab@ " + line.getLine(); + return DOCSTATUS_Invalid; + } + + checkMaterialPolicy(line,movementQty.subtract(qtyOnLineMA)); + } + + + if (line.getM_AttributeSetInstance_ID() == 0) { - MMovementLineMA ma = mas[j]; - // + MMovementLineMA mas[] = MMovementLineMA.get(getCtx(), + line.getM_MovementLine_ID(), get_TrxName()); + for (int j = 0; j < mas.length; j++) + { + MMovementLineMA ma = mas[j]; + // + MLocator locator = new MLocator (getCtx(), line.getM_Locator_ID(), get_TrxName()); + //Update Storage + if (!MStorageOnHand.add(getCtx(),locator.getM_Warehouse_ID(), + line.getM_Locator_ID(), + line.getM_Product_ID(), + ma.getM_AttributeSetInstance_ID(), + ma.getMovementQty().negate(),ma.getDateMaterialPolicy(), get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; + return DocAction.STATUS_Invalid; + } + + int M_AttributeSetInstanceTo_ID = line.getM_AttributeSetInstanceTo_ID(); + //only can be same asi if locator is different + if (M_AttributeSetInstanceTo_ID == 0 && line.getM_Locator_ID() != line.getM_LocatorTo_ID()) + { + M_AttributeSetInstanceTo_ID = ma.getM_AttributeSetInstance_ID(); + } + //Update Storage + MLocator locatorTo = new MLocator (getCtx(), line.getM_LocatorTo_ID(), get_TrxName()); + if (!MStorageOnHand.add(getCtx(),locatorTo.getM_Warehouse_ID(), + line.getM_LocatorTo_ID(), + line.getM_Product_ID(), + M_AttributeSetInstanceTo_ID, + ma.getMovementQty(),ma.getDateMaterialPolicy(), get_TrxName())) + { + String lastError = CLogger.retrieveErrorString(""); + m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; + return DocAction.STATUS_Invalid; + } + + // + trxFrom = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_MovementFrom, + line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + ma.getMovementQty().negate(), getMovementDate(), get_TrxName()); + trxFrom.setM_MovementLine_ID(line.getM_MovementLine_ID()); + if (!trxFrom.save()) + { + m_processMsg = "Transaction From not inserted (MA)"; + return DocAction.STATUS_Invalid; + } + // + MTransaction trxTo = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_MovementTo, + line.getM_LocatorTo_ID(), line.getM_Product_ID(), M_AttributeSetInstanceTo_ID, + ma.getMovementQty(), getMovementDate(), get_TrxName()); + trxTo.setM_MovementLine_ID(line.getM_MovementLine_ID()); + if (!trxTo.save()) + { + m_processMsg = "Transaction To not inserted (MA)"; + return DocAction.STATUS_Invalid; + } + } + } + // Fallback - We have ASI + if (trxFrom == null) + { + Timestamp dateMPolicy= null; + MStorageOnHand[] storages = null; + if (line.getMovementQty().compareTo(Env.ZERO) > 0) { + // Find Date Material Policy bases on ASI + storages = MStorageOnHand.getWarehouse(getCtx(), 0, + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), null, + MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, + line.getM_Locator_ID(), get_TrxName()); + } else { + //Case of reversal + storages = MStorageOnHand.getWarehouse(getCtx(), 0, + line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), null, + MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, + line.getM_LocatorTo_ID(), get_TrxName()); + } + for (MStorageOnHand storage : storages) { + if (storage.getQtyOnHand().compareTo(line.getMovementQty()) >= 0) { + dateMPolicy = storage.getDateMaterialPolicy(); + break; + } + } + + if (dateMPolicy == null && storages.length > 0) + dateMPolicy = storages[0].getDateMaterialPolicy(); + MLocator locator = new MLocator (getCtx(), line.getM_Locator_ID(), get_TrxName()); //Update Storage + Timestamp effDateMPolicy = dateMPolicy; + if (dateMPolicy == null && line.getMovementQty().negate().signum() > 0) + effDateMPolicy = getMovementDate(); if (!MStorageOnHand.add(getCtx(),locator.getM_Warehouse_ID(), line.getM_Locator_ID(), line.getM_Product_ID(), - ma.getM_AttributeSetInstance_ID(), - ma.getMovementQty().negate(),ma.getDateMaterialPolicy(), get_TrxName())) + line.getM_AttributeSetInstance_ID(), + line.getMovementQty().negate(),effDateMPolicy, get_TrxName())) { String lastError = CLogger.retrieveErrorString(""); m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; return DocAction.STATUS_Invalid; } - - int M_AttributeSetInstanceTo_ID = line.getM_AttributeSetInstanceTo_ID(); - //only can be same asi if locator is different - if (M_AttributeSetInstanceTo_ID == 0 && line.getM_Locator_ID() != line.getM_LocatorTo_ID()) - { - M_AttributeSetInstanceTo_ID = ma.getM_AttributeSetInstance_ID(); - } - //Update Storage + + //Update Storage + effDateMPolicy = dateMPolicy; + if (dateMPolicy == null && line.getMovementQty().signum() > 0) + effDateMPolicy = getMovementDate(); MLocator locatorTo = new MLocator (getCtx(), line.getM_LocatorTo_ID(), get_TrxName()); if (!MStorageOnHand.add(getCtx(),locatorTo.getM_Warehouse_ID(), line.getM_LocatorTo_ID(), line.getM_Product_ID(), - M_AttributeSetInstanceTo_ID, - ma.getMovementQty(),ma.getDateMaterialPolicy(), get_TrxName())) + line.getM_AttributeSetInstanceTo_ID(), + line.getMovementQty(),effDateMPolicy, get_TrxName())) { String lastError = CLogger.retrieveErrorString(""); m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; return DocAction.STATUS_Invalid; } - + // trxFrom = new MTransaction (getCtx(), line.getAD_Org_ID(), MTransaction.MOVEMENTTYPE_MovementFrom, - line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), - ma.getMovementQty().negate(), getMovementDate(), get_TrxName()); + line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.getMovementQty().negate(), getMovementDate(), get_TrxName()); trxFrom.setM_MovementLine_ID(line.getM_MovementLine_ID()); if (!trxFrom.save()) { - m_processMsg = "Transaction From not inserted (MA)"; + m_processMsg = "Transaction From not inserted"; return DocAction.STATUS_Invalid; } // MTransaction trxTo = new MTransaction (getCtx(), line.getAD_Org_ID(), MTransaction.MOVEMENTTYPE_MovementTo, - line.getM_LocatorTo_ID(), line.getM_Product_ID(), M_AttributeSetInstanceTo_ID, - ma.getMovementQty(), getMovementDate(), get_TrxName()); + line.getM_LocatorTo_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), + line.getMovementQty(), getMovementDate(), get_TrxName()); trxTo.setM_MovementLine_ID(line.getM_MovementLine_ID()); if (!trxTo.save()) { - m_processMsg = "Transaction To not inserted (MA)"; + m_processMsg = "Transaction To not inserted"; return DocAction.STATUS_Invalid; } - } - } - // Fallback - We have ASI - if (trxFrom == null) - { - Timestamp dateMPolicy= null; - MStorageOnHand[] storages = null; - if (line.getMovementQty().compareTo(Env.ZERO) > 0) { - // Find Date Material Policy bases on ASI - storages = MStorageOnHand.getWarehouse(getCtx(), 0, - line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), null, - MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, - line.getM_Locator_ID(), get_TrxName()); - } else { - //Case of reversal - storages = MStorageOnHand.getWarehouse(getCtx(), 0, - line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), null, - MClient.MMPOLICY_FiFo.equals(product.getMMPolicy()), false, - line.getM_LocatorTo_ID(), get_TrxName()); - } - for (MStorageOnHand storage : storages) { - if (storage.getQtyOnHand().compareTo(line.getMovementQty()) >= 0) { - dateMPolicy = storage.getDateMaterialPolicy(); - break; - } - } - - if (dateMPolicy == null && storages.length > 0) - dateMPolicy = storages[0].getDateMaterialPolicy(); - - MLocator locator = new MLocator (getCtx(), line.getM_Locator_ID(), get_TrxName()); - //Update Storage - Timestamp effDateMPolicy = dateMPolicy; - if (dateMPolicy == null && line.getMovementQty().negate().signum() > 0) - effDateMPolicy = getMovementDate(); - if (!MStorageOnHand.add(getCtx(),locator.getM_Warehouse_ID(), - line.getM_Locator_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstance_ID(), - line.getMovementQty().negate(),effDateMPolicy, get_TrxName())) - { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; - return DocAction.STATUS_Invalid; - } - - //Update Storage - effDateMPolicy = dateMPolicy; - if (dateMPolicy == null && line.getMovementQty().signum() > 0) - effDateMPolicy = getMovementDate(); - MLocator locatorTo = new MLocator (getCtx(), line.getM_LocatorTo_ID(), get_TrxName()); - if (!MStorageOnHand.add(getCtx(),locatorTo.getM_Warehouse_ID(), - line.getM_LocatorTo_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstanceTo_ID(), - line.getMovementQty(),effDateMPolicy, get_TrxName())) - { - String lastError = CLogger.retrieveErrorString(""); - m_processMsg = "Cannot correct Inventory OnHand (MA) - " + lastError; - return DocAction.STATUS_Invalid; - } - - // - trxFrom = new MTransaction (getCtx(), line.getAD_Org_ID(), - MTransaction.MOVEMENTTYPE_MovementFrom, - line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), - line.getMovementQty().negate(), getMovementDate(), get_TrxName()); - trxFrom.setM_MovementLine_ID(line.getM_MovementLine_ID()); - if (!trxFrom.save()) - { - m_processMsg = "Transaction From not inserted"; - return DocAction.STATUS_Invalid; - } - // - MTransaction trxTo = new MTransaction (getCtx(), line.getAD_Org_ID(), - MTransaction.MOVEMENTTYPE_MovementTo, - line.getM_LocatorTo_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), - line.getMovementQty(), getMovementDate(), get_TrxName()); - trxTo.setM_MovementLine_ID(line.getM_MovementLine_ID()); - if (!trxTo.save()) - { - m_processMsg = "Transaction To not inserted"; - return DocAction.STATUS_Invalid; - } - } // Fallback - } // product stock + } // Fallback + } // product stock + } + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + errors.append(Msg.getElement(getCtx(), "Line")).append(" ").append(line.getLine()).append(": "); + errors.append(e.getMessage()).append("\n"); + } } // for all lines + + if (errors.toString().length() > 0) + { + m_processMsg = errors.toString(); + return DocAction.STATUS_Invalid; + } + // User Validation String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); if (valid != null) diff --git a/org.adempiere.base/src/org/compiere/model/MProjectIssue.java b/org.adempiere.base/src/org/compiere/model/MProjectIssue.java index 729a8c122d..0d84a4ee9e 100644 --- a/org.adempiere.base/src/org/compiere/model/MProjectIssue.java +++ b/org.adempiere.base/src/org/compiere/model/MProjectIssue.java @@ -22,8 +22,11 @@ import java.sql.Timestamp; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.compiere.util.DB; import org.compiere.util.Env; +import org.compiere.util.Msg; /** * Project Issue Model @@ -172,23 +175,35 @@ public class MProjectIssue extends X_C_ProjectIssue dateMPolicy = t; } - if (MStorageOnHand.add(getCtx(), loc.getM_Warehouse_ID(), getM_Locator_ID(), - getM_Product_ID(), getM_AttributeSetInstance_ID(), - getMovementQty().negate(),dateMPolicy, get_TrxName())) + try { - if (mTrx.save(get_TrxName())) + if (MStorageOnHand.add(getCtx(), loc.getM_Warehouse_ID(), getM_Locator_ID(), + getM_Product_ID(), getM_AttributeSetInstance_ID(), + getMovementQty().negate(),dateMPolicy, get_TrxName())) { - setProcessed (true); - if (save()) - return true; + if (mTrx.save(get_TrxName())) + { + setProcessed (true); + if (save()) + return true; + else + log.log(Level.SEVERE, "Issue not saved"); // requires trx !! + } else - log.log(Level.SEVERE, "Issue not saved"); // requires trx !! + log.log(Level.SEVERE, "Transaction not saved"); // requires trx !! } else - log.log(Level.SEVERE, "Transaction not saved"); // requires trx !! + log.log(Level.SEVERE, "Storage not updated"); // OK } - else - log.log(Level.SEVERE, "Storage not updated"); // OK + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + StringBuilder error = new StringBuilder(); + error.append(Msg.getElement(getCtx(), "Line")).append(" ").append(getLine()).append(": "); + error.append(e.getMessage()).append("\n"); + throw new AdempiereException(error.toString()); + } + // return false; } // process diff --git a/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java b/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java index c20f5284d2..677522cfe4 100644 --- a/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java +++ b/org.adempiere.base/src/org/compiere/model/MStorageOnHand.java @@ -27,11 +27,10 @@ import java.util.List; import java.util.Properties; import java.util.logging.Level; -import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; 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; /** @@ -735,7 +734,8 @@ public class MStorageOnHand extends X_M_StorageOnHand if (getQtyOnHand().signum() == -1) { MWarehouse wh = MWarehouse.get(Env.getCtx(), getM_Warehouse_ID()); if (wh.isDisallowNegativeInv()) { - throw new AdempiereException(Msg.getMsg(Env.getCtx(), "NegativeInventoryDisallowed")); + throw new NegativeInventoryDisallowedException(getCtx(), getM_Warehouse_ID(), getM_Product_ID(), getM_AttributeSetInstance_ID(), getM_Locator_ID(), + getQtyOnHand().subtract(addition), addition.negate()); } } } @@ -899,13 +899,15 @@ public class MStorageOnHand extends X_M_StorageOnHand if (getQtyOnHand().compareTo(BigDecimal.ZERO) < 0 || QtyOnHand.compareTo(Env.ZERO) < 0) { - log.saveError("Error", Msg.getMsg(getCtx(), "NegativeInventoryDisallowed")); + log.saveError("Error", new NegativeInventoryDisallowedException(getCtx(), getM_Warehouse_ID(), getM_Product_ID(), + getM_AttributeSetInstance_ID(), getM_Locator_ID(), QtyOnHand.subtract(getQtyOnHand()), getQtyOnHand().negate())); return false; } if (getM_AttributeSetInstance_ID() > 0 && getQtyOnHand().signum() < 0) { - log.saveError("Error", Msg.getMsg(getCtx(), "NegativeInventoryDisallowed")); + log.saveError("Error", new NegativeInventoryDisallowedException(getCtx(), getM_Warehouse_ID(), getM_Product_ID(), + getM_AttributeSetInstance_ID(), getM_Locator_ID(), QtyOnHand.subtract(getQtyOnHand()), getQtyOnHand().negate())); return false; } } diff --git a/org.adempiere.base/src/org/eevolution/model/MDDOrder.java b/org.adempiere.base/src/org/eevolution/model/MDDOrder.java index abc0e43dd1..ef01e324ba 100644 --- a/org.adempiere.base/src/org/eevolution/model/MDDOrder.java +++ b/org.adempiere.base/src/org/eevolution/model/MDDOrder.java @@ -27,6 +27,7 @@ import java.util.Properties; import java.util.logging.Level; import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.NegativeInventoryDisallowedException; import org.compiere.model.MBPartner; import org.compiere.model.MBPartnerLocation; import org.compiere.model.MDocType; @@ -847,6 +848,7 @@ public class MDDOrder extends X_DD_Order implements DocAction BigDecimal Volume = Env.ZERO; BigDecimal Weight = Env.ZERO; + StringBuilder errors = new StringBuilder(); // Always check and (un) Reserve Inventory for (MDDOrderLine line : lines) { @@ -874,35 +876,47 @@ public class MDDOrder extends X_DD_Order implements DocAction MProduct product = line.getProduct(); if (product != null) { - if (product.isStocked()) + try { - // Update Storage - if (!MStorageOnHand.add(getCtx(), locator_to.getM_Warehouse_ID(), locator_to.getM_Locator_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstance_ID(), - Env.ZERO,null, get_TrxName())) + if (product.isStocked()) { - throw new AdempiereException(); - } - - if (!MStorageOnHand.add(getCtx(), locator_from.getM_Warehouse_ID(), locator_from.getM_Locator_ID(), - line.getM_Product_ID(), - line.getM_AttributeSetInstanceTo_ID(), - Env.ZERO,null, get_TrxName())) - { - throw new AdempiereException(); - } - - } // stockec - // update line - line.setQtyReserved(line.getQtyReserved().add(reserved_ordered)); - line.saveEx(); - // - Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); - Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); + // Update Storage + if (!MStorageOnHand.add(getCtx(), locator_to.getM_Warehouse_ID(), locator_to.getM_Locator_ID(), + line.getM_Product_ID(), + line.getM_AttributeSetInstance_ID(), + Env.ZERO,null, get_TrxName())) + { + throw new AdempiereException(); + } + + if (!MStorageOnHand.add(getCtx(), locator_from.getM_Warehouse_ID(), locator_from.getM_Locator_ID(), + line.getM_Product_ID(), + line.getM_AttributeSetInstanceTo_ID(), + Env.ZERO,null, get_TrxName())) + { + throw new AdempiereException(); + } + + } // stockec + // update line + line.setQtyReserved(line.getQtyReserved().add(reserved_ordered)); + line.saveEx(); + // + Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); + Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); + } + catch (NegativeInventoryDisallowedException e) + { + log.severe(e.getMessage()); + errors.append(Msg.getElement(getCtx(), "Line")).append(" ").append(line.getLine()).append(": "); + errors.append(e.getMessage()).append("\n"); + } } // product } // reverse inventory + if (errors.toString().length() > 0) + throw new AdempiereException(errors.toString()); + setVolume(Volume); setWeight(Weight); } // reserveStock From fa258f891a9d5cf18d337aa30209c7f7920b943b Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Fri, 15 Sep 2017 17:58:03 +0200 Subject: [PATCH 09/21] IDEMPIERE-3321 Add Mandatory Logic to Report & Process Parameter. Fix for swing / thanks to Ricardo Santana (ralexsander) --- .../src/org/compiere/apps/ProcessParameterPanel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.adempiere.ui.swing/src/org/compiere/apps/ProcessParameterPanel.java b/org.adempiere.ui.swing/src/org/compiere/apps/ProcessParameterPanel.java index 58c9088e62..19b440fe3b 100644 --- a/org.adempiere.ui.swing/src/org/compiere/apps/ProcessParameterPanel.java +++ b/org.adempiere.ui.swing/src/org/compiere/apps/ProcessParameterPanel.java @@ -207,7 +207,7 @@ public class ProcessParameterPanel extends CPanel implements VetoableChangeListe + "p.AD_Reference_ID, p.AD_Process_Para_ID, " + "p.FieldLength, p.IsMandatory, p.IsRange, p.ColumnName, " + "p.DefaultValue, p.DefaultValue2, p.VFormat, p.ValueMin, p.ValueMax, " - + "p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode, p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern " + + "p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode, p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern, p.MandatoryLogic " + "FROM AD_Process_Para p" + " LEFT OUTER JOIN AD_Val_Rule vr ON (p.AD_Val_Rule_ID=vr.AD_Val_Rule_ID) " + "WHERE p.AD_Process_ID=?" // 1 @@ -218,7 +218,7 @@ public class ProcessParameterPanel extends CPanel implements VetoableChangeListe + "p.AD_Reference_ID, p.AD_Process_Para_ID, " + "p.FieldLength, p.IsMandatory, p.IsRange, p.ColumnName, " + "p.DefaultValue, p.DefaultValue2, p.VFormat, p.ValueMin, p.ValueMax, " - + "p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode, p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern " + + "p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode, p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern, p.MandatoryLogic " + "FROM AD_Process_Para p" + " INNER JOIN AD_Process_Para_Trl t ON (p.AD_Process_Para_ID=t.AD_Process_Para_ID)" + " LEFT OUTER JOIN AD_Val_Rule vr ON (p.AD_Val_Rule_ID=vr.AD_Val_Rule_ID) " From db66155f8de127fa5d95ad23ffa56b03d99010d4 Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Mon, 18 Sep 2017 17:11:30 +0200 Subject: [PATCH 10/21] IDEMPIERE-3461 For Mobile Compatibility of zk / thanks to Eduardo Gil --- .../org/adempiere/webui/AdempiereWebUI.java | 3 ++- .../src/org/adempiere/webui/WLogin.java | 13 ++++------- .../webui/desktop/DefaultDesktop.java | 12 ++++++++++ .../default/css/fragment/desktop.css.dsp | 22 +++++++++++++++++++ .../theme/default/zul/desktop/header.zul | 17 +++++--------- 5 files changed, 46 insertions(+), 21 deletions(-) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java index c4b823b039..b7674e854b 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java @@ -448,7 +448,7 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb String ua = Servlets.getUserAgent((ServletRequest) Executions.getCurrent().getNativeRequest()); clientInfo.userAgent = ua; ua = ua.toLowerCase(); - clientInfo.tablet = ua.indexOf("ipad") >= 0 || ua.indexOf("iphone") >= 0 || ua.indexOf("android") >= 0; + clientInfo.tablet = Executions.getCurrent().getBrowser("mobile") !=null; if (getDesktop() != null && getDesktop().getSession() != null) { getDesktop().getSession().setAttribute(CLIENT_INFO, clientInfo); } @@ -456,6 +456,7 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb Env.setContext(Env.getCtx(), "#clientInfo_desktopWidth", clientInfo.desktopWidth); Env.setContext(Env.getCtx(), "#clientInfo_desktopHeight", clientInfo.desktopHeight); Env.setContext(Env.getCtx(), "#clientInfo_orientation", clientInfo.orientation); + Env.setContext(Env.getCtx(), "#clientInfo_mobile", clientInfo.tablet); } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/WLogin.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/WLogin.java index ac3a08cde4..c123d9e34f 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/WLogin.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/WLogin.java @@ -17,24 +17,21 @@ package org.adempiere.webui; import java.util.Locale; import java.util.Properties; -import javax.servlet.ServletRequest; - import org.adempiere.webui.apps.AEnv; import org.adempiere.webui.part.AbstractUIPart; import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.util.ZKUpdateUtil; import org.adempiere.webui.window.LoginWindow; -import org.zkoss.web.servlet.Servlets; import org.zkoss.zhtml.Text; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.metainfo.PageDefinition; import org.zkoss.zul.Borderlayout; +import org.zkoss.zul.Div; import org.zkoss.zul.East; import org.zkoss.zul.North; import org.zkoss.zul.South; import org.zkoss.zul.West; -import org.zkoss.zul.Div; import org.zkoss.zul.Window; /** @@ -82,15 +79,13 @@ public class WLogin extends AbstractUIPart browserWarningWindow.doOverlapped(); } - String ua = Servlets.getUserAgent((ServletRequest) Executions.getCurrent().getNativeRequest()); - ua = ua.toLowerCase(); - boolean mobile = ua.indexOf("ipad") >= 0 || ua.indexOf("iphone") >= 0 || ua.indexOf("android") >= 0; - + boolean mobile = Executions.getCurrent().getBrowser("mobile") !=null; West west = layout.getWest(); if (west.getFirstChild() != null && west.getFirstChild().getFirstChild() != null) { + west.setCollapsible(true); + west.setSplittable(true); if (mobile) { - west.setCollapsible(true); west.setOpen(false); } } else { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java index 98cb2cec18..98fe8529b7 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java @@ -224,6 +224,13 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria boolean menuCollapsed= pref.isPropertyBool(UserPreference.P_MENU_COLLAPSED); w.setOpen(!menuCollapsed); + boolean mobile = Executions.getCurrent().getBrowser("mobile") !=null; + w.setCollapsible(true); + + if (mobile) { + w.setOpen(false); + } + East e = layout.getEast(); e.addEventListener(Events.ON_OPEN, new EventListener() { @Override @@ -293,6 +300,11 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria helpController.render(e, this); + if (mobile) { + e.setVisible(false); + e.setOpen(false); + } + Center windowArea = layout.getCenter(); windowContainer.createPart(windowArea); diff --git a/org.adempiere.ui.zk/theme/default/css/fragment/desktop.css.dsp b/org.adempiere.ui.zk/theme/default/css/fragment/desktop.css.dsp index c8e3f37d85..c0a1773d50 100644 --- a/org.adempiere.ui.zk/theme/default/css/fragment/desktop.css.dsp +++ b/org.adempiere.ui.zk/theme/default/css/fragment/desktop.css.dsp @@ -231,3 +231,25 @@ height: 16px; padding: 3px 3px; } +@media screen and (min-width: 720px) { + .desktop-header > .z-hlayout-inner { + width: 50%; + height: 100%; + } +} +@media screen and (max-width: 720px) { + .desktop-header { + width: 100%; + height: 100%; + + } + .desktop-user-panel { + float: none; + } + .desktop-header > .z-hlayout-inner { + width: 100%; + height: 40%; + display: block; + } + +} diff --git a/org.adempiere.ui.zk/theme/default/zul/desktop/header.zul b/org.adempiere.ui.zk/theme/default/zul/desktop/header.zul index 7df7a93103..ef83edb555 100644 --- a/org.adempiere.ui.zk/theme/default/zul/desktop/header.zul +++ b/org.adempiere.ui.zk/theme/default/zul/desktop/header.zul @@ -1,19 +1,15 @@