From 809c1cf2daa706f06e8ad79ed337fec4549503ce Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Sat, 2 May 2009 21:14:09 +0000 Subject: [PATCH] Bug fix for [1985481] - Processed documents can be edited https://sourceforge.net/tracker/?func=detail&atid=879332&aid=1985481&group_id=176962 --- base/src/org/compiere/model/GridTab.java | 54 +++++++++++++++++-- base/src/org/compiere/model/GridTable.java | 35 ++++++++++-- base/src/org/compiere/model/GridWindow.java | 4 +- .../org/compiere/model/MAllocationHdr.java | 16 +++++- .../org/compiere/model/MAllocationLine.java | 6 ++- .../org/compiere/model/MBankStatement.java | 14 ++++- .../compiere/model/MBankStatementLine.java | 19 ++++++- base/src/org/compiere/model/MCash.java | 14 ++++- base/src/org/compiere/model/MCashLine.java | 4 ++ base/src/org/compiere/model/MInOutLine.java | 8 ++- base/src/org/compiere/model/MInventory.java | 17 +++++- .../org/compiere/model/MInventoryLine.java | 4 ++ base/src/org/compiere/model/MInvoice.java | 18 ++++++- base/src/org/compiere/model/MInvoiceLine.java | 7 ++- base/src/org/compiere/model/MJournal.java | 16 +++++- base/src/org/compiere/model/MJournalLine.java | 24 ++++++++- base/src/org/compiere/model/MMovement.java | 14 ++++- .../src/org/compiere/model/MMovementLine.java | 4 ++ base/src/org/compiere/model/MOrder.java | 17 +++++- base/src/org/compiere/model/MOrderLine.java | 5 +- base/src/org/compiere/model/MRMA.java | 15 +++++- base/src/org/compiere/model/MRMALine.java | 8 ++- base/src/org/compiere/model/MRequisition.java | 16 +++++- .../org/compiere/model/MRequisitionLine.java | 7 ++- base/src/org/compiere/model/MTimeExpense.java | 14 ++++- .../org/compiere/model/MTimeExpenseLine.java | 25 +++++++-- base/src/org/eevolution/model/MDDOrder.java | 21 ++++++-- .../org/eevolution/model/MDDOrderLine.java | 5 +- client/src/org/compiere/apps/APanel.java | 20 ++++++- .../webui/panel/AbstractADWindowPanel.java | 24 +++++++-- 30 files changed, 404 insertions(+), 51 deletions(-) diff --git a/base/src/org/compiere/model/GridTab.java b/base/src/org/compiere/model/GridTab.java index de7cdd1076..9152570cc4 100644 --- a/base/src/org/compiere/model/GridTab.java +++ b/base/src/org/compiere/model/GridTab.java @@ -1,5 +1,5 @@ /****************************************************************************** - * Product: Adempiere ERP & CRM Smart Business Solution * + * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * * 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 * @@ -83,7 +83,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable /** * */ - private static final long serialVersionUID = 1010889420871357683L; + private static final long serialVersionUID = 1021401221795805887L; /** * Create Tab (Model) from Value Object. @@ -92,8 +92,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable * DataStatusListener for communicating changes of the underlying data * @param vo Value Object */ - public GridTab(GridTabVO vo) + public GridTab(GridTabVO vo, GridWindow w) { + m_window = w; m_vo = vo; // Create MTable m_mTable = new GridTable (m_vo.ctx, m_vo.AD_Table_ID, m_vo.TableName, m_vo.WindowNo, m_vo.TabNo, true); @@ -106,6 +107,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable /** Value Object */ private GridTabVO m_vo; + // The window of this tab + private GridWindow m_window; + /** The Table Model for Query */ private GridTable m_mTable = null; @@ -857,7 +861,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable /************************************************************************** - * Uncoditionally Save data + * Unconditionally Save data * @param manualCmd if true, no vetoable PropertyChange will be fired for save confirmation from MTable * @return true if save complete (or nor required) */ @@ -866,6 +870,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable log.fine("#" + m_vo.TabNo + " - row=" + m_currentRow); try { + if (hasChangedCurrentTabAndParents()) + return false; + boolean retValue = (m_mTable.dataSave(manualCmd) == GridTable.SAVE_OK); if (manualCmd) setCurrentRow(m_currentRow, false); @@ -879,6 +886,45 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable return false; } // dataSave + // Validate if the current tab record has changed in database or any parent record + // Return if there are changes + public boolean hasChangedCurrentTabAndParents() { + String msg = null; + // Carlos Ruiz / globalqss - [ adempiere-Bugs-1985481 ] Processed documents can be edited + // Validate that current record has not changed and validate that every parent above has not changed + if (m_mTable.hasChanged(m_currentRow)) { + // return error stating that current record has changed and it cannot be saved + msg = "Current record was changed by another user, please ReQuery"; + log.saveError("CurrentRecordModified", msg, false); + return true; + } + if (isDetail()) { + // get parent tab + // the parent tab is the first tab above with level = this_tab_level-1 + int level = m_vo.TabLevel; + for (int i = m_window.getTabIndex(this) - 1; i >= 0; i--) { + GridTab parentTab = m_window.getTab(i); + if (parentTab.m_vo.TabLevel == level-1) { + // this is parent tab + if (parentTab.m_mTable.hasChanged(parentTab.m_currentRow)) { + // return error stating that current record has changed and it cannot be saved + msg = "Record on parent tab " + parentTab.getName() + " was changed by another user, please ReQuery"; + log.saveError("ParentRecordModified", msg, false); + return true; + } else { + // search for the next parent + if (parentTab.isDetail()) { + level = parentTab.m_vo.TabLevel; + } else { + break; + } + } + } + } + } + return false; + } + /** * Do we need to Save? diff --git a/base/src/org/compiere/model/GridTable.java b/base/src/org/compiere/model/GridTable.java index 4b4971c509..c006a6a33e 100644 --- a/base/src/org/compiere/model/GridTable.java +++ b/base/src/org/compiere/model/GridTable.java @@ -1,5 +1,5 @@ /****************************************************************************** - * Product: Adempiere ERP & CRM Smart Business Solution * + * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved. * * 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 * @@ -50,7 +50,7 @@ import org.compiere.util.ValueNamePair; /** * Grid Table Model for JDBC access including buffering. *
- *		The following data types are handeled
+ *		The following data types are handled
  *			Integer		for all IDs
  *			BigDecimal	for all Numbers
  *			Timestamp	for all Dates
@@ -80,7 +80,7 @@ public class GridTable extends AbstractTableModel
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = -4468782288142337285L;
+	private static final long serialVersionUID = 4071601543088224064L;
 
 	/**
 	 *	JDBC Based Buffered Table
@@ -3146,4 +3146,33 @@ public class GridTable extends AbstractTableModel
 			}
 		}
 	}	//	setFieldVFormat	
+
+	// verify if the current record has changed
+	public boolean hasChanged(int row) {
+		// not so aggressive (it can has still concurrency problems)
+		// compare Updated, IsProcessed
+		if (getKeyID(row) > 0) {
+			int colUpdated = findColumn("Updated");
+			if (colUpdated > 0) {
+				Timestamp memUpdated = (Timestamp) getValueAt(row, colUpdated);
+				Timestamp dbUpdated = DB.getSQLValueTSEx(null, "SELECT Updated FROM " + m_tableName + " WHERE " + m_tableName + "_ID=?" , getKeyID(row));
+				if (! memUpdated.equals(dbUpdated))
+					return true;
+			}
+			
+			int colProcessed = findColumn("Processed");
+			if (colProcessed > 0) {
+				Boolean memProcessed = (Boolean) getValueAt(row, colProcessed);
+				String dbProcessedS = DB.getSQLValueStringEx(null, "SELECT Processed FROM " + m_tableName + " WHERE " + m_tableName + "_ID=?" , getKeyID(row));
+				Boolean dbProcessed = Boolean.TRUE;
+				if (! dbProcessedS.equals("Y"))
+					dbProcessed = Boolean.FALSE;
+				if (! memProcessed.equals(dbProcessed))
+					return true;
+			}
+		}
+
+		// @TODO: configurable aggressive - compare each column with the DB
+		return false;
+	}
 }
diff --git a/base/src/org/compiere/model/GridWindow.java b/base/src/org/compiere/model/GridWindow.java
index 51da9b9561..7d1d41a47f 100644
--- a/base/src/org/compiere/model/GridWindow.java
+++ b/base/src/org/compiere/model/GridWindow.java
@@ -1,5 +1,5 @@
 /******************************************************************************
- * Product: Adempiere ERP & CRM Smart Business Solution                        *
+ * Product: Adempiere ERP & CRM Smart Business Solution                       *
  * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
  * 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   *
@@ -141,7 +141,7 @@ public class GridWindow implements Serializable
 			GridTabVO mTabVO = (GridTabVO)m_vo.Tabs.get(t);
 			if (mTabVO != null)
 			{
-				GridTab mTab = new GridTab(mTabVO);
+				GridTab mTab = new GridTab(mTabVO, this);
 				m_tabs.add(mTab);
 			}
 		}	//  for all tabs
diff --git a/base/src/org/compiere/model/MAllocationHdr.java b/base/src/org/compiere/model/MAllocationHdr.java
index 7cd2da6842..3b3c83a280 100644
--- a/base/src/org/compiere/model/MAllocationHdr.java
+++ b/base/src/org/compiere/model/MAllocationHdr.java
@@ -1,5 +1,5 @@
 /******************************************************************************
- * Product: Adempiere ERP & CRM Smart Business Solution                        *
+ * Product: Adempiere ERP & CRM Smart Business Solution                       *
  * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
  * 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   *
@@ -52,7 +52,7 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = 4780252534394959680L;
+	private static final long serialVersionUID = 8726957992840702609L;
 
 
 	/**
@@ -758,5 +758,17 @@ public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction
 				log.log(Level.SEVERE, "BP not updated - " + bp);
 		}
 	}	//	updateBP
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
 	
 }   //  MAllocation
diff --git a/base/src/org/compiere/model/MAllocationLine.java b/base/src/org/compiere/model/MAllocationLine.java
index f79045a505..a8001c081f 100644
--- a/base/src/org/compiere/model/MAllocationLine.java
+++ b/base/src/org/compiere/model/MAllocationLine.java
@@ -25,6 +25,7 @@ import java.util.logging.Level;
 import org.compiere.util.CLogger;
 import org.compiere.util.DB;
 import org.compiere.util.Env;
+import org.compiere.util.Msg;
 
 
 /**
@@ -35,7 +36,6 @@ import org.compiere.util.Env;
  */
 public class MAllocationLine extends X_C_AllocationLine
 {
-
 	/**
 	 * 
 	 */
@@ -183,6 +183,10 @@ public class MAllocationLine extends X_C_AllocationLine
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "C_AllocationLine"));
+			return false;
+		}
 		if (!newRecord  
 			&& (is_ValueChanged("C_BPartner_ID") || is_ValueChanged("C_Invoice_ID")))
 		{
diff --git a/base/src/org/compiere/model/MBankStatement.java b/base/src/org/compiere/model/MBankStatement.java
index f0b96e6fa5..240600b173 100644
--- a/base/src/org/compiere/model/MBankStatement.java
+++ b/base/src/org/compiere/model/MBankStatement.java
@@ -47,7 +47,7 @@ public class MBankStatement extends X_C_BankStatement implements DocAction
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = -6137737123031721451L;
+	private static final long serialVersionUID = -859925588789443186L;
 
 	/**
 	 * 	Standard Constructor
@@ -671,5 +671,17 @@ public class MBankStatement extends X_C_BankStatement implements DocAction
 	//	return pl.getC_Currency_ID();
 		return 0;
 	}	//	getC_Currency_ID
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
 	
 }	//	MBankStatement
diff --git a/base/src/org/compiere/model/MBankStatementLine.java b/base/src/org/compiere/model/MBankStatementLine.java
index cd91340493..ad7922a9e5 100644
--- a/base/src/org/compiere/model/MBankStatementLine.java
+++ b/base/src/org/compiere/model/MBankStatementLine.java
@@ -44,7 +44,7 @@ import org.compiere.util.Msg;
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = -7260240724584085587L;
+	private static final long serialVersionUID = 1914411222159254809L;
 
 	/**
 	 * 	Standard Constructor
@@ -154,6 +154,10 @@ import org.compiere.util.Msg;
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "C_BankStatementLine"));
+			return false;
+		}
 		if (getChargeAmt().signum() != 0 && getC_Charge_ID() == 0)
 		{
 			log.saveError("FillMandatory", Msg.getElement(getCtx(), "C_Charge_ID"));
@@ -198,6 +202,19 @@ import org.compiere.util.Msg;
 		return true;
 	}	//	beforeSave
 	
+	/** Parent					*/
+	private MBankStatement			m_parent = null;
+	
+	/**
+	 * 	Get Parent
+	 *	@return parent
+	 */
+	public MBankStatement getParent()
+	{
+		if (m_parent == null)
+			m_parent = new MBankStatement (getCtx(), getC_BankStatement_ID(), get_TrxName());
+		return m_parent;
+	}	//	getParent
 	
 	/**
 	 * 	After Save
diff --git a/base/src/org/compiere/model/MCash.java b/base/src/org/compiere/model/MCash.java
index 5274adb60c..59a472669c 100644
--- a/base/src/org/compiere/model/MCash.java
+++ b/base/src/org/compiere/model/MCash.java
@@ -53,7 +53,7 @@ public class MCash extends X_C_Cash implements DocAction
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = 9153922329895288746L;
+	private static final long serialVersionUID = -1221144207418749593L;
 
 
 	/**
@@ -838,4 +838,16 @@ public class MCash extends X_C_Cash implements DocAction
 		return getCashBook().getC_Currency_ID();
 	}	//	getC_Currency_ID
 
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
+	
 }	//	MCash
diff --git a/base/src/org/compiere/model/MCashLine.java b/base/src/org/compiere/model/MCashLine.java
index 4effb852b5..5202795a8a 100644
--- a/base/src/org/compiere/model/MCashLine.java
+++ b/base/src/org/compiere/model/MCashLine.java
@@ -303,6 +303,10 @@ public class MCashLine extends X_C_CashLine
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "C_CashLine"));
+			return false;
+		}
 		//	Cannot change generated Invoices
 		if (is_ValueChanged(COLUMNNAME_C_Invoice_ID))
 		{
diff --git a/base/src/org/compiere/model/MInOutLine.java b/base/src/org/compiere/model/MInOutLine.java
index 891198f37c..cf6170551d 100644
--- a/base/src/org/compiere/model/MInOutLine.java
+++ b/base/src/org/compiere/model/MInOutLine.java
@@ -1,5 +1,5 @@
 /******************************************************************************
- * Product: Adempiere ERP & CRM Smart Business Solution                        *
+ * Product: Adempiere ERP & CRM Smart Business Solution                       *
  * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
  * 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   *
@@ -488,7 +488,11 @@ public class MInOutLine extends X_M_InOutLine
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
-		log.fine("");	
+		log.fine("");
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "M_InOutLine"));
+			return false;
+		}
 		// Locator is mandatory if no charge is defined - teo_sarca BF [ 2757978 ]
 		if (getM_Locator_ID() <= 0 && getC_Charge_ID() <= 0)
 		{
diff --git a/base/src/org/compiere/model/MInventory.java b/base/src/org/compiere/model/MInventory.java
index 2db103b3b8..9d815f0a70 100644
--- a/base/src/org/compiere/model/MInventory.java
+++ b/base/src/org/compiere/model/MInventory.java
@@ -47,9 +47,9 @@ import org.compiere.util.Msg;
 public class MInventory extends X_M_Inventory implements DocAction
 {
 	/**
-	 * generated serialVersionUID
+	 * 
 	 */
-	private static final long serialVersionUID = 6039577059413522140L;
+	private static final long serialVersionUID = 910998472569265447L;
 
 
 	/**
@@ -969,4 +969,17 @@ public class MInventory extends X_M_Inventory implements DocAction
 		
 		return "";
 	}
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
+	
 }	//	MInventory
diff --git a/base/src/org/compiere/model/MInventoryLine.java b/base/src/org/compiere/model/MInventoryLine.java
index c3a89d273a..9ee54c59bd 100644
--- a/base/src/org/compiere/model/MInventoryLine.java
+++ b/base/src/org/compiere/model/MInventoryLine.java
@@ -304,6 +304,10 @@ public class MInventoryLine extends X_M_InventoryLine
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "M_InventoryLine"));
+			return false;
+		}
 		if (newRecord && m_isManualEntry)
 		{
 			//	Product requires ASI
diff --git a/base/src/org/compiere/model/MInvoice.java b/base/src/org/compiere/model/MInvoice.java
index f95cdbe447..ab8aff1a2d 100644
--- a/base/src/org/compiere/model/MInvoice.java
+++ b/base/src/org/compiere/model/MInvoice.java
@@ -57,7 +57,11 @@ import org.eevolution.model.MPPProductBOMLine;
  */
 public class MInvoice extends X_C_Invoice implements DocAction
 {
-	private static final long serialVersionUID = 1L;
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -11169828430680188L;
+
 
 	/**
 	 * 	Get Payments Of BPartner
@@ -2382,5 +2386,17 @@ public class MInvoice extends X_C_Invoice implements DocAction
 		
 		return "";
 	}
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
 	
 }	//	MInvoice
diff --git a/base/src/org/compiere/model/MInvoiceLine.java b/base/src/org/compiere/model/MInvoiceLine.java
index f0f82c9995..ca819b5997 100644
--- a/base/src/org/compiere/model/MInvoiceLine.java
+++ b/base/src/org/compiere/model/MInvoiceLine.java
@@ -1,5 +1,5 @@
 /******************************************************************************
- * Product: Adempiere ERP & CRM Smart Business Solution                        *
+ * Product: Adempiere ERP & CRM Smart Business Solution                       *
  * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
  * 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   *
@@ -27,6 +27,7 @@ import java.util.logging.Level;
 import org.compiere.util.CLogger;
 import org.compiere.util.DB;
 import org.compiere.util.Env;
+import org.compiere.util.Msg;
 
 
 /**
@@ -764,6 +765,10 @@ public class MInvoiceLine extends X_C_InvoiceLine
 	protected boolean beforeSave (boolean newRecord)
 	{
 		log.fine("New=" + newRecord);
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "C_InvoiceLine"));
+			return false;
+		}
 		// Re-set invoice header (need to update m_IsSOTrx flag) - phib [ 1686773 ]
 		setInvoice(getParent());
 		//	Charge
diff --git a/base/src/org/compiere/model/MJournal.java b/base/src/org/compiere/model/MJournal.java
index 97b0e175c3..d6f9d6e8ed 100644
--- a/base/src/org/compiere/model/MJournal.java
+++ b/base/src/org/compiere/model/MJournal.java
@@ -52,7 +52,7 @@ public class MJournal extends X_GL_Journal implements DocAction
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = 5461368562157627495L;
+	private static final long serialVersionUID = -364132249042527640L;
 
 	/**
 	 * 	Standard Constructor
@@ -882,5 +882,17 @@ public class MJournal extends X_GL_Journal implements DocAction
 	{
 		return getTotalDr();
 	}	//	getApprovalAmt
-	
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
+
 }	//	MJournal
diff --git a/base/src/org/compiere/model/MJournalLine.java b/base/src/org/compiere/model/MJournalLine.java
index e38116af3a..8a4bb3c18f 100644
--- a/base/src/org/compiere/model/MJournalLine.java
+++ b/base/src/org/compiere/model/MJournalLine.java
@@ -1,5 +1,5 @@
 /******************************************************************************
- * Product: Adempiere ERP & CRM Smart Business Solution                        *
+ * Product: Adempiere ERP & CRM Smart Business Solution                       *
  * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
  * 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   *
@@ -23,6 +23,7 @@ import java.util.Properties;
 
 import org.compiere.util.DB;
 import org.compiere.util.Env;
+import org.compiere.util.Msg;
 
 /**
  *  Journal Line Model
@@ -35,7 +36,7 @@ public class MJournalLine extends X_GL_JournalLine
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = 1873359101491826792L;
+	private static final long serialVersionUID = -7008806797777773843L;
 
 	/**
 	 * 	Standard Constructor
@@ -90,6 +91,21 @@ public class MJournalLine extends X_GL_JournalLine
 		
 	}	//	MJournalLine
 
+	/** Parent					*/
+	private MJournal	m_parent = null;
+	
+	/**
+	 * 	Get Parent
+	 *	@return parent
+	 */
+	public MJournal getParent()
+	{
+		if (m_parent == null)
+			m_parent = new MJournal (getCtx(), getGL_Journal_ID(), get_TrxName());
+		return m_parent;
+	}	//	getParent
+	
+
 	/**	Currency Precision		*/
 	private int					m_precision = 2;
 	/**	Account Combination		*/
@@ -270,6 +286,10 @@ public class MJournalLine extends X_GL_JournalLine
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "GL_JournalLine"));
+			return false;
+		}
 		//	Acct Amts
 		BigDecimal rate = getCurrencyRate();
 		BigDecimal amt = rate.multiply(getAmtSourceDr());
