IDEMPIERE-5065 Material Receipt Line not available for matching after… (#1019)

* IDEMPIERE-5065 Material Receipt Line not available for matching after reversal of Match PO

* IDEMPIERE-5065 Material Receipt Line not available for matching after reversal of Match PO

Fix unit test

* IDEMPIERE-5065 Material Receipt Line not available for matching after reversal of Match PO

Remove unreliable and potentially dangerous match po consolidation
This commit is contained in:
hengsin 2021-12-14 01:48:48 +08:00 committed by GitHub
parent 6923fbe18e
commit 74b6604d57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1041 additions and 134 deletions

View File

@ -53,7 +53,7 @@ public class MatchPOReverse extends SvrProcess {
if (reversalDate == null) {
reversalDate = new Timestamp(System.currentTimeMillis());
}
if (!po.reverse(reversalDate))
if (!po.reverse(reversalDate, true))
throw new AdempiereException("Failed to reverse matching");
}
return "@OK@";

View File

@ -138,7 +138,7 @@ public abstract class Doc
* M_Requisition POR
**************************************************************************/
private static final String DOC_TYPE_BY_DOC_BASE_TYPE_SQL = "SELECT C_DocType_ID FROM C_DocType WHERE AD_Client_ID=? AND DocBaseType=? AND IsActive='Y' ORDER BY IsDefault DESC, C_DocType_ID";
public static final String DOC_TYPE_BY_DOC_BASE_TYPE_SQL = "SELECT C_DocType_ID FROM C_DocType WHERE AD_Client_ID=? AND DocBaseType=? AND IsActive='Y' ORDER BY IsDefault DESC, C_DocType_ID";
/** AR Invoices - ARI */
public static final String DOCTYPE_ARInvoice = MDocType.DOCBASETYPE_ARInvoice;

View File

@ -31,6 +31,10 @@ import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import org.adempiere.base.Core;
import org.adempiere.util.IReservationTracer;
import org.adempiere.util.IReservationTracerFactory;
import org.compiere.acct.Doc;
import org.compiere.process.DocAction;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
@ -1274,84 +1278,12 @@ public class MMatchPO extends X_M_MatchPO
.append (",C_OrderLine_ID=").append (getC_OrderLine_ID())
.append (",M_InOutLine_ID=").append (getM_InOutLine_ID())
.append (",C_InvoiceLine_ID=").append (getC_InvoiceLine_ID())
.append (",Processed=").append(isProcessed())
.append (",Posted=").append(isPosted())
.append ("]");
return sb.toString ();
} // toString
/**
* Consolidate MPO entries.
* (data conversion issue)
* @param ctx context
*/
public static void consolidate(Properties ctx)
{
String sql = "SELECT * FROM M_MatchPO po "
+ "WHERE EXISTS (SELECT 1 FROM M_MatchPO x "
+ "WHERE po.C_OrderLine_ID=x.C_OrderLine_ID AND po.Qty=x.Qty "
+ "GROUP BY C_OrderLine_ID, Qty "
+ "HAVING COUNT(*) = 2) "
+ " AND AD_Client_ID=?"
+ "ORDER BY C_OrderLine_ID, M_InOutLine_ID";
PreparedStatement pstmt = null;
ResultSet rs = null;
int success = 0;
int errors = 0;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setInt(1, Env.getAD_Client_ID(ctx));
rs = pstmt.executeQuery ();
while (rs.next ())
{
MMatchPO po1 = new MMatchPO (ctx, rs, null);
if (rs.next())
{
MMatchPO po2 = new MMatchPO (ctx, rs, null);
if (po1.getM_InOutLine_ID() != 0 && po1.getC_InvoiceLine_ID() == 0
&& po2.getM_InOutLine_ID() == 0 && po2.getC_InvoiceLine_ID() != 0)
{
StringBuilder s1 = new StringBuilder("UPDATE M_MatchPO SET C_InvoiceLine_ID=")
.append(po2.getC_InvoiceLine_ID())
.append(" WHERE M_MatchPO_ID=").append(po1.getM_MatchPO_ID());
int no1 = DB.executeUpdate(s1.toString(), null);
if (no1 != 1)
{
errors++;
s_log.warning("Not updated M_MatchPO_ID=" + po1.getM_MatchPO_ID());
continue;
}
//
String s2 = "DELETE FROM Fact_Acct WHERE AD_Table_ID=473 AND Record_ID=?";
int no2 = DB.executeUpdate(s2, po2.getM_MatchPO_ID(), null);
String s3 = "DELETE FROM M_MatchPO WHERE M_MatchPO_ID=?";
int no3 = DB.executeUpdate(s3, po2.getM_MatchPO_ID(), null);
if (no2 == 0 && no3 == 1)
success++;
else
{
s_log.warning("M_MatchPO_ID=" + po2.getM_MatchPO_ID()
+ " - Deleted=" + no2 + ", Acct=" + no3);
errors++;
}
}
}
}
}
catch (Exception e)
{
s_log.log (Level.SEVERE, sql, e);
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
if (errors == 0 && success == 0)
;
else
if (s_log.isLoggable(Level.INFO)) s_log.info("Success #" + success + " - Error #" + errors);
} // consolidate
/**
* Reverse MatchPO.
* @param reversalDate
@ -1360,6 +1292,19 @@ public class MMatchPO extends X_M_MatchPO
*/
public boolean reverse(Timestamp reversalDate)
{
return reverse(reversalDate, false);
}
/**
* Reverse MatchPO.
* @param reversalDate
* @param reverseMatchingOnly true if MR is not reverse
* @return boolean
* @throws Exception
*/
public boolean reverse(Timestamp reversalDate, boolean reverseMatchingOnly)
{
if (this.isProcessed() && this.getReversal_ID() == 0)
{
@ -1389,6 +1334,50 @@ public class MMatchPO extends X_M_MatchPO
this.setReversal_ID(reversal.getM_MatchPO_ID());
this.saveEx();
//update qtyOrdered
if (reverseMatchingOnly && reversal.getM_InOutLine_ID() > 0 && reversal.getC_OrderLine_ID() > 0)
{
MInOutLine sLine = new MInOutLine(Env.getCtx(), reversal.getM_InOutLine_ID(), get_TrxName());
if (sLine.getMovementQty().compareTo(this.getQty()) == 0 && sLine.getC_OrderLine_ID() == reversal.getC_OrderLine_ID())
{
//clear c_orderline from shipment so we can match the shipment again (to the same or different order line)
sLine.setC_OrderLine_ID(0);
sLine.saveEx();
}
//add back qtyOrdered
MOrderLine oLine = new MOrderLine(Env.getCtx(), reversal.getC_OrderLine_ID(), get_TrxName());
BigDecimal storageReservationToUpdate = oLine.getQtyReserved();
oLine.setQtyReserved(oLine.getQtyReserved().add(getQty()));
BigDecimal reservedAndDelivered = oLine.getQtyDelivered().add(oLine.getQtyReserved());
if (reservedAndDelivered.compareTo(oLine.getQtyOrdered()) > 0)
{
oLine.setQtyReserved(oLine.getQtyReserved().subtract(reservedAndDelivered.subtract(oLine.getQtyOrdered())));
if (oLine.getQtyReserved().signum()==-1)
oLine.setQtyReserved(Env.ZERO);
}
oLine.saveEx();
storageReservationToUpdate = storageReservationToUpdate.subtract(oLine.getQtyReserved());
if (storageReservationToUpdate.signum() != 0)
{
IReservationTracer tracer = null;
IReservationTracerFactory factory = Core.getReservationTracerFactory();
if (factory != null)
{
int docTypeId = DB.getSQLValue((String)null, Doc.DOC_TYPE_BY_DOC_BASE_TYPE_SQL, getAD_Client_ID(), Doc.DOCTYPE_MatMatchPO);
tracer = factory.newTracer(docTypeId, reversal.getDocumentNo(), 10,
reversal.get_Table_ID(), reversal.get_ID(), oLine.getM_Warehouse_ID(),
oLine.getM_Product_ID(), oLine.getM_AttributeSetInstance_ID(), oLine.getParent().isSOTrx(),
get_TrxName());
}
boolean success = MStorageReservation.add (Env.getCtx(), oLine.getM_Warehouse_ID(),
oLine.getM_Product_ID(),
oLine.getM_AttributeSetInstance_ID(),
storageReservationToUpdate.negate(), oLine.getParent().isSOTrx(), get_TrxName(), tracer);
if (!success)
return false;
}
}
// auto create new matchpo if have invoice line
if ( reversal.getC_InvoiceLine_ID() > 0 && reversal.getM_InOutLine_ID() > 0 )
{

View File

@ -55,7 +55,6 @@ import org.adempiere.webui.util.ZKUpdateUtil;
import org.compiere.apps.form.Match;
import org.compiere.minigrid.ColumnInfo;
import org.compiere.minigrid.IDColumn;
import org.compiere.model.MMatchPO;
import org.compiere.util.CLogger;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
@ -112,7 +111,6 @@ public class WMatch extends Match
LayoutUtils.addSclass("status-border", statusBar);
//
MMatchPO.consolidate(Env.getCtx());
cmd_matchTo();
}
catch(Exception e)

View File

@ -25,6 +25,7 @@ import org.adempiere.base.Core;
import org.adempiere.exceptions.AdempiereException;
import org.adempiere.util.IReservationTracer;
import org.adempiere.util.IReservationTracerFactory;
import org.compiere.acct.Doc;
import org.compiere.minigrid.IDColumn;
import org.compiere.minigrid.IMiniTable;
import org.compiere.model.MClient;
@ -35,6 +36,7 @@ import org.compiere.model.MMatchPO;
import org.compiere.model.MOrderLine;
import org.compiere.model.MRole;
import org.compiere.model.MStorageReservation;
import org.compiere.model.Query;
import org.compiere.process.DocumentEngine;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
@ -55,39 +57,33 @@ public class Match
Msg.getElement(Env.getCtx(), "C_Invoice_ID", false),
Msg.getElement(Env.getCtx(), "M_InOut_ID", false),
Msg.getElement(Env.getCtx(), "C_Order_ID", false) };
private static final int MATCH_INVOICE = 0;
private static final int MATCH_SHIPMENT = 1;
private static final int MATCH_ORDER = 2;
private static final int MODE_NOTMATCHED = 0;
public static final int MATCH_INVOICE = 0;
public static final int MATCH_SHIPMENT = 1;
public static final int MATCH_ORDER = 2;
public static final int MODE_NOTMATCHED = 0;
//private static final int MODE_MATCHED = 1;
/** Indexes in Table */
private static final int I_BPartner = 3;
private static final int I_Line = 4;
private static final int I_Product = 5;
private static final int I_QTY = 6;
private static final int I_MATCHED = 7;
//private static final int I_Org = 8; //JAVIER
public static final int I_BPartner = 3;
public static final int I_Line = 4;
public static final int I_Product = 5;
public static final int I_QTY = 6;
public static final int I_MATCHED = 7;
private StringBuffer m_sql = null;
private String m_dateColumn = "";
private String m_qtyColumn = "";
private String m_groupBy = "";
private StringBuffer m_linetype = null;
//private BigDecimal m_xMatched = Env.ZERO;
//private BigDecimal m_xMatchedTo = Env.ZERO;
private String m_trxName = null;
/**
* Match From Changed - Fill Match To
*/
protected Vector<String> cmd_matchFrom(String selection)
{
// if (log.isLoggable(Level.FINE)) log.fine( "VMatch.cmd_matchFrom");
//String selection = (String)matchFrom.getSelectedItem();
Vector<String> vector = new Vector<String>(2);
if (selection.equals(m_matchOptions[MATCH_INVOICE]))
vector.add(m_matchOptions[MATCH_SHIPMENT]);
@ -103,13 +99,11 @@ public class Match
/**
* Search Button Pressed - Fill xMatched
* Search Button Pressed - Fill match from
*/
protected IMiniTable cmd_search(IMiniTable xMatchedTable, int display, String matchToString, Integer Product, Integer Vendor, Timestamp from, Timestamp to, boolean matched)
public IMiniTable cmd_search(IMiniTable xMatchedTable, int display, String matchToString, Integer Product, Integer Vendor, Timestamp from, Timestamp to, boolean matched)
{
// ** Create SQL **
//int display = matchFrom.getSelectedIndex();
//String matchToString = (String)matchTo.getSelectedItem();
int matchToType = MATCH_INVOICE;
if (matchToString.equals(m_matchOptions[MATCH_SHIPMENT]))
matchToType = MATCH_SHIPMENT;
@ -132,8 +126,6 @@ public class Match
m_sql.append(" AND hdr.C_BPartner_ID=").append(Vendor);
}
// Date
//Timestamp from = (Timestamp)dateFrom.getValue();
//Timestamp to = (Timestamp)dateTo.getValue();
if (from != null && to != null)
m_sql.append(" AND ").append(m_dateColumn).append(" BETWEEN ")
.append(DB.TO_DATE(from)).append(" AND ").append(DB.TO_DATE(to));
@ -152,14 +144,13 @@ public class Match
/**
* Process Button Pressed - Process Matching
*/
protected void cmd_process(IMiniTable xMatchedTable, IMiniTable xMatchedToTable, int matchMode, int matchFrom, Object matchTo, BigDecimal m_xMatched)
public void cmd_process(IMiniTable xMatchedTable, IMiniTable xMatchedToTable, int matchMode, int matchFrom, String matchTo, BigDecimal m_xMatched)
{
log.config("");
// Matched From
int matchedRow = xMatchedTable.getSelectedRow();
if (matchedRow < 0)
return;
// KeyNamePair BPartner = (KeyNamePair)xMatchedTable.getValueAt(matchedRow, I_BPartner);
KeyNamePair lineMatched = (KeyNamePair)xMatchedTable.getValueAt(matchedRow, I_Line);
KeyNamePair Product = (KeyNamePair)xMatchedTable.getValueAt(matchedRow, I_Product);
@ -207,39 +198,45 @@ public class Match
}
// Create it
String innerTrxName = Trx.createTrxName("Match");
Trx innerTrx = Trx.get(innerTrxName, true);
String innerTrxName = m_trxName == null ? Trx.createTrxName("Match") : null;
Trx innerTrx = innerTrxName != null ? Trx.get(innerTrxName, true) : null;
if (innerTrx != null)
innerTrx.setDisplayName(getClass().getName()+"_cmd_process");
try{
if (createMatchRecord(invoice, M_InOutLine_ID, Line_ID, BigDecimal.valueOf(qty), innerTrxName))
try {
if (createMatchRecord(invoice, M_InOutLine_ID, Line_ID, BigDecimal.valueOf(qty), m_trxName != null ? m_trxName : innerTrxName)) {
if (innerTrx != null)
innerTrx.commit();
else
} else {
if (innerTrx != null)
innerTrx.rollback();
}catch(Exception ex){
else
Trx.get(m_trxName, false).rollback();
}
} catch(Exception ex) {
if (innerTrx != null)
innerTrx.rollback();
throw new AdempiereException(ex);
}finally{
} finally {
if (innerTrx != null) {
innerTrx.close();
innerTrx = null;
}
}
}
// requery
//cmd_search();
}
} // cmd_process
/**
* Fill xMatchedTo
* Fill match to
*/
protected IMiniTable cmd_searchTo(IMiniTable xMatchedTable, IMiniTable xMatchedToTable, String displayString, int matchToType, boolean sameBPartner, boolean sameProduct, boolean sameQty, boolean matched)
public IMiniTable cmd_searchTo(IMiniTable xMatchedTable, IMiniTable xMatchedToTable, String displayString, int matchToType, boolean sameBPartner, boolean sameProduct, boolean sameQty, boolean matched)
{
int row = xMatchedTable.getSelectedRow();
if (log.isLoggable(Level.CONFIG)) log.config("Row=" + row);
// ** Create SQL **
//String displayString = (String)matchTo.getSelectedItem();
int display = MATCH_INVOICE;
if (displayString.equals(m_matchOptions[MATCH_SHIPMENT]))
display = MATCH_SHIPMENT;
@ -248,11 +245,9 @@ public class Match
KeyNamePair lineMatched = (KeyNamePair)xMatchedTable.getValueAt(row, I_Line);
//int matchToType = matchFrom.getSelectedIndex();
tableInit (display, matchToType, matched, lineMatched); // sets m_sql
// ** Add Where Clause **
KeyNamePair BPartner = (KeyNamePair)xMatchedTable.getValueAt(row, I_BPartner);
//KeyNamePair Org = (KeyNamePair)xMatchedTable.getValueAt(row, I_Org); //JAVIER
KeyNamePair Product = (KeyNamePair)xMatchedTable.getValueAt(row, I_Product);
if (log.isLoggable(Level.FINE)) log.fine("BPartner=" + BPartner + " - Product=" + Product);
//
@ -327,7 +322,14 @@ public class Match
m_qtyColumn = "lin.QtyOrdered";
m_sql.append("SELECT hdr.C_Order_ID,hdr.DocumentNo, hdr.DateOrdered, bp.Name,hdr.C_BPartner_ID,"
+ " lin.Line,lin.C_OrderLine_ID, p.Name,lin.M_Product_ID,"
+ " lin.QtyOrdered,SUM(COALESCE(mo.Qty,0)), org.Name, hdr.AD_Org_ID " //JAVIER
+ " lin.QtyOrdered,");
if (matchToType == MATCH_SHIPMENT)
m_sql.append("SUM(CASE WHEN (mo.M_InOutLine_ID IS NOT NULL) THEN COALESCE(mo.Qty,0) ELSE 0 END), ");
else if (matchToType == MATCH_INVOICE)
m_sql.append("SUM(CASE WHEN (mo.C_InvoiceLine_ID IS NOT NULL) THEN COALESCE(mo.Qty,0) ELSE 0 END), ");
else
m_sql.append("SUM(COALESCE(mo.Qty,0)), ");
m_sql.append("org.Name, hdr.AD_Org_ID " //JAVIER
+ "FROM C_Order hdr"
+ " INNER JOIN AD_Org org ON (hdr.AD_Org_ID=org.AD_Org_ID)" //JAVIER
+ " INNER JOIN C_BPartner bp ON (hdr.C_BPartner_ID=bp.C_BPartner_ID)"
@ -355,8 +357,14 @@ public class Match
m_groupBy = " GROUP BY hdr.C_Order_ID,hdr.DocumentNo,hdr.DateOrdered,bp.Name,hdr.C_BPartner_ID,"
+ " lin.Line,lin.C_OrderLine_ID,p.Name,lin.M_Product_ID,lin.QtyOrdered, org.Name, hdr.AD_Org_ID " //JAVIER
+ "HAVING "
+ (matched ? "0" : "lin.QtyOrdered")
+ "<>SUM(COALESCE(mo.Qty,0))";
+ (matched ? "0" : "lin.QtyOrdered");
if (matchToType == MATCH_SHIPMENT)
m_groupBy = m_groupBy + "<>SUM(CASE WHEN (mo.M_InOutLine_ID IS NOT NULL) THEN COALESCE(mo.Qty,0) ELSE 0 END) ";
else if (matchToType == MATCH_INVOICE)
m_groupBy = m_groupBy + "<>SUM(CASE WHEN (mo.C_InvoiceLine_ID IS NOT NULL) THEN COALESCE(mo.Qty,0) ELSE 0 END) ";
else
m_groupBy = m_groupBy + "<>SUM(COALESCE(mo.Qty,0)) ";
}
else // Shipment
{
@ -364,7 +372,12 @@ public class Match
m_qtyColumn = "lin.MovementQty";
m_sql.append("SELECT hdr.M_InOut_ID,hdr.DocumentNo, hdr.MovementDate, bp.Name,hdr.C_BPartner_ID,"
+ " lin.Line,lin.M_InOutLine_ID, p.Name,lin.M_Product_ID,"
+ " CASE WHEN (dt.DocBaseType='MMS' AND hdr.issotrx='N') THEN lin.MovementQty * -1 ELSE lin.MovementQty END,SUM(NVL(m.Qty,0)),org.Name, hdr.AD_Org_ID " //JAVIER
+ " CASE WHEN (dt.DocBaseType='MMS' AND hdr.issotrx='N') THEN lin.MovementQty * -1 ELSE lin.MovementQty END,");
if (matchToType == MATCH_ORDER)
m_sql.append("SUM(CASE WHEN m.M_InOutLine_ID IS NOT NULL THEN COALESCE(m.Qty,0) ELSE 0 END),");
else
m_sql.append("SUM(COALESCE(m.Qty,0)),");
m_sql.append("org.Name, hdr.AD_Org_ID " //JAVIER
+ "FROM M_InOut hdr"
+ " INNER JOIN AD_Org org ON (hdr.AD_Org_ID=org.AD_Org_ID)" //JAVIER
+ " INNER JOIN C_BPartner bp ON (hdr.C_BPartner_ID=bp.C_BPartner_ID)"
@ -383,10 +396,12 @@ public class Match
m_groupBy = " GROUP BY hdr.M_InOut_ID,hdr.DocumentNo,hdr.MovementDate,bp.Name,hdr.C_BPartner_ID,"
+ " lin.Line,lin.M_InOutLine_ID,p.Name,lin.M_Product_ID,lin.MovementQty, org.Name, hdr.AD_Org_ID, dt.DocBaseType " //JAVIER
+ "HAVING "
+ (matched ? "0" : "CASE WHEN (dt.DocBaseType='MMS' AND hdr.issotrx='N') THEN lin.MovementQty * -1 ELSE lin.MovementQty END")
+ "<>SUM(NVL(m.Qty,0))";
+ (matched ? "0" : "CASE WHEN (dt.DocBaseType='MMS' AND hdr.issotrx='N') THEN lin.MovementQty * -1 ELSE lin.MovementQty END");
if (matchToType == MATCH_ORDER)
m_groupBy = m_groupBy + "<>SUM(CASE WHEN m.M_InOutLine_ID IS NOT NULL THEN COALESCE(m.Qty,0) ELSE 0 END)";
else
m_groupBy = m_groupBy + "<>SUM(COALESCE(m.Qty,0))";
}
// Log.trace(7, "VMatch.tableInit", m_sql + "\n" + m_groupBy);
} // tableInit
@ -396,7 +411,6 @@ public class Match
*/
protected void tableLoad (IMiniTable table)
{
// log.finest(m_sql + " - " + m_groupBy);
String sql = MRole.getDefault().addAccessSQL(
m_sql.toString(), "hdr", MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO)
+ m_groupBy;
@ -405,7 +419,7 @@ public class Match
ResultSet rs = null;
try
{
stmt = DB.createStatement();
stmt = DB.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, m_trxName);
rs = stmt.executeQuery(sql);
table.loadTable(rs);
}
@ -518,7 +532,7 @@ public class Match
// Create PO - Shipment Link
if (sLine.getM_Product_ID() != 0)
{
MMatchPO match = new MMatchPO (sLine, null, qty);
MMatchPO match = getOrCreate(Line_ID, qty, sLine, trxName);
match.setC_OrderLine_ID(Line_ID);
if (!match.save())
{
@ -555,4 +569,43 @@ public class Match
}
return success;
} // createMatchRecord
private MMatchPO getOrCreate(int C_OrderLine_ID, BigDecimal qty, MInOutLine sLine, String trxName) {
Query query = new Query(Env.getCtx(), MMatchPO.Table_Name, "C_OrderLine_ID=? AND Qty=? AND Posted IN (?,?) AND M_InOutLine_ID IS NULL", trxName);
MMatchPO matchPO = query.setParameters(C_OrderLine_ID, qty, Doc.STATUS_NotPosted, Doc.STATUS_Deferred).first();
if (matchPO != null) {
matchPO.setM_InOutLine_ID(sLine.getM_InOutLine_ID());
return matchPO;
} else {
return new MMatchPO (sLine, null, qty);
}
}
/**
*
* @param trxName
*/
public void setTrxName(String trxName) {
m_trxName = trxName;
}
/**
*
* @return trxName
*/
public String getTrxName() {
return m_trxName;
}
/**
*
* @param matchType MATCH_INVOICE, MATCH_SHIPMENT or MATCH_ORDER
* @return display text for match type
*/
public String getMatchTypeText(int matchType) {
if (matchType >= 0 && matchType < m_matchOptions.length)
return m_matchOptions[matchType];
return null;
}
}

