diff --git a/extend/src/test/functional/inventory/CSVFactory.java b/extend/src/test/functional/inventory/CSVFactory.java new file mode 100644 index 0000000000..f9d95910f4 --- /dev/null +++ b/extend/src/test/functional/inventory/CSVFactory.java @@ -0,0 +1,191 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 2009 SC ARHIPAC SERVICE SRL. All Rights Reserved. * + * 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 test.functional.inventory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.text.DateFormat; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import org.supercsv.io.CsvListReader; +import org.supercsv.io.ICsvListReader; +import org.supercsv.prefs.CsvPreference; + +/** + * @author Teo Sarca, www.arhipac.ro + */ +public class CSVFactory +{ + public static final DateFormat s_dateFormat = new SimpleDateFormat("dd.MM.yyyy"); + + private ICsvListReader reader; + + public Collection read(InputStream in) throws Exception + { + ArrayList tests = new ArrayList(); + // + reader = new CsvListReader(new InputStreamReader(in), CsvPreference.STANDARD_PREFERENCE); + String[] header = getCSVHeader(); + // + List line; + int last_lineNo = -1; + MMScenario scenario = null; + try + { + while ( (line = reader.read()) != null) + { + if (last_lineNo == -1 || last_lineNo + 1 < reader.getLineNumber()) + { + if (scenario != null) + { + tests.add(scenario); + } + scenario = new MMScenario("junit-test-line_"+(new DecimalFormat("000").format(reader.getLineNumber()))); + } + readDocument(scenario, header, line); + last_lineNo = reader.getLineNumber(); + } + } + catch (Exception e) + { + throw new RuntimeException("Error on line "+reader.getLineNumber()+": "+e.getLocalizedMessage(), e); + } + if (scenario != null) + { + tests.add(scenario); + } + // + return tests; + } + + private String[] getCSVHeader() throws IOException + { + String[] header = reader.getCSVHeader(true); + for (int i = 0; i < header.length; i++) + { + header[i] = header[i].trim().replaceAll("\\s", ""); + } + return header; + } + + private void readDocument(MMScenario scenario, String[] header, List line) + { + MMDocument doc = new MMDocument(scenario); + doc.csvLineNo = reader.getLineNumber(); + doc.DocBaseType = getValue("DocType", String.class, header, line); + doc.DocumentNo = getValue("DocumentNo", String.class, header, line); + doc.LocatorValue = getValue("LocatorValue", String.class, header, line); + doc.LocatorValueTo = getValue("LocatorValueTo", String.class, header, line); + doc.ProductValue = getValue("ProductValue", String.class, header, line); + doc.Price = getValue("Price", BigDecimal.class, header, line); + doc.Qty = getValue("Qty", BigDecimal.class, header, line); + doc.QtyOrdered = getValue("QtyOrdered", BigDecimal.class, header, line); + doc.QtyReserved = getValue("QtyReserved", BigDecimal.class, header, line); + doc.ASI = getValue("ASI", String.class, header, line); + doc.Date = getValue("MovementDate", Timestamp.class, header, line); + doc.PODocumentNo = getValue("PODocumentNo", String.class, header, line); + doc.IsReversal = getValue("IsReversal", Boolean.class, header, line); + scenario.docs.add(doc); + } + + /** + * TODO: refactor + * @param + * @param name + * @param clazz + * @param headers + * @param values + * @return + */ + @SuppressWarnings("unchecked") + public static T getValue(String name, Class clazz, String[] headers, List values) + { + String value = null; + for (int i = 0; i < headers.length; i++) + { + if (name.equalsIgnoreCase(headers[i])) + { + if (values.size() > i) + { + value = values.get(i); + } + break; + } + } + if (value != null && value.trim().length() == 0) + { + value = null; + } + // + if (String.class == clazz) + { + return (T)value; + } + else if (BigDecimal.class == clazz) + { + if (value == null) + return (T)BigDecimal.ZERO; + else + return (T)new BigDecimal(value); + } + else if (Integer.class == clazz) + { + if (value == null) + return (T)Integer.valueOf(0); + else + return (T)((Integer)Integer.parseInt(value)); + } + else if (Timestamp.class == clazz) + { + if (value == null) + return null; + else + { + try + { + Date date = s_dateFormat.parse(value); + return (T)new Timestamp(date.getTime()); + } + catch(ParseException e) + { + throw new RuntimeException(e); + } + } + } + else if (Boolean.class == clazz) + { + if (value == null) + return (T)Boolean.FALSE; + else if ("Y".equals(value)) + return (T)Boolean.TRUE; + else if ("N".equals(value)) + return (T)Boolean.FALSE; + else + throw new IllegalStateException("Invalid boolean value '"+value+"'"); + } + else + { + throw new IllegalArgumentException("clazz not supported - "+clazz); + } + } +} diff --git a/extend/src/test/functional/inventory/InventoryTest.java b/extend/src/test/functional/inventory/InventoryTest.java new file mode 100644 index 0000000000..6ff2067500 --- /dev/null +++ b/extend/src/test/functional/inventory/InventoryTest.java @@ -0,0 +1,277 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 2009 SC ARHIPAC SERVICE SRL. All Rights Reserved. * + * 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 test.functional.inventory; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; + +import org.adempiere.exceptions.AdempiereException; +import org.adempiere.exceptions.DBException; +import org.compiere.model.MDocType; +import org.compiere.model.MLocator; +import org.compiere.model.MProduct; +import org.compiere.model.MStorage; +import org.compiere.process.DocAction; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.Trx; +import org.compiere.util.TrxRunnable; +import org.compiere.util.Util; + +import test.AdempiereTestCase; + + +/** + * Run Inventory Tests + * @author Teo Sarca, www.arhipac.ro + */ +public class InventoryTest extends AdempiereTestCase +{ + @Override + protected void setUp() throws Exception + { + super.setUp(); + Env.setContext(Env.getCtx(), "$C_Currency_ID", 346); + +// MLocator locator = InventoryUtil.getCreateLocator(-1, "junit-wh1", "l1"); + Env.setContext(Env.getCtx(), "#M_Warehouse_ID", -1); + + Env.setContext(Env.getCtx(), "#PO_PriceList_ID", + InventoryUtil.getCreatePriceList("junit-PO", false).get_ID()); + Env.setContext(Env.getCtx(), "#SO_PriceList_ID", + InventoryUtil.getCreatePriceList("junit-SO", true).get_ID()); + } + + public void test01() throws Exception + { + InputStream in; + if (System.getProperty("TestFile") != null) + { + in = new FileInputStream(System.getProperty("TestFile")); + } + else //if (System.getProperty("UseArhipacURL") != null) + { + String url = "http://spreadsheets.google.com/pub?key=p_F3GDtQxWTA9YbH8MRkyKA&output=csv&gid=0"; + in = new URL(url).openStream(); + } + // + CSVFactory factory = new CSVFactory(); + Collection tests = factory.read(in); + String trxName = getTrxName(); + // + for (MMScenario scenario : tests) + { + if ("junit-test-line_053".compareTo(scenario.name) <= 0) + runMMScenario(scenario, trxName); +// break; + } + } + + private void runMMScenario(MMScenario scenario, String trxName) +{ + scenario.key = ""+System.currentTimeMillis(); + for (MMDocument doc : scenario.docs) + { + boolean ok = false; + try + { + createDocument(doc, trxName); + ok = true; + } + catch (Exception e) + { + InventoryTestException ie = (e instanceof InventoryTestException ? (InventoryTestException)e : new InventoryTestException(e)); + ie.scenario = scenario; + ie.document = doc; + throw ie; + } + finally + { + if (!ok) + { + dumpStatus(doc, trxName); + try + { + commit(); + } + catch (Exception e){} + } + } + } + } + + private void createDocument(final MMDocument doc, String trxName) + { + Trx.run(trxName, new TrxRunnable(){ + @Override + public void run(String trxName) { + if (doc.IsReversal) + { + MMDocument docOrig = doc.scenario.get(doc.DocBaseType, doc.DocumentNo); + doc.ProductValue = docOrig.ProductValue; + doc.LocatorValue = docOrig.LocatorValue; + doc.LocatorValueTo = docOrig.LocatorValueTo; + InventoryUtil.processDocument(docOrig, DocAction.ACTION_Reverse_Correct, DocAction.STATUS_Reversed); + return; + } + + if (doc.ProductValue == null) + throw new RuntimeException("ProductValue is null"); + doc.ProductValue = "junit-"+doc.ProductValue+"-"+doc.scenario.key; + + // Check document No conflict + if (doc.scenario.get(doc.DocBaseType, doc.DocumentNo) != null) + { + throw new AdempiereException("DocumentNo conflict - "+doc.DocumentNo); + } + + if (MDocType.DOCBASETYPE_PurchaseOrder.equals(doc.DocBaseType) + || MDocType.DOCBASETYPE_SalesOrder.equals(doc.DocBaseType) ) + { + InventoryUtil.createOrder(doc, trxName); + } + else if (MDocType.DOCBASETYPE_MaterialReceipt.equals(doc.DocBaseType) + || MDocType.DOCBASETYPE_MaterialDelivery.equals(doc.DocBaseType) ) + { + InventoryUtil.createInOut(doc, trxName); + } + else if (MDocType.DOCBASETYPE_MaterialMovement.equals(doc.DocBaseType)) + { + InventoryUtil.createMovement(doc, trxName); + } + else if (MDocType.DOCBASETYPE_MaterialPhysicalInventory.equals(doc.DocBaseType)) + { + InventoryUtil.createInventory(doc, trxName); + } + else if ("TST".equals(doc.DocBaseType)) + { + assertStorage(doc, trxName); + } + else + { + throw new AdempiereException("DocBaseType not supported for "+doc); + } + }}); + } + + private void assertStorage(MMDocument doc, String trxName) + { + MLocator locator = InventoryUtil.getCreateLocator(-1, doc.LocatorValue, doc.LocatorValue); + MProduct product = InventoryUtil.getCreateProduct(doc.ProductValue, null); + int M_ASI_ID = -1; + if (!Util.isEmpty(doc.ASI, true)) + { + M_ASI_ID = doc.scenario.getM_ASI_ID(doc.ASI); + } + ArrayList params = new ArrayList(); + String sql = "SELECT" + +" COALESCE(SUM(QtyOnHand),0)" + +",COALESCE(SUM(QtyReserved),0)" + +",COALESCE(SUM(QtyOrdered),0)" + +" FROM M_Storage" + +" WHERE M_Locator_ID=? AND M_Product_ID=?"; + params.add(locator.get_ID()); + params.add(product.get_ID()); + if (M_ASI_ID >= 0) + { + sql += " AND "+MStorage.COLUMNNAME_M_AttributeSetInstance_ID+"=?"; + params.add(M_ASI_ID); + } + PreparedStatement pstmt = null; + ResultSet rs = null; + BigDecimal qtyOnHand = Env.ZERO; + BigDecimal qtyOrdered = Env.ZERO; + BigDecimal qtyReserved = Env.ZERO; + try + { + pstmt = DB.prepareStatement(sql, trxName); + DB.setParameters(pstmt, params); + rs = pstmt.executeQuery(); + if (rs.next()) + { + qtyOnHand = rs.getBigDecimal(1); + qtyReserved = rs.getBigDecimal(2); + qtyOrdered = rs.getBigDecimal(3); + } + } + catch (SQLException e) + { + throw new DBException(e, sql); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // + // + assertEquals("QtyOnHand not match "+doc, doc.Qty, qtyOnHand); + assertEquals("QtyReserved not match "+doc, doc.QtyReserved, qtyReserved); + assertEquals("QtyOrdered not match "+doc, doc.QtyOrdered, qtyOrdered); + } + + private void dumpStatus(MMDocument doc, String trxName) + { + MProduct product = InventoryUtil.getCreateProduct(doc.ProductValue, null); + MStorage[] storage = MStorage.getOfProduct(getCtx(), product.get_ID(), trxName); + + System.err.println("STORAGE____________________________________________________"); + System.err.println(" "+doc); + for (MStorage s : storage) + { + System.err.println(""+s); + } + // + System.err.println(doc.scenario.toString()); + // + System.err.println("___________________________________________________________"); + System.err.flush(); + System.err.flush(); + } +} + +class InventoryTestException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public MMScenario scenario = null; + public MMDocument document = null; + + public InventoryTestException(Exception e) + { + super(e); + } + + @Override + public String getMessage() + { + String retValue = ""; + + retValue = super.getMessage() + + "\n document=" + this.document + + "\n scenario=" + this.scenario + ; + + return retValue; + } + + +} \ No newline at end of file diff --git a/extend/src/test/functional/inventory/InventoryUtil.java b/extend/src/test/functional/inventory/InventoryUtil.java new file mode 100644 index 0000000000..12ba569493 --- /dev/null +++ b/extend/src/test/functional/inventory/InventoryUtil.java @@ -0,0 +1,502 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 2009 SC ARHIPAC SERVICE SRL. All Rights Reserved. * + * 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 test.functional.inventory; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.Properties; + +import org.adempiere.exceptions.AdempiereException; +import org.compiere.model.MBPGroup; +import org.compiere.model.MBPartner; +import org.compiere.model.MBPartnerLocation; +import org.compiere.model.MCharge; +import org.compiere.model.MDocType; +import org.compiere.model.MInOut; +import org.compiere.model.MInOutLine; +import org.compiere.model.MInventory; +import org.compiere.model.MInventoryLine; +import org.compiere.model.MLocation; +import org.compiere.model.MLocator; +import org.compiere.model.MMovement; +import org.compiere.model.MMovementLine; +import org.compiere.model.MOrder; +import org.compiere.model.MOrderLine; +import org.compiere.model.MPriceList; +import org.compiere.model.MPriceListVersion; +import org.compiere.model.MProduct; +import org.compiere.model.MProductCategory; +import org.compiere.model.MProductPrice; +import org.compiere.model.MTaxCategory; +import org.compiere.model.MWarehouse; +import org.compiere.model.PO; +import org.compiere.model.Query; +import org.compiere.process.DocAction; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.TimeUtil; +import org.compiere.util.Util; + +/** + * @author Teo Sarca, www.arhipac.ro + */ +public final class InventoryUtil +{ + public static MProduct getCreateProduct(String value, String MMPolicy) + { + Properties ctx = Env.getCtx(); + MProduct product = new Query(ctx, MProduct.Table_Name, "Value=?", null) + .setParameters(new Object[]{value}) + .setOnlyActiveRecords(true) + .setClient_ID() + .firstOnly(); + if (product == null) + { + product = new MProduct(ctx, 0, null); + } + product.setValue(value); + product.setName(value); + setGeneratedTag(product); + product.setProductType(MProduct.PRODUCTTYPE_Item); + product.setIsStocked(true); + product.setC_UOM_ID(100); // Each + product.setC_TaxCategory_ID(getDefault_TaxCategory_ID()); + product.setM_Product_Category_ID(getCreateProductCategory(value, MMPolicy).get_ID()); + product.saveEx(); + // + getCreateProductPrice("#PO_PriceList_ID", product.get_ID(), 10); + getCreateProductPrice("#SO_PriceList_ID", product.get_ID(), 20); + // + return product; + } + + public static MProductPrice getCreateProductPrice (String ctxPriceList, int M_Product_ID, int price) + { + Properties ctx = Env.getCtx(); + int M_PriceList_ID = Env.getContextAsInt(ctx, ctxPriceList); + MPriceList pl = MPriceList.get(ctx, M_PriceList_ID, null); + MPriceListVersion plv = pl.getPriceListVersion(null); + // + BigDecimal priceBD = BigDecimal.valueOf(price); + MProductPrice pp = MProductPrice.get(ctx, plv.get_ID(), M_Product_ID, null); + if (pp == null) + { + pp = new MProductPrice(plv, M_Product_ID, priceBD, priceBD, priceBD); + } + pp.setPrices(priceBD, priceBD, priceBD); + pp.saveEx(); + return pp; + } + + public static MProductCategory getCreateProductCategory(String value, String MMPolicy) + { + if (MMPolicy == null) + MMPolicy = MProductCategory.MMPOLICY_FiFo; + Properties ctx = Env.getCtx(); + String whereClause = MProductCategory.COLUMNNAME_Value+"=?"; + MProductCategory pc = new Query(ctx, MProductCategory.Table_Name, whereClause, null) + .setParameters(new Object[]{value}) + .setOnlyActiveRecords(true) + .setClient_ID() + .firstOnly(); + if (pc == null) + { + pc = new MProductCategory(ctx, 0, null); + } + pc.setValue(value); + pc.setName(value); + setGeneratedTag(pc); + pc.setMMPolicy(MMPolicy); + // + pc.saveEx(); + return pc; + } + + public static int getDefault_TaxCategory_ID() + { + Properties ctx = Env.getCtx(); + return new Query(ctx, MTaxCategory.Table_Name, "IsDefault='Y'", null) + .setClient_ID() + .setOnlyActiveRecords(true) + .firstOnly() + .get_ID(); + } + + public static MBPartner getCreatePartner(String value) + { + Properties ctx = Env.getCtx(); + String whereClause = MBPartner.COLUMNNAME_Value+"=?"; + MBPartner bp = new Query(ctx, MBPartner.Table_Name, whereClause, null) + .setParameters(new Object[]{value}) + .setClient_ID() + .firstOnly(); + if (bp == null) + { + bp = new MBPartner(ctx, 0, null); + } + bp.setValue(value); + bp.setName(value); + setGeneratedTag(bp); + bp.setIsCustomer(true); + bp.setIsVendor(true); + bp.setC_BP_Group_ID(MBPGroup.getDefault(ctx).get_ID()); + bp.saveEx(); + // + if (bp.getLocations(false).length == 0) + { + MLocation loc = new MLocation(ctx, 0, null); + loc.saveEx(); + // + MBPartnerLocation bpl = new MBPartnerLocation(bp); + bpl.setC_Location_ID(loc.get_ID()); + bpl.saveEx(); + } + return bp; + } + + public static MOrder createOrder(MMDocument doc, String trxName) + { + Properties ctx = Env.getCtx(); + int M_PriceList_ID; + boolean isSOTrx; + if (MDocType.DOCBASETYPE_SalesOrder.equals(doc.DocBaseType)) + { + M_PriceList_ID = Env.getContextAsInt(ctx, "SO_PriceList_ID"); + isSOTrx = true; + } + else if (MDocType.DOCBASETYPE_PurchaseOrder.equals(doc.DocBaseType)) + { + M_PriceList_ID = Env.getContextAsInt(ctx, "PO_PriceList_ID"); + isSOTrx = false; + } + else + { + throw new IllegalArgumentException("DocBaseType not supported - "+doc); + } + // + int AD_Org_ID = getFirst_Org_ID(); + MLocator locator = getCreateLocator(AD_Org_ID, doc.LocatorValue, doc.LocatorValue); + // + MOrder order = new MOrder(ctx, 0, trxName); + order.setAD_Org_ID(AD_Org_ID); + order.setIsSOTrx(isSOTrx); + order.setC_DocTypeTarget_ID(); + order.setDateOrdered(doc.Date); + order.setDateAcct(doc.Date); + order.setDatePromised(doc.Date); + order.setBPartner(getCreatePartner(doc.BPValue)); + order.setM_PriceList_ID(M_PriceList_ID); + order.setM_Warehouse_ID(locator.getM_Warehouse_ID()); + setGeneratedTag(order); + order.saveEx(); + // + MProduct product = getCreateProduct(doc.ProductValue, null); + MOrderLine line = new MOrderLine(order); + line.setProduct(product); + line.setQty(doc.Qty); + line.saveEx(); + // + doc.document = order; + processDocument(doc, MOrder.DOCACTION_Complete, MOrder.DOCSTATUS_Completed); + return order; + } + + public static MInOut createInOut(MMDocument doc, String trxName) + { + MOrder order; + if (MDocType.DOCBASETYPE_MaterialReceipt.equals(doc.DocBaseType)) + { + order = (MOrder)doc.scenario.get(MDocType.DOCBASETYPE_PurchaseOrder, doc.PODocumentNo).document; + } + else if (MDocType.DOCBASETYPE_MaterialDelivery.equals(doc.DocBaseType)) + { + order = (MOrder)doc.scenario.get(MDocType.DOCBASETYPE_SalesOrder, doc.PODocumentNo).document; + } + else + { + throw new IllegalArgumentException("DocBaseType not supported - "+doc); + } + +// if (trxName != null && trxName.equals(order.get_TrxName())) +// throw new AdempiereException("Internal exception - not same trxName"); + + MInOut io = new MInOut(order, 0, doc.Date); + setGeneratedTag(io); + io.saveEx(); + // + MInOutLine iol = null; + for (MOrderLine oline : order.getLines(true, null)) + { + iol = new MInOutLine(io); + iol.setOrderLine(oline, 0, doc.Qty); + iol.setQty(doc.Qty); + iol.saveEx(); + break; + } + // + doc.document = io; + processDocument(doc, MInOut.DOCACTION_Complete, MInOut.DOCSTATUS_Completed); + + if (!Util.isEmpty(doc.ASI)) + { + iol.load(trxName); + doc.scenario.registerASICode(doc.ASI, iol.getM_AttributeSetInstance_ID(), !io.isSOTrx()); + } + + return io; + } + + public static MMovement createMovement(MMDocument doc, String trxName) + { + Properties ctx = Env.getCtx(); + int AD_Org_ID = getFirst_Org_ID(); + MProduct product = getCreateProduct(doc.ProductValue, null); + MLocator locator = getCreateLocator(AD_Org_ID, doc.LocatorValue, doc.LocatorValue); + MLocator locatorTo = getCreateLocator(AD_Org_ID, doc.LocatorValueTo, doc.LocatorValueTo); + // + MMovement m = new MMovement(ctx, 0, trxName); + m.setAD_Org_ID(AD_Org_ID); + m.setMovementDate(doc.Date); + m.saveEx(); + // + MMovementLine line = new MMovementLine(m); + line.setM_Product_ID(product.get_ID()); + line.setM_Locator_ID(locator.get_ID()); + line.setM_LocatorTo_ID(locatorTo.get_ID()); + line.setMovementQty(doc.Qty); + line.saveEx(); + // + doc.document = m; + processDocument(doc, MMovement.DOCACTION_Complete, MMovement.DOCSTATUS_Completed); + return m; + } + + public static MInventory createInventory(MMDocument doc, String trxName) + { + Properties ctx = Env.getCtx(); + int AD_Org_ID = getFirst_Org_ID(); + MProduct product = getCreateProduct(doc.ProductValue, null); + MLocator locator = getCreateLocator(AD_Org_ID, doc.LocatorValue, doc.LocatorValue); + // + MInventory inv = new MInventory(ctx, 0, trxName); + inv.setAD_Org_ID(AD_Org_ID); +// inv.setIsInternalUseInventory(true); + inv.setMovementDate(doc.Date); + inv.setM_Warehouse_ID(locator.getM_Warehouse_ID()); + setGeneratedTag(inv); + inv.saveEx(); + // + MInventoryLine line = new MInventoryLine(inv, + locator.get_ID(), product.get_ID(), 0, + null, null); + line.setQtyInternalUse(doc.Qty); + line.setC_Charge_ID(getCreateCharge("junit-charge").get_ID()); + line.saveEx(); + // + doc.document = inv; + processDocument(doc, MInventory.DOCACTION_Complete, MInventory.DOCSTATUS_Completed); + + if (!Util.isEmpty(doc.ASI)) + { + line.load(trxName); + doc.scenario.registerASICode(doc.ASI, line.getM_AttributeSetInstance_ID(), line.getQtyInternalUse().signum() <= 0); + } + + return inv; + } + + + + + + + + public static int getFirst_Org_ID() + { + int AD_Org_ID = Env.getAD_Org_ID(Env.getCtx()); + if (AD_Org_ID > 0) + return AD_Org_ID; + String sql = "SELECT MIN(AD_Org_ID) FROM AD_Org WHERE AD_Client_ID=?"; + return DB.getSQLValueEx(null, sql, Env.getAD_Client_ID(Env.getCtx())); + } + + /** + * Helper Method : Create Warehouse + */ + public static MWarehouse getCreateWarehouse(int AD_Org_ID, String value) + { + if (AD_Org_ID <= 0) + AD_Org_ID = getFirst_Org_ID(); + Properties ctx = Env.getCtx(); + String whereClause = "AD_Org_ID=? AND Value=?"; + MWarehouse wh = new Query(ctx, MWarehouse.Table_Name, whereClause, null) + .setParameters(new Object[]{AD_Org_ID, value}) + .setClient_ID() + .firstOnly(); + if (wh != null) + return wh; + wh = new MWarehouse(ctx, 0, null); + wh.setAD_Org_ID(AD_Org_ID); + wh.setValue(value); + wh.setName(value); + + MLocation loc = new MLocation(ctx, 0, null); + loc.saveEx(); + wh.setC_Location_ID(loc.get_ID()); + wh.saveEx(); + return wh; + } + + /** + * Helper Method : Create Locator + */ + public static MLocator getCreateLocator(int AD_Org_ID, String whValue, String value) + { + if (AD_Org_ID <= 0) + AD_Org_ID = getFirst_Org_ID(); + MWarehouse wh = getCreateWarehouse(AD_Org_ID, whValue); + MLocator locator = null; + for (MLocator loc : wh.getLocators(false)) + { + if (loc.getValue().equals(value)) + { + locator = loc; + break; + } + } + if (locator == null) + { + locator = new MLocator(wh, value); + locator.setXYZ(value, value, value); + } + if (wh.getLocators(false).length == 0) + { + locator.setIsDefault(true); + } + locator.saveEx(); + // + return locator; + } + + public static MPriceList getCreatePriceList(String value, boolean IsSOPriceList) + { + Properties ctx = Env.getCtx(); + int C_Currency_ID = Env.getContextAsInt(ctx, "$C_Currency_ID"); + String whereClause = MPriceList.COLUMNNAME_Name+"=?" + +" AND "+MPriceList.COLUMNNAME_IsSOPriceList+"=?" + +" AND "+MPriceList.COLUMNNAME_C_Currency_ID+"=?"; + MPriceList pl = new Query(ctx, MPriceList.Table_Name, whereClause, null) + .setParameters(new Object[]{value, IsSOPriceList, C_Currency_ID}) + .setClient_ID() + .setOnlyActiveRecords(true) + .firstOnly(); + if (pl == null) + { + pl = new MPriceList(ctx, 0, null); + pl.setName(value); + pl.setIsSOPriceList(IsSOPriceList); + pl.setC_Currency_ID(C_Currency_ID); + } + setGeneratedTag(pl); + pl.setIsTaxIncluded(false); + pl.saveEx(); + // + Timestamp ValidFrom = TimeUtil.getDay(1970, 1, 1); + MPriceListVersion plv = pl.getPriceListVersion(ValidFrom); + if (plv == null) + { + plv = new MPriceListVersion(pl); + plv.setValidFrom(ValidFrom); + plv.setM_DiscountSchema_ID(getM_DiscountSchema_ID()); + setGeneratedTag(plv); + plv.saveEx(); + } + // + return pl; + } + public static int getM_DiscountSchema_ID() + { + Properties ctx = Env.getCtx(); + int AD_Client_ID = Env.getAD_Client_ID(ctx); + final String sql = "SELECT MIN(M_DiscountSchema_ID) FROM M_DiscountSchema" + + " WHERE AD_Client_ID=?"; + return DB.getSQLValueEx(null, sql, AD_Client_ID); + + } + + public static void setGeneratedTag(PO po) + { + String desc = "Generated by "+InventoryUtil.class + +" on "+new Timestamp(System.currentTimeMillis()); + po.set_ValueOfColumn("Description", desc); + } + + public static void processDocument(MMDocument doc, + String docAction, String targetDocStatus) + { + PO po = (PO)doc.document; +// po.saveEx(); + po.load(po.get_TrxName()); + po.set_ValueOfColumn("DocAction", docAction); + // + try + { + if (!doc.document.processIt(docAction)) + throw new AdempiereException(doc.document.getProcessMsg()); + } + catch (Exception e) + { + throw (e instanceof AdempiereException ? (AdempiereException)e : new AdempiereException(e)); + } + po.saveEx(); + // + // Check DocSatus + if (targetDocStatus != null && !targetDocStatus.equals(doc.document.getDocStatus())) + { + throw new AdempiereException("Doc process error "+doc + +" (TargetDocStatus="+targetDocStatus + +", DocStatus="+doc.document.getDocStatus() + +")"); + } + // + // Is Completed ? + if (DocAction.STATUS_Completed.equals(doc.document.getDocStatus())) + { + // Track ASI: + if (!Util.isEmpty(doc.ASI)) + { + + } + } + } + + public static MCharge getCreateCharge(String value) + { + Properties ctx = Env.getCtx(); + String whereClause = MCharge.COLUMNNAME_Name+"=?"; + MCharge charge = new Query(ctx, MCharge.Table_Name, whereClause, null) + .setParameters(new Object[]{value}) + .setOnlyActiveRecords(true) + .setClient_ID() + .firstOnly(); + if (charge == null) + { + charge = new MCharge(ctx, 0, null); + charge.setName(value); + setGeneratedTag(charge); + charge.saveEx(); + } + return charge; + } +} diff --git a/extend/src/test/functional/inventory/MMDocument.java b/extend/src/test/functional/inventory/MMDocument.java new file mode 100644 index 0000000000..83245d67f4 --- /dev/null +++ b/extend/src/test/functional/inventory/MMDocument.java @@ -0,0 +1,86 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 2009 SC ARHIPAC SERVICE SRL. All Rights Reserved. * + * 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 test.functional.inventory; + +import java.math.BigDecimal; +import java.sql.Timestamp; + +import org.compiere.process.DocAction; + +/** + * @author Teo Sarca, www.arhipac.ro + */ +public class MMDocument +{ + public final MMScenario scenario; + + public int csvLineNo = -1; + public String DocBaseType; + public String DocumentNo; + public String BPValue = "junit-test-bp01"; + public String LocatorValue; + public String LocatorValueTo; + public String ProductValue; + public BigDecimal Price; + public BigDecimal Qty; + public BigDecimal QtyOrdered; + public BigDecimal QtyReserved; + public Timestamp Date; + public String ASI; + public String PODocumentNo; + public boolean IsReversal = false; + // + public DocAction document = null; + + public MMDocument(MMScenario scenario) + { + this.scenario = scenario; + } + + /** + * Constructs a String with all attributes + * in name = value format. + * + * @return a String representation + * of this object. + */ + public String toString() + { + final String TAB = " "; + + String retValue = ""; + + retValue = "MMDocument ( " +// + super.toString() + TAB + + "csvLineNo = " + this.csvLineNo + TAB + + "DocBaseType = " + this.DocBaseType + TAB + + "DocumentNo = " + this.DocumentNo + TAB + + "LocatorValue = " + this.LocatorValue + TAB + + "LocatorValueTo = " + this.LocatorValueTo + TAB + + "ProductValue = " + this.ProductValue + TAB + + "Price = " + this.Price + TAB + + "Qty = " + this.Qty + TAB + + "QtyOrdered = " + this.QtyOrdered + TAB + + "QtyReserved = " + this.QtyReserved + TAB + + "ASI = " + this.ASI + TAB + + "Date = " + this.Date + TAB + + "PODocumentNo = " + this.PODocumentNo + TAB + + "IsReversal = " + this.IsReversal + TAB + + "document = " + this.document + TAB + + " )"; + + return retValue; + } + +} diff --git a/extend/src/test/functional/inventory/MMScenario.java b/extend/src/test/functional/inventory/MMScenario.java new file mode 100644 index 0000000000..43807c17ca --- /dev/null +++ b/extend/src/test/functional/inventory/MMScenario.java @@ -0,0 +1,107 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 2009 SC ARHIPAC SERVICE SRL. All Rights Reserved. * + * 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 test.functional.inventory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import org.adempiere.exceptions.AdempiereException; + +/** + * @author Teo Sarca, www.arhipac.ro + */ +public class MMScenario +{ + public final String name; + public String key = null; + public final List docs = new ArrayList(); + + /** ASI Code -> M_AttributeSetInstance_ID */ + public final HashMap asiCodes = new HashMap(); + + public MMScenario(String name) + { + this.name = name; + } + + public MMDocument get(String docBaseType, String documentNo) + { + if (documentNo == null) + throw new IllegalArgumentException("documentNo is null"); + // + for (MMDocument doc : docs) + { + if (docBaseType.equals(doc.DocBaseType) && documentNo.equals(documentNo)) + return doc; + } + throw new AdempiereException("["+name+"] document not found for" + +" DocBaseType="+docBaseType+", documentNo="+documentNo); + } + + public void registerASICode(String asiCode, int M_ASI_ID, boolean isCreated) + { + asiCode = asiCode.trim(); + + if (isCreated && asiCodes.get(asiCode) != null) + { + throw new AdempiereException("ASI Should be unique : Code="+asiCode+", ID="+M_ASI_ID); + } + asiCodes.put(asiCode, M_ASI_ID); + } + + public int getM_ASI_ID(String asiCode) + { + asiCode = asiCode.trim(); + if (asiCode.equals("0")) + return 0; + + Integer asi_id = asiCodes.get(asiCode); + if (asi_id == null) + { + throw new AdempiereException("No ASI created for Code="+asiCode); + } + return asi_id; + } + + @Override + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append("__SCENARIO________________________________________________\n"); + sb.append(" Name : "+this.name).append("\n"); + sb.append(" Key : "+this.key).append("\n"); + // + sb.append("Lines: \n"); + for (MMDocument doc : this.docs) + { + sb.append(" ").append(doc.toString()).append("\n"); + } + // + sb.append("ASI(Code=>ID): "); + for (Entry entry : this.asiCodes.entrySet()) + { + sb.append("(") + .append(entry.getKey()).append("=>").append(entry.getValue()) + .append("); "); + } + sb.append("\n"); + // + sb.append("__________________________________________________________\n"); + return sb.toString(); + } + + +}