diff --git a/base/src/org/compiere/model/MMovement.java b/base/src/org/compiere/model/MMovement.java
index 9dab0e8070..f8e1f9fbf1 100644
--- a/base/src/org/compiere/model/MMovement.java
+++ b/base/src/org/compiere/model/MMovement.java
@@ -49,7 +49,7 @@ public class MMovement extends X_M_Movement implements DocAction
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = -2900004259579407998L;
+	private static final long serialVersionUID = 3634169801280239573L;
 
 	/**
 	 * 	Standard Constructor
@@ -867,6 +867,18 @@ public class MMovement extends X_M_Movement implements DocAction
 	{
 		return m_reversal;
 	}	//	isReversal
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
 	
 }	//	MMovement
 
diff --git a/base/src/org/compiere/model/MMovementLine.java b/base/src/org/compiere/model/MMovementLine.java
index 5d60fc6f60..d6989c15c0 100644
--- a/base/src/org/compiere/model/MMovementLine.java
+++ b/base/src/org/compiere/model/MMovementLine.java
@@ -166,6 +166,10 @@ public class MMovementLine extends X_M_MovementLine
 	@Override
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "M_MovementLine"));
+			return false;
+		}
 		//	Set Line No
 		if (getLine() == 0)
 		{
diff --git a/base/src/org/compiere/model/MOrder.java b/base/src/org/compiere/model/MOrder.java
index 3ce6bc0014..7a07270f81 100644
--- a/base/src/org/compiere/model/MOrder.java
+++ b/base/src/org/compiere/model/MOrder.java
@@ -58,7 +58,10 @@ import org.eevolution.model.MPPProductBOMLine;
  */
 public class MOrder extends X_C_Order implements DocAction
 {
-	private static final long serialVersionUID = 1L;
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -1575104995897726572L;
 
 	/**
 	 * 	Create new Order by copying
@@ -2368,5 +2371,17 @@ public class MOrder extends X_C_Order implements DocAction
 		
 		return "";
 	}
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
 	
 }	//	MOrder
diff --git a/base/src/org/compiere/model/MOrderLine.java b/base/src/org/compiere/model/MOrderLine.java
index 6509d45cb3..f14a9fb178 100644
--- a/base/src/org/compiere/model/MOrderLine.java
+++ b/base/src/org/compiere/model/MOrderLine.java
@@ -48,7 +48,6 @@ import org.compiere.util.Msg;
  */
 public class MOrderLine extends X_C_OrderLine
 {
-	
 	/**
 	 * 
 	 */
@@ -720,6 +719,10 @@ public class MOrderLine extends X_C_OrderLine
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "C_OrderLine"));
+			return false;
+		}
 		//	Get Defaults from Parent
 		if (getC_BPartner_ID() == 0 || getC_BPartner_Location_ID() == 0
 			|| getM_Warehouse_ID() == 0 
diff --git a/base/src/org/compiere/model/MRMA.java b/base/src/org/compiere/model/MRMA.java
index b25ca733c8..5e5979ff89 100644
--- a/base/src/org/compiere/model/MRMA.java
+++ b/base/src/org/compiere/model/MRMA.java
@@ -43,7 +43,7 @@ public class MRMA extends X_M_RMA implements DocAction
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = -7911024337541702346L;
+	private static final long serialVersionUID = -2967208431264929454L;
 
 	/**
 	 * 	Standard Constructor
@@ -704,4 +704,17 @@ public class MRMA extends X_M_RMA implements DocAction
 	{
 		return getAmt();
 	}	//	getApprovalAmt
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
+	
 }	//	MRMA
diff --git a/base/src/org/compiere/model/MRMALine.java b/base/src/org/compiere/model/MRMALine.java
index 3af6d700bd..513cfce3ce 100644
--- a/base/src/org/compiere/model/MRMALine.java
+++ b/base/src/org/compiere/model/MRMALine.java
@@ -1,5 +1,5 @@
 /******************************************************************************
- * Product: Adempiere ERP & CRM Smart Business Solution                        *
+ * Product: Adempiere ERP & CRM Smart Business Solution                       *
  * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
  * 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   *
@@ -22,6 +22,7 @@ import java.util.Properties;
 
 import org.compiere.util.DB;
 import org.compiere.util.Env;
+import org.compiere.util.Msg;
 
 
 /**
@@ -32,7 +33,6 @@ import org.compiere.util.Env;
  */
 public class MRMALine extends X_M_RMALine
 {
-
 	/**
 	 * 
 	 */
@@ -230,6 +230,10 @@ public class MRMALine extends X_M_RMALine
      */
     protected boolean beforeSave(boolean newRecord)
     {
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "M_RMALine"));
+			return false;
+		}
         if (this.getM_InOutLine_ID() == 0 && this.getC_Charge_ID() == 0)
         {
             log.saveError("FillMandatory", "Shipment/Receipt Line or charge should be entered");
diff --git a/base/src/org/compiere/model/MRequisition.java b/base/src/org/compiere/model/MRequisition.java
index 40e54c417f..2d64f5f48a 100644
--- a/base/src/org/compiere/model/MRequisition.java
+++ b/base/src/org/compiere/model/MRequisition.java
@@ -1,5 +1,5 @@
 /******************************************************************************
- * Product: Adempiere ERP & CRM Smart Business Solution                        *
+ * Product: Adempiere ERP & CRM Smart Business Solution                       *
  * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
  * 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   *
@@ -49,7 +49,7 @@ public class MRequisition extends X_M_Requisition implements DocAction
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = -4111474920471624816L;
+	private static final long serialVersionUID = 898606565778668659L;
 
 	/**
 	 * 	Standard Constructor
@@ -578,5 +578,17 @@ public class MRequisition extends X_M_Requisition implements DocAction
 	{
 		return MUser.get(getCtx(), getAD_User_ID()).getName();
 	}	//	getUserName
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
 	
 }	//	MRequisition
diff --git a/base/src/org/compiere/model/MRequisitionLine.java b/base/src/org/compiere/model/MRequisitionLine.java
index 704c81714f..1cc4a6182f 100644
--- a/base/src/org/compiere/model/MRequisitionLine.java
+++ b/base/src/org/compiere/model/MRequisitionLine.java
@@ -1,5 +1,5 @@
 /******************************************************************************
- * Product: Adempiere ERP & CRM Smart Business Solution                        *
+ * Product: Adempiere ERP & CRM Smart Business Solution                       *
  * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
  * 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   *
@@ -26,6 +26,7 @@ import java.util.logging.Level;
 import org.adempiere.exceptions.AdempiereException;
 import org.compiere.util.DB;
 import org.compiere.util.Env;
+import org.compiere.util.Msg;
 /**
  *	Requisition Line Model
  *	
@@ -249,6 +250,10 @@ public class MRequisitionLine extends X_M_RequisitionLine
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "M_RequisitionLine"));
+			return false;
+		}
 		if (getLine() == 0)
 		{
 			String sql = "SELECT COALESCE(MAX(Line),0)+10 FROM M_RequisitionLine WHERE M_Requisition_ID=?";
diff --git a/base/src/org/compiere/model/MTimeExpense.java b/base/src/org/compiere/model/MTimeExpense.java
index 2d11ed7a5d..6ee3a60212 100644
--- a/base/src/org/compiere/model/MTimeExpense.java
+++ b/base/src/org/compiere/model/MTimeExpense.java
@@ -47,7 +47,7 @@ public class MTimeExpense extends X_S_TimeExpense implements DocAction
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = -6059700156141021371L;
+	private static final long serialVersionUID = 1567303438502090279L;
 
 
 	/**
@@ -580,5 +580,17 @@ public class MTimeExpense extends X_S_TimeExpense implements DocAction
 		MPriceList pl = MPriceList.get(getCtx(), getM_PriceList_ID(), get_TrxName());
 		return pl.getC_Currency_ID();
 	}	//	getC_Currency_ID
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
 	
 }	//	MTimeExpense
diff --git a/base/src/org/compiere/model/MTimeExpenseLine.java b/base/src/org/compiere/model/MTimeExpenseLine.java
index 98121c9f33..38a36cc7a0 100644
--- a/base/src/org/compiere/model/MTimeExpenseLine.java
+++ b/base/src/org/compiere/model/MTimeExpenseLine.java
@@ -1,5 +1,5 @@
 /******************************************************************************
- * Product: Adempiere ERP & CRM Smart Business Solution                        *
+ * Product: Adempiere ERP & CRM Smart Business Solution                       *
  * Copyright (C) 1999-2006 ComPiere, Inc. All Rights Reserved.                *
  * 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   *
@@ -23,6 +23,7 @@ import java.util.Properties;
 
 import org.compiere.util.DB;
 import org.compiere.util.Env;
+import org.compiere.util.Msg;
 
 /**
  * 	Time + Expense Line Model
@@ -35,8 +36,7 @@ public class MTimeExpenseLine extends X_S_TimeExpenseLine
 	/**
 	 * 
 	 */
-	private static final long serialVersionUID = -241908493119023444L;
-
+	private static final long serialVersionUID = -815975460880303779L;
 
 	/**
 	 * 	Standard Constructor
@@ -80,6 +80,20 @@ public class MTimeExpenseLine extends X_S_TimeExpenseLine
 		super(ctx, rs, trxName);
 	}	//	MTimeExpenseLine
 
+	/** Parent					*/
+	private MTimeExpense			m_parent = null;
+	
+	/**
+	 * 	Get Parent
+	 *	@return parent
+	 */
+	public MTimeExpense getParent()
+	{
+		if (m_parent == null)
+			m_parent = new MTimeExpense (getCtx(), getS_TimeExpense_ID(), get_TrxName());
+		return m_parent;
+	}	//	getParent
+
 	/**	Currency of Report			*/
 	private int m_C_Currency_Report_ID = 0;
 
@@ -176,6 +190,10 @@ public class MTimeExpenseLine extends X_S_TimeExpenseLine
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "S_TimeExpenseLine"));
+			return false;
+		}
 		//	Calculate Converted Amount
 		if (newRecord || is_ValueChanged("ExpenseAmt") || is_ValueChanged("C_Currency_ID"))
 		{
@@ -196,7 +214,6 @@ public class MTimeExpenseLine extends X_S_TimeExpenseLine
 		return true;
 	}	//	beforeSave
 	