View File

@ -29,7 +29,8 @@ Require-Bundle: org.adempiere.base;bundle-version="9.0.0",
org.adempiere.payment.processor;bundle-version="9.0.0",
org.compiere.db.postgresql.provider;bundle-version="9.0.0",
org.idempiere.webservices;bundle-version="9.0.0",
org.adempiere.ui.zk;bundle-version="9.0.0"
org.adempiere.ui.zk;bundle-version="9.0.0",
org.adempiere.ui;bundle-version="9.0.0"
Bundle-ActivationPolicy: lazy
Bundle-Activator: org.idempiere.test.TestActivator
Bundle-RequiredExecutionEnvironment: JavaSE-11

View File

@ -29,7 +29,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.math.BigDecimal;
import java.sql.Timestamp;
import org.compiere.apps.form.Match;
import org.compiere.minigrid.ColumnInfo;
import org.compiere.minigrid.IDColumn;
import org.compiere.model.MBPartner;
import org.compiere.model.MDocType;
import org.compiere.model.MInOut;
@ -41,12 +45,18 @@ import org.compiere.model.MMatchPO;
import org.compiere.model.MOrder;
import org.compiere.model.MOrderLine;
import org.compiere.model.MProduct;
import org.compiere.model.MStorageOnHand;
import org.compiere.model.MStorageReservation;
import org.compiere.model.MWarehouse;
import org.compiere.process.DocAction;
import org.compiere.process.ProcessInfo;
import org.compiere.process.ServerProcessCtl;
import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
import org.compiere.util.Msg;
import org.compiere.wf.MWorkflow;
import org.idempiere.test.AbstractTestCase;
import org.idempiere.test.ui.MiniTableImpl;
import org.junit.jupiter.api.Test;
/**
@ -484,4 +494,395 @@ public class MatchPOTest extends AbstractTestCase {
rollback();
}
@Test
public void testReverseFullyMatchPO() {
MBPartner bpartner = MBPartner.get(Env.getCtx(), 114); // Tree Farm Inc.
MProduct product = MProduct.get(Env.getCtx(), 124); // Elm Tree
int initialOnHand = MStorageOnHand.getQtyOnHand(product.get_ID(), getM_Warehouse_ID(), 0, getTrxName()).intValue();
int initialOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
order.setBPartner(bpartner);
order.setIsSOTrx(false);
order.setC_DocTypeTarget_ID();
order.setDocStatus(DocAction.STATUS_Drafted);
order.setDocAction(DocAction.ACTION_Complete);
order.saveEx();
BigDecimal orderQty = new BigDecimal("1");
MOrderLine orderLine = new MOrderLine(order);
orderLine.setLine(10);
orderLine.setProduct(product);
orderLine.setQty(orderQty);
orderLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
order.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
orderLine.load(getTrxName());
assertEquals(1, orderLine.getQtyReserved().intValue(), "Unexpected order line qty ordered value");
int newOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
assertEquals(initialOnOrdered+1, newOnOrdered, "Unexpected qty on ordered value");
MInOut receipt = new MInOut(order, 122, order.getDateOrdered()); // MM Receipt
receipt.saveEx();
MWarehouse wh = MWarehouse.get(Env.getCtx(), receipt.getM_Warehouse_ID());
int M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID();
MInOutLine receiptLine = new MInOutLine(receipt);
receiptLine.setOrderLine(orderLine, M_Locator_ID, orderQty);
receiptLine.setLine(10);
receiptLine.setQty(orderQty);
receiptLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
receipt.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, receipt.getDocStatus());
orderLine.load(getTrxName());
assertEquals(0, orderLine.getQtyReserved().intValue(), "Unexpected order line qty ordered value");
assertEquals(1, orderLine.getQtyDelivered().intValue(), "Unexpected order line qty delivered value");
newOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
assertEquals(initialOnOrdered, newOnOrdered, "Unexpected qty on ordered value");
int newOnHand = MStorageOnHand.getQtyOnHand(product.get_ID(), getM_Warehouse_ID(), 0, getTrxName()).intValue();
assertEquals(initialOnHand+1, newOnHand, "Unexpected qty on hand value");
MMatchPO[] matchPOs = MMatchPO.getOrderLine(Env.getCtx(), orderLine.get_ID(), getTrxName());
assertEquals(1, matchPOs.length, "Unexpected number of MatchPO for order line");
int matchedPOReverse = 200016;
info = new ProcessInfo("MatchPOReverse", matchedPOReverse, MMatchPO.Table_ID, matchPOs[0].get_ID());
ServerProcessCtl.process(info, getTrx(), false);
assertFalse(info.isError(), info.getSummary());
orderLine.load(getTrxName());
assertEquals(1, orderLine.getQtyReserved().intValue(), "Unexpected order line qty ordered value");
assertEquals(0, orderLine.getQtyDelivered().intValue(), "Unexpected order line qty delivered value");
receiptLine.load(getTrxName());
assertEquals(0, receiptLine.getC_OrderLine_ID(), "Unexpected order line ID value for receipt line");
matchPOs = MMatchPO.getOrderLine(Env.getCtx(), orderLine.get_ID(), getTrxName());
assertEquals(2, matchPOs.length, "Unexpected number of MatchPO for order line");
newOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
assertEquals(initialOnOrdered+1, newOnOrdered, "Unexpected qty on ordered value");
newOnHand = MStorageOnHand.getQtyOnHand(product.get_ID(), getM_Warehouse_ID(), 0, getTrxName()).intValue();
assertEquals(initialOnHand+1, newOnHand, "Unexpected qty on hand value");
MiniTableImpl fromTable = new MiniTableImpl();
MiniTableImpl toTable = new MiniTableImpl();
ColumnInfo[] layout = new ColumnInfo[] {
new ColumnInfo(" ", ".", IDColumn.class, false, false, ""),
new ColumnInfo(Msg.translate(Env.getCtx(), "DocumentNo"), ".", String.class), // 1
new ColumnInfo(Msg.translate(Env.getCtx(), "Date"), ".", Timestamp.class),
new ColumnInfo(Msg.translate(Env.getCtx(), "C_BPartner_ID"),".", KeyNamePair.class, "."), // 3
new ColumnInfo(Msg.translate(Env.getCtx(), "Line"), ".", KeyNamePair.class, "."),
new ColumnInfo(Msg.translate(Env.getCtx(), "M_Product_ID"), ".", KeyNamePair.class, "."), // 5
new ColumnInfo(Msg.translate(Env.getCtx(), "Qty"), ".", Double.class),
new ColumnInfo(Msg.translate(Env.getCtx(), "Matched"), ".", Double.class)
};
fromTable.prepareTable(layout, null, null, false, null);
toTable.prepareTable(layout, null, null, false, null);
Match match = new Match();
match.setTrxName(getTrxName());
match.cmd_search(fromTable, Match.MATCH_SHIPMENT, match.getMatchTypeText(Match.MATCH_ORDER), product.get_ID(), bpartner.get_ID(), null, null, false);
assertTrue(fromTable.getRowCount()>0, "Unexpected number of records for not matched Material Receipt: " + fromTable.getRowCount());
int selectedRow = -1;
for(int i = 0; i < fromTable.getRowCount(); i++) {
String docNo = (String)fromTable.getValueAt(i, 1);
if (receipt.getDocumentNo().equals(docNo)) {
int matched = ((Number)fromTable.getValueAt(i, 7)).intValue();
assertEquals(0, matched, "Unexpected matched qty for Material Receipt line");
int qty = ((Number)fromTable.getValueAt(i, 6)).intValue();
assertEquals(receiptLine.getMovementQty().intValue(), qty, "Unexpected qty for Material Receipt line");
selectedRow = i;
break;
}
}
assertTrue(selectedRow >= 0, "Can't find not matched Material Receipt line");
fromTable.setSelectedRow(selectedRow);
match.cmd_searchTo(fromTable, toTable, match.getMatchTypeText(Match.MATCH_ORDER), Match.MATCH_SHIPMENT, true, true, false, false);
assertTrue(toTable.getRowCount()>0, "Unexpected number of records for not matched Order Line: " + fromTable.getRowCount());
int selectedOrderRow = -1;
for(int i = 0; i < toTable.getRowCount(); i++) {
String docNo = (String)toTable.getValueAt(i, 1);
if (order.getDocumentNo().equals(docNo)) {
int matched = ((Number)toTable.getValueAt(i, 7)).intValue();
assertEquals(0, matched, "Unexpected matched qty for PO line");
int qty = ((Number)toTable.getValueAt(i, 6)).intValue();
assertEquals(orderLine.getQtyOrdered().intValue(), qty, "Unexpected qty for PO line");
selectedOrderRow = i;
break;
}
}
assertTrue(selectedOrderRow >= 0, "Can't find not matched PO line");
//create vendor invoice
MInvoice invoice = new MInvoice(order, MDocType.getOfDocBaseType(Env.getCtx(), MDocType.DOCBASETYPE_APInvoice)[0].getC_DocType_ID(), order.getDateAcct());
invoice.setDocStatus(DocAction.STATUS_Drafted);
invoice.setDocAction(DocAction.ACTION_Complete);
invoice.saveEx();
MInvoiceLine invoiceLine = new MInvoiceLine(invoice);
invoiceLine.setOrderLine(orderLine);
invoiceLine.setLine(10);
invoiceLine.setProduct(product);
invoiceLine.setQty(orderQty);
invoiceLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
invoice.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, invoice.getDocStatus());
orderLine.load(getTrxName());
assertEquals(invoiceLine.getQtyInvoiced().intValue(), orderLine.getQtyInvoiced().intValue(), "Unexpected order line qty invoiced");
matchPOs = MMatchPO.getOrderLine(Env.getCtx(), orderLine.get_ID(), getTrxName());
assertEquals(3, matchPOs.length, "Unexpected number of MatchPO for order line");
fromTable.prepareTable(layout, null, null, false, null);
toTable.prepareTable(layout, null, null, false, null);
match.cmd_search(fromTable, Match.MATCH_SHIPMENT, match.getMatchTypeText(Match.MATCH_ORDER), product.get_ID(), bpartner.get_ID(), null, null, false);
assertTrue(fromTable.getRowCount()>0, "Unexpected number of records for not matched Material Receipt: " + fromTable.getRowCount());
selectedRow = -1;
for(int i = 0; i < fromTable.getRowCount(); i++) {
String docNo = (String)fromTable.getValueAt(i, 1);
if (receipt.getDocumentNo().equals(docNo)) {
int matched = ((Number)fromTable.getValueAt(i, 7)).intValue();
assertEquals(0, matched, "Unexpected matched qty for Material Receipt line");
int qty = ((Number)fromTable.getValueAt(i, 6)).intValue();
assertEquals(receiptLine.getMovementQty().intValue(), qty, "Unexpected qty for Material Receipt line");
selectedRow = i;
break;
}
}
assertTrue(selectedRow >= 0, "Can't find not matched Material Receipt line");
fromTable.setSelectedRow(selectedRow);
match.cmd_searchTo(fromTable, toTable, match.getMatchTypeText(Match.MATCH_ORDER), Match.MATCH_SHIPMENT, true, true, false, false);
assertTrue(toTable.getRowCount()>0, "Unexpected number of records for not matched Order Line: " + fromTable.getRowCount());
selectedOrderRow = -1;
for(int i = 0; i < toTable.getRowCount(); i++) {
String docNo = (String)toTable.getValueAt(i, 1);
if (order.getDocumentNo().equals(docNo)) {
int matched = ((Number)toTable.getValueAt(i, 7)).intValue();
assertEquals(0, matched, "Unexpected matched qty for PO line");
int qty = ((Number)toTable.getValueAt(i, 6)).intValue();
assertEquals(orderLine.getQtyOrdered().intValue(), qty, "Unexpected qty for PO line");
selectedOrderRow = i;
break;
}
}
assertTrue(selectedOrderRow >= 0, "Can't find not matched PO line");
IDColumn idColumn = (IDColumn)toTable.getValueAt(selectedOrderRow, 0);
idColumn.setSelected(true);
match.cmd_process(fromTable, toTable, Match.MODE_NOTMATCHED, Match.MATCH_SHIPMENT, match.getMatchTypeText(Match.MATCH_ORDER), new BigDecimal(1));
orderLine.load(getTrxName());
assertEquals(0, orderLine.getQtyReserved().intValue(), "Unexpected order line qty ordered value");
assertEquals(1, orderLine.getQtyDelivered().intValue(), "Unexpected order line qty delivered value");
receiptLine.load(getTrxName());
assertEquals(orderLine.getC_OrderLine_ID(), receiptLine.getC_OrderLine_ID(), "Unexpected order line ID value for receipt line");
matchPOs = MMatchPO.getOrderLine(Env.getCtx(), orderLine.get_ID(), getTrxName());
assertEquals(3, matchPOs.length, "Unexpected number of MatchPO for order line");
newOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
assertEquals(initialOnOrdered, newOnOrdered, "Unexpected qty on ordered value");
newOnHand = MStorageOnHand.getQtyOnHand(product.get_ID(), getM_Warehouse_ID(), 0, getTrxName()).intValue();
assertEquals(initialOnHand+1, newOnHand, "Unexpected qty on hand value");
}
@Test
public void testReversePartialMatchPO() {
MBPartner bpartner = MBPartner.get(Env.getCtx(), 114); // Tree Farm Inc.
MProduct product = MProduct.get(Env.getCtx(), 124); // Elm Tree
int initialOnHand = MStorageOnHand.getQtyOnHand(product.get_ID(), getM_Warehouse_ID(), 0, getTrxName()).intValue();
int initialOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
order.setBPartner(bpartner);
order.setIsSOTrx(false);
order.setC_DocTypeTarget_ID();
order.setDocStatus(DocAction.STATUS_Drafted);
order.setDocAction(DocAction.ACTION_Complete);
order.saveEx();
BigDecimal orderQty = new BigDecimal("2");
MOrderLine orderLine = new MOrderLine(order);
orderLine.setLine(10);
orderLine.setProduct(product);
orderLine.setQty(orderQty);
orderLine.saveEx();
ProcessInfo info = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
order.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, order.getDocStatus());
orderLine.load(getTrxName());
assertEquals(2, orderLine.getQtyReserved().intValue(), "Unexpected order line qty ordered value");
int newOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
assertEquals(initialOnOrdered+2, newOnOrdered, "Unexpected qty on ordered value");
MInOut receipt = new MInOut(order, 122, order.getDateOrdered()); // MM Receipt
receipt.saveEx();
MWarehouse wh = MWarehouse.get(Env.getCtx(), receipt.getM_Warehouse_ID());
int M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID();
BigDecimal receiptQty = new BigDecimal("1");
MInOutLine receiptLine = new MInOutLine(receipt);
receiptLine.setOrderLine(orderLine, M_Locator_ID, receiptQty);
receiptLine.setLine(10);
receiptLine.setQty(receiptQty);
receiptLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(receipt, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
receipt.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, receipt.getDocStatus());
orderLine.load(getTrxName());
assertEquals(1, orderLine.getQtyReserved().intValue(), "Unexpected order line qty ordered value");
assertEquals(1, orderLine.getQtyDelivered().intValue(), "Unexpected order line qty delivered value");
newOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
assertEquals(initialOnOrdered+1, newOnOrdered, "Unexpected qty on ordered value");
int newOnHand = MStorageOnHand.getQtyOnHand(product.get_ID(), getM_Warehouse_ID(), 0, getTrxName()).intValue();
assertEquals(initialOnHand+1, newOnHand, "Unexpected qty on hand value");
MMatchPO[] matchPOs = MMatchPO.getOrderLine(Env.getCtx(), orderLine.get_ID(), getTrxName());
assertEquals(1, matchPOs.length, "Unexpected number of MatchPO for order line");
int matchedPOReverse = 200016;
info = new ProcessInfo("MatchPOReverse", matchedPOReverse, MMatchPO.Table_ID, matchPOs[0].get_ID());
ServerProcessCtl.process(info, getTrx(), false);
assertFalse(info.isError(), info.getSummary());
orderLine.load(getTrxName());
assertEquals(2, orderLine.getQtyReserved().intValue(), "Unexpected order line qty ordered value");
assertEquals(0, orderLine.getQtyDelivered().intValue(), "Unexpected order line qty delivered value");
receiptLine.load(getTrxName());
assertEquals(0, receiptLine.getC_OrderLine_ID(), "Unexpected order line ID value for receipt line");
matchPOs = MMatchPO.getOrderLine(Env.getCtx(), orderLine.get_ID(), getTrxName());
assertEquals(2, matchPOs.length, "Unexpected number of MatchPO for order line");
newOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
assertEquals(initialOnOrdered+2, newOnOrdered, "Unexpected qty on ordered value");
newOnHand = MStorageOnHand.getQtyOnHand(product.get_ID(), getM_Warehouse_ID(), 0, getTrxName()).intValue();
assertEquals(initialOnHand+1, newOnHand, "Unexpected qty on hand value");
MiniTableImpl fromTable = new MiniTableImpl();
MiniTableImpl toTable = new MiniTableImpl();
ColumnInfo[] layout = new ColumnInfo[] {
new ColumnInfo(" ", ".", IDColumn.class, false, false, ""),
new ColumnInfo(Msg.translate(Env.getCtx(), "DocumentNo"), ".", String.class), // 1
new ColumnInfo(Msg.translate(Env.getCtx(), "Date"), ".", Timestamp.class),
new ColumnInfo(Msg.translate(Env.getCtx(), "C_BPartner_ID"),".", KeyNamePair.class, "."), // 3
new ColumnInfo(Msg.translate(Env.getCtx(), "Line"), ".", KeyNamePair.class, "."),
new ColumnInfo(Msg.translate(Env.getCtx(), "M_Product_ID"), ".", KeyNamePair.class, "."), // 5
new ColumnInfo(Msg.translate(Env.getCtx(), "Qty"), ".", Double.class),
new ColumnInfo(Msg.translate(Env.getCtx(), "Matched"), ".", Double.class)
};
fromTable.prepareTable(layout, null, null, false, null);
toTable.prepareTable(layout, null, null, false, null);
Match match = new Match();
match.setTrxName(getTrxName());
match.cmd_search(fromTable, Match.MATCH_SHIPMENT, match.getMatchTypeText(Match.MATCH_ORDER), product.get_ID(), bpartner.get_ID(), null, null, false);
assertTrue(fromTable.getRowCount()>0, "Unexpected number of records for not matched Material Receipt: " + fromTable.getRowCount());
int selectedRow = -1;
for(int i = 0; i < fromTable.getRowCount(); i++) {
String docNo = (String)fromTable.getValueAt(i, 1);
if (receipt.getDocumentNo().equals(docNo)) {
int matched = ((Number)fromTable.getValueAt(i, 7)).intValue();
assertEquals(0, matched, "Unexpected matched qty for Material Receipt line");
int qty = ((Number)fromTable.getValueAt(i, 6)).intValue();
assertEquals(receiptLine.getMovementQty().intValue(), qty, "Unexpected qty for Material Receipt line");
selectedRow = i;
break;
}
}
assertTrue(selectedRow >= 0, "Can't find not matched Material Receipt line");
fromTable.setSelectedRow(selectedRow);
match.cmd_searchTo(fromTable, toTable, match.getMatchTypeText(Match.MATCH_ORDER), Match.MATCH_SHIPMENT, true, true, false, false);
assertTrue(toTable.getRowCount()>0, "Unexpected number of records for not matched Order Line: " + fromTable.getRowCount());
int selectedOrderRow = -1;
for(int i = 0; i < toTable.getRowCount(); i++) {
String docNo = (String)toTable.getValueAt(i, 1);
if (order.getDocumentNo().equals(docNo)) {
int matched = ((Number)toTable.getValueAt(i, 7)).intValue();
assertEquals(0, matched, "Unexpected matched qty for PO line");
int qty = ((Number)toTable.getValueAt(i, 6)).intValue();
assertEquals(orderLine.getQtyOrdered().intValue(), qty, "Unexpected qty for PO line");
selectedOrderRow = i;
break;
}
}
assertTrue(selectedOrderRow >= 0, "Can't find not matched PO line");
//create vendor invoice
MInvoice invoice = new MInvoice(order, MDocType.getOfDocBaseType(Env.getCtx(), MDocType.DOCBASETYPE_APInvoice)[0].getC_DocType_ID(), order.getDateAcct());
invoice.setDocStatus(DocAction.STATUS_Drafted);
invoice.setDocAction(DocAction.ACTION_Complete);
invoice.saveEx();
MInvoiceLine invoiceLine = new MInvoiceLine(invoice);
invoiceLine.setOrderLine(orderLine);
invoiceLine.setLine(10);
invoiceLine.setProduct(product);
invoiceLine.setQty(orderQty);
invoiceLine.saveEx();
info = MWorkflow.runDocumentActionWorkflow(invoice, DocAction.ACTION_Complete);
assertFalse(info.isError(), info.getSummary());
invoice.load(getTrxName());
assertEquals(DocAction.STATUS_Completed, invoice.getDocStatus());
orderLine.load(getTrxName());
assertEquals(invoiceLine.getQtyInvoiced().intValue(), orderLine.getQtyInvoiced().intValue(), "Unexpected order line qty invoiced");
fromTable.prepareTable(layout, null, null, false, null);
toTable.prepareTable(layout, null, null, false, null);
match.cmd_search(fromTable, Match.MATCH_SHIPMENT, match.getMatchTypeText(Match.MATCH_ORDER), product.get_ID(), bpartner.get_ID(), null, null, false);
assertTrue(fromTable.getRowCount()>0, "Unexpected number of records for not matched Material Receipt: " + fromTable.getRowCount());
selectedRow = -1;
for(int i = 0; i < fromTable.getRowCount(); i++) {
String docNo = (String)fromTable.getValueAt(i, 1);
if (receipt.getDocumentNo().equals(docNo)) {
int matched = ((Number)fromTable.getValueAt(i, 7)).intValue();
assertEquals(0, matched, "Unexpected matched qty for Material Receipt line");
int qty = ((Number)fromTable.getValueAt(i, 6)).intValue();
assertEquals(receiptLine.getMovementQty().intValue(), qty, "Unexpected qty for Material Receipt line");
selectedRow = i;
break;
}
}
assertTrue(selectedRow >= 0, "Can't find not matched Material Receipt line");
fromTable.setSelectedRow(selectedRow);
match.cmd_searchTo(fromTable, toTable, match.getMatchTypeText(Match.MATCH_ORDER), Match.MATCH_SHIPMENT, true, true, false, false);
assertTrue(toTable.getRowCount()>0, "Unexpected number of records for not matched Order Line: " + fromTable.getRowCount());
selectedOrderRow = -1;
for(int i = 0; i < toTable.getRowCount(); i++) {
String docNo = (String)toTable.getValueAt(i, 1);
if (order.getDocumentNo().equals(docNo)) {
int matched = ((Number)toTable.getValueAt(i, 7)).intValue();
assertEquals(0, matched, "Unexpected matched qty for PO line");
int qty = ((Number)toTable.getValueAt(i, 6)).intValue();
assertEquals(orderLine.getQtyOrdered().intValue(), qty, "Unexpected qty for PO line");
selectedOrderRow = i;
break;
}
}
assertTrue(selectedOrderRow >= 0, "Can't find not matched PO line");
IDColumn idColumn = (IDColumn)toTable.getValueAt(selectedOrderRow, 0);
idColumn.setSelected(true);
match.cmd_process(fromTable, toTable, Match.MODE_NOTMATCHED, Match.MATCH_SHIPMENT, match.getMatchTypeText(Match.MATCH_ORDER), new BigDecimal(1));
orderLine.load(getTrxName());
assertEquals(1, orderLine.getQtyReserved().intValue(), "Unexpected order line qty ordered value");
assertEquals(1, orderLine.getQtyDelivered().intValue(), "Unexpected order line qty delivered value");
receiptLine.load(getTrxName());
assertEquals(orderLine.getC_OrderLine_ID(), receiptLine.getC_OrderLine_ID(), "Unexpected order line ID value for receipt line");
matchPOs = MMatchPO.getOrderLine(Env.getCtx(), orderLine.get_ID(), getTrxName());
assertEquals(4, matchPOs.length, "Unexpected number of MatchPO for order line");
newOnOrdered = MStorageReservation.getQty(product.get_ID(), getM_Warehouse_ID(), 0, false, getTrxName()).intValue();
assertEquals(initialOnOrdered+1, newOnOrdered, "Unexpected qty on ordered value");
newOnHand = MStorageOnHand.getQtyOnHand(product.get_ID(), getM_Warehouse_ID(), 0, getTrxName()).intValue();
assertEquals(initialOnHand+1, newOnHand, "Unexpected qty on hand value");
}
}

View File

@ -0,0 +1,343 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.idempiere.test.ui;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.compiere.minigrid.ColumnInfo;
import org.compiere.minigrid.IDColumn;
import org.compiere.minigrid.IMiniTable;
import org.compiere.model.PO;
import org.compiere.util.KeyNamePair;
/**
*
* @author hengsin
*
*/
public class MiniTableImpl implements IMiniTable {
/** Array of table details. */
private List<TableColumn> m_tableColumns = new ArrayList<TableColumn>();
private List<Map<String, Object>> model = new ArrayList<Map<String,Object>>();
private ColumnInfo[] m_layout;
private int m_keyColumnIndex;
private int m_selectedRow;
public MiniTableImpl() {
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
@Override
public Object getValueAt(int row, int column) {
if (column < m_tableColumns.size()) {
String columnName = m_tableColumns.get(column).getHeaderValue();
if (row < model.size()) {
return model.get(row).get(columnName);
}
}
return null;
}
@Override
public void setValueAt(Object value, int row, int column) {
if (column < m_tableColumns.size()) {
String columnName = m_tableColumns.get(column).getHeaderValue();
if (row < model.size()) {
model.get(row).put(columnName, value);
}
}
}
@Override
public int convertColumnIndexToModel(int viewColumnIndex) {
return viewColumnIndex;
}
@Override
public void setColumnReadOnly(int index, boolean readOnly) {
}
@Override
public String prepareTable(ColumnInfo[] layout, String from, String where, boolean multiSelection,
String tableName) {
m_layout = layout;
m_tableColumns.clear();
model.clear();
for (int columnIndex = 0; columnIndex < layout.length; columnIndex++) {
addColumn(layout[columnIndex].getColHeader(), layout[columnIndex].getColDescription(), layout[columnIndex].getAD_Reference_ID(), layout[columnIndex].getColClass());
if (layout[columnIndex].getColClass() == IDColumn.class)
{
m_keyColumnIndex = columnIndex;
}
}
return null;
}
@Override
public void addColumn(String header) {
addColumn(header, null, 0, null);
}
@Override
public void setColumnClass(int index, Class<?> classType, boolean readOnly, String header) {
if (index < m_tableColumns.size()) {
m_tableColumns.get(index).setColumnClass(classType);
}
}
@Override
public void setColumnClass(int index, Class<?> classType, boolean readOnly) {
if (index < m_tableColumns.size()) {
m_tableColumns.get(index).setColumnClass(classType);
}
}
@Override
public void loadTable(ResultSet rs) {
model.clear();
try
{
while (rs.next())
{
Map<String, Object> row = new HashMap<String, Object>();
int rsColOffset = 1;
for (int col = 0; col < m_layout.length; col++)
{
//reset the data value
Object data = null;
Class<?> columnClass = m_layout[col].getColClass();
int rsColIndex = col + rsColOffset;
if (columnClass == IDColumn.class)
{
data = new IDColumn(rs.getInt(rsColIndex));
}
else if (columnClass == Boolean.class)
{
data = Boolean.valueOf(rs.getString(rsColIndex).equals("Y"));
}
else if (columnClass == Timestamp.class)
{
data = rs.getTimestamp(rsColIndex);
}
else if (columnClass == BigDecimal.class)
{
data = rs.getBigDecimal(rsColIndex);
}
else if (columnClass == Double.class)
{
data = Double.valueOf(rs.getDouble(rsColIndex));
}
else if (columnClass == Integer.class)
{
data = Integer.valueOf(rs.getInt(rsColIndex));
}
else if (columnClass == KeyNamePair.class)
{
String display = rs.getString(rsColIndex);
int key = rs.getInt(rsColIndex + 1);
data = new KeyNamePair(key, display);
rsColOffset++;
}
else
{
String s = rs.getString(rsColIndex);
if (s != null)
{
data = s.trim(); // problems with NCHAR
}
else
{
data=null;
}
}
// store in underlying model
row.put(m_tableColumns.get(col).headerValue, data);
}
model.add(row);
}
}
catch (SQLException exception)
{
throw new RuntimeException(exception);
}
}
@Override
public void loadTable(PO[] pos) {
model.clear();
for (int poIndex = 0; poIndex < pos.length; poIndex++)
{
PO myPO = pos[poIndex];
Map<String, Object> row = new HashMap<String, Object>();
for (int col = 0; col < m_layout.length; col++)
{
String columnName = m_layout[col].getColSQL();
Object data = myPO.get_Value(columnName);
if (data != null)
{
Class<?> columnClass = m_layout[col].getColClass();
if (columnClass == IDColumn.class)
{
data = new IDColumn(((Integer)data).intValue());
}
else if (columnClass == Double.class)
{
data = Double.valueOf(((BigDecimal)data).doubleValue());
}
}
// store
row.put(m_tableColumns.get(col).headerValue, data);
}
model.add(row);
}
}
@Override
public Integer getSelectedRowKey() {
int row = getSelectedRow();
// make common function
return getRowKeyAt (row);
}
/**
* get key of record at index
* @param index
* @return
*/
public Integer getRowKeyAt (int index){
if (index < 0 || m_keyColumnIndex < 0 || index >= model.size())
return null;
Object data = model.get(index).get(m_tableColumns.get(m_keyColumnIndex).headerValue);
if (data instanceof IDColumn)
{
data = ((IDColumn)data).getRecord_ID();
}
if (data instanceof Integer)
{
return (Integer)data;
}
return null;
}
@Override
public int getSelectedRow() {
return m_selectedRow;
}
public void setSelectedRow(int selected) {
if (selected < model.size())
m_selectedRow = selected;
}
@Override
public void setRowCount(int rowCount) {
}
@Override
public ColumnInfo[] getLayoutInfo() {
return m_layout;
}
@Override
public int getColumnCount() {
return m_tableColumns.size();
}
@Override
public int getRowCount() {
return model.size();
}
@Override
public void setMultiSelection(boolean multiSelection) {
}
@Override
public boolean isMultiSelection() {
return false;
}
@Override
public int getColorCode(int row) {
return 0;
}
@Override
public void setColorCompare(Object dataCompare) {
}
@Override
public void repaint() {
}
@Override
public void autoSize() {
}
@Override
public void setShowTotals(boolean show) {
}
/**
* Add Table Column and specify the column header.
*
* @param header name of column header
* @param description
* @param colClass
*/
public void addColumn (String header, String description, int AD_Reference_ID, Class<?> colClass)
{
TableColumn column = new TableColumn();
column.setHeaderValue(header);
column.setTooltipText(description);
column.setAD_Reference_ID(AD_Reference_ID);
column.setColumnClass(colClass);
m_tableColumns.add(column);
} // addColumn
}

View File

@ -0,0 +1,122 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* 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., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.idempiere.test.ui;
/**
*
* @author hengsin
*
*/
public class TableColumn
{
/** The header value of the column. */
protected String headerValue;
protected Class<?> columnClass;
protected String tooltipText;
private int AD_Reference_ID;
/**
* Cover method, using a default width of 75
* @see #WTableColumn(int)
*/
public TableColumn() {
headerValue = null;
}
/**
* Sets the <code>Object</code> whose string representation will be
* used as the value for the <code>headerRenderer</code>. When the
* <code>WTableColumn</code> is created, the default <code>headerValue</code>
* is <code>null</code>.
*
* @param headerValue the new headerValue
* @see #getHeaderValue
*/
public void setHeaderValue(String headerValue)
{
this.headerValue = headerValue;
return;
}
/**
* Returns the <code>Object</code> used as the value for the header
* renderer.
*
* @return the <code>headerValue</code> property
* @see #setHeaderValue
*/
public String getHeaderValue()
{
return headerValue;
}
/**
*
* @return Class
*/
public Class<?> getColumnClass()
{
return columnClass;
}
/**
*
* @param columnClass
*/
public void setColumnClass(Class<?> columnClass)
{
this.columnClass = columnClass;
}
/**
* @return tooltip text
*/
public String getTooltipText() {
return tooltipText;
}
/**
* @param tooltipText
*/
public void setTooltipText(String tooltipText) {
this.tooltipText = tooltipText;
}
public int getAD_Reference_ID() {
return AD_Reference_ID;
}
public void setAD_Reference_ID(int AD_Reference_ID) {
this.AD_Reference_ID=AD_Reference_ID;
}
}