IDEMPIERE-493 Add validation for matching

This commit is contained in:
Heng Sin Low 2012-11-08 07:50:01 +08:00
parent 7023ce3eea
commit ba27cda7cf
4 changed files with 386 additions and 53 deletions

View File

@ -1552,16 +1552,18 @@ public class MInOut extends X_M_InOut implements DocAction
log.fine("PO Matching");
// Ship - PO
MMatchPO po = MMatchPO.create (null, sLine, getMovementDate(), matchQty);
boolean isNewMatchPO = false;
if (po.get_ID() == 0)
isNewMatchPO = true;
if (!po.save(get_TrxName()))
{
m_processMsg = "Could not create PO Matching";
return DocAction.STATUS_Invalid;
if (po != null) {
boolean isNewMatchPO = false;
if (po.get_ID() == 0)
isNewMatchPO = true;
if (!po.save(get_TrxName()))
{
m_processMsg = "Could not create PO Matching";
return DocAction.STATUS_Invalid;
}
if (isNewMatchPO)
addDocsPostProcess(po);
}
if (isNewMatchPO)
addDocsPostProcess(po);
// Update PO with ASI
if ( oLine != null && oLine.getM_AttributeSetInstance_ID() == 0
&& sLine.getMovementQty().compareTo(oLine.getQtyOrdered()) == 0) // just if full match [ 1876965 ]
@ -1580,18 +1582,21 @@ public class MInOut extends X_M_InOut implements DocAction
// Ship - Invoice
MMatchPO po = MMatchPO.create (iLine, sLine,
getMovementDate(), matchQty);
boolean isNewMatchPO = false;
if (po.get_ID() == 0)
isNewMatchPO = true;
if (!po.save(get_TrxName()))
{
m_processMsg = "Could not create PO(Inv) Matching";
return DocAction.STATUS_Invalid;
if (po != null) {
boolean isNewMatchPO = false;
if (po.get_ID() == 0)
isNewMatchPO = true;
if (!po.save(get_TrxName()))
{
m_processMsg = "Could not create PO(Inv) Matching";
return DocAction.STATUS_Invalid;
}
if (isNewMatchPO)
addDocsPostProcess(po);
}
if (isNewMatchPO)
addDocsPostProcess(po);
// Update PO with ASI
oLine = new MOrderLine (getCtx(), po.getC_OrderLine_ID(), get_TrxName());
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 ]
{

View File

@ -222,6 +222,33 @@ public class MMatchInv extends X_M_MatchInv
return true;
} // beforeSave
@Override
protected boolean afterSave(boolean newRecord, boolean success) {
if (!success)
return false;
if (getM_InOutLine_ID() > 0)
{
MInOutLine line = new MInOutLine(getCtx(), getM_InOutLine_ID(), get_TrxName());
BigDecimal matchedQty = DB.getSQLValueBD(get_TrxName(), "SELECT Coalesce(SUM(Qty),0) FROM M_MatchInv WHERE M_InOutLine_ID=?" , getM_InOutLine_ID());
if (matchedQty != null && matchedQty.compareTo(line.getMovementQty()) > 0)
{
throw new IllegalStateException("Total matched qty > movement qty. MatchedQty="+matchedQty+", MovementQty="+line.getMovementQty()+", Line="+line);
}
}
if (getC_InvoiceLine_ID() > 0)
{
MInvoiceLine line = new MInvoiceLine(getCtx(), getC_InvoiceLine_ID(), get_TrxName());
BigDecimal matchedQty = DB.getSQLValueBD(get_TrxName(), "SELECT Coalesce(SUM(Qty),0) FROM M_MatchInv WHERE C_InvoiceLine_ID=?" , getC_InvoiceLine_ID());
if (matchedQty != null && matchedQty.compareTo(line.getQtyInvoiced()) > 0)
{
throw new IllegalStateException("Total matched qty > invoiced qty. MatchedQty="+matchedQty+", InvoicedQty="+line.getQtyInvoiced()+", Line="+line);
}
}
return true;
}
/**
* Get the later Date Acct from invoice or shipment
* @return date or null
@ -279,7 +306,9 @@ public class MMatchInv extends X_M_MatchInv
deleteMatchInvCostDetail();
// end AZ
// delete m_matchinv doesn't auto make m_matchpo invalid
// Get Order and decrease invoices
/*
MInvoiceLine iLine = new MInvoiceLine (getCtx(), getC_InvoiceLine_ID(), get_TrxName());
int C_OrderLine_ID = iLine.getC_OrderLine_ID();
if (C_OrderLine_ID == 0)
@ -303,6 +332,7 @@ public class MMatchInv extends X_M_MatchInv
mPO[i].saveEx();
}
}
*/
}
return success;
} // afterDelete

View File

@ -19,6 +19,7 @@ package org.compiere.model;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Savepoint;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Properties;
@ -27,6 +28,8 @@ import java.util.logging.Level;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Trx;
import org.compiere.util.ValueNamePair;
/**
* Match PO Model.
@ -100,6 +103,53 @@ public class MMatchPO extends X_M_MatchPO
return retValue;
} // get
/**
* Get PO Match of Receipt Line
* @param ctx context
* @param M_InOutLine_ID receipt
* @param trxName transaction
* @return array of matches
*/
public static MMatchPO[] get (Properties ctx,
int M_InOutLine_ID, String trxName)
{
if (M_InOutLine_ID == 0)
return new MMatchPO[]{};
//
String sql = "SELECT * FROM M_MatchPO WHERE M_InOutLine_ID=?";
ArrayList<MMatchPO> list = new ArrayList<MMatchPO>();
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement (sql, trxName);
pstmt.setInt (1, M_InOutLine_ID);
rs = pstmt.executeQuery ();
while (rs.next ())
list.add (new MMatchPO (ctx, rs, trxName));
}
catch (Exception e)
{
s_log.log(Level.SEVERE, sql, e);
if (e instanceof RuntimeException)
{
throw (RuntimeException)e;
}
else
{
throw new IllegalStateException(e);
}
}
finally
{
DB.close(rs, pstmt);
}
MMatchPO[] retValue = new MMatchPO[list.size()];
list.toArray (retValue);
return retValue;
} // get
/**
* Get PO Matches of receipt
* @param ctx context
@ -249,8 +299,54 @@ public class MMatchPO extends X_M_MatchPO
C_OrderLine_ID = sLine.getC_OrderLine_ID();
}
if (C_OrderLine_ID > 0)
{
return create(ctx, iLine, sLine, C_OrderLine_ID, dateTrx, qty, trxName);
}
else
{
if (sLine != null && iLine != null)
{
MMatchPO[] matchpos = MMatchPO.get(ctx, sLine.getM_InOutLine_ID(), trxName);
for (MMatchPO matchpo : matchpos)
{
C_OrderLine_ID = matchpo.getC_OrderLine_ID();
MOrderLine orderLine = new MOrderLine(ctx, C_OrderLine_ID, trxName);
BigDecimal toInvoice = orderLine.getQtyOrdered().subtract(orderLine.getQtyInvoiced());
if (toInvoice.signum() <= 0)
continue;
BigDecimal matchQty = qty;
if (matchQty.compareTo(toInvoice) > 0)
matchQty = toInvoice;
if (matchQty.signum() <= 0)
continue;
MMatchPO newMatchPO = create(ctx, iLine, sLine, C_OrderLine_ID, dateTrx, matchQty, trxName);
if (!newMatchPO.save())
{
String msg = "Failed to update match po.";
ValueNamePair error = CLogger.retrieveError();
if (error != null)
{
msg = msg + " " + error.getName();
}
throw new RuntimeException(msg);
}
qty = qty.subtract(matchQty);
if (qty.signum() <= 0)
return newMatchPO;
}
}
return null;
}
}
private static MMatchPO create(Properties ctx, MInvoiceLine iLine,
MInOutLine sLine, int C_OrderLine_ID, Timestamp dateTrx,
BigDecimal qty, String trxName) {
MMatchPO retValue = null;
String sql = "SELECT * FROM M_MatchPO WHERE C_OrderLine_ID=?";
String sql = "SELECT * FROM M_MatchPO WHERE C_OrderLine_ID=? ORDER BY M_MatchPO_ID";
PreparedStatement pstmt = null;
ResultSet rs = null;
try
@ -261,14 +357,17 @@ public class MMatchPO extends X_M_MatchPO
while (rs.next ())
{
MMatchPO mpo = new MMatchPO (ctx, rs, trxName);
if (qty.compareTo(mpo.getQty()) == 0)
if (qty.compareTo(mpo.getQty()) >= 0)
{
BigDecimal toMatch = qty;
BigDecimal matchQty = mpo.getQty();
if (toMatch.compareTo(matchQty) > 0)
toMatch = matchQty;
if (iLine != null)
{
if (mpo.getC_InvoiceLine_ID() == 0
|| mpo.getC_InvoiceLine_ID() == iLine.getC_InvoiceLine_ID())
if ((mpo.getC_InvoiceLine_ID() == 0)
|| (mpo.getC_InvoiceLine_ID() == iLine.getC_InvoiceLine_ID()))
{
mpo.setC_InvoiceLine_ID(iLine);
if (iLine.getM_AttributeSetInstance_ID() != 0)
{
if (mpo.getM_AttributeSetInstance_ID() == 0)
@ -282,10 +381,10 @@ public class MMatchPO extends X_M_MatchPO
}
if (sLine != null)
{
if (mpo.getM_InOutLine_ID() == 0
|| mpo.getM_InOutLine_ID() == sLine.getM_InOutLine_ID())
if ((mpo.getM_InOutLine_ID() == 0)
|| (mpo.getM_InOutLine_ID() == sLine.getM_InOutLine_ID()))
{
mpo.setM_InOutLine_ID(sLine.getM_InOutLine_ID());
if (sLine.getM_AttributeSetInstance_ID() != 0)
{
if (mpo.getM_AttributeSetInstance_ID() == 0)
@ -297,33 +396,152 @@ public class MMatchPO extends X_M_MatchPO
else
continue;
}
if ((iLine != null || mpo.getC_InvoiceLine_ID() > 0) && (sLine != null || mpo.getM_InOutLine_ID() > 0))
{
int M_InOutLine_ID = sLine != null ? sLine.getM_InOutLine_ID() : mpo.getM_InOutLine_ID();
int C_InvoiceLine_ID = iLine != null ? iLine.getC_InvoiceLine_ID() : mpo.getC_InvoiceLine_ID();
//verify invoiceline not already linked to another inoutline
int tmpInOutLineId = DB.getSQLValue(mpo.get_TrxName(), "SELECT M_InOutLine_ID FROM C_InvoiceLine WHERE C_InvoiceLine_ID="+C_InvoiceLine_ID);
if (tmpInOutLineId > 0 && tmpInOutLineId != M_InOutLine_ID)
{
continue;
}
//verify m_matchinv not created yet
int cnt = DB.getSQLValue(mpo.get_TrxName(), "SELECT Count(*) FROM M_MatchInv WHERE M_InOutLine_ID="+M_InOutLine_ID
+" AND C_InvoiceLine_ID="+C_InvoiceLine_ID);
if (cnt <= 0)
{
Trx trx = trxName != null ? Trx.get(trxName, false) : null;
Savepoint savepoint = trx != null ? trx.getConnection().setSavepoint() : null;
MMatchInv matchInv = new MMatchInv(mpo.getCtx(), 0, mpo.get_TrxName());
matchInv.setC_InvoiceLine_ID(C_InvoiceLine_ID);
matchInv.setM_Product_ID(mpo.getM_Product_ID());
matchInv.setM_InOutLine_ID(M_InOutLine_ID);
matchInv.setAD_Client_ID(mpo.getAD_Client_ID());
matchInv.setAD_Org_ID(mpo.getAD_Org_ID());
matchInv.setM_AttributeSetInstance_ID(mpo.getM_AttributeSetInstance_ID());
matchInv.setQty(mpo.getQty());
matchInv.setDateTrx(dateTrx);
if (!matchInv.save())
{
if (savepoint != null)
{
trx.getConnection().rollback(savepoint);
savepoint = null;
}
else
{
matchInv.delete(true);
}
String msg = "Failed to auto match invoice.";
ValueNamePair error = CLogger.retrieveError();
if (error != null)
{
msg = msg + " " + error.getName();
}
//log as debug message and continue
s_log.fine(msg);
continue;
}
if (savepoint != null)
{
try {
trx.getConnection().releaseSavepoint(savepoint);
} catch (Exception e) {}
}
}
}
if (iLine != null)
mpo.setC_InvoiceLine_ID(iLine);
if (sLine != null)
mpo.setM_InOutLine_ID(sLine.getM_InOutLine_ID());
if (!mpo.save())
{
String msg = "Failed to update match po.";
ValueNamePair error = CLogger.retrieveError();
if (error != null)
{
msg = msg + " " + error.getName();
}
throw new RuntimeException(msg);
}
qty = qty.subtract(toMatch);
if (qty.signum() <= 0)
{
retValue = mpo;
break;
}
}
}
}
catch (Exception e)
{
s_log.log(Level.SEVERE, sql, e);
if (e instanceof RuntimeException)
{
throw (RuntimeException)e;
}
else
{
throw new IllegalStateException(e);
}
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
// Create New
if (retValue == null)
{
if (sLine != null)
BigDecimal sLineMatchedQty = null;
if (sLine != null && iLine != null)
{
retValue = new MMatchPO (sLine, dateTrx, qty);
if (iLine != null)
retValue.setC_InvoiceLine_ID(iLine);
sLineMatchedQty = DB.getSQLValueBD(sLine.get_TrxName(), "SELECT Sum(Qty) FROM M_MatchPO WHERE C_OrderLine_ID="+C_OrderLine_ID+" AND M_InOutLine_ID=?", sLine.getM_InOutLine_ID());
}
if (sLine != null && (sLine.getC_OrderLine_ID() == C_OrderLine_ID || iLine == null)
&& (sLineMatchedQty == null || sLineMatchedQty.signum() <= 0))
{
if (qty.signum() > 0)
{
retValue = new MMatchPO (sLine, dateTrx, qty);
retValue.setC_OrderLine_ID(C_OrderLine_ID);
if (iLine != null)
retValue.setC_InvoiceLine_ID(iLine);
if (!retValue.save())
{
String msg = "Failed to update match po.";
ValueNamePair error = CLogger.retrieveError();
if (error != null)
{
msg = msg + " " + error.getName();
}
throw new RuntimeException(msg);
}
}
}
else if (iLine != null)
{
retValue = new MMatchPO (iLine, dateTrx, qty);
if (qty.signum() > 0)
{
retValue = new MMatchPO (iLine, dateTrx, qty);
retValue.setC_OrderLine_ID(C_OrderLine_ID);
if (!retValue.save())
{
String msg = "Failed to update match po.";
ValueNamePair error = CLogger.retrieveError();
if (error != null)
{
msg = msg + " " + error.getName();
}
throw new RuntimeException(msg);
}
}
}
}
@ -617,6 +835,20 @@ public class MMatchPO extends X_M_MatchPO
setPriceMatchDifference(difference);
setIsApproved(true);
}
//validate against M_MatchInv
if (getM_InOutLine_ID() > 0 && getC_InvoiceLine_ID() > 0)
{
int cnt = DB.getSQLValue(get_TrxName(), "SELECT Count(*) FROM M_MatchInv WHERE M_InOutLine_ID="+getM_InOutLine_ID()
+" AND C_InvoiceLine_ID="+getC_InvoiceLine_ID());
if (cnt <= 0)
{
MInvoiceLine invoiceLine = new MInvoiceLine(getCtx(), getC_InvoiceLine_ID(), get_TrxName());
MInOutLine inoutLine = new MInOutLine(getCtx(), getM_InOutLine_ID(), get_TrxName());
throw new IllegalStateException("[MatchPO] Missing corresponding invoice matching record for invoice line "
+ invoiceLine + " and receipt line " + inoutLine);
}
}
}
return true;
@ -632,6 +864,46 @@ public class MMatchPO extends X_M_MatchPO
@Override
protected boolean afterSave (boolean newRecord, boolean success)
{
//perform matched qty validation
if (success)
{
if (getM_InOutLine_ID() > 0)
{
MInOutLine line = new MInOutLine(getCtx(), getM_InOutLine_ID(), get_TrxName());
BigDecimal matchedQty = DB.getSQLValueBD(get_TrxName(), "SELECT Coalesce(SUM(Qty),0) FROM M_MatchPO WHERE M_InOutLine_ID=?" , getM_InOutLine_ID());
if (matchedQty != null && matchedQty.compareTo(line.getMovementQty()) > 0)
{
throw new IllegalStateException("Total matched qty > movement qty. MatchedQty="+matchedQty+", MovementQty="+line.getMovementQty()+", Line="+line);
}
}
if (getC_InvoiceLine_ID() > 0)
{
MInvoiceLine line = new MInvoiceLine(getCtx(), getC_InvoiceLine_ID(), get_TrxName());
BigDecimal matchedQty = DB.getSQLValueBD(get_TrxName(), "SELECT Coalesce(SUM(Qty),0) FROM M_MatchPO WHERE C_InvoiceLine_ID=?" , getC_InvoiceLine_ID());
if (matchedQty != null && matchedQty.compareTo(line.getQtyInvoiced()) > 0)
{
throw new IllegalStateException("Total matched qty > invoiced qty. MatchedQty="+matchedQty+", InvoicedQty="+line.getQtyInvoiced()+", Line="+line);
}
}
if (getC_OrderLine_ID() > 0)
{
MOrderLine line = new MOrderLine(getCtx(), getC_OrderLine_ID(), get_TrxName());
BigDecimal invoicedQty = DB.getSQLValueBD(get_TrxName(), "SELECT Coalesce(SUM(Qty),0) FROM M_MatchPO WHERE C_InvoiceLine_ID > 0 and C_OrderLine_ID=?" , getC_OrderLine_ID());
if (invoicedQty != null && invoicedQty.compareTo(line.getQtyOrdered()) > 0)
{
throw new IllegalStateException("Total matched invoiced qty > ordered qty. MatchedInvoicedQty="+invoicedQty+", OrderedQty="+line.getQtyOrdered()+", Line="+line);
}
BigDecimal deliveredQty = DB.getSQLValueBD(get_TrxName(), "SELECT Coalesce(SUM(Qty),0) FROM M_MatchPO WHERE M_InOutLine_ID > 0 and C_OrderLine_ID=?" , getC_OrderLine_ID());
if (deliveredQty != null && deliveredQty.compareTo(line.getQtyOrdered()) > 0)
{
throw new IllegalStateException("Total matched delivered qty > ordered qty. MatchedDeliveredQty="+deliveredQty+", OrderedQty="+line.getQtyOrdered()+", Line="+line);
}
}
}
// Purchase Order Delivered/Invoiced
// (Reserved in VMatch and MInOut.completeIt)
if (success && getC_OrderLine_ID() != 0)

View File

@ -21,6 +21,7 @@ import java.sql.Timestamp;
import java.util.Vector;
import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.minigrid.IDColumn;
import org.compiere.minigrid.IMiniTable;
import org.compiere.model.MClient;
@ -38,6 +39,7 @@ import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
import org.compiere.util.Msg;
import org.compiere.util.Trx;
import org.compiere.util.ValueNamePair;
public class Match
{
@ -416,50 +418,74 @@ public class Match
{
MMatchInv match = new MMatchInv (iLine, null, qty);
match.setM_InOutLine_ID(M_InOutLine_ID);
if (match.save()) {
success = true;
if (MClient.isClientAccountingImmediate()) {
String ignoreError = DocumentEngine.postImmediate(match.getCtx(), match.getAD_Client_ID(), match.get_Table_ID(), match.get_ID(), true, match.get_TrxName());
match.saveEx();
success = true;
if (MClient.isClientAccountingImmediate()) {
String ignoreError = DocumentEngine.postImmediate(match.getCtx(), match.getAD_Client_ID(), match.get_Table_ID(), match.get_ID(), true, match.get_TrxName());
if (ignoreError != null) {
log.info(ignoreError);
}
}
else
log.log(Level.SEVERE, "Inv Match not created: " + match);
}
else
success = true;
// Create PO - Invoice Link = corrects PO
if (iLine.getC_OrderLine_ID() != 0 && iLine.getM_Product_ID() != 0)
if (iLine.getM_Product_ID() != 0)
{
MMatchPO matchPO = MMatchPO.create(iLine, sLine, null, qty);
matchPO.setC_InvoiceLine_ID(iLine);
matchPO.setM_InOutLine_ID(M_InOutLine_ID);
if (!matchPO.save())
log.log(Level.SEVERE, "PO(Inv) Match not created: " + matchPO);
if (MClient.isClientAccountingImmediate()) {
String ignoreError = DocumentEngine.postImmediate(matchPO.getCtx(), matchPO.getAD_Client_ID(), matchPO.get_Table_ID(), matchPO.get_ID(), true, matchPO.get_TrxName());
BigDecimal matchedQty = DB.getSQLValueBD(iLine.get_TrxName(), "SELECT Coalesce(SUM(Qty),0) FROM M_MatchPO WHERE C_InvoiceLine_ID=?" , iLine.getC_InvoiceLine_ID());
if (matchedQty.add(qty).compareTo(iLine.getQtyInvoiced()) <= 0)
{
MMatchPO matchPO = MMatchPO.create(iLine, sLine, null, qty);
if (matchPO != null)
{
matchPO.saveEx();
if (MClient.isClientAccountingImmediate()) {
String ignoreError = DocumentEngine.postImmediate(matchPO.getCtx(), matchPO.getAD_Client_ID(), matchPO.get_Table_ID(), matchPO.get_ID(), true, matchPO.get_TrxName());
if (ignoreError != null)
log.info(ignoreError);
}
}
}
}
}
else // Shipment - Order
{
// Update Shipment Line
sLine.setC_OrderLine_ID(Line_ID);
sLine.saveEx();
// Update Order Line
MOrderLine oLine = new MOrderLine(Env.getCtx(), Line_ID, trxName);
if (oLine.get_ID() != 0) // other in MInOut.completeIt
{
oLine.setQtyReserved(oLine.getQtyReserved().subtract(qty));
if(!oLine.save())
log.severe("QtyReserved not updated - C_OrderLine_ID=" + Line_ID);
oLine.saveEx();
}
// Update Shipment Line
BigDecimal toDeliver = oLine.getQtyOrdered().subtract(oLine.getQtyDelivered());
if (sLine.getMovementQty().compareTo(toDeliver) <= 0)
{
sLine.setC_OrderLine_ID(Line_ID);
sLine.saveEx();
}
else if (sLine.getC_OrderLine_ID() != 0)
{
sLine.setC_OrderLine_ID(0);
sLine.saveEx();
}
// Create PO - Shipment Link
if (sLine.getM_Product_ID() != 0)
{
MMatchPO match = new MMatchPO (sLine, null, qty);
match.setC_OrderLine_ID(Line_ID);
if (!match.save())
log.log(Level.SEVERE, "PO Match not created: " + match);
{
String msg = "PO Match not created: " + match;
ValueNamePair error = CLogger.retrieveError();
if (error != null)
{
msg = msg + ". " + error.getName();
}
throw new AdempiereException(msg);
}
else
{
success = true;