/****************************************************************************** * Product: iDempiere ERP & CRM Smart Business Solution * * Copyright (C) 2019 Elaine Tan * * * * This program is free software; you can redistribute it and/or modify it * * under the terms version 2 of the GNU General Public License as published * * by the Free Software Foundation. This program is distributed in the hope * * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * *****************************************************************************/ package org.compiere.model; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Properties; import org.compiere.process.DocAction; import org.compiere.util.Env; /** * Helper methods for auto matching of M_MatchPO records. * @author hengsin * */ public class MatchPOAutoMatch { /** * @param ctx * @param C_OrderLine_ID * @param trxName * @return not fully matched matchpo records */ public static List getNotMatchedMatchPOList(Properties ctx, int C_OrderLine_ID, String trxName) { List notMatchedMatchPOList = new ArrayList(); List creditMemoMatchPOList = new ArrayList(); List notMatchedCreditMemoMatchPOList = new ArrayList(); MMatchPO[] mpos = MMatchPO.getOrderLine(ctx, C_OrderLine_ID, trxName); for (MMatchPO mpo : mpos) { if (mpo.getReversal_ID() == 0 && mpo.getRef_MatchPO_ID() == 0) { if (mpo.getQty().signum() < 0) { if (mpo.getC_InvoiceLine_ID() > 0 && mpo.getM_InOutLine_ID() == 0) { String docStatus = mpo.getC_InvoiceLine().getC_Invoice().getDocStatus(); if (docStatus.equals(DocAction.STATUS_Completed) || docStatus.equals(DocAction.STATUS_Closed)) { creditMemoMatchPOList.add(mpo); continue; } } } notMatchedMatchPOList.add(mpo); } } if (!notMatchedMatchPOList.isEmpty()) { Collections.sort(notMatchedMatchPOList, new Comparator() { @Override public int compare(MMatchPO arg0, MMatchPO arg1) { return arg0.getM_MatchPO_ID() > arg1.getM_MatchPO_ID() ? 1 : (arg0.getM_MatchPO_ID()==arg1.getM_MatchPO_ID() ? 0 : -1); } }); } if (!creditMemoMatchPOList.isEmpty()) { BigDecimal totalNotMatchingCreditMemoQty = Env.ZERO; for (MMatchPO matchPOCreditMemo : creditMemoMatchPOList) { boolean found = false; int Ref_InvoiceLine_ID = matchPOCreditMemo.getC_InvoiceLine().getRef_InvoiceLine_ID(); if (Ref_InvoiceLine_ID > 0) { for (MMatchPO matchPO : notMatchedMatchPOList) { if (!matchPO.isPosted() && matchPO.getC_InvoiceLine_ID() == Ref_InvoiceLine_ID && matchPO.getM_InOutLine_ID() == 0) { if (matchPO.getQty().compareTo(matchPOCreditMemo.getQty().negate()) == 0) { notMatchedMatchPOList.remove(matchPO); found = true; break; } } } } if (found) continue; for (MMatchPO matchPO : notMatchedMatchPOList) { if (!matchPO.isPosted() && matchPO.getC_InvoiceLine_ID() > 0 && matchPO.getM_InOutLine_ID() == 0) { if (matchPO.getQty().compareTo(matchPOCreditMemo.getQty().negate()) == 0) { notMatchedMatchPOList.remove(matchPO); found = true; break; } } } if (!found) { totalNotMatchingCreditMemoQty = totalNotMatchingCreditMemoQty.add(matchPOCreditMemo.getQty().negate()); notMatchedCreditMemoMatchPOList.add(matchPOCreditMemo); } } if (totalNotMatchingCreditMemoQty.signum() != 0) { BigDecimal totalInvoiceQty = Env.ZERO; for (MMatchPO matchPO : notMatchedMatchPOList) { if (!matchPO.isPosted() && matchPO.getC_InvoiceLine_ID() > 0 && matchPO.getM_InOutLine_ID() == 0) totalInvoiceQty = totalInvoiceQty.add(matchPO.getQty()); } if (totalNotMatchingCreditMemoQty.compareTo(totalInvoiceQty) == 0) notMatchedMatchPOList.clear(); } } return notMatchedMatchPOList; } /** * auto match matchpo (between match po created by vendor credit memo and match po created by vendor invoice) * @param ctx * @param C_OrderLine_ID * @param currentPO * @param trxName */ public static void match(Properties ctx, int C_OrderLine_ID, MMatchPO currentPO, String trxName) { List notMatchedMatchPOList = new ArrayList(); List creditMemoMatchPOList = new ArrayList(); List matchedMatchPOList = new ArrayList(); //find not matched POs MMatchPO[] mpos = MMatchPO.getOrderLine(ctx, C_OrderLine_ID, trxName); for (MMatchPO mpo : mpos) { if (mpo.getReversal_ID() == 0 && mpo.getRef_MatchPO_ID() == 0) { if (mpo.getQty().signum() < 0) { if (mpo.getC_InvoiceLine_ID() > 0 && mpo.getM_InOutLine_ID() == 0) { String docStatus = mpo.getC_InvoiceLine().getC_Invoice().getDocStatus(); if ((currentPO != null && mpo.getM_MatchPO_ID() == currentPO.getM_MatchPO_ID()) || docStatus.equals(DocAction.STATUS_Completed) || docStatus.equals(DocAction.STATUS_Closed)) { creditMemoMatchPOList.add(mpo); } } continue; } notMatchedMatchPOList.add(mpo); } } if (!notMatchedMatchPOList.isEmpty()) { Collections.sort(notMatchedMatchPOList, new Comparator() { @Override public int compare(MMatchPO arg0, MMatchPO arg1) { return arg0.getM_MatchPO_ID() > arg1.getM_MatchPO_ID() ? 1 : (arg0.getM_MatchPO_ID()==arg1.getM_MatchPO_ID() ? 0 : -1); } }); } if (!creditMemoMatchPOList.isEmpty()) { for (MMatchPO matchPOCreditMemo : creditMemoMatchPOList) { BigDecimal creditMemoQty = matchPOCreditMemo.getQty().negate(); int Ref_InvoiceLine_ID = matchPOCreditMemo.getC_InvoiceLine().getRef_InvoiceLine_ID(); if (Ref_InvoiceLine_ID > 0) { for (MMatchPO matchPO : notMatchedMatchPOList) { if (!matchPO.isPosted() && matchPO.getC_InvoiceLine_ID() == Ref_InvoiceLine_ID && matchPO.getM_InOutLine_ID() == 0) { if (matchPO.getQty().compareTo(creditMemoQty) > 0) { matchPO.setQty(matchPO.getQty().subtract(creditMemoQty)); matchPO.saveEx(trxName); MInvoiceLine iLine = new MInvoiceLine(ctx, matchPO.getC_InvoiceLine_ID(), trxName); MMatchPO po = new MMatchPO(iLine, iLine.getC_Invoice().getDateInvoiced(), creditMemoQty); po.setC_OrderLine_ID(C_OrderLine_ID); po.setRef_MatchPO_ID(matchPOCreditMemo.getM_MatchPO_ID()); po.setPosted(true); po.saveEx(trxName); matchPOCreditMemo.setRef_MatchPO_ID(po.getM_MatchPO_ID()); matchPOCreditMemo.setPosted(true); matchPOCreditMemo.saveEx(trxName); MMatchInv matchInv1 = MMatchPO.createMatchInv(po, po.getC_InvoiceLine_ID(), po.getM_InOutLine_ID(), po.getQty(), po.getDateTrx(), trxName); MMatchInv matchInv2 = MMatchPO.createMatchInv(matchPOCreditMemo, matchPOCreditMemo.getC_InvoiceLine_ID(), matchPOCreditMemo.getM_InOutLine_ID(), matchPOCreditMemo.getQty(), matchPOCreditMemo.getDateTrx(), trxName); if (matchInv1 != null && matchInv2 != null) { matchInv1.setRef_MatchInv_ID(matchInv2.getM_MatchInv_ID()); matchInv1.saveEx(trxName); matchInv2.setRef_MatchInv_ID(matchInv1.getM_MatchInv_ID()); matchInv2.saveEx(trxName); } else break; matchedMatchPOList.add(po); creditMemoQty = creditMemoQty.subtract(po.getQty()); } else if (matchPO.getQty().compareTo(creditMemoQty) == 0) { matchPO.setRef_MatchPO_ID(matchPOCreditMemo.getM_MatchPO_ID()); matchPO.setPosted(true); matchPO.saveEx(trxName); matchPOCreditMemo.setRef_MatchPO_ID(matchPO.getM_MatchPO_ID()); matchPOCreditMemo.setPosted(true); matchPOCreditMemo.saveEx(trxName); MMatchInv matchInv1 = MMatchPO.createMatchInv(matchPO, matchPO.getC_InvoiceLine_ID(), matchPO.getM_InOutLine_ID(), matchPO.getQty(), matchPO.getDateTrx(), trxName); MMatchInv matchInv2 = MMatchPO.createMatchInv(matchPOCreditMemo, matchPOCreditMemo.getC_InvoiceLine_ID(), matchPOCreditMemo.getM_InOutLine_ID(), matchPOCreditMemo.getQty(), matchPOCreditMemo.getDateTrx(), trxName); if (matchInv1 != null && matchInv2 != null) { matchInv1.setRef_MatchInv_ID(matchInv2.getM_MatchInv_ID()); matchInv1.saveEx(trxName); matchInv2.setRef_MatchInv_ID(matchInv1.getM_MatchInv_ID()); matchInv2.saveEx(trxName); } else break; matchedMatchPOList.add(matchPO); creditMemoQty = creditMemoQty.subtract(matchPO.getQty()); } else if (matchPO.getQty().compareTo(creditMemoQty) < 0) { matchPOCreditMemo.setQty(matchPOCreditMemo.getQty().add(matchPO.getQty())); matchPOCreditMemo.saveEx(trxName); MInvoiceLine iLine = new MInvoiceLine(ctx, matchPOCreditMemo.getC_InvoiceLine_ID(), trxName); MMatchPO po = new MMatchPO(iLine, iLine.getC_Invoice().getDateInvoiced(), matchPO.getQty().negate()); po.setC_OrderLine_ID(C_OrderLine_ID); po.setRef_MatchPO_ID(matchPO.getM_MatchPO_ID()); po.setPosted(true); po.saveEx(trxName); matchPO.setRef_MatchPO_ID(po.getM_MatchPO_ID()); matchPO.setPosted(true); matchPO.saveEx(trxName); MMatchInv matchInv1 = MMatchPO.createMatchInv(po, po.getC_InvoiceLine_ID(), po.getM_InOutLine_ID(), po.getQty(), po.getDateTrx(), trxName); MMatchInv matchInv2 = MMatchPO.createMatchInv(matchPO, matchPO.getC_InvoiceLine_ID(), matchPO.getM_InOutLine_ID(), matchPO.getQty(), matchPO.getDateTrx(), trxName); if (matchInv1 != null && matchInv2 != null) { matchInv1.setRef_MatchInv_ID(matchInv2.getM_MatchInv_ID()); matchInv1.saveEx(trxName); matchInv2.setRef_MatchInv_ID(matchInv1.getM_MatchInv_ID()); matchInv2.saveEx(trxName); } else break; matchedMatchPOList.add(matchPO); creditMemoQty = creditMemoQty.subtract(matchPO.getQty()); } if (creditMemoQty.signum() == 0) break; } } for (MMatchPO matchedMatchPO : matchedMatchPOList) notMatchedMatchPOList.remove(matchedMatchPO); } if (creditMemoQty.signum() == 0) continue; for (MMatchPO matchPO : notMatchedMatchPOList) { if (!matchPO.isPosted() && matchPO.getC_InvoiceLine_ID() > 0 && matchPO.getM_InOutLine_ID() == 0) { if (matchPO.getQty().compareTo(creditMemoQty) > 0) { matchPO.setQty(matchPO.getQty().subtract(creditMemoQty)); matchPO.saveEx(trxName); MInvoiceLine iLine = new MInvoiceLine(ctx, matchPO.getC_InvoiceLine_ID(), trxName); MMatchPO po = new MMatchPO(iLine, iLine.getC_Invoice().getDateInvoiced(), creditMemoQty); po.setC_OrderLine_ID(C_OrderLine_ID); po.setRef_MatchPO_ID(matchPOCreditMemo.getM_MatchPO_ID()); po.setPosted(true); po.saveEx(trxName); matchPOCreditMemo.setRef_MatchPO_ID(po.getM_MatchPO_ID()); matchPOCreditMemo.setPosted(true); matchPOCreditMemo.saveEx(trxName); MMatchInv matchInv1 = MMatchPO.createMatchInv(po, po.getC_InvoiceLine_ID(), po.getM_InOutLine_ID(), po.getQty(), po.getDateTrx(), trxName); MMatchInv matchInv2 = MMatchPO.createMatchInv(matchPOCreditMemo, matchPOCreditMemo.getC_InvoiceLine_ID(), matchPOCreditMemo.getM_InOutLine_ID(), matchPOCreditMemo.getQty(), matchPOCreditMemo.getDateTrx(), trxName); if (matchInv1 != null && matchInv2 != null) { matchInv1.setRef_MatchInv_ID(matchInv2.getM_MatchInv_ID()); matchInv1.saveEx(trxName); matchInv2.setRef_MatchInv_ID(matchInv1.getM_MatchInv_ID()); matchInv2.saveEx(trxName); } else break; matchedMatchPOList.add(po); creditMemoQty = creditMemoQty.subtract(po.getQty()); } else if (matchPO.getQty().compareTo(creditMemoQty) == 0) { matchPO.setRef_MatchPO_ID(matchPOCreditMemo.getM_MatchPO_ID()); matchPO.setPosted(true); matchPO.saveEx(trxName); matchPOCreditMemo.setRef_MatchPO_ID(matchPO.getM_MatchPO_ID()); matchPOCreditMemo.setPosted(true); matchPOCreditMemo.saveEx(trxName); MMatchInv matchInv1 = MMatchPO.createMatchInv(matchPO, matchPO.getC_InvoiceLine_ID(), matchPO.getM_InOutLine_ID(), matchPO.getQty(), matchPO.getDateTrx(), trxName); MMatchInv matchInv2 = MMatchPO.createMatchInv(matchPOCreditMemo, matchPOCreditMemo.getC_InvoiceLine_ID(), matchPOCreditMemo.getM_InOutLine_ID(), matchPOCreditMemo.getQty(), matchPOCreditMemo.getDateTrx(), trxName); if (matchInv1 != null && matchInv2 != null) { matchInv1.setRef_MatchInv_ID(matchInv2.getM_MatchInv_ID()); matchInv1.saveEx(trxName); matchInv2.setRef_MatchInv_ID(matchInv1.getM_MatchInv_ID()); matchInv2.saveEx(trxName); } else break; matchedMatchPOList.add(matchPO); creditMemoQty = creditMemoQty.subtract(matchPO.getQty()); } else if (matchPO.getQty().compareTo(creditMemoQty) < 0) { matchPOCreditMemo.setQty(matchPOCreditMemo.getQty().add(matchPO.getQty())); matchPOCreditMemo.saveEx(trxName); MInvoiceLine iLine = new MInvoiceLine(ctx, matchPOCreditMemo.getC_InvoiceLine_ID(), trxName); MMatchPO po = new MMatchPO(iLine, iLine.getC_Invoice().getDateInvoiced(), matchPO.getQty().negate()); po.setC_OrderLine_ID(C_OrderLine_ID); po.setRef_MatchPO_ID(matchPO.getM_MatchPO_ID()); po.setPosted(true); po.saveEx(trxName); matchPO.setRef_MatchPO_ID(po.getM_MatchPO_ID()); matchPO.setPosted(true); matchPO.saveEx(trxName); MMatchInv matchInv1 = MMatchPO.createMatchInv(po, po.getC_InvoiceLine_ID(), po.getM_InOutLine_ID(), po.getQty(), po.getDateTrx(), trxName); MMatchInv matchInv2 = MMatchPO.createMatchInv(matchPO, matchPO.getC_InvoiceLine_ID(), matchPO.getM_InOutLine_ID(), matchPO.getQty(), matchPO.getDateTrx(), trxName); if (matchInv1 != null && matchInv2 != null) { matchInv1.setRef_MatchInv_ID(matchInv2.getM_MatchInv_ID()); matchInv1.saveEx(trxName); matchInv2.setRef_MatchInv_ID(matchInv1.getM_MatchInv_ID()); matchInv2.saveEx(trxName); } else break; matchedMatchPOList.add(matchPO); creditMemoQty = creditMemoQty.subtract(matchPO.getQty()); } if (creditMemoQty.signum() == 0) break; } } for (MMatchPO matchedMatchPO : matchedMatchPOList) notMatchedMatchPOList.remove(matchedMatchPO); } } if (currentPO != null) { for (MMatchPO matchPOCreditMemo : creditMemoMatchPOList) { if (matchPOCreditMemo.getM_MatchPO_ID() == currentPO.getM_MatchPO_ID()) { if (matchPOCreditMemo.getReversal_ID() == 0 && matchPOCreditMemo.getRef_MatchPO_ID() == 0) throw new RuntimeException("Failed to find the corresponding invoice matched po"); } } } } /** * remove matchpo and matchinv records between vendor invoice and vendor cm * @param ctx * @param C_Invoice_ID * @param trxName */ public static void unmatch(Properties ctx, int C_Invoice_ID, String trxName) { MMatchInv[] minvs = MMatchInv.getInvoice(ctx, C_Invoice_ID, trxName); for(MMatchInv minv : minvs) { if (minv.getReversal_ID() > 0 || minv.getRef_MatchInv_ID() == 0) continue; MMatchInv refMatchInv = new MMatchInv(ctx, minv.getRef_MatchInv_ID(), trxName); refMatchInv.deleteEx(true); minv.deleteEx(true); } List unmatchedOrderLineID = new ArrayList(); MMatchPO[] mpos = MMatchPO.getInvoice(ctx, C_Invoice_ID, trxName); for (MMatchPO mpo : mpos) { if (mpo.getReversal_ID() > 0 || mpo.getRef_MatchPO_ID() == 0) continue; int C_OrderLine_ID = mpo.getC_OrderLine_ID(); if (!unmatchedOrderLineID.contains(C_OrderLine_ID)) { unmatchedOrderLineID.add(C_OrderLine_ID); MMatchPO[] pos = MMatchPO.getOrderLine(ctx, C_OrderLine_ID, trxName); for (MMatchPO po : pos) { if (po.getReversal_ID() == 0 && po.getC_InvoiceLine_ID() > 0 && po.getRef_MatchPO_ID() > 0) { if (po.getC_InvoiceLine().getC_Invoice_ID() == C_Invoice_ID) { po.setRef_MatchPO_ID(0); po.setPosted(false); po.saveEx(trxName); } else if (po.getRef_MatchPO().getC_InvoiceLine().getC_Invoice_ID() == C_Invoice_ID) { po.setRef_MatchPO_ID(0); po.setPosted(false); po.saveEx(trxName); } } } } } } }