-	
 	/**
 	 * 	After Save
 	 *	@param newRecord new
diff --git a/base/src/org/eevolution/model/MDDOrder.java b/base/src/org/eevolution/model/MDDOrder.java
index e591ef4f72..ae8168a48c 100644
--- a/base/src/org/eevolution/model/MDDOrder.java
+++ b/base/src/org/eevolution/model/MDDOrder.java
@@ -63,7 +63,10 @@ import org.compiere.util.Util;
  */
 public class MDDOrder extends X_DD_Order implements DocAction
 {
-	private static final long serialVersionUID = 1L;
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -2407222565384020843L;
 
 	/**
 	 * 	Create new Order by copying
@@ -1248,7 +1251,17 @@ public class MDDOrder extends X_DD_Order implements DocAction
 		// TODO Auto-generated method stub
 		return 0;
 	}
-	
-	
-	
+
+	/**
+	 * 	Document Status is Complete or Closed
+	 *	@return true if CO, CL or RE
+	 */
+	public boolean isComplete()
+	{
+		String ds = getDocStatus();
+		return DOCSTATUS_Completed.equals(ds) 
+			|| DOCSTATUS_Closed.equals(ds)
+			|| DOCSTATUS_Reversed.equals(ds);
+	}	//	isComplete
+
 }	//	MDDOrder
diff --git a/base/src/org/eevolution/model/MDDOrderLine.java b/base/src/org/eevolution/model/MDDOrderLine.java
index 4417d7fc58..6c77f8f1f7 100644
--- a/base/src/org/eevolution/model/MDDOrderLine.java
+++ b/base/src/org/eevolution/model/MDDOrderLine.java
@@ -48,7 +48,6 @@ import org.compiere.util.Msg;
  */
 public class MDDOrderLine extends X_DD_OrderLine
 {
-	
 	/**
 	 * 
 	 */
@@ -514,6 +513,10 @@ public class MDDOrderLine extends X_DD_OrderLine
 	 */
 	protected boolean beforeSave (boolean newRecord)
 	{
+		if (newRecord && getParent().isComplete()) {
+			log.saveError("ParentComplete", Msg.translate(getCtx(), "DD_OrderLine"));
+			return false;
+		}
 		//	Get Defaults from Parent
 		/*if (getC_BPartner_ID() == 0 || getC_BPartner_Location_ID() == 0
 			|| getM_Warehouse_ID() == 0)
diff --git a/client/src/org/compiere/apps/APanel.java b/client/src/org/compiere/apps/APanel.java
index de6f581301..8b74d93846 100644
--- a/client/src/org/compiere/apps/APanel.java
+++ b/client/src/org/compiere/apps/APanel.java
@@ -1215,6 +1215,7 @@ public final class APanel extends CPanel
 						{
 							if (!m_curTab.dataSave(true))
 							{	//  there is a problem, so we go back
+								showLastError();
 								m_curWinTab.setSelectedIndex(m_curTabIndex);
 								setBusy(false, true);
 								return;
@@ -1225,6 +1226,7 @@ public final class APanel extends CPanel
 						{   //  yes we want to save
 							if (!m_curTab.dataSave(true))
 							{   //  there is a problem, so we go back
+								showLastError();
 								m_curWinTab.setSelectedIndex(m_curTabIndex);
 								setBusy(false, true);
 								return;
@@ -1844,8 +1846,7 @@ public final class APanel extends CPanel
 		//   if there is no previous error
 		if (manualCmd && !retValue && !m_errorDisplayed)
 		{
-			ADialog.error(m_curWindowNo, this, "SaveIgnored");
-			setStatusLine(Msg.getMsg(m_ctx, "SaveIgnored"), true);
+			showLastError();
 		}
 		if (retValue)
 			m_curGC.rowChanged(true, m_curTab.getRecord_ID());
@@ -1864,6 +1865,15 @@ public final class APanel extends CPanel
 		return retValue;
 	}   //  cmd_save
 
+	private void showLastError() {
+		String msg = CLogger.retrieveErrorString(null);
+		if (msg != null)
+			ADialog.error(m_curWindowNo, this, msg);
+		else
+			ADialog.error(m_curWindowNo, this, "SaveIgnored");
+		setStatusLine(Msg.getMsg(m_ctx, "SaveIgnored"), true);
+	}
+
 	/**
 	 *  Ignore
 	 */
@@ -2251,6 +2261,12 @@ public final class APanel extends CPanel
 	{
 		log.info(vButton.toString());
 
+		if (m_curTab.hasChangedCurrentTabAndParents()) {
+			String msg = CLogger.retrieveErrorString("Please ReQuery Window");
+			ADialog.error(m_curWindowNo, this, msg);
+			return;
+		}
+
 		boolean startWOasking = false;
 //		boolean batch = false;
 		String col = vButton.getColumnName();
diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java b/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java
index d446dcfc44..a6e7f9e213 100644
--- a/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java
+++ b/zkwebui/WEB-INF/src/org/adempiere/webui/panel/AbstractADWindowPanel.java
@@ -97,8 +97,6 @@ import org.zkoss.zul.Menupopup;
 public abstract class AbstractADWindowPanel extends AbstractUIPart implements ToolbarListener,
         EventListener, DataStatusListener, ActionListener, ASyncProcess
 {
-    private static final long    serialVersionUID = 1L;
-
     private static final CLogger logger;
 
     static
@@ -765,6 +763,7 @@ public abstract class AbstractADWindowPanel extends AbstractUIPart implements To
 					{
 						if (!curTab.dataSave(true))
 						{
+							showLastError();
 							//  there is a problem, stop here
 							return false;
 						}
@@ -774,6 +773,7 @@ public abstract class AbstractADWindowPanel extends AbstractUIPart implements To
 					{   //  yes we want to save
 						if (!curTab.dataSave(true))
 						{
+							showLastError();
 							//  there is a problem, stop here
 							return false;
 						}
@@ -1210,9 +1210,7 @@ public abstract class AbstractADWindowPanel extends AbstractUIPart implements To
 
 	        if (!retValue)
 	        {
-	        	//actual error will prompt in the dataStatusChanged event
-//	            FDialog.error(curWindowNo, parent, "SaveIgnored");
-	            statusBar.setStatusLine(Msg.getMsg(Env.getCtx(), "SaveIgnored"), true);
+	        	showLastError();
 	            return false;
 	        }
 	        curTabpanel.dynamicDisplay(0);
@@ -1221,6 +1219,16 @@ public abstract class AbstractADWindowPanel extends AbstractUIPart implements To
     	}
     }
 
+	private void showLastError() {
+		String msg = CLogger.retrieveErrorString(null);
+		if (msg != null)
+			FDialog.error(curWindowNo, parent, msg);
+		else
+			FDialog.error(curWindowNo, parent, "SaveIgnored");
+		//actual error will prompt in the dataStatusChanged event
+		statusBar.setStatusLine(Msg.getMsg(Env.getCtx(), "SaveIgnored"), true);
+	}
+
     /**
      * @see ToolbarListener#onDelete()
      */
@@ -1596,6 +1604,12 @@ public abstract class AbstractADWindowPanel extends AbstractUIPart implements To
 	 */
 	private void actionButton (WButtonEditor wButton)
 	{
+		if (curTab.hasChangedCurrentTabAndParents()) {
+			String msg = CLogger.retrieveErrorString("Please ReQuery Window");
+			FDialog.error(curWindowNo, parent, msg);
+			return;
+		}
+
 		logger.info(wButton.toString());
 
 		boolean startWOasking = false;