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.ISupportMask;
|
||||||
import org.adempiere.webui.LayoutUtils;
|
import org.adempiere.webui.LayoutUtils;
|
||||||
import org.adempiere.webui.apps.AEnv;
|
|
||||||
import org.adempiere.webui.event.DialogEvents;
|
import org.adempiere.webui.event.DialogEvents;
|
||||||
import org.adempiere.webui.factory.ButtonFactory;
|
import org.adempiere.webui.factory.ButtonFactory;
|
||||||
import org.adempiere.webui.util.ZKUpdateUtil;
|
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 INFORMATION = "~./zul/img/msgbox/info-btn.png";
|
||||||
public static final String ERROR = "~./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
|
* @deprecated Should use {@link #ProcessInfoDialog(String, String, ProcessInfo, boolean)} for flexible show message
|
||||||
* @param title
|
* @param title
|
||||||
|
@ -71,11 +72,22 @@ public class ProcessInfoDialog extends Window implements EventListener<Event> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* show result after run a process
|
* show result after run a process
|
||||||
* @param title
|
* @deprecated
|
||||||
* @param header
|
* @param title ignore
|
||||||
|
* @param header ignore
|
||||||
* @param pi
|
* @param pi
|
||||||
|
* @param needFillLogFromDb
|
||||||
*/
|
*/
|
||||||
public ProcessInfoDialog(String title, String header, ProcessInfo pi, boolean 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)
|
if (needFillLogFromDb)
|
||||||
ProcessInfoUtil.setLogFromDB(pi);
|
ProcessInfoUtil.setLogFromDB(pi);
|
||||||
init(pi.getTitle(), null, pi, null);
|
init(pi.getTitle(), null, pi, null);
|
||||||
|
@ -181,6 +193,10 @@ public class ProcessInfoDialog extends Window implements EventListener<Event> {
|
||||||
if (log.getAD_Table_ID() > 0
|
if (log.getAD_Table_ID() > 0
|
||||||
&& log.getRecord_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);
|
pnlMessage.appendChild(recordLink);
|
||||||
} else {
|
} else {
|
||||||
|
@ -207,6 +223,22 @@ public class ProcessInfoDialog extends Window implements EventListener<Event> {
|
||||||
btnOk.focus();
|
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
|
* after run a process, call this function to show result in a dialog
|
||||||
* @param pi
|
* @param pi
|
||||||
|
@ -216,7 +248,7 @@ public class ProcessInfoDialog extends Window implements EventListener<Event> {
|
||||||
* just pass false, other pass true to avoid duplicate message
|
* just pass false, other pass true to avoid duplicate message
|
||||||
*/
|
*/
|
||||||
public static ProcessInfoDialog showProcessInfo (ProcessInfo pi, int windowNo, final Component comp, boolean needFillLogFromDb) {
|
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);
|
final ISupportMask supportMask = LayoutUtils.showWindowWithMask(dialog, comp, LayoutUtils.OVERLAP_PARENT);
|
||||||
dialog.addEventListener(DialogEvents.ON_WINDOW_CLOSE, new EventListener<Event>() {
|
dialog.addEventListener(DialogEvents.ON_WINDOW_CLOSE, new EventListener<Event>() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,12 +11,13 @@ Require-Bundle: org.adempiere.base;bundle-version="0.0.0",
|
||||||
Export-Package: org.compiere.apps,
|
Export-Package: org.compiere.apps,
|
||||||
org.compiere.apps.form,
|
org.compiere.apps.form,
|
||||||
org.compiere.apps.wf,
|
org.compiere.apps.wf,
|
||||||
|
org.compiere.css,
|
||||||
org.compiere.grid,
|
org.compiere.grid,
|
||||||
org.compiere.grid.ed,
|
org.compiere.grid.ed,
|
||||||
org.compiere.css,
|
|
||||||
org.compiere.install,
|
org.compiere.install,
|
||||||
org.compiere.minigrid,
|
org.compiere.minigrid,
|
||||||
org.compiere.print,
|
org.compiere.print,
|
||||||
|
org.idempiere.apps.form,
|
||||||
org.netbeans.api.visual.action,
|
org.netbeans.api.visual.action,
|
||||||
org.netbeans.api.visual.anchor,
|
org.netbeans.api.visual.anchor,
|
||||||
org.netbeans.api.visual.border,
|
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