IDEMPIERE-5327 Refactoring and clean up of BOM Drop form (#1381)
* IDEMPIERE-5327 Refactoring and clean up of BOM Drop form * IDEMPIERE-5327 Refactoring and clean up of BOM Drop form - Fix scrolling of center content. * IDEMPIERE-5327 Refactoring and clean up of BOM Drop form - add Feature group support
This commit is contained in:
parent
2ba935a393
commit
d4801e3cb2
File diff suppressed because it is too large
Load Diff
|
@ -18,7 +18,6 @@ import java.text.SimpleDateFormat;
|
|||
|
||||
import org.adempiere.webui.ISupportMask;
|
||||
import org.adempiere.webui.LayoutUtils;
|
||||
import org.adempiere.webui.apps.AEnv;
|
||||
import org.adempiere.webui.event.DialogEvents;
|
||||
import org.adempiere.webui.factory.ButtonFactory;
|
||||
import org.adempiere.webui.util.ZKUpdateUtil;
|
||||
|
@ -58,6 +57,8 @@ public class ProcessInfoDialog extends Window implements EventListener<Event> {
|
|||
public static final String INFORMATION = "~./zul/img/msgbox/info-btn.png";
|
||||
public static final String ERROR = "~./zul/img/msgbox/info-btn.png";
|
||||
|
||||
private boolean isAutoCloseAfterZoom = false;
|
||||
|
||||
/**
|
||||
* @deprecated Should use {@link #ProcessInfoDialog(String, String, ProcessInfo, boolean)} for flexible show message
|
||||
* @param title
|
||||
|
@ -71,11 +72,22 @@ public class ProcessInfoDialog extends Window implements EventListener<Event> {
|
|||
|
||||
/**
|
||||
* show result after run a process
|
||||
* @param title
|
||||
* @param header
|
||||
* @deprecated
|
||||
* @param title ignore
|
||||
* @param header ignore
|
||||
* @param pi
|
||||
* @param needFillLogFromDb
|
||||
*/
|
||||
public ProcessInfoDialog(String title, String header, ProcessInfo pi, boolean needFillLogFromDb) {
|
||||
this(pi, needFillLogFromDb);
|
||||
}
|
||||
|
||||
/**
|
||||
* show result after run a process
|
||||
* @param pi
|
||||
* @param needFillLogFromDb
|
||||
*/
|
||||
public ProcessInfoDialog(ProcessInfo pi, boolean needFillLogFromDb) {
|
||||
if (needFillLogFromDb)
|
||||
ProcessInfoUtil.setLogFromDB(pi);
|
||||
init(pi.getTitle(), null, pi, null);
|
||||
|
@ -180,7 +192,11 @@ public class ProcessInfoDialog extends Window implements EventListener<Event> {
|
|||
|
||||
if (log.getAD_Table_ID() > 0
|
||||
&& log.getRecord_ID() > 0) {
|
||||
DocumentLink recordLink = new DocumentLink(sb.toString(), log.getAD_Table_ID(), log.getRecord_ID());
|
||||
DocumentLink recordLink = new DocumentLink(sb.toString(), log.getAD_Table_ID(), log.getRecord_ID());
|
||||
recordLink.addEventListener(Events.ON_CLICK, e -> {
|
||||
if (isAutoCloseAfterZoom())
|
||||
this.detach();
|
||||
});
|
||||
|
||||
pnlMessage.appendChild(recordLink);
|
||||
} else {
|
||||
|
@ -207,6 +223,22 @@ public class ProcessInfoDialog extends Window implements EventListener<Event> {
|
|||
btnOk.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* enable/disable auto close of dialog after zoom using document link
|
||||
* @param autoClose
|
||||
*/
|
||||
public void setAutoCloseAfterZoom(boolean autoClose) {
|
||||
isAutoCloseAfterZoom = autoClose;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return auto close after zoom state
|
||||
*/
|
||||
public boolean isAutoCloseAfterZoom() {
|
||||
return isAutoCloseAfterZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* after run a process, call this function to show result in a dialog
|
||||
* @param pi
|
||||
|
@ -216,7 +248,7 @@ public class ProcessInfoDialog extends Window implements EventListener<Event> {
|
|||
* just pass false, other pass true to avoid duplicate message
|
||||
*/
|
||||
public static ProcessInfoDialog showProcessInfo (ProcessInfo pi, int windowNo, final Component comp, boolean needFillLogFromDb) {
|
||||
ProcessInfoDialog dialog = new ProcessInfoDialog(AEnv.getDialogHeader(Env.getCtx(), windowNo),AEnv.getDialogHeader(Env.getCtx(), windowNo), pi, needFillLogFromDb);
|
||||
ProcessInfoDialog dialog = new ProcessInfoDialog(pi, needFillLogFromDb);
|
||||
final ISupportMask supportMask = LayoutUtils.showWindowWithMask(dialog, comp, LayoutUtils.OVERLAP_PARENT);
|
||||
dialog.addEventListener(DialogEvents.ON_WINDOW_CLOSE, new EventListener<Event>() {
|
||||
@Override
|
||||
|
|
|
@ -11,12 +11,13 @@ Require-Bundle: org.adempiere.base;bundle-version="0.0.0",
|
|||
Export-Package: org.compiere.apps,
|
||||
org.compiere.apps.form,
|
||||
org.compiere.apps.wf,
|
||||
org.compiere.css,
|
||||
org.compiere.grid,
|
||||
org.compiere.grid.ed,
|
||||
org.compiere.css,
|
||||
org.compiere.install,
|
||||
org.compiere.minigrid,
|
||||
org.compiere.print,
|
||||
org.idempiere.apps.form,
|
||||
org.netbeans.api.visual.action,
|
||||
org.netbeans.api.visual.anchor,
|
||||
org.netbeans.api.visual.border,
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
/***********************************************************************
|
||||
* 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.apps.form;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.adempiere.exceptions.AdempiereException;
|
||||
import org.adempiere.util.Callback;
|
||||
import org.compiere.model.MClient;
|
||||
import org.compiere.model.MInvoice;
|
||||
import org.compiere.model.MInvoiceLine;
|
||||
import org.compiere.model.MOrder;
|
||||
import org.compiere.model.MOrderLine;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.MProject;
|
||||
import org.compiere.model.MProjectLine;
|
||||
import org.compiere.model.MRole;
|
||||
import org.compiere.util.DB;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.KeyNamePair;
|
||||
import org.eevolution.model.MPPProductBOM;
|
||||
import org.eevolution.model.MPPProductBOMLine;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
public class BOMDrop {
|
||||
|
||||
/**
|
||||
* default constructor
|
||||
*/
|
||||
public BOMDrop() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param product
|
||||
* @return
|
||||
*/
|
||||
public int getMaxBOMDeep(MProduct product) {
|
||||
return getBOMDeep(product, 0);
|
||||
}
|
||||
|
||||
private int getBOMDeep (MProduct product, int curentBomDeep) {
|
||||
int bomDeep = curentBomDeep;
|
||||
if (product.isBOM()) {
|
||||
MPPProductBOM bom = MPPProductBOM.getDefault(product, (String)null);
|
||||
if (bom != null) {
|
||||
for (MPPProductBOMLine bomLine : bom.getLines()) {
|
||||
int testBomDeep = getBOMDeep(bomLine.getProduct(), curentBomDeep + 1);
|
||||
if (testBomDeep > bomDeep) {
|
||||
bomDeep = testBomDeep;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bomDeep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Array of draft orders (C_Order_ID, DocumentNo_GrandTotal)
|
||||
* @param trxName optional trx name
|
||||
* @return array of draft orders
|
||||
*/
|
||||
public KeyNamePair[] getDraftOrders(String trxName) {
|
||||
String sql = "SELECT C_Order_ID, DocumentNo || '_' || GrandTotal "
|
||||
+ "FROM C_Order "
|
||||
+ "WHERE Processed='N' AND DocStatus='DR' "
|
||||
+ "ORDER BY DocumentNo";
|
||||
|
||||
return DB.getKeyNamePairs(trxName, MRole.getDefault().addAccessSQL(
|
||||
sql, "C_Order", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Array of open non service Projects (C_Project_ID, Name)
|
||||
* @param trxName optional trx name
|
||||
* @return array of non service projects
|
||||
*/
|
||||
public KeyNamePair[] getNonServiceProjects(String trxName) {
|
||||
String sql = "SELECT C_Project_ID, Name "
|
||||
+ "FROM C_Project "
|
||||
+ "WHERE Processed='N' AND IsSummary='N' AND IsActive='Y'"
|
||||
+ " AND ProjectCategory<>'S' "
|
||||
+ "ORDER BY Name";
|
||||
|
||||
return DB.getKeyNamePairs(trxName, MRole.getDefault().addAccessSQL(
|
||||
sql, "C_Project", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Array of draft Invoices (C_Invoice_ID, DocumentNo_GrandTotal)
|
||||
* @param trxName optional trx name
|
||||
* @return array of draft invoices
|
||||
*/
|
||||
public KeyNamePair[] getDraftInvoices(String trxName) {
|
||||
String sql = "SELECT C_Invoice_ID, DocumentNo || '_' || GrandTotal "
|
||||
+ "FROM C_Invoice "
|
||||
+ "WHERE Processed='N' AND DocStatus='DR' "
|
||||
+ "ORDER BY DocumentNo";
|
||||
|
||||
return DB.getKeyNamePairs(trxName, MRole.getDefault().addAccessSQL(
|
||||
sql, "C_Invoice", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get BOM lines from default BOM of product, sorted by component type and line number
|
||||
*
|
||||
* @param product
|
||||
* @return MPPProductBOMLine[]
|
||||
*/
|
||||
public MPPProductBOMLine[] getBOMLine(MProduct product) {
|
||||
MPPProductBOM bom = MPPProductBOM.getDefault(product, (String)null);
|
||||
MPPProductBOMLine[] bomLines = bom.getLines();
|
||||
//sort by feature
|
||||
Arrays.sort(bomLines, new Comparator<MPPProductBOMLine>() {
|
||||
@Override
|
||||
public int compare(MPPProductBOMLine arg0, MPPProductBOMLine arg1) {
|
||||
String feature1 = arg0.getFeature() != null ? arg0.getFeature() : "";
|
||||
String feature2 = arg1.getFeature() != null ? arg1.getFeature() : "";
|
||||
return feature1.compareTo(feature2);
|
||||
}
|
||||
});
|
||||
|
||||
//sort by component type
|
||||
Arrays.sort(bomLines, new Comparator<MPPProductBOMLine>() {
|
||||
@Override
|
||||
public int compare(MPPProductBOMLine arg0, MPPProductBOMLine arg1) {
|
||||
return arg0.getComponentType().compareTo(arg1.getComponentType());
|
||||
}
|
||||
});
|
||||
|
||||
// 2nd sort by Line Number in order to correspond with BOM Structure, patch 2015-03-31
|
||||
Arrays.sort(bomLines, new Comparator<MPPProductBOMLine>() {
|
||||
@Override
|
||||
public int compare(MPPProductBOMLine arg0, MPPProductBOMLine arg1) {
|
||||
String t1 = String.valueOf(arg0.getLine()+100000);
|
||||
String t2 = String.valueOf(arg1.getLine()+100000);
|
||||
return t1.compareTo(t2);
|
||||
}
|
||||
});
|
||||
|
||||
return bomLines;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param C_Order_ID
|
||||
* @param selectedItems
|
||||
* @param trxName
|
||||
* @return {@link MOrder}
|
||||
*/
|
||||
public MOrder saveOrderLines(int C_Order_ID, List<SelectedItem> selectedItems, String trxName) {
|
||||
MOrder order = new MOrder (Env.getCtx(), C_Order_ID, trxName);
|
||||
|
||||
if (order.get_ID() == 0) {
|
||||
throw new AdempiereException("Not found - C_Order_ID=" + C_Order_ID);
|
||||
}
|
||||
|
||||
for (SelectedItem selectedItem : selectedItems) {
|
||||
addOrderLine(order, selectedItem.M_Product_ID, selectedItem.qty);
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param order
|
||||
* @param M_Product_ID
|
||||
* @param qty
|
||||
* @return {@link MOrderLine}
|
||||
*/
|
||||
private MOrderLine addOrderLine(MOrder order, int M_Product_ID, BigDecimal qty) {
|
||||
// Create Line
|
||||
MOrderLine ol = new MOrderLine(order);
|
||||
ol.setM_Product_ID(M_Product_ID, true);
|
||||
ol.setQty(qty);
|
||||
ol.setPrice();
|
||||
ol.setTax();
|
||||
ol.saveEx();
|
||||
|
||||
return ol;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param C_Invoice_ID
|
||||
* @param selectedItems
|
||||
* @param trxName
|
||||
* @return {@link MInvoice}
|
||||
*/
|
||||
public MInvoice saveInvoiceLines(int C_Invoice_ID, List<SelectedItem> selectedItems, String trxName) {
|
||||
MInvoice invoice = new MInvoice (Env.getCtx(), C_Invoice_ID, trxName);
|
||||
if (invoice.get_ID() == 0) {
|
||||
throw new AdempiereException("Not found - C_Invoice_ID=" + C_Invoice_ID);
|
||||
}
|
||||
|
||||
for(SelectedItem selectedItem : selectedItems) {
|
||||
addInvoiceLine(invoice, selectedItem.M_Product_ID, selectedItem.qty);
|
||||
}
|
||||
return invoice;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param invoice
|
||||
* @param M_Product_ID
|
||||
* @param qty
|
||||
* @return {@link MInvoiceLine}
|
||||
*/
|
||||
private MInvoiceLine addInvoiceLine(MInvoice invoice, int M_Product_ID, BigDecimal qty) {
|
||||
MInvoiceLine il = new MInvoiceLine (invoice);
|
||||
il.setM_Product_ID(M_Product_ID, true);
|
||||
il.setQty(qty);
|
||||
il.setPrice();
|
||||
il.setTax();
|
||||
il.saveEx();
|
||||
return il;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param C_Project_ID
|
||||
* @param selectedItems
|
||||
* @param trxName
|
||||
* @return {@link MProject}
|
||||
*/
|
||||
public MProject saveProjectLines(int C_Project_ID, List<SelectedItem> selectedItems, String trxName) {
|
||||
MProject project = new MProject (Env.getCtx(), C_Project_ID, trxName);
|
||||
if (project.get_ID() == 0) {
|
||||
throw new AdempiereException("Not found - C_Project_ID=" + C_Project_ID);
|
||||
}
|
||||
|
||||
for(SelectedItem selectedItem : selectedItems) {
|
||||
addProjectLine(project, selectedItem.M_Product_ID, selectedItem.qty);
|
||||
}
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param project
|
||||
* @param M_Product_ID
|
||||
* @param qty
|
||||
* @return {@link MProjectLine}
|
||||
*/
|
||||
private MProjectLine addProjectLine(MProject project, int M_Product_ID, BigDecimal qty) {
|
||||
MProjectLine pl = new MProjectLine (project);
|
||||
pl.setM_Product_ID(M_Product_ID);
|
||||
pl.setPlannedQty(qty);
|
||||
pl.saveEx();
|
||||
|
||||
return pl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param product
|
||||
* @param qty
|
||||
* @param callback
|
||||
*/
|
||||
public void addBOMLines(MProduct product, BigDecimal qty, Callback<BOMLine> callback) {
|
||||
addBOMLines(product, qty, callback, 0);
|
||||
}
|
||||
|
||||
private void addBOMLines(MProduct product, BigDecimal qty, Callback<BOMLine> callback, int bomLevel) {
|
||||
MPPProductBOMLine[] bomLines = getBOMLine(product);
|
||||
for (int i = 0; i < bomLines.length; i++)
|
||||
addBOMLines(bomLines[i], qty, callback, bomLevel);
|
||||
}
|
||||
|
||||
private void addBOMLines(MPPProductBOMLine productBOMLine, BigDecimal qty, Callback<BOMLine> callback, int bomLevel) {
|
||||
MProduct product = productBOMLine.getProduct();
|
||||
|
||||
if (product == null)
|
||||
return;
|
||||
|
||||
BOMLine bomLine = new BOMLine(productBOMLine, qty, bomLevel);
|
||||
callback.onCallback(bomLine);
|
||||
|
||||
if (product.isBOM() && product.isVerified()) {
|
||||
addBOMLines(product, bomLine.getLineQty(), callback, bomLevel + 1); // recursive
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* selected bom item
|
||||
*/
|
||||
public static class SelectedItem {
|
||||
private int M_Product_ID;
|
||||
private BigDecimal qty;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param M_Product_ID
|
||||
* @param qty
|
||||
*/
|
||||
public SelectedItem(int M_Product_ID, BigDecimal qty) {
|
||||
this.M_Product_ID = M_Product_ID;
|
||||
this.qty = qty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bom line
|
||||
*/
|
||||
public static class BOMLine {
|
||||
private MPPProductBOMLine bomLine;
|
||||
private BigDecimal lineQty;
|
||||
private String bomType;
|
||||
private int bomLevel;
|
||||
private String feature;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bomLine
|
||||
* @param qty
|
||||
* @param level
|
||||
*/
|
||||
public BOMLine(MPPProductBOMLine bomLine, BigDecimal qty, int level) {
|
||||
this.bomLine = bomLine;
|
||||
lineQty = bomLine.getQtyBOM().multiply(qty).setScale(MClient.get(Env.getCtx()).getAcctSchema().getStdPrecision(), RoundingMode.HALF_UP);
|
||||
bomType = bomLine.getComponentType();
|
||||
if (bomType == null)
|
||||
bomType = MPPProductBOMLine.COMPONENTTYPE_Component;
|
||||
bomLevel = level;
|
||||
feature = bomLine.getFeature();
|
||||
if (feature == null)
|
||||
feature = "";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {@link MPPProductBOMLine}
|
||||
*/
|
||||
public MPPProductBOMLine getProductBOMLine() {
|
||||
return bomLine;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return qty require
|
||||
*/
|
||||
public BigDecimal getLineQty() {
|
||||
return lineQty;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return type of bom (component, variant, phantom, etc)
|
||||
*/
|
||||
public String getBOMType() {
|
||||
return bomType;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return bom level (start from 0)
|
||||
*/
|
||||
public int getBOMLevel() {
|
||||
return bomLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return feature of bom line
|
||||
*/
|
||||
public String getFeature() {
|
||||
return feature;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return parent product
|
||||
*/
|
||||
public MProduct getParentProduct() {
|
||||
MPPProductBOM parent = bomLine.getParent();
|
||||
return MProduct.get(parent.getM_Product_ID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
/***********************************************************************
|
||||
* 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.form;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.compiere.model.MBPartner;
|
||||
import org.compiere.model.MDocType;
|
||||
import org.compiere.model.MInvoice;
|
||||
import org.compiere.model.MInvoiceLine;
|
||||
import org.compiere.model.MOrder;
|
||||
import org.compiere.model.MOrderLine;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.model.MProject;
|
||||
import org.compiere.model.MProjectLine;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.KeyNamePair;
|
||||
import org.eevolution.model.MPPProductBOMLine;
|
||||
import org.idempiere.apps.form.BOMDrop;
|
||||
import org.idempiere.apps.form.BOMDrop.SelectedItem;
|
||||
import org.idempiere.test.AbstractTestCase;
|
||||
import org.idempiere.test.DictionaryIDs;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
public class BOMDropFormTest extends AbstractTestCase {
|
||||
|
||||
public BOMDropFormTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDropToOrder() {
|
||||
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||
MBPartner bp = MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id);
|
||||
order.setBPartner(bp);
|
||||
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
|
||||
order.saveEx();
|
||||
|
||||
BOMDrop bomDrop = new BOMDrop();
|
||||
KeyNamePair[] orders = bomDrop.getDraftOrders(getTrxName());
|
||||
boolean found = false;
|
||||
for (KeyNamePair knp : orders) {
|
||||
if (knp.getKey() == order.get_ID()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue(found, "Failed to retrieve open draft order");
|
||||
|
||||
MProduct product = MProduct.get(DictionaryIDs.M_Product.P_CHAIR.id);
|
||||
MPPProductBOMLine[] bomLines = bomDrop.getBOMLine(product);
|
||||
assertTrue(bomLines.length > 0, "Failed to retrieve BOM lines for " + product.toString());
|
||||
|
||||
List<SelectedItem> selectedItems = new ArrayList<>();
|
||||
List<String> selectedParent = new ArrayList<>();
|
||||
Map<Integer, Integer> variantMap = new HashMap<>();
|
||||
bomDrop.addBOMLines(product, new BigDecimal("1"), b -> {
|
||||
MProduct component = b.getProductBOMLine().getProduct();
|
||||
if (MPPProductBOMLine.COMPONENTTYPE_Variant.equals(b.getBOMType())) {
|
||||
if (variantMap.containsKey(b.getBOMLevel()))
|
||||
return;
|
||||
else
|
||||
variantMap.put(b.getBOMLevel(), component.get_ID());
|
||||
} else if (variantMap.containsKey(b.getBOMLevel())) {
|
||||
variantMap.remove(b.getBOMLevel());
|
||||
}
|
||||
|
||||
if (b.getBOMLevel() > 0) {
|
||||
String key = b.getParentProduct().get_ID() + "_" + (b.getBOMLevel()-1);
|
||||
if (!selectedParent.contains(key))
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.isBOM() && component.isVerified()) {
|
||||
String key = component.get_ID()+"_"+b.getBOMLevel();
|
||||
selectedParent.add(key);
|
||||
return;
|
||||
}
|
||||
SelectedItem si = new SelectedItem(component.get_ID(), b.getLineQty());
|
||||
selectedItems.add(si);
|
||||
});
|
||||
|
||||
assertEquals(8, selectedItems.size(), "Unexpected number of components");
|
||||
|
||||
bomDrop.saveOrderLines(order.getC_Order_ID(), selectedItems, getTrxName());
|
||||
|
||||
order.load(getTrxName());
|
||||
MOrderLine[] orderLines = order.getLines(true, null);
|
||||
assertEquals(selectedItems.size(), orderLines.length, "Unexpected number of order lines");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDropToInvoice() {
|
||||
MInvoice invoice = new MInvoice(Env.getCtx(), 0, getTrxName());
|
||||
MBPartner bp = MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id);
|
||||
invoice.setBPartner(bp);
|
||||
invoice.setC_DocTypeTarget_ID(MDocType.DOCBASETYPE_ARInvoice);
|
||||
invoice.saveEx();
|
||||
|
||||
BOMDrop bomDrop = new BOMDrop();
|
||||
KeyNamePair[] invoices = bomDrop.getDraftInvoices(getTrxName());
|
||||
boolean found = false;
|
||||
for (KeyNamePair knp : invoices) {
|
||||
if (knp.getKey() == invoice.get_ID()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue(found, "Failed to retrieve open draft invoice");
|
||||
|
||||
MProduct product = MProduct.get(DictionaryIDs.M_Product.P_CHAIR.id);
|
||||
MPPProductBOMLine[] bomLines = bomDrop.getBOMLine(product);
|
||||
assertTrue(bomLines.length > 0, "Failed to retrieve BOM lines for " + product.toString());
|
||||
|
||||
List<SelectedItem> selectedItems = new ArrayList<>();
|
||||
List<String> selectedParent = new ArrayList<>();
|
||||
Map<Integer, Integer> variantMap = new HashMap<>();
|
||||
bomDrop.addBOMLines(product, new BigDecimal("1"), b -> {
|
||||
MProduct component = b.getProductBOMLine().getProduct();
|
||||
if (MPPProductBOMLine.COMPONENTTYPE_Variant.equals(b.getBOMType())) {
|
||||
if (variantMap.containsKey(b.getBOMLevel()))
|
||||
return;
|
||||
else
|
||||
variantMap.put(b.getBOMLevel(), component.get_ID());
|
||||
} else if (variantMap.containsKey(b.getBOMLevel())) {
|
||||
variantMap.remove(b.getBOMLevel());
|
||||
}
|
||||
|
||||
if (b.getBOMLevel() > 0) {
|
||||
String key = b.getParentProduct().get_ID() + "_" + (b.getBOMLevel()-1);
|
||||
if (!selectedParent.contains(key))
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.isBOM() && component.isVerified()) {
|
||||
String key = component.get_ID()+"_"+b.getBOMLevel();
|
||||
selectedParent.add(key);
|
||||
return;
|
||||
}
|
||||
SelectedItem si = new SelectedItem(component.get_ID(), b.getLineQty());
|
||||
selectedItems.add(si);
|
||||
});
|
||||
|
||||
assertEquals(8, selectedItems.size(), "Unexpected number of components");
|
||||
|
||||
bomDrop.saveInvoiceLines(invoice.get_ID(), selectedItems, getTrxName());
|
||||
|
||||
invoice.load(getTrxName());
|
||||
MInvoiceLine[] invoiceLines = invoice.getLines(true);
|
||||
assertEquals(selectedItems.size(), invoiceLines.length, "Unexpected number of invoice lines");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDropToProject() {
|
||||
MProject project = new MProject(Env.getCtx(), 0, getTrxName());
|
||||
MBPartner bp = MBPartner.get(Env.getCtx(), DictionaryIDs.C_BPartner.JOE_BLOCK.id);
|
||||
project.setC_BPartner_ID(bp.get_ID());
|
||||
project.setName("testDropToProject");
|
||||
project.setC_Currency_ID(DictionaryIDs.C_Currency.USD.id);
|
||||
project.saveEx();
|
||||
|
||||
BOMDrop bomDrop = new BOMDrop();
|
||||
KeyNamePair[] projects = bomDrop.getNonServiceProjects(getTrxName());
|
||||
boolean found = false;
|
||||
for (KeyNamePair knp : projects) {
|
||||
if (knp.getKey() == project.get_ID()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue(found, "Failed to retrieve open non service project");
|
||||
|
||||
MProduct product = MProduct.get(DictionaryIDs.M_Product.P_CHAIR.id);
|
||||
MPPProductBOMLine[] bomLines = bomDrop.getBOMLine(product);
|
||||
assertTrue(bomLines.length > 0, "Failed to retrieve BOM lines for " + product.toString());
|
||||
|
||||
List<SelectedItem> selectedItems = new ArrayList<>();
|
||||
List<String> selectedParent = new ArrayList<>();
|
||||
Map<Integer, Integer> variantMap = new HashMap<>();
|
||||
bomDrop.addBOMLines(product, new BigDecimal("1"), b -> {
|
||||
MProduct component = b.getProductBOMLine().getProduct();
|
||||
if (MPPProductBOMLine.COMPONENTTYPE_Variant.equals(b.getBOMType())) {
|
||||
if (variantMap.containsKey(b.getBOMLevel()))
|
||||
return;
|
||||
else
|
||||
variantMap.put(b.getBOMLevel(), component.get_ID());
|
||||
} else if (variantMap.containsKey(b.getBOMLevel())) {
|
||||
variantMap.remove(b.getBOMLevel());
|
||||
}
|
||||
|
||||
if (b.getBOMLevel() > 0) {
|
||||
String key = b.getParentProduct().get_ID() + "_" + (b.getBOMLevel()-1);
|
||||
if (!selectedParent.contains(key))
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.isBOM() && component.isVerified()) {
|
||||
String key = component.get_ID()+"_"+b.getBOMLevel();
|
||||
selectedParent.add(key);
|
||||
return;
|
||||
}
|
||||
SelectedItem si = new SelectedItem(component.get_ID(), b.getLineQty());
|
||||
selectedItems.add(si);
|
||||
});
|
||||
|
||||
assertEquals(8, selectedItems.size(), "Unexpected number of components");
|
||||
|
||||
bomDrop.saveProjectLines(project.get_ID(), selectedItems, getTrxName());
|
||||
|
||||
project.load(getTrxName());
|
||||
MProjectLine[] projectLines = project.getLines();
|
||||
assertEquals(selectedItems.size(), projectLines.length, "Unexpected number of project lines");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue