diff --git a/base/src/org/adempiere/pipo/handler/RoleElementHandler.java b/base/src/org/adempiere/pipo/handler/RoleElementHandler.java new file mode 100644 index 0000000000..45ea17c24b --- /dev/null +++ b/base/src/org/adempiere/pipo/handler/RoleElementHandler.java @@ -0,0 +1,510 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 Adempiere, Inc. 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. * + * + * Copyright (C) 2005 Robert Klein. robeklein@hotmail.com + * Contributor(s): Low Heng Sin hengsin@avantz.com + *****************************************************************************/ +package org.adempiere.pipo.handler; + +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.logging.Level; + +import javax.xml.transform.sax.TransformerHandler; + +import org.adempiere.pipo.AbstractElementHandler; +import org.adempiere.pipo.Element; +import org.adempiere.pipo.exception.DatabaseAccessException; +import org.adempiere.pipo.exception.POSaveFailedException; +import org.compiere.model.MRole; +import org.compiere.model.X_AD_Form; +import org.compiere.model.X_AD_Package_Exp_Detail; +import org.compiere.model.X_AD_Process; +import org.compiere.model.X_AD_Role; +import org.compiere.model.X_AD_Task; +import org.compiere.model.X_AD_User; +import org.compiere.model.X_AD_Window; +import org.compiere.model.X_AD_Workflow; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +public class RoleElementHandler extends AbstractElementHandler { + + private List roles = new ArrayList(); + + private OrgRoleElementHandler orgHandler = new OrgRoleElementHandler(); + private ProcessAccessElementHandler processHandler = new ProcessAccessElementHandler(); + private UserRoleElementHandler userHandler = new UserRoleElementHandler(); + private WindowAccessElementHandler windowHandler = new WindowAccessElementHandler(); + private FormAccessElementHandler formHandler = new FormAccessElementHandler(); + private TaskAccessElementHandler taskHandler = new TaskAccessElementHandler(); + private WorkflowAccessElementHandler workflowHandler = new WorkflowAccessElementHandler(); + + public void startElement(Properties ctx, Element element) + throws SAXException { + String elementValue = element.getElementValue(); + Attributes atts = element.attributes; + log.info(elementValue + " " + atts.getValue("Name")); + + String name = atts.getValue("Name"); + + int id = get_ID(ctx, "AD_Role", name); + MRole m_Role = new MRole(ctx, id, getTrxName(ctx)); + + int AD_Backup_ID = -1; + String Object_Status = null; + if (id > 0) { + AD_Backup_ID = copyRecord(ctx, "AD_Role", m_Role); + Object_Status = "Update"; + } else { + Object_Status = "New"; + AD_Backup_ID = 0; + } + + m_Role.setName(name); + name = atts.getValue("treemenuname"); + if (name != null && name.trim().length() > 0) { + id = get_IDWithColumn(ctx, "AD_Tree", "Name", name); + if (id <= 0) { + element.defer = true; + return; + } + m_Role.setAD_Tree_Menu_ID(id); + } + + name = atts.getValue("treeorgname"); + if (name != null && name.trim().length() > 0) { + id = get_IDWithColumn(ctx, "AD_Tree", "Name", name); + if (id <= 0) { + element.defer = true; + return; + } + m_Role.setAD_Tree_Org_ID(id); + } + + name = atts.getValue("currencycode"); + if (name != null && name.trim().length() > 0) { + id = get_IDWithColumn(ctx, "C_Currency", "ISO_Code", name); + if (id <= 0) { + element.defer = true; + return; + } + m_Role.setC_Currency_ID(id); + } + + name = atts.getValue("supervisorid"); + if (name != null && name.trim().length() > 0) { + id = get_IDWithColumn(ctx, "AD_User", "Name", name); + if (id <= 0) { + element.defer = true; + return; + } + m_Role.setC_Currency_ID(id); + } + + m_Role.setDescription(getStringValue(atts,"Description")); + String amtApproval = getStringValue(atts,"AmtApproval"); + if (amtApproval != null) + m_Role.setAmtApproval(new BigDecimal(amtApproval)); + m_Role.setIsActive(atts.getValue("isActive") != null ? Boolean.valueOf( + atts.getValue("isActive")).booleanValue() : true); + m_Role + .setIsAccessAllOrgs(atts.getValue("isAccessAllOrgs") != null ? Boolean + .valueOf(atts.getValue("isAccessAllOrgs")) + .booleanValue() + : true); + m_Role + .setIsCanApproveOwnDoc(atts.getValue("isCanApproveOwnDoc") != null ? Boolean + .valueOf(atts.getValue("isCanApproveOwnDoc")) + .booleanValue() + : true); + m_Role.setIsCanExport(atts.getValue("isCanExport") != null ? Boolean + .valueOf(atts.getValue("isCanExport")).booleanValue() : true); + m_Role.setIsCanReport(atts.getValue("isCanReport") != null ? Boolean + .valueOf(atts.getValue("isCanReport")).booleanValue() : true); + m_Role.setIsChangeLog(atts.getValue("isChangeLog") != null ? Boolean + .valueOf(atts.getValue("isChangeLog")).booleanValue() : true); + m_Role.setIsManual(atts.getValue("isManual") != null ? Boolean.valueOf( + atts.getValue("isManual")).booleanValue() : true); + m_Role + .setIsPersonalAccess(atts.getValue("isPersonalAccess") != null ? Boolean + .valueOf(atts.getValue("isPersonalAccess")) + .booleanValue() + : true); + m_Role + .setIsPersonalLock(atts.getValue("isPersonalLock") != null ? Boolean + .valueOf(atts.getValue("isPersonalLock")) + .booleanValue() + : true); + m_Role.setIsShowAcct(atts.getValue("isShowAcct") != null ? Boolean + .valueOf(atts.getValue("isShowAcct")).booleanValue() : true); + m_Role + .setIsUseUserOrgAccess(atts.getValue("isUseUserOrgAccess") != null ? Boolean + .valueOf(atts.getValue("isUseUserOrgAccess")) + .booleanValue() + : true); + m_Role + .setOverwritePriceLimit(atts.getValue("isOverwritePriceLimit") != null ? Boolean + .valueOf(atts.getValue("isOverwritePriceLimit")) + .booleanValue() + : true); + m_Role.setPreferenceType(atts.getValue("PreferenceType")); + m_Role.setUserLevel(atts.getValue("UserLevel")); + m_Role.setAllow_Info_Account(Boolean.valueOf(atts.getValue("AllowInfoAccount"))); + m_Role.setAllow_Info_Asset(Boolean.valueOf(atts.getValue("AllowInfoAsset"))); + m_Role.setAllow_Info_BPartner(Boolean.valueOf(atts.getValue("AllowInfoBPartner"))); + m_Role.setAllow_Info_CashJournal(Boolean.valueOf(atts.getValue("AllowInfoCashJournal"))); + m_Role.setAllow_Info_InOut(Boolean.valueOf(atts.getValue("AllowInfoInOut"))); + m_Role.setAllow_Info_Invoice(Boolean.valueOf(atts.getValue("AllowInfoInvoice"))); + m_Role.setAllow_Info_Order(Boolean.valueOf(atts.getValue("AllowInfoOrder"))); + m_Role.setAllow_Info_Payment(Boolean.valueOf(atts.getValue("AllowInfoPayment"))); + m_Role.setAllow_Info_Product(Boolean.valueOf(atts.getValue("AllowInfoProduct"))); + m_Role.setAllow_Info_Resource(Boolean.valueOf(atts.getValue("AllowInfoResource"))); + m_Role.setAllow_Info_Schedule(Boolean.valueOf(atts.getValue("AllowInfoSchedule"))); + + if (m_Role.save(getTrxName(ctx)) == true) { + + record_log(ctx, 1, m_Role.getName(), "Role", m_Role.get_ID(), + AD_Backup_ID, Object_Status, "AD_Role", get_IDWithColumn( + ctx, "AD_Table", "TableName", "AD_Role")); + } else { + + record_log(ctx, 0, m_Role.getName(), "Role", m_Role.get_ID(), + AD_Backup_ID, Object_Status, "AD_Role", get_IDWithColumn( + ctx, "AD_Table", "TableName", "AD_Role")); + throw new POSaveFailedException("Role"); + } + } + + public void endElement(Properties ctx, Element element) throws SAXException { + } + + public void create(Properties ctx, TransformerHandler document) + throws SAXException { + int Role_id = Env.getContextAsInt(ctx, + X_AD_Package_Exp_Detail.COLUMNNAME_AD_Role_ID); + if (roles.contains(Role_id)) + return; + roles.add(Role_id); + X_AD_Role m_Role = new X_AD_Role(ctx, Role_id, null); + AttributesImpl atts = new AttributesImpl(); + createRoleBinding(atts, m_Role); + document.startElement("", "", "role", atts); + + // Process org access + String sql = "SELECT * FROM AD_Role_OrgAccess WHERE AD_Role_ID= " + + Role_id; + PreparedStatement pstmt = null; + pstmt = DB.prepareStatement(sql, getTrxName(ctx)); + try { + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + createOrgAccess(ctx, document, rs.getInt("AD_Org_ID"), rs + .getInt("AD_Role_ID")); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + + catch (Exception e) { + log.log(Level.SEVERE, "AD_Role_OrgAccess", e); + throw new DatabaseAccessException("Failed to export organization role access."); + } + // Process user assignment access + sql = "SELECT * FROM AD_User_Roles WHERE AD_Role_ID= " + Role_id; + pstmt = null; + pstmt = DB.prepareStatement(sql, getTrxName(ctx)); + try { + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + createUserRole(ctx, document, rs.getInt("AD_User_ID"), + rs.getInt("AD_Role_ID"), rs.getInt("AD_Org_ID")); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + + catch (Exception e) { + log.log(Level.SEVERE, "AD_User_Roles", e); + throw new DatabaseAccessException("Failed to export user role assignment."); + } + + // Process AD_Window_Access Values + sql = "SELECT * FROM AD_Window_Access WHERE AD_Role_ID= " + Role_id; + pstmt = null; + pstmt = DB.prepareStatement(sql, getTrxName(ctx)); + try { + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + createWindowAccess(ctx, document, rs + .getInt("AD_Window_ID"), rs.getInt("AD_Role_ID")); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + + catch (Exception e) { + log.log(Level.SEVERE, "AD_Window_Access", e); + throw new DatabaseAccessException("Failed to export window access."); + } + + // Process AD_Process_Access Values + sql = "SELECT * FROM AD_Process_Access WHERE AD_Role_ID= " + Role_id; + pstmt = null; + pstmt = DB.prepareStatement(sql, getTrxName(ctx)); + try { + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + createProcessAccess(ctx, document, rs + .getInt("AD_Process_ID"), rs.getInt("AD_Role_ID")); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + + catch (Exception e) { + log.log(Level.SEVERE, "AD_Process_Access", e); + throw new DatabaseAccessException("Failed to export process access."); + } + + // Process AD_Form_Access Values + sql = "SELECT * FROM AD_Form_Access WHERE AD_Role_ID= " + Role_id; + pstmt = null; + pstmt = DB.prepareStatement(sql, getTrxName(ctx)); + try { + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + createFormAccess(ctx, document, rs.getInt("AD_Form_ID"), + rs.getInt("AD_Role_ID")); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + + catch (Exception e) { + log.log(Level.SEVERE, "AD_Form_Access", e); + throw new DatabaseAccessException("Failed to export form access."); + } + + // Process AD_Workflow_Access Values + sql = "SELECT * FROM AD_Workflow_Access WHERE AD_Role_ID= " + Role_id; + pstmt = null; + pstmt = DB.prepareStatement(sql, getTrxName(ctx)); + try { + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + createWorkflowAccess(ctx, document, rs + .getInt("AD_Workflow_ID"), rs.getInt("AD_Role_ID")); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + + catch (Exception e) { + log.log(Level.SEVERE, "AD_Workflow_Access", e); + throw new DatabaseAccessException("Failed to export workflow access."); + } + + // Process AD_Task_Access Values + sql = "SELECT * FROM AD_Task_Access WHERE AD_Role_ID= " + Role_id; + pstmt = null; + pstmt = DB.prepareStatement(sql, getTrxName(ctx)); + try { + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + createTaskAccess(ctx, document, rs.getInt("AD_Task_ID"), rs + .getInt("AD_Role_ID")); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + + catch (Exception e) { + log.log(Level.SEVERE, "AD_Task_Access", e); + throw new DatabaseAccessException("Failed to export task access."); + } finally { + try { + if (pstmt != null) + pstmt.close(); + } catch (Exception e) { + } + pstmt = null; + } + document.endElement("", "", "role"); + } + + private void createTaskAccess(Properties ctx, TransformerHandler document, + int AD_Task_ID, int AD_Role_ID) throws SAXException { + Env.setContext(ctx, X_AD_Task.COLUMNNAME_AD_Task_ID, AD_Task_ID); + Env.setContext(ctx, X_AD_Role.COLUMNNAME_AD_Role_ID, AD_Role_ID); + taskHandler.create(ctx, document); + ctx.remove(X_AD_Task.COLUMNNAME_AD_Task_ID); + ctx.remove(X_AD_Role.COLUMNNAME_AD_Role_ID); + } + + private void createWorkflowAccess(Properties ctx, + TransformerHandler document, int AD_Workflow_ID, int AD_Role_ID) throws SAXException { + Env.setContext(ctx, X_AD_Workflow.COLUMNNAME_AD_Workflow_ID, AD_Workflow_ID); + Env.setContext(ctx, X_AD_Role.COLUMNNAME_AD_Role_ID, AD_Role_ID); + workflowHandler.create(ctx, document); + ctx.remove(X_AD_Workflow.COLUMNNAME_AD_Workflow_ID); + ctx.remove(X_AD_Role.COLUMNNAME_AD_Role_ID); + } + + private void createFormAccess(Properties ctx, TransformerHandler document, + int AD_Form_ID, int AD_Role_ID) throws SAXException { + Env.setContext(ctx, X_AD_Form.COLUMNNAME_AD_Form_ID, AD_Form_ID); + Env.setContext(ctx, X_AD_Role.COLUMNNAME_AD_Role_ID, AD_Role_ID); + formHandler.create(ctx, document); + ctx.remove(X_AD_Form.COLUMNNAME_AD_Form_ID); + ctx.remove(X_AD_Role.COLUMNNAME_AD_Role_ID); + } + + private void createProcessAccess(Properties ctx, + TransformerHandler document, int AD_Process_ID, int AD_Role_ID) throws SAXException { + Env.setContext(ctx, X_AD_Process.COLUMNNAME_AD_Process_ID, AD_Process_ID); + Env.setContext(ctx, X_AD_Role.COLUMNNAME_AD_Role_ID, AD_Role_ID); + processHandler.create(ctx, document); + ctx.remove(X_AD_Process.COLUMNNAME_AD_Process_ID); + ctx.remove(X_AD_Role.COLUMNNAME_AD_Role_ID); + } + + private void createWindowAccess(Properties ctx, + TransformerHandler document, int AD_Window_ID, int AD_Role_ID) throws SAXException { + Env.setContext(ctx, X_AD_Window.COLUMNNAME_AD_Window_ID, AD_Window_ID); + Env.setContext(ctx, X_AD_Role.COLUMNNAME_AD_Role_ID, AD_Role_ID); + windowHandler.create(ctx, document); + ctx.remove(X_AD_Window.COLUMNNAME_AD_Window_ID); + ctx.remove(X_AD_Role.COLUMNNAME_AD_Role_ID); + } + + private void createUserRole(Properties ctx, TransformerHandler document, + int AD_User_ID, int AD_Role_ID, int AD_Org_ID) throws SAXException { + Env.setContext(ctx, X_AD_User.COLUMNNAME_AD_User_ID, AD_User_ID); + Env.setContext(ctx, X_AD_Role.COLUMNNAME_AD_Role_ID, AD_Role_ID); + Env.setContext(ctx, "AD_Org_ID", AD_Org_ID); + userHandler.create(ctx, document); + ctx.remove(X_AD_User.COLUMNNAME_AD_User_ID); + ctx.remove(X_AD_Role.COLUMNNAME_AD_Role_ID); + ctx.remove("AD_Org_ID"); + } + + private void createOrgAccess(Properties ctx, TransformerHandler document, + int AD_Org_ID, int AD_Role_ID) throws SAXException { + Env.setContext(ctx, "AD_Org_ID", AD_Org_ID); + Env.setContext(ctx, X_AD_Role.COLUMNNAME_AD_Role_ID, AD_Role_ID); + orgHandler.create(ctx, document); + ctx.remove("AD_Org_ID"); + ctx.remove(X_AD_Role.COLUMNNAME_AD_Role_ID); + } + + private AttributesImpl createRoleBinding(AttributesImpl atts, + X_AD_Role m_Role) { + String sql = null; + String name = null; + atts.clear(); + + if (m_Role.getAD_Tree_Menu_ID() > 0) { + sql = "SELECT Name FROM AD_Tree WHERE AD_Tree_ID=? AND AD_Tree.TreeType='MM'"; + name = DB.getSQLValueString(null, sql, m_Role.getAD_Tree_Menu_ID()); + atts.addAttribute("", "", "treemenuname", "CDATA", name); + } else + atts.addAttribute("", "", "treemenuname", "CDATA", ""); + + if (m_Role.getAD_Tree_Org_ID() > 0) { + sql = "SELECT Name FROM AD_Tree WHERE AD_Tree_ID=? AND AD_Tree.TreeType='OO'"; + name = DB.getSQLValueString(null, sql, m_Role.getAD_Tree_Org_ID()); + atts.addAttribute("", "", "treeorgname", "CDATA", name); + } else + atts.addAttribute("", "", "treeorgname", "CDATA", ""); + + if (m_Role.getC_Currency_ID() > 0) { + sql = "SELECT ISO_Code FROM C_Currency WHERE C_Currency_ID=?"; + name = DB.getSQLValueString(null, sql, m_Role.getC_Currency_ID()); + atts.addAttribute("", "", "currencycode", "CDATA", name); + } else + atts.addAttribute("", "", "currencycode", "CDATA", ""); + + if (m_Role.getSupervisor_ID() > 0) { + sql = "SELECT Name FROM AD_User WHERE AD_User_ID=?"; + name = DB.getSQLValueString(null, sql, m_Role.getC_Currency_ID()); + atts.addAttribute("", "", "supervisorid", "CDATA", name); + } else + atts.addAttribute("", "", "supervisorid", "CDATA", ""); + + atts.addAttribute("", "", "Description", "CDATA", (m_Role + .getDescription() != null ? m_Role.getDescription() : "")); + atts.addAttribute("", "", "Name", "CDATA", + (m_Role.getName() != null ? m_Role.getName() : "")); + atts.addAttribute("", "", "AmtApproval", "CDATA", ("" + m_Role + .getAmtApproval())); + atts.addAttribute("", "", "isAccessAllOrgs", "CDATA", (m_Role + .isAccessAllOrgs() == true ? "true" : "false")); + atts.addAttribute("", "", "isActive", "CDATA", + (m_Role.isActive() == true ? "true" : "false")); + atts.addAttribute("", "", "isCanApproveOwnDoc", "CDATA", (m_Role + .isCanApproveOwnDoc() == true ? "true" : "false")); + atts.addAttribute("", "", "isCanExport", "CDATA", + (m_Role.isCanExport() == true ? "true" : "false")); + atts.addAttribute("", "", "isCanReport", "CDATA", + (m_Role.isCanReport() == true ? "true" : "false")); + atts.addAttribute("", "", "isChangeLog", "CDATA", + (m_Role.isChangeLog() == true ? "true" : "false")); + atts.addAttribute("", "", "isManual", "CDATA", + (m_Role.isManual() == true ? "true" : "false")); + atts.addAttribute("", "", "isPersonalAccess", "CDATA", (m_Role + .isPersonalAccess() == true ? "true" : "false")); + atts.addAttribute("", "", "isPersonalLock", "CDATA", (m_Role + .isPersonalLock() == true ? "true" : "false")); + atts.addAttribute("", "", "isShowAcct", "CDATA", + (m_Role.isShowAcct() == true ? "true" : "false")); + atts.addAttribute("", "", "isUseUserOrgAccess", "CDATA", (m_Role + .isUseUserOrgAccess() == true ? "true" : "false")); + atts.addAttribute("", "", "isOverwritePriceLimit", "CDATA", (m_Role + .isOverwritePriceLimit() == true ? "true" : "false")); + atts + .addAttribute("", "", "PreferenceType", "CDATA", (m_Role + .getPreferenceType() != null ? m_Role + .getPreferenceType() : "")); + atts.addAttribute("", "", "UserLevel", "CDATA", + (m_Role.getUserLevel() != null ? m_Role.getUserLevel() : "")); + + atts.addAttribute("", "", "AllowInfoAccount", "CDATA", Boolean.toString(m_Role.isAllow_Info_Account())); + atts.addAttribute("", "", "AllowInfoAsset", "CDATA", Boolean.toString(m_Role.isAllow_Info_Asset())); + atts.addAttribute("", "", "AllowInfoBPartner", "CDATA", Boolean.toString(m_Role.isAllow_Info_BPartner())); + atts.addAttribute("", "", "AllowInfoCashJournal", "CDATA", Boolean.toString(m_Role.isAllow_Info_CashJournal())); + atts.addAttribute("", "", "AllowInfoInOut", "CDATA", Boolean.toString(m_Role.isAllow_Info_InOut())); + atts.addAttribute("", "", "AllowInfoInvoice", "CDATA", Boolean.toString(m_Role.isAllow_Info_Invoice())); + atts.addAttribute("", "", "AllowInfoOrder", "CDATA", Boolean.toString(m_Role.isAllow_Info_Order())); + atts.addAttribute("", "", "AllowInfoPayment", "CDATA", Boolean.toString(m_Role.isAllow_Info_Payment())); + atts.addAttribute("", "", "AllowInfoProduct", "CDATA", Boolean.toString(m_Role.isAllow_Info_Product())); + atts.addAttribute("", "", "AllowInfoResource", "CDATA", Boolean.toString(m_Role.isAllow_Info_Resource())); + atts.addAttribute("", "", "AllowInfoSchedule", "CDATA", Boolean.toString(m_Role.isAllow_Info_Schedule())); + + return atts; + } +} diff --git a/base/src/org/compiere/model/MAllocationHdr.java b/base/src/org/compiere/model/MAllocationHdr.java new file mode 100644 index 0000000000..3e51e03be7 --- /dev/null +++ b/base/src/org/compiere/model/MAllocationHdr.java @@ -0,0 +1,738 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.File; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Properties; +import java.util.logging.Level; + +import org.compiere.process.DocAction; +import org.compiere.process.DocumentEngine; +import org.compiere.util.CLogger; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.Msg; + +/** + * Payment Allocation Model. + * Allocation Trigger update C_BPartner + * + * @author Jorg Janke + * @version $Id: MAllocationHdr.java,v 1.3 2006/07/30 00:51:03 jjanke Exp $ + */ +public final class MAllocationHdr extends X_C_AllocationHdr implements DocAction +{ + /** + * Get Allocations of Payment + * @param ctx context + * @param C_Payment_ID payment + * @return allocations of payment + * @param trxName transaction + */ + public static MAllocationHdr[] getOfPayment (Properties ctx, int C_Payment_ID, String trxName) + { + String sql = "SELECT * FROM C_AllocationHdr h " + + "WHERE IsActive='Y'" + + " AND EXISTS (SELECT * FROM C_AllocationLine l " + + "WHERE h.C_AllocationHdr_ID=l.C_AllocationHdr_ID AND l.C_Payment_ID=?)"; + ArrayList list = new ArrayList(); + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, trxName); + pstmt.setInt(1, C_Payment_ID); + rs = pstmt.executeQuery(); + while (rs.next()) + list.add (new MAllocationHdr(ctx, rs, trxName)); + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + MAllocationHdr[] retValue = new MAllocationHdr[list.size()]; + list.toArray(retValue); + return retValue; + } // getOfPayment + + /** + * Get Allocations of Invoice + * @param ctx context + * @param C_Invoice_ID payment + * @return allocations of payment + * @param trxName transaction + */ + public static MAllocationHdr[] getOfInvoice (Properties ctx, int C_Invoice_ID, String trxName) + { + String sql = "SELECT * FROM C_AllocationHdr h " + + "WHERE IsActive='Y'" + + " AND EXISTS (SELECT * FROM C_AllocationLine l " + + "WHERE h.C_AllocationHdr_ID=l.C_AllocationHdr_ID AND l.C_Invoice_ID=?)"; + ArrayList list = new ArrayList(); + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, trxName); + pstmt.setInt(1, C_Invoice_ID); + rs = pstmt.executeQuery(); + while (rs.next()) + list.add (new MAllocationHdr(ctx, rs, trxName)); + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + MAllocationHdr[] retValue = new MAllocationHdr[list.size()]; + list.toArray(retValue); + return retValue; + } // getOfInvoice + + /** Logger */ + private static CLogger s_log = CLogger.getCLogger(MAllocationHdr.class); + + + /************************************************************************** + * Standard Constructor + * @param ctx context + * @param C_AllocationHdr_ID id + * @param trxName transaction + */ + public MAllocationHdr (Properties ctx, int C_AllocationHdr_ID, String trxName) + { + super (ctx, C_AllocationHdr_ID, trxName); + if (C_AllocationHdr_ID == 0) + { + // setDocumentNo (null); + setDateTrx (new Timestamp(System.currentTimeMillis())); + setDateAcct (getDateTrx()); + setDocAction (DOCACTION_Complete); // CO + setDocStatus (DOCSTATUS_Drafted); // DR + // setC_Currency_ID (0); + setApprovalAmt (Env.ZERO); + setIsApproved (false); + setIsManual (false); + // + setPosted (false); + setProcessed (false); + setProcessing(false); + } + } // MAllocation + + /** + * Mandatory New Constructor + * @param ctx context + * @param IsManual manual trx + * @param DateTrx date (if null today) + * @param C_Currency_ID currency + * @param description description + * @param trxName transaction + */ + public MAllocationHdr (Properties ctx, boolean IsManual, Timestamp DateTrx, + int C_Currency_ID, String description, String trxName) + { + this (ctx, 0, trxName); + setIsManual(IsManual); + if (DateTrx != null) + { + setDateTrx (DateTrx); + setDateAcct (DateTrx); + } + setC_Currency_ID (C_Currency_ID); + if (description != null) + setDescription(description); + } // create Allocation + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MAllocationHdr (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MAllocation + + /** Lines */ + private MAllocationLine[] m_lines = null; + + /** + * Get Lines + * @param requery if true requery + * @return lines + */ + public MAllocationLine[] getLines (boolean requery) + { + if (m_lines != null && m_lines.length != 0 && !requery) { + set_TrxName(m_lines, get_TrxName()); + return m_lines; + } + // + String sql = "SELECT * FROM C_AllocationLine WHERE C_AllocationHdr_ID=?"; + ArrayList list = new ArrayList(); + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, getC_AllocationHdr_ID()); + rs = pstmt.executeQuery (); + while (rs.next ()) + { + MAllocationLine line = new MAllocationLine(getCtx(), rs, get_TrxName()); + line.setParent(this); + list.add (line); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // + m_lines = new MAllocationLine[list.size ()]; + list.toArray (m_lines); + return m_lines; + } // getLines + + /** + * Set Processed + * @param processed Processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + if (get_ID() == 0) + return; + String sql = "UPDATE C_AllocationHdr SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE C_AllocationHdr_ID=" + getC_AllocationHdr_ID(); + int no = DB.executeUpdate(sql, get_TrxName()); + m_lines = null; + log.fine(processed + " - #" + no); + } // setProcessed + + + /************************************************************************** + * Before Save + * @param newRecord + * @return save + */ + protected boolean beforeSave (boolean newRecord) + { + // Changed from Not to Active + if (!newRecord && is_ValueChanged("IsActive") && isActive()) + { + log.severe ("Cannot Re-Activate deactivated Allocations"); + return false; + } + return true; + } // beforeSave + + /** + * Before Delete. + * @return true if acct was deleted + */ + protected boolean beforeDelete () + { + String trxName = get_TrxName(); + if (trxName == null || trxName.length() == 0) + log.warning ("No transaction"); + if (isPosted()) + { + if (!MPeriod.isOpen(getCtx(), getDateTrx(), MDocType.DOCBASETYPE_PaymentAllocation)) + { + log.warning ("Period Closed"); + return false; + } + setPosted(false); + if (MFactAcct.delete (Table_ID, get_ID(), trxName) < 0) + return false; + } + // Mark as Inactive + setIsActive(false); // updated DB for line delete/process + String sql = "UPDATE C_AllocationHdr SET IsActive='N' WHERE C_AllocationHdr_ID=?"; + DB.executeUpdate(sql, getC_AllocationHdr_ID(), trxName); + + // Unlink + getLines(true); + HashSet bps = new HashSet(); + for (int i = 0; i < m_lines.length; i++) + { + MAllocationLine line = m_lines[i]; + bps.add(new Integer(line.getC_BPartner_ID())); + if (!line.delete(true, trxName)) + return false; + } + updateBP(bps); + return true; + } // beforeDelete + + /** + * After Save + * @param newRecord + * @param success + * @return success + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + return success; + } // afterSave + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // processIt + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info(toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + /** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getDateAcct(), MDocType.DOCBASETYPE_PaymentAllocation)) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + getLines(false); + if (m_lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + // Add up Amounts & validate + BigDecimal approval = Env.ZERO; + for (int i = 0; i < m_lines.length; i++) + { + MAllocationLine line = m_lines[i]; + approval = approval.add(line.getWriteOffAmt()).add(line.getDiscountAmt()); + // Make sure there is BP + if (line.getC_BPartner_ID() == 0) + { + m_processMsg = "No Business Partner"; + return DocAction.STATUS_Invalid; + } + } + setApprovalAmt(approval); + // + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + if (!DOCACTION_Complete.equals(getDocAction())) + setDocAction(DOCACTION_Complete); + + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info(toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info(toString()); + setIsApproved(false); + return true; + } // rejectIt + + /** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Implicit Approval + if (!isApproved()) + approveIt(); + log.info(toString()); + + // Link + getLines(false); + HashSet bps = new HashSet(); + for (int i = 0; i < m_lines.length; i++) + { + MAllocationLine line = m_lines[i]; + bps.add(new Integer(line.processIt(false))); // not reverse + } + updateBP(bps); + + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Void Document. + * Same as Close. + * @return true if success + */ + public boolean voidIt() + { + log.info(toString()); + + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + boolean retValue = reverseIt(); + + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + + setDocAction(DOCACTION_None); + + return retValue; + } // voidIt + + /** + * Close Document. + * Cancel not delivered Qunatities + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + setDocAction(DOCACTION_None); + + return true; + } // closeIt + + /** + * Reverse Correction + * @return true if success + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + boolean retValue = reverseIt(); + + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + setDocAction(DOCACTION_None); + return retValue; + } // reverseCorrectionIt + + /** + * Reverse Accrual - none + * @return false + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + boolean retValue = reverseIt(); + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + setDocAction(DOCACTION_None); + return retValue; + } // reverseAccrualIt + + /** + * Re-activate + * @return false + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + return false; + } // reActivateIt + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MAllocationHdr["); + sb.append(get_ID()).append("-").append(getSummary()).append ("]"); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + return Msg.getElement(getCtx(), "C_AllocationHdr_ID") + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + // ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID()); + // if (re == null) + return null; + // return re.getPDF(file); + } // createPDF + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Total Lines = 123.00 (#1) + sb.append(": ") + .append(Msg.translate(getCtx(),"ApprovalAmt")).append("=").append(getApprovalAmt()) + .append(" (#").append(getLines(false).length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getCreatedBy(); + } // getDoc_User_ID + + + /************************************************************************** + * Reverse Allocation. + * Period needs to be open + * @return true if reversed + */ + private boolean reverseIt() + { + if (!isActive()) + throw new IllegalStateException("Allocation already reversed (not active)"); + + // Can we delete posting + if (!MPeriod.isOpen(getCtx(), getDateTrx(), MPeriodControl.DOCBASETYPE_PaymentAllocation)) + throw new IllegalStateException("@PeriodClosed@"); + + // Set Inactive + setIsActive (false); + setDocumentNo(getDocumentNo()+"^"); + setDocStatus(DOCSTATUS_Reversed); // for direct calls + if (!save() || isActive()) + throw new IllegalStateException("Cannot de-activate allocation"); + + // Delete Posting + int no = MFactAcct.delete(MAllocationHdr.Table_ID, getC_AllocationHdr_ID(), get_TrxName()); + log.fine("Fact_Acct deleted #" + no); + + // Unlink Invoices + getLines(true); + HashSet bps = new HashSet(); + for (int i = 0; i < m_lines.length; i++) + { + MAllocationLine line = m_lines[i]; + line.setIsActive(false); + line.save(); + bps.add(new Integer(line.processIt(true))); // reverse + } + updateBP(bps); + return true; + } // reverse + + + /** + * Update Open Balance of BP's + * @param bps list of business partners + */ + private void updateBP(HashSet bps) + { + log.info("#" + bps.size()); + Iterator it = bps.iterator(); + while (it.hasNext()) + { + int C_BPartner_ID = it.next(); + MBPartner bp = new MBPartner(getCtx(), C_BPartner_ID, get_TrxName()); + bp.setTotalOpenBalance(); // recalculates from scratch + // bp.setSOCreditStatus(); // called automatically + if (bp.save()) + log.fine(bp.toString()); + else + log.log(Level.SEVERE, "BP not updated - " + bp); + } + } // updateBP + +} // MAllocation diff --git a/base/src/org/compiere/model/MCash.java b/base/src/org/compiere/model/MCash.java new file mode 100644 index 0000000000..1cc11b01bb --- /dev/null +++ b/base/src/org/compiere/model/MCash.java @@ -0,0 +1,776 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.File; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Properties; +import java.util.logging.Level; + +import org.compiere.process.DocAction; +import org.compiere.process.DocumentEngine; +import org.compiere.util.CLogger; +import org.compiere.util.DB; +import org.compiere.util.DisplayType; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.compiere.util.TimeUtil; + +/** + * Cash Journal Model + * + * @author Jorg Janke + * @version $Id: MCash.java,v 1.3 2006/07/30 00:51:03 jjanke Exp $ + * + * @author Teo Sarca, SC ARHIPAC SERVICE SRL + *
  • BF [ 1831997 ] Cash journal allocation reversed + *
  • BF [ 1894524 ] Pay an reversed invoice + */ +public class MCash extends X_C_Cash implements DocAction +{ + /** + * Get Cash Journal for currency, org and date + * @param ctx context + * @param C_Currency_ID currency + * @param AD_Org_ID org + * @param dateAcct date + * @param trxName transaction + * @return cash + */ + public static MCash get (Properties ctx, int AD_Org_ID, + Timestamp dateAcct, int C_Currency_ID, String trxName) + { + MCash retValue = null; + // Existing Journal + String sql; + sql = "SELECT * FROM C_Cash c " + + "WHERE c.AD_Org_ID=?" // #1 + + " AND TRUNC(c.StatementDate)=?" // #2 + + " AND c.Processed='N'" + + " AND EXISTS (SELECT * FROM C_CashBook cb " + + "WHERE c.C_CashBook_ID=cb.C_CashBook_ID AND cb.AD_Org_ID=c.AD_Org_ID" + + " AND cb.C_Currency_ID=?)"; // #3 + + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + pstmt.setInt (1, AD_Org_ID); + pstmt.setTimestamp (2, TimeUtil.getDay(dateAcct)); + pstmt.setInt (3, C_Currency_ID); + rs = pstmt.executeQuery (); + if (rs.next ()) + retValue = new MCash (ctx, rs, trxName); + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + if (retValue != null) + return retValue; + + // Get CashBook + MCashBook cb = MCashBook.get (ctx, AD_Org_ID, C_Currency_ID); + if (cb == null) + { + s_log.warning("No CashBook for AD_Org_ID=" + AD_Org_ID + ", C_Currency_ID=" + C_Currency_ID); + return null; + } + + // Create New Journal + retValue = new MCash (cb, dateAcct); + retValue.save(trxName); + return retValue; + } // get + + /** + * Get Cash Journal for CashBook and date + * @param ctx context + * @param C_CashBook_ID cashbook + * @param dateAcct date + * @param trxName transaction + * @return cash + */ + public static MCash get (Properties ctx, int C_CashBook_ID, + Timestamp dateAcct, String trxName) + { + MCash retValue = null; + // Existing Journal + String sql = "SELECT * FROM C_Cash c " + + "WHERE c.C_CashBook_ID=?" // #1 + + " AND TRUNC(c.StatementDate)=?" // #2 + + " AND c.Processed='N'"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + pstmt.setInt (1, C_CashBook_ID); + pstmt.setTimestamp (2, TimeUtil.getDay(dateAcct)); + rs = pstmt.executeQuery (); + if (rs.next ()) + retValue = new MCash (ctx, rs, trxName); + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + if (retValue != null) + return retValue; + + // Get CashBook + MCashBook cb = new MCashBook (ctx, C_CashBook_ID, trxName); + if (cb.get_ID() ==0) + { + s_log.warning("Not found C_CashBook_ID=" + C_CashBook_ID); + return null; + } + + // Create New Journal + retValue = new MCash (cb, dateAcct); + retValue.save(trxName); + return retValue; + } // get + + /** Static Logger */ + private static CLogger s_log = CLogger.getCLogger (MCash.class); + + + /************************************************************************** + * Standard Constructor + * @param ctx context + * @param C_Cash_ID id + * @param trxName transaction + */ + public MCash (Properties ctx, int C_Cash_ID, String trxName) + { + super (ctx, C_Cash_ID, trxName); + if (C_Cash_ID == 0) + { + // setC_CashBook_ID (0); // FK + setBeginningBalance (Env.ZERO); + setEndingBalance (Env.ZERO); + setStatementDifference(Env.ZERO); + setDocAction(DOCACTION_Complete); + setDocStatus(DOCSTATUS_Drafted); + // + Timestamp today = TimeUtil.getDay(System.currentTimeMillis()); + setStatementDate (today); // @#Date@ + setDateAcct (today); // @#Date@ + String name = DisplayType.getDateFormat(DisplayType.Date).format(today) + + " " + MOrg.get(ctx, getAD_Org_ID()).getValue(); + setName (name); + setIsApproved(false); + setPosted (false); // N + setProcessed (false); + } + } // MCash + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MCash (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MCash + + /** + * Parent Constructor + * @param cb cash book + * @param today date - if null today + */ + public MCash (MCashBook cb, Timestamp today) + { + this (cb.getCtx(), 0, cb.get_TrxName()); + setClientOrg(cb); + setC_CashBook_ID(cb.getC_CashBook_ID()); + if (today != null) + { + setStatementDate (today); + setDateAcct (today); + String name = DisplayType.getDateFormat(DisplayType.Date).format(today) + + " " + cb.getName(); + setName (name); + } + m_book = cb; + } // MCash + + /** Lines */ + private MCashLine[] m_lines = null; + /** CashBook */ + private MCashBook m_book = null; + + /** + * Get Lines + * @param requery requery + * @return lines + */ + public MCashLine[] getLines (boolean requery) + { + if (m_lines != null && !requery) { + set_TrxName(m_lines, get_TrxName()); + return m_lines; + } + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM C_CashLine WHERE C_Cash_ID=? ORDER BY Line"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, getC_Cash_ID()); + rs = pstmt.executeQuery (); + while (rs.next ()) + list.add (new MCashLine (getCtx(), rs, get_TrxName())); + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + + m_lines = new MCashLine[list.size ()]; + list.toArray (m_lines); + return m_lines; + } // getLines + + /** + * Get Cash Book + * @return cash book + */ + public MCashBook getCashBook() + { + if (m_book == null) + m_book = MCashBook.get(getCtx(), getC_CashBook_ID()); + return m_book; + } // getCashBook + + /** + * Get Document No + * @return name + */ + public String getDocumentNo() + { + return getName(); + } // getDocumentNo + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + return Msg.getElement(getCtx(), "C_Cash_ID") + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + // ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID()); + // if (re == null) + return null; + // return re.getPDF(file); + } // createPDF + + /** + * Before Save + * @param newRecord + * @return true + */ + protected boolean beforeSave (boolean newRecord) + { + setAD_Org_ID(getCashBook().getAD_Org_ID()); + if (getAD_Org_ID() == 0) + { + log.saveError("Error", Msg.parseTranslation(getCtx(), "@AD_Org_ID@")); + return false; + } + // Calculate End Balance + setEndingBalance(getBeginningBalance().add(getStatementDifference())); + return true; + } // beforeSave + + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // process + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info(toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + /** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getDateAcct(), MDocType.DOCBASETYPE_CashJournal)) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + MCashLine[] lines = getLines(false); + if (lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + // Add up Amounts + BigDecimal difference = Env.ZERO; + int C_Currency_ID = getC_Currency_ID(); + for (int i = 0; i < lines.length; i++) + { + MCashLine line = lines[i]; + if (!line.isActive()) + continue; + if (C_Currency_ID == line.getC_Currency_ID()) + difference = difference.add(line.getAmount()); + else + { + BigDecimal amt = MConversionRate.convert(getCtx(), line.getAmount(), + line.getC_Currency_ID(), C_Currency_ID, getDateAcct(), 0, + getAD_Client_ID(), getAD_Org_ID()); + if (amt == null) + { + m_processMsg = "No Conversion Rate found - @C_CashLine_ID@= " + line.getLine(); + return DocAction.STATUS_Invalid; + } + difference = difference.add(amt); + } + } + setStatementDifference(difference); + // setEndingBalance(getBeginningBalance().add(getStatementDifference())); + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + if (!DOCACTION_Complete.equals(getDocAction())) + setDocAction(DOCACTION_Complete); + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info(toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info(toString()); + setIsApproved(false); + return true; + } // rejectIt + + /** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + + // Implicit Approval + if (!isApproved()) + approveIt(); + // + log.info(toString()); + + MCashLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MCashLine line = lines[i]; + if (MCashLine.CASHTYPE_Invoice.equals(line.getCashType())) + { + // Check if the invoice is completed - teo_sarca BF [ 1894524 ] + MInvoice invoice = line.getInvoice(); + if (!MInvoice.DOCSTATUS_Completed.equals(invoice.getDocStatus()) + && !MInvoice.DOCSTATUS_Closed.equals(invoice.getDocStatus())) + { + m_processMsg = "@Line@ "+line.getLine()+": @InvoiceCreateDocNotCompleted@"; + return DocAction.STATUS_Invalid; + } + // + String name = Msg.translate(getCtx(), "C_Cash_ID") + ": " + getName() + + " - " + Msg.translate(getCtx(), "Line") + " " + line.getLine(); + MAllocationHdr hdr = new MAllocationHdr(getCtx(), false, + getDateAcct(), line.getC_Currency_ID(), + name, get_TrxName()); + hdr.setAD_Org_ID(getAD_Org_ID()); + if (!hdr.save()) + { + m_processMsg = CLogger.retrieveErrorString("Could not create Allocation Hdr"); + return DocAction.STATUS_Invalid; + } + // Allocation Line + MAllocationLine aLine = new MAllocationLine (hdr, line.getAmount(), + line.getDiscountAmt(), line.getWriteOffAmt(), Env.ZERO); + aLine.setC_Invoice_ID(line.getC_Invoice_ID()); + aLine.setC_CashLine_ID(line.getC_CashLine_ID()); + if (!aLine.save()) + { + m_processMsg = CLogger.retrieveErrorString("Could not create Allocation Line"); + return DocAction.STATUS_Invalid; + } + // Should start WF + if(!hdr.processIt(DocAction.ACTION_Complete)) { + m_processMsg = CLogger.retrieveErrorString("Could not process Allocation"); + return DocAction.STATUS_Invalid; + } + if (!hdr.save()) { + m_processMsg = CLogger.retrieveErrorString("Could not save Allocation"); + return DocAction.STATUS_Invalid; + } + } + else if (MCashLine.CASHTYPE_BankAccountTransfer.equals(line.getCashType())) + { + // Payment just as intermediate info + MPayment pay = new MPayment (getCtx(), 0, get_TrxName()); + pay.setAD_Org_ID(getAD_Org_ID()); + String documentNo = getName(); + pay.setDocumentNo(documentNo); + pay.setR_PnRef(documentNo); + pay.set_Value("TrxType", "X"); // Transfer + pay.set_Value("TenderType", "X"); + // + pay.setC_BankAccount_ID(line.getC_BankAccount_ID()); + pay.setC_DocType_ID(true); // Receipt + pay.setDateTrx(getStatementDate()); + pay.setDateAcct(getDateAcct()); + pay.setAmount(line.getC_Currency_ID(), line.getAmount().negate()); // Transfer + pay.setDescription(line.getDescription()); + pay.setDocStatus(MPayment.DOCSTATUS_Closed); + pay.setDocAction(MPayment.DOCACTION_None); + pay.setPosted(true); + pay.setIsAllocated(true); // Has No Allocation! + pay.setProcessed(true); + if (!pay.save()) + { + m_processMsg = CLogger.retrieveErrorString("Could not create Payment"); + return DocAction.STATUS_Invalid; + } + } + } + + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + // + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Void Document. + * Same as Close. + * @return true if success + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + setDocAction(DOCACTION_None); + return false; + } // voidIt + + /** + * Close Document. + * Cancel not delivered Qunatities + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + setDocAction(DOCACTION_None); + return true; + } // closeIt + + /** + * Reverse Correction + * @return true if success + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + return false; + } // reverseCorrectionIt + + /** + * Reverse Accrual - none + * @return true if success + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return false; + } // reverseAccrualIt + + /** + * Re-activate + * @return true if success + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + setProcessed(false); + if (reverseCorrectIt()) + return true; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + return false; + } // reActivateIt + + /** + * Set Processed + * @param processed processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + String sql = "UPDATE C_CashLine SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE C_Cash_ID=" + getC_Cash_ID(); + int noLine = DB.executeUpdate (sql, get_TrxName()); + m_lines = null; + log.fine(processed + " - Lines=" + noLine); + } // setProcessed + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MCash["); + sb.append (get_ID ()) + .append ("-").append (getName()) + .append(", Balance=").append(getBeginningBalance()) + .append("->").append(getEndingBalance()) + .append ("]"); + return sb.toString (); + } // toString + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getName()); + // : Total Lines = 123.00 (#1) + sb.append(": ") + .append(Msg.translate(getCtx(),"BeginningBalance")).append("=").append(getBeginningBalance()) + .append(",") + .append(Msg.translate(getCtx(),"EndingBalance")).append("=").append(getEndingBalance()) + .append(" (#").append(getLines(false).length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getCreatedBy(); + } // getDoc_User_ID + + /** + * Get Document Approval Amount + * @return amount difference + */ + public BigDecimal getApprovalAmt() + { + return getStatementDifference(); + } // getApprovalAmt + + /** + * Get Currency + * @return Currency + */ + public int getC_Currency_ID () + { + return getCashBook().getC_Currency_ID(); + } // getC_Currency_ID + +} // MCash diff --git a/base/src/org/compiere/model/MInOut.java b/base/src/org/compiere/model/MInOut.java new file mode 100644 index 0000000000..8bc4e6bfce --- /dev/null +++ b/base/src/org/compiere/model/MInOut.java @@ -0,0 +1,2034 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.print.*; +import org.compiere.process.*; +import org.compiere.util.*; + +/** + * Shipment Model + * + * @author Jorg Janke + * @version $Id: MInOut.java,v 1.4 2006/07/30 00:51:03 jjanke Exp $ + * + * Modifications: Added the RMA functionality (Ashley Ramdass) + * @author Karsten Thiemann, Schaeffer AG + *
  • Bug [ 1759431 ] Problems with VCreateFrom + */ +public class MInOut extends X_M_InOut implements DocAction +{ + /** + * Create Shipment From Order + * @param order order + * @param movementDate optional movement date + * @param forceDelivery ignore order delivery rule + * @param allAttributeInstances if true, all attribute set instances + * @param minGuaranteeDate optional minimum guarantee date if all attribute instances + * @param complete complete document (Process if false, Complete if true) + * @param trxName transaction + * @return Shipment or null + */ + public static MInOut createFrom (MOrder order, Timestamp movementDate, + boolean forceDelivery, boolean allAttributeInstances, Timestamp minGuaranteeDate, + boolean complete, String trxName) + { + if (order == null) + throw new IllegalArgumentException("No Order"); + // + if (!forceDelivery && DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule())) + { + return null; + } + + // Create Meader + MInOut retValue = new MInOut (order, 0, movementDate); + retValue.setDocAction(complete ? DOCACTION_Complete : DOCACTION_Prepare); + + // Check if we can create the lines + MOrderLine[] oLines = order.getLines(true, "M_Product_ID"); + for (int i = 0; i < oLines.length; i++) + { + BigDecimal qty = oLines[i].getQtyOrdered().subtract(oLines[i].getQtyDelivered()); + // Nothing to deliver + if (qty.signum() == 0) + continue; + // Stock Info + MStorage[] storages = null; + MProduct product = oLines[i].getProduct(); + if (product != null && product.get_ID() != 0 && product.isStocked()) + { + String MMPolicy = product.getMMPolicy(); + storages = MStorage.getWarehouse (order.getCtx(), order.getM_Warehouse_ID(), + oLines[i].getM_Product_ID(), oLines[i].getM_AttributeSetInstance_ID(), + product.getM_AttributeSet_ID(), + allAttributeInstances, minGuaranteeDate, + MClient.MMPOLICY_FiFo.equals(MMPolicy), trxName); + } else { + continue; + } + + if (!forceDelivery) + { + BigDecimal maxQty = Env.ZERO; + for (int ll = 0; ll < storages.length; ll++) + maxQty = maxQty.add(storages[ll].getQtyOnHand()); + if (DELIVERYRULE_Availability.equals(order.getDeliveryRule())) + { + if (maxQty.compareTo(qty) < 0) + qty = maxQty; + } + else if (DELIVERYRULE_CompleteLine.equals(order.getDeliveryRule())) + { + if (maxQty.compareTo(qty) < 0) + continue; + } + } + // Create Line + if (retValue.get_ID() == 0) // not saved yet + retValue.save(trxName); + // Create a line until qty is reached + for (int ll = 0; ll < storages.length; ll++) + { + BigDecimal lineQty = storages[ll].getQtyOnHand(); + if (lineQty.compareTo(qty) > 0) + lineQty = qty; + MInOutLine line = new MInOutLine (retValue); + line.setOrderLine(oLines[i], storages[ll].getM_Locator_ID(), + order.isSOTrx() ? lineQty : Env.ZERO); + line.setQty(lineQty); // Correct UOM for QtyEntered + if (oLines[i].getQtyEntered().compareTo(oLines[i].getQtyOrdered()) != 0) + line.setQtyEntered(lineQty + .multiply(oLines[i].getQtyEntered()) + .divide(oLines[i].getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + line.setC_Project_ID(oLines[i].getC_Project_ID()); + line.save(trxName); + // Delivered everything ? + qty = qty.subtract(lineQty); + // storage[ll].changeQtyOnHand(lineQty, !order.isSOTrx()); // Credit Memo not considered + // storage[ll].save(get_TrxName()); + if (qty.signum() == 0) + break; + } + } // for all order lines + + // No Lines saved + if (retValue.get_ID() == 0) + return null; + + return retValue; + } // createFrom + + /** + * Create new Shipment by copying + * @param from shipment + * @param dateDoc date of the document date + * @param C_DocType_ID doc type + * @param isSOTrx sales order + * @param counter create counter links + * @param trxName trx + * @param setOrder set the order link + * @return Shipment + */ + public static MInOut copyFrom (MInOut from, Timestamp dateDoc, + int C_DocType_ID, boolean isSOTrx, boolean counter, String trxName, boolean setOrder) + { + MInOut to = new MInOut (from.getCtx(), 0, null); + to.set_TrxName(trxName); + copyValues(from, to, from.getAD_Client_ID(), from.getAD_Org_ID()); + to.set_ValueNoCheck ("M_InOut_ID", I_ZERO); + to.set_ValueNoCheck ("DocumentNo", null); + // + to.setDocStatus (DOCSTATUS_Drafted); // Draft + to.setDocAction(DOCACTION_Complete); + // + to.setC_DocType_ID (C_DocType_ID); + to.setIsSOTrx(isSOTrx); + if (counter) + to.setMovementType (isSOTrx ? MOVEMENTTYPE_CustomerShipment : MOVEMENTTYPE_VendorReceipts); + // + to.setDateOrdered (dateDoc); + to.setDateAcct (dateDoc); + to.setMovementDate(dateDoc); + to.setDatePrinted(null); + to.setIsPrinted (false); + to.setDateReceived(null); + to.setNoPackages(0); + to.setShipDate(null); + to.setPickDate(null); + to.setIsInTransit(false); + // + to.setIsApproved (false); + to.setC_Invoice_ID(0); + to.setTrackingNo(null); + to.setIsInDispute(false); + // + to.setPosted (false); + to.setProcessed (false); + //[ 1633721 ] Reverse Documents- Processing=Y + to.setProcessing(false); + to.setC_Order_ID(0); // Overwritten by setOrder + to.setM_RMA_ID(0); // Overwritten by setOrder + if (counter) + { + to.setC_Order_ID(0); + to.setRef_InOut_ID(from.getM_InOut_ID()); + // Try to find Order/Invoice link + if (from.getC_Order_ID() != 0) + { + MOrder peer = new MOrder (from.getCtx(), from.getC_Order_ID(), from.get_TrxName()); + if (peer.getRef_Order_ID() != 0) + to.setC_Order_ID(peer.getRef_Order_ID()); + } + if (from.getC_Invoice_ID() != 0) + { + MInvoice peer = new MInvoice (from.getCtx(), from.getC_Invoice_ID(), from.get_TrxName()); + if (peer.getRef_Invoice_ID() != 0) + to.setC_Invoice_ID(peer.getRef_Invoice_ID()); + } + } + else + { + to.setRef_InOut_ID(0); + if (setOrder) + { + to.setC_Order_ID(from.getC_Order_ID()); + to.setM_RMA_ID(from.getM_RMA_ID()); // Copy also RMA + } + } + // + if (!to.save(trxName)) + throw new IllegalStateException("Could not create Shipment"); + if (counter) + from.setRef_InOut_ID(to.getM_InOut_ID()); + + if (to.copyLinesFrom(from, counter, setOrder) == 0) + throw new IllegalStateException("Could not create Shipment Lines"); + + return to; + } // copyFrom + + + /************************************************************************** + * Standard Constructor + * @param ctx context + * @param M_InOut_ID + * @param trxName rx name + */ + public MInOut (Properties ctx, int M_InOut_ID, String trxName) + { + super (ctx, M_InOut_ID, trxName); + if (M_InOut_ID == 0) + { + // setDocumentNo (null); + // setC_BPartner_ID (0); + // setC_BPartner_Location_ID (0); + // setM_Warehouse_ID (0); + // setC_DocType_ID (0); + setIsSOTrx (false); + setMovementDate (new Timestamp (System.currentTimeMillis ())); + setDateAcct (getMovementDate()); + // setMovementType (MOVEMENTTYPE_CustomerShipment); + setDeliveryRule (DELIVERYRULE_Availability); + setDeliveryViaRule (DELIVERYVIARULE_Pickup); + setFreightCostRule (FREIGHTCOSTRULE_FreightIncluded); + setDocStatus (DOCSTATUS_Drafted); + setDocAction (DOCACTION_Complete); + setPriorityRule (PRIORITYRULE_Medium); + setNoPackages(0); + setIsInTransit(false); + setIsPrinted (false); + setSendEMail (false); + setIsInDispute(false); + // + setIsApproved(false); + super.setProcessed (false); + setProcessing(false); + setPosted(false); + } + } // MInOut + + /** + * Load Constructor + * @param ctx context + * @param rs result set record + * @param trxName transaction + */ + public MInOut (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MInOut + + /** + * Order Constructor - create header only + * @param order order + * @param movementDate optional movement date (default today) + * @param C_DocTypeShipment_ID document type or 0 + */ + public MInOut (MOrder order, int C_DocTypeShipment_ID, Timestamp movementDate) + { + this (order.getCtx(), 0, order.get_TrxName()); + setClientOrg(order); + setC_BPartner_ID (order.getC_BPartner_ID()); + setC_BPartner_Location_ID (order.getC_BPartner_Location_ID()); // shipment address + setAD_User_ID(order.getAD_User_ID()); + // + setM_Warehouse_ID (order.getM_Warehouse_ID()); + setIsSOTrx (order.isSOTrx()); + setMovementType (order.isSOTrx() ? MOVEMENTTYPE_CustomerShipment : MOVEMENTTYPE_VendorReceipts); + if (C_DocTypeShipment_ID == 0) + C_DocTypeShipment_ID = DB.getSQLValue(null, + "SELECT C_DocTypeShipment_ID FROM C_DocType WHERE C_DocType_ID=?", + order.getC_DocType_ID()); + setC_DocType_ID (C_DocTypeShipment_ID); + + // Default - Today + if (movementDate != null) + setMovementDate (movementDate); + setDateAcct (getMovementDate()); + + // Copy from Order + setC_Order_ID(order.getC_Order_ID()); + setDeliveryRule (order.getDeliveryRule()); + setDeliveryViaRule (order.getDeliveryViaRule()); + setM_Shipper_ID(order.getM_Shipper_ID()); + setFreightCostRule (order.getFreightCostRule()); + setFreightAmt(order.getFreightAmt()); + setSalesRep_ID(order.getSalesRep_ID()); + // + setC_Activity_ID(order.getC_Activity_ID()); + setC_Campaign_ID(order.getC_Campaign_ID()); + setC_Charge_ID(order.getC_Charge_ID()); + setChargeAmt(order.getChargeAmt()); + // + setC_Project_ID(order.getC_Project_ID()); + setDateOrdered(order.getDateOrdered()); + setDescription(order.getDescription()); + setPOReference(order.getPOReference()); + setSalesRep_ID(order.getSalesRep_ID()); + setAD_OrgTrx_ID(order.getAD_OrgTrx_ID()); + setUser1_ID(order.getUser1_ID()); + setUser2_ID(order.getUser2_ID()); + setPriorityRule(order.getPriorityRule()); + } // MInOut + + /** + * Invoice Constructor - create header only + * @param invoice invoice + * @param C_DocTypeShipment_ID document type or 0 + * @param movementDate optional movement date (default today) + * @param M_Warehouse_ID warehouse + */ + public MInOut (MInvoice invoice, int C_DocTypeShipment_ID, Timestamp movementDate, int M_Warehouse_ID) + { + this (invoice.getCtx(), 0, invoice.get_TrxName()); + setClientOrg(invoice); + setC_BPartner_ID (invoice.getC_BPartner_ID()); + setC_BPartner_Location_ID (invoice.getC_BPartner_Location_ID()); // shipment address + setAD_User_ID(invoice.getAD_User_ID()); + // + setM_Warehouse_ID (M_Warehouse_ID); + setIsSOTrx (invoice.isSOTrx()); + setMovementType (invoice.isSOTrx() ? MOVEMENTTYPE_CustomerShipment : MOVEMENTTYPE_VendorReceipts); + MOrder order = null; + if (invoice.getC_Order_ID() != 0) + order = new MOrder (invoice.getCtx(), invoice.getC_Order_ID(), invoice.get_TrxName()); + if (C_DocTypeShipment_ID == 0 && order != null) + C_DocTypeShipment_ID = DB.getSQLValue(null, + "SELECT C_DocTypeShipment_ID FROM C_DocType WHERE C_DocType_ID=?", + order.getC_DocType_ID()); + if (C_DocTypeShipment_ID != 0) + setC_DocType_ID (C_DocTypeShipment_ID); + else + setC_DocType_ID(); + + // Default - Today + if (movementDate != null) + setMovementDate (movementDate); + setDateAcct (getMovementDate()); + + // Copy from Invoice + setC_Order_ID(invoice.getC_Order_ID()); + setSalesRep_ID(invoice.getSalesRep_ID()); + // + setC_Activity_ID(invoice.getC_Activity_ID()); + setC_Campaign_ID(invoice.getC_Campaign_ID()); + setC_Charge_ID(invoice.getC_Charge_ID()); + setChargeAmt(invoice.getChargeAmt()); + // + setC_Project_ID(invoice.getC_Project_ID()); + setDateOrdered(invoice.getDateOrdered()); + setDescription(invoice.getDescription()); + setPOReference(invoice.getPOReference()); + setAD_OrgTrx_ID(invoice.getAD_OrgTrx_ID()); + setUser1_ID(invoice.getUser1_ID()); + setUser2_ID(invoice.getUser2_ID()); + + if (order != null) + { + setDeliveryRule (order.getDeliveryRule()); + setDeliveryViaRule (order.getDeliveryViaRule()); + setM_Shipper_ID(order.getM_Shipper_ID()); + setFreightCostRule (order.getFreightCostRule()); + setFreightAmt(order.getFreightAmt()); + } + } // MInOut + + /** + * Copy Constructor - create header only + * @param original original + * @param movementDate optional movement date (default today) + * @param C_DocTypeShipment_ID document type or 0 + */ + public MInOut (MInOut original, int C_DocTypeShipment_ID, Timestamp movementDate) + { + this (original.getCtx(), 0, original.get_TrxName()); + setClientOrg(original); + setC_BPartner_ID (original.getC_BPartner_ID()); + setC_BPartner_Location_ID (original.getC_BPartner_Location_ID()); // shipment address + setAD_User_ID(original.getAD_User_ID()); + // + setM_Warehouse_ID (original.getM_Warehouse_ID()); + setIsSOTrx (original.isSOTrx()); + setMovementType (original.getMovementType()); + if (C_DocTypeShipment_ID == 0) + setC_DocType_ID(original.getC_DocType_ID()); + else + setC_DocType_ID (C_DocTypeShipment_ID); + + // Default - Today + if (movementDate != null) + setMovementDate (movementDate); + setDateAcct (getMovementDate()); + + // Copy from Order + setC_Order_ID(original.getC_Order_ID()); + setDeliveryRule (original.getDeliveryRule()); + setDeliveryViaRule (original.getDeliveryViaRule()); + setM_Shipper_ID(original.getM_Shipper_ID()); + setFreightCostRule (original.getFreightCostRule()); + setFreightAmt(original.getFreightAmt()); + setSalesRep_ID(original.getSalesRep_ID()); + // + setC_Activity_ID(original.getC_Activity_ID()); + setC_Campaign_ID(original.getC_Campaign_ID()); + setC_Charge_ID(original.getC_Charge_ID()); + setChargeAmt(original.getChargeAmt()); + // + setC_Project_ID(original.getC_Project_ID()); + setDateOrdered(original.getDateOrdered()); + setDescription(original.getDescription()); + setPOReference(original.getPOReference()); + setSalesRep_ID(original.getSalesRep_ID()); + setAD_OrgTrx_ID(original.getAD_OrgTrx_ID()); + setUser1_ID(original.getUser1_ID()); + setUser2_ID(original.getUser2_ID()); + } // MInOut + + + /** Lines */ + private MInOutLine[] m_lines = null; + /** Confirmations */ + private MInOutConfirm[] m_confirms = null; + /** BPartner */ + private MBPartner m_partner = null; + + + /** + * Get Document Status + * @return Document Status Clear Text + */ + public String getDocStatusName() + { + return MRefList.getListName(getCtx(), 131, getDocStatus()); + } // getDocStatusName + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + /** + * String representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MInOut[") + .append (get_ID()).append("-").append(getDocumentNo()) + .append(",DocStatus=").append(getDocStatus()) + .append ("]"); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.SHIPMENT, getM_InOut_ID(), get_TrxName()); + if (re == null) + return null; + return re.getPDF(file); + } // createPDF + + /** + * Get Lines of Shipment + * @param requery refresh from db + * @return lines + */ + public MInOutLine[] getLines (boolean requery) + { + if (m_lines != null && !requery) { + set_TrxName(m_lines, get_TrxName()); + return m_lines; + } + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_InOutLine WHERE M_InOut_ID=? ORDER BY Line"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getM_InOut_ID()); + rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MInOutLine(getCtx(), rs, get_TrxName())); + rs.close(); + rs = null; + pstmt.close(); + pstmt = null; + } + catch (SQLException ex) + { + log.log(Level.SEVERE, sql, ex); + list = null; + // throw new DBException(ex); + } + finally + { + try + { + if (rs != null) + rs.close(); + if (pstmt != null) + pstmt.close(); + } + catch (SQLException e) + { + } + } + pstmt = null; + rs = null; + // + if (list == null) + return null; + // + m_lines = new MInOutLine[list.size()]; + list.toArray(m_lines); + return m_lines; + } // getMInOutLines + + /** + * Get Lines of Shipment + * @return lines + */ + public MInOutLine[] getLines() + { + return getLines(false); + } // getLines + + + /** + * Get Confirmations + * @param requery requery + * @return array of Confirmations + */ + public MInOutConfirm[] getConfirmations(boolean requery) + { + if (m_confirms != null && !requery) + return m_confirms; + + ArrayList list = new ArrayList (); + String sql = "SELECT * FROM M_InOutConfirm WHERE M_InOut_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, getM_InOut_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + list.add(new MInOutConfirm(getCtx(), rs, get_TrxName())); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + m_confirms = new MInOutConfirm[list.size ()]; + list.toArray (m_confirms); + return m_confirms; + } // getConfirmations + + + /** + * Copy Lines From other Shipment + * @param otherShipment shipment + * @param counter set counter info + * @param setOrder set order link + * @return number of lines copied + */ + public int copyLinesFrom (MInOut otherShipment, boolean counter, boolean setOrder) + { + if (isProcessed() || isPosted() || otherShipment == null) + return 0; + MInOutLine[] fromLines = otherShipment.getLines(false); + int count = 0; + for (int i = 0; i < fromLines.length; i++) + { + MInOutLine line = new MInOutLine (this); + MInOutLine fromLine = fromLines[i]; + line.set_TrxName(get_TrxName()); + if (counter) // header + PO.copyValues(fromLine, line, getAD_Client_ID(), getAD_Org_ID()); + else + PO.copyValues(fromLine, line, fromLine.getAD_Client_ID(), fromLine.getAD_Org_ID()); + line.setM_InOut_ID(getM_InOut_ID()); + line.set_ValueNoCheck ("M_InOutLine_ID", I_ZERO); // new + // Reset + if (!setOrder) + { + line.setC_OrderLine_ID(0); + line.setM_RMALine_ID(0); // Reset RMA Line + } + if (!counter) + line.setM_AttributeSetInstance_ID(0); + // line.setS_ResourceAssignment_ID(0); + line.setRef_InOutLine_ID(0); + line.setIsInvoiced(false); + // + line.setConfirmedQty(Env.ZERO); + line.setPickedQty(Env.ZERO); + line.setScrappedQty(Env.ZERO); + line.setTargetQty(Env.ZERO); + // Set Locator based on header Warehouse + if (getM_Warehouse_ID() != otherShipment.getM_Warehouse_ID()) + { + line.setM_Locator_ID(0); + line.setM_Locator_ID(Env.ZERO); + } + // + if (counter) + { + line.setRef_InOutLine_ID(fromLine.getM_InOutLine_ID()); + if (fromLine.getC_OrderLine_ID() != 0) + { + MOrderLine peer = new MOrderLine (getCtx(), fromLine.getC_OrderLine_ID(), get_TrxName()); + if (peer.getRef_OrderLine_ID() != 0) + line.setC_OrderLine_ID(peer.getRef_OrderLine_ID()); + } + } + // + line.setProcessed(false); + if (line.save(get_TrxName())) + count++; + // Cross Link + if (counter) + { + fromLine.setRef_InOutLine_ID(line.getM_InOutLine_ID()); + fromLine.save(get_TrxName()); + } + } + if (fromLines.length != count) + log.log(Level.SEVERE, "Line difference - From=" + fromLines.length + " <> Saved=" + count); + return count; + } // copyLinesFrom + + /** Reversal Flag */ + private boolean m_reversal = false; + + /** + * Set Reversal + * @param reversal reversal + */ + private void setReversal(boolean reversal) + { + m_reversal = reversal; + } // setReversal + /** + * Is Reversal + * @return reversal + */ + private boolean isReversal() + { + return m_reversal; + } // isReversal + + /** + * Set Processed. + * Propergate to Lines/Taxes + * @param processed processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + if (get_ID() == 0) + return; + String sql = "UPDATE M_InOutLine SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE M_InOut_ID=" + getM_InOut_ID(); + int noLine = DB.executeUpdate(sql, get_TrxName()); + m_lines = null; + log.fine(processed + " - Lines=" + noLine); + } // setProcessed + + /** + * Get BPartner + * @return partner + */ + public MBPartner getBPartner() + { + if (m_partner == null) + m_partner = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + return m_partner; + } // getPartner + + /** + * Set Document Type + * @param DocBaseType doc type MDocType.DOCBASETYPE_ + */ + public void setC_DocType_ID (String DocBaseType) + { + String sql = "SELECT C_DocType_ID FROM C_DocType " + + "WHERE AD_Client_ID=? AND DocBaseType=?" + + " AND IsActive='Y'" + + " AND IsSOTrx='" + (isSOTrx() ? "Y" : "N") + "' " + + "ORDER BY IsDefault DESC"; + int C_DocType_ID = DB.getSQLValue(null, sql, getAD_Client_ID(), DocBaseType); + if (C_DocType_ID <= 0) + log.log(Level.SEVERE, "Not found for AC_Client_ID=" + + getAD_Client_ID() + " - " + DocBaseType); + else + { + log.fine("DocBaseType=" + DocBaseType + " - C_DocType_ID=" + C_DocType_ID); + setC_DocType_ID (C_DocType_ID); + boolean isSOTrx = MDocType.DOCBASETYPE_MaterialDelivery.equals(DocBaseType); + setIsSOTrx (isSOTrx); + } + } // setC_DocType_ID + + /** + * Set Default C_DocType_ID. + * Based on SO flag + */ + public void setC_DocType_ID() + { + if (isSOTrx()) + setC_DocType_ID(MDocType.DOCBASETYPE_MaterialDelivery); + else + setC_DocType_ID(MDocType.DOCBASETYPE_MaterialReceipt); + } // setC_DocType_ID + + /** + * Set Business Partner Defaults & Details + * @param bp business partner + */ + public void setBPartner (MBPartner bp) + { + if (bp == null) + return; + + setC_BPartner_ID(bp.getC_BPartner_ID()); + + // Set Locations + MBPartnerLocation[] locs = bp.getLocations(false); + if (locs != null) + { + for (int i = 0; i < locs.length; i++) + { + if (locs[i].isShipTo()) + setC_BPartner_Location_ID(locs[i].getC_BPartner_Location_ID()); + } + // set to first if not set + if (getC_BPartner_Location_ID() == 0 && locs.length > 0) + setC_BPartner_Location_ID(locs[0].getC_BPartner_Location_ID()); + } + if (getC_BPartner_Location_ID() == 0) + log.log(Level.SEVERE, "Has no To Address: " + bp); + + // Set Contact + MUser[] contacts = bp.getContacts(false); + if (contacts != null && contacts.length > 0) // get first User + setAD_User_ID(contacts[0].getAD_User_ID()); + } // setBPartner + + /** + * Create the missing next Confirmation + */ + public void createConfirmation() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + boolean pick = dt.isPickQAConfirm(); + boolean ship = dt.isShipConfirm(); + // Nothing to do + if (!pick && !ship) + { + log.fine("No need"); + return; + } + + // Create Both .. after each other + if (pick && ship) + { + boolean havePick = false; + boolean haveShip = false; + MInOutConfirm[] confirmations = getConfirmations(false); + for (int i = 0; i < confirmations.length; i++) + { + MInOutConfirm confirm = confirmations[i]; + if (MInOutConfirm.CONFIRMTYPE_PickQAConfirm.equals(confirm.getConfirmType())) + { + if (!confirm.isProcessed()) // wait intil done + { + log.fine("Unprocessed: " + confirm); + return; + } + havePick = true; + } + else if (MInOutConfirm.CONFIRMTYPE_ShipReceiptConfirm.equals(confirm.getConfirmType())) + haveShip = true; + } + // Create Pick + if (!havePick) + { + MInOutConfirm.create (this, MInOutConfirm.CONFIRMTYPE_PickQAConfirm, false); + return; + } + // Create Ship + if (!haveShip) + { + MInOutConfirm.create (this, MInOutConfirm.CONFIRMTYPE_ShipReceiptConfirm, false); + return; + } + return; + } + // Create just one + if (pick) + MInOutConfirm.create (this, MInOutConfirm.CONFIRMTYPE_PickQAConfirm, true); + else if (ship) + MInOutConfirm.create (this, MInOutConfirm.CONFIRMTYPE_ShipReceiptConfirm, true); + } // createConfirmation + + + /** + * Set Warehouse and check/set Organization + * @param M_Warehouse_ID id + */ + public void setM_Warehouse_ID (int M_Warehouse_ID) + { + if (M_Warehouse_ID == 0) + { + log.severe("Ignored - Cannot set AD_Warehouse_ID to 0"); + return; + } + super.setM_Warehouse_ID (M_Warehouse_ID); + // + MWarehouse wh = MWarehouse.get(getCtx(), getM_Warehouse_ID()); + if (wh.getAD_Org_ID() != getAD_Org_ID()) + { + log.warning("M_Warehouse_ID=" + M_Warehouse_ID + + ", Overwritten AD_Org_ID=" + getAD_Org_ID() + "->" + wh.getAD_Org_ID()); + setAD_Org_ID(wh.getAD_Org_ID()); + } + } // setM_Warehouse_ID + + + /** + * Before Save + * @param newRecord new + * @return true or false + */ + protected boolean beforeSave (boolean newRecord) + { + // Warehouse Org + if (newRecord) + { + MWarehouse wh = MWarehouse.get(getCtx(), getM_Warehouse_ID()); + if (wh.getAD_Org_ID() != getAD_Org_ID()) + { + log.saveError("WarehouseOrgConflict", ""); + return false; + } + } + + // Shipment/Receipt can have either Order/RMA (For Movement type) + if (getC_Order_ID() != 0 && getM_RMA_ID() != 0) + { + log.saveError("OrderOrRMA", ""); + return false; + } + + // Shipment - Needs Order/RMA + if (!getMovementType().contentEquals(MInOut.MOVEMENTTYPE_CustomerReturns) && isSOTrx() && getC_Order_ID() == 0 && getM_RMA_ID() == 0) + { + log.saveError("FillMandatory", Msg.translate(getCtx(), "C_Order_ID")); + return false; + } + + if (isSOTrx() && getM_RMA_ID() != 0) + { + // Set Document and Movement type for this Receipt + MRMA rma = new MRMA(getCtx(), getM_RMA_ID(), get_TrxName()); + MDocType docType = MDocType.get(getCtx(), rma.getC_DocType_ID()); + setC_DocType_ID(docType.getC_DocTypeShipment_ID()); + } + + return true; + } // beforeSave + + /** + * After Save + * @param newRecord new + * @param success success + * @return success + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + if (!success || newRecord) + return success; + + if (is_ValueChanged("AD_Org_ID")) + { + String sql = "UPDATE M_InOutLine ol" + + " SET AD_Org_ID =" + + "(SELECT AD_Org_ID" + + " FROM M_InOut o WHERE ol.M_InOut_ID=o.M_InOut_ID) " + + "WHERE M_InOut_ID=" + getC_Order_ID(); + int no = DB.executeUpdate(sql, get_TrxName()); + log.fine("Lines -> #" + no); + } + return true; + } // afterSave + + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // process + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info(toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + /** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + + // Order OR RMA can be processed on a shipment/receipt + if (getC_Order_ID() != 0 && getM_RMA_ID() != 0) + { + m_processMsg = "@OrderOrRMA@"; + return DocAction.STATUS_Invalid; + } + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getDateAcct(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + + // Credit Check + if (isSOTrx() && !isReversal()) + { + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus())) + { + m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus())) + { + m_processMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + BigDecimal notInvoicedAmt = MBPartner.getNotInvoicedAmt(getC_BPartner_ID()); + if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus(notInvoicedAmt))) + { + m_processMsg = "@BPartnerOverSCreditHold@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + ", @NotInvoicedAmt@=" + notInvoicedAmt + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + } + + // Lines + MInOutLine[] lines = getLines(true); + if (lines == null || lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + BigDecimal Volume = Env.ZERO; + BigDecimal Weight = Env.ZERO; + + // Mandatory Attributes + for (int i = 0; i < lines.length; i++) + { + MInOutLine line = lines[i]; + MProduct product = line.getProduct(); + if (product != null) + { + Volume = Volume.add(product.getVolume().multiply(line.getMovementQty())); + Weight = Weight.add(product.getWeight().multiply(line.getMovementQty())); + } + // + if (line.getM_AttributeSetInstance_ID() != 0) + continue; + if (product != null && product.isASIMandatory(isSOTrx())) + { + m_processMsg = "@M_AttributeSet_ID@ @IsMandatory@ (@Line@ #" + lines[i].getLine() + + ", @M_Product_ID@=" + product.getValue() + ")"; + return DocAction.STATUS_Invalid; + } + } + setVolume(Volume); + setWeight(Weight); + + if (!isReversal()) // don't change reversal + { + createConfirmation(); + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + if (!DOCACTION_Complete.equals(getDocAction())) + setDocAction(DOCACTION_Complete); + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info(toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info(toString()); + setIsApproved(false); + return true; + } // rejectIt + + /** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Outstanding (not processed) Incoming Confirmations ? + MInOutConfirm[] confirmations = getConfirmations(true); + for (int i = 0; i < confirmations.length; i++) + { + MInOutConfirm confirm = confirmations[i]; + if (!confirm.isProcessed()) + { + if (MInOutConfirm.CONFIRMTYPE_CustomerConfirmation.equals(confirm.getConfirmType())) + continue; + // + m_processMsg = "Open @M_InOutConfirm_ID@: " + + confirm.getConfirmTypeName() + " - " + confirm.getDocumentNo(); + return DocAction.STATUS_InProgress; + } + } + + + // Implicit Approval + if (!isApproved()) + approveIt(); + log.info(toString()); + StringBuffer info = new StringBuffer(); + + // For all lines + MInOutLine[] lines = getLines(false); + for (int lineIndex = 0; lineIndex < lines.length; lineIndex++) + { + MInOutLine sLine = lines[lineIndex]; + MProduct product = sLine.getProduct(); + + // Qty & Type + String MovementType = getMovementType(); + BigDecimal Qty = sLine.getMovementQty(); + if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return + Qty = Qty.negate(); + BigDecimal QtySO = Env.ZERO; + BigDecimal QtyPO = Env.ZERO; + + // Update Order Line + MOrderLine oLine = null; + if (sLine.getC_OrderLine_ID() != 0) + { + oLine = new MOrderLine (getCtx(), sLine.getC_OrderLine_ID(), get_TrxName()); + log.fine("OrderLine - Reserved=" + oLine.getQtyReserved() + + ", Delivered=" + oLine.getQtyDelivered()); + if (isSOTrx()) + QtySO = sLine.getMovementQty(); + else + QtyPO = sLine.getMovementQty(); + } + + + // Load RMA Line + MRMALine rmaLine = null; + + if (sLine.getM_RMALine_ID() != 0) + { + rmaLine = new MRMALine(getCtx(), sLine.getM_RMALine_ID(), get_TrxName()); + } + + log.info("Line=" + sLine.getLine() + " - Qty=" + sLine.getMovementQty()); + + checkMaterialPolicy(sLine); + // Stock Movement - Counterpart MOrder.reserveStock + if (product != null + && product.isStocked() ) + { + log.fine("Material Transaction"); + MTransaction mtrx = null; + //same warehouse in order and receipt? + boolean sameWarehouse = true; + // Reservation ASI - assume none + int reservationAttributeSetInstance_ID = 0; // sLine.getM_AttributeSetInstance_ID(); + if (oLine != null) { + reservationAttributeSetInstance_ID = oLine.getM_AttributeSetInstance_ID(); + sameWarehouse = oLine.getM_Warehouse_ID()==getM_Warehouse_ID(); + } + // + if (sLine.getM_AttributeSetInstance_ID() == 0) + { + MInOutLineMA mas[] = MInOutLineMA.get(getCtx(), + sLine.getM_InOutLine_ID(), get_TrxName()); + for (int j = 0; j < mas.length; j++) + { + MInOutLineMA ma = mas[j]; + BigDecimal QtyMA = ma.getMovementQty(); + if (MovementType.charAt(1) == '-') // C- Customer Shipment - V- Vendor Return + QtyMA = QtyMA.negate(); + BigDecimal QtySOMA = Env.ZERO; + BigDecimal QtyPOMA = Env.ZERO; + if (sLine.getC_OrderLine_ID() != 0) + { + if (isSOTrx()) + QtySOMA = ma.getMovementQty(); + else + QtyPOMA = ma.getMovementQty(); + } + BigDecimal diffQtyOrdered = QtyPOMA.negate(); + if (!sameWarehouse) { + diffQtyOrdered = Env.ZERO; + } + // Update Storage - see also VMatch.createMatchRecord + if (!MStorage.add(getCtx(), getM_Warehouse_ID(), + sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), + ma.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, + QtyMA, QtySOMA.negate(), diffQtyOrdered, get_TrxName())) + { + m_processMsg = "Cannot correct Inventory (MA)"; + return DocAction.STATUS_Invalid; + } + if (!sameWarehouse) { + //correct qtyOrdered in warehouse of order + MWarehouse wh = MWarehouse.get(getCtx(), oLine.getM_Warehouse_ID()); + if (!MStorage.add(getCtx(), oLine.getM_Warehouse_ID(), + wh.getDefaultLocator().getM_Locator_ID(), + sLine.getM_Product_ID(), + ma.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, + Env.ZERO, Env.ZERO, QtyPOMA.negate(), get_TrxName())) + { + m_processMsg = "Cannot correct Inventory (MA) in order warehouse"; + return DocAction.STATUS_Invalid; + } + } + // Create Transaction + mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), + MovementType, sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + QtyMA, getMovementDate(), get_TrxName()); + mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); + if (!mtrx.save()) + { + m_processMsg = "Could not create Material Transaction (MA)"; + return DocAction.STATUS_Invalid; + } + } + } + // sLine.getM_AttributeSetInstance_ID() != 0 + if (mtrx == null) + { + BigDecimal diffQtyOrdered = QtyPO.negate(); + if (!sameWarehouse) { + diffQtyOrdered = Env.ZERO; + } + // Fallback: Update Storage - see also VMatch.createMatchRecord + if (!MStorage.add(getCtx(), getM_Warehouse_ID(), + sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), + sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, + Qty, QtySO.negate(), diffQtyOrdered, get_TrxName())) + { + m_processMsg = "Cannot correct Inventory"; + return DocAction.STATUS_Invalid; + } + if (!sameWarehouse) { + //correct qtyOrdered in warehouse of order + MWarehouse wh = MWarehouse.get(getCtx(), oLine.getM_Warehouse_ID()); + if (!MStorage.add(getCtx(), oLine.getM_Warehouse_ID(), + wh.getDefaultLocator().getM_Locator_ID(), + sLine.getM_Product_ID(), + sLine.getM_AttributeSetInstance_ID(), reservationAttributeSetInstance_ID, + Env.ZERO, Env.ZERO, QtyPO.negate(), get_TrxName())) + { + m_processMsg = "Cannot correct Inventory"; + return DocAction.STATUS_Invalid; + } + } + // FallBack: Create Transaction + mtrx = new MTransaction (getCtx(), sLine.getAD_Org_ID(), + MovementType, sLine.getM_Locator_ID(), + sLine.getM_Product_ID(), sLine.getM_AttributeSetInstance_ID(), + Qty, getMovementDate(), get_TrxName()); + mtrx.setM_InOutLine_ID(sLine.getM_InOutLine_ID()); + if (!mtrx.save()) + { + m_processMsg = "Could not create Material Transaction"; + return DocAction.STATUS_Invalid; + } + } + } // stock movement + + // Correct Order Line + if (product != null && oLine != null) // other in VMatch.createMatchRecord + oLine.setQtyReserved(oLine.getQtyReserved().subtract(sLine.getMovementQty())); + + // Update Sales Order Line + if (oLine != null) + { + if (isSOTrx() // PO is done by Matching + || sLine.getM_Product_ID() == 0) // PO Charges, empty lines + { + if (isSOTrx()) + oLine.setQtyDelivered(oLine.getQtyDelivered().subtract(Qty)); + else + oLine.setQtyDelivered(oLine.getQtyDelivered().add(Qty)); + oLine.setDateDelivered(getMovementDate()); // overwrite=last + } + if (!oLine.save()) + { + m_processMsg = "Could not update Order Line"; + return DocAction.STATUS_Invalid; + } + else + log.fine("OrderLine -> Reserved=" + oLine.getQtyReserved() + + ", Delivered=" + oLine.getQtyReserved()); + } + // Update RMA Line Qty Delivered + else if (rmaLine != null) + { + if (isSOTrx()) + { + rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().add(Qty)); + } + else + { + rmaLine.setQtyDelivered(rmaLine.getQtyDelivered().subtract(Qty)); + } + if (!rmaLine.save()) + { + m_processMsg = "Could not update RMA Line"; + return DocAction.STATUS_Invalid; + } + } + + // Create Asset for SO + if (product != null + && isSOTrx() + && product.isCreateAsset() + && sLine.getMovementQty().signum() > 0 + && !isReversal()) + { + log.fine("Asset"); + info.append("@A_Asset_ID@: "); + int noAssets = sLine.getMovementQty().intValue(); + if (!product.isOneAssetPerUOM()) + noAssets = 1; + for (int i = 0; i < noAssets; i++) + { + if (i > 0) + info.append(" - "); + int deliveryCount = i+1; + if (!product.isOneAssetPerUOM()) + deliveryCount = 0; + MAsset asset = new MAsset (this, sLine, deliveryCount); + if (!asset.save(get_TrxName())) + { + m_processMsg = "Could not create Asset"; + return DocAction.STATUS_Invalid; + } + info.append(asset.getValue()); + } + } // Asset + + + // Matching + if (!isSOTrx() + && sLine.getM_Product_ID() != 0 + && !isReversal()) + { + BigDecimal matchQty = sLine.getMovementQty(); + // Invoice - Receipt Match (requires Product) + MInvoiceLine iLine = MInvoiceLine.getOfInOutLine (sLine); + if (iLine != null && iLine.getM_Product_ID() != 0) + { + if (matchQty.compareTo(iLine.getQtyInvoiced())>0) + matchQty = iLine.getQtyInvoiced(); + + MMatchInv[] matches = MMatchInv.get(getCtx(), + sLine.getM_InOutLine_ID(), iLine.getC_InvoiceLine_ID(), get_TrxName()); + if (matches == null || matches.length == 0) + { + MMatchInv inv = new MMatchInv (iLine, getMovementDate(), matchQty); + if (sLine.getM_AttributeSetInstance_ID() != iLine.getM_AttributeSetInstance_ID()) + { + iLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + iLine.save(); // update matched invoice with ASI + inv.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + } + if (!inv.save(get_TrxName())) + { + m_processMsg = "Could not create Inv Matching"; + return DocAction.STATUS_Invalid; + } + } + } + + // Link to Order + if (sLine.getC_OrderLine_ID() != 0) + { + log.fine("PO Matching"); + // Ship - PO + MMatchPO po = MMatchPO.create (null, sLine, getMovementDate(), matchQty); + if (!po.save(get_TrxName())) + { + m_processMsg = "Could not create PO Matching"; + return DocAction.STATUS_Invalid; + } + // Update PO with ASI + if ( oLine != null && oLine.getM_AttributeSetInstance_ID() == 0 + && sLine.getMovementQty().compareTo(oLine.getQtyOrdered()) == 0) // just if full match [ 1876965 ] + { + oLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + oLine.save(get_TrxName()); + } + } + else // No Order - Try finding links via Invoice + { + // Invoice has an Order Link + if (iLine != null && iLine.getC_OrderLine_ID() != 0) + { + // Invoice is created before Shipment + log.fine("PO(Inv) Matching"); + // Ship - Invoice + MMatchPO po = MMatchPO.create (iLine, sLine, + getMovementDate(), matchQty); + if (!po.save(get_TrxName())) + { + m_processMsg = "Could not create PO(Inv) Matching"; + return DocAction.STATUS_Invalid; + } + // Update PO with ASI + oLine = new MOrderLine (getCtx(), po.getC_OrderLine_ID(), get_TrxName()); + if ( oLine != null && oLine.getM_AttributeSetInstance_ID() == 0 + && sLine.getMovementQty().compareTo(oLine.getQtyOrdered()) == 0) // just if full match [ 1876965 ] + { + oLine.setM_AttributeSetInstance_ID(sLine.getM_AttributeSetInstance_ID()); + oLine.save(get_TrxName()); + } + } + } // No Order + } // PO Matching + + } // for all lines + + // Counter Documents + MInOut counter = createCounterDoc(); + if (counter != null) + info.append(" - @CounterDoc@: @M_InOut_ID@=").append(counter.getDocumentNo()); + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + m_processMsg = info.toString(); + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setMovementDate(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Check Material Policy + * Sets line ASI + */ + private void checkMaterialPolicy(MInOutLine line) + { + int no = MInOutLineMA.deleteInOutLineMA(line.getM_InOutLine_ID(), get_TrxName()); + if (no > 0) + log.config("Delete old #" + no); + + // Incoming Trx + String MovementType = getMovementType(); + boolean inTrx = MovementType.charAt(1) == '+'; // V+ Vendor Receipt + + boolean needSave = false; + MProduct product = line.getProduct(); + + // Need to have Location + if (product != null + && line.getM_Locator_ID() == 0) + { + line.setM_Warehouse_ID(getM_Warehouse_ID()); + line.setM_Locator_ID(inTrx ? Env.ZERO : line.getMovementQty()); // default Locator + needSave = true; + } + + // Attribute Set Instance + if (product != null + && line.getM_AttributeSetInstance_ID() == 0) + { + if (inTrx) + { + MAttributeSetInstance asi = new MAttributeSetInstance(getCtx(), 0, get_TrxName()); + asi.setClientOrg(getAD_Client_ID(), 0); + asi.setM_AttributeSet_ID(product.getM_AttributeSet_ID()); + if (asi.save()) + { + line.setM_AttributeSetInstance_ID(asi.getM_AttributeSetInstance_ID()); + log.config("New ASI=" + line); + needSave = true; + } + } + else // Outgoing Trx + { + String MMPolicy = product.getMMPolicy(); + MStorage[] storages = MStorage.getAllWithASI(getCtx(), + line.getM_Product_ID(), line.getM_Locator_ID(), + MClient.MMPOLICY_FiFo.equals(MMPolicy), get_TrxName()); + BigDecimal qtyToDeliver = line.getMovementQty(); + for (int ii = 0; ii < storages.length; ii++) + { + MStorage storage = storages[ii]; + if (ii == 0) + { + if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) + { + line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); + needSave = true; + log.config("Direct - " + line); + qtyToDeliver = Env.ZERO; + } + else + { + log.config("Split - " + line); + MInOutLineMA ma = new MInOutLineMA (line, + storage.getM_AttributeSetInstance_ID(), + storage.getQtyOnHand()); + if (!ma.save()) + ; + qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); + log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); + } + } + else // create addl material allocation + { + MInOutLineMA ma = new MInOutLineMA (line, + storage.getM_AttributeSetInstance_ID(), + qtyToDeliver); + if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) + qtyToDeliver = Env.ZERO; + else + { + ma.setMovementQty(storage.getQtyOnHand()); + qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); + } + if (!ma.save()) + ; + log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); + } + if (qtyToDeliver.signum() == 0) + break; + } // for all storages + + // No AttributeSetInstance found for remainder + if (qtyToDeliver.signum() != 0) + { + MInOutLineMA ma = new MInOutLineMA (line, + 0, qtyToDeliver); + if (!ma.save()) + ; + log.fine("##: " + ma); + } + } // outgoing Trx + } // attributeSetInstance + + if (needSave && !line.save()) + log.severe("NOT saved " + line); + } // checkMaterialPolicy + + + /************************************************************************** + * Create Counter Document + * @return InOut + */ + private MInOut createCounterDoc() + { + // Is this a counter doc ? + if (getRef_InOut_ID() != 0) + return null; + + // Org Must be linked to BPartner + MOrg org = MOrg.get(getCtx(), getAD_Org_ID()); + int counterC_BPartner_ID = org.getLinkedC_BPartner_ID(get_TrxName()); + if (counterC_BPartner_ID == 0) + return null; + // Business Partner needs to be linked to Org + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + int counterAD_Org_ID = bp.getAD_OrgBP_ID_Int(); + if (counterAD_Org_ID == 0) + return null; + + MBPartner counterBP = new MBPartner (getCtx(), counterC_BPartner_ID, null); + MOrgInfo counterOrgInfo = MOrgInfo.get(getCtx(), counterAD_Org_ID); + log.info("Counter BP=" + counterBP.getName()); + + // Document Type + int C_DocTypeTarget_ID = 0; + MDocTypeCounter counterDT = MDocTypeCounter.getCounterDocType(getCtx(), getC_DocType_ID()); + if (counterDT != null) + { + log.fine(counterDT.toString()); + if (!counterDT.isCreateCounter() || !counterDT.isValid()) + return null; + C_DocTypeTarget_ID = counterDT.getCounter_C_DocType_ID(); + } + else // indirect + { + C_DocTypeTarget_ID = MDocTypeCounter.getCounterDocType_ID(getCtx(), getC_DocType_ID()); + log.fine("Indirect C_DocTypeTarget_ID=" + C_DocTypeTarget_ID); + if (C_DocTypeTarget_ID <= 0) + return null; + } + + // Deep Copy + MInOut counter = copyFrom(this, getMovementDate(), + C_DocTypeTarget_ID, !isSOTrx(), true, get_TrxName(), true); + + // + counter.setAD_Org_ID(counterAD_Org_ID); + counter.setM_Warehouse_ID(counterOrgInfo.getM_Warehouse_ID()); + // + counter.setBPartner(counterBP); + // Refernces (Should not be required + counter.setSalesRep_ID(getSalesRep_ID()); + counter.save(get_TrxName()); + + String MovementType = counter.getMovementType(); + boolean inTrx = MovementType.charAt(1) == '+'; // V+ Vendor Receipt + + // Update copied lines + MInOutLine[] counterLines = counter.getLines(true); + for (int i = 0; i < counterLines.length; i++) + { + MInOutLine counterLine = counterLines[i]; + counterLine.setClientOrg(counter); + counterLine.setM_Warehouse_ID(counter.getM_Warehouse_ID()); + counterLine.setM_Locator_ID(0); + counterLine.setM_Locator_ID(inTrx ? Env.ZERO : counterLine.getMovementQty()); + // + counterLine.save(get_TrxName()); + } + + log.fine(counter.toString()); + + // Document Action + if (counterDT != null) + { + if (counterDT.getDocAction() != null) + { + counter.setDocAction(counterDT.getDocAction()); + counter.processIt(counterDT.getDocAction()); + counter.save(get_TrxName()); + } + } + return counter; + } // createCounterDoc + + /** + * Void Document. + * @return true if success + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + if (DOCSTATUS_Closed.equals(getDocStatus()) + || DOCSTATUS_Reversed.equals(getDocStatus()) + || DOCSTATUS_Voided.equals(getDocStatus())) + { + m_processMsg = "Document Closed: " + getDocStatus(); + return false; + } + + // Not Processed + if (DOCSTATUS_Drafted.equals(getDocStatus()) + || DOCSTATUS_Invalid.equals(getDocStatus()) + || DOCSTATUS_InProgress.equals(getDocStatus()) + || DOCSTATUS_Approved.equals(getDocStatus()) + || DOCSTATUS_NotApproved.equals(getDocStatus()) ) + { + // Set lines to 0 + MInOutLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInOutLine line = lines[i]; + BigDecimal old = line.getMovementQty(); + if (old.signum() != 0) + { + line.setQty(Env.ZERO); + line.addDescription("Void (" + old + ")"); + line.save(get_TrxName()); + } + } + } + else + { + return reverseCorrectIt(); + } + + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // voidIt + + /** + * Close Document. + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // closeIt + + /** + * Reverse Correction - same date + * @return true if success + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (!MPeriod.isOpen(getCtx(), getDateAcct(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return false; + } + + // Reverse/Delete Matching + if (!isSOTrx()) + { + MMatchInv[] mInv = MMatchInv.getInOut(getCtx(), getM_InOut_ID(), get_TrxName()); + for (int i = 0; i < mInv.length; i++) + mInv[i].delete(true); + MMatchPO[] mPO = MMatchPO.getInOut(getCtx(), getM_InOut_ID(), get_TrxName()); + for (int i = 0; i < mPO.length; i++) + { + if (mPO[i].getC_InvoiceLine_ID() == 0) + mPO[i].delete(true); + else + { + mPO[i].setM_InOutLine_ID(0); + mPO[i].save(); + + } + } + } + + // Deep Copy + MInOut reversal = copyFrom (this, getMovementDate(), + getC_DocType_ID(), isSOTrx(), false, get_TrxName(), true); + if (reversal == null) + { + m_processMsg = "Could not create Ship Reversal"; + return false; + } + reversal.setReversal(true); + + // Reverse Line Qty + MInOutLine[] sLines = getLines(false); + MInOutLine[] rLines = reversal.getLines(false); + for (int i = 0; i < rLines.length; i++) + { + MInOutLine rLine = rLines[i]; + rLine.setQtyEntered(rLine.getQtyEntered().negate()); + rLine.setMovementQty(rLine.getMovementQty().negate()); + rLine.setM_AttributeSetInstance_ID(sLines[i].getM_AttributeSetInstance_ID()); + if (!rLine.save(get_TrxName())) + { + m_processMsg = "Could not correct Ship Reversal Line"; + return false; + } + // We need to copy MA + if (rLine.getM_AttributeSetInstance_ID() == 0) + { + MInOutLineMA mas[] = MInOutLineMA.get(getCtx(), + sLines[i].getM_InOutLine_ID(), get_TrxName()); + for (int j = 0; j < mas.length; j++) + { + MInOutLineMA ma = new MInOutLineMA (rLine, + mas[j].getM_AttributeSetInstance_ID(), + mas[j].getMovementQty().negate()); + if (!ma.save()) + ; + } + } + // De-Activate Asset + MAsset asset = MAsset.getFromShipment(getCtx(), sLines[i].getM_InOutLine_ID(), get_TrxName()); + if (asset != null) + { + asset.setIsActive(false); + asset.addDescription("(" + reversal.getDocumentNo() + " #" + rLine.getLine() + "<-)"); + asset.save(); + } + } + reversal.setC_Order_ID(getC_Order_ID()); + // Set M_RMA_ID + reversal.setM_RMA_ID(getM_RMA_ID()); + reversal.addDescription("{->" + getDocumentNo() + ")"); + // + if (!reversal.processIt(DocAction.ACTION_Complete) + || !reversal.getDocStatus().equals(DocAction.STATUS_Completed)) + { + m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg(); + return false; + } + reversal.closeIt(); + reversal.setProcessing (false); + reversal.setDocStatus(DOCSTATUS_Reversed); + reversal.setDocAction(DOCACTION_None); + reversal.save(get_TrxName()); + // + addDescription("(" + reversal.getDocumentNo() + "<-)"); + + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + m_processMsg = reversal.getDocumentNo(); + setProcessed(true); + setDocStatus(DOCSTATUS_Reversed); // may come from void + setDocAction(DOCACTION_None); + return true; + } // reverseCorrectionIt + + /** + * Reverse Accrual - none + * @return false + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return false; + } // reverseAccrualIt + + /** + * Re-activate + * @return false + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + return false; + } // reActivateIt + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Total Lines = 123.00 (#1) + sb.append(":") + // .append(Msg.translate(getCtx(),"TotalLines")).append("=").append(getTotalLines()) + .append(" (#").append(getLines(false).length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getSalesRep_ID(); + } // getDoc_User_ID + + /** + * Get Document Approval Amount + * @return amount + */ + public BigDecimal getApprovalAmt() + { + return Env.ZERO; + } // getApprovalAmt + + /** + * Get C_Currency_ID + * @return Accounting Currency + */ + public int getC_Currency_ID () + { + return Env.getContextAsInt(getCtx(),"$C_Currency_ID"); + } // getC_Currency_ID + + /** + * Document Status is Complete or Closed + * @return true if CO, CL or RE + */ + public boolean isComplete() + { + String ds = getDocStatus(); + return DOCSTATUS_Completed.equals(ds) + || DOCSTATUS_Closed.equals(ds) + || DOCSTATUS_Reversed.equals(ds); + } // isComplete + +} // MInOut diff --git a/base/src/org/compiere/model/MInventory.java b/base/src/org/compiere/model/MInventory.java new file mode 100644 index 0000000000..7bf79d52eb --- /dev/null +++ b/base/src/org/compiere/model/MInventory.java @@ -0,0 +1,972 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.process.*; +import org.compiere.util.*; + +/** + * Physical Inventory Model + * + * @author Jorg Janke + * @version $Id: MInventory.java,v 1.3 2006/07/30 00:51:05 jjanke Exp $ + */ +public class MInventory extends X_M_Inventory implements DocAction +{ + /** + * Get Inventory from Cache + * @param ctx context + * @param M_Inventory_ID id + * @return MInventory + */ + public static MInventory get (Properties ctx, int M_Inventory_ID) + { + Integer key = new Integer (M_Inventory_ID); + MInventory retValue = (MInventory) s_cache.get (key); + if (retValue != null) + return retValue; + retValue = new MInventory (ctx, M_Inventory_ID, null); + if (retValue.get_ID () != 0) + s_cache.put (key, retValue); + return retValue; + } // get + + /** Cache */ + private static CCache s_cache = new CCache("M_Inventory", 5, 5); + + + /** + * Standard Constructor + * @param ctx context + * @param M_Inventory_ID id + * @param trxName transaction + */ + public MInventory (Properties ctx, int M_Inventory_ID, String trxName) + { + super (ctx, M_Inventory_ID, trxName); + if (M_Inventory_ID == 0) + { + // setName (null); + // setM_Warehouse_ID (0); // FK + setMovementDate (new Timestamp(System.currentTimeMillis())); + setDocAction (DOCACTION_Complete); // CO + setDocStatus (DOCSTATUS_Drafted); // DR + setIsApproved (false); + setMovementDate (new Timestamp(System.currentTimeMillis())); // @#Date@ + setPosted (false); + setProcessed (false); + } + } // MInventory + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MInventory (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MInventory + + /** + * Warehouse Constructor + * @param wh warehouse + */ + public MInventory (MWarehouse wh) + { + this (wh.getCtx(), 0, wh.get_TrxName()); + setClientOrg(wh); + setM_Warehouse_ID(wh.getM_Warehouse_ID()); + } // MInventory + + + /** Lines */ + private MInventoryLine[] m_lines = null; + + /** + * Get Lines + * @param requery requery + * @return array of lines + */ + public MInventoryLine[] getLines (boolean requery) + { + if (m_lines != null && !requery) { + set_TrxName(m_lines, get_TrxName()); + return m_lines; + } + // + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_InventoryLine WHERE M_Inventory_ID=? ORDER BY Line"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, getM_Inventory_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + list.add (new MInventoryLine (getCtx(), rs, get_TrxName())); + rs.close (); + pstmt.close (); + pstmt = null; + } catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } catch (Exception e) + { + pstmt = null; + } + + m_lines = new MInventoryLine[list.size ()]; + list.toArray (m_lines); + return m_lines; + } // getLines + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + /** + * Overwrite Client/Org - from Import. + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + public void setClientOrg (int AD_Client_ID, int AD_Org_ID) + { + super.setClientOrg(AD_Client_ID, AD_Org_ID); + } // setClientOrg + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MInventory["); + sb.append (get_ID()) + .append ("-").append (getDocumentNo()) + .append (",M_Warehouse_ID=").append(getM_Warehouse_ID()) + .append ("]"); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + // ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID()); + // if (re == null) + return null; + // return re.getPDF(file); + } // createPDF + + + /** + * Before Save + * @param newRecord new + * @return true + */ + protected boolean beforeSave (boolean newRecord) + { + if (getC_DocType_ID() == 0) + { + MDocType types[] = MDocType.getOfDocBaseType(getCtx(), MDocType.DOCBASETYPE_MaterialPhysicalInventory); + if (types.length > 0) // get first + setC_DocType_ID(types[0].getC_DocType_ID()); + else + { + log.saveError("Error", Msg.parseTranslation(getCtx(), "@NotFound@ @C_DocType_ID@")); + return false; + } + } + return true; + } // beforeSave + + + /** + * Set Processed. + * Propergate to Lines/Taxes + * @param processed processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + if (get_ID() == 0) + return; + String sql = "UPDATE M_InventoryLine SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE M_Inventory_ID=" + getM_Inventory_ID(); + int noLine = DB.executeUpdate(sql, get_TrxName()); + m_lines = null; + log.fine("Processed=" + processed + " - Lines=" + noLine); + } // setProcessed + + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // processIt + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info(toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + /** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getMovementDate(), MDocType.DOCBASETYPE_MaterialPhysicalInventory)) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + MInventoryLine[] lines = getLines(false); + if (lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + + // TODO: Add up Amounts + // setApprovalAmt(); + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + if (!DOCACTION_Complete.equals(getDocAction())) + setDocAction(DOCACTION_Complete); + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info(toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info(toString()); + setIsApproved(false); + return true; + } // rejectIt + + /** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Implicit Approval + if (!isApproved()) + approveIt(); + log.info(toString()); + + //vpj-cd begin e-evolution recalculate the attribute instances and qty. + MInventoryLine[] linesup = getLines(false); + for (int i = 0; i < linesup.length; i++) + { + MInventoryLine line = linesup[i]; + + String sql1 = "Delete From M_InventoryLineMA " + + " WHERE M_InventoryLine_ID=" +line.getM_InventoryLine_ID(); + int no = DB.executeUpdate(sql1, get_TrxName()); + log.info("MA deleted " + no); + + StringBuffer sql = new StringBuffer( + "SELECT s.M_Product_ID, s.M_Locator_ID, s.M_AttributeSetInstance_ID," + + " s.QtyOnHand, p.M_AttributeSet_ID " + + "FROM M_Product p" + + " INNER JOIN M_Storage s ON (s.M_Product_ID=p.M_Product_ID)" + + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID) " + + "WHERE l.M_Warehouse_ID=?" + + " AND p.IsActive='Y' AND p.IsStocked='Y' and p.ProductType='I'" + + " AND s.M_Locator_ID=" +line.getM_Locator_ID() + + " AND s.M_Product_ID=" +line.getM_Product_ID() + + " AND s.QtyOnHand <> 0 " + ); + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql.toString(), get_TrxName()); + int index = 1; + pstmt.setInt (index++, getM_Warehouse_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MInventoryLineMA maup = new MInventoryLineMA (line, + rs.getInt(3), rs.getBigDecimal(4)); + + if (!maup.save()) + ; + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql.toString(), e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + } + //vpj-cd e-evolution recalculate the attribute instances and qty END. + // + MInventoryLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInventoryLine line = lines[i]; + if (!line.isActive()) + continue; + + MTransaction trx = null; + if (line.getM_AttributeSetInstance_ID() == 0) + { + BigDecimal qtyDiff = line.getQtyInternalUse().negate(); + if (qtyDiff.signum() == 0) + qtyDiff = line.getQtyCount().subtract(line.getQtyBook()); + // + if (qtyDiff.signum() > 0) + { + // Storage + MStorage storage = MStorage.get(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), 0, get_TrxName()); + if (storage == null) + storage = MStorage.getCreate(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), 0, get_TrxName()); + BigDecimal qtyNew = storage.getQtyOnHand().add(qtyDiff); + log.fine("Diff=" + qtyDiff + + " - OnHand=" + storage.getQtyOnHand() + "->" + qtyNew); + storage.setQtyOnHand(qtyNew); + storage.setDateLastInventory(getMovementDate()); + if (!storage.save(get_TrxName())) + { + m_processMsg = "Storage not updated(1)"; + return DocAction.STATUS_Invalid; + } + log.fine(storage.toString()); + // Transaction + trx = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_InventoryIn, + line.getM_Locator_ID(), line.getM_Product_ID(), 0, + qtyDiff, getMovementDate(), get_TrxName()); + trx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); + if (!trx.save()) + { + m_processMsg = "Transaction not inserted(1)"; + return DocAction.STATUS_Invalid; + } + } + else // negative qty + { + MInventoryLineMA mas[] = MInventoryLineMA.get(getCtx(), + line.getM_InventoryLine_ID(), get_TrxName()); + for (int j = 0; j < mas.length; j++) + { + MInventoryLineMA ma = mas[j]; + // Storage + MStorage storage = MStorage.get(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), get_TrxName()); + if (storage == null) + storage = MStorage.getCreate(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), get_TrxName()); + // + BigDecimal maxDiff = qtyDiff; + if (maxDiff.signum() < 0 + && ma.getMovementQty().compareTo(maxDiff.negate()) < 0) + maxDiff = ma.getMovementQty().negate(); + BigDecimal qtyNew = ma.getMovementQty().add(maxDiff); // Storage+Diff + log.fine("MA Qty=" + ma.getMovementQty() + + ",Diff=" + qtyDiff + "|" + maxDiff + + " - OnHand=" + storage.getQtyOnHand() + "->" + qtyNew + + " {" + ma.getM_AttributeSetInstance_ID() + "}"); + // + storage.setQtyOnHand(qtyNew); + storage.setDateLastInventory(getMovementDate()); + if (!storage.save(get_TrxName())) + { + m_processMsg = "Storage not updated (MA)"; + return DocAction.STATUS_Invalid; + } + log.fine(storage.toString()); + + // Transaction + trx = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_InventoryIn, + line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + maxDiff, getMovementDate(), get_TrxName()); + trx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); + if (!trx.save()) + { + m_processMsg = "Transaction not inserted (MA)"; + return DocAction.STATUS_Invalid; + } + // + qtyDiff = qtyDiff.subtract(maxDiff); + if (qtyDiff.signum() == 0) + break; + } + } // negative qty + } + + // Fallback + if (trx == null) + { + // Storage + MStorage storage = MStorage.get(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), get_TrxName()); + if (storage == null) + storage = MStorage.getCreate(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), get_TrxName()); + // + BigDecimal qtyDiff = line.getQtyInternalUse().negate(); + if (Env.ZERO.compareTo(qtyDiff) == 0) + qtyDiff = line.getQtyCount().subtract(line.getQtyBook()); + BigDecimal qtyNew = storage.getQtyOnHand().add(qtyDiff); + log.fine("Count=" + line.getQtyCount() + + ",Book=" + line.getQtyBook() + ", Difference=" + qtyDiff + + " - OnHand=" + storage.getQtyOnHand() + "->" + qtyNew); + // + storage.setQtyOnHand(qtyNew); + storage.setDateLastInventory(getMovementDate()); + if (!storage.save(get_TrxName())) + { + m_processMsg = "Storage not updated(2)"; + return DocAction.STATUS_Invalid; + } + log.fine(storage.toString()); + + // Transaction + trx = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_InventoryIn, + line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + qtyDiff, getMovementDate(), get_TrxName()); + trx.setM_InventoryLine_ID(line.getM_InventoryLine_ID()); + if (!trx.save()) + { + m_processMsg = "Transaction not inserted(2)"; + return DocAction.STATUS_Invalid; + } + } // Fallback + + } // for all lines + + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + // + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setMovementDate(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Check Material Policy. + * (NOT USED) + * Sets line ASI + */ + private void checkMaterialPolicy() + { + int no = MInventoryLineMA.deleteInventoryMA(getM_Inventory_ID(), get_TrxName()); + if (no > 0) + log.config("Delete old #" + no); + MInventoryLine[] lines = getLines(false); + + // Check Lines + for (int i = 0; i < lines.length; i++) + { + MInventoryLine line = lines[i]; + boolean needSave = false; + + // Attribute Set Instance + if (line.getM_AttributeSetInstance_ID() == 0) + { + MProduct product = MProduct.get(getCtx(), line.getM_Product_ID()); + BigDecimal qtyDiff = line.getQtyInternalUse().negate(); + if (Env.ZERO.compareTo(qtyDiff) == 0) + qtyDiff = line.getQtyCount().subtract(line.getQtyBook()); + log.fine("Count=" + line.getQtyCount() + + ",Book=" + line.getQtyBook() + ", Difference=" + qtyDiff); + if (qtyDiff.signum() > 0) // Incoming Trx + { + MAttributeSetInstance asi = new MAttributeSetInstance(getCtx(), 0, get_TrxName()); + asi.setClientOrg(getAD_Client_ID(), 0); + asi.setM_AttributeSet_ID(product.getM_AttributeSet_ID()); + if (asi.save()) + { + line.setM_AttributeSetInstance_ID(asi.getM_AttributeSetInstance_ID()); + needSave = true; + } + } + else // Outgoing Trx + { + String MMPolicy = product.getMMPolicy(); + MStorage[] storages = MStorage.getAllWithASI(getCtx(), + line.getM_Product_ID(), line.getM_Locator_ID(), + MClient.MMPOLICY_FiFo.equals(MMPolicy), get_TrxName()); + BigDecimal qtyToDeliver = qtyDiff.negate(); + for (int ii = 0; ii < storages.length; ii++) + { + MStorage storage = storages[ii]; + if (ii == 0) + { + if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) + { + line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); + needSave = true; + log.config("Direct - " + line); + qtyToDeliver = Env.ZERO; + } + else + { + log.config("Split - " + line); + MInventoryLineMA ma = new MInventoryLineMA (line, + storage.getM_AttributeSetInstance_ID(), + storage.getQtyOnHand().negate()); + if (!ma.save()) + ; + qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); + log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); + } + } + else // create addl material allocation + { + MInventoryLineMA ma = new MInventoryLineMA (line, + storage.getM_AttributeSetInstance_ID(), + qtyToDeliver.negate()); + if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) + qtyToDeliver = Env.ZERO; + else + { + ma.setMovementQty(storage.getQtyOnHand().negate()); + qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); + } + if (!ma.save()) + ; + log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); + } + if (qtyToDeliver.signum() == 0) + break; + } // for all storages + + // No AttributeSetInstance found for remainder + if (qtyToDeliver.signum() != 0) + { + MInventoryLineMA ma = new MInventoryLineMA (line, + 0, qtyToDeliver.negate()); + if (!ma.save()) + ; + log.fine("##: " + ma); + } + } // outgoing Trx + } // attributeSetInstance + + if (needSave && !line.save()) + log.severe("NOT saved " + line); + } // for all lines + + } // checkMaterialPolicy + + /** + * Void Document. + * @return false + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + if (DOCSTATUS_Closed.equals(getDocStatus()) + || DOCSTATUS_Reversed.equals(getDocStatus()) + || DOCSTATUS_Voided.equals(getDocStatus())) + { + m_processMsg = "Document Closed: " + getDocStatus(); + return false; + } + + // Not Processed + if (DOCSTATUS_Drafted.equals(getDocStatus()) + || DOCSTATUS_Invalid.equals(getDocStatus()) + || DOCSTATUS_InProgress.equals(getDocStatus()) + || DOCSTATUS_Approved.equals(getDocStatus()) + || DOCSTATUS_NotApproved.equals(getDocStatus()) ) + { + // Set lines to 0 + MInventoryLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInventoryLine line = lines[i]; + BigDecimal oldCount = line.getQtyCount(); + BigDecimal oldInternal = line.getQtyInternalUse(); + if (oldCount.compareTo(line.getQtyBook()) != 0 + || oldInternal.signum() != 0) + { + line.setQtyInternalUse(Env.ZERO); + line.setQtyCount(line.getQtyBook()); + line.addDescription("Void (" + oldCount + "/" + oldInternal + ")"); + line.save(get_TrxName()); + } + } + } + else + { + return reverseCorrectIt(); + } + + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // voidIt + + /** + * Close Document. + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + setDocAction(DOCACTION_None); + return true; + } // closeIt + + /** + * Reverse Correction + * @return false + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (!MPeriod.isOpen(getCtx(), getMovementDate(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return false; + } + + // Deep Copy + MInventory reversal = new MInventory(getCtx(), 0, get_TrxName()); + copyValues(this, reversal, getAD_Client_ID(), getAD_Org_ID()); + reversal.setDocStatus(DOCSTATUS_Drafted); + reversal.setDocAction(DOCACTION_Complete); + reversal.setIsApproved (false); + reversal.setPosted(false); + reversal.setProcessed(false); + reversal.addDescription("{->" + getDocumentNo() + ")"); + if (!reversal.save()) + { + m_processMsg = "Could not create Inventory Reversal"; + return false; + } + + // Reverse Line Qty + MInventoryLine[] oLines = getLines(true); + for (int i = 0; i < oLines.length; i++) + { + MInventoryLine oLine = oLines[i]; + MInventoryLine rLine = new MInventoryLine(getCtx(), 0, get_TrxName()); + copyValues(oLine, rLine, oLine.getAD_Client_ID(), oLine.getAD_Org_ID()); + rLine.setM_Inventory_ID(reversal.getM_Inventory_ID()); + rLine.setParent(reversal); + // + rLine.setQtyBook (oLine.getQtyCount()); // switch + rLine.setQtyCount (oLine.getQtyBook()); + rLine.setQtyInternalUse (oLine.getQtyInternalUse().negate()); + if (!rLine.save()) + { + m_processMsg = "Could not create Inventory Reversal Line"; + return false; + } + } + // + if (!reversal.processIt(DocAction.ACTION_Complete)) + { + m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg(); + return false; + } + reversal.closeIt(); + reversal.setDocStatus(DOCSTATUS_Reversed); + reversal.setDocAction(DOCACTION_None); + reversal.save(); + m_processMsg = reversal.getDocumentNo(); + + // Update Reversed (this) + addDescription("(" + reversal.getDocumentNo() + "<-)"); + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + setProcessed(true); + setDocStatus(DOCSTATUS_Reversed); // may come from void + setDocAction(DOCACTION_None); + + return true; + } // reverseCorrectionIt + + /** + * Reverse Accrual + * @return false + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return false; + } // reverseAccrualIt + + /** + * Re-activate + * @return false + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + return false; + } // reActivateIt + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Total Lines = 123.00 (#1) + sb.append(": ") + .append(Msg.translate(getCtx(),"ApprovalAmt")).append("=").append(getApprovalAmt()) + .append(" (#").append(getLines(false).length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getUpdatedBy(); + } // getDoc_User_ID + + /** + * Get Document Currency + * @return C_Currency_ID + */ + public int getC_Currency_ID() + { + // MPriceList pl = MPriceList.get(getCtx(), getM_PriceList_ID()); + // return pl.getC_Currency_ID(); + return 0; + } // getC_Currency_ID + +} // MInventory diff --git a/base/src/org/compiere/model/MInventoryLine.java b/base/src/org/compiere/model/MInventoryLine.java new file mode 100644 index 0000000000..583e059af9 --- /dev/null +++ b/base/src/org/compiere/model/MInventoryLine.java @@ -0,0 +1,450 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; + +import org.compiere.util.*; + +/** + * Physical Inventory Line Model + * + * @author Jorg Janke + * @version $Id: MInventoryLine.java,v 1.3 2006/07/30 00:51:02 jjanke Exp $ + * + * @author Teo Sarca, SC ARHIPAC SERVICE SRL + *
  • BF [ 1817757 ] Error on saving MInventoryLine in a custom environment + */ +public class MInventoryLine extends X_M_InventoryLine +{ + /** + * Get Inventory Line with parameters + * @param inventory inventory + * @param M_Locator_ID locator + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @return line or null + */ + public static MInventoryLine get (MInventory inventory, + int M_Locator_ID, int M_Product_ID, int M_AttributeSetInstance_ID) + { + MInventoryLine retValue = null; + String sql = "SELECT * FROM M_InventoryLine " + + "WHERE M_Inventory_ID=? AND M_Locator_ID=?" + + " AND M_Product_ID=? AND M_AttributeSetInstance_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, inventory.get_TrxName()); + pstmt.setInt (1, inventory.getM_Inventory_ID()); + pstmt.setInt(2, M_Locator_ID); + pstmt.setInt(3, M_Product_ID); + pstmt.setInt(4, M_AttributeSetInstance_ID); + ResultSet rs = pstmt.executeQuery (); + if (rs.next ()) + retValue = new MInventoryLine (inventory.getCtx(), rs, inventory.get_TrxName()); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + s_log.log (Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + return retValue; + } // get + + + /** Logger */ + private static CLogger s_log = CLogger.getCLogger (MInventoryLine.class); + + + /************************************************************************** + * Default Constructor + * @param ctx context + * @param M_InventoryLine_ID line + * @param trxName transaction + */ + public MInventoryLine (Properties ctx, int M_InventoryLine_ID, String trxName) + { + super (ctx, M_InventoryLine_ID, trxName); + if (M_InventoryLine_ID == 0) + { + // setM_Inventory_ID (0); // Parent + // setM_InventoryLine_ID (0); // PK + // setM_Locator_ID (0); // FK + setLine(0); + // setM_Product_ID (0); // FK + setM_AttributeSetInstance_ID(0); // FK + setInventoryType (INVENTORYTYPE_InventoryDifference); + setQtyBook (Env.ZERO); + setQtyCount (Env.ZERO); + setProcessed(false); + } + } // MInventoryLine + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MInventoryLine (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MInventoryLine + + /** + * Detail Constructor. + * Locator/Product/AttributeSetInstance must be unique + * @param inventory parent + * @param M_Locator_ID locator + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID instance + * @param QtyBook book value + * @param QtyCount count value + */ + public MInventoryLine (MInventory inventory, + int M_Locator_ID, int M_Product_ID, int M_AttributeSetInstance_ID, + BigDecimal QtyBook, BigDecimal QtyCount) + { + this (inventory.getCtx(), 0, inventory.get_TrxName()); + if (inventory.get_ID() == 0) + throw new IllegalArgumentException("Header not saved"); + m_parent = inventory; + setM_Inventory_ID (inventory.getM_Inventory_ID()); // Parent + setClientOrg (inventory.getAD_Client_ID(), inventory.getAD_Org_ID()); + setM_Locator_ID (M_Locator_ID); // FK + setM_Product_ID (M_Product_ID); // FK + setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID); + // + if (QtyBook != null) + setQtyBook (QtyBook); + if (QtyCount != null && QtyCount.signum() != 0) + setQtyCount (QtyCount); + m_isManualEntry = false; + } // MInventoryLine + + /** Manually created */ + private boolean m_isManualEntry = true; + /** Parent */ + private MInventory m_parent = null; + /** Product */ + private MProduct m_product = null; + + /** + * Get Qty Book + * @return Qty Book + */ + public BigDecimal getQtyBook () + { + BigDecimal bd = super.getQtyBook (); + if (bd == null) + bd = Env.ZERO; + return bd; + } // getQtyBook + + /** + * Get Qty Count + * @return Qty Count + */ + public BigDecimal getQtyCount () + { + BigDecimal bd = super.getQtyCount(); + if (bd == null) + bd = Env.ZERO; + return bd; + } // getQtyBook + + /** + * Get Product + * @return product or null if not defined + */ + public MProduct getProduct() + { + int M_Product_ID = getM_Product_ID(); + if (M_Product_ID == 0) + return null; + if (m_product != null && m_product.getM_Product_ID() != M_Product_ID) + m_product = null; // reset + if (m_product == null) + m_product = MProduct.get(getCtx(), M_Product_ID); + return m_product; + } // getProduct + + /** + * Set Count Qty - enforce UOM + * @param QtyCount qty + */ + public void setQtyCount (BigDecimal QtyCount) + { + if (QtyCount != null) + { + MProduct product = getProduct(); + if (product != null) + { + int precision = product.getUOMPrecision(); + QtyCount = QtyCount.setScale(precision, BigDecimal.ROUND_HALF_UP); + } + } + super.setQtyCount(QtyCount); + } // setQtyCount + + /** + * Set Internal Use Qty - enforce UOM + * @param QtyInternalUse qty + */ + public void setQtyInternalUse (BigDecimal QtyInternalUse) + { + if (QtyInternalUse != null) + { + MProduct product = getProduct(); + if (product != null) + { + int precision = product.getUOMPrecision(); + QtyInternalUse = QtyInternalUse.setScale(precision, BigDecimal.ROUND_HALF_UP); + } + } + super.setQtyInternalUse(QtyInternalUse); + } // setQtyInternalUse + + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + /** + * Get Parent + * @param parent parent + */ + protected void setParent(MInventory parent) + { + m_parent = parent; + } // setParent + + /** + * Get Parent + * @return parent + */ + public MInventory getParent() + { + if (m_parent == null) + m_parent = new MInventory (getCtx(), getM_Inventory_ID(), get_TrxName()); + return m_parent; + } // getParent + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MInventoryLine["); + sb.append (get_ID()) + .append("-M_Product_ID=").append (getM_Product_ID()) + .append(",QtyCount=").append(getQtyCount()) + .append(",QtyInternalUse=").append(getQtyInternalUse()) + .append(",QtyBook=").append(getQtyBook()) + .append(",M_AttributeSetInstance_ID=").append(getM_AttributeSetInstance_ID()) + .append("]"); + return sb.toString (); + } // toString + + /** + * Before Save + * @param newRecord new + * @return true if can be saved + */ + protected boolean beforeSave (boolean newRecord) + { + if (newRecord && m_isManualEntry) + { + // Product requires ASI + if (getM_AttributeSetInstance_ID() == 0) + { + MProduct product = MProduct.get(getCtx(), getM_Product_ID()); + if (product != null && product.isASIMandatory(isSOTrx())) + { + log.saveError("FillMandatory", Msg.getElement(getCtx(), COLUMNNAME_M_AttributeSetInstance_ID)); + return false; + } + } // No ASI + } // new or manual + + // Set Line No + if (getLine() == 0) + { + String sql = "SELECT COALESCE(MAX(Line),0)+10 AS DefaultValue FROM M_InventoryLine WHERE M_Inventory_ID=?"; + int ii = DB.getSQLValue (get_TrxName(), sql, getM_Inventory_ID()); + setLine (ii); + } + + // Enforce Qty UOM + if (newRecord || is_ValueChanged("QtyCount")) + setQtyCount(getQtyCount()); + if (newRecord || is_ValueChanged("QtyInternalUse")) + setQtyInternalUse(getQtyInternalUse()); + + // InternalUse Inventory + if (getQtyInternalUse().signum() != 0) + { + if (!INVENTORYTYPE_ChargeAccount.equals(getInventoryType())) + setInventoryType(INVENTORYTYPE_ChargeAccount); + // + if (getC_Charge_ID() == 0) + { + log.saveError("InternalUseNeedsCharge", ""); + return false; + } + } + else if (INVENTORYTYPE_ChargeAccount.equals(getInventoryType())) + { + if (getC_Charge_ID() == 0) + { + log.saveError("FillMandatory", Msg.getElement(getCtx(), "C_Charge_ID")); + return false; + } + } + else if (getC_Charge_ID() != 0) + setC_Charge_ID(0); + + // Set AD_Org to parent if not charge + if (getC_Charge_ID() == 0) + setAD_Org_ID(getParent().getAD_Org_ID()); + + return true; + } // beforeSave + + /** + * After Save + * @param newRecord new + * @param success success + * @return true + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + if (!success) + return false; + + // Create MA + if (newRecord && success + && m_isManualEntry && getM_AttributeSetInstance_ID() == 0) + createMA(); + return true; + } // afterSave + + /** + * Create Material Allocations for new Instances + */ + private void createMA() + { + MStorage[] storages = MStorage.getAll(getCtx(), getM_Product_ID(), + getM_Locator_ID(), get_TrxName()); + boolean allZeroASI = true; + for (int i = 0; i < storages.length; i++) + { + if (storages[i].getM_AttributeSetInstance_ID() != 0) + { + allZeroASI = false; + break; + } + } + if (allZeroASI) + return; + + MInventoryLineMA ma = null; + BigDecimal sum = Env.ZERO; + for (int i = 0; i < storages.length; i++) + { + MStorage storage = storages[i]; + if (storage.getQtyOnHand().signum() == 0) + continue; + if (ma != null + && ma.getM_AttributeSetInstance_ID() == storage.getM_AttributeSetInstance_ID()) + ma.setMovementQty(ma.getMovementQty().add(storage.getQtyOnHand())); + else + ma = new MInventoryLineMA (this, + storage.getM_AttributeSetInstance_ID(), storage.getQtyOnHand()); + if (!ma.save()) + ; + sum = sum.add(storage.getQtyOnHand()); + } + if (sum.compareTo(getQtyBook()) != 0) + { + log.warning("QtyBook=" + getQtyBook() + " corrected to Sum of MA=" + sum); + setQtyBook(sum); + } + } // createMA + + /** + * Is Internal Use Inventory + * @return true if is internal use inventory + */ + public boolean isInternalUseInventory() { + /* TODO: need to add M_Inventory.IsInternalUseInventory flag + see FR [ 1879029 ] Added IsInternalUseInventory flag to M_Inventory table + MInventory parent = getParent(); + return parent != null && parent.isInternalUseInventory(); + */ + return getQtyInternalUse().signum() != 0; + } + + /** + * Get Movement Qty (absolute value) + *
  • negative value means outgoing trx + *
  • positive value means incoming trx + * @return movement qty + */ + public BigDecimal getMovementQty() { + if(isInternalUseInventory()) { + return getQtyInternalUse().negate(); + } + else { + return getQtyCount().subtract(getQtyBook()); + } + } + + /** + * @return true if is an outgoing transaction + */ + public boolean isSOTrx() { + return getMovementQty().signum() < 0; + } +} // MInventoryLine diff --git a/base/src/org/compiere/model/MInventoryLineMA.java b/base/src/org/compiere/model/MInventoryLineMA.java new file mode 100644 index 0000000000..7b29af56f3 --- /dev/null +++ b/base/src/org/compiere/model/MInventoryLineMA.java @@ -0,0 +1,149 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.util.*; + + +/** + * Inventory Material Allocation + * + * @author Jorg Janke + * @version $Id: MInventoryLineMA.java,v 1.3 2006/07/30 00:51:04 jjanke Exp $ + */ +public class MInventoryLineMA extends X_M_InventoryLineMA +{ + /** + * Get Material Allocations for Line + * @param ctx context + * @param M_InventoryLine_ID line + * @param trxName trx + * @return allocations + */ + public static MInventoryLineMA[] get (Properties ctx, int M_InventoryLine_ID, String trxName) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_InventoryLineMA WHERE M_InventoryLine_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + pstmt.setInt (1, M_InventoryLine_ID); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + list.add (new MInventoryLineMA (ctx, rs, trxName)); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + s_log.log (Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + MInventoryLineMA[] retValue = new MInventoryLineMA[list.size ()]; + list.toArray (retValue); + return retValue; + } // get + + /** + * Delete all Material Allocation for Inventory + * @param M_Inventory_ID inventory + * @param trxName transaction + * @return number of rows deleted or -1 for error + */ + public static int deleteInventoryMA (int M_Inventory_ID, String trxName) + { + String sql = "DELETE FROM M_InventoryLineMA ma WHERE EXISTS " + + "(SELECT * FROM M_InventoryLine l WHERE l.M_InventoryLine_ID=ma.M_InventoryLine_ID" + + " AND M_Inventory_ID=" + M_Inventory_ID + ")"; + return DB.executeUpdate(sql, trxName); + } // deleteInventoryMA + + /** Logger */ + private static CLogger s_log = CLogger.getCLogger (MInventoryLineMA.class); + + + /************************************************************************** + * Standard Constructor + * @param ctx context + * @param M_InventoryLineMA_ID ignored + * @param trxName trx + */ + public MInventoryLineMA (Properties ctx, int M_InventoryLineMA_ID, String trxName) + { + super (ctx, M_InventoryLineMA_ID, trxName); + if (M_InventoryLineMA_ID != 0) + throw new IllegalArgumentException("Multi-Key"); + } // MInventoryLineMA + + /** + * Load Cosntructor + * @param ctx context + * @param rs result set + * @param trxName trx + */ + public MInventoryLineMA (Properties ctx, ResultSet rs, String trxName) + { + super (ctx, rs, trxName); + } // MInventoryLineMA + + /** + * Parent Constructor + * @param parent parent + * @param M_AttributeSetInstance_ID asi + * @param MovementQty qty + */ + public MInventoryLineMA (MInventoryLine parent, int M_AttributeSetInstance_ID, BigDecimal MovementQty) + { + this (parent.getCtx(), 0, parent.get_TrxName()); + setClientOrg(parent); + setM_InventoryLine_ID(parent.getM_InventoryLine_ID()); + // + setM_AttributeSetInstance_ID(M_AttributeSetInstance_ID); + setMovementQty(MovementQty); + } // MInventoryLineMA + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MInventoryLineMA["); + sb.append("M_InventoryLine_ID=").append(getM_InventoryLine_ID()) + .append(",M_AttributeSetInstance_ID=").append(getM_AttributeSetInstance_ID()) + .append(", Qty=").append(getMovementQty()) + .append ("]"); + return sb.toString (); + } // toString + +} // MInventoryLineMA diff --git a/base/src/org/compiere/model/MInvoice.java b/base/src/org/compiere/model/MInvoice.java new file mode 100644 index 0000000000..e1be96502f --- /dev/null +++ b/base/src/org/compiere/model/MInvoice.java @@ -0,0 +1,2368 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.print.*; +import org.compiere.process.*; +import org.compiere.util.*; + + +/** + * Invoice Model. + * Please do not set DocStatus and C_DocType_ID directly. + * They are set in the process() method. + * Use DocAction and C_DocTypeTarget_ID instead. + * + * @author Jorg Janke + * @version $Id: MInvoice.java,v 1.2 2006/07/30 00:51:02 jjanke Exp $ + * + * Modifications: Added RMA functionality (Ashley Ramdass) + */ +public class MInvoice extends X_C_Invoice implements DocAction +{ + /** + * Get Payments Of BPartner + * @param ctx context + * @param C_BPartner_ID id + * @param trxName transaction + * @return array + */ + public static MInvoice[] getOfBPartner (Properties ctx, int C_BPartner_ID, String trxName) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM C_Invoice WHERE C_BPartner_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, trxName); + pstmt.setInt(1, C_BPartner_ID); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MInvoice(ctx,rs, trxName)); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + // + MInvoice[] retValue = new MInvoice[list.size()]; + list.toArray(retValue); + return retValue; + } // getOfBPartner + + /** + * Create new Invoice by copying + * @param from invoice + * @param dateDoc date of the document date + * @param C_DocTypeTarget_ID target doc type + * @param isSOTrx sales order + * @param counter create counter links + * @param trxName trx + * @param setOrder set Order links + * @return Invoice + */ + public static MInvoice copyFrom (MInvoice from, Timestamp dateDoc, + int C_DocTypeTarget_ID, boolean isSOTrx, boolean counter, + String trxName, boolean setOrder) + { + MInvoice to = new MInvoice (from.getCtx(), 0, null); + to.set_TrxName(trxName); + PO.copyValues (from, to, from.getAD_Client_ID(), from.getAD_Org_ID()); + to.set_ValueNoCheck ("C_Invoice_ID", I_ZERO); + to.set_ValueNoCheck ("DocumentNo", null); + // + to.setDocStatus (DOCSTATUS_Drafted); // Draft + to.setDocAction(DOCACTION_Complete); + // + to.setC_DocType_ID(0); + to.setC_DocTypeTarget_ID (C_DocTypeTarget_ID); + to.setIsSOTrx(isSOTrx); + // + to.setDateInvoiced (dateDoc); + to.setDateAcct (dateDoc); + to.setDatePrinted(null); + to.setIsPrinted (false); + // + to.setIsApproved (false); + to.setC_Payment_ID(0); + to.setC_CashLine_ID(0); + to.setIsPaid (false); + to.setIsInDispute(false); + // + // Amounts are updated by trigger when adding lines + to.setGrandTotal(Env.ZERO); + to.setTotalLines(Env.ZERO); + // + to.setIsTransferred (false); + to.setPosted (false); + to.setProcessed (false); + //[ 1633721 ] Reverse Documents- Processing=Y + to.setProcessing(false); + // delete references + to.setIsSelfService(false); + if (!setOrder) + to.setC_Order_ID(0); + if (counter) + { + to.setRef_Invoice_ID(from.getC_Invoice_ID()); + // Try to find Order link + if (from.getC_Order_ID() != 0) + { + MOrder peer = new MOrder (from.getCtx(), from.getC_Order_ID(), from.get_TrxName()); + if (peer.getRef_Order_ID() != 0) + to.setC_Order_ID(peer.getRef_Order_ID()); + } + } + else + to.setRef_Invoice_ID(0); + + if (!to.save(trxName)) + throw new IllegalStateException("Could not create Invoice"); + if (counter) + from.setRef_Invoice_ID(to.getC_Invoice_ID()); + + // Lines + if (to.copyLinesFrom(from, counter, setOrder) == 0) + throw new IllegalStateException("Could not create Invoice Lines"); + + return to; + } // copyFrom + + /** + * Get PDF File Name + * @param documentDir directory + * @param C_Invoice_ID invoice + * @return file name + */ + public static String getPDFFileName (String documentDir, int C_Invoice_ID) + { + StringBuffer sb = new StringBuffer (documentDir); + if (sb.length() == 0) + sb.append("."); + if (!sb.toString().endsWith(File.separator)) + sb.append(File.separator); + sb.append("C_Invoice_ID_") + .append(C_Invoice_ID) + .append(".pdf"); + return sb.toString(); + } // getPDFFileName + + + /** + * Get MInvoice from Cache + * @param ctx context + * @param C_Invoice_ID id + * @return MInvoice + */ + public static MInvoice get (Properties ctx, int C_Invoice_ID) + { + Integer key = new Integer (C_Invoice_ID); + MInvoice retValue = (MInvoice) s_cache.get (key); + if (retValue != null) + return retValue; + retValue = new MInvoice (ctx, C_Invoice_ID, null); + if (retValue.get_ID () != 0) + s_cache.put (key, retValue); + return retValue; + } // get + + /** Cache */ + private static CCache s_cache = new CCache("C_Invoice", 20, 2); // 2 minutes + + + /************************************************************************** + * Invoice Constructor + * @param ctx context + * @param C_Invoice_ID invoice or 0 for new + * @param trxName trx name + */ + public MInvoice (Properties ctx, int C_Invoice_ID, String trxName) + { + super (ctx, C_Invoice_ID, trxName); + if (C_Invoice_ID == 0) + { + setDocStatus (DOCSTATUS_Drafted); // Draft + setDocAction (DOCACTION_Complete); + // + setPaymentRule(PAYMENTRULE_OnCredit); // Payment Terms + + setDateInvoiced (new Timestamp (System.currentTimeMillis ())); + setDateAcct (new Timestamp (System.currentTimeMillis ())); + // + setChargeAmt (Env.ZERO); + setTotalLines (Env.ZERO); + setGrandTotal (Env.ZERO); + // + setIsSOTrx (true); + setIsTaxIncluded (false); + setIsApproved (false); + setIsDiscountPrinted (false); + setIsPaid (false); + setSendEMail (false); + setIsPrinted (false); + setIsTransferred (false); + setIsSelfService(false); + setIsPayScheduleValid(false); + setIsInDispute(false); + setPosted(false); + super.setProcessed (false); + setProcessing(false); + } + } // MInvoice + + /** + * Load Constructor + * @param ctx context + * @param rs result set record + * @param trxName transaction + */ + public MInvoice (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MInvoice + + /** + * Create Invoice from Order + * @param order order + * @param C_DocTypeTarget_ID target document type + * @param invoiceDate date or null + */ + public MInvoice (MOrder order, int C_DocTypeTarget_ID, Timestamp invoiceDate) + { + this (order.getCtx(), 0, order.get_TrxName()); + setClientOrg(order); + setOrder(order); // set base settings + // + if (C_DocTypeTarget_ID == 0) + C_DocTypeTarget_ID = DB.getSQLValue(null, + "SELECT C_DocTypeInvoice_ID FROM C_DocType WHERE C_DocType_ID=?", + order.getC_DocType_ID()); + setC_DocTypeTarget_ID(C_DocTypeTarget_ID); + if (invoiceDate != null) + setDateInvoiced(invoiceDate); + setDateAcct(getDateInvoiced()); + // + setSalesRep_ID(order.getSalesRep_ID()); + // + setC_BPartner_ID(order.getBill_BPartner_ID()); + setC_BPartner_Location_ID(order.getBill_Location_ID()); + setAD_User_ID(order.getBill_User_ID()); + } // MInvoice + + /** + * Create Invoice from Shipment + * @param ship shipment + * @param invoiceDate date or null + */ + public MInvoice (MInOut ship, Timestamp invoiceDate) + { + this (ship.getCtx(), 0, ship.get_TrxName()); + setClientOrg(ship); + setShipment(ship); // set base settings + // + setC_DocTypeTarget_ID(); + if (invoiceDate != null) + setDateInvoiced(invoiceDate); + setDateAcct(getDateInvoiced()); + // + setSalesRep_ID(ship.getSalesRep_ID()); + setAD_User_ID(ship.getAD_User_ID()); + } // MInvoice + + /** + * Create Invoice from Batch Line + * @param batch batch + * @param line batch line + */ + public MInvoice (MInvoiceBatch batch, MInvoiceBatchLine line) + { + this (line.getCtx(), 0, line.get_TrxName()); + setClientOrg(line); + setDocumentNo(line.getDocumentNo()); + // + setIsSOTrx(batch.isSOTrx()); + MBPartner bp = new MBPartner (line.getCtx(), line.getC_BPartner_ID(), line.get_TrxName()); + setBPartner(bp); // defaults + // + setIsTaxIncluded(line.isTaxIncluded()); + // May conflict with default price list + setC_Currency_ID(batch.getC_Currency_ID()); + setC_ConversionType_ID(batch.getC_ConversionType_ID()); + // + // setPaymentRule(order.getPaymentRule()); + // setC_PaymentTerm_ID(order.getC_PaymentTerm_ID()); + // setPOReference(""); + setDescription(batch.getDescription()); + // setDateOrdered(order.getDateOrdered()); + // + setAD_OrgTrx_ID(line.getAD_OrgTrx_ID()); + setC_Project_ID(line.getC_Project_ID()); + // setC_Campaign_ID(line.getC_Campaign_ID()); + setC_Activity_ID(line.getC_Activity_ID()); + setUser1_ID(line.getUser1_ID()); + setUser2_ID(line.getUser2_ID()); + // + setC_DocTypeTarget_ID(line.getC_DocType_ID()); + setDateInvoiced(line.getDateInvoiced()); + setDateAcct(line.getDateAcct()); + // + setSalesRep_ID(batch.getSalesRep_ID()); + // + setC_BPartner_ID(line.getC_BPartner_ID()); + setC_BPartner_Location_ID(line.getC_BPartner_Location_ID()); + setAD_User_ID(line.getAD_User_ID()); + } // MInvoice + + /** Open Amount */ + private BigDecimal m_openAmt = null; + + /** Invoice Lines */ + private MInvoiceLine[] m_lines; + /** Invoice Taxes */ + private MInvoiceTax[] m_taxes; + /** Logger */ + private static CLogger s_log = CLogger.getCLogger(MInvoice.class); + + /** + * Overwrite Client/Org if required + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + public void setClientOrg (int AD_Client_ID, int AD_Org_ID) + { + super.setClientOrg(AD_Client_ID, AD_Org_ID); + } // setClientOrg + + /** + * Set Business Partner Defaults & Details + * @param bp business partner + */ + public void setBPartner (MBPartner bp) + { + if (bp == null) + return; + + setC_BPartner_ID(bp.getC_BPartner_ID()); + // Set Defaults + int ii = 0; + if (isSOTrx()) + ii = bp.getC_PaymentTerm_ID(); + else + ii = bp.getPO_PaymentTerm_ID(); + if (ii != 0) + setC_PaymentTerm_ID(ii); + // + if (isSOTrx()) + ii = bp.getM_PriceList_ID(); + else + ii = bp.getPO_PriceList_ID(); + if (ii != 0) + setM_PriceList_ID(ii); + // + String ss = bp.getPaymentRule(); + if (ss != null) + setPaymentRule(ss); + + + // Set Locations + MBPartnerLocation[] locs = bp.getLocations(false); + if (locs != null) + { + for (int i = 0; i < locs.length; i++) + { + if ((locs[i].isBillTo() && isSOTrx()) + || (locs[i].isPayFrom() && !isSOTrx())) + setC_BPartner_Location_ID(locs[i].getC_BPartner_Location_ID()); + } + // set to first + if (getC_BPartner_Location_ID() == 0 && locs.length > 0) + setC_BPartner_Location_ID(locs[0].getC_BPartner_Location_ID()); + } + if (getC_BPartner_Location_ID() == 0) + log.log(Level.SEVERE, "Has no To Address: " + bp); + + // Set Contact + MUser[] contacts = bp.getContacts(false); + if (contacts != null && contacts.length > 0) // get first User + setAD_User_ID(contacts[0].getAD_User_ID()); + } // setBPartner + + /** + * Set Order References + * @param order order + */ + public void setOrder (MOrder order) + { + if (order == null) + return; + + setC_Order_ID(order.getC_Order_ID()); + setIsSOTrx(order.isSOTrx()); + setIsDiscountPrinted(order.isDiscountPrinted()); + setIsSelfService(order.isSelfService()); + setSendEMail(order.isSendEMail()); + // + setM_PriceList_ID(order.getM_PriceList_ID()); + setIsTaxIncluded(order.isTaxIncluded()); + setC_Currency_ID(order.getC_Currency_ID()); + setC_ConversionType_ID(order.getC_ConversionType_ID()); + // + setPaymentRule(order.getPaymentRule()); + setC_PaymentTerm_ID(order.getC_PaymentTerm_ID()); + setPOReference(order.getPOReference()); + setDescription(order.getDescription()); + setDateOrdered(order.getDateOrdered()); + // + setAD_OrgTrx_ID(order.getAD_OrgTrx_ID()); + setC_Project_ID(order.getC_Project_ID()); + setC_Campaign_ID(order.getC_Campaign_ID()); + setC_Activity_ID(order.getC_Activity_ID()); + setUser1_ID(order.getUser1_ID()); + setUser2_ID(order.getUser2_ID()); + } // setOrder + + /** + * Set Shipment References + * @param ship shipment + */ + public void setShipment (MInOut ship) + { + if (ship == null) + return; + + setIsSOTrx(ship.isSOTrx()); + // + MBPartner bp = new MBPartner (getCtx(), ship.getC_BPartner_ID(), null); + setBPartner (bp); + // + setSendEMail(ship.isSendEMail()); + // + setPOReference(ship.getPOReference()); + setDescription(ship.getDescription()); + setDateOrdered(ship.getDateOrdered()); + // + setAD_OrgTrx_ID(ship.getAD_OrgTrx_ID()); + setC_Project_ID(ship.getC_Project_ID()); + setC_Campaign_ID(ship.getC_Campaign_ID()); + setC_Activity_ID(ship.getC_Activity_ID()); + setUser1_ID(ship.getUser1_ID()); + setUser2_ID(ship.getUser2_ID()); + // + if (ship.getC_Order_ID() != 0) + { + setC_Order_ID(ship.getC_Order_ID()); + MOrder order = new MOrder (getCtx(), ship.getC_Order_ID(), get_TrxName()); + setIsDiscountPrinted(order.isDiscountPrinted()); + setM_PriceList_ID(order.getM_PriceList_ID()); + setIsTaxIncluded(order.isTaxIncluded()); + setC_Currency_ID(order.getC_Currency_ID()); + setC_ConversionType_ID(order.getC_ConversionType_ID()); + setPaymentRule(order.getPaymentRule()); + setC_PaymentTerm_ID(order.getC_PaymentTerm_ID()); + // + MDocType dt = MDocType.get(getCtx(), order.getC_DocType_ID()); + if (dt.getC_DocTypeInvoice_ID() != 0) + setC_DocTypeTarget_ID(dt.getC_DocTypeInvoice_ID()); + // Overwrite Invoice Address + setC_BPartner_Location_ID(order.getBill_Location_ID()); + } + // Check if Shipment/Receipt is based on RMA + if (ship.getM_RMA_ID() != 0) + { + MRMA rma = new MRMA(getCtx(), ship.getM_RMA_ID(), get_TrxName()); + MOrder rmaOrder = rma.getOriginalOrder(); + setM_RMA_ID(ship.getM_RMA_ID()); + setIsSOTrx(rma.isSOTrx()); + setM_PriceList_ID(rmaOrder.getM_PriceList_ID()); + setIsTaxIncluded(rmaOrder.isTaxIncluded()); + setC_Currency_ID(rmaOrder.getC_Currency_ID()); + setC_ConversionType_ID(rmaOrder.getC_ConversionType_ID()); + setPaymentRule(rmaOrder.getPaymentRule()); + setC_PaymentTerm_ID(rmaOrder.getC_PaymentTerm_ID()); + + // Retrieves the invoice DocType + MDocType dt = MDocType.get(getCtx(), rma.getC_DocType_ID()); + if (dt.getC_DocTypeInvoice_ID() != 0) + { + setC_DocTypeTarget_ID(dt.getC_DocTypeInvoice_ID()); + } + setC_BPartner_Location_ID(rmaOrder.getBill_Location_ID()); + } + + } // setShipment + + /** + * Set Target Document Type + * @param DocBaseType doc type MDocType.DOCBASETYPE_ + */ + public void setC_DocTypeTarget_ID (String DocBaseType) + { + String sql = "SELECT C_DocType_ID FROM C_DocType " + + "WHERE AD_Client_ID=? AND DocBaseType=?" + + " AND IsActive='Y' " + + "ORDER BY IsDefault DESC"; + int C_DocType_ID = DB.getSQLValue(null, sql, getAD_Client_ID(), DocBaseType); + if (C_DocType_ID <= 0) + log.log(Level.SEVERE, "Not found for AC_Client_ID=" + + getAD_Client_ID() + " - " + DocBaseType); + else + { + log.fine(DocBaseType); + setC_DocTypeTarget_ID (C_DocType_ID); + boolean isSOTrx = MDocType.DOCBASETYPE_ARInvoice.equals(DocBaseType) + || MDocType.DOCBASETYPE_ARCreditMemo.equals(DocBaseType); + setIsSOTrx (isSOTrx); + } + } // setC_DocTypeTarget_ID + + /** + * Set Target Document Type. + * Based on SO flag AP/AP Invoice + */ + public void setC_DocTypeTarget_ID () + { + if (getC_DocTypeTarget_ID() > 0) + return; + if (isSOTrx()) + setC_DocTypeTarget_ID(MDocType.DOCBASETYPE_ARInvoice); + else + setC_DocTypeTarget_ID(MDocType.DOCBASETYPE_APInvoice); + } // setC_DocTypeTarget_ID + + + /** + * Get Grand Total + * @param creditMemoAdjusted adjusted for CM (negative) + * @return grand total + */ + public BigDecimal getGrandTotal (boolean creditMemoAdjusted) + { + if (!creditMemoAdjusted) + return super.getGrandTotal(); + // + BigDecimal amt = getGrandTotal(); + if (isCreditMemo()) + return amt.negate(); + return amt; + } // getGrandTotal + + + /** + * Get Invoice Lines of Invoice + * @param whereClause starting with AND + * @return lines + */ + private MInvoiceLine[] getLines (String whereClause) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM C_InvoiceLine WHERE C_Invoice_ID=? "; + if (whereClause != null) + sql += whereClause; + sql += " ORDER BY Line"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Invoice_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + MInvoiceLine il = new MInvoiceLine(getCtx(), rs, get_TrxName()); + il.setInvoice(this); + list.add(il); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "getLines", e); + } + finally + { + try + { + if (pstmt != null) + pstmt.close (); + } + catch (Exception e) + {} + pstmt = null; + } + + // + MInvoiceLine[] lines = new MInvoiceLine[list.size()]; + list.toArray(lines); + return lines; + } // getLines + + /** + * Get Invoice Lines + * @param requery + * @return lines + */ + public MInvoiceLine[] getLines (boolean requery) + { + if (m_lines == null || m_lines.length == 0 || requery) + m_lines = getLines(null); + set_TrxName(m_lines, get_TrxName()); + return m_lines; + } // getLines + + /** + * Get Lines of Invoice + * @return lines + */ + public MInvoiceLine[] getLines() + { + return getLines(false); + } // getLines + + + /** + * Renumber Lines + * @param step start and step + */ + public void renumberLines (int step) + { + int number = step; + MInvoiceLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInvoiceLine line = lines[i]; + line.setLine(number); + line.save(); + number += step; + } + m_lines = null; + } // renumberLines + + /** + * Copy Lines From other Invoice. + * @param otherInvoice invoice + * @param counter create counter links + * @param setOrder set order links + * @return number of lines copied + */ + public int copyLinesFrom (MInvoice otherInvoice, boolean counter, boolean setOrder) + { + if (isProcessed() || isPosted() || otherInvoice == null) + return 0; + MInvoiceLine[] fromLines = otherInvoice.getLines(false); + int count = 0; + for (int i = 0; i < fromLines.length; i++) + { + MInvoiceLine line = new MInvoiceLine (getCtx(), 0, get_TrxName()); + MInvoiceLine fromLine = fromLines[i]; + if (counter) // header + PO.copyValues (fromLine, line, getAD_Client_ID(), getAD_Org_ID()); + else + PO.copyValues (fromLine, line, fromLine.getAD_Client_ID(), fromLine.getAD_Org_ID()); + line.setC_Invoice_ID(getC_Invoice_ID()); + line.setInvoice(this); + line.set_ValueNoCheck ("C_InvoiceLine_ID", I_ZERO); // new + // Reset + if (!setOrder) + line.setC_OrderLine_ID(0); + line.setRef_InvoiceLine_ID(0); + line.setM_InOutLine_ID(0); + line.setA_Asset_ID(0); + line.setM_AttributeSetInstance_ID(0); + line.setS_ResourceAssignment_ID(0); + // New Tax + if (getC_BPartner_ID() != otherInvoice.getC_BPartner_ID()) + line.setTax(); // recalculate + // + if (counter) + { + line.setRef_InvoiceLine_ID(fromLine.getC_InvoiceLine_ID()); + if (fromLine.getC_OrderLine_ID() != 0) + { + MOrderLine peer = new MOrderLine (getCtx(), fromLine.getC_OrderLine_ID(), get_TrxName()); + if (peer.getRef_OrderLine_ID() != 0) + line.setC_OrderLine_ID(peer.getRef_OrderLine_ID()); + } + line.setM_InOutLine_ID(0); + if (fromLine.getM_InOutLine_ID() != 0) + { + MInOutLine peer = new MInOutLine (getCtx(), fromLine.getM_InOutLine_ID(), get_TrxName()); + if (peer.getRef_InOutLine_ID() != 0) + line.setM_InOutLine_ID(peer.getRef_InOutLine_ID()); + } + } + // + line.setProcessed(false); + if (line.save(get_TrxName())) + count++; + // Cross Link + if (counter) + { + fromLine.setRef_InvoiceLine_ID(line.getC_InvoiceLine_ID()); + fromLine.save(get_TrxName()); + } + + // MZ Goodwill + // copy the landed cost + line.copyLandedCostFrom(fromLine); + line.allocateLandedCosts(); + // end MZ + } + if (fromLines.length != count) + log.log(Level.SEVERE, "Line difference - From=" + fromLines.length + " <> Saved=" + count); + return count; + } // copyLinesFrom + + /** Reversal Flag */ + private boolean m_reversal = false; + + /** + * Set Reversal + * @param reversal reversal + */ + private void setReversal(boolean reversal) + { + m_reversal = reversal; + } // setReversal + /** + * Is Reversal + * @return reversal + */ + private boolean isReversal() + { + return m_reversal; + } // isReversal + + /** + * Get Taxes + * @param requery requery + * @return array of taxes + */ + public MInvoiceTax[] getTaxes (boolean requery) + { + if (m_taxes != null && !requery) + return m_taxes; + String sql = "SELECT * FROM C_InvoiceTax WHERE C_Invoice_ID=?"; + ArrayList list = new ArrayList(); + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, getC_Invoice_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + list.add(new MInvoiceTax(getCtx(), rs, get_TrxName())); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "getTaxes", e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + m_taxes = new MInvoiceTax[list.size ()]; + list.toArray (m_taxes); + return m_taxes; + } // getTaxes + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + /** + * Is it a Credit Memo? + * @return true if CM + */ + public boolean isCreditMemo() + { + MDocType dt = MDocType.get(getCtx(), + getC_DocType_ID()==0 ? getC_DocTypeTarget_ID() : getC_DocType_ID()); + return MDocType.DOCBASETYPE_APCreditMemo.equals(dt.getDocBaseType()) + || MDocType.DOCBASETYPE_ARCreditMemo.equals(dt.getDocBaseType()); + } // isCreditMemo + + /** + * Set Processed. + * Propergate to Lines/Taxes + * @param processed processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + if (get_ID() == 0) + return; + String set = "SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE C_Invoice_ID=" + getC_Invoice_ID(); + int noLine = DB.executeUpdate("UPDATE C_InvoiceLine " + set, get_TrxName()); + int noTax = DB.executeUpdate("UPDATE C_InvoiceTax " + set, get_TrxName()); + m_lines = null; + m_taxes = null; + log.fine(processed + " - Lines=" + noLine + ", Tax=" + noTax); + } // setProcessed + + /** + * Validate Invoice Pay Schedule + * @return pay schedule is valid + */ + public boolean validatePaySchedule() + { + MInvoicePaySchedule[] schedule = MInvoicePaySchedule.getInvoicePaySchedule + (getCtx(), getC_Invoice_ID(), 0, get_TrxName()); + log.fine("#" + schedule.length); + if (schedule.length == 0) + { + setIsPayScheduleValid(false); + return false; + } + // Add up due amounts + BigDecimal total = Env.ZERO; + for (int i = 0; i < schedule.length; i++) + { + schedule[i].setParent(this); + BigDecimal due = schedule[i].getDueAmt(); + if (due != null) + total = total.add(due); + } + boolean valid = getGrandTotal().compareTo(total) == 0; + setIsPayScheduleValid(valid); + + // Update Schedule Lines + for (int i = 0; i < schedule.length; i++) + { + if (schedule[i].isValid() != valid) + { + schedule[i].setIsValid(valid); + schedule[i].save(get_TrxName()); + } + } + return valid; + } // validatePaySchedule + + + /************************************************************************** + * Before Save + * @param newRecord new + * @return true + */ + protected boolean beforeSave (boolean newRecord) + { + log.fine(""); + // No Partner Info - set Template + if (getC_BPartner_ID() == 0) + setBPartner(MBPartner.getTemplate(getCtx(), getAD_Client_ID())); + if (getC_BPartner_Location_ID() == 0) + setBPartner(new MBPartner(getCtx(), getC_BPartner_ID(), null)); + + // Price List + if (getM_PriceList_ID() == 0) + { + int ii = Env.getContextAsInt(getCtx(), "#M_PriceList_ID"); + if (ii != 0) + setM_PriceList_ID(ii); + else + { + String sql = "SELECT M_PriceList_ID FROM M_PriceList WHERE AD_Client_ID=? AND IsDefault='Y'"; + ii = DB.getSQLValue (null, sql, getAD_Client_ID()); + if (ii != 0) + setM_PriceList_ID (ii); + } + } + + // Currency + if (getC_Currency_ID() == 0) + { + String sql = "SELECT C_Currency_ID FROM M_PriceList WHERE M_PriceList_ID=?"; + int ii = DB.getSQLValue (null, sql, getM_PriceList_ID()); + if (ii != 0) + setC_Currency_ID (ii); + else + setC_Currency_ID(Env.getContextAsInt(getCtx(), "#C_Currency_ID")); + } + + // Sales Rep + if (getSalesRep_ID() == 0) + { + int ii = Env.getContextAsInt(getCtx(), "#SalesRep_ID"); + if (ii != 0) + setSalesRep_ID (ii); + } + + // Document Type + if (getC_DocType_ID() == 0) + setC_DocType_ID (0); // make sure it's set to 0 + if (getC_DocTypeTarget_ID() == 0) + setC_DocTypeTarget_ID(isSOTrx() ? MDocType.DOCBASETYPE_ARInvoice : MDocType.DOCBASETYPE_APInvoice); + + // Payment Term + if (getC_PaymentTerm_ID() == 0) + { + int ii = Env.getContextAsInt(getCtx(), "#C_PaymentTerm_ID"); + if (ii != 0) + setC_PaymentTerm_ID (ii); + else + { + String sql = "SELECT C_PaymentTerm_ID FROM C_PaymentTerm WHERE AD_Client_ID=? AND IsDefault='Y'"; + ii = DB.getSQLValue(null, sql, getAD_Client_ID()); + if (ii != 0) + setC_PaymentTerm_ID (ii); + } + } + return true; + } // beforeSave + + /** + * Before Delete + * @return true if it can be deleted + */ + protected boolean beforeDelete () + { + if (getC_Order_ID() != 0) + { + log.saveError("Error", Msg.getMsg(getCtx(), "CannotDelete")); + return false; + } + return true; + } // beforeDelete + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MInvoice[") + .append(get_ID()).append("-").append(getDocumentNo()) + .append(",GrandTotal=").append(getGrandTotal()); + if (m_lines != null) + sb.append(" (#").append(m_lines.length).append(")"); + sb.append ("]"); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + + /** + * After Save + * @param newRecord new + * @param success success + * @return success + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + if (!success || newRecord) + return success; + + if (is_ValueChanged("AD_Org_ID")) + { + String sql = "UPDATE C_InvoiceLine ol" + + " SET AD_Org_ID =" + + "(SELECT AD_Org_ID" + + " FROM C_Invoice o WHERE ol.C_Invoice_ID=o.C_Invoice_ID) " + + "WHERE C_Invoice_ID=" + getC_Invoice_ID(); + int no = DB.executeUpdate(sql, get_TrxName()); + log.fine("Lines -> #" + no); + } + return true; + } // afterSave + + + /** + * Set Price List (and Currency) when valid + * @param M_PriceList_ID price list + */ + public void setM_PriceList_ID (int M_PriceList_ID) + { + String sql = "SELECT M_PriceList_ID, C_Currency_ID " + + "FROM M_PriceList WHERE M_PriceList_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, M_PriceList_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + super.setM_PriceList_ID (rs.getInt(1)); + setC_Currency_ID (rs.getInt(2)); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "setM_PriceList_ID", e); + } + finally + { + try + { + if (pstmt != null) + pstmt.close (); + } + catch (Exception e) + {} + pstmt = null; + } + } // setM_PriceList_ID + + + /** + * Get Allocated Amt in Invoice Currency + * @return pos/neg amount or null + */ + public BigDecimal getAllocatedAmt () + { + BigDecimal retValue = null; + String sql = "SELECT SUM(currencyConvert(al.Amount+al.DiscountAmt+al.WriteOffAmt," + + "ah.C_Currency_ID, i.C_Currency_ID,ah.DateTrx,COALESCE(i.C_ConversionType_ID,0), al.AD_Client_ID,al.AD_Org_ID)) " + + "FROM C_AllocationLine al" + + " INNER JOIN C_AllocationHdr ah ON (al.C_AllocationHdr_ID=ah.C_AllocationHdr_ID)" + + " INNER JOIN C_Invoice i ON (al.C_Invoice_ID=i.C_Invoice_ID) " + + "WHERE al.C_Invoice_ID=?" + + " AND ah.IsActive='Y' AND al.IsActive='Y'"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Invoice_ID()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + retValue = rs.getBigDecimal(1); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + // log.fine("getAllocatedAmt - " + retValue); + // ? ROUND(NVL(v_AllocatedAmt,0), 2); + return retValue; + } // getAllocatedAmt + + /** + * Test Allocation (and set paid flag) + * @return true if updated + */ + public boolean testAllocation() + { + boolean change = false; + + if ( isProcessed() ) { + BigDecimal alloc = getAllocatedAmt(); // absolute + if (alloc == null) + alloc = Env.ZERO; + BigDecimal total = getGrandTotal(); + if (!isSOTrx()) + total = total.negate(); + if (isCreditMemo()) + total = total.negate(); + boolean test = total.compareTo(alloc) == 0; + change = test != isPaid(); + if (change) + setIsPaid(test); + log.fine("Paid=" + test + + " (" + alloc + "=" + total + ")"); + } + + return change; + } // testAllocation + + /** + * Set Paid Flag for invoices + * @param ctx context + * @param C_BPartner_ID if 0 all + * @param trxName transaction + */ + public static void setIsPaid (Properties ctx, int C_BPartner_ID, String trxName) + { + int counter = 0; + String sql = "SELECT * FROM C_Invoice " + + "WHERE IsPaid='N' AND DocStatus IN ('CO','CL')"; + if (C_BPartner_ID > 1) + sql += " AND C_BPartner_ID=?"; + else + sql += " AND AD_Client_ID=" + Env.getAD_Client_ID(ctx); + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + if (C_BPartner_ID > 1) + pstmt.setInt (1, C_BPartner_ID); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MInvoice invoice = new MInvoice(ctx, rs, trxName); + if (invoice.testAllocation()) + if (invoice.save()) + counter++; + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + s_log.config("#" + counter); + /**/ + } // setIsPaid + + /** + * Get Open Amount. + * Used by web interface + * @return Open Amt + */ + public BigDecimal getOpenAmt () + { + return getOpenAmt (true, null); + } // getOpenAmt + + /** + * Get Open Amount + * @param creditMemoAdjusted adjusted for CM (negative) + * @param paymentDate ignored Payment Date + * @return Open Amt + */ + public BigDecimal getOpenAmt (boolean creditMemoAdjusted, Timestamp paymentDate) + { + if (isPaid()) + return Env.ZERO; + // + if (m_openAmt == null) + { + m_openAmt = getGrandTotal(); + if (paymentDate != null) + { + // Payment Discount + // Payment Schedule + } + BigDecimal allocated = getAllocatedAmt(); + if (allocated != null) + { + allocated = allocated.abs(); // is absolute + m_openAmt = m_openAmt.subtract(allocated); + } + } + // + if (!creditMemoAdjusted) + return m_openAmt; + if (isCreditMemo()) + return m_openAmt.negate(); + return m_openAmt; + } // getOpenAmt + + + /** + * Get Document Status + * @return Document Status Clear Text + */ + public String getDocStatusName() + { + return MRefList.getListName(getCtx(), 131, getDocStatus()); + } // getDocStatusName + + + /************************************************************************** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID(), get_TrxName()); + if (re == null) + return null; + return re.getPDF(file); + } // createPDF + + /** + * Get PDF File Name + * @param documentDir directory + * @return file name + */ + public String getPDFFileName (String documentDir) + { + return getPDFFileName (documentDir, getC_Invoice_ID()); + } // getPDFFileName + + /** + * Get ISO Code of Currency + * @return Currency ISO + */ + public String getCurrencyISO() + { + return MCurrency.getISO_Code (getCtx(), getC_Currency_ID()); + } // getCurrencyISO + + /** + * Get Currency Precision + * @return precision + */ + public int getPrecision() + { + return MCurrency.getStdPrecision(getCtx(), getC_Currency_ID()); + } // getPrecision + + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // process + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info("unlockIt - " + toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info("invalidateIt - " + toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + /** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + MDocType dt = MDocType.get(getCtx(), getC_DocTypeTarget_ID()); + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getDateAcct(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + // Lines + MInvoiceLine[] lines = getLines(true); + if (lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + // No Cash Book + if (PAYMENTRULE_Cash.equals(getPaymentRule()) + && MCashBook.get(getCtx(), getAD_Org_ID(), getC_Currency_ID()) == null) + { + m_processMsg = "@NoCashBook@"; + return DocAction.STATUS_Invalid; + } + + // Convert/Check DocType + if (getC_DocType_ID() != getC_DocTypeTarget_ID() ) + setC_DocType_ID(getC_DocTypeTarget_ID()); + if (getC_DocType_ID() == 0) + { + m_processMsg = "No Document Type"; + return DocAction.STATUS_Invalid; + } + + explodeBOM(); + if (!calculateTaxTotal()) // setTotals + { + m_processMsg = "Error calculating Tax"; + return DocAction.STATUS_Invalid; + } + + createPaySchedule(); + + // Credit Status + if (isSOTrx() && !isReversal()) + { + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), null); + if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus())) + { + m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + } + + // Landed Costs + if (!isSOTrx()) + { + for (int i = 0; i < lines.length; i++) + { + MInvoiceLine line = lines[i]; + String error = line.allocateLandedCosts(); + if (error != null && error.length() > 0) + { + m_processMsg = error; + return DocAction.STATUS_Invalid; + } + } + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Add up Amounts + m_justPrepared = true; + if (!DOCACTION_Complete.equals(getDocAction())) + setDocAction(DOCACTION_Complete); + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Explode non stocked BOM. + */ + private void explodeBOM () + { + String where = "AND IsActive='Y' AND EXISTS " + + "(SELECT * FROM M_Product p WHERE C_InvoiceLine.M_Product_ID=p.M_Product_ID" + + " AND p.IsBOM='Y' AND p.IsVerified='Y' AND p.IsStocked='N')"; + // + String sql = "SELECT COUNT(*) FROM C_InvoiceLine " + + "WHERE C_Invoice_ID=? " + where; + int count = DB.getSQLValue(get_TrxName(), sql, getC_Invoice_ID()); + while (count != 0) + { + renumberLines (100); + + // Order Lines with non-stocked BOMs + MInvoiceLine[] lines = getLines (where); + for (int i = 0; i < lines.length; i++) + { + MInvoiceLine line = lines[i]; + MProduct product = MProduct.get (getCtx(), line.getM_Product_ID()); + log.fine(product.getName()); + // New Lines + int lineNo = line.getLine (); + MProductBOM[] boms = MProductBOM.getBOMLines (product); + for (int j = 0; j < boms.length; j++) + { + MProductBOM bom = boms[j]; + MInvoiceLine newLine = new MInvoiceLine (this); + newLine.setLine (++lineNo); + newLine.setM_Product_ID (bom.getProduct().getM_Product_ID(), + bom.getProduct().getC_UOM_ID()); + newLine.setQty (line.getQtyInvoiced().multiply( + bom.getBOMQty ())); // Invoiced/Entered + if (bom.getDescription () != null) + newLine.setDescription (bom.getDescription ()); + // + newLine.setPrice (); + newLine.save (get_TrxName()); + } + // Convert into Comment Line + line.setM_Product_ID (0); + line.setM_AttributeSetInstance_ID (0); + line.setPriceEntered (Env.ZERO); + line.setPriceActual (Env.ZERO); + line.setPriceLimit (Env.ZERO); + line.setPriceList (Env.ZERO); + line.setLineNetAmt (Env.ZERO); + // + String description = product.getName (); + if (product.getDescription () != null) + description += " " + product.getDescription (); + if (line.getDescription () != null) + description += " " + line.getDescription (); + line.setDescription (description); + line.save (get_TrxName()); + } // for all lines with BOM + + m_lines = null; + count = DB.getSQLValue (get_TrxName(), sql, getC_Invoice_ID ()); + renumberLines (10); + } // while count != 0 + } // explodeBOM + + /** + * Calculate Tax and Total + * @return true if calculated + */ + private boolean calculateTaxTotal() + { + log.fine(""); + // Delete Taxes + DB.executeUpdate("DELETE C_InvoiceTax WHERE C_Invoice_ID=" + getC_Invoice_ID(), get_TrxName()); + m_taxes = null; + + // Lines + BigDecimal totalLines = Env.ZERO; + ArrayList taxList = new ArrayList(); + MInvoiceLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInvoiceLine line = lines[i]; + /** Sync ownership for SO + if (isSOTrx() && line.getAD_Org_ID() != getAD_Org_ID()) + { + line.setAD_Org_ID(getAD_Org_ID()); + line.save(); + } **/ + Integer taxID = new Integer(line.getC_Tax_ID()); + if (!taxList.contains(taxID)) + { + MInvoiceTax iTax = MInvoiceTax.get (line, getPrecision(), + false, get_TrxName()); // current Tax + if (iTax != null) + { + iTax.setIsTaxIncluded(isTaxIncluded()); + if (!iTax.calculateTaxFromLines()) + return false; + if (!iTax.save()) + return false; + taxList.add(taxID); + } + } + totalLines = totalLines.add(line.getLineNetAmt()); + } + + // Taxes + BigDecimal grandTotal = totalLines; + MInvoiceTax[] taxes = getTaxes(true); + for (int i = 0; i < taxes.length; i++) + { + MInvoiceTax iTax = taxes[i]; + MTax tax = iTax.getTax(); + if (tax.isSummary()) + { + MTax[] cTaxes = tax.getChildTaxes(false); // Multiple taxes + for (int j = 0; j < cTaxes.length; j++) + { + MTax cTax = cTaxes[j]; + BigDecimal taxAmt = cTax.calculateTax(iTax.getTaxBaseAmt(), isTaxIncluded(), getPrecision()); + // + MInvoiceTax newITax = new MInvoiceTax(getCtx(), 0, get_TrxName()); + newITax.setClientOrg(this); + newITax.setC_Invoice_ID(getC_Invoice_ID()); + newITax.setC_Tax_ID(cTax.getC_Tax_ID()); + newITax.setPrecision(getPrecision()); + newITax.setIsTaxIncluded(isTaxIncluded()); + newITax.setTaxBaseAmt(iTax.getTaxBaseAmt()); + newITax.setTaxAmt(taxAmt); + if (!newITax.save(get_TrxName())) + return false; + // + if (!isTaxIncluded()) + grandTotal = grandTotal.add(taxAmt); + } + if (!iTax.delete(true, get_TrxName())) + return false; + } + else + { + if (!isTaxIncluded()) + grandTotal = grandTotal.add(iTax.getTaxAmt()); + } + } + // + setTotalLines(totalLines); + setGrandTotal(grandTotal); + return true; + } // calculateTaxTotal + + + /** + * (Re) Create Pay Schedule + * @return true if valid schedule + */ + private boolean createPaySchedule() + { + if (getC_PaymentTerm_ID() == 0) + return false; + MPaymentTerm pt = new MPaymentTerm(getCtx(), getC_PaymentTerm_ID(), null); + log.fine(pt.toString()); + return pt.apply(this); // calls validate pay schedule + } // createPaySchedule + + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info(toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info(toString()); + setIsApproved(false); + return true; + } // rejectIt + + /** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Implicit Approval + if (!isApproved()) + approveIt(); + log.info(toString()); + StringBuffer info = new StringBuffer(); + + // Create Cash + if (PAYMENTRULE_Cash.equals(getPaymentRule())) + { + // Modifications for POSterita + /* + MCash cash = MCash.get (getCtx(), getAD_Org_ID(), + getDateInvoiced(), getC_Currency_ID(), get_TrxName()); + */ + + MCash cash; + + int posId = Env.getContextAsInt(getCtx(),Env.POS_ID); + + if (posId != 0) + { + MPOS pos = new MPOS(getCtx(),posId,get_TrxName()); + int cashBookId = pos.getC_CashBook_ID(); + cash = MCash.get(getCtx(),cashBookId,getDateInvoiced(),get_TrxName()); + } + else + { + cash = MCash.get (getCtx(), getAD_Org_ID(), + getDateInvoiced(), getC_Currency_ID(), get_TrxName()); + } + + // End Posterita Modifications + + if (cash == null || cash.get_ID() == 0) + { + m_processMsg = "@NoCashBook@"; + return DocAction.STATUS_Invalid; + } + MCashLine cl = new MCashLine (cash); + cl.setInvoice(this); + if (!cl.save(get_TrxName())) + { + m_processMsg = "Could not save Cash Journal Line"; + return DocAction.STATUS_Invalid; + } + info.append("@C_Cash_ID@: " + cash.getName() + " #" + cl.getLine()); + setC_CashLine_ID(cl.getC_CashLine_ID()); + } // CashBook + + // Update Order & Match + int matchInv = 0; + int matchPO = 0; + MInvoiceLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInvoiceLine line = lines[i]; + + // Update Order Line + MOrderLine ol = null; + if (line.getC_OrderLine_ID() != 0) + { + if (isSOTrx() + || line.getM_Product_ID() == 0) + { + ol = new MOrderLine (getCtx(), line.getC_OrderLine_ID(), get_TrxName()); + if (line.getQtyInvoiced() != null) + ol.setQtyInvoiced(ol.getQtyInvoiced().add(line.getQtyInvoiced())); + if (!ol.save(get_TrxName())) + { + m_processMsg = "Could not update Order Line"; + return DocAction.STATUS_Invalid; + } + } + // Order Invoiced Qty updated via Matching Inv-PO + else if (!isSOTrx() + && line.getM_Product_ID() != 0 + && !isReversal()) + { + // MatchPO is created also from MInOut when Invoice exists before Shipment + BigDecimal matchQty = line.getQtyInvoiced(); + MMatchPO po = MMatchPO.create (line, null, + getDateInvoiced(), matchQty); + if (!po.save(get_TrxName())) + { + m_processMsg = "Could not create PO Matching"; + return DocAction.STATUS_Invalid; + } + else + matchPO++; + } + } + + // Matching - Inv-Shipment + if (!isSOTrx() + && line.getM_InOutLine_ID() != 0 + && line.getM_Product_ID() != 0 + && !isReversal()) + { + MInOutLine receiptLine = new MInOutLine (getCtx(),line.getM_InOutLine_ID(), get_TrxName()); + BigDecimal matchQty = line.getQtyInvoiced(); + + if (receiptLine.getMovementQty().compareTo(matchQty) < 0) + matchQty = receiptLine.getMovementQty(); + + MMatchInv inv = new MMatchInv(line, getDateInvoiced(), matchQty); + if (!inv.save(get_TrxName())) + { + m_processMsg = "Could not create Invoice Matching"; + return DocAction.STATUS_Invalid; + } + else + matchInv++; + } + } // for all lines + if (matchInv > 0) + info.append(" @M_MatchInv_ID@#").append(matchInv).append(" "); + if (matchPO > 0) + info.append(" @M_MatchPO_ID@#").append(matchPO).append(" "); + + + + // Update BP Statistics + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + // Update total revenue and balance / credit limit (reversed on AllocationLine.processIt) + BigDecimal invAmt = MConversionRate.convertBase(getCtx(), getGrandTotal(true), // CM adjusted + getC_Currency_ID(), getDateAcct(), 0, getAD_Client_ID(), getAD_Org_ID()); + if (invAmt == null) + { + m_processMsg = "Could not convert C_Currency_ID=" + getC_Currency_ID() + + " to base C_Currency_ID=" + MClient.get(Env.getCtx()).getC_Currency_ID(); + return DocAction.STATUS_Invalid; + } + // Total Balance + BigDecimal newBalance = bp.getTotalOpenBalance(false); + if (newBalance == null) + newBalance = Env.ZERO; + if (isSOTrx()) + { + newBalance = newBalance.add(invAmt); + // + if (bp.getFirstSale() == null) + bp.setFirstSale(getDateInvoiced()); + BigDecimal newLifeAmt = bp.getActualLifeTimeValue(); + if (newLifeAmt == null) + newLifeAmt = invAmt; + else + newLifeAmt = newLifeAmt.add(invAmt); + BigDecimal newCreditAmt = bp.getSO_CreditUsed(); + if (newCreditAmt == null) + newCreditAmt = invAmt; + else + newCreditAmt = newCreditAmt.add(invAmt); + // + log.fine("GrandTotal=" + getGrandTotal(true) + "(" + invAmt + + ") BP Life=" + bp.getActualLifeTimeValue() + "->" + newLifeAmt + + ", Credit=" + bp.getSO_CreditUsed() + "->" + newCreditAmt + + ", Balance=" + bp.getTotalOpenBalance(false) + " -> " + newBalance); + bp.setActualLifeTimeValue(newLifeAmt); + bp.setSO_CreditUsed(newCreditAmt); + } // SO + else + { + newBalance = newBalance.subtract(invAmt); + log.fine("GrandTotal=" + getGrandTotal(true) + "(" + invAmt + + ") Balance=" + bp.getTotalOpenBalance(false) + " -> " + newBalance); + } + bp.setTotalOpenBalance(newBalance); + bp.setSOCreditStatus(); + if (!bp.save(get_TrxName())) + { + m_processMsg = "Could not update Business Partner"; + return DocAction.STATUS_Invalid; + } + + // User - Last Result/Contact + if (getAD_User_ID() != 0) + { + MUser user = new MUser (getCtx(), getAD_User_ID(), get_TrxName()); + user.setLastContact(new Timestamp(System.currentTimeMillis())); + user.setLastResult(Msg.translate(getCtx(), "C_Invoice_ID") + ": " + getDocumentNo()); + if (!user.save(get_TrxName())) + { + m_processMsg = "Could not update Business Partner User"; + return DocAction.STATUS_Invalid; + } + } // user + + // Update Project + if (isSOTrx() && getC_Project_ID() != 0) + { + MProject project = new MProject (getCtx(), getC_Project_ID(), get_TrxName()); + BigDecimal amt = getGrandTotal(true); + int C_CurrencyTo_ID = project.getC_Currency_ID(); + if (C_CurrencyTo_ID != getC_Currency_ID()) + amt = MConversionRate.convert(getCtx(), amt, getC_Currency_ID(), C_CurrencyTo_ID, + getDateAcct(), 0, getAD_Client_ID(), getAD_Org_ID()); + if (amt == null) + { + m_processMsg = "Could not convert C_Currency_ID=" + getC_Currency_ID() + + " to Project C_Currency_ID=" + C_CurrencyTo_ID; + return DocAction.STATUS_Invalid; + } + BigDecimal newAmt = project.getInvoicedAmt(); + if (newAmt == null) + newAmt = amt; + else + newAmt = newAmt.add(amt); + log.fine("GrandTotal=" + getGrandTotal(true) + "(" + amt + + ") Project " + project.getName() + + " - Invoiced=" + project.getInvoicedAmt() + "->" + newAmt); + project.setInvoicedAmt(newAmt); + if (!project.save(get_TrxName())) + { + m_processMsg = "Could not update Project"; + return DocAction.STATUS_Invalid; + } + } // project + + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + // Counter Documents + MInvoice counter = createCounterDoc(); + if (counter != null) + info.append(" - @CounterDoc@: @C_Invoice_ID@=").append(counter.getDocumentNo()); + + m_processMsg = info.toString().trim(); + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setDateInvoiced(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Create Counter Document + * @return counter invoice + */ + private MInvoice createCounterDoc() + { + // Is this a counter doc ? + if (getRef_Invoice_ID() != 0) + return null; + + // Org Must be linked to BPartner + MOrg org = MOrg.get(getCtx(), getAD_Org_ID()); + int counterC_BPartner_ID = org.getLinkedC_BPartner_ID(get_TrxName()); + if (counterC_BPartner_ID == 0) + return null; + // Business Partner needs to be linked to Org + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), null); + int counterAD_Org_ID = bp.getAD_OrgBP_ID_Int(); + if (counterAD_Org_ID == 0) + return null; + + MBPartner counterBP = new MBPartner (getCtx(), counterC_BPartner_ID, null); + MOrgInfo counterOrgInfo = MOrgInfo.get(getCtx(), counterAD_Org_ID); + log.info("Counter BP=" + counterBP.getName()); + + // Document Type + int C_DocTypeTarget_ID = 0; + MDocTypeCounter counterDT = MDocTypeCounter.getCounterDocType(getCtx(), getC_DocType_ID()); + if (counterDT != null) + { + log.fine(counterDT.toString()); + if (!counterDT.isCreateCounter() || !counterDT.isValid()) + return null; + C_DocTypeTarget_ID = counterDT.getCounter_C_DocType_ID(); + } + else // indirect + { + C_DocTypeTarget_ID = MDocTypeCounter.getCounterDocType_ID(getCtx(), getC_DocType_ID()); + log.fine("Indirect C_DocTypeTarget_ID=" + C_DocTypeTarget_ID); + if (C_DocTypeTarget_ID <= 0) + return null; + } + + // Deep Copy + MInvoice counter = copyFrom(this, getDateInvoiced(), + C_DocTypeTarget_ID, !isSOTrx(), true, get_TrxName(), true); + // + counter.setAD_Org_ID(counterAD_Org_ID); + // counter.setM_Warehouse_ID(counterOrgInfo.getM_Warehouse_ID()); + // + counter.setBPartner(counterBP); + // Refernces (Should not be required + counter.setSalesRep_ID(getSalesRep_ID()); + counter.save(get_TrxName()); + + // Update copied lines + MInvoiceLine[] counterLines = counter.getLines(true); + for (int i = 0; i < counterLines.length; i++) + { + MInvoiceLine counterLine = counterLines[i]; + counterLine.setClientOrg(counter); + counterLine.setInvoice(counter); // copies header values (BP, etc.) + counterLine.setPrice(); + counterLine.setTax(); + // + counterLine.save(get_TrxName()); + } + + log.fine(counter.toString()); + + // Document Action + if (counterDT != null) + { + if (counterDT.getDocAction() != null) + { + counter.setDocAction(counterDT.getDocAction()); + counter.processIt(counterDT.getDocAction()); + counter.save(get_TrxName()); + } + } + return counter; + } // createCounterDoc + + /** + * Void Document. + * @return true if success + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + if (DOCSTATUS_Closed.equals(getDocStatus()) + || DOCSTATUS_Reversed.equals(getDocStatus()) + || DOCSTATUS_Voided.equals(getDocStatus())) + { + m_processMsg = "Document Closed: " + getDocStatus(); + setDocAction(DOCACTION_None); + return false; + } + + // Not Processed + if (DOCSTATUS_Drafted.equals(getDocStatus()) + || DOCSTATUS_Invalid.equals(getDocStatus()) + || DOCSTATUS_InProgress.equals(getDocStatus()) + || DOCSTATUS_Approved.equals(getDocStatus()) + || DOCSTATUS_NotApproved.equals(getDocStatus()) ) + { + // Set lines to 0 + MInvoiceLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MInvoiceLine line = lines[i]; + BigDecimal old = line.getQtyInvoiced(); + if (old.compareTo(Env.ZERO) != 0) + { + line.setQty(Env.ZERO); + line.setTaxAmt(Env.ZERO); + line.setLineNetAmt(Env.ZERO); + line.setLineTotalAmt(Env.ZERO); + line.addDescription(Msg.getMsg(getCtx(), "Voided") + " (" + old + ")"); + // Unlink Shipment + if (line.getM_InOutLine_ID() != 0) + { + MInOutLine ioLine = new MInOutLine(getCtx(), line.getM_InOutLine_ID(), get_TrxName()); + ioLine.setIsInvoiced(false); + ioLine.save(get_TrxName()); + line.setM_InOutLine_ID(0); + } + line.save(get_TrxName()); + } + } + addDescription(Msg.getMsg(getCtx(), "Voided")); + setIsPaid(true); + setC_Payment_ID(0); + } + else + { + return reverseCorrectIt(); + } + + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // voidIt + + /** + * Close Document. + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // closeIt + + /** + * Reverse Correction - same date + * @return true if success + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (!MPeriod.isOpen(getCtx(), getDateAcct(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return false; + } + // + MAllocationHdr[] allocations = MAllocationHdr.getOfInvoice(getCtx(), + getC_Invoice_ID(), get_TrxName()); + for (int i = 0; i < allocations.length; i++) + { + allocations[i].setDocAction(DocAction.ACTION_Reverse_Correct); + allocations[i].reverseCorrectIt(); + allocations[i].save(get_TrxName()); + } + // Reverse/Delete Matching + if (!isSOTrx()) + { + MMatchInv[] mInv = MMatchInv.getInvoice(getCtx(), getC_Invoice_ID(), get_TrxName()); + for (int i = 0; i < mInv.length; i++) + mInv[i].delete(true); + MMatchPO[] mPO = MMatchPO.getInvoice(getCtx(), getC_Invoice_ID(), get_TrxName()); + for (int i = 0; i < mPO.length; i++) + { + if (mPO[i].getM_InOutLine_ID() == 0) + mPO[i].delete(true); + else + { + mPO[i].setC_InvoiceLine_ID(null); + mPO[i].save(get_TrxName()); + } + } + } + // + load(get_TrxName()); // reload allocation reversal info + + // Deep Copy + MInvoice reversal = copyFrom (this, getDateInvoiced(), + getC_DocType_ID(), isSOTrx(), false, get_TrxName(), true); + if (reversal == null) + { + m_processMsg = "Could not create Invoice Reversal"; + return false; + } + reversal.setReversal(true); + + // Reverse Line Qty + MInvoiceLine[] rLines = reversal.getLines(false); + for (int i = 0; i < rLines.length; i++) + { + MInvoiceLine rLine = rLines[i]; + rLine.setQtyEntered(rLine.getQtyEntered().negate()); + rLine.setQtyInvoiced(rLine.getQtyInvoiced().negate()); + rLine.setLineNetAmt(rLine.getLineNetAmt().negate()); + if (rLine.getTaxAmt() != null && rLine.getTaxAmt().compareTo(Env.ZERO) != 0) + rLine.setTaxAmt(rLine.getTaxAmt().negate()); + if (rLine.getLineTotalAmt() != null && rLine.getLineTotalAmt().compareTo(Env.ZERO) != 0) + rLine.setLineTotalAmt(rLine.getLineTotalAmt().negate()); + if (!rLine.save(get_TrxName())) + { + m_processMsg = "Could not correct Invoice Reversal Line"; + return false; + } + } + reversal.setC_Order_ID(getC_Order_ID()); + reversal.addDescription("{->" + getDocumentNo() + ")"); + // + if (!reversal.processIt(DocAction.ACTION_Complete)) + { + m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg(); + return false; + } + reversal.setC_Payment_ID(0); + reversal.setIsPaid(true); + reversal.closeIt(); + reversal.setProcessing (false); + reversal.setDocStatus(DOCSTATUS_Reversed); + reversal.setDocAction(DOCACTION_None); + reversal.save(get_TrxName()); + m_processMsg = reversal.getDocumentNo(); + // + addDescription("(" + reversal.getDocumentNo() + "<-)"); + + // Clean up Reversed (this) + MInvoiceLine[] iLines = getLines(false); + for (int i = 0; i < iLines.length; i++) + { + MInvoiceLine iLine = iLines[i]; + if (iLine.getM_InOutLine_ID() != 0) + { + MInOutLine ioLine = new MInOutLine(getCtx(), iLine.getM_InOutLine_ID(), get_TrxName()); + ioLine.setIsInvoiced(false); + ioLine.save(get_TrxName()); + // Reconsiliation + iLine.setM_InOutLine_ID(0); + iLine.save(get_TrxName()); + } + } + setProcessed(true); + setDocStatus(DOCSTATUS_Reversed); // may come from void + setDocAction(DOCACTION_None); + setC_Payment_ID(0); + setIsPaid(true); + + // Create Allocation + MAllocationHdr alloc = new MAllocationHdr(getCtx(), false, getDateAcct(), + getC_Currency_ID(), + Msg.translate(getCtx(), "C_Invoice_ID") + ": " + getDocumentNo() + "/" + reversal.getDocumentNo(), + get_TrxName()); + alloc.setAD_Org_ID(getAD_Org_ID()); + if (alloc.save()) + { + // Amount + BigDecimal gt = getGrandTotal(true); + if (!isSOTrx()) + gt = gt.negate(); + // Orig Line + MAllocationLine aLine = new MAllocationLine (alloc, gt, + Env.ZERO, Env.ZERO, Env.ZERO); + aLine.setC_Invoice_ID(getC_Invoice_ID()); + aLine.save(); + // Reversal Line + MAllocationLine rLine = new MAllocationLine (alloc, gt.negate(), + Env.ZERO, Env.ZERO, Env.ZERO); + rLine.setC_Invoice_ID(reversal.getC_Invoice_ID()); + rLine.save(); + // Process It + if (alloc.processIt(DocAction.ACTION_Complete)) + alloc.save(); + } + + //MZ Goodwill + if (!isSOTrx()) + { + // delete Matched Invoice Cost Detail + MInvoiceLine[] lines = getLines(); + for (int i = 0; i < lines.length; i++) + { + MCostDetail cd = MCostDetail.get (getCtx(), "C_InvoiceLine_ID=? AND M_AttributeSetInstance_ID=?", + lines[i].getC_InvoiceLine_ID(), lines[i].getM_AttributeSetInstance_ID(), get_TrxName()); + if (cd != null) + { + cd.setProcessed(false); + cd.delete(true); + } + } + } + //End MZ + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + return true; + } // reverseCorrectIt + + /** + * Reverse Accrual - none + * @return false + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return false; + } // reverseAccrualIt + + /** + * Re-activate + * @return false + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + + return false; + } // reActivateIt + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Grand Total = 123.00 (#1) + sb.append(": "). + append(Msg.translate(getCtx(),"GrandTotal")).append("=").append(getGrandTotal()) + .append(" (#").append(getLines(false).length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getSalesRep_ID(); + } // getDoc_User_ID + + /** + * Get Document Approval Amount + * @return amount + */ + public BigDecimal getApprovalAmt() + { + return getGrandTotal(); + } // getApprovalAmt + + /** + * + * @param rma + */ + public void setRMA(MRMA rma) + { + setM_RMA_ID(rma.getM_RMA_ID()); + setAD_Org_ID(rma.getAD_Org_ID()); + setDescription(rma.getDescription()); + setC_BPartner_ID(rma.getC_BPartner_ID()); + setSalesRep_ID(rma.getSalesRep_ID()); + + setGrandTotal(rma.getAmt()); + setIsSOTrx(rma.isSOTrx()); + setTotalLines(rma.getAmt()); + + MInvoice originalInvoice = rma.getOriginalInvoice(); + + if (originalInvoice == null) + { + throw new IllegalStateException("Not invoiced - RMA: " + rma.getDocumentNo()); + } + + setC_BPartner_Location_ID(originalInvoice.getC_BPartner_Location_ID()); + setAD_User_ID(originalInvoice.getAD_User_ID()); + setC_Currency_ID(originalInvoice.getC_Currency_ID()); + setIsTaxIncluded(originalInvoice.isTaxIncluded()); + setM_PriceList_ID(originalInvoice.getM_PriceList_ID()); + setC_Project_ID(originalInvoice.getC_Project_ID()); + setC_Activity_ID(originalInvoice.getC_Activity_ID()); + setC_Campaign_ID(originalInvoice.getC_Campaign_ID()); + setUser1_ID(originalInvoice.getUser1_ID()); + setUser2_ID(originalInvoice.getUser2_ID()); + } + +} // MInvoice diff --git a/base/src/org/compiere/model/MJournal.java b/base/src/org/compiere/model/MJournal.java new file mode 100644 index 0000000000..429d9c9774 --- /dev/null +++ b/base/src/org/compiere/model/MJournal.java @@ -0,0 +1,874 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.process.*; +import org.compiere.util.*; + +/** + * GL Journal Model + * + * @author Jorg Janke + * @version $Id: MJournal.java,v 1.3 2006/07/30 00:51:03 jjanke Exp $ + * + * @author Teo Sarca, SC ARHIPAC SERVICE SRL + *
  • BF [ 1619150 ] Usability/Consistency: reversed gl journal description + *
  • BF [ 1775358 ] GL Journal DateAcct/C_Period_ID issue + */ +public class MJournal extends X_GL_Journal implements DocAction +{ + /** + * Standard Constructor + * @param ctx context + * @param GL_Journal_ID id + * @param trxName transaction + */ + public MJournal (Properties ctx, int GL_Journal_ID, String trxName) + { + super (ctx, GL_Journal_ID, trxName); + if (GL_Journal_ID == 0) + { + // setGL_Journal_ID (0); // PK + // setC_AcctSchema_ID (0); + // setC_Currency_ID (0); + // setC_DocType_ID (0); + // setC_Period_ID (0); + // + setCurrencyRate (Env.ONE); + // setC_ConversionType_ID(0); + setDateAcct (new Timestamp(System.currentTimeMillis())); + setDateDoc (new Timestamp(System.currentTimeMillis())); + // setDescription (null); + setDocAction (DOCACTION_Complete); + setDocStatus (DOCSTATUS_Drafted); + // setDocumentNo (null); + // setGL_Category_ID (0); + setPostingType (POSTINGTYPE_Actual); + setTotalCr (Env.ZERO); + setTotalDr (Env.ZERO); + setIsApproved (false); + setIsPrinted (false); + setPosted (false); + setProcessed(false); + } + } // MJournal + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MJournal (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MJournal + + /** + * Parent Constructor. + * @param parent batch + */ + public MJournal (MJournalBatch parent) + { + this (parent.getCtx(), 0, parent.get_TrxName()); + setClientOrg(parent); + setGL_JournalBatch_ID(parent.getGL_JournalBatch_ID()); + setC_DocType_ID(parent.getC_DocType_ID()); + setPostingType(parent.getPostingType()); + // + setDateDoc(parent.getDateDoc()); + setC_Period_ID(parent.getC_Period_ID()); + setDateAcct(parent.getDateAcct()); + setC_Currency_ID(parent.getC_Currency_ID()); + } // MJournal + + /** + * Copy Constructor. + * Dos not copy: Dates/Period + * @param original original + */ + public MJournal (MJournal original) + { + this (original.getCtx(), 0, original.get_TrxName()); + setClientOrg(original); + setGL_JournalBatch_ID(original.getGL_JournalBatch_ID()); + // + setC_AcctSchema_ID(original.getC_AcctSchema_ID()); + setGL_Budget_ID(original.getGL_Budget_ID()); + setGL_Category_ID(original.getGL_Category_ID()); + setPostingType(original.getPostingType()); + setDescription(original.getDescription()); + setC_DocType_ID(original.getC_DocType_ID()); + setControlAmt(original.getControlAmt()); + // + setC_Currency_ID(original.getC_Currency_ID()); + setC_ConversionType_ID(original.getC_ConversionType_ID()); + setCurrencyRate(original.getCurrencyRate()); + + // setDateDoc(original.getDateDoc()); + // setDateAcct(original.getDateAcct()); + // setC_Period_ID(original.getC_Period_ID()); + } // MJournal + + + /** + * Overwrite Client/Org if required + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + public void setClientOrg (int AD_Client_ID, int AD_Org_ID) + { + super.setClientOrg(AD_Client_ID, AD_Org_ID); + } // setClientOrg + + /** + * Set Accounting Date. + * Set also Period if not set earlier + * @param DateAcct date + */ + public void setDateAcct (Timestamp DateAcct) + { + super.setDateAcct(DateAcct); + if (DateAcct == null) + return; + if (getC_Period_ID() != 0) + return; + int C_Period_ID = MPeriod.getC_Period_ID(getCtx(), DateAcct); + if (C_Period_ID == 0) + log.warning("setDateAcct - Period not found"); + else + setC_Period_ID(C_Period_ID); + } // setDateAcct + + /** + * Set Currency Info + * @param C_Currency_ID currenct + * @param C_ConversionType_ID type + * @param CurrencyRate rate + */ + public void setCurrency (int C_Currency_ID, int C_ConversionType_ID, BigDecimal CurrencyRate) + { + if (C_Currency_ID != 0) + setC_Currency_ID(C_Currency_ID); + if (C_ConversionType_ID != 0) + setC_ConversionType_ID(C_ConversionType_ID); + if (CurrencyRate != null && CurrencyRate.compareTo(Env.ZERO) == 0) + setCurrencyRate(CurrencyRate); + } // setCurrency + + /** + * Add to Description + * @param description text + * @since 3.1.4 + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } + + /************************************************************************** + * Get Journal Lines + * @param requery requery + * @return Array of lines + */ + public MJournalLine[] getLines (boolean requery) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM GL_JournalLine WHERE GL_Journal_ID=? ORDER BY Line"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getGL_Journal_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MJournalLine (getCtx(), rs, get_TrxName())); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (SQLException ex) + { + log.log(Level.SEVERE, "getLines", ex); + } + try + { + if (pstmt != null) + pstmt.close(); + } + catch (SQLException ex1) + { + } + pstmt = null; + // + MJournalLine[] retValue = new MJournalLine[list.size()]; + list.toArray(retValue); + return retValue; + } // getLines + + /** + * Copy Lines from other Journal + * @param fromJournal Journal + * @param dateAcct date used - if null original + * @param typeCR type of copying (C)orrect=negate - (R)everse=flip dr/cr - otherwise just copy + * @return number of lines copied + */ + public int copyLinesFrom (MJournal fromJournal, Timestamp dateAcct, char typeCR) + { + if (isProcessed() || fromJournal == null) + return 0; + int count = 0; + MJournalLine[] fromLines = fromJournal.getLines(false); + for (int i = 0; i < fromLines.length; i++) + { + MJournalLine toLine = new MJournalLine (getCtx(), 0, fromJournal.get_TrxName()); + PO.copyValues(fromLines[i], toLine, getAD_Client_ID(), getAD_Org_ID()); + toLine.setGL_Journal_ID(getGL_Journal_ID()); + // + if (dateAcct != null) + toLine.setDateAcct(dateAcct); + // Amounts + if (typeCR == 'C') // correct + { + toLine.setAmtSourceDr(fromLines[i].getAmtSourceDr().negate()); + toLine.setAmtSourceCr(fromLines[i].getAmtSourceCr().negate()); + } + else if (typeCR == 'R') // reverse + { + toLine.setAmtSourceDr(fromLines[i].getAmtSourceCr()); + toLine.setAmtSourceCr(fromLines[i].getAmtSourceDr()); + } + toLine.setIsGenerated(true); + toLine.setProcessed(false); + if (toLine.save()) + count++; + } + if (fromLines.length != count) + log.log(Level.SEVERE, "Line difference - JournalLines=" + fromLines.length + " <> Saved=" + count); + + return count; + } // copyLinesFrom + + /** + * Set Processed. + * Propergate to Lines/Taxes + * @param processed processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + if (get_ID() == 0) + return; + String sql = "UPDATE GL_JournalLine SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE GL_Journal_ID=" + getGL_Journal_ID(); + int noLine = DB.executeUpdate(sql, get_TrxName()); + log.fine(processed + " - Lines=" + noLine); + } // setProcessed + + + /************************************************************************** + * Before Save + * @param newRecord new + * @return true + */ + protected boolean beforeSave (boolean newRecord) + { + // Imported Journals may not have date + if (getDateDoc() == null) + { + if (getDateAcct() == null) + setDateDoc(new Timestamp(System.currentTimeMillis())); + else + setDateDoc(getDateAcct()); + } + if (getDateAcct() == null) + setDateAcct(getDateDoc()); + + // Update DateAcct on lines - teo_sarca BF [ 1775358 ] + if (is_ValueChanged(COLUMNNAME_DateAcct)) { + int no = DB.executeUpdate( + "UPDATE GL_JournalLine SET "+MJournalLine.COLUMNNAME_DateAcct+"=? WHERE GL_Journal_ID=?", + new Object[]{getDateAcct(), getGL_Journal_ID()}, + false, get_TrxName()); + log.finest("Updated GL_JournalLine.DateAcct #" + no); + } + return true; + } // beforeSave + + + /** + * After Save. + * Update Batch Total + * @param newRecord true if new record + * @param success true if success + * @return success + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + if (!success) + return success; + return updateBatch(); + } // afterSave + + /** + * After Delete + * @param success true if deleted + * @return true if success + */ + protected boolean afterDelete (boolean success) + { + if (!success) + return success; + return updateBatch(); + } // afterDelete + + /** + * Update Batch total + * @return true if ok + */ + private boolean updateBatch() + { + String sql = "UPDATE GL_JournalBatch jb" + + " SET (TotalDr, TotalCr) = (SELECT COALESCE(SUM(TotalDr),0), COALESCE(SUM(TotalCr),0)" + + " FROM GL_Journal j WHERE j.IsActive='Y' AND jb.GL_JournalBatch_ID=j.GL_JournalBatch_ID) " + + "WHERE GL_JournalBatch_ID=" + getGL_JournalBatch_ID(); + int no = DB.executeUpdate(sql, get_TrxName()); + if (no != 1) + log.warning("afterSave - Update Batch #" + no); + return no == 1; + } // updateBatch + + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // process + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info(toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + return true; + } // invalidateIt + + /** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + + // Get Period + MPeriod period = MPeriod.get (getCtx(), getDateAcct()); + if (period == null) + { + log.warning("No Period for " + getDateAcct()); + m_processMsg = "@PeriodNotFound@"; + return DocAction.STATUS_Invalid; + } + // Standard Period + if (period.getC_Period_ID() != getC_Period_ID() + && period.isStandardPeriod()) + { + m_processMsg = "@PeriodNotValid@"; + return DocAction.STATUS_Invalid; + } + boolean open = period.isOpen(dt.getDocBaseType(), getDateAcct()); + if (!open) + { + log.warning(period.getName() + + ": Not open for " + dt.getDocBaseType() + " (" + getDateAcct() + ")"); + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + + // Lines + MJournalLine[] lines = getLines(true); + if (lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + + // Add up Amounts + BigDecimal AmtSourceDr = Env.ZERO; + BigDecimal AmtSourceCr = Env.ZERO; + for (int i = 0; i < lines.length; i++) + { + MJournalLine line = lines[i]; + if (!isActive()) + continue; + // + if (line.isDocControlled()) + { + m_processMsg = "@DocControlledError@ - @Line@=" + line.getLine() + + " - " + line.getAccountElementValue(); + return DocAction.STATUS_Invalid; + } + // + AmtSourceDr = AmtSourceDr.add(line.getAmtSourceDr()); + AmtSourceCr = AmtSourceCr.add(line.getAmtSourceCr()); + } + setTotalDr(AmtSourceDr); + setTotalCr(AmtSourceCr); + + // Control Amount + if (Env.ZERO.compareTo(getControlAmt()) != 0 + && getControlAmt().compareTo(getTotalDr()) != 0) + { + m_processMsg = "@ControlAmtError@"; + return DocAction.STATUS_Invalid; + } + + // Unbalanced Jornal & Not Suspense + if (AmtSourceDr.compareTo(AmtSourceCr) != 0) + { + MAcctSchemaGL gl = MAcctSchemaGL.get(getCtx(), getC_AcctSchema_ID()); + if (gl == null || !gl.isUseSuspenseBalancing()) + { + m_processMsg = "@UnbalancedJornal@"; + return DocAction.STATUS_Invalid; + } + } + + if (!DOCACTION_Complete.equals(getDocAction())) + setDocAction(DOCACTION_Complete); + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info(toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info(toString()); + setIsApproved(false); + return true; + } // rejectIt + + /** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Implicit Approval + if (!isApproved()) + approveIt(); + log.info(toString()); + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + // + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setDateDoc(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Void Document. + * @return true if success + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + boolean ok_to_void = false; + if (DOCSTATUS_Drafted.equals(getDocStatus()) + || DOCSTATUS_Invalid.equals(getDocStatus())) + { + setProcessed(true); + setDocAction(DOCACTION_None); + ok_to_void = true; + } else { + return false; + } + + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + + return ok_to_void; + } // voidIt + + /** + * Close Document. + * Cancel not delivered Qunatities + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + + boolean ok_to_close = false; + if (DOCSTATUS_Completed.equals(getDocStatus())) + { + setProcessed(true); + setDocAction(DOCACTION_None); + ok_to_close = true; + } else { + return false; + } + + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + return ok_to_close; + } // closeIt + + /** + * Reverse Correction (in same batch). + * As if nothing happened - same date + * @return true if success + */ + public boolean reverseCorrectIt() + { + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + boolean ok_correct = (reverseCorrectIt(getGL_JournalBatch_ID()) != null); + + if (! ok_correct) + return false; + + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + return ok_correct; + } // reverseCorrectIt + + /** + * Reverse Correction. + * As if nothing happened - same date + * @param GL_JournalBatch_ID reversal batch + * @return reversed Journal or null + */ + public MJournal reverseCorrectIt (int GL_JournalBatch_ID) + { + log.info(toString()); + // Journal + MJournal reverse = new MJournal (this); + reverse.setGL_JournalBatch_ID(GL_JournalBatch_ID); + reverse.setDateDoc(getDateDoc()); + reverse.setC_Period_ID(getC_Period_ID()); + reverse.setDateAcct(getDateAcct()); + // Reverse indicator + reverse.addDescription("(->" + getDocumentNo() + ")"); + if (!reverse.save()) + return null; + addDescription("(" + reverse.getDocumentNo() + "<-)"); + + // Lines + reverse.copyLinesFrom(this, null, 'C'); + // + setProcessed(true); + setDocAction(DOCACTION_None); + return reverse; + } // reverseCorrectionIt + + /** + * Reverse Accrual (sane batch). + * Flip Dr/Cr - Use Today's date + * @return true if success + */ + public boolean reverseAccrualIt() + { + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + boolean ok_reverse = (reverseAccrualIt (getGL_JournalBatch_ID()) != null); + + if (! ok_reverse) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return ok_reverse; + } // reverseAccrualIt + + /** + * Reverse Accrual. + * Flip Dr/Cr - Use Today's date + * @param GL_JournalBatch_ID reversal batch + * @return reversed journal or null + */ + public MJournal reverseAccrualIt (int GL_JournalBatch_ID) + { + log.info(toString()); + // Journal + MJournal reverse = new MJournal (this); + reverse.setGL_JournalBatch_ID(GL_JournalBatch_ID); + reverse.setDateDoc(new Timestamp(System.currentTimeMillis())); + reverse.set_ValueNoCheck ("C_Period_ID", null); // reset + reverse.setDateAcct(reverse.getDateDoc()); + // Reverse indicator + String description = reverse.getDescription(); + if (description == null) + description = "** " + getDocumentNo() + " **"; + else + description += " ** " + getDocumentNo() + " **"; + reverse.setDescription(description); + if (!reverse.save()) + return null; + + // Lines + reverse.copyLinesFrom(this, reverse.getDateAcct(), 'R'); + // + setProcessed(true); + setDocAction(DOCACTION_None); + return reverse; + } // reverseAccrualIt + + /** + * Re-activate + * @return true if success + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + return false; + } // reActivateIt + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Total Lines = 123.00 (#1) + sb.append(": ") + .append(Msg.translate(getCtx(),"TotalDr")).append("=").append(getTotalDr()) + .append(" ") + .append(Msg.translate(getCtx(),"TotalCR")).append("=").append(getTotalCr()) + .append(" (#").append(getLines(false).length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MJournal["); + sb.append(get_ID()).append(",").append(getDescription()) + .append(",DR=").append(getTotalDr()) + .append(",CR=").append(getTotalCr()) + .append ("]"); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + // ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID()); + // if (re == null) + return null; + // return re.getPDF(file); + } // createPDF + + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID (Created) + */ + public int getDoc_User_ID() + { + return getCreatedBy(); + } // getDoc_User_ID + + /** + * Get Document Approval Amount + * @return DR amount + */ + public BigDecimal getApprovalAmt() + { + return getTotalDr(); + } // getApprovalAmt + +} // MJournal diff --git a/base/src/org/compiere/model/MJournalBatch.java b/base/src/org/compiere/model/MJournalBatch.java new file mode 100644 index 0000000000..491515f7a9 --- /dev/null +++ b/base/src/org/compiere/model/MJournalBatch.java @@ -0,0 +1,838 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.process.*; +import org.compiere.util.*; + +/** + * Journal Batch Model + * + * @author Jorg Janke + * @version $Id: MJournalBatch.java,v 1.3 2006/07/30 00:51:03 jjanke Exp $ + */ +public class MJournalBatch extends X_GL_JournalBatch implements DocAction +{ + /** + * Create new Journal Batch by copying + * @param ctx context + * @param GL_JournalBatch_ID journal batch + * @param dateDoc date of the document date + * @param trxName transaction + * @return Journal Batch + */ + public static MJournalBatch copyFrom (Properties ctx, int GL_JournalBatch_ID, + Timestamp dateDoc, String trxName) + { + MJournalBatch from = new MJournalBatch (ctx, GL_JournalBatch_ID, trxName); + if (from.getGL_JournalBatch_ID() == 0) + throw new IllegalArgumentException ("From Journal Batch not found GL_JournalBatch_ID=" + GL_JournalBatch_ID); + // + MJournalBatch to = new MJournalBatch (ctx, 0, trxName); + PO.copyValues(from, to, from.getAD_Client_ID(), from.getAD_Org_ID()); + to.set_ValueNoCheck ("DocumentNo", null); + to.set_ValueNoCheck ("C_Period_ID", null); + to.setDateAcct(dateDoc); + to.setDateDoc(dateDoc); + to.setDocStatus(DOCSTATUS_Drafted); + to.setDocAction(DOCACTION_Complete); + to.setIsApproved(false); + to.setProcessed (false); + // + if (!to.save()) + throw new IllegalStateException("Could not create Journal Batch"); + + if (to.copyDetailsFrom(from) == 0) + throw new IllegalStateException("Could not create Journal Batch Details"); + + return to; + } // copyFrom + + + /************************************************************************** + * Standard Construvtore + * @param ctx context + * @param GL_JournalBatch_ID id if 0 - create actual batch + * @param trxName transaction + */ + public MJournalBatch (Properties ctx, int GL_JournalBatch_ID, String trxName) + { + super (ctx, GL_JournalBatch_ID, trxName); + if (GL_JournalBatch_ID == 0) + { + // setGL_JournalBatch_ID (0); PK + // setDescription (null); + // setDocumentNo (null); + // setC_DocType_ID (0); + setPostingType (POSTINGTYPE_Actual); + setDocAction (DOCACTION_Complete); + setDocStatus (DOCSTATUS_Drafted); + setTotalCr (Env.ZERO); + setTotalDr (Env.ZERO); + setProcessed (false); + setProcessing (false); + setIsApproved(false); + } + } // MJournalBatch + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MJournalBatch (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MJournalBatch + + /** + * Copy Constructor. + * Dos not copy: Dates/Period + * @param original original + */ + public MJournalBatch (MJournalBatch original) + { + this (original.getCtx(), 0, original.get_TrxName()); + setClientOrg(original); + setGL_JournalBatch_ID(original.getGL_JournalBatch_ID()); + // + // setC_AcctSchema_ID(original.getC_AcctSchema_ID()); + // setGL_Budget_ID(original.getGL_Budget_ID()); + setGL_Category_ID(original.getGL_Category_ID()); + setPostingType(original.getPostingType()); + setDescription(original.getDescription()); + setC_DocType_ID(original.getC_DocType_ID()); + setControlAmt(original.getControlAmt()); + // + setC_Currency_ID(original.getC_Currency_ID()); + // setC_ConversionType_ID(original.getC_ConversionType_ID()); + // setCurrencyRate(original.getCurrencyRate()); + + // setDateDoc(original.getDateDoc()); + // setDateAcct(original.getDateAcct()); + // setC_Period_ID(original.getC_Period_ID()); + } // MJournal + + + + /** + * Overwrite Client/Org if required + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + public void setClientOrg (int AD_Client_ID, int AD_Org_ID) + { + super.setClientOrg(AD_Client_ID, AD_Org_ID); + } // setClientOrg + + /** + * Set Accounting Date. + * Set also Period if not set earlier + * @param DateAcct date + */ + public void setDateAcct (Timestamp DateAcct) + { + super.setDateAcct(DateAcct); + if (DateAcct == null) + return; + if (getC_Period_ID() != 0) + return; + int C_Period_ID = MPeriod.getC_Period_ID(getCtx(), DateAcct); + if (C_Period_ID == 0) + log.warning("Period not found"); + else + setC_Period_ID(C_Period_ID); + } // setDateAcct + + /** + * Get Journal Lines + * @param requery requery + * @return Array of lines + */ + public MJournal[] getJournals (boolean requery) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM GL_Journal WHERE GL_JournalBatch_ID=? ORDER BY DocumentNo"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getGL_JournalBatch_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MJournal (getCtx(), rs, get_TrxName())); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (SQLException ex) + { + log.log(Level.SEVERE, sql, ex); + } + try + { + if (pstmt != null) + pstmt.close(); + } + catch (SQLException ex1) + { + } + pstmt = null; + // + MJournal[] retValue = new MJournal[list.size()]; + list.toArray(retValue); + return retValue; + } // getJournals + + /** + * Copy Journal/Lines from other Journal Batch + * @param jb Journal Batch + * @return number of journals + lines copied + */ + public int copyDetailsFrom (MJournalBatch jb) + { + if (isProcessed() || jb == null) + return 0; + int count = 0; + int lineCount = 0; + MJournal[] fromJournals = jb.getJournals(false); + for (int i = 0; i < fromJournals.length; i++) + { + MJournal toJournal = new MJournal (getCtx(), 0, jb.get_TrxName()); + PO.copyValues(fromJournals[i], toJournal, getAD_Client_ID(), getAD_Org_ID()); + toJournal.setGL_JournalBatch_ID(getGL_JournalBatch_ID()); + toJournal.set_ValueNoCheck ("DocumentNo", null); // create new + toJournal.set_ValueNoCheck ("C_Period_ID", null); + toJournal.setDateDoc(getDateDoc()); // dates from this Batch + toJournal.setDateAcct(getDateAcct()); + toJournal.setDocStatus(MJournal.DOCSTATUS_Drafted); + toJournal.setDocAction(MJournal.DOCACTION_Complete); + toJournal.setTotalCr(Env.ZERO); + toJournal.setTotalDr(Env.ZERO); + toJournal.setIsApproved(false); + toJournal.setIsPrinted(false); + toJournal.setPosted(false); + toJournal.setProcessed(false); + if (toJournal.save()) + { + count++; + lineCount += toJournal.copyLinesFrom(fromJournals[i], getDateAcct(), 'x'); + } + } + if (fromJournals.length != count) + log.log(Level.SEVERE, "Line difference - Journals=" + fromJournals.length + " <> Saved=" + count); + + return count + lineCount; + } // copyLinesFrom + + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // process + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info("unlockIt - " + toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info("invalidateIt - " + toString()); + return true; + } // invalidateIt + + /** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getDateAcct(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + + // Add up Amounts & prepare them + MJournal[] journals = getJournals(false); + if (journals.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + + BigDecimal TotalDr = Env.ZERO; + BigDecimal TotalCr = Env.ZERO; + for (int i = 0; i < journals.length; i++) + { + MJournal journal = journals[i]; + if (!journal.isActive()) + continue; + // Prepare if not closed + if (DOCSTATUS_Closed.equals(journal.getDocStatus()) + || DOCSTATUS_Voided.equals(journal.getDocStatus()) + || DOCSTATUS_Reversed.equals(journal.getDocStatus()) + || DOCSTATUS_Completed.equals(journal.getDocStatus())) + ; + else + { + String status = journal.prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + { + journal.setDocStatus(status); + journal.save(); + m_processMsg = journal.getProcessMsg(); + return status; + } + journal.setDocStatus(DOCSTATUS_InProgress); + journal.save(); + } + // + TotalDr = TotalDr.add(journal.getTotalDr()); + TotalCr = TotalCr.add(journal.getTotalCr()); + } + setTotalDr(TotalDr); + setTotalCr(TotalCr); + + // Control Amount + if (Env.ZERO.compareTo(getControlAmt()) != 0 + && getControlAmt().compareTo(getTotalDr()) != 0) + { + m_processMsg = "@ControlAmtError@"; + return DocAction.STATUS_Invalid; + } + +// Bug 1353695 Currency Rate and COnbversion Type should get copied from journal to lines + for (int i = 0; i < journals.length; i++) + { + MJournal journal = journals[i]; + MJournalLine[] lines = journal.getLines(true); + if (journal.getCurrencyRate() != null && journal.getCurrencyRate().compareTo(Env.ZERO) != 0) + { + for (int j = 0; j < lines.length; j++) + { + MJournalLine line = lines[j]; + line.setCurrencyRate(journal.getCurrencyRate()); + line.save(); + } + } + if (journal.getC_ConversionType_ID() > 0) + { + for (int j = 0; j < lines.length; j++) + { + MJournalLine line = lines[j]; + line.setC_ConversionType_ID(journal.getC_ConversionType_ID()); + line.save(); + } + } + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Add up Amounts + m_justPrepared = true; + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info("approveIt - " + toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info("rejectIt - " + toString()); + setIsApproved(false); + return true; + } // rejectIt + + /** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + log.info("completeIt - " + toString()); + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Implicit Approval + approveIt(); + + // Add up Amounts & complete them + MJournal[] journals = getJournals(true); + BigDecimal TotalDr = Env.ZERO; + BigDecimal TotalCr = Env.ZERO; + for (int i = 0; i < journals.length; i++) + { + MJournal journal = journals[i]; + if (!journal.isActive()) + { + journal.setProcessed(true); + journal.setDocStatus(DOCSTATUS_Voided); + journal.setDocAction(DOCACTION_None); + journal.save(); + continue; + } + // Complete if not closed + if (DOCSTATUS_Closed.equals(journal.getDocStatus()) + || DOCSTATUS_Voided.equals(journal.getDocStatus()) + || DOCSTATUS_Reversed.equals(journal.getDocStatus()) + || DOCSTATUS_Completed.equals(journal.getDocStatus())) + ; + else + { + String status = journal.completeIt(); + if (!DocAction.STATUS_Completed.equals(status)) + { + journal.setDocStatus(status); + journal.save(); + m_processMsg = journal.getProcessMsg(); + return status; + } + journal.setDocStatus(DOCSTATUS_Completed); + journal.save(); + } + // + TotalDr = TotalDr.add(journal.getTotalDr()); + TotalCr = TotalCr.add(journal.getTotalCr()); + } + setTotalDr(TotalDr); + setTotalCr(TotalCr); + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + // + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setDateDoc(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Void Document. + * @return false + */ + public boolean voidIt() + { + log.info("voidIt - " + toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + + return false; + } // voidIt + + /** + * Close Document. + * @return true if success + */ + public boolean closeIt() + { + log.info("closeIt - " + toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + + MJournal[] journals = getJournals(true); + for (int i = 0; i < journals.length; i++) + { + MJournal journal = journals[i]; + if (!journal.isActive() && !journal.isProcessed()) + { + journal.setProcessed(true); + journal.setDocStatus(DOCSTATUS_Voided); + journal.setDocAction(DOCACTION_None); + journal.save(); + continue; + } + if (DOCSTATUS_Drafted.equals(journal.getDocStatus()) + || DOCSTATUS_InProgress.equals(journal.getDocStatus()) + || DOCSTATUS_Invalid.equals(journal.getDocStatus())) + { + m_processMsg = "Journal not Completed: " + journal.getSummary(); + return false; + } + + // Close if not closed + if (DOCSTATUS_Closed.equals(journal.getDocStatus()) + || DOCSTATUS_Voided.equals(journal.getDocStatus()) + || DOCSTATUS_Reversed.equals(journal.getDocStatus())) + ; + else + { + if (!journal.closeIt()) + { + m_processMsg = "Cannot close: " + journal.getSummary(); + return false; + } + journal.save(); + } + } + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + return true; + } // closeIt + + /** + * Reverse Correction. + * As if nothing happened - same date + * @return true if success + */ + public boolean reverseCorrectIt() + { + log.info("reverseCorrectIt - " + toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + MJournal[] journals = getJournals(true); + // check prerequisites + for (int i = 0; i < journals.length; i++) + { + MJournal journal = journals[i]; + if (!journal.isActive()) + continue; + // All need to be closed/Completed + if (DOCSTATUS_Completed.equals(journal.getDocStatus())) + ; + else + { + m_processMsg = "All Journals need to be Completed: " + journal.getSummary(); + return false; + } + } + + // Reverse it + MJournalBatch reverse = new MJournalBatch (this); + reverse.setDateDoc(getDateDoc()); + reverse.setC_Period_ID(getC_Period_ID()); + reverse.setDateAcct(getDateAcct()); + // Reverse indicator + String description = reverse.getDescription(); + if (description == null) + description = "** " + getDocumentNo() + " **"; + else + description += " ** " + getDocumentNo() + " **"; + reverse.setDescription(description); + reverse.save(); + // + + // Reverse Journals + for (int i = 0; i < journals.length; i++) + { + MJournal journal = journals[i]; + if (!journal.isActive()) + continue; + if (journal.reverseCorrectIt(reverse.getGL_JournalBatch_ID()) == null) + { + m_processMsg = "Could not reverse " + journal; + return false; + } + journal.save(); + } + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + return true; + } // reverseCorrectionIt + + /** + * Reverse Accrual. + * Flip Dr/Cr - Use Today's date + * @return true if success + */ + public boolean reverseAccrualIt() + { + log.info("reverseAccrualIt - " + toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + MJournal[] journals = getJournals(true); + // check prerequisites + for (int i = 0; i < journals.length; i++) + { + MJournal journal = journals[i]; + if (!journal.isActive()) + continue; + // All need to be closed/Completed + if (DOCSTATUS_Completed.equals(journal.getDocStatus())) + ; + else + { + m_processMsg = "All Journals need to be Completed: " + journal.getSummary(); + return false; + } + } + // Reverse it + MJournalBatch reverse = new MJournalBatch (this); + reverse.setC_Period_ID(0); + reverse.setDateDoc(new Timestamp(System.currentTimeMillis())); + reverse.setDateAcct(reverse.getDateDoc()); + // Reverse indicator + String description = reverse.getDescription(); + if (description == null) + description = "** " + getDocumentNo() + " **"; + else + description += " ** " + getDocumentNo() + " **"; + reverse.setDescription(description); + reverse.save(); + + // Reverse Journals + for (int i = 0; i < journals.length; i++) + { + MJournal journal = journals[i]; + if (!journal.isActive()) + continue; + if (journal.reverseAccrualIt(reverse.getGL_JournalBatch_ID()) == null) + { + m_processMsg = "Could not reverse " + journal; + return false; + } + journal.save(); + } + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return true; + } // reverseAccrualIt + + /** + * Re-activate - same as reverse correct + * @return true if success + */ + public boolean reActivateIt() + { + log.info("reActivateIt - " + toString()); + + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + // setProcessed(false); + if (! reverseCorrectIt()) + return false; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + return true; + } // reActivateIt + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Total Lines = 123.00 (#1) + sb.append(": ") + .append(Msg.translate(getCtx(),"TotalDr")).append("=").append(getTotalDr()) + .append(" ") + .append(Msg.translate(getCtx(),"TotalCR")).append("=").append(getTotalCr()) + .append(" (#").append(getJournals(false).length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MJournalBatch["); + sb.append(get_ID()).append(",").append(getDescription()) + .append(",DR=").append(getTotalDr()) + .append(",CR=").append(getTotalCr()) + .append ("]"); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + // ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID()); + // if (re == null) + return null; + // return re.getPDF(file); + } // createPDF + + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID (Created By) + */ + public int getDoc_User_ID() + { + return getCreatedBy(); + } // getDoc_User_ID + + /** + * Get Document Approval Amount + * @return DR amount + */ + public BigDecimal getApprovalAmt() + { + return getTotalDr(); + } // getApprovalAmt + +} // MJournalBatch diff --git a/base/src/org/compiere/model/MLocator.java b/base/src/org/compiere/model/MLocator.java new file mode 100644 index 0000000000..40be411f6d --- /dev/null +++ b/base/src/org/compiere/model/MLocator.java @@ -0,0 +1,304 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Warehouse Locator Object + * + * @author Jorg Janke + * @version $Id: MLocator.java,v 1.3 2006/07/30 00:58:37 jjanke Exp $ + */ +public class MLocator extends X_M_Locator +{ + /** + * Get oldest Default Locator of warehouse with locator + * @param ctx context + * @param M_Locator_ID locator + * @return locator or null + */ + public static MLocator getDefault (Properties ctx, int M_Locator_ID) + { + String trxName = null; + MLocator retValue = null; + String sql = "SELECT * FROM M_Locator l " + + "WHERE IsDefault='Y'" + + " AND EXISTS (SELECT * FROM M_Locator lx " + + "WHERE l.M_Warehouse_ID=lx.M_Warehouse_ID AND lx.M_Locator_ID=?) " + + "ORDER BY Created"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + pstmt.setInt (1, M_Locator_ID); + rs = pstmt.executeQuery (); + while (rs.next ()) + retValue = new MLocator (ctx, rs, trxName); + } + catch (Exception e) + { + s_log.log (Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + + return retValue; + } // getDefault + + + /** + * Get the Locator with the combination or create new one + * @param ctx Context + * @param M_Warehouse_ID warehouse + * @param Value value + * @param X x + * @param Y y + * @param Z z + * @return locator + */ + public static MLocator get (Properties ctx, int M_Warehouse_ID, String Value, + String X, String Y, String Z) + { + MLocator retValue = null; + String sql = "SELECT * FROM M_Locator WHERE M_Warehouse_ID=? AND X=? AND Y=? AND Z=?"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, M_Warehouse_ID); + pstmt.setString(2, X); + pstmt.setString(3, Y); + pstmt.setString(4, Z); + rs = pstmt.executeQuery(); + if (rs.next()) + retValue = new MLocator (ctx, rs, null); + } + catch (SQLException ex) + { + s_log.log(Level.SEVERE, "get", ex); + } + finally { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // + if (retValue == null) + { + MWarehouse wh = MWarehouse.get (ctx, M_Warehouse_ID); + retValue = new MLocator (wh, Value); + retValue.setXYZ(X, Y, Z); + retValue.save(); + } + return retValue; + } // get + + /** + * Get Locator from Cache + * @param ctx context + * @param M_Locator_ID id + * @return MLocator + */ + public static MLocator get (Properties ctx, int M_Locator_ID) + { + if (s_cache == null) + s_cache = new CCache("M_Locator", 20); + Integer key = new Integer (M_Locator_ID); + MLocator retValue = (MLocator) s_cache.get (key); + if (retValue != null) + return retValue; + retValue = new MLocator (ctx, M_Locator_ID, null); + if (retValue.get_ID () != 0) + s_cache.put (key, retValue); + return retValue; + } // get + + /** Cache */ + private static CCache s_cache; + + /** Logger */ + private static CLogger s_log = CLogger.getCLogger (MLocator.class); + + + /************************************************************************** + * Standard Locator Constructor + * @param ctx Context + * @param M_Locator_ID id + * @param trxName transaction + */ + public MLocator (Properties ctx, int M_Locator_ID, String trxName) + { + super (ctx, M_Locator_ID, trxName); + if (M_Locator_ID == 0) + { + // setM_Locator_ID (0); // PK + // setM_Warehouse_ID (0); // Parent + setIsDefault (false); + setPriorityNo (50); + // setValue (null); + // setX (null); + // setY (null); + // setZ (null); + } + } // MLocator + + /** + * New Locator Constructor with XYZ=000 + * @param warehouse parent + * @param Value value + */ + public MLocator (MWarehouse warehouse, String Value) + { + this (warehouse.getCtx(), 0, warehouse.get_TrxName()); + setClientOrg(warehouse); + setM_Warehouse_ID (warehouse.getM_Warehouse_ID()); // Parent + setValue (Value); + setXYZ("0","0","0"); + } // MLocator + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MLocator (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MLocator + + /** + * Get String Representation + * @return Value + */ + public String toString() + { + return getValue(); + } // getValue + + /** + * Set Location + * @param X x + * @param Y y + * @param Z z + */ + public void setXYZ (String X, String Y, String Z) + { + setX (X); + setY (Y); + setZ (Z); + } // setXYZ + + + /** + * Get Warehouse Name + * @return name + */ + public String getWarehouseName() + { + MWarehouse wh = MWarehouse.get(getCtx(), getM_Warehouse_ID()); + if (wh.get_ID() == 0) + return "<" + getM_Warehouse_ID() + ">"; + return wh.getName(); + } // getWarehouseName + + /** + * Can Locator Store Product + * @param M_Product_ID id + * @return true if can be stored + */ + public boolean isCanStoreProduct (int M_Product_ID) + { + // BF [ 1759245 ] Locator field cleared in Physical Inventory + // CarlosRuiz - globalqss comments: + // The algorithm to search if a product can be stored is wrong, it looks for: + // * M_Storage to see if the product is already in the locator + // * If the product has this locator defined as default + // This implies that every time you create a new product you must create initial inventory zero for all locators where the product can be stored. + // A good enhancement could be a new table to indicate when a locator is exclusive for some products, but I consider current approach not working. + return true; + + /* + // Default Locator + if (M_Product_ID == 0 || isDefault()) + return true; + + int count = 0; + PreparedStatement pstmt = null; + // Already Stored + String sql = "SELECT COUNT(*) FROM M_Storage s WHERE s.M_Locator_ID=? AND s.M_Product_ID=?"; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, getM_Locator_ID()); + pstmt.setInt (2, M_Product_ID); + ResultSet rs = pstmt.executeQuery (); + if (rs.next ()) + count = rs.getInt(1); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log (Level.SEVERE, sql, e); + } + // Default Product Locator + if (count == 0) + { + sql = "SELECT COUNT(*) FROM M_Product s WHERE s.M_Locator_ID=? AND s.M_Product_ID=?"; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, getM_Locator_ID()); + pstmt.setInt (2, M_Product_ID); + ResultSet rs = pstmt.executeQuery (); + if (rs.next ()) + count = rs.getInt(1); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log (Level.SEVERE, sql, e); + } + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + return count != 0; + */ + } // isCanStoreProduct + +} // MLocator diff --git a/base/src/org/compiere/model/MMenu.java b/base/src/org/compiere/model/MMenu.java new file mode 100644 index 0000000000..fce012c5bd --- /dev/null +++ b/base/src/org/compiere/model/MMenu.java @@ -0,0 +1,169 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Menu Model + * + * @author Jorg Janke + * @version $Id: MMenu.java,v 1.3 2006/07/30 00:58:18 jjanke Exp $ + */ +public class MMenu extends X_AD_Menu +{ + + /** + * Get menues with where clause + * @param ctx context + * @param whereClause where clause w/o the actual WHERE + * @return MMenu + * @deprecated + */ + public static MMenu[] get (Properties ctx, String whereClause) + { + return get(ctx, whereClause, null); + } + + /** + * Get menues with where clause + * @param ctx context + * @param whereClause where clause w/o the actual WHERE + * @param trxName transaction + * @return MMenu + */ + public static MMenu[] get (Properties ctx, String whereClause, String trxName) + { + String sql = "SELECT * FROM AD_Menu"; + if (whereClause != null && whereClause.length() > 0) + sql += " WHERE " + whereClause; + ArrayList list = new ArrayList(); + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + rs = pstmt.executeQuery (); + while (rs.next ()) + list.add (new MMenu (ctx, rs, trxName)); + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + finally { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + MMenu[] retValue = new MMenu[list.size()]; + list.toArray (retValue); + return retValue; + } // get + + /** Static Logger */ + private static CLogger s_log = CLogger.getCLogger (MMenu.class); + + /************************************************************************** + * Standard Constructor + * @param ctx context + * @param AD_Menu_ID id + * @param trxName transaction + */ + public MMenu (Properties ctx, int AD_Menu_ID, String trxName) + { + super (ctx, AD_Menu_ID, trxName); + if (AD_Menu_ID == 0) + { + setEntityType (ENTITYTYPE_UserMaintained); // U + setIsReadOnly (false); // N + setIsSOTrx (false); + setIsSummary (false); + // setName (null); + } + } // MMenu + + /** + * Load Contrusctor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MMenu (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MMenu + + /** + * Before Save + * @param newRecord new + * @return true + */ + protected boolean beforeSave (boolean newRecord) + { + // Reset info + if (isSummary() && getAction() != null) + setAction(null); + String action = getAction(); + if (action == null) + action = ""; + // Clean up references + if (getAD_Window_ID() != 0 && !action.equals(ACTION_Window)) + setAD_Window_ID(0); + if (getAD_Form_ID() != 0 && !action.equals(ACTION_Form)) + setAD_Form_ID(0); + if (getAD_Workflow_ID() != 0 && !action.equals(ACTION_WorkFlow)) + setAD_Workflow_ID(0); + if (getAD_Workbench_ID() != 0 && !action.equals(ACTION_Workbench)) + setAD_Workbench_ID(0); + if (getAD_Task_ID() != 0 && !action.equals(ACTION_Task)) + setAD_Task_ID(0); + if (getAD_Process_ID() != 0 + && !(action.equals(ACTION_Process) || action.equals(ACTION_Report))) + setAD_Process_ID(0); + return true; + } // beforeSave + + + /** + * After Save + * @param newRecord new + * @param success success + * @return success + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + if (newRecord) + insert_Tree(MTree_Base.TREETYPE_Menu); + return success; + } // afterSave + + /** + * After Delete + * @param success + * @return deleted + */ + protected boolean afterDelete (boolean success) + { + if (success) + delete_Tree(MTree_Base.TREETYPE_Menu); + return success; + } // afterDelete + +} // MMenu diff --git a/base/src/org/compiere/model/MMovement.java b/base/src/org/compiere/model/MMovement.java new file mode 100644 index 0000000000..f1342b6cba --- /dev/null +++ b/base/src/org/compiere/model/MMovement.java @@ -0,0 +1,894 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.*; +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.process.*; +import org.compiere.util.*; + +/** + * Inventory Movement Model + * + * @author Jorg Janke + * @version $Id: MMovement.java,v 1.3 2006/07/30 00:51:03 jjanke Exp $ + */ +public class MMovement extends X_M_Movement implements DocAction +{ + /** + * Standard Constructor + * @param ctx context + * @param M_Movement_ID id + * @param trxName transaction + */ + public MMovement (Properties ctx, int M_Movement_ID, String trxName) + { + super (ctx, M_Movement_ID, trxName); + if (M_Movement_ID == 0) + { + // setC_DocType_ID (0); + setDocAction (DOCACTION_Complete); // CO + setDocStatus (DOCSTATUS_Drafted); // DR + setIsApproved (false); + setIsInTransit (false); + setMovementDate (new Timestamp(System.currentTimeMillis())); // @#Date@ + setPosted (false); + super.setProcessed (false); + } + } // MMovement + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MMovement (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MMovement + + /** Lines */ + private MMovementLine[] m_lines = null; + /** Confirmations */ + private MMovementConfirm[] m_confirms = null; + + /** + * Get Lines + * @param requery requery + * @return array of lines + */ + public MMovementLine[] getLines (boolean requery) + { + if (m_lines != null && !requery) { + set_TrxName(m_lines, get_TrxName()); + return m_lines; + } + // + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_MovementLine WHERE M_Movement_ID=? ORDER BY Line"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, getM_Movement_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + list.add (new MMovementLine (getCtx(), rs, get_TrxName())); + } + rs.close (); + pstmt.close (); + pstmt = null; + } catch (Exception e) + { + log.log(Level.SEVERE, "getLines", e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } catch (Exception e) + { + pstmt = null; + } + + m_lines = new MMovementLine[list.size ()]; + list.toArray (m_lines); + return m_lines; + } // getLines + + /** + * Get Confirmations + * @param requery requery + * @return array of Confirmations + */ + public MMovementConfirm[] getConfirmations(boolean requery) + { + if (m_confirms != null && !requery) + return m_confirms; + + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_MovementConfirm WHERE M_Movement_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, getM_Movement_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + list.add(new MMovementConfirm(getCtx(), rs, get_TrxName())); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "getConfirmations", e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + m_confirms = new MMovementConfirm[list.size ()]; + list.toArray (m_confirms); + return m_confirms; + } // getConfirmations + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + // ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.INVOICE, getC_Invoice_ID()); + // if (re == null) + return null; + // return re.getPDF(file); + } // createPDF + + + /** + * Before Save + * @param newRecord new + * @return true + */ + protected boolean beforeSave (boolean newRecord) + { + if (getC_DocType_ID() == 0) + { + MDocType types[] = MDocType.getOfDocBaseType(getCtx(), MDocType.DOCBASETYPE_MaterialMovement); + if (types.length > 0) // get first + setC_DocType_ID(types[0].getC_DocType_ID()); + else + { + log.saveError("Error", Msg.parseTranslation(getCtx(), "@NotFound@ @C_DocType_ID@")); + return false; + } + } + return true; + } // beforeSave + + /** + * Set Processed. + * Propergate to Lines/Taxes + * @param processed processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + if (get_ID() == 0) + return; + String sql = "UPDATE M_MovementLine SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE M_Movement_ID=" + getM_Movement_ID(); + int noLine = DB.executeUpdate(sql, get_TrxName()); + m_lines = null; + log.fine("Processed=" + processed + " - Lines=" + noLine); + } // setProcessed + + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // processIt + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info(toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + /** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getMovementDate(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + MMovementLine[] lines = getLines(false); + if (lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + // Add up Amounts + + + checkMaterialPolicy(); + + // Confirmation + if (dt.isInTransit()) + createConfirmation(); + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + if (!DOCACTION_Complete.equals(getDocAction())) + setDocAction(DOCACTION_Complete); + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Create Movement Confirmation + */ + private void createConfirmation() + { + MMovementConfirm[] confirmations = getConfirmations(false); + if (confirmations.length > 0) + return; + + // Create Confirmation + MMovementConfirm.create (this, false); + } // createConfirmation + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info(toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info(toString()); + setIsApproved(false); + return true; + } // rejectIt + + /** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Outstanding (not processed) Incoming Confirmations ? + MMovementConfirm[] confirmations = getConfirmations(true); + for (int i = 0; i < confirmations.length; i++) + { + MMovementConfirm confirm = confirmations[i]; + if (!confirm.isProcessed()) + { + m_processMsg = "Open: @M_MovementConfirm_ID@ - " + + confirm.getDocumentNo(); + return DocAction.STATUS_InProgress; + } + } + + // Implicit Approval + if (!isApproved()) + approveIt(); + log.info(toString()); + + // + MMovementLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MMovementLine line = lines[i]; + MTransaction trxFrom = null; + if (line.getM_AttributeSetInstance_ID() == 0) + { + MMovementLineMA mas[] = MMovementLineMA.get(getCtx(), + line.getM_MovementLine_ID(), get_TrxName()); + for (int j = 0; j < mas.length; j++) + { + MMovementLineMA ma = mas[j]; + // + MStorage storageFrom = MStorage.get(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), get_TrxName()); + if (storageFrom == null) + storageFrom = MStorage.getCreate(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), get_TrxName()); + // + MStorage storageTo = MStorage.get(getCtx(), line.getM_LocatorTo_ID(), + line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), get_TrxName()); + if (storageTo == null) + storageTo = MStorage.getCreate(getCtx(), line.getM_LocatorTo_ID(), + line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), get_TrxName()); + // + storageFrom.setQtyOnHand(storageFrom.getQtyOnHand().subtract(ma.getMovementQty())); + if (!storageFrom.save(get_TrxName())) + { + m_processMsg = "Storage From not updated (MA)"; + return DocAction.STATUS_Invalid; + } + // + storageTo.setQtyOnHand(storageTo.getQtyOnHand().add(ma.getMovementQty())); + if (!storageTo.save(get_TrxName())) + { + m_processMsg = "Storage To not updated (MA)"; + return DocAction.STATUS_Invalid; + } + + // + trxFrom = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_MovementFrom, + line.getM_Locator_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + ma.getMovementQty().negate(), getMovementDate(), get_TrxName()); + trxFrom.setM_MovementLine_ID(line.getM_MovementLine_ID()); + if (!trxFrom.save()) + { + m_processMsg = "Transaction From not inserted (MA)"; + return DocAction.STATUS_Invalid; + } + // + MTransaction trxTo = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_MovementTo, + line.getM_LocatorTo_ID(), line.getM_Product_ID(), ma.getM_AttributeSetInstance_ID(), + ma.getMovementQty(), getMovementDate(), get_TrxName()); + trxTo.setM_MovementLine_ID(line.getM_MovementLine_ID()); + if (!trxTo.save()) + { + m_processMsg = "Transaction To not inserted (MA)"; + return DocAction.STATUS_Invalid; + } + } + } + // Fallback - We have ASI + if (trxFrom == null) + { + MStorage storageFrom = MStorage.get(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), get_TrxName()); + if (storageFrom == null) + storageFrom = MStorage.getCreate(getCtx(), line.getM_Locator_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), get_TrxName()); + // + MStorage storageTo = MStorage.get(getCtx(), line.getM_LocatorTo_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), get_TrxName()); + if (storageTo == null) + storageTo = MStorage.getCreate(getCtx(), line.getM_LocatorTo_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), get_TrxName()); + // + storageFrom.setQtyOnHand(storageFrom.getQtyOnHand().subtract(line.getMovementQty())); + if (!storageFrom.save(get_TrxName())) + { + m_processMsg = "Storage From not updated"; + return DocAction.STATUS_Invalid; + } + // + storageTo.setQtyOnHand(storageTo.getQtyOnHand().add(line.getMovementQty())); + if (!storageTo.save(get_TrxName())) + { + m_processMsg = "Storage To not updated"; + return DocAction.STATUS_Invalid; + } + + // + trxFrom = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_MovementFrom, + line.getM_Locator_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + line.getMovementQty().negate(), getMovementDate(), get_TrxName()); + trxFrom.setM_MovementLine_ID(line.getM_MovementLine_ID()); + if (!trxFrom.save()) + { + m_processMsg = "Transaction From not inserted"; + return DocAction.STATUS_Invalid; + } + // + MTransaction trxTo = new MTransaction (getCtx(), line.getAD_Org_ID(), + MTransaction.MOVEMENTTYPE_MovementTo, + line.getM_LocatorTo_ID(), line.getM_Product_ID(), line.getM_AttributeSetInstanceTo_ID(), + line.getMovementQty(), getMovementDate(), get_TrxName()); + trxTo.setM_MovementLine_ID(line.getM_MovementLine_ID()); + if (!trxTo.save()) + { + m_processMsg = "Transaction To not inserted"; + return DocAction.STATUS_Invalid; + } + } // Fallback + } // for all lines + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + // + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setMovementDate(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Check Material Policy + * Sets line ASI + */ + private void checkMaterialPolicy() + { + int no = MMovementLineMA.deleteMovementMA(getM_Movement_ID(), get_TrxName()); + if (no > 0) + log.config("Delete old #" + no); + MMovementLine[] lines = getLines(false); + + // Check Lines + for (int i = 0; i < lines.length; i++) + { + MMovementLine line = lines[i]; + boolean needSave = false; + + // Attribute Set Instance + if (line.getM_AttributeSetInstance_ID() == 0) + { + MProduct product = MProduct.get(getCtx(), line.getM_Product_ID()); + String MMPolicy = product.getMMPolicy(); + MStorage[] storages = MStorage.getAllWithASI(getCtx(), + line.getM_Product_ID(), line.getM_Locator_ID(), + MClient.MMPOLICY_FiFo.equals(MMPolicy), get_TrxName()); + BigDecimal qtyToDeliver = line.getMovementQty(); + for (int ii = 0; ii < storages.length; ii++) + { + MStorage storage = storages[ii]; + if (ii == 0) + { + if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) + { + line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); + needSave = true; + log.config("Direct - " + line); + qtyToDeliver = Env.ZERO; + } + else + { + log.config("Split - " + line); + MMovementLineMA ma = new MMovementLineMA (line, + storage.getM_AttributeSetInstance_ID(), + storage.getQtyOnHand()); + if (!ma.save()) + ; + qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); + log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); + } + } + else // create addl material allocation + { + MMovementLineMA ma = new MMovementLineMA (line, + storage.getM_AttributeSetInstance_ID(), + qtyToDeliver); + if (storage.getQtyOnHand().compareTo(qtyToDeliver) >= 0) + qtyToDeliver = Env.ZERO; + else + { + ma.setMovementQty(storage.getQtyOnHand()); + qtyToDeliver = qtyToDeliver.subtract(storage.getQtyOnHand()); + } + if (!ma.save()) + ; + log.fine("#" + ii + ": " + ma + ", QtyToDeliver=" + qtyToDeliver); + } + if (qtyToDeliver.signum() == 0) + break; + } // for all storages + + // No AttributeSetInstance found for remainder + if (qtyToDeliver.signum() != 0) + { + MMovementLineMA ma = new MMovementLineMA (line, + 0, qtyToDeliver); + if (!ma.save()) + ; + log.fine("##: " + ma); + } + } // attributeSetInstance + + if (needSave && !line.save()) + log.severe("NOT saved " + line); + } // for all lines + + } // checkMaterialPolicy + + /** + * Void Document. + * @return true if success + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + if (DOCSTATUS_Closed.equals(getDocStatus()) + || DOCSTATUS_Reversed.equals(getDocStatus()) + || DOCSTATUS_Voided.equals(getDocStatus())) + { + m_processMsg = "Document Closed: " + getDocStatus(); + return false; + } + + // Not Processed + if (DOCSTATUS_Drafted.equals(getDocStatus()) + || DOCSTATUS_Invalid.equals(getDocStatus()) + || DOCSTATUS_InProgress.equals(getDocStatus()) + || DOCSTATUS_Approved.equals(getDocStatus()) + || DOCSTATUS_NotApproved.equals(getDocStatus()) ) + { + // Set lines to 0 + MMovementLine[] lines = getLines(false); + for (int i = 0; i < lines.length; i++) + { + MMovementLine line = lines[i]; + BigDecimal old = line.getMovementQty(); + if (old.compareTo(Env.ZERO) != 0) + { + line.setMovementQty(Env.ZERO); + line.addDescription("Void (" + old + ")"); + line.save(get_TrxName()); + } + } + } + else + { + return reverseCorrectIt(); + } + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // voidIt + + /** + * Close Document. + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + // Close Not delivered Qty + setDocAction(DOCACTION_None); + return true; + } // closeIt + + /** + * Reverse Correction + * @return false + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (!MPeriod.isOpen(getCtx(), getMovementDate(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return false; + } + + // Deep Copy + MMovement reversal = new MMovement(getCtx(), 0, get_TrxName()); + copyValues(this, reversal, getAD_Client_ID(), getAD_Org_ID()); + reversal.setDocStatus(DOCSTATUS_Drafted); + reversal.setDocAction(DOCACTION_Complete); + reversal.setIsApproved (false); + reversal.setIsInTransit (false); + reversal.setPosted(false); + reversal.setProcessed(false); + reversal.addDescription("{->" + getDocumentNo() + ")"); + if (!reversal.save()) + { + m_processMsg = "Could not create Movement Reversal"; + return false; + } + + // Reverse Line Qty + MMovementLine[] oLines = getLines(true); + for (int i = 0; i < oLines.length; i++) + { + MMovementLine oLine = oLines[i]; + MMovementLine rLine = new MMovementLine(getCtx(), 0, get_TrxName()); + copyValues(oLine, rLine, oLine.getAD_Client_ID(), oLine.getAD_Org_ID()); + rLine.setM_Movement_ID(reversal.getM_Movement_ID()); + // + rLine.setMovementQty(rLine.getMovementQty().negate()); + rLine.setTargetQty(Env.ZERO); + rLine.setScrappedQty(Env.ZERO); + rLine.setConfirmedQty(Env.ZERO); + rLine.setProcessed(false); + if (!rLine.save()) + { + m_processMsg = "Could not create Movement Reversal Line"; + return false; + } + } + // + if (!reversal.processIt(DocAction.ACTION_Complete)) + { + m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg(); + return false; + } + reversal.closeIt(); + reversal.setDocStatus(DOCSTATUS_Reversed); + reversal.setDocAction(DOCACTION_None); + reversal.save(); + m_processMsg = reversal.getDocumentNo(); + + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + // Update Reversed (this) + addDescription("(" + reversal.getDocumentNo() + "<-)"); + setProcessed(true); + setDocStatus(DOCSTATUS_Reversed); // may come from void + setDocAction(DOCACTION_None); + + return true; + } // reverseCorrectionIt + + /** + * Reverse Accrual - none + * @return false + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return false; + } // reverseAccrualIt + + /** + * Re-activate + * @return false + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + return false; + } // reActivateIt + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Total Lines = 123.00 (#1) + sb.append(": ") + .append(Msg.translate(getCtx(),"ApprovalAmt")).append("=").append(getApprovalAmt()) + .append(" (#").append(getLines(false).length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getCreatedBy(); + } // getDoc_User_ID + + /** + * Get Document Currency + * @return C_Currency_ID + */ + public int getC_Currency_ID() + { + // MPriceList pl = MPriceList.get(getCtx(), getM_PriceList_ID()); + // return pl.getC_Currency_ID(); + return 0; + } // getC_Currency_ID + +} // MMovement + diff --git a/base/src/org/compiere/model/MMovementLine.java b/base/src/org/compiere/model/MMovementLine.java new file mode 100644 index 0000000000..b290f19d5b --- /dev/null +++ b/base/src/org/compiere/model/MMovementLine.java @@ -0,0 +1,205 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.math.*; +import java.sql.*; +import java.util.*; +import org.compiere.util.*; + +/** + * Inventory Move Line Model + * + * @author Jorg Janke + * @version $Id: MMovementLine.java,v 1.3 2006/07/30 00:51:03 jjanke Exp $ + */ +public class MMovementLine extends X_M_MovementLine +{ + /** + * Standard Cosntructor + * @param ctx context + * @param M_MovementLine_ID id + * @param trxName transaction + */ + public MMovementLine (Properties ctx, int M_MovementLine_ID, String trxName) + { + super (ctx, M_MovementLine_ID, trxName); + if (M_MovementLine_ID == 0) + { + // setM_LocatorTo_ID (0); // @M_LocatorTo_ID@ + // setM_Locator_ID (0); // @M_Locator_ID@ + // setM_MovementLine_ID (0); + // setLine (0); + // setM_Product_ID (0); + setM_AttributeSetInstance_ID(0); // ID + setMovementQty (Env.ZERO); // 1 + setTargetQty (Env.ZERO); // 0 + setScrappedQty(Env.ZERO); + setConfirmedQty(Env.ZERO); + setProcessed (false); + } + } // MMovementLine + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MMovementLine (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MMovementLine + + /** + * Parent constructor + * @param parent parent + */ + public MMovementLine (MMovement parent) + { + this (parent.getCtx(), 0, parent.get_TrxName()); + setClientOrg(parent); + setM_Movement_ID(parent.getM_Movement_ID()); + } // MMovementLine + + /** + * Get AttributeSetInstance To + * @return ASI + */ + public int getM_AttributeSetInstanceTo_ID () + { + int M_AttributeSetInstanceTo_ID = super.getM_AttributeSetInstanceTo_ID(); + if (M_AttributeSetInstanceTo_ID == 0) + M_AttributeSetInstanceTo_ID = super.getM_AttributeSetInstance_ID(); + return M_AttributeSetInstanceTo_ID; + } // getM_AttributeSetInstanceTo_ID + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + /** + * Get Product + * @return product or null if not defined + */ + public MProduct getProduct() + { + if (getM_Product_ID() != 0) + return MProduct.get(getCtx(), getM_Product_ID()); + return null; + } // getProduct + + /** + * Set Movement Qty - enforce UOM + * @param MovementQty qty + */ + public void setMovementQty (BigDecimal MovementQty) + { + if (MovementQty != null) + { + MProduct product = getProduct(); + if (product != null) + { + int precision = product.getUOMPrecision(); + MovementQty = MovementQty.setScale(precision, BigDecimal.ROUND_HALF_UP); + } + } + super.setMovementQty(MovementQty); + } // setMovementQty + + /** Parent */ + private MMovement m_parent = null; + + /** + * get Parent + * @return Parent Movement + */ + public MMovement getParent() + { + if (m_parent == null) + m_parent = new MMovement (getCtx(), getM_Movement_ID(), get_TrxName()); + return m_parent; + } // getParent + + + /** + * Before Save + * @param newRecord new + * @return true + */ + protected boolean beforeSave (boolean newRecord) + { + // Set Line No + if (getLine() == 0) + { + String sql = "SELECT COALESCE(MAX(Line),0)+10 AS DefaultValue FROM M_MovementLine WHERE M_Movement_ID=?"; + int ii = DB.getSQLValue (get_TrxName(), sql, getM_Movement_ID()); + setLine (ii); + } + + if (getM_Locator_ID() == getM_LocatorTo_ID()) + { + log.saveError("Error", Msg.parseTranslation(getCtx(), "@M_Locator_ID@ == @M_LocatorTo_ID@")); + return false; + } + + if (getMovementQty().signum() == 0) + { + log.saveError("FillMandatory", Msg.getElement(getCtx(), "MovementQty")); + return false; + } + + // Qty Precision + if (newRecord || is_ValueChanged(COLUMNNAME_MovementQty)) + setMovementQty(getMovementQty()); + + // Mandatory Instance + MProduct product = getProduct(); + if (getM_AttributeSetInstance_ID() == 0) { + if (product != null && product.isASIMandatory(false)) { + log.saveError("FillMandatory", Msg.getElement(getCtx(), COLUMNNAME_M_AttributeSetInstance_ID)); + return false; + } + } + if (getM_AttributeSetInstanceTo_ID() == 0) + { + if (getM_AttributeSetInstance_ID() != 0) // set to from + setM_AttributeSetInstanceTo_ID(getM_AttributeSetInstance_ID()); + else + { + if (product != null && product.isASIMandatory(true)) + { + log.saveError("FillMandatory", Msg.getElement(getCtx(), COLUMNNAME_M_AttributeSetInstanceTo_ID)); + return false; + } + } + } // ASI + + return true; + } // beforeSave + + +} // MMovementLine diff --git a/base/src/org/compiere/model/MOrder.java b/base/src/org/compiere/model/MOrder.java new file mode 100644 index 0000000000..fe9f976aad --- /dev/null +++ b/base/src/org/compiere/model/MOrder.java @@ -0,0 +1,2330 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.File; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Properties; +import java.util.logging.Level; + +import org.compiere.print.ReportEngine; +import org.compiere.process.DocAction; +import org.compiere.process.DocumentEngine; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.Msg; + +/** + * Order Model. + * Please do not set DocStatus and C_DocType_ID directly. + * They are set in the process() method. + * Use DocAction and C_DocTypeTarget_ID instead. + * + * @author Jorg Janke + * @version $Id: MOrder.java,v 1.5 2006/10/06 00:42:24 jjanke Exp $ + */ +public class MOrder extends X_C_Order implements DocAction +{ + /** + * Create new Order by copying + * @param from order + * @param dateDoc date of the document date + * @param C_DocTypeTarget_ID target document type + * @param isSOTrx sales order + * @param counter create counter links + * @param copyASI copy line attributes Attribute Set Instance, Resaouce Assignment + * @param trxName trx + * @return Order + */ + public static MOrder copyFrom (MOrder from, Timestamp dateDoc, + int C_DocTypeTarget_ID, boolean isSOTrx, boolean counter, boolean copyASI, + String trxName) + { + MOrder to = new MOrder (from.getCtx(), 0, trxName); + to.set_TrxName(trxName); + PO.copyValues(from, to, from.getAD_Client_ID(), from.getAD_Org_ID()); + to.set_ValueNoCheck ("C_Order_ID", I_ZERO); + to.set_ValueNoCheck ("DocumentNo", null); + // + to.setDocStatus (DOCSTATUS_Drafted); // Draft + to.setDocAction(DOCACTION_Complete); + // + to.setC_DocType_ID(0); + to.setC_DocTypeTarget_ID (C_DocTypeTarget_ID); + to.setIsSOTrx(isSOTrx); + // + to.setIsSelected (false); + to.setDateOrdered (dateDoc); + to.setDateAcct (dateDoc); + to.setDatePromised (dateDoc); // assumption + to.setDatePrinted(null); + to.setIsPrinted (false); + // + to.setIsApproved (false); + to.setIsCreditApproved(false); + to.setC_Payment_ID(0); + to.setC_CashLine_ID(0); + // Amounts are updated when adding lines + to.setGrandTotal(Env.ZERO); + to.setTotalLines(Env.ZERO); + // + to.setIsDelivered(false); + to.setIsInvoiced(false); + to.setIsSelfService(false); + to.setIsTransferred (false); + to.setPosted (false); + to.setProcessed (false); + if (counter) + to.setRef_Order_ID(from.getC_Order_ID()); + else + to.setRef_Order_ID(0); + // + if (!to.save(trxName)) + throw new IllegalStateException("Could not create Order"); + if (counter) + from.setRef_Order_ID(to.getC_Order_ID()); + + if (to.copyLinesFrom(from, counter, copyASI) == 0) + throw new IllegalStateException("Could not create Order Lines"); + + return to; + } // copyFrom + + + /************************************************************************** + * Default Constructor + * @param ctx context + * @param C_Order_ID order to load, (0 create new order) + * @param trxName trx name + */ + public MOrder(Properties ctx, int C_Order_ID, String trxName) + { + super (ctx, C_Order_ID, trxName); + // New + if (C_Order_ID == 0) + { + setDocStatus(DOCSTATUS_Drafted); + setDocAction (DOCACTION_Prepare); + // + setDeliveryRule (DELIVERYRULE_Availability); + setFreightCostRule (FREIGHTCOSTRULE_FreightIncluded); + setInvoiceRule (INVOICERULE_Immediate); + setPaymentRule(PAYMENTRULE_OnCredit); + setPriorityRule (PRIORITYRULE_Medium); + setDeliveryViaRule (DELIVERYVIARULE_Pickup); + // + setIsDiscountPrinted (false); + setIsSelected (false); + setIsTaxIncluded (false); + setIsSOTrx (true); + setIsDropShip(false); + setSendEMail (false); + // + setIsApproved(false); + setIsPrinted(false); + setIsCreditApproved(false); + setIsDelivered(false); + setIsInvoiced(false); + setIsTransferred(false); + setIsSelfService(false); + // + super.setProcessed(false); + setProcessing(false); + setPosted(false); + + setDateAcct (new Timestamp(System.currentTimeMillis())); + setDatePromised (new Timestamp(System.currentTimeMillis())); + setDateOrdered (new Timestamp(System.currentTimeMillis())); + + setFreightAmt (Env.ZERO); + setChargeAmt (Env.ZERO); + setTotalLines (Env.ZERO); + setGrandTotal (Env.ZERO); + } + } // MOrder + + /************************************************************************** + * Project Constructor + * @param project Project to create Order from + * @param IsSOTrx sales order + * @param DocSubTypeSO if SO DocType Target (default DocSubTypeSO_OnCredit) + */ + public MOrder (MProject project, boolean IsSOTrx, String DocSubTypeSO) + { + this (project.getCtx(), 0, project.get_TrxName()); + setAD_Client_ID(project.getAD_Client_ID()); + setAD_Org_ID(project.getAD_Org_ID()); + setC_Campaign_ID(project.getC_Campaign_ID()); + setSalesRep_ID(project.getSalesRep_ID()); + // + setC_Project_ID(project.getC_Project_ID()); + setDescription(project.getName()); + Timestamp ts = project.getDateContract(); + if (ts != null) + setDateOrdered (ts); + ts = project.getDateFinish(); + if (ts != null) + setDatePromised (ts); + // + setC_BPartner_ID(project.getC_BPartner_ID()); + setC_BPartner_Location_ID(project.getC_BPartner_Location_ID()); + setAD_User_ID(project.getAD_User_ID()); + // + setM_Warehouse_ID(project.getM_Warehouse_ID()); + setM_PriceList_ID(project.getM_PriceList_ID()); + setC_PaymentTerm_ID(project.getC_PaymentTerm_ID()); + // + setIsSOTrx(IsSOTrx); + if (IsSOTrx) + { + if (DocSubTypeSO == null || DocSubTypeSO.length() == 0) + setC_DocTypeTarget_ID(DocSubTypeSO_OnCredit); + else + setC_DocTypeTarget_ID(DocSubTypeSO); + } + else + setC_DocTypeTarget_ID(); + } // MOrder + + /** + * Load Constructor + * @param ctx context + * @param rs result set record + * @param trxName transaction + */ + public MOrder (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MOrder + + /** Order Lines */ + private MOrderLine[] m_lines = null; + /** Tax Lines */ + private MOrderTax[] m_taxes = null; + /** Force Creation of order */ + private boolean m_forceCreation = false; + + /** + * Overwrite Client/Org if required + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + public void setClientOrg (int AD_Client_ID, int AD_Org_ID) + { + super.setClientOrg(AD_Client_ID, AD_Org_ID); + } // setClientOrg + + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + /** + * Set Business Partner (Ship+Bill) + * @param C_BPartner_ID bpartner + */ + public void setC_BPartner_ID (int C_BPartner_ID) + { + super.setC_BPartner_ID (C_BPartner_ID); + super.setBill_BPartner_ID (C_BPartner_ID); + } // setC_BPartner_ID + + /** + * Set Business Partner Location (Ship+Bill) + * @param C_BPartner_Location_ID bp location + */ + public void setC_BPartner_Location_ID (int C_BPartner_Location_ID) + { + super.setC_BPartner_Location_ID (C_BPartner_Location_ID); + super.setBill_Location_ID(C_BPartner_Location_ID); + } // setC_BPartner_Location_ID + + /** + * Set Business Partner Contact (Ship+Bill) + * @param AD_User_ID user + */ + public void setAD_User_ID (int AD_User_ID) + { + super.setAD_User_ID (AD_User_ID); + super.setBill_User_ID (AD_User_ID); + } // setAD_User_ID + + /** + * Set Ship Business Partner + * @param C_BPartner_ID bpartner + */ + public void setShip_BPartner_ID (int C_BPartner_ID) + { + super.setC_BPartner_ID (C_BPartner_ID); + } // setShip_BPartner_ID + + /** + * Set Ship Business Partner Location + * @param C_BPartner_Location_ID bp location + */ + public void setShip_Location_ID (int C_BPartner_Location_ID) + { + super.setC_BPartner_Location_ID (C_BPartner_Location_ID); + } // setShip_Location_ID + + /** + * Set Ship Business Partner Contact + * @param AD_User_ID user + */ + public void setShip_User_ID (int AD_User_ID) + { + super.setAD_User_ID (AD_User_ID); + } // setShip_User_ID + + + /** + * Set Warehouse + * @param M_Warehouse_ID warehouse + */ + public void setM_Warehouse_ID (int M_Warehouse_ID) + { + super.setM_Warehouse_ID (M_Warehouse_ID); + } // setM_Warehouse_ID + + /** + * Set Drop Ship + * @param IsDropShip drop ship + */ + public void setIsDropShip (boolean IsDropShip) + { + super.setIsDropShip (IsDropShip); + } // setIsDropShip + + /*************************************************************************/ + + /** Sales Order Sub Type - SO */ + public static final String DocSubTypeSO_Standard = "SO"; + /** Sales Order Sub Type - OB */ + public static final String DocSubTypeSO_Quotation = "OB"; + /** Sales Order Sub Type - ON */ + public static final String DocSubTypeSO_Proposal = "ON"; + /** Sales Order Sub Type - PR */ + public static final String DocSubTypeSO_Prepay = "PR"; + /** Sales Order Sub Type - WR */ + public static final String DocSubTypeSO_POS = "WR"; + /** Sales Order Sub Type - WP */ + public static final String DocSubTypeSO_Warehouse = "WP"; + /** Sales Order Sub Type - WI */ + public static final String DocSubTypeSO_OnCredit = "WI"; + /** Sales Order Sub Type - RM */ + public static final String DocSubTypeSO_RMA = "RM"; + + /** + * Set Target Sales Document Type + * @param DocSubTypeSO_x SO sub type - see DocSubTypeSO_* + */ + public void setC_DocTypeTarget_ID (String DocSubTypeSO_x) + { + String sql = "SELECT C_DocType_ID FROM C_DocType " + + "WHERE AD_Client_ID=? AND AD_Org_ID IN (0," + getAD_Org_ID() + + ") AND DocSubTypeSO=? " + + "ORDER BY AD_Org_ID DESC, IsDefault DESC"; + int C_DocType_ID = DB.getSQLValue(null, sql, getAD_Client_ID(), DocSubTypeSO_x); + if (C_DocType_ID <= 0) + log.severe ("Not found for AD_Client_ID=" + getAD_Client_ID () + ", SubType=" + DocSubTypeSO_x); + else + { + log.fine("(SO) - " + DocSubTypeSO_x); + setC_DocTypeTarget_ID (C_DocType_ID); + setIsSOTrx(true); + } + } // setC_DocTypeTarget_ID + + /** + * Set Target Document Type. + * Standard Order or PO + */ + public void setC_DocTypeTarget_ID () + { + if (isSOTrx()) // SO = Std Order + { + setC_DocTypeTarget_ID(DocSubTypeSO_Standard); + return; + } + // PO + String sql = "SELECT C_DocType_ID FROM C_DocType " + + "WHERE AD_Client_ID=? AND AD_Org_ID IN (0," + getAD_Org_ID() + + ") AND DocBaseType='POO' " + + "ORDER BY AD_Org_ID DESC, IsDefault DESC"; + int C_DocType_ID = DB.getSQLValue(null, sql, getAD_Client_ID()); + if (C_DocType_ID <= 0) + log.severe ("No POO found for AD_Client_ID=" + getAD_Client_ID ()); + else + { + log.fine("(PO) - " + C_DocType_ID); + setC_DocTypeTarget_ID (C_DocType_ID); + } + } // setC_DocTypeTarget_ID + + + /** + * Set Business Partner Defaults & Details. + * SOTrx should be set. + * @param bp business partner + */ + public void setBPartner (MBPartner bp) + { + if (bp == null) + return; + + setC_BPartner_ID(bp.getC_BPartner_ID()); + // Defaults Payment Term + int ii = 0; + if (isSOTrx()) + ii = bp.getC_PaymentTerm_ID(); + else + ii = bp.getPO_PaymentTerm_ID(); + if (ii != 0) + setC_PaymentTerm_ID(ii); + // Default Price List + if (isSOTrx()) + ii = bp.getM_PriceList_ID(); + else + ii = bp.getPO_PriceList_ID(); + if (ii != 0) + setM_PriceList_ID(ii); + // Default Delivery/Via Rule + String ss = bp.getDeliveryRule(); + if (ss != null) + setDeliveryRule(ss); + ss = bp.getDeliveryViaRule(); + if (ss != null) + setDeliveryViaRule(ss); + // Default Invoice/Payment Rule + ss = bp.getInvoiceRule(); + if (ss != null) + setInvoiceRule(ss); + ss = bp.getPaymentRule(); + if (ss != null) + setPaymentRule(ss); + // Sales Rep + ii = bp.getSalesRep_ID(); + if (ii != 0) + setSalesRep_ID(ii); + + + // Set Locations + MBPartnerLocation[] locs = bp.getLocations(false); + if (locs != null) + { + for (int i = 0; i < locs.length; i++) + { + if (locs[i].isShipTo()) + super.setC_BPartner_Location_ID(locs[i].getC_BPartner_Location_ID()); + if (locs[i].isBillTo()) + setBill_Location_ID(locs[i].getC_BPartner_Location_ID()); + } + // set to first + if (getC_BPartner_Location_ID() == 0 && locs.length > 0) + super.setC_BPartner_Location_ID(locs[0].getC_BPartner_Location_ID()); + if (getBill_Location_ID() == 0 && locs.length > 0) + setBill_Location_ID(locs[0].getC_BPartner_Location_ID()); + } + if (getC_BPartner_Location_ID() == 0) + log.log(Level.SEVERE, "MOrder.setBPartner - Has no Ship To Address: " + bp); + if (getBill_Location_ID() == 0) + log.log(Level.SEVERE, "MOrder.setBPartner - Has no Bill To Address: " + bp); + + // Set Contact + MUser[] contacts = bp.getContacts(false); + if (contacts != null && contacts.length == 1) + setAD_User_ID(contacts[0].getAD_User_ID()); + } // setBPartner + + + /** + * Copy Lines From other Order + * @param otherOrder order + * @param counter set counter info + * @param copyASI copy line attributes Attribute Set Instance, Resaouce Assignment + * @return number of lines copied + */ + public int copyLinesFrom (MOrder otherOrder, boolean counter, boolean copyASI) + { + if (isProcessed() || isPosted() || otherOrder == null) + return 0; + MOrderLine[] fromLines = otherOrder.getLines(false, null); + int count = 0; + for (int i = 0; i < fromLines.length; i++) + { + MOrderLine line = new MOrderLine (this); + PO.copyValues(fromLines[i], line, getAD_Client_ID(), getAD_Org_ID()); + line.setC_Order_ID(getC_Order_ID()); + line.setOrder(this); + line.set_ValueNoCheck ("C_OrderLine_ID", I_ZERO); // new + // References + if (!copyASI) + { + line.setM_AttributeSetInstance_ID(0); + line.setS_ResourceAssignment_ID(0); + } + if (counter) + line.setRef_OrderLine_ID(fromLines[i].getC_OrderLine_ID()); + else + line.setRef_OrderLine_ID(0); + // + line.setQtyDelivered(Env.ZERO); + line.setQtyInvoiced(Env.ZERO); + line.setQtyReserved(Env.ZERO); + line.setDateDelivered(null); + line.setDateInvoiced(null); + // Tax + if (getC_BPartner_ID() != otherOrder.getC_BPartner_ID()) + line.setTax(); // recalculate + // + // + line.setProcessed(false); + if (line.save(get_TrxName())) + count++; + // Cross Link + if (counter) + { + fromLines[i].setRef_OrderLine_ID(line.getC_OrderLine_ID()); + fromLines[i].save(get_TrxName()); + } + } + if (fromLines.length != count) + log.log(Level.SEVERE, "Line difference - From=" + fromLines.length + " <> Saved=" + count); + return count; + } // copyLinesFrom + + + /************************************************************************** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MOrder[") + .append(get_ID()).append("-").append(getDocumentNo()) + .append(",IsSOTrx=").append(isSOTrx()) + .append(",C_DocType_ID=").append(getC_DocType_ID()) + .append(", GrandTotal=").append(getGrandTotal()) + .append ("]"); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.ORDER, getC_Order_ID(), get_TrxName()); + if (re == null) + return null; + return re.getPDF(file); + } // createPDF + + /** + * Set Price List (and Currency, TaxIncluded) when valid + * @param M_PriceList_ID price list + */ + public void setM_PriceList_ID (int M_PriceList_ID) + { + MPriceList pl = MPriceList.get(getCtx(), M_PriceList_ID, null); + if (pl.get_ID() == M_PriceList_ID) + { + super.setM_PriceList_ID(M_PriceList_ID); + setC_Currency_ID(pl.getC_Currency_ID()); + setIsTaxIncluded(pl.isTaxIncluded()); + } + } // setM_PriceList_ID + + + /************************************************************************** + * Get Lines of Order + * @param whereClause where clause or null (starting with AND) + * @param orderClause order clause + * @return lines + */ + public MOrderLine[] getLines (String whereClause, String orderClause) + { + ArrayList list = new ArrayList (); + StringBuffer sql = new StringBuffer("SELECT * FROM C_OrderLine WHERE C_Order_ID=? "); + if (whereClause != null) + sql.append(whereClause); + if (orderClause != null) + sql.append(" ").append(orderClause); + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql.toString(), get_TrxName()); + pstmt.setInt(1, getC_Order_ID()); + rs = pstmt.executeQuery(); + while (rs.next()) + { + MOrderLine ol = new MOrderLine(getCtx(), rs, get_TrxName()); + ol.setHeaderInfo (this); + list.add(ol); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, sql.toString(), e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // + MOrderLine[] lines = new MOrderLine[list.size ()]; + list.toArray (lines); + return lines; + } // getLines + + /** + * Get Lines of Order + * @param requery requery + * @param orderBy optional order by column + * @return lines + */ + public MOrderLine[] getLines (boolean requery, String orderBy) + { + if (m_lines != null && !requery) { + set_TrxName(m_lines, get_TrxName()); + return m_lines; + } + // + String orderClause = "ORDER BY "; + if (orderBy != null && orderBy.length() > 0) + orderClause += orderBy; + else + orderClause += "Line"; + m_lines = getLines(null, orderClause); + return m_lines; + } // getLines + + /** + * Get Lines of Order. + * (useb by web store) + * @return lines + */ + public MOrderLine[] getLines() + { + return getLines(false, null); + } // getLines + + /** + * Renumber Lines + * @param step start and step + */ + public void renumberLines (int step) + { + int number = step; + MOrderLine[] lines = getLines(true, null); // Line is default + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + line.setLine(number); + line.save(get_TrxName()); + number += step; + } + m_lines = null; + } // renumberLines + + /** + * Does the Order Line belong to this Order + * @param C_OrderLine_ID line + * @return true if part of the order + */ + public boolean isOrderLine(int C_OrderLine_ID) + { + if (m_lines == null) + getLines(); + for (int i = 0; i < m_lines.length; i++) + if (m_lines[i].getC_OrderLine_ID() == C_OrderLine_ID) + return true; + return false; + } // isOrderLine + + /** + * Get Taxes of Order + * @param requery requery + * @return array of taxes + */ + public MOrderTax[] getTaxes(boolean requery) + { + if (m_taxes != null && !requery) + return m_taxes; + // + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM C_OrderTax WHERE C_Order_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Order_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MOrderTax(getCtx(), rs, get_TrxName())); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "getTaxes", e); + } + finally + { + try + { + if (pstmt != null) + pstmt.close (); + } + catch (Exception e) + {} + pstmt = null; + } + // + m_taxes = new MOrderTax[list.size ()]; + list.toArray (m_taxes); + return m_taxes; + } // getTaxes + + + /** + * Get Invoices of Order + * @return invoices + */ + public MInvoice[] getInvoices() + { + ArrayList list = new ArrayList(); + String sql = " SELECT DISTINCT i.* FROM C_InvoiceLine il " + + "INNER JOIN C_OrderLine ol ON (ol.C_OrderLine_ID = il.C_OrderLine_ID) " + + "INNER JOIN C_Order o ON (o.C_Order_ID = ol.C_Order_ID) " + + "INNER JOIN C_Invoice i ON (i.C_Invoice_ID = il.C_Invoice_ID) " + + "WHERE o.C_Order_ID=? " + + "ORDER BY i.Created DESC"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Order_ID()); + rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MInvoice(getCtx(), rs, get_TrxName())); + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // + MInvoice[] retValue = new MInvoice[list.size()]; + list.toArray(retValue); + return retValue; + } // getInvoices + + /** + * Get latest Invoice of Order + * @return invoice id or 0 + */ + public int getC_Invoice_ID() + { + int C_Invoice_ID = 0; + String sql = "SELECT C_Invoice_ID FROM C_Invoice " + + "WHERE C_Order_ID=? AND DocStatus IN ('CO','CL') " + + "ORDER BY Created DESC"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Order_ID()); + rs = pstmt.executeQuery(); + if (rs.next()) + C_Invoice_ID = rs.getInt(1); + } + catch (Exception e) + { + log.log(Level.SEVERE, "getC_Invoice_ID", e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + return C_Invoice_ID; + } // getC_Invoice_ID + + + /** + * Get Shipments of Order + * @return shipments + */ + public MInOut[] getShipments() + { + ArrayList list = new ArrayList(); + String sql = "SELECT DISTINCT io.* FROM M_InOutLine iol " + + "INNER JOIN M_InOut io ON (io.M_InOut_ID = iol.M_InOut_ID) " + + "INNER JOIN C_ORDERLINE ol ON (ol.C_ORDERLINE_ID=iol.C_ORDERLINE_ID) " + + "INNER JOIN C_ORDER o ON (o.C_ORDER_ID=ol.C_ORDER_ID) " + + "WHERE o.C_ORDER_ID=? " + + "ORDER BY io.Created DESC"; + + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Order_ID()); + rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MInOut(getCtx(), rs, get_TrxName())); + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // + MInOut[] retValue = new MInOut[list.size()]; + list.toArray(retValue); + return retValue; + } // getShipments + + /** + * Get ISO Code of Currency + * @return Currency ISO + */ + public String getCurrencyISO() + { + return MCurrency.getISO_Code (getCtx(), getC_Currency_ID()); + } // getCurrencyISO + + /** + * Get Currency Precision + * @return precision + */ + public int getPrecision() + { + return MCurrency.getStdPrecision(getCtx(), getC_Currency_ID()); + } // getPrecision + + /** + * Get Document Status + * @return Document Status Clear Text + */ + public String getDocStatusName() + { + return MRefList.getListName(getCtx(), 131, getDocStatus()); + } // getDocStatusName + + /** + * Set DocAction + * @param DocAction doc action + */ + public void setDocAction (String DocAction) + { + setDocAction (DocAction, false); + } // setDocAction + + /** + * Set DocAction + * @param DocAction doc oction + * @param forceCreation force creation + */ + public void setDocAction (String DocAction, boolean forceCreation) + { + super.setDocAction (DocAction); + m_forceCreation = forceCreation; + } // setDocAction + + /** + * Set Processed. + * Propergate to Lines/Taxes + * @param processed processed + */ + public void setProcessed (boolean processed) + { + super.setProcessed (processed); + if (get_ID() == 0) + return; + String set = "SET Processed='" + + (processed ? "Y" : "N") + + "' WHERE C_Order_ID=" + getC_Order_ID(); + int noLine = DB.executeUpdate("UPDATE C_OrderLine " + set, get_TrxName()); + int noTax = DB.executeUpdate("UPDATE C_OrderTax " + set, get_TrxName()); + m_lines = null; + m_taxes = null; + log.fine("setProcessed - " + processed + " - Lines=" + noLine + ", Tax=" + noTax); + } // setProcessed + + + + /************************************************************************** + * Before Save + * @param newRecord new + * @return save + */ + protected boolean beforeSave (boolean newRecord) + { + // Client/Org Check + if (getAD_Org_ID() == 0) + { + int context_AD_Org_ID = Env.getAD_Org_ID(getCtx()); + if (context_AD_Org_ID != 0) + { + setAD_Org_ID(context_AD_Org_ID); + log.warning("Changed Org to Context=" + context_AD_Org_ID); + } + } + if (getAD_Client_ID() == 0) + { + m_processMsg = "AD_Client_ID = 0"; + return false; + } + + // New Record Doc Type - make sure DocType set to 0 + if (newRecord && getC_DocType_ID() == 0) + setC_DocType_ID (0); + + // Default Warehouse + if (getM_Warehouse_ID() == 0) + { + int ii = Env.getContextAsInt(getCtx(), "#M_Warehouse_ID"); + if (ii != 0) + setM_Warehouse_ID(ii); + else + { + log.saveError("FillMandatory", Msg.getElement(getCtx(), "M_Warehouse_ID")); + return false; + } + } + // Warehouse Org + if (newRecord + || is_ValueChanged("AD_Org_ID") || is_ValueChanged("M_Warehouse_ID")) + { + MWarehouse wh = MWarehouse.get(getCtx(), getM_Warehouse_ID()); + if (wh.getAD_Org_ID() != getAD_Org_ID()) + log.saveWarning("WarehouseOrgConflict", ""); + } + // Reservations in Warehouse + if (!newRecord && is_ValueChanged("M_Warehouse_ID")) + { + MOrderLine[] lines = getLines(false,null); + for (int i = 0; i < lines.length; i++) + { + if (!lines[i].canChangeWarehouse()) + return false; + } + } + + // No Partner Info - set Template + if (getC_BPartner_ID() == 0) + setBPartner(MBPartner.getTemplate(getCtx(), getAD_Client_ID())); + if (getC_BPartner_Location_ID() == 0) + setBPartner(new MBPartner(getCtx(), getC_BPartner_ID(), null)); + // No Bill - get from Ship + if (getBill_BPartner_ID() == 0) + { + setBill_BPartner_ID(getC_BPartner_ID()); + setBill_Location_ID(getC_BPartner_Location_ID()); + } + if (getBill_Location_ID() == 0) + setBill_Location_ID(getC_BPartner_Location_ID()); + + // Default Price List + if (getM_PriceList_ID() == 0) + { + int ii = DB.getSQLValue(null, + "SELECT M_PriceList_ID FROM M_PriceList " + + "WHERE AD_Client_ID=? AND IsSOPriceList=? " + + "ORDER BY IsDefault DESC", getAD_Client_ID(), isSOTrx() ? "Y" : "N"); + if (ii != 0) + setM_PriceList_ID (ii); + } + // Default Currency + if (getC_Currency_ID() == 0) + { + String sql = "SELECT C_Currency_ID FROM M_PriceList WHERE M_PriceList_ID=?"; + int ii = DB.getSQLValue (null, sql, getM_PriceList_ID()); + if (ii != 0) + setC_Currency_ID (ii); + else + setC_Currency_ID(Env.getContextAsInt(getCtx(), "#C_Currency_ID")); + } + + // Default Sales Rep + if (getSalesRep_ID() == 0) + { + int ii = Env.getContextAsInt(getCtx(), "#SalesRep_ID"); + if (ii != 0) + setSalesRep_ID (ii); + } + + // Default Document Type + if (getC_DocTypeTarget_ID() == 0) + setC_DocTypeTarget_ID(DocSubTypeSO_Standard); + + // Default Payment Term + if (getC_PaymentTerm_ID() == 0) + { + int ii = Env.getContextAsInt(getCtx(), "#C_PaymentTerm_ID"); + if (ii != 0) + setC_PaymentTerm_ID(ii); + else + { + String sql = "SELECT C_PaymentTerm_ID FROM C_PaymentTerm WHERE AD_Client_ID=? AND IsDefault='Y'"; + ii = DB.getSQLValue(null, sql, getAD_Client_ID()); + if (ii != 0) + setC_PaymentTerm_ID (ii); + } + } + + return true; + } // beforeSave + + + /** + * After Save + * @param newRecord new + * @param success success + * @return true if can be saved + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + if (!success || newRecord) + return success; + + // Propagate Description changes + if (is_ValueChanged("Description") || is_ValueChanged("POReference")) + { + String sql = "UPDATE C_Invoice i" + + " SET (Description,POReference)=" + + "(SELECT Description,POReference " + + "FROM C_Order o WHERE i.C_Order_ID=o.C_Order_ID) " + + "WHERE DocStatus NOT IN ('RE','CL') AND C_Order_ID=" + getC_Order_ID(); + int no = DB.executeUpdate(sql, get_TrxName()); + log.fine("Description -> #" + no); + } + + // Propagate Changes of Payment Info to existing (not reversed/closed) invoices + if (is_ValueChanged("PaymentRule") || is_ValueChanged("C_PaymentTerm_ID") + || is_ValueChanged("DateAcct") || is_ValueChanged("C_Payment_ID") + || is_ValueChanged("C_CashLine_ID")) + { + String sql = "UPDATE C_Invoice i " + + "SET (PaymentRule,C_PaymentTerm_ID,DateAcct,C_Payment_ID,C_CashLine_ID)=" + + "(SELECT PaymentRule,C_PaymentTerm_ID,DateAcct,C_Payment_ID,C_CashLine_ID " + + "FROM C_Order o WHERE i.C_Order_ID=o.C_Order_ID)" + + "WHERE DocStatus NOT IN ('RE','CL') AND C_Order_ID=" + getC_Order_ID(); + // Don't touch Closed/Reversed entries + int no = DB.executeUpdate(sql, get_TrxName()); + log.fine("Payment -> #" + no); + } + + // Sync Lines + afterSaveSync("AD_Org_ID"); + afterSaveSync("C_BPartner_ID"); + afterSaveSync("C_BPartner_Location_ID"); + afterSaveSync("DateOrdered"); + afterSaveSync("DatePromised"); + afterSaveSync("M_Warehouse_ID"); + afterSaveSync("M_Shipper_ID"); + afterSaveSync("C_Currency_ID"); + // + return true; + } // afterSave + + private void afterSaveSync (String columnName) + { + if (is_ValueChanged(columnName)) + { + String sql = "UPDATE C_OrderLine ol" + + " SET " + columnName + " =" + + "(SELECT " + columnName + + " FROM C_Order o WHERE ol.C_Order_ID=o.C_Order_ID) " + + "WHERE C_Order_ID=" + getC_Order_ID(); + int no = DB.executeUpdate(sql, get_TrxName()); + log.fine(columnName + " Lines -> #" + no); + } + } // afterSaveSync + + /** + * Before Delete + * @return true of it can be deleted + */ + protected boolean beforeDelete () + { + if (isProcessed()) + return false; + + getLines(); + for (int i = 0; i < m_lines.length; i++) + { + if (!m_lines[i].beforeDelete()) + return false; + } + return true; + } // beforeDelete + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // processIt + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info("unlockIt - " + toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + + /************************************************************************** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + MDocType dt = MDocType.get(getCtx(), getC_DocTypeTarget_ID()); + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getDateAcct(), dt.getDocBaseType())) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + + // Lines + MOrderLine[] lines = getLines(true, "M_Product_ID"); + if (lines.length == 0) + { + m_processMsg = "@NoLines@"; + return DocAction.STATUS_Invalid; + } + + // Bug 1564431 + if (getDeliveryRule() != null && getDeliveryRule().equals(MOrder.DELIVERYRULE_CompleteOrder)) + { + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + MProduct product = line.getProduct(); + if (product != null && product.isExcludeAutoDelivery()) + { + m_processMsg = "@M_Product_ID@ "+product.getValue()+" @IsExcludeAutoDelivery@"; + return DocAction.STATUS_Invalid; + } + } + } + + // Convert DocType to Target + if (getC_DocType_ID() != getC_DocTypeTarget_ID() ) + { + // Cannot change Std to anything else if different warehouses + if (getC_DocType_ID() != 0) + { + MDocType dtOld = MDocType.get(getCtx(), getC_DocType_ID()); + if (MDocType.DOCSUBTYPESO_StandardOrder.equals(dtOld.getDocSubTypeSO()) // From SO + && !MDocType.DOCSUBTYPESO_StandardOrder.equals(dt.getDocSubTypeSO())) // To !SO + { + for (int i = 0; i < lines.length; i++) + { + if (lines[i].getM_Warehouse_ID() != getM_Warehouse_ID()) + { + log.warning("different Warehouse " + lines[i]); + m_processMsg = "@CannotChangeDocType@"; + return DocAction.STATUS_Invalid; + } + } + } + } + + // New or in Progress/Invalid + if (DOCSTATUS_Drafted.equals(getDocStatus()) + || DOCSTATUS_InProgress.equals(getDocStatus()) + || DOCSTATUS_Invalid.equals(getDocStatus()) + || getC_DocType_ID() == 0) + { + setC_DocType_ID(getC_DocTypeTarget_ID()); + } + else // convert only if offer + { + if (dt.isOffer()) + setC_DocType_ID(getC_DocTypeTarget_ID()); + else + { + m_processMsg = "@CannotChangeDocType@"; + return DocAction.STATUS_Invalid; + } + } + } // convert DocType + + // Mandatory Product Attribute Set Instance + String mandatoryType = "='Y'"; // IN ('Y','S') + String sql = "SELECT COUNT(*) " + + "FROM C_OrderLine ol" + + " INNER JOIN M_Product p ON (ol.M_Product_ID=p.M_Product_ID)" + + " INNER JOIN M_AttributeSet pas ON (p.M_AttributeSet_ID=pas.M_AttributeSet_ID) " + + "WHERE pas.MandatoryType" + mandatoryType + + " AND ol.M_AttributeSetInstance_ID IS NULL" + + " AND ol.C_Order_ID=?"; + int no = DB.getSQLValue(get_TrxName(), sql, getC_Order_ID()); + if (no != 0) + { + m_processMsg = "@LinesWithoutProductAttribute@ (" + no + ")"; + return DocAction.STATUS_Invalid; + } + + // Lines + if (explodeBOM()) + lines = getLines(true, "M_Product_ID"); + if (!reserveStock(dt, lines)) + { + m_processMsg = "Cannot reserve Stock"; + return DocAction.STATUS_Invalid; + } + if (!calculateTaxTotal()) + { + m_processMsg = "Error calculating tax"; + return DocAction.STATUS_Invalid; + } + + // Credit Check + if (isSOTrx()) + { + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + if (MBPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus())) + { + m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus())) + { + m_processMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + BigDecimal grandTotal = MConversionRate.convertBase(getCtx(), + getGrandTotal(), getC_Currency_ID(), getDateOrdered(), + getC_ConversionType_ID(), getAD_Client_ID(), getAD_Org_ID()); + if (MBPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus(grandTotal))) + { + m_processMsg = "@BPartnerOverOCreditHold@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + ", @GrandTotal@=" + grandTotal + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + // if (!DOCACTION_Complete.equals(getDocAction())) don't set for just prepare + // setDocAction(DOCACTION_Complete); + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Explode non stocked BOM. + * @return true if bom exploded + */ + private boolean explodeBOM() + { + boolean retValue = false; + String where = "AND IsActive='Y' AND EXISTS " + + "(SELECT * FROM M_Product p WHERE C_OrderLine.M_Product_ID=p.M_Product_ID" + + " AND p.IsBOM='Y' AND p.IsVerified='Y' AND p.IsStocked='N')"; + // + String sql = "SELECT COUNT(*) FROM C_OrderLine " + + "WHERE C_Order_ID=? " + where; + int count = DB.getSQLValue(get_TrxName(), sql, getC_Order_ID()); + while (count != 0) + { + retValue = true; + renumberLines (1000); // max 999 bom items + + // Order Lines with non-stocked BOMs + MOrderLine[] lines = getLines (where, "ORDER BY Line"); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + MProduct product = MProduct.get (getCtx(), line.getM_Product_ID()); + log.fine(product.getName()); + // New Lines + int lineNo = line.getLine (); + MProductBOM[] boms = MProductBOM.getBOMLines (product); + for (int j = 0; j < boms.length; j++) + { + MProductBOM bom = boms[j]; + MOrderLine newLine = new MOrderLine (this); + newLine.setLine (++lineNo); + newLine.setM_Product_ID (bom.getProduct () + .getM_Product_ID ()); + newLine.setC_UOM_ID (bom.getProduct ().getC_UOM_ID ()); + newLine.setQty (line.getQtyOrdered ().multiply ( + bom.getBOMQty ())); + if (bom.getDescription () != null) + newLine.setDescription (bom.getDescription ()); + // + newLine.setPrice (); + newLine.save (get_TrxName()); + } + // Convert into Comment Line + line.setM_Product_ID (0); + line.setM_AttributeSetInstance_ID (0); + line.setPrice (Env.ZERO); + line.setPriceLimit (Env.ZERO); + line.setPriceList (Env.ZERO); + line.setLineNetAmt (Env.ZERO); + line.setFreightAmt (Env.ZERO); + // + String description = product.getName (); + if (product.getDescription () != null) + description += " " + product.getDescription (); + if (line.getDescription () != null) + description += " " + line.getDescription (); + line.setDescription (description); + line.save (get_TrxName()); + } // for all lines with BOM + + m_lines = null; // force requery + count = DB.getSQLValue (get_TrxName(), sql, getC_Invoice_ID ()); + renumberLines (10); + } // while count != 0 + return retValue; + } // explodeBOM + + + /** + * Reserve Inventory. + * Counterpart: MInOut.completeIt() + * @param dt document type or null + * @param lines order lines (ordered by M_Product_ID for deadlock prevention) + * @return true if (un) reserved + */ + private boolean reserveStock (MDocType dt, MOrderLine[] lines) + { + if (dt == null) + dt = MDocType.get(getCtx(), getC_DocType_ID()); + + // Binding + boolean binding = !dt.isProposal(); + // Not binding - i.e. Target=0 + if (DOCACTION_Void.equals(getDocAction()) + // Closing Binding Quotation + || (MDocType.DOCSUBTYPESO_Quotation.equals(dt.getDocSubTypeSO()) + && DOCACTION_Close.equals(getDocAction())) + || isDropShip() ) + binding = false; + boolean isSOTrx = isSOTrx(); + log.fine("Binding=" + binding + " - IsSOTrx=" + isSOTrx); + // Force same WH for all but SO/PO + int header_M_Warehouse_ID = getM_Warehouse_ID(); + if (MDocType.DOCSUBTYPESO_StandardOrder.equals(dt.getDocSubTypeSO()) + || MDocType.DOCBASETYPE_PurchaseOrder.equals(dt.getDocBaseType())) + header_M_Warehouse_ID = 0; // don't enforce + + BigDecimal Volume = Env.ZERO; + BigDecimal Weight = Env.ZERO; + + // Always check and (un) Reserve Inventory + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + // Check/set WH/Org + if (header_M_Warehouse_ID != 0) // enforce WH + { + if (header_M_Warehouse_ID != line.getM_Warehouse_ID()) + line.setM_Warehouse_ID(header_M_Warehouse_ID); + if (getAD_Org_ID() != line.getAD_Org_ID()) + line.setAD_Org_ID(getAD_Org_ID()); + } + // Binding + BigDecimal target = binding ? line.getQtyOrdered() : Env.ZERO; + BigDecimal difference = target + .subtract(line.getQtyReserved()) + .subtract(line.getQtyDelivered()); + if (difference.signum() == 0) + { + MProduct product = line.getProduct(); + if (product != null) + { + Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); + Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); + } + continue; + } + + log.fine("Line=" + line.getLine() + + " - Target=" + target + ",Difference=" + difference + + " - Ordered=" + line.getQtyOrdered() + + ",Reserved=" + line.getQtyReserved() + ",Delivered=" + line.getQtyDelivered()); + + // Check Product - Stocked and Item + MProduct product = line.getProduct(); + if (product != null) + { + if (product.isStocked()) + { + BigDecimal ordered = isSOTrx ? Env.ZERO : difference; + BigDecimal reserved = isSOTrx ? difference : Env.ZERO; + int M_Locator_ID = 0; + // Get Locator to reserve + if (line.getM_AttributeSetInstance_ID() != 0) // Get existing Location + M_Locator_ID = MStorage.getM_Locator_ID (line.getM_Warehouse_ID(), + line.getM_Product_ID(), line.getM_AttributeSetInstance_ID(), + ordered, get_TrxName()); + // Get default Location + if (M_Locator_ID == 0) + { + // try to take default locator for product first + // if it is from the selected warehouse + MWarehouse wh = MWarehouse.get(getCtx(), line.getM_Warehouse_ID()); + M_Locator_ID = product.getM_Locator_ID(); + if (M_Locator_ID!=0) { + MLocator locator = new MLocator(getCtx(), product.getM_Locator_ID(), get_TrxName()); + //product has default locator defined but is not from the order warehouse + if(locator.getM_Warehouse_ID()!=wh.get_ID()) { + M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); + } + } else { + M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); + } + } + // Update Storage + if (!MStorage.add(getCtx(), line.getM_Warehouse_ID(), M_Locator_ID, + line.getM_Product_ID(), + line.getM_AttributeSetInstance_ID(), line.getM_AttributeSetInstance_ID(), + Env.ZERO, reserved, ordered, get_TrxName())) + return false; + } // stockec + // update line + line.setQtyReserved(line.getQtyReserved().add(difference)); + if (!line.save(get_TrxName())) + return false; + // + Volume = Volume.add(product.getVolume().multiply(line.getQtyOrdered())); + Weight = Weight.add(product.getWeight().multiply(line.getQtyOrdered())); + } // product + } // reverse inventory + + setVolume(Volume); + setWeight(Weight); + return true; + } // reserveStock + + /** + * Calculate Tax and Total + * @return true if tax total calculated + */ + public boolean calculateTaxTotal() + { + log.fine(""); + // Delete Taxes + DB.executeUpdate("DELETE C_OrderTax WHERE C_Order_ID=" + getC_Order_ID(), get_TrxName()); + m_taxes = null; + + // Lines + BigDecimal totalLines = Env.ZERO; + ArrayList taxList = new ArrayList(); + MOrderLine[] lines = getLines(); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + Integer taxID = new Integer(line.getC_Tax_ID()); + if (!taxList.contains(taxID)) + { + MOrderTax oTax = MOrderTax.get (line, getPrecision(), + false, get_TrxName()); // current Tax + oTax.setIsTaxIncluded(isTaxIncluded()); + if (!oTax.calculateTaxFromLines()) + return false; + if (!oTax.save(get_TrxName())) + return false; + taxList.add(taxID); + } + totalLines = totalLines.add(line.getLineNetAmt()); + } + + // Taxes + BigDecimal grandTotal = totalLines; + MOrderTax[] taxes = getTaxes(true); + for (int i = 0; i < taxes.length; i++) + { + MOrderTax oTax = taxes[i]; + MTax tax = oTax.getTax(); + if (tax.isSummary()) + { + MTax[] cTaxes = tax.getChildTaxes(false); + for (int j = 0; j < cTaxes.length; j++) + { + MTax cTax = cTaxes[j]; + BigDecimal taxAmt = cTax.calculateTax(oTax.getTaxBaseAmt(), isTaxIncluded(), getPrecision()); + // + MOrderTax newOTax = new MOrderTax(getCtx(), 0, get_TrxName()); + newOTax.setClientOrg(this); + newOTax.setC_Order_ID(getC_Order_ID()); + newOTax.setC_Tax_ID(cTax.getC_Tax_ID()); + newOTax.setPrecision(getPrecision()); + newOTax.setIsTaxIncluded(isTaxIncluded()); + newOTax.setTaxBaseAmt(oTax.getTaxBaseAmt()); + newOTax.setTaxAmt(taxAmt); + if (!newOTax.save(get_TrxName())) + return false; + // + if (!isTaxIncluded()) + grandTotal = grandTotal.add(taxAmt); + } + if (!oTax.delete(true, get_TrxName())) + return false; + if (!oTax.save(get_TrxName())) + return false; + } + else + { + if (!isTaxIncluded()) + grandTotal = grandTotal.add(oTax.getTaxAmt()); + } + } + // + setTotalLines(totalLines); + setGrandTotal(grandTotal); + return true; + } // calculateTaxTotal + + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info("approveIt - " + toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info("rejectIt - " + toString()); + setIsApproved(false); + return true; + } // rejectIt + + + /************************************************************************** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + String DocSubTypeSO = dt.getDocSubTypeSO(); + + // Just prepare + if (DOCACTION_Prepare.equals(getDocAction())) + { + setProcessed(false); + return DocAction.STATUS_InProgress; + } + // Offers + if (MDocType.DOCSUBTYPESO_Proposal.equals(DocSubTypeSO) + || MDocType.DOCSUBTYPESO_Quotation.equals(DocSubTypeSO)) + { + // Binding + if (MDocType.DOCSUBTYPESO_Quotation.equals(DocSubTypeSO)) + reserveStock(dt, getLines(true, "M_Product_ID")); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + setProcessed(true); + return DocAction.STATUS_Completed; + } + // Waiting Payment - until we have a payment + if (!m_forceCreation + && MDocType.DOCSUBTYPESO_PrepayOrder.equals(DocSubTypeSO) + && getC_Payment_ID() == 0 && getC_CashLine_ID() == 0) + { + setProcessed(true); + return DocAction.STATUS_WaitingPayment; + } + + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Implicit Approval + if (!isApproved()) + approveIt(); + getLines(true,null); + log.info(toString()); + StringBuffer info = new StringBuffer(); + + boolean realTimePOS = false; + + // Create SO Shipment - Force Shipment + MInOut shipment = null; + if (MDocType.DOCSUBTYPESO_OnCreditOrder.equals(DocSubTypeSO) // (W)illCall(I)nvoice + || MDocType.DOCSUBTYPESO_WarehouseOrder.equals(DocSubTypeSO) // (W)illCall(P)ickup + || MDocType.DOCSUBTYPESO_POSOrder.equals(DocSubTypeSO) // (W)alkIn(R)eceipt + || MDocType.DOCSUBTYPESO_PrepayOrder.equals(DocSubTypeSO)) + { + if (!DELIVERYRULE_Force.equals(getDeliveryRule())) + setDeliveryRule(DELIVERYRULE_Force); + // + shipment = createShipment (dt, realTimePOS ? null : getDateOrdered()); + if (shipment == null) + return DocAction.STATUS_Invalid; + info.append("@M_InOut_ID@: ").append(shipment.getDocumentNo()); + String msg = shipment.getProcessMsg(); + if (msg != null && msg.length() > 0) + info.append(" (").append(msg).append(")"); + } // Shipment + + + // Create SO Invoice - Always invoice complete Order + if ( MDocType.DOCSUBTYPESO_POSOrder.equals(DocSubTypeSO) + || MDocType.DOCSUBTYPESO_OnCreditOrder.equals(DocSubTypeSO) + || MDocType.DOCSUBTYPESO_PrepayOrder.equals(DocSubTypeSO)) + { + MInvoice invoice = createInvoice (dt, shipment, realTimePOS ? null : getDateOrdered()); + if (invoice == null) + return DocAction.STATUS_Invalid; + info.append(" - @C_Invoice_ID@: ").append(invoice.getDocumentNo()); + String msg = invoice.getProcessMsg(); + if (msg != null && msg.length() > 0) + info.append(" (").append(msg).append(")"); + } // Invoice + + // Counter Documents + MOrder counter = createCounterDoc(); + if (counter != null) + info.append(" - @CounterDoc@: @Order@=").append(counter.getDocumentNo()); + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + if (info.length() > 0) + info.append(" - "); + info.append(valid); + m_processMsg = info.toString(); + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + setProcessed(true); + m_processMsg = info.toString(); + // + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setDateOrdered(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Create Shipment + * @param dt order document type + * @param movementDate optional movement date (default today) + * @return shipment or null + */ + private MInOut createShipment(MDocType dt, Timestamp movementDate) + { + log.info("For " + dt); + MInOut shipment = new MInOut (this, dt.getC_DocTypeShipment_ID(), movementDate); + // shipment.setDateAcct(getDateAcct()); + if (!shipment.save(get_TrxName())) + { + m_processMsg = "Could not create Shipment"; + return null; + } + // + MOrderLine[] oLines = getLines(true, null); + for (int i = 0; i < oLines.length; i++) + { + MOrderLine oLine = oLines[i]; + // + MInOutLine ioLine = new MInOutLine(shipment); + // Qty = Ordered - Delivered + BigDecimal MovementQty = oLine.getQtyOrdered().subtract(oLine.getQtyDelivered()); + // Location + int M_Locator_ID = MStorage.getM_Locator_ID (oLine.getM_Warehouse_ID(), + oLine.getM_Product_ID(), oLine.getM_AttributeSetInstance_ID(), + MovementQty, get_TrxName()); + if (M_Locator_ID == 0) // Get default Location + { + MWarehouse wh = MWarehouse.get(getCtx(), oLine.getM_Warehouse_ID()); + M_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); + } + // + ioLine.setOrderLine(oLine, M_Locator_ID, MovementQty); + ioLine.setQty(MovementQty); + if (oLine.getQtyEntered().compareTo(oLine.getQtyOrdered()) != 0) + ioLine.setQtyEntered(MovementQty + .multiply(oLine.getQtyEntered()) + .divide(oLine.getQtyOrdered(), 6, BigDecimal.ROUND_HALF_UP)); + if (!ioLine.save(get_TrxName())) + { + m_processMsg = "Could not create Shipment Line"; + return null; + } + } + // Manually Process Shipment + String status = shipment.completeIt(); + shipment.setDocStatus(status); + shipment.save(get_TrxName()); + if (!DOCSTATUS_Completed.equals(status)) + { + m_processMsg = "@M_InOut_ID@: " + shipment.getProcessMsg(); + return null; + } + return shipment; + } // createShipment + + /** + * Create Invoice + * @param dt order document type + * @param shipment optional shipment + * @param invoiceDate invoice date + * @return invoice or null + */ + private MInvoice createInvoice (MDocType dt, MInOut shipment, Timestamp invoiceDate) + { + log.info(dt.toString()); + MInvoice invoice = new MInvoice (this, dt.getC_DocTypeInvoice_ID(), invoiceDate); + if (!invoice.save(get_TrxName())) + { + m_processMsg = "Could not create Invoice"; + return null; + } + + // If we have a Shipment - use that as a base + if (shipment != null) + { + if (!INVOICERULE_AfterDelivery.equals(getInvoiceRule())) + setInvoiceRule(INVOICERULE_AfterDelivery); + // + MInOutLine[] sLines = shipment.getLines(false); + for (int i = 0; i < sLines.length; i++) + { + MInOutLine sLine = sLines[i]; + // + MInvoiceLine iLine = new MInvoiceLine(invoice); + iLine.setShipLine(sLine); + // Qty = Delivered + iLine.setQtyEntered(sLine.getQtyEntered()); + iLine.setQtyInvoiced(sLine.getMovementQty()); + if (!iLine.save(get_TrxName())) + { + m_processMsg = "Could not create Invoice Line from Shipment Line"; + return null; + } + // + sLine.setIsInvoiced(true); + if (!sLine.save(get_TrxName())) + { + log.warning("Could not update Shipment line: " + sLine); + } + } + } + else // Create Invoice from Order + { + if (!INVOICERULE_Immediate.equals(getInvoiceRule())) + setInvoiceRule(INVOICERULE_Immediate); + // + MOrderLine[] oLines = getLines(); + for (int i = 0; i < oLines.length; i++) + { + MOrderLine oLine = oLines[i]; + // + MInvoiceLine iLine = new MInvoiceLine(invoice); + iLine.setOrderLine(oLine); + // Qty = Ordered - Invoiced + iLine.setQtyInvoiced(oLine.getQtyOrdered().subtract(oLine.getQtyInvoiced())); + if (oLine.getQtyOrdered().compareTo(oLine.getQtyEntered()) == 0) + iLine.setQtyEntered(iLine.getQtyInvoiced()); + else + iLine.setQtyEntered(iLine.getQtyInvoiced().multiply(oLine.getQtyEntered()) + .divide(oLine.getQtyOrdered(), 12, BigDecimal.ROUND_HALF_UP)); + if (!iLine.save(get_TrxName())) + { + m_processMsg = "Could not create Invoice Line from Order Line"; + return null; + } + } + } + // Manually Process Invoice + String status = invoice.completeIt(); + invoice.setDocStatus(status); + invoice.save(get_TrxName()); + setC_CashLine_ID(invoice.getC_CashLine_ID()); + if (!DOCSTATUS_Completed.equals(status)) + { + m_processMsg = "@C_Invoice_ID@: " + invoice.getProcessMsg(); + return null; + } + return invoice; + } // createInvoice + + /** + * Create Counter Document + * @return counter order + */ + private MOrder createCounterDoc() + { + // Is this itself a counter doc ? + if (getRef_Order_ID() != 0) + return null; + + // Org Must be linked to BPartner + MOrg org = MOrg.get(getCtx(), getAD_Org_ID()); + int counterC_BPartner_ID = org.getLinkedC_BPartner_ID(get_TrxName()); + if (counterC_BPartner_ID == 0) + return null; + // Business Partner needs to be linked to Org + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + int counterAD_Org_ID = bp.getAD_OrgBP_ID_Int(); + if (counterAD_Org_ID == 0) + return null; + + MBPartner counterBP = new MBPartner (getCtx(), counterC_BPartner_ID, null); + MOrgInfo counterOrgInfo = MOrgInfo.get(getCtx(), counterAD_Org_ID); + log.info("Counter BP=" + counterBP.getName()); + + // Document Type + int C_DocTypeTarget_ID = 0; + MDocTypeCounter counterDT = MDocTypeCounter.getCounterDocType(getCtx(), getC_DocType_ID()); + if (counterDT != null) + { + log.fine(counterDT.toString()); + if (!counterDT.isCreateCounter() || !counterDT.isValid()) + return null; + C_DocTypeTarget_ID = counterDT.getCounter_C_DocType_ID(); + } + else // indirect + { + C_DocTypeTarget_ID = MDocTypeCounter.getCounterDocType_ID(getCtx(), getC_DocType_ID()); + log.fine("Indirect C_DocTypeTarget_ID=" + C_DocTypeTarget_ID); + if (C_DocTypeTarget_ID <= 0) + return null; + } + // Deep Copy + MOrder counter = copyFrom (this, getDateOrdered(), + C_DocTypeTarget_ID, !isSOTrx(), true, false, get_TrxName()); + // + counter.setAD_Org_ID(counterAD_Org_ID); + counter.setM_Warehouse_ID(counterOrgInfo.getM_Warehouse_ID()); + // + counter.setBPartner(counterBP); + counter.setDatePromised(getDatePromised()); // default is date ordered + // Refernces (Should not be required + counter.setSalesRep_ID(getSalesRep_ID()); + counter.save(get_TrxName()); + + // Update copied lines + MOrderLine[] counterLines = counter.getLines(true, null); + for (int i = 0; i < counterLines.length; i++) + { + MOrderLine counterLine = counterLines[i]; + counterLine.setOrder(counter); // copies header values (BP, etc.) + counterLine.setPrice(); + counterLine.setTax(); + counterLine.save(get_TrxName()); + } + log.fine(counter.toString()); + + // Document Action + if (counterDT != null) + { + if (counterDT.getDocAction() != null) + { + counter.setDocAction(counterDT.getDocAction()); + counter.processIt(counterDT.getDocAction()); + counter.save(get_TrxName()); + } + } + return counter; + } // createCounterDoc + + /** + * Void Document. + * Set Qtys to 0 - Sales: reverse all documents + * @return true if success + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + MOrderLine[] lines = getLines(true, "M_Product_ID"); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + BigDecimal old = line.getQtyOrdered(); + if (old.signum() != 0) + { + line.addDescription(Msg.getMsg(getCtx(), "Voided") + " (" + old + ")"); + line.setQty(Env.ZERO); + line.setLineNetAmt(Env.ZERO); + line.save(get_TrxName()); + } + } + addDescription(Msg.getMsg(getCtx(), "Voided")); + // Clear Reservations + if (!reserveStock(null, lines)) + { + m_processMsg = "Cannot unreserve Stock (void)"; + return false; + } + + if (!createReversals()) + return false; + + //MZ Goodwill + if (!isSOTrx()) + { + // delete Matched PO Cost Detail + MOrderLine[] linesMZ = getLines(); + for (int i = 0; i < lines.length; i++) + { + MMatchPO[] mPO = MMatchPO.getOrderLine(getCtx(), linesMZ[i].getC_OrderLine_ID(), get_TrxName()); + // delete Cost Detail if the Matched PO has been deleted + if (mPO.length == 0) + { + MCostDetail cd = MCostDetail.get(getCtx(), "C_OrderLine_ID=? AND M_AttributeSetInstance_ID=?", + linesMZ[i].getC_OrderLine_ID(), linesMZ[i].getM_AttributeSetInstance_ID(), get_TrxName()); + if (cd != null) + { + cd.setProcessed(false); + cd.delete(true); + } + } + } + } + //End MZ + + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // voidIt + + /** + * Create Shipment/Invoice Reversals + * @return true if success + */ + private boolean createReversals() + { + // Cancel only Sales + if (!isSOTrx()) + return true; + + log.info("createReversals"); + StringBuffer info = new StringBuffer(); + + // Reverse All *Shipments* + info.append("@M_InOut_ID@:"); + MInOut[] shipments = getShipments(); + for (int i = 0; i < shipments.length; i++) + { + MInOut ship = shipments[i]; + // if closed - ignore + if (MInOut.DOCSTATUS_Closed.equals(ship.getDocStatus()) + || MInOut.DOCSTATUS_Reversed.equals(ship.getDocStatus()) + || MInOut.DOCSTATUS_Voided.equals(ship.getDocStatus()) ) + continue; + ship.set_TrxName(get_TrxName()); + + // If not completed - void - otherwise reverse it + if (!MInOut.DOCSTATUS_Completed.equals(ship.getDocStatus())) + { + if (ship.voidIt()) + ship.setDocStatus(MInOut.DOCSTATUS_Voided); + } + else if (ship.reverseCorrectIt()) // completed shipment + { + ship.setDocStatus(MInOut.DOCSTATUS_Reversed); + info.append(" ").append(ship.getDocumentNo()); + } + else + { + m_processMsg = "Could not reverse Shipment " + ship; + return false; + } + ship.setDocAction(MInOut.DOCACTION_None); + ship.save(get_TrxName()); + } // for all shipments + + // Reverse All *Invoices* + info.append(" - @C_Invoice_ID@:"); + MInvoice[] invoices = getInvoices(); + for (int i = 0; i < invoices.length; i++) + { + MInvoice invoice = invoices[i]; + // if closed - ignore + if (MInvoice.DOCSTATUS_Closed.equals(invoice.getDocStatus()) + || MInvoice.DOCSTATUS_Reversed.equals(invoice.getDocStatus()) + || MInvoice.DOCSTATUS_Voided.equals(invoice.getDocStatus()) ) + continue; + invoice.set_TrxName(get_TrxName()); + + // If not completed - void - otherwise reverse it + if (!MInvoice.DOCSTATUS_Completed.equals(invoice.getDocStatus())) + { + if (invoice.voidIt()) + invoice.setDocStatus(MInvoice.DOCSTATUS_Voided); + } + else if (invoice.reverseCorrectIt()) // completed invoice + { + invoice.setDocStatus(MInvoice.DOCSTATUS_Reversed); + info.append(" ").append(invoice.getDocumentNo()); + } + else + { + m_processMsg = "Could not reverse Invoice " + invoice; + return false; + } + invoice.setDocAction(MInvoice.DOCACTION_None); + invoice.save(get_TrxName()); + } // for all shipments + + m_processMsg = info.toString(); + return true; + } // createReversals + + + /** + * Close Document. + * Cancel not delivered Qunatities + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + + // Close Not delivered Qty - SO/PO + MOrderLine[] lines = getLines(true, "M_Product_ID"); + for (int i = 0; i < lines.length; i++) + { + MOrderLine line = lines[i]; + BigDecimal old = line.getQtyOrdered(); + if (old.compareTo(line.getQtyDelivered()) != 0) + { + line.setQtyLostSales(line.getQtyOrdered().subtract(line.getQtyDelivered())); + line.setQtyOrdered(line.getQtyDelivered()); + // QtyEntered unchanged + line.addDescription("Close (" + old + ")"); + line.save(get_TrxName()); + } + } + // Clear Reservations + if (!reserveStock(null, lines)) + { + m_processMsg = "Cannot unreserve Stock (close)"; + return false; + } + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // closeIt + + /** + * Reverse Correction - same void + * @return true if success + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + return voidIt(); + } // reverseCorrectionIt + + /** + * Reverse Accrual - none + * @return false + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return false; + } // reverseAccrualIt + + /** + * Re-activate. + * @return true if success + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + + + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + String DocSubTypeSO = dt.getDocSubTypeSO(); + + // Replace Prepay with POS to revert all doc + if (MDocType.DOCSUBTYPESO_PrepayOrder.equals (DocSubTypeSO)) + { + MDocType newDT = null; + MDocType[] dts = MDocType.getOfClient (getCtx()); + for (int i = 0; i < dts.length; i++) + { + MDocType type = dts[i]; + if (MDocType.DOCSUBTYPESO_PrepayOrder.equals(type.getDocSubTypeSO())) + { + if (type.isDefault() || newDT == null) + newDT = type; + } + } + if (newDT == null) + return false; + else + setC_DocType_ID (newDT.getC_DocType_ID()); + } + + // PO - just re-open + if (!isSOTrx()) + log.info("Existing documents not modified - " + dt); + // Reverse Direct Documents + else if (MDocType.DOCSUBTYPESO_OnCreditOrder.equals(DocSubTypeSO) // (W)illCall(I)nvoice + || MDocType.DOCSUBTYPESO_WarehouseOrder.equals(DocSubTypeSO) // (W)illCall(P)ickup + || MDocType.DOCSUBTYPESO_POSOrder.equals(DocSubTypeSO)) // (W)alkIn(R)eceipt + { + if (!createReversals()) + return false; + } + else + { + log.info("Existing documents not modified - SubType=" + DocSubTypeSO); + } + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + setDocAction(DOCACTION_Complete); + setProcessed(false); + return true; + } // reActivateIt + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Grand Total = 123.00 (#1) + sb.append(": "). + append(Msg.translate(getCtx(),"GrandTotal")).append("=").append(getGrandTotal()); + if (m_lines != null) + sb.append(" (#").append(m_lines.length).append(")"); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getSalesRep_ID(); + } // getDoc_User_ID + + /** + * Get Document Approval Amount + * @return amount + */ + public BigDecimal getApprovalAmt() + { + return getGrandTotal(); + } // getApprovalAmt + +} // MOrder diff --git a/base/src/org/compiere/model/MPayment.java b/base/src/org/compiere/model/MPayment.java new file mode 100644 index 0000000000..d09cf93d21 --- /dev/null +++ b/base/src/org/compiere/model/MPayment.java @@ -0,0 +1,2400 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.*; +import java.math.*; +import java.rmi.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; + +import org.compiere.db.*; +import org.compiere.interfaces.*; +import org.compiere.process.*; +import org.compiere.util.*; + +/** + * Payment Model. + * - retrieve and create payments for invoice + *
    + *  Event chain
    + *  - Payment inserted
    + *      C_Payment_Trg fires
    + *          update DocumentNo with payment summary
    + *  - Payment posted (C_Payment_Post)
    + *      create allocation line
    + *          C_Allocation_Trg fires
    + *              Update C_BPartner Open Item Amount
    + *      update invoice (IsPaid)
    + *      link invoice-payment if batch
    + *
    + *  Lifeline:
    + *  -   Created by VPayment or directly
    + *  -   When changed in VPayment
    + *      - old payment is reversed
    + *      - new payment created
    + *
    + *  When Payment is posed, the Allocation is made
    + *  
    + * @author Jorg Janke + * @version $Id: MPayment.java,v 1.4 2006/10/02 05:18:39 jjanke Exp $ + */ +public final class MPayment extends X_C_Payment + implements DocAction, ProcessCall +{ + /** + * Get Payments Of BPartner + * @param ctx context + * @param C_BPartner_ID id + * @param trxName transaction + * @return array + */ + public static MPayment[] getOfBPartner (Properties ctx, int C_BPartner_ID, String trxName) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM C_Payment WHERE C_BPartner_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, trxName); + pstmt.setInt(1, C_BPartner_ID); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new MPayment(ctx,rs, trxName)); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + // + MPayment[] retValue = new MPayment[list.size()]; + list.toArray(retValue); + return retValue; + } // getOfBPartner + + + /************************************************************************** + * Default Constructor + * @param ctx context + * @param C_Payment_ID payment to load, (0 create new payment) + * @param trxName trx name + */ + public MPayment (Properties ctx, int C_Payment_ID, String trxName) + { + super (ctx, C_Payment_ID, trxName); + // New + if (C_Payment_ID == 0) + { + setDocAction(DOCACTION_Complete); + setDocStatus(DOCSTATUS_Drafted); + setTrxType(TRXTYPE_Sales); + // + setR_AvsAddr (R_AVSZIP_Unavailable); + setR_AvsZip (R_AVSZIP_Unavailable); + // + setIsReceipt (true); + setIsApproved (false); + setIsReconciled (false); + setIsAllocated(false); + setIsOnline (false); + setIsSelfService(false); + setIsDelayedCapture (false); + setIsPrepayment(false); + setProcessed(false); + setProcessing(false); + setPosted (false); + // + setPayAmt(Env.ZERO); + setDiscountAmt(Env.ZERO); + setTaxAmt(Env.ZERO); + setWriteOffAmt(Env.ZERO); + setIsOverUnderPayment (false); + setOverUnderAmt(Env.ZERO); + // + setDateTrx (new Timestamp(System.currentTimeMillis())); + setDateAcct (getDateTrx()); + setTenderType(TENDERTYPE_Check); + } + } // MPayment + + /** + * Load Constructor + * @param ctx context + * @param rs result set record + * @param trxName transaction + */ + public MPayment (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MPayment + + /** Temporary Payment Processors */ + private MPaymentProcessor[] m_mPaymentProcessors = null; + /** Temporary Payment Processor */ + private MPaymentProcessor m_mPaymentProcessor = null; + /** Logger */ + private static CLogger s_log = CLogger.getCLogger (MPayment.class); + /** Error Message */ + private String m_errorMessage = null; + + /** Reversal Indicator */ + public static String REVERSE_INDICATOR = "^"; + + /** + * Reset Payment to new status + */ + public void resetNew() + { + setC_Payment_ID(0); // forces new Record + set_ValueNoCheck ("DocumentNo", null); + setDocAction(DOCACTION_Prepare); + setDocStatus(DOCSTATUS_Drafted); + setProcessed(false); + setPosted (false); + setIsReconciled (false); + setIsAllocated(false); + setIsOnline(false); + setIsDelayedCapture (false); + // setC_BPartner_ID(0); + setC_Invoice_ID(0); + setC_Order_ID(0); + setC_Charge_ID(0); + setC_Project_ID(0); + setIsPrepayment(false); + } // resetNew + + /** + * Is Cashbook Transfer Trx + * @return true if Cash Trx + */ + public boolean isCashTrx() + { + return "X".equals(getTenderType()); + } // isCashTrx + + /************************************************************************** + * Set Credit Card. + * Need to set PatmentProcessor after Amount/Currency Set + * + * @param TrxType Transaction Type see TRX_ + * @param creditCardType CC type + * @param creditCardNumber CC number + * @param creditCardVV CC verification + * @param creditCardExpMM CC Exp MM + * @param creditCardExpYY CC Exp YY + * @return true if valid + */ + public boolean setCreditCard (String TrxType, String creditCardType, String creditCardNumber, + String creditCardVV, int creditCardExpMM, int creditCardExpYY) + { + setTenderType(TENDERTYPE_CreditCard); + setTrxType(TrxType); + // + setCreditCardType (creditCardType); + setCreditCardNumber (creditCardNumber); + setCreditCardVV (creditCardVV); + setCreditCardExpMM (creditCardExpMM); + setCreditCardExpYY (creditCardExpYY); + // + int check = MPaymentValidate.validateCreditCardNumber(creditCardNumber, creditCardType).length() + + MPaymentValidate.validateCreditCardExp(creditCardExpMM, creditCardExpYY).length(); + if (creditCardVV.length() > 0) + check += MPaymentValidate.validateCreditCardVV(creditCardVV, creditCardType).length(); + return check == 0; + } // setCreditCard + + /** + * Set Credit Card - Exp. + * Need to set PatmentProcessor after Amount/Currency Set + * + * @param TrxType Transaction Type see TRX_ + * @param creditCardType CC type + * @param creditCardNumber CC number + * @param creditCardVV CC verification + * @param creditCardExp CC Exp + * @return true if valid + */ + public boolean setCreditCard (String TrxType, String creditCardType, String creditCardNumber, + String creditCardVV, String creditCardExp) + { + return setCreditCard(TrxType, creditCardType, creditCardNumber, + creditCardVV, MPaymentValidate.getCreditCardExpMM(creditCardExp), + MPaymentValidate.getCreditCardExpYY(creditCardExp)); + } // setCreditCard + + /** + * Set ACH BankAccount Info + * + * @param C_BankAccount_ID bank account + * @param isReceipt true if receipt + * @return true if valid + */ + public boolean setBankACH (MPaySelectionCheck preparedPayment) + { + // Our Bank + setC_BankAccount_ID(preparedPayment.getParent().getC_BankAccount_ID()); + // Target Bank + int C_BP_BankAccount_ID = preparedPayment.getC_BP_BankAccount_ID(); + MBPBankAccount ba = new MBPBankAccount (preparedPayment.getCtx(), C_BP_BankAccount_ID, null); + setRoutingNo(ba.getRoutingNo()); + setAccountNo(ba.getAccountNo()); + setIsReceipt (X_C_Order.PAYMENTRULE_DirectDebit.equals // AR only + (preparedPayment.getPaymentRule())); + // + int check = MPaymentValidate.validateRoutingNo(getRoutingNo()).length() + + MPaymentValidate.validateAccountNo(getAccountNo()).length(); + return check == 0; + } // setBankACH + + /** + * Set ACH BankAccount Info + * + * @param C_BankAccount_ID bank account + * @param isReceipt true if receipt + * @param tenderType - Direct Debit or Direct Deposit + * @param routingNo routing + * @param accountNo account + * @return true if valid + */ + public boolean setBankACH (int C_BankAccount_ID, boolean isReceipt, String tenderType, + String routingNo, String accountNo) + { + setTenderType (tenderType); + setIsReceipt (isReceipt); + // + if (C_BankAccount_ID > 0 + && (routingNo == null || routingNo.length() == 0 || accountNo == null || accountNo.length() == 0)) + setBankAccountDetails(C_BankAccount_ID); + else + { + setC_BankAccount_ID(C_BankAccount_ID); + setRoutingNo (routingNo); + setAccountNo (accountNo); + } + setCheckNo (""); + // + int check = MPaymentValidate.validateRoutingNo(routingNo).length() + + MPaymentValidate.validateAccountNo(accountNo).length(); + return check == 0; + } // setBankACH + + /** + * Set Check BankAccount Info + * + * @param C_BankAccount_ID bank account + * @param isReceipt true if receipt + * @param checkNo chack no + * @return true if valid + */ + public boolean setBankCheck (int C_BankAccount_ID, boolean isReceipt, String checkNo) + { + return setBankCheck (C_BankAccount_ID, isReceipt, null, null, checkNo); + } // setBankCheck + + /** + * Set Check BankAccount Info + * + * @param C_BankAccount_ID bank account + * @param isReceipt true if receipt + * @param routingNo routing no + * @param accountNo account no + * @param checkNo chack no + * @return true if valid + */ + public boolean setBankCheck (int C_BankAccount_ID, boolean isReceipt, + String routingNo, String accountNo, String checkNo) + { + setTenderType (TENDERTYPE_Check); + setIsReceipt (isReceipt); + // + if (C_BankAccount_ID > 0 + && (routingNo == null || routingNo.length() == 0 + || accountNo == null || accountNo.length() == 0)) + setBankAccountDetails(C_BankAccount_ID); + else + { + setC_BankAccount_ID(C_BankAccount_ID); + setRoutingNo (routingNo); + setAccountNo (accountNo); + } + setCheckNo (checkNo); + // + int check = MPaymentValidate.validateRoutingNo(routingNo).length() + + MPaymentValidate.validateAccountNo(accountNo).length() + + MPaymentValidate.validateCheckNo(checkNo).length(); + return check == 0; // no error message + } // setBankCheck + + /** + * Set Bank Account Details. + * Look up Routing No & Bank Acct No + * @param C_BankAccount_ID bank account + */ + public void setBankAccountDetails (int C_BankAccount_ID) + { + if (C_BankAccount_ID == 0) + return; + setC_BankAccount_ID(C_BankAccount_ID); + // + String sql = "SELECT b.RoutingNo, ba.AccountNo " + + "FROM C_BankAccount ba" + + " INNER JOIN C_Bank b ON (ba.C_Bank_ID=b.C_Bank_ID) " + + "WHERE C_BankAccount_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, C_BankAccount_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + setRoutingNo (rs.getString(1)); + setAccountNo (rs.getString(2)); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + } // setBankAccountDetails + + /** + * Set Account Address + * + * @param name name + * @param street street + * @param city city + * @param state state + * @param zip zip + * @param country country + */ + public void setAccountAddress (String name, String street, + String city, String state, String zip, String country) + { + setA_Name (name); + setA_Street (street); + setA_City (city); + setA_State (state); + setA_Zip (zip); + setA_Country(country); + } // setAccountAddress + + + /************************************************************************** + * Process Payment + * @return true if approved + */ + public boolean processOnline() + { + log.info ("Amt=" + getPayAmt()); + // + setIsOnline(true); + setErrorMessage(null); + // prevent charging twice + if (isApproved()) + { + log.info("Already processed - " + getR_Result() + " - " + getR_RespMsg()); + setErrorMessage("Payment already Processed"); + return true; + } + + if (m_mPaymentProcessor == null) + setPaymentProcessor(); + if (m_mPaymentProcessor == null) + { + log.log(Level.WARNING, "No Payment Processor Model"); + setErrorMessage("No Payment Processor Model"); + return false; + } + + boolean approved = false; + /** Process Payment on Server */ + if (DB.isRemoteObjects()) + { + Server server = CConnection.get().getServer(); + try + { + if (server != null) + { // See ServerBean + String trxName = null; // unconditionally save + save(trxName); // server reads from disk + approved = server.paymentOnline (getCtx(), getC_Payment_ID(), + m_mPaymentProcessor.getC_PaymentProcessor_ID(), trxName); + if (CLogMgt.isLevelFinest()) + s_log.fine("server => " + approved); + load(trxName); // server saves to disk + setIsApproved(approved); + return approved; + } + log.log(Level.WARNING, "AppsServer not found"); + } + catch (RemoteException ex) + { + log.log(Level.SEVERE, "AppsServer error", ex); + } + } + /** **/ + + // Try locally + try + { + PaymentProcessor pp = PaymentProcessor.create(m_mPaymentProcessor, this); + if (pp == null) + setErrorMessage("No Payment Processor"); + else + { + approved = pp.processCC (); + if (approved) + setErrorMessage(null); + else + setErrorMessage("From " + getCreditCardName() + ": " + getR_RespMsg()); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "processOnline", e); + setErrorMessage("Payment Processor Error"); + } + setIsApproved(approved); + return approved; + } // processOnline + + /** + * Process Online Payment. + * implements ProcessCall after standard constructor + * Called when pressing the Process_Online button in C_Payment + * + * @param ctx Context + * @param pi Process Info + * @param trx transaction + * @return true if the next process should be performed + */ + public boolean startProcess (Properties ctx, ProcessInfo pi, Trx trx) + { + log.info("startProcess - " + pi.getRecord_ID()); + boolean retValue = false; + // + if (pi.getRecord_ID() != get_ID()) + { + log.log(Level.SEVERE, "startProcess - Not same Payment - " + pi.getRecord_ID()); + return false; + } + // Process it + retValue = processOnline(); + save(); + return retValue; // Payment processed + } // startProcess + + + /** + * Before Save + * @param newRecord new + * @return save + */ + protected boolean beforeSave (boolean newRecord) + { + // We have a charge + if (getC_Charge_ID() != 0) + { + if (newRecord || is_ValueChanged("C_Charge_ID")) + { + setC_Order_ID(0); + setC_Invoice_ID(0); + setWriteOffAmt(Env.ZERO); + setDiscountAmt(Env.ZERO); + setIsOverUnderPayment(false); + setOverUnderAmt(Env.ZERO); + setIsPrepayment(false); + } + } + // We need a BPartner + else if (getC_BPartner_ID() == 0 && !isCashTrx()) + { + if (getC_Invoice_ID() != 0) + ; + else if (getC_Order_ID() != 0) + ; + else + { + log.saveError("Error", Msg.parseTranslation(getCtx(), "@NotFound@: @C_BPartner_ID@")); + return false; + } + } + // Prepayment: No charge and order or project (not as acct dimension) + if (newRecord + || is_ValueChanged("C_Charge_ID") || is_ValueChanged("C_Invoice_ID") + || is_ValueChanged("C_Order_ID") || is_ValueChanged("C_Project_ID")) + setIsPrepayment (getC_Charge_ID() == 0 + && getC_BPartner_ID() != 0 + && (getC_Order_ID() != 0 + || (getC_Project_ID() != 0 && getC_Invoice_ID() == 0))); + if (isPrepayment()) + { + if (newRecord + || is_ValueChanged("C_Order_ID") || is_ValueChanged("C_Project_ID")) + { + setWriteOffAmt(Env.ZERO); + setDiscountAmt(Env.ZERO); + setIsOverUnderPayment(false); + setOverUnderAmt(Env.ZERO); + } + } + + // Document Type/Receipt + if (getC_DocType_ID() == 0) + setC_DocType_ID(); + else + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + setIsReceipt(dt.isSOTrx()); + } + setDocumentNo(); + // + if (getDateAcct() == null) + setDateAcct(getDateTrx()); + // + if (!isOverUnderPayment()) + setOverUnderAmt(Env.ZERO); + + // Organization + if ((newRecord || is_ValueChanged("C_BankAccount_ID")) + && getC_Charge_ID() == 0) // allow different org for charge + { + MBankAccount ba = MBankAccount.get(getCtx(), getC_BankAccount_ID()); + if (ba.getAD_Org_ID() != 0) + setAD_Org_ID(ba.getAD_Org_ID()); + } + + // [ adempiere-Bugs-1885417 ] Validate BP on Payment Prepare or BeforeSave + // there is bp and (invoice or order) + if (getC_BPartner_ID() != 0 && (getC_Invoice_ID() != 0 || getC_Order_ID() != 0)) { + if (getC_Invoice_ID() != 0) { + MInvoice inv = new MInvoice(getCtx(), getC_Invoice_ID(), get_TrxName()); + if (inv.getC_BPartner_ID() != getC_BPartner_ID()) { + log.saveError("Error", Msg.parseTranslation(getCtx(), "BP different from BP Invoice")); + return false; + } + } + if (getC_Order_ID() != 0) { + MOrder ord = new MOrder(getCtx(), getC_Order_ID(), get_TrxName()); + if (ord.getC_BPartner_ID() != getC_BPartner_ID()) { + log.saveError("Error", Msg.parseTranslation(getCtx(), "BP different from BP Order")); + return false; + } + } + } + + return true; + } // beforeSave + + /** + * Get Allocated Amt in Payment Currency + * @return amount or null + */ + public BigDecimal getAllocatedAmt () + { + BigDecimal retValue = null; + if (getC_Charge_ID() != 0) + return getPayAmt(); + // + String sql = "SELECT SUM(currencyConvert(al.Amount," + + "ah.C_Currency_ID, p.C_Currency_ID,ah.DateTrx,p.C_ConversionType_ID, al.AD_Client_ID,al.AD_Org_ID)) " + + "FROM C_AllocationLine al" + + " INNER JOIN C_AllocationHdr ah ON (al.C_AllocationHdr_ID=ah.C_AllocationHdr_ID) " + + " INNER JOIN C_Payment p ON (al.C_Payment_ID=p.C_Payment_ID) " + + "WHERE al.C_Payment_ID=?" + + " AND ah.IsActive='Y' AND al.IsActive='Y'"; + // + " AND al.C_Invoice_ID IS NOT NULL"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Payment_ID()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + retValue = rs.getBigDecimal(1); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "getAllocatedAmt", e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + // log.fine("getAllocatedAmt - " + retValue); + // ? ROUND(NVL(v_AllocatedAmt,0), 2); + return retValue; + } // getAllocatedAmt + + /** + * Test Allocation (and set allocated flag) + * @return true if updated + */ + public boolean testAllocation() + { + // Cash Trx always allocated + if (isCashTrx()) + { + if (!isAllocated()) + { + setIsAllocated(true); + return true; + } + return false; + } + // + BigDecimal alloc = getAllocatedAmt(); + if (alloc == null) + alloc = Env.ZERO; + BigDecimal total = getPayAmt(); + if (!isReceipt()) + total = total.negate(); + boolean test = total.compareTo(alloc) == 0; + boolean change = test != isAllocated(); + if (change) + setIsAllocated(test); + log.fine("Allocated=" + test + + " (" + alloc + "=" + total + ")"); + return change; + } // testAllocation + + /** + * Set Allocated Flag for payments + * @param ctx context + * @param C_BPartner_ID if 0 all + * @param trxName trx + */ + public static void setIsAllocated (Properties ctx, int C_BPartner_ID, String trxName) + { + int counter = 0; + String sql = "SELECT * FROM C_Payment " + + "WHERE IsAllocated='N' AND DocStatus IN ('CO','CL')"; + if (C_BPartner_ID > 1) + sql += " AND C_BPartner_ID=?"; + else + sql += " AND AD_Client_ID=" + Env.getAD_Client_ID(ctx); + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + if (C_BPartner_ID > 1) + pstmt.setInt (1, C_BPartner_ID); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + { + MPayment pay = new MPayment (ctx, rs, trxName); + if (pay.testAllocation()) + if (pay.save()) + counter++; + } + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + s_log.config("#" + counter); + } // setIsAllocated + + /************************************************************************** + * Set Error Message + * @param errorMessage error message + */ + public void setErrorMessage(String errorMessage) + { + m_errorMessage = errorMessage; + } // setErrorMessage + + /** + * Get Error Message + * @return error message + */ + public String getErrorMessage() + { + return m_errorMessage; + } // getErrorMessage + + + /** + * Set Bank Account for Payment. + * @param C_BankAccount_ID C_BankAccount_ID + */ + public void setC_BankAccount_ID (int C_BankAccount_ID) + { + if (C_BankAccount_ID == 0) + { + setPaymentProcessor(); + if (getC_BankAccount_ID() == 0) + throw new IllegalArgumentException("Can't find Bank Account"); + } + else + super.setC_BankAccount_ID(C_BankAccount_ID); + } // setC_BankAccount_ID + + /** + * Set BankAccount and PaymentProcessor + * @return true if found + */ + public boolean setPaymentProcessor () + { + return setPaymentProcessor (getTenderType(), getCreditCardType()); + } // setPaymentProcessor + + /** + * Set BankAccount and PaymentProcessor + * @param tender TenderType see TENDER_ + * @param CCType CC Type see CC_ + * @return true if found + */ + public boolean setPaymentProcessor (String tender, String CCType) + { + m_mPaymentProcessor = null; + // Get Processor List + if (m_mPaymentProcessors == null || m_mPaymentProcessors.length == 0) + m_mPaymentProcessors = MPaymentProcessor.find (getCtx(), tender, CCType, getAD_Client_ID(), + getC_Currency_ID(), getPayAmt(), get_TrxName()); + // Relax Amount + if (m_mPaymentProcessors == null || m_mPaymentProcessors.length == 0) + m_mPaymentProcessors = MPaymentProcessor.find (getCtx(), tender, CCType, getAD_Client_ID(), + getC_Currency_ID(), Env.ZERO, get_TrxName()); + if (m_mPaymentProcessors == null || m_mPaymentProcessors.length == 0) + return false; + + // Find the first right one + for (int i = 0; i < m_mPaymentProcessors.length; i++) + { + if (m_mPaymentProcessors[i].accepts (tender, CCType)) + { + m_mPaymentProcessor = m_mPaymentProcessors[i]; + } + } + if (m_mPaymentProcessor != null) + setC_BankAccount_ID (m_mPaymentProcessor.getC_BankAccount_ID()); + // + return m_mPaymentProcessor != null; + } // setPaymentProcessor + + + /** + * Get Accepted Credit Cards for PayAmt (default 0) + * @return credit cards + */ + public ValueNamePair[] getCreditCards () + { + return getCreditCards(getPayAmt()); + } // getCreditCards + + + /** + * Get Accepted Credit Cards for amount + * @param amt trx amount + * @return credit cards + */ + public ValueNamePair[] getCreditCards (BigDecimal amt) + { + try + { + if (m_mPaymentProcessors == null || m_mPaymentProcessors.length == 0) + m_mPaymentProcessors = MPaymentProcessor.find (getCtx (), null, null, + getAD_Client_ID (), getC_Currency_ID (), amt, get_TrxName()); + // + HashMap map = new HashMap(); // to eliminate duplicates + for (int i = 0; i < m_mPaymentProcessors.length; i++) + { + if (m_mPaymentProcessors[i].isAcceptAMEX ()) + map.put (CREDITCARDTYPE_Amex, getCreditCardPair (CREDITCARDTYPE_Amex)); + if (m_mPaymentProcessors[i].isAcceptDiners ()) + map.put (CREDITCARDTYPE_Diners, getCreditCardPair (CREDITCARDTYPE_Diners)); + if (m_mPaymentProcessors[i].isAcceptDiscover ()) + map.put (CREDITCARDTYPE_Discover, getCreditCardPair (CREDITCARDTYPE_Discover)); + if (m_mPaymentProcessors[i].isAcceptMC ()) + map.put (CREDITCARDTYPE_MasterCard, getCreditCardPair (CREDITCARDTYPE_MasterCard)); + if (m_mPaymentProcessors[i].isAcceptCorporate ()) + map.put (CREDITCARDTYPE_PurchaseCard, getCreditCardPair (CREDITCARDTYPE_PurchaseCard)); + if (m_mPaymentProcessors[i].isAcceptVisa ()) + map.put (CREDITCARDTYPE_Visa, getCreditCardPair (CREDITCARDTYPE_Visa)); + } // for all payment processors + // + ValueNamePair[] retValue = new ValueNamePair[map.size ()]; + map.values ().toArray (retValue); + log.fine("getCreditCards - #" + retValue.length + " - Processors=" + m_mPaymentProcessors.length); + return retValue; + } + catch (Exception ex) + { + ex.printStackTrace(); + return null; + } + } // getCreditCards + + /** + * Get Type and name pair + * @param CreditCardType credit card Type + * @return pair + */ + private ValueNamePair getCreditCardPair (String CreditCardType) + { + return new ValueNamePair (CreditCardType, getCreditCardName(CreditCardType)); + } // getCreditCardPair + + + /************************************************************************** + * Credit Card Number + * @param CreditCardNumber CreditCard Number + */ + public void setCreditCardNumber (String CreditCardNumber) + { + super.setCreditCardNumber (MPaymentValidate.checkNumeric(CreditCardNumber)); + } // setCreditCardNumber + + /** + * Verification Code + * @param newCreditCardVV CC verification + */ + public void setCreditCardVV(String newCreditCardVV) + { + super.setCreditCardVV (MPaymentValidate.checkNumeric(newCreditCardVV)); + } // setCreditCardVV + + /** + * Two Digit CreditCard MM + * @param CreditCardExpMM Exp month + */ + public void setCreditCardExpMM (int CreditCardExpMM) + { + if (CreditCardExpMM < 1 || CreditCardExpMM > 12) + ; + else + super.setCreditCardExpMM (CreditCardExpMM); + } // setCreditCardExpMM + + /** + * Two digit CreditCard YY (til 2020) + * @param newCreditCardExpYY 2 or 4 digit year + */ + public void setCreditCardExpYY (int newCreditCardExpYY) + { + int CreditCardExpYY = newCreditCardExpYY; + if (newCreditCardExpYY > 1999) + CreditCardExpYY = newCreditCardExpYY-2000; + super.setCreditCardExpYY(CreditCardExpYY); + } // setCreditCardExpYY + + /** + * CreditCard Exp MMYY + * @param mmyy Exp in form of mmyy + * @return true if valid + */ + public boolean setCreditCardExp (String mmyy) + { + if (MPaymentValidate.validateCreditCardExp(mmyy).length() != 0) + return false; + // + String exp = MPaymentValidate.checkNumeric(mmyy); + String mmStr = exp.substring(0,2); + String yyStr = exp.substring(2,4); + setCreditCardExpMM (Integer.parseInt(mmStr)); + setCreditCardExpYY (Integer.parseInt(yyStr)); + return true; + } // setCreditCardExp + + + /** + * CreditCard Exp MMYY + * @param delimiter / - or null + * @return Exp + */ + public String getCreditCardExp(String delimiter) + { + String mm = String.valueOf(getCreditCardExpMM()); + String yy = String.valueOf(getCreditCardExpYY()); + + StringBuffer retValue = new StringBuffer(); + if (mm.length() == 1) + retValue.append("0"); + retValue.append(mm); + // + if (delimiter != null) + retValue.append(delimiter); + // + if (yy.length() == 1) + retValue.append("0"); + retValue.append(yy); + // + return (retValue.toString()); + } // getCreditCardExp + + /** + * MICR + * @param MICR MICR + */ + public void setMicr (String MICR) + { + super.setMicr (MPaymentValidate.checkNumeric(MICR)); + } // setBankMICR + + /** + * Routing No + * @param RoutingNo Routing No + */ + public void setRoutingNo(String RoutingNo) + { + super.setRoutingNo (MPaymentValidate.checkNumeric(RoutingNo)); + } // setBankRoutingNo + + + /** + * Bank Account No + * @param AccountNo AccountNo + */ + public void setAccountNo (String AccountNo) + { + super.setAccountNo (MPaymentValidate.checkNumeric(AccountNo)); + } // setBankAccountNo + + + /** + * Check No + * @param CheckNo Check No + */ + public void setCheckNo(String CheckNo) + { + super.setCheckNo(MPaymentValidate.checkNumeric(CheckNo)); + } // setBankCheckNo + + + /** + * Set DocumentNo to Payment info. + * If there is a R_PnRef that is set automatically + */ + private void setDocumentNo() + { + // Cash Transfer + if ("X".equals(getTenderType())) + return; + // Current Document No + String documentNo = getDocumentNo(); + // Existing reversal + if (documentNo != null + && documentNo.indexOf(REVERSE_INDICATOR) >= 0) + return; + + // If external number exists - enforce it + if (getR_PnRef() != null && getR_PnRef().length() > 0) + { + if (!getR_PnRef().equals(documentNo)) + setDocumentNo(getR_PnRef()); + return; + } + + documentNo = ""; + // globalqss - read configuration to assign credit card or check number number for Payments + // Credit Card + if (TENDERTYPE_CreditCard.equals(getTenderType())) + { + if (MSysConfig.getBooleanValue("PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CREDIT_CARD", true, getAD_Client_ID())) { + documentNo = getCreditCardType() + + " " + Obscure.obscure(getCreditCardNumber()) + + " " + getCreditCardExpMM() + + "/" + getCreditCardExpYY(); + } + } + // Own Check No + else if (TENDERTYPE_Check.equals(getTenderType()) + && !isReceipt() + && getCheckNo() != null && getCheckNo().length() > 0) + { + if (MSysConfig.getBooleanValue("PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_PAYMENT", true, getAD_Client_ID())) { + documentNo = getCheckNo(); + } + } + // Customer Check: Routing: Account #Check + else if (TENDERTYPE_Check.equals(getTenderType()) + && isReceipt()) + { + if (MSysConfig.getBooleanValue("PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_RECEIPT", true, getAD_Client_ID())) { + if (getRoutingNo() != null) + documentNo = getRoutingNo() + ": "; + if (getAccountNo() != null) + documentNo += getAccountNo(); + if (getCheckNo() != null) + { + if (documentNo.length() > 0) + documentNo += " "; + documentNo += "#" + getCheckNo(); + } + } + } + + // Set Document No + documentNo = documentNo.trim(); + if (documentNo.length() > 0) + setDocumentNo(documentNo); + } // setDocumentNo + + /** + * Set Refernce No (and Document No) + * @param R_PnRef reference + */ + public void setR_PnRef (String R_PnRef) + { + super.setR_PnRef (R_PnRef); + if (R_PnRef != null) + setDocumentNo (R_PnRef); + } // setR_PnRef + + // --------------- + + /** + * Set Payment Amount + * @param PayAmt Pay Amt + */ + public void setPayAmt (BigDecimal PayAmt) + { + super.setPayAmt(PayAmt == null ? Env.ZERO : PayAmt); + } // setPayAmt + + /** + * Set Payment Amount + * + * @param C_Currency_ID currency + * @param payAmt amount + */ + public void setAmount (int C_Currency_ID, BigDecimal payAmt) + { + if (C_Currency_ID == 0) + C_Currency_ID = MClient.get(getCtx()).getC_Currency_ID(); + setC_Currency_ID(C_Currency_ID); + setPayAmt(payAmt); + } // setAmount + + /** + * Discount Amt + * @param DiscountAmt Discount + */ + public void setDiscountAmt (BigDecimal DiscountAmt) + { + super.setDiscountAmt (DiscountAmt == null ? Env.ZERO : DiscountAmt); + } // setDiscountAmt + + /** + * WriteOff Amt + * @param WriteOffAmt WriteOff + */ + public void setWriteOffAmt (BigDecimal WriteOffAmt) + { + super.setWriteOffAmt (WriteOffAmt == null ? Env.ZERO : WriteOffAmt); + } // setWriteOffAmt + + /** + * OverUnder Amt + * @param OverUnderAmt OverUnder + */ + public void setOverUnderAmt (BigDecimal OverUnderAmt) + { + super.setOverUnderAmt (OverUnderAmt == null ? Env.ZERO : OverUnderAmt); + setIsOverUnderPayment(getOverUnderAmt().compareTo(Env.ZERO) != 0); + } // setOverUnderAmt + + /** + * Tax Amt + * @param TaxAmt Tax + */ + public void setTaxAmt (BigDecimal TaxAmt) + { + super.setTaxAmt (TaxAmt == null ? Env.ZERO : TaxAmt); + } // setTaxAmt + + /** + * Set Info from BP Bank Account + * @param ba BP bank account + */ + public void setBP_BankAccount (MBPBankAccount ba) + { + log.fine("" + ba); + if (ba == null) + return; + setC_BPartner_ID(ba.getC_BPartner_ID()); + setAccountAddress(ba.getA_Name(), ba.getA_Street(), ba.getA_City(), + ba.getA_State(), ba.getA_Zip(), ba.getA_Country()); + setA_EMail(ba.getA_EMail()); + setA_Ident_DL(ba.getA_Ident_DL()); + setA_Ident_SSN(ba.getA_Ident_SSN()); + // CC + if (ba.getCreditCardType() != null) + setCreditCardType(ba.getCreditCardType()); + if (ba.getCreditCardNumber() != null) + setCreditCardNumber(ba.getCreditCardNumber()); + if (ba.getCreditCardExpMM() != 0) + setCreditCardExpMM(ba.getCreditCardExpMM()); + if (ba.getCreditCardExpYY() != 0) + setCreditCardExpYY(ba.getCreditCardExpYY()); + if (ba.getCreditCardVV() != null) + setCreditCardVV(ba.getCreditCardVV()); + // Bank + if (ba.getAccountNo() != null) + setAccountNo(ba.getAccountNo()); + if (ba.getRoutingNo() != null) + setRoutingNo(ba.getRoutingNo()); + } // setBP_BankAccount + + /** + * Save Info from BP Bank Account + * @param ba BP bank account + * @return true if saved + */ + public boolean saveToBP_BankAccount (MBPBankAccount ba) + { + if (ba == null) + return false; + ba.setA_Name(getA_Name()); + ba.setA_Street(getA_Street()); + ba.setA_City(getA_City()); + ba.setA_State(getA_State()); + ba.setA_Zip(getA_Zip()); + ba.setA_Country(getA_Country()); + ba.setA_EMail(getA_EMail()); + ba.setA_Ident_DL(getA_Ident_DL()); + ba.setA_Ident_SSN(getA_Ident_SSN()); + // CC + ba.setCreditCardType(getCreditCardType()); + ba.setCreditCardNumber(getCreditCardNumber()); + ba.setCreditCardExpMM(getCreditCardExpMM()); + ba.setCreditCardExpYY(getCreditCardExpYY()); + ba.setCreditCardVV(getCreditCardVV()); + // Bank + if (getAccountNo() != null) + ba.setAccountNo(getAccountNo()); + if (getRoutingNo() != null) + ba.setRoutingNo(getRoutingNo()); + // Trx + ba.setR_AvsAddr(getR_AvsAddr()); + ba.setR_AvsZip(getR_AvsZip()); + // + boolean ok = ba.save(get_TrxName()); + log.fine("saveToBP_BankAccount - " + ba); + return ok; + } // setBP_BankAccount + + /** + * Set Doc Type bases on IsReceipt + */ + private void setC_DocType_ID () + { + setC_DocType_ID(isReceipt()); + } // setC_DocType_ID + + /** + * Set Doc Type + * @param isReceipt is receipt + */ + public void setC_DocType_ID (boolean isReceipt) + { + setIsReceipt(isReceipt); + String sql = "SELECT C_DocType_ID FROM C_DocType WHERE AD_Client_ID=? AND DocBaseType=? ORDER BY IsDefault DESC"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getAD_Client_ID()); + if (isReceipt) + pstmt.setString(2, X_C_DocType.DOCBASETYPE_ARReceipt); + else + pstmt.setString(2, X_C_DocType.DOCBASETYPE_APPayment); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + setC_DocType_ID(rs.getInt(1)); + else + log.warning ("setDocType - NOT found - isReceipt=" + isReceipt); + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + } // setC_DocType_ID + + + /** + * Set Document Type + * @param C_DocType_ID doc type + */ + public void setC_DocType_ID (int C_DocType_ID) + { + // if (getDocumentNo() != null && getC_DocType_ID() != C_DocType_ID) + // setDocumentNo(null); + super.setC_DocType_ID(C_DocType_ID); + } // setC_DocType_ID + + /** + * Verify Document Type with Invoice + * @return true if ok + */ + private boolean verifyDocType() + { + if (getC_DocType_ID() == 0) + return false; + // + Boolean invoiceSO = null; + // Check Invoice First + if (getC_Invoice_ID() > 0) + { + String sql = "SELECT idt.IsSOTrx " + + "FROM C_Invoice i" + + " INNER JOIN C_DocType idt ON (i.C_DocType_ID=idt.C_DocType_ID) " + + "WHERE i.C_Invoice_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Invoice_ID()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + invoiceSO = new Boolean ("Y".equals(rs.getString(1))); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + } // Invoice + + // DocumentType + Boolean paymentSO = null; + PreparedStatement pstmt = null; + String sql = "SELECT IsSOTrx " + + "FROM C_DocType " + + "WHERE C_DocType_ID=?"; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_DocType_ID()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + paymentSO = new Boolean ("Y".equals(rs.getString(1))); + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + // No Payment info + if (paymentSO == null) + return false; + setIsReceipt(paymentSO.booleanValue()); + + // We have an Invoice .. and it does not match + if (invoiceSO != null + && invoiceSO.booleanValue() != paymentSO.booleanValue()) + return false; + // OK + return true; + } // verifyDocType + + + /** + * Get ISO Code of Currency + * @return Currency ISO + */ + public String getCurrencyISO() + { + return MCurrency.getISO_Code (getCtx(), getC_Currency_ID()); + } // getCurrencyISO + + /** + * Get Document Status + * @return Document Status Clear Text + */ + public String getDocStatusName() + { + return MRefList.getListName(getCtx(), 131, getDocStatus()); + } // getDocStatusName + + /** + * Get Name of Credit Card + * @return Name + */ + public String getCreditCardName() + { + return getCreditCardName(getCreditCardType()); + } // getCreditCardName + + /** + * Get Name of Credit Card + * @param CreditCardType credit card type + * @return Name + */ + public String getCreditCardName(String CreditCardType) + { + if (CreditCardType == null) + return "--"; + else if (CREDITCARDTYPE_MasterCard.equals(CreditCardType)) + return "MasterCard"; + else if (CREDITCARDTYPE_Visa.equals(CreditCardType)) + return "Visa"; + else if (CREDITCARDTYPE_Amex.equals(CreditCardType)) + return "Amex"; + else if (CREDITCARDTYPE_ATM.equals(CreditCardType)) + return "ATM"; + else if (CREDITCARDTYPE_Diners.equals(CreditCardType)) + return "Diners"; + else if (CREDITCARDTYPE_Discover.equals(CreditCardType)) + return "Discover"; + else if (CREDITCARDTYPE_PurchaseCard.equals(CreditCardType)) + return "PurchaseCard"; + return "?" + CreditCardType + "?"; + } // getCreditCardName + + /** + * Add to Description + * @param description text + */ + public void addDescription (String description) + { + String desc = getDescription(); + if (desc == null) + setDescription(description); + else + setDescription(desc + " | " + description); + } // addDescription + + + /** + * Get Pay Amt + * @param absolute if true the absolute amount (i.e. negative if payment) + * @return amount + */ + public BigDecimal getPayAmt (boolean absolute) + { + if (isReceipt()) + return super.getPayAmt(); + return super.getPayAmt().negate(); + } // getPayAmt + + /** + * Get Pay Amt in cents + * @return amount in cents + */ + public int getPayAmtInCents () + { + BigDecimal bd = super.getPayAmt().multiply(Env.ONEHUNDRED); + return bd.intValue(); + } // getPayAmtInCents + + /************************************************************************** + * Process document + * @param processAction document action + * @return true if performed + */ + public boolean processIt (String processAction) + { + m_processMsg = null; + DocumentEngine engine = new DocumentEngine (this, getDocStatus()); + return engine.processIt (processAction, getDocAction()); + } // process + + /** Process Message */ + private String m_processMsg = null; + /** Just Prepared Flag */ + private boolean m_justPrepared = false; + + /** + * Unlock Document. + * @return true if success + */ + public boolean unlockIt() + { + log.info(toString()); + setProcessing(false); + return true; + } // unlockIt + + /** + * Invalidate Document + * @return true if success + */ + public boolean invalidateIt() + { + log.info(toString()); + setDocAction(DOCACTION_Prepare); + return true; + } // invalidateIt + + + /************************************************************************** + * Prepare Document + * @return new status (In Progress or Invalid) + */ + public String prepareIt() + { + log.info(toString()); + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Std Period open? + if (!MPeriod.isOpen(getCtx(), getDateAcct(), + isReceipt() ? X_C_DocType.DOCBASETYPE_ARReceipt : X_C_DocType.DOCBASETYPE_APPayment)) + { + m_processMsg = "@PeriodClosed@"; + return DocAction.STATUS_Invalid; + } + + // Unsuccessful Online Payment + if (isOnline() && !isApproved()) + { + if (getR_Result() != null) + m_processMsg = "@OnlinePaymentFailed@"; + else + m_processMsg = "@PaymentNotProcessed@"; + return DocAction.STATUS_Invalid; + } + + // Waiting Payment - Need to create Invoice & Shipment + if (getC_Order_ID() != 0 && getC_Invoice_ID() == 0) + { // see WebOrder.process + MOrder order = new MOrder (getCtx(), getC_Order_ID(), get_TrxName()); + if (DOCSTATUS_WaitingPayment.equals(order.getDocStatus())) + { + order.setC_Payment_ID(getC_Payment_ID()); + order.setDocAction(X_C_Order.DOCACTION_WaitComplete); + order.set_TrxName(get_TrxName()); + // boolean ok = + order.processIt (X_C_Order.DOCACTION_WaitComplete); + m_processMsg = order.getProcessMsg(); + order.save(get_TrxName()); + // Set Invoice + MInvoice[] invoices = order.getInvoices(); + int length = invoices.length; + if (length > 0) // get last invoice + setC_Invoice_ID (invoices[length-1].getC_Invoice_ID()); + // + if (getC_Invoice_ID() == 0) + { + m_processMsg = "@NotFound@ @C_Invoice_ID@"; + return DocAction.STATUS_Invalid; + } + } // WaitingPayment + } + + // Consistency of Invoice / Document Type and IsReceipt + if (!verifyDocType()) + { + m_processMsg = "@PaymentDocTypeInvoiceInconsistent@"; + return DocAction.STATUS_Invalid; + } + + // Do not pay when Credit Stop/Hold + if (!isReceipt()) + { + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + if (X_C_BPartner.SOCREDITSTATUS_CreditStop.equals(bp.getSOCreditStatus())) + { + m_processMsg = "@BPartnerCreditStop@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + if (X_C_BPartner.SOCREDITSTATUS_CreditHold.equals(bp.getSOCreditStatus())) + { + m_processMsg = "@BPartnerCreditHold@ - @TotalOpenBalance@=" + + bp.getTotalOpenBalance() + + ", @SO_CreditLimit@=" + bp.getSO_CreditLimit(); + return DocAction.STATUS_Invalid; + } + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_PREPARE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + m_justPrepared = true; + if (!DOCACTION_Complete.equals(getDocAction())) + setDocAction(DOCACTION_Complete); + return DocAction.STATUS_InProgress; + } // prepareIt + + /** + * Approve Document + * @return true if success + */ + public boolean approveIt() + { + log.info(toString()); + setIsApproved(true); + return true; + } // approveIt + + /** + * Reject Approval + * @return true if success + */ + public boolean rejectIt() + { + log.info(toString()); + setIsApproved(false); + return true; + } // rejectIt + + + /************************************************************************** + * Complete Document + * @return new status (Complete, In Progress, Invalid, Waiting ..) + */ + public String completeIt() + { + // Re-Check + if (!m_justPrepared) + { + String status = prepareIt(); + if (!DocAction.STATUS_InProgress.equals(status)) + return status; + } + + m_processMsg = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_BEFORE_COMPLETE); + if (m_processMsg != null) + return DocAction.STATUS_Invalid; + + // Implicit Approval + if (!isApproved()) + approveIt(); + log.info(toString()); + + // Charge Handling + if (getC_Charge_ID() != 0) + { + setIsAllocated(true); + } + else + { + allocateIt(); // Create Allocation Records + testAllocation(); + } + + // Project update + if (getC_Project_ID() != 0) + { + // MProject project = new MProject(getCtx(), getC_Project_ID()); + } + // Update BP for Prepayments + if (getC_BPartner_ID() != 0 && getC_Invoice_ID() == 0) + { + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + bp.setTotalOpenBalance(); + bp.save(); + } + + // Counter Doc + MPayment counter = createCounterDoc(); + if (counter != null) + m_processMsg += " @CounterDoc@: @C_Payment_ID@=" + counter.getDocumentNo(); + + // User Validation + String valid = ModelValidationEngine.get().fireDocValidate(this, ModelValidator.TIMING_AFTER_COMPLETE); + if (valid != null) + { + m_processMsg = valid; + return DocAction.STATUS_Invalid; + } + + // Set the definite document number after completed (if needed) + setDefiniteDocumentNo(); + + // + setProcessed(true); + setDocAction(DOCACTION_Close); + return DocAction.STATUS_Completed; + } // completeIt + + /** + * Set the definite document number after completed + */ + private void setDefiniteDocumentNo() { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + if (dt.isOverwriteDateOnComplete()) { + setDateTrx(new Timestamp (System.currentTimeMillis())); + } + if (dt.isOverwriteSeqOnComplete()) { + String value = DB.getDocumentNo(getC_DocType_ID(), get_TrxName(), true, this); + if (value != null) + setDocumentNo(value); + } + } + + /** + * Create Counter Document + * @return payment + */ + private MPayment createCounterDoc() + { + // Is this a counter doc ? + if (getRef_Payment_ID() != 0) + return null; + + // Org Must be linked to BPartner + MOrg org = MOrg.get(getCtx(), getAD_Org_ID()); + int counterC_BPartner_ID = org.getLinkedC_BPartner_ID(get_TrxName()); + if (counterC_BPartner_ID == 0) + return null; + // Business Partner needs to be linked to Org + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + int counterAD_Org_ID = bp.getAD_OrgBP_ID_Int(); + if (counterAD_Org_ID == 0) + return null; + + MBPartner counterBP = new MBPartner (getCtx(), counterC_BPartner_ID, get_TrxName()); + // MOrgInfo counterOrgInfo = MOrgInfo.get(getCtx(), counterAD_Org_ID); + log.info("Counter BP=" + counterBP.getName()); + + // Document Type + int C_DocTypeTarget_ID = 0; + MDocTypeCounter counterDT = MDocTypeCounter.getCounterDocType(getCtx(), getC_DocType_ID()); + if (counterDT != null) + { + log.fine(counterDT.toString()); + if (!counterDT.isCreateCounter() || !counterDT.isValid()) + return null; + C_DocTypeTarget_ID = counterDT.getCounter_C_DocType_ID(); + } + else // indirect + { + C_DocTypeTarget_ID = MDocTypeCounter.getCounterDocType_ID(getCtx(), getC_DocType_ID()); + log.fine("Indirect C_DocTypeTarget_ID=" + C_DocTypeTarget_ID); + if (C_DocTypeTarget_ID <= 0) + return null; + } + + // Deep Copy + MPayment counter = new MPayment (getCtx(), 0, get_TrxName()); + counter.setAD_Org_ID(counterAD_Org_ID); + counter.setC_BPartner_ID(counterBP.getC_BPartner_ID()); + counter.setIsReceipt(!isReceipt()); + counter.setC_DocType_ID(C_DocTypeTarget_ID); + counter.setTrxType(getTrxType()); + counter.setTenderType(getTenderType()); + // + counter.setPayAmt(getPayAmt()); + counter.setDiscountAmt(getDiscountAmt()); + counter.setTaxAmt(getTaxAmt()); + counter.setWriteOffAmt(getWriteOffAmt()); + counter.setIsOverUnderPayment (isOverUnderPayment()); + counter.setOverUnderAmt(getOverUnderAmt()); + counter.setC_Currency_ID(getC_Currency_ID()); + counter.setC_ConversionType_ID(getC_ConversionType_ID()); + // + counter.setDateTrx (getDateTrx()); + counter.setDateAcct (getDateAcct()); + counter.setRef_Payment_ID(getC_Payment_ID()); + // + String sql = "SELECT C_BankAccount_ID FROM C_BankAccount " + + "WHERE C_Currency_ID=? AND AD_Org_ID IN (0,?) AND IsActive='Y' " + + "ORDER BY IsDefault DESC"; + int C_BankAccount_ID = DB.getSQLValue(get_TrxName(), sql, getC_Currency_ID(), counterAD_Org_ID); + counter.setC_BankAccount_ID(C_BankAccount_ID); + + // Refernces + counter.setC_Activity_ID(getC_Activity_ID()); + counter.setC_Campaign_ID(getC_Campaign_ID()); + counter.setC_Project_ID(getC_Project_ID()); + counter.setUser1_ID(getUser1_ID()); + counter.setUser2_ID(getUser2_ID()); + counter.save(get_TrxName()); + log.fine(counter.toString()); + setRef_Payment_ID(counter.getC_Payment_ID()); + + // Document Action + if (counterDT != null) + { + if (counterDT.getDocAction() != null) + { + counter.setDocAction(counterDT.getDocAction()); + counter.processIt(counterDT.getDocAction()); + counter.save(get_TrxName()); + } + } + return counter; + } // createCounterDoc + + /** + * Allocate It. + * Only call when there is NO allocation as it will create duplicates. + * If an invoice exists, it allocates that + * otherwise it allocates Payment Selection. + * @return true if allocated + */ + public boolean allocateIt() + { + // Create invoice Allocation - See also MCash.completeIt + if (getC_Invoice_ID() != 0) + return allocateInvoice(); + // Invoices of a AP Payment Selection + if (allocatePaySelection()) + return true; + + if (getC_Order_ID() != 0) + return false; + + // Allocate to multiple Payments based on entry + MPaymentAllocate[] pAllocs = MPaymentAllocate.get(this); + if (pAllocs.length == 0) + return false; + + MAllocationHdr alloc = new MAllocationHdr(getCtx(), false, + getDateTrx(), getC_Currency_ID(), + Msg.translate(getCtx(), "C_Payment_ID") + ": " + getDocumentNo(), + get_TrxName()); + alloc.setAD_Org_ID(getAD_Org_ID()); + if (!alloc.save()) + { + log.severe("P.Allocations not created"); + return false; + } + // Lines + for (int i = 0; i < pAllocs.length; i++) + { + MPaymentAllocate pa = pAllocs[i]; + MAllocationLine aLine = null; + if (isReceipt()) + aLine = new MAllocationLine (alloc, pa.getAmount(), + pa.getDiscountAmt(), pa.getWriteOffAmt(), pa.getOverUnderAmt()); + else + aLine = new MAllocationLine (alloc, pa.getAmount().negate(), + pa.getDiscountAmt().negate(), pa.getWriteOffAmt().negate(), pa.getOverUnderAmt().negate()); + aLine.setDocInfo(pa.getC_BPartner_ID(), 0, pa.getC_Invoice_ID()); + aLine.setPaymentInfo(getC_Payment_ID(), 0); + if (!aLine.save(get_TrxName())) + log.warning("P.Allocations - line not saved"); + else + { + pa.setC_AllocationLine_ID(aLine.getC_AllocationLine_ID()); + pa.save(); + } + } + // Should start WF + alloc.processIt(DocAction.ACTION_Complete); + m_processMsg = "@C_AllocationHdr_ID@: " + alloc.getDocumentNo(); + return alloc.save(get_TrxName()); + } // allocateIt + + /** + * Allocate single AP/AR Invoice + * @return true if allocated + */ + private boolean allocateInvoice() + { + // calculate actual allocation + BigDecimal allocationAmt = getPayAmt(); // underpayment + if (getOverUnderAmt().signum() < 0 && getPayAmt().signum() > 0) + allocationAmt = allocationAmt.add(getOverUnderAmt()); // overpayment (negative) + + MAllocationHdr alloc = new MAllocationHdr(getCtx(), false, + getDateTrx(), getC_Currency_ID(), + Msg.translate(getCtx(), "C_Payment_ID") + ": " + getDocumentNo() + " [1]", get_TrxName()); + alloc.setAD_Org_ID(getAD_Org_ID()); + if (!alloc.save()) + { + log.log(Level.SEVERE, "Could not create Allocation Hdr"); + return false; + } + MAllocationLine aLine = null; + if (isReceipt()) + aLine = new MAllocationLine (alloc, allocationAmt, + getDiscountAmt(), getWriteOffAmt(), getOverUnderAmt()); + else + aLine = new MAllocationLine (alloc, allocationAmt.negate(), + getDiscountAmt().negate(), getWriteOffAmt().negate(), getOverUnderAmt().negate()); + aLine.setDocInfo(getC_BPartner_ID(), 0, getC_Invoice_ID()); + aLine.setC_Payment_ID(getC_Payment_ID()); + if (!aLine.save(get_TrxName())) + { + log.log(Level.SEVERE, "Could not create Allocation Line"); + return false; + } + // Should start WF + alloc.processIt(DocAction.ACTION_Complete); + alloc.save(get_TrxName()); + m_processMsg = "@C_AllocationHdr_ID@: " + alloc.getDocumentNo(); + + // Get Project from Invoice + int C_Project_ID = DB.getSQLValue(get_TrxName(), + "SELECT MAX(C_Project_ID) FROM C_Invoice WHERE C_Invoice_ID=?", getC_Invoice_ID()); + if (C_Project_ID > 0 && getC_Project_ID() == 0) + setC_Project_ID(C_Project_ID); + else if (C_Project_ID > 0 && getC_Project_ID() > 0 && C_Project_ID != getC_Project_ID()) + log.warning("Invoice C_Project_ID=" + C_Project_ID + + " <> Payment C_Project_ID=" + getC_Project_ID()); + return true; + } // allocateInvoice + + /** + * Allocate Payment Selection + * @return true if allocated + */ + private boolean allocatePaySelection() + { + MAllocationHdr alloc = new MAllocationHdr(getCtx(), false, + getDateTrx(), getC_Currency_ID(), + Msg.translate(getCtx(), "C_Payment_ID") + ": " + getDocumentNo() + " [n]", get_TrxName()); + alloc.setAD_Org_ID(getAD_Org_ID()); + + String sql = "SELECT psc.C_BPartner_ID, psl.C_Invoice_ID, psl.IsSOTrx, " // 1..3 + + " psl.PayAmt, psl.DiscountAmt, psl.DifferenceAmt, psl.OpenAmt " + + "FROM C_PaySelectionLine psl" + + " INNER JOIN C_PaySelectionCheck psc ON (psl.C_PaySelectionCheck_ID=psc.C_PaySelectionCheck_ID) " + + "WHERE psc.C_Payment_ID=?"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql, get_TrxName()); + pstmt.setInt(1, getC_Payment_ID()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + int C_BPartner_ID = rs.getInt(1); + int C_Invoice_ID = rs.getInt(2); + if (C_BPartner_ID == 0 && C_Invoice_ID == 0) + continue; + boolean isSOTrx = "Y".equals(rs.getString(3)); + BigDecimal PayAmt = rs.getBigDecimal(4); + BigDecimal DiscountAmt = rs.getBigDecimal(5); + BigDecimal WriteOffAmt = rs.getBigDecimal(6); + BigDecimal OpenAmt = rs.getBigDecimal(7); + BigDecimal OverUnderAmt = OpenAmt.subtract(PayAmt) + .subtract(DiscountAmt).subtract(WriteOffAmt); + // + if (alloc.get_ID() == 0 && !alloc.save(get_TrxName())) + { + log.log(Level.SEVERE, "Could not create Allocation Hdr"); + rs.close(); + pstmt.close(); + return false; + } + MAllocationLine aLine = null; + if (isSOTrx) + aLine = new MAllocationLine (alloc, PayAmt, + DiscountAmt, WriteOffAmt, OverUnderAmt); + else + aLine = new MAllocationLine (alloc, PayAmt.negate(), + DiscountAmt.negate(), WriteOffAmt.negate(), OverUnderAmt.negate()); + aLine.setDocInfo(C_BPartner_ID, 0, C_Invoice_ID); + aLine.setC_Payment_ID(getC_Payment_ID()); + if (!aLine.save(get_TrxName())) + log.log(Level.SEVERE, "Could not create Allocation Line"); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "allocatePaySelection", e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + + // Should start WF + boolean ok = true; + if (alloc.get_ID() == 0) + { + log.fine("No Allocation created - C_Payment_ID=" + + getC_Payment_ID()); + ok = false; + } + else + { + alloc.processIt(DocAction.ACTION_Complete); + ok = alloc.save(get_TrxName()); + m_processMsg = "@C_AllocationHdr_ID@: " + alloc.getDocumentNo(); + } + return ok; + } // allocatePaySelection + + /** + * De-allocate Payment. + * Unkink Invoices and Orders and delete Allocations + */ + private void deAllocate() + { + if (getC_Order_ID() != 0) + setC_Order_ID(0); + // if (getC_Invoice_ID() == 0) + // return; + // De-Allocate all + MAllocationHdr[] allocations = MAllocationHdr.getOfPayment(getCtx(), + getC_Payment_ID(), get_TrxName()); + log.fine("#" + allocations.length); + for (int i = 0; i < allocations.length; i++) + { + allocations[i].set_TrxName(get_TrxName()); + allocations[i].setDocAction(DocAction.ACTION_Reverse_Correct); + allocations[i].processIt(DocAction.ACTION_Reverse_Correct); + allocations[i].save(); + } + + // Unlink (in case allocation did not get it) + if (getC_Invoice_ID() != 0) + { + // Invoice + String sql = "UPDATE C_Invoice " + + "SET C_Payment_ID = NULL, IsPaid='N' " + + "WHERE C_Invoice_ID=" + getC_Invoice_ID() + + " AND C_Payment_ID=" + getC_Payment_ID(); + int no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Unlink Invoice #" + no); + // Order + sql = "UPDATE C_Order o " + + "SET C_Payment_ID = NULL " + + "WHERE EXISTS (SELECT * FROM C_Invoice i " + + "WHERE o.C_Order_ID=i.C_Order_ID AND i.C_Invoice_ID=" + getC_Invoice_ID() + ")" + + " AND C_Payment_ID=" + getC_Payment_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Unlink Order #" + no); + } + // + setC_Invoice_ID(0); + setIsAllocated(false); + } // deallocate + + /** + * Void Document. + * @return true if success + */ + public boolean voidIt() + { + log.info(toString()); + // Before Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_VOID); + if (m_processMsg != null) + return false; + + if (DOCSTATUS_Closed.equals(getDocStatus()) + || DOCSTATUS_Reversed.equals(getDocStatus()) + || DOCSTATUS_Voided.equals(getDocStatus())) + { + m_processMsg = "Document Closed: " + getDocStatus(); + setDocAction(DOCACTION_None); + return false; + } + // If on Bank Statement, don't void it - reverse it + if (getC_BankStatementLine_ID() > 0) + return reverseCorrectIt(); + + // Not Processed + if (DOCSTATUS_Drafted.equals(getDocStatus()) + || DOCSTATUS_Invalid.equals(getDocStatus()) + || DOCSTATUS_InProgress.equals(getDocStatus()) + || DOCSTATUS_Approved.equals(getDocStatus()) + || DOCSTATUS_NotApproved.equals(getDocStatus()) ) + { + addDescription(Msg.getMsg(getCtx(), "Voided") + " (" + getPayAmt() + ")"); + setPayAmt(Env.ZERO); + setDiscountAmt(Env.ZERO); + setWriteOffAmt(Env.ZERO); + setOverUnderAmt(Env.ZERO); + setIsAllocated(false); + // Unlink & De-Allocate + deAllocate(); + } + else + return reverseCorrectIt(); + + // + // After Void + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_VOID); + if (m_processMsg != null) + return false; + + setProcessed(true); + setDocAction(DOCACTION_None); + return true; + } // voidIt + + /** + * Close Document. + * @return true if success + */ + public boolean closeIt() + { + log.info(toString()); + // Before Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_CLOSE); + if (m_processMsg != null) + return false; + // After Close + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_CLOSE); + if (m_processMsg != null) + return false; + setDocAction(DOCACTION_None); + return true; + } // closeIt + + /** + * Reverse Correction + * @return true if success + */ + public boolean reverseCorrectIt() + { + log.info(toString()); + // Before reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSECORRECT); + if (m_processMsg != null) + return false; + + // Std Period open? + Timestamp dateAcct = getDateAcct(); + if (!MPeriod.isOpen(getCtx(), dateAcct, + isReceipt() ? X_C_DocType.DOCBASETYPE_ARReceipt : X_C_DocType.DOCBASETYPE_APPayment)) + dateAcct = new Timestamp(System.currentTimeMillis()); + + // Auto Reconcile if not on Bank Statement + boolean reconciled = getC_BankStatementLine_ID() == 0; //AZ Goodwill + + // Create Reversal + MPayment reversal = new MPayment (getCtx(), 0, get_TrxName()); + copyValues(this, reversal); + reversal.setClientOrg(this); + reversal.setC_Order_ID(0); + reversal.setC_Invoice_ID(0); + reversal.setDateAcct(dateAcct); + // + reversal.setDocumentNo(getDocumentNo() + REVERSE_INDICATOR); // indicate reversals + reversal.setDocStatus(DOCSTATUS_Drafted); + reversal.setDocAction(DOCACTION_Complete); + // + reversal.setPayAmt(getPayAmt().negate()); + reversal.setDiscountAmt(getDiscountAmt().negate()); + reversal.setWriteOffAmt(getWriteOffAmt().negate()); + reversal.setOverUnderAmt(getOverUnderAmt().negate()); + // + reversal.setIsAllocated(true); + reversal.setIsReconciled(reconciled); // to put on bank statement + reversal.setIsOnline(false); + reversal.setIsApproved(true); + reversal.setR_PnRef(null); + reversal.setR_Result(null); + reversal.setR_RespMsg(null); + reversal.setR_AuthCode(null); + reversal.setR_Info(null); + reversal.setProcessing(false); + reversal.setOProcessing("N"); + reversal.setProcessed(false); + reversal.setPosted(false); + reversal.setDescription(getDescription()); + reversal.addDescription("{->" + getDocumentNo() + ")"); + reversal.save(get_TrxName()); + // Post Reversal + if (!reversal.processIt(DocAction.ACTION_Complete)) + { + m_processMsg = "Reversal ERROR: " + reversal.getProcessMsg(); + return false; + } + reversal.closeIt(); + reversal.setDocStatus(DOCSTATUS_Reversed); + reversal.setDocAction(DOCACTION_None); + reversal.save(get_TrxName()); + + // Unlink & De-Allocate + deAllocate(); + setIsReconciled (reconciled); + setIsAllocated (true); // the allocation below is overwritten + // Set Status + addDescription("(" + reversal.getDocumentNo() + "<-)"); + setDocStatus(DOCSTATUS_Reversed); + setDocAction(DOCACTION_None); + setProcessed(true); + + // Create automatic Allocation + MAllocationHdr alloc = new MAllocationHdr (getCtx(), false, + getDateTrx(), getC_Currency_ID(), + Msg.translate(getCtx(), "C_Payment_ID") + ": " + reversal.getDocumentNo(), get_TrxName()); + alloc.setAD_Org_ID(getAD_Org_ID()); + if (!alloc.save()) + log.warning("Automatic allocation - hdr not saved"); + else + { + // Original Allocation + MAllocationLine aLine = new MAllocationLine (alloc, getPayAmt(true), + Env.ZERO, Env.ZERO, Env.ZERO); + aLine.setDocInfo(getC_BPartner_ID(), 0, 0); + aLine.setPaymentInfo(getC_Payment_ID(), 0); + if (!aLine.save(get_TrxName())) + log.warning("Automatic allocation - line not saved"); + // Reversal Allocation + aLine = new MAllocationLine (alloc, reversal.getPayAmt(true), + Env.ZERO, Env.ZERO, Env.ZERO); + aLine.setDocInfo(reversal.getC_BPartner_ID(), 0, 0); + aLine.setPaymentInfo(reversal.getC_Payment_ID(), 0); + if (!aLine.save(get_TrxName())) + log.warning("Automatic allocation - reversal line not saved"); + } + alloc.processIt(DocAction.ACTION_Complete); + alloc.save(get_TrxName()); + // + StringBuffer info = new StringBuffer (reversal.getDocumentNo()); + info.append(" - @C_AllocationHdr_ID@: ").append(alloc.getDocumentNo()); + + // Update BPartner + if (getC_BPartner_ID() != 0) + { + MBPartner bp = new MBPartner (getCtx(), getC_BPartner_ID(), get_TrxName()); + bp.setTotalOpenBalance(); + bp.save(get_TrxName()); + } + // After reverseCorrect + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSECORRECT); + if (m_processMsg != null) + return false; + + m_processMsg = info.toString(); + return true; + } // reverseCorrectionIt + + /** + * Get Bank Statement Line of payment or 0 + * @return id or 0 + */ + private int getC_BankStatementLine_ID() + { + String sql = "SELECT C_BankStatementLine_ID FROM C_BankStatementLine WHERE C_Payment_ID=?"; + int id = DB.getSQLValue(get_TrxName(), sql, getC_Payment_ID()); + if (id < 0) + return 0; + return id; + } // getC_BankStatementLine_ID + + /** + * Reverse Accrual - none + * @return true if success + */ + public boolean reverseAccrualIt() + { + log.info(toString()); + + // Before reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + // After reverseAccrual + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REVERSEACCRUAL); + if (m_processMsg != null) + return false; + + return false; + } // reverseAccrualIt + + /** + * Re-activate + * @return true if success + */ + public boolean reActivateIt() + { + log.info(toString()); + // Before reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_BEFORE_REACTIVATE); + if (m_processMsg != null) + return false; + + if (! reverseCorrectIt()) + return false; + + // After reActivate + m_processMsg = ModelValidationEngine.get().fireDocValidate(this,ModelValidator.TIMING_AFTER_REACTIVATE); + if (m_processMsg != null) + return false; + + return true; + } // reActivateIt + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("MPayment["); + sb.append(get_ID()).append("-").append(getDocumentNo()) + .append(",Receipt=").append(isReceipt()) + .append(",PayAmt=").append(getPayAmt()) + .append(",Discount=").append(getDiscountAmt()) + .append(",WriteOff=").append(getWriteOffAmt()) + .append(",OverUnder=").append(getOverUnderAmt()); + return sb.toString (); + } // toString + + /** + * Get Document Info + * @return document info (untranslated) + */ + public String getDocumentInfo() + { + MDocType dt = MDocType.get(getCtx(), getC_DocType_ID()); + return dt.getName() + " " + getDocumentNo(); + } // getDocumentInfo + + /** + * Create PDF + * @return File or null + */ + public File createPDF () + { + try + { + File temp = File.createTempFile(get_TableName()+get_ID()+"_", ".pdf"); + return createPDF (temp); + } + catch (Exception e) + { + log.severe("Could not create PDF - " + e.getMessage()); + } + return null; + } // getPDF + + /** + * Create PDF file + * @param file output file + * @return file if success + */ + public File createPDF (File file) + { + // ReportEngine re = ReportEngine.get (getCtx(), ReportEngine.PAYMENT, getC_Payment_ID()); + // if (re == null) + return null; + // return re.getPDF(file); + } // createPDF + + + /************************************************************************* + * Get Summary + * @return Summary of Document + */ + public String getSummary() + { + StringBuffer sb = new StringBuffer(); + sb.append(getDocumentNo()); + // : Total Lines = 123.00 (#1) + sb.append(": ") + .append(Msg.translate(getCtx(),"PayAmt")).append("=").append(getPayAmt()) + .append(",").append(Msg.translate(getCtx(),"WriteOffAmt")).append("=").append(getWriteOffAmt()); + // - Description + if (getDescription() != null && getDescription().length() > 0) + sb.append(" - ").append(getDescription()); + return sb.toString(); + } // getSummary + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg() + { + return m_processMsg; + } // getProcessMsg + + /** + * Get Document Owner (Responsible) + * @return AD_User_ID + */ + public int getDoc_User_ID() + { + return getCreatedBy(); + } // getDoc_User_ID + + /** + * Get Document Approval Amount + * @return amount payment(AP) or write-off(AR) + */ + public BigDecimal getApprovalAmt() + { + if (isReceipt()) + return getWriteOffAmt(); + return getPayAmt(); + } // getApprovalAmt + +} // MPayment diff --git a/base/src/org/compiere/model/MStorage.java b/base/src/org/compiere/model/MStorage.java new file mode 100644 index 0000000000..ebcfbfc860 --- /dev/null +++ b/base/src/org/compiere/model/MStorage.java @@ -0,0 +1,651 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.math.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Inventory Storage Model + * + * @author Jorg Janke + * @version $Id: MStorage.java,v 1.3 2006/07/30 00:51:05 jjanke Exp $ + */ +public class MStorage extends X_M_Storage +{ + /** + * Get Storage Info + * @param ctx context + * @param M_Locator_ID locator + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID instance + * @param trxName transaction + * @return existing or null + */ + public static MStorage get (Properties ctx, int M_Locator_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, String trxName) + { + MStorage retValue = null; + String sql = "SELECT * FROM M_Storage " + + "WHERE M_Locator_ID=? AND M_Product_ID=? AND "; + if (M_AttributeSetInstance_ID == 0) + sql += "(M_AttributeSetInstance_ID=? OR M_AttributeSetInstance_ID IS NULL)"; + else + sql += "M_AttributeSetInstance_ID=?"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + pstmt.setInt (1, M_Locator_ID); + pstmt.setInt (2, M_Product_ID); + pstmt.setInt (3, M_AttributeSetInstance_ID); + rs = pstmt.executeQuery (); + if (rs.next ()) + retValue = new MStorage (ctx, rs, trxName); + } + catch (SQLException ex) + { + s_log.log(Level.SEVERE, sql, ex); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + pstmt = null; + if (retValue == null) + s_log.fine("Not Found - M_Locator_ID=" + M_Locator_ID + + ", M_Product_ID=" + M_Product_ID + ", M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID); + else + s_log.fine("M_Locator_ID=" + M_Locator_ID + + ", M_Product_ID=" + M_Product_ID + ", M_AttributeSetInstance_ID=" + M_AttributeSetInstance_ID); + return retValue; + } // get + + /** + * Get all Storages for Product with ASI and QtyOnHand > 0 + * @param ctx context + * @param M_Product_ID product + * @param M_Locator_ID locator + * @param FiFo first in-first-out + * @param trxName transaction + * @return existing or null + */ + public static MStorage[] getAllWithASI (Properties ctx, int M_Product_ID, int M_Locator_ID, + boolean FiFo, String trxName) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_Storage " + + "WHERE M_Product_ID=? AND M_Locator_ID=?" + + " AND M_AttributeSetInstance_ID > 0" + + " AND QtyOnHand > 0 " + + "ORDER BY M_AttributeSetInstance_ID"; + if (!FiFo) + sql += " DESC"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + pstmt.setInt (1, M_Product_ID); + pstmt.setInt (2, M_Locator_ID); + rs = pstmt.executeQuery (); + while (rs.next ()) + list.add(new MStorage (ctx, rs, trxName)); + } + catch (SQLException ex) + { + s_log.log(Level.SEVERE, sql, ex); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + pstmt = null; + MStorage[] retValue = new MStorage[list.size()]; + list.toArray(retValue); + return retValue; + } // getAllWithASI + + /** + * Get all Storages for Product + * @param ctx context + * @param M_Product_ID product + * @param M_Locator_ID locator + * @param trxName transaction + * @return existing or null + */ + public static MStorage[] getAll (Properties ctx, + int M_Product_ID, int M_Locator_ID, String trxName) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_Storage " + + "WHERE M_Product_ID=? AND M_Locator_ID=?" + + " AND QtyOnHand <> 0 " + + "ORDER BY M_AttributeSetInstance_ID"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + pstmt.setInt (1, M_Product_ID); + pstmt.setInt (2, M_Locator_ID); + rs = pstmt.executeQuery (); + while (rs.next ()) + list.add(new MStorage (ctx, rs, trxName)); + } + catch (SQLException ex) + { + s_log.log(Level.SEVERE, sql, ex); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + pstmt = null; + MStorage[] retValue = new MStorage[list.size()]; + list.toArray(retValue); + return retValue; + } // getAll + + + /** + * Get Storage Info for Product across warehouses + * @param ctx context + * @param M_Product_ID product + * @param trxName transaction + * @return existing or null + */ + public static MStorage[] getOfProduct (Properties ctx, int M_Product_ID, String trxName) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_Storage " + + "WHERE M_Product_ID=?"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, trxName); + pstmt.setInt (1, M_Product_ID); + rs = pstmt.executeQuery (); + while (rs.next ()) + list.add(new MStorage (ctx, rs, trxName)); + } + catch (SQLException ex) + { + s_log.log(Level.SEVERE, sql, ex); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + MStorage[] retValue = new MStorage[list.size()]; + list.toArray(retValue); + return retValue; + } // getOfProduct + + /** + * Get Storage Info for Warehouse + * @param ctx context + * @param M_Warehouse_ID + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID instance + * @param M_AttributeSet_ID attribute set + * @param allAttributeInstances if true, all attribute set instances + * @param minGuaranteeDate optional minimum guarantee date if all attribute instances + * @param FiFo first in-first-out + * @param trxName transaction + * @return existing - ordered by location priority (desc) and/or guarantee date + */ + public static MStorage[] getWarehouse (Properties ctx, int M_Warehouse_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, int M_AttributeSet_ID, + boolean allAttributeInstances, Timestamp minGuaranteeDate, + boolean FiFo, String trxName) + { + if (M_Warehouse_ID == 0 || M_Product_ID == 0) + return new MStorage[0]; + + if (M_AttributeSet_ID == 0) + allAttributeInstances = true; + else + { + MAttributeSet mas = MAttributeSet.get(ctx, M_AttributeSet_ID); + if (!mas.isInstanceAttribute()) + allAttributeInstances = true; + } + + ArrayList list = new ArrayList(); + // Specific Attribute Set Instance + String sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID," + + "s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy," + + "s.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.DateLastInventory " + + "FROM M_Storage s" + + " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID) " + + "WHERE l.M_Warehouse_ID=?" + + " AND s.M_Product_ID=?" + + " AND COALESCE(s.M_AttributeSetInstance_ID,0)=? " + + "ORDER BY l.PriorityNo DESC, M_AttributeSetInstance_ID"; + if (!FiFo) + sql += " DESC"; + // All Attribute Set Instances + if (allAttributeInstances) + { + sql = "SELECT s.M_Product_ID,s.M_Locator_ID,s.M_AttributeSetInstance_ID," + + "s.AD_Client_ID,s.AD_Org_ID,s.IsActive,s.Created,s.CreatedBy,s.Updated,s.UpdatedBy," + + "s.QtyOnHand,s.QtyReserved,s.QtyOrdered,s.DateLastInventory " + + "FROM M_Storage s" + + " INNER JOIN M_Locator l ON (l.M_Locator_ID=s.M_Locator_ID)" + + " LEFT OUTER JOIN M_AttributeSetInstance asi ON (s.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) " + + "WHERE l.M_Warehouse_ID=?" + + " AND s.M_Product_ID=? "; + if (minGuaranteeDate != null) + { + sql += "AND (asi.GuaranteeDate IS NULL OR asi.GuaranteeDate>?) " + + "ORDER BY asi.GuaranteeDate, M_AttributeSetInstance_ID"; + if (!FiFo) + sql += " DESC"; + sql += ", l.PriorityNo DESC, s.QtyOnHand DESC"; + } + else + { + sql += "ORDER BY l.PriorityNo DESC, l.M_Locator_ID, s.M_AttributeSetInstance_ID"; + if (!FiFo) + sql += " DESC"; + sql += ", s.QtyOnHand DESC"; + } + } + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, trxName); + pstmt.setInt(1, M_Warehouse_ID); + pstmt.setInt(2, M_Product_ID); + if (!allAttributeInstances) + pstmt.setInt(3, M_AttributeSetInstance_ID); + else if (minGuaranteeDate != null) + pstmt.setTimestamp(3, minGuaranteeDate); + rs = pstmt.executeQuery(); + while (rs.next()) + list.add (new MStorage (ctx, rs, trxName)); + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + MStorage[] retValue = new MStorage[list.size()]; + list.toArray(retValue); + return retValue; + } // getWarehouse + + + /** + * Create or Get Storage Info + * @param ctx context + * @param M_Locator_ID locator + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID instance + * @param trxName transaction + * @return existing/new or null + */ + public static MStorage getCreate (Properties ctx, int M_Locator_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, String trxName) + { + if (M_Locator_ID == 0) + throw new IllegalArgumentException("M_Locator_ID=0"); + if (M_Product_ID == 0) + throw new IllegalArgumentException("M_Product_ID=0"); + MStorage retValue = get(ctx, M_Locator_ID, M_Product_ID, M_AttributeSetInstance_ID, trxName); + if (retValue != null) + return retValue; + + // Insert row based on locator + MLocator locator = new MLocator (ctx, M_Locator_ID, trxName); + if (locator.get_ID() != M_Locator_ID) + throw new IllegalArgumentException("Not found M_Locator_ID=" + M_Locator_ID); + // + retValue = new MStorage (locator, M_Product_ID, M_AttributeSetInstance_ID); + retValue.save(trxName); + s_log.fine("New " + retValue); + return retValue; + } // getCreate + + + /** + * Update Storage Info add. + * Called from MProjectIssue + * @param ctx context + * @param M_Warehouse_ID warehouse + * @param M_Locator_ID locator + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID AS Instance + * @param reservationAttributeSetInstance_ID reservation AS Instance + * @param diffQtyOnHand add on hand + * @param diffQtyReserved add reserved + * @param diffQtyOrdered add order + * @param trxName transaction + * @return true if updated + */ + public static boolean add (Properties ctx, int M_Warehouse_ID, int M_Locator_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, int reservationAttributeSetInstance_ID, + BigDecimal diffQtyOnHand, + BigDecimal diffQtyReserved, BigDecimal diffQtyOrdered, String trxName) + { + MStorage storage = null; + StringBuffer diffText = new StringBuffer("("); + + // Get Storage + if (storage == null) + storage = getCreate (ctx, M_Locator_ID, + M_Product_ID, M_AttributeSetInstance_ID, trxName); + // Verify + if (storage.getM_Locator_ID() != M_Locator_ID + && storage.getM_Product_ID() != M_Product_ID + && storage.getM_AttributeSetInstance_ID() != M_AttributeSetInstance_ID) + { + s_log.severe ("No Storage found - M_Locator_ID=" + M_Locator_ID + + ",M_Product_ID=" + M_Product_ID + ",ASI=" + M_AttributeSetInstance_ID); + return false; + } + + // CarlosRuiz - globalqss - Fix [ 1725383 ] QtyOrdered wrongly updated + MProduct prd = new MProduct(ctx, M_Product_ID, trxName); + if (prd.getM_AttributeSet_ID() == 0) { + // Product doesn't manage attribute set, always reserved with 0 + reservationAttributeSetInstance_ID = 0; + } + // + + MStorage storage0 = null; + if (M_AttributeSetInstance_ID != reservationAttributeSetInstance_ID) + { + storage0 = get(ctx, M_Locator_ID, + M_Product_ID, reservationAttributeSetInstance_ID, trxName); + if (storage0 == null) // create if not existing - should not happen + { + MWarehouse wh = MWarehouse.get(ctx, M_Warehouse_ID); + int xM_Locator_ID = wh.getDefaultLocator().getM_Locator_ID(); + storage0 = getCreate (ctx, xM_Locator_ID, + M_Product_ID, reservationAttributeSetInstance_ID, trxName); + } + } + boolean changed = false; + if (diffQtyOnHand != null && diffQtyOnHand.signum() != 0) + { + storage.setQtyOnHand (storage.getQtyOnHand().add (diffQtyOnHand)); + diffText.append("OnHand=").append(diffQtyOnHand); + changed = true; + } + if (diffQtyReserved != null && diffQtyReserved.signum() != 0) + { + if (storage0 == null) + storage.setQtyReserved (storage.getQtyReserved().add (diffQtyReserved)); + else + storage0.setQtyReserved (storage0.getQtyReserved().add (diffQtyReserved)); + diffText.append(" Reserved=").append(diffQtyReserved); + changed = true; + } + if (diffQtyOrdered != null && diffQtyOrdered.signum() != 0) + { + if (storage0 == null) + storage.setQtyOrdered (storage.getQtyOrdered().add (diffQtyOrdered)); + else + storage0.setQtyOrdered (storage0.getQtyOrdered().add (diffQtyOrdered)); + diffText.append(" Ordered=").append(diffQtyOrdered); + changed = true; + } + if (changed) + { + diffText.append(") -> ").append(storage.toString()); + s_log.fine(diffText.toString()); + if (storage0 != null) + storage0.save(trxName); // No AttributeSetInstance (reserved/ordered) + return storage.save (trxName); + } + + return true; + } // add + + + /************************************************************************** + * Get Location with highest Locator Priority and a sufficient OnHand Qty + * @param M_Warehouse_ID warehouse + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID asi + * @param Qty qty + * @param trxName transaction + * @return id + */ + public static int getM_Locator_ID (int M_Warehouse_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, BigDecimal Qty, + String trxName) + { + int M_Locator_ID = 0; + int firstM_Locator_ID = 0; + String sql = "SELECT s.M_Locator_ID, s.QtyOnHand " + + "FROM M_Storage s" + + " INNER JOIN M_Locator l ON (s.M_Locator_ID=l.M_Locator_ID)" + + " INNER JOIN M_Product p ON (s.M_Product_ID=p.M_Product_ID)" + + " LEFT OUTER JOIN M_AttributeSet mas ON (p.M_AttributeSet_ID=mas.M_AttributeSet_ID) " + + "WHERE l.M_Warehouse_ID=?" + + " AND s.M_Product_ID=?" + + " AND (mas.IsInstanceAttribute IS NULL OR mas.IsInstanceAttribute='N' OR s.M_AttributeSetInstance_ID=?)" + + " AND l.IsActive='Y' " + + "ORDER BY l.PriorityNo DESC, s.QtyOnHand DESC"; + + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, trxName); + pstmt.setInt(1, M_Warehouse_ID); + pstmt.setInt(2, M_Product_ID); + pstmt.setInt(3, M_AttributeSetInstance_ID); + rs = pstmt.executeQuery(); + while (rs.next()) + { + BigDecimal QtyOnHand = rs.getBigDecimal(2); + if (QtyOnHand != null && Qty.compareTo(QtyOnHand) <= 0) + { + M_Locator_ID = rs.getInt(1); + break; + } + if (firstM_Locator_ID == 0) + firstM_Locator_ID = rs.getInt(1); + } + } + catch (SQLException ex) + { + s_log.log(Level.SEVERE, sql, ex); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + if (M_Locator_ID != 0) + return M_Locator_ID; + return firstM_Locator_ID; + } // getM_Locator_ID + + /** + * Get Available Qty. + * The call is accurate only if there is a storage record + * and assumes that the product is stocked + * @param M_Warehouse_ID wh + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID masi + * @param trxName transaction + * @return qty available (QtyOnHand-QtyReserved) or null + * @deprecated Since 331b. Please use {@link #getQtyAvailable(int, int, int, int, String)}. + */ + public static BigDecimal getQtyAvailable (int M_Warehouse_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, String trxName) + { + return getQtyAvailable(M_Warehouse_ID, 0, M_Product_ID, M_AttributeSetInstance_ID, trxName); + } + + /** + * Get Warehouse/Locator Available Qty. + * The call is accurate only if there is a storage record + * and assumes that the product is stocked + * @param M_Warehouse_ID wh (if the M_Locator_ID!=0 then M_Warehouse_ID is ignored) + * @param M_Locator_ID locator (if 0, the whole warehouse will be evaluated) + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID masi + * @param trxName transaction + * @return qty available (QtyOnHand-QtyReserved) or null if error + */ + public static BigDecimal getQtyAvailable (int M_Warehouse_ID, int M_Locator_ID, + int M_Product_ID, int M_AttributeSetInstance_ID, String trxName) + { + ArrayList params = new ArrayList(); + StringBuffer sql = new StringBuffer("SELECT COALESCE(SUM(s.QtyOnHand-s.QtyReserved),0)") + .append(" FROM M_Storage s") + .append(" WHERE s.M_Product_ID=?"); + params.add(M_Product_ID); + // Warehouse level + if (M_Locator_ID == 0) { + sql.append(" AND EXISTS (SELECT 1 FROM M_Locator l WHERE s.M_Locator_ID=l.M_Locator_ID AND l.M_Warehouse_ID=?)"); + params.add(M_Warehouse_ID); + } + // Locator level + else { + sql.append(" AND s.M_Locator_ID=?"); + params.add(M_Locator_ID); + } + // With ASI + if (M_AttributeSetInstance_ID != 0) { + sql.append(" AND s.M_AttributeSetInstance_ID=?"); + params.add(M_AttributeSetInstance_ID); + } + // + BigDecimal retValue = DB.getSQLValueBD(trxName, sql.toString(), params); + if (CLogMgt.isLevelFine()) + s_log.fine("M_Warehouse_ID=" + M_Warehouse_ID + ", M_Locator_ID=" + M_Locator_ID + + ",M_Product_ID=" + M_Product_ID + " = " + retValue); + return retValue; + } // getQtyAvailable + + + /************************************************************************** + * Persistency Constructor + * @param ctx context + * @param ignored ignored + * @param trxName transaction + */ + public MStorage (Properties ctx, int ignored, String trxName) + { + super(ctx, 0, trxName); + if (ignored != 0) + throw new IllegalArgumentException("Multi-Key"); + // + setQtyOnHand (Env.ZERO); + setQtyOrdered (Env.ZERO); + setQtyReserved (Env.ZERO); + } // MStorage + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MStorage (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MStorage + + /** + * Full NEW Constructor + * @param locator (parent) locator + * @param M_Product_ID product + * @param M_AttributeSetInstance_ID attribute + */ + private MStorage (MLocator locator, int M_Product_ID, int M_AttributeSetInstance_ID) + { + this (locator.getCtx(), 0, locator.get_TrxName()); + setClientOrg(locator); + setM_Locator_ID (locator.getM_Locator_ID()); + setM_Product_ID (M_Product_ID); + setM_AttributeSetInstance_ID (M_AttributeSetInstance_ID); + } // MStorage + + /** Log */ + private static CLogger s_log = CLogger.getCLogger (MStorage.class); + /** Warehouse */ + private int m_M_Warehouse_ID = 0; + + /** + * Change Qty OnHand + * @param qty quantity + * @param add add if true + */ + public void changeQtyOnHand (BigDecimal qty, boolean add) + { + if (qty == null || qty.signum() == 0) + return; + if (add) + setQtyOnHand(getQtyOnHand().add(qty)); + else + setQtyOnHand(getQtyOnHand().subtract(qty)); + } // changeQtyOnHand + + /** + * Get M_Warehouse_ID of Locator + * @return warehouse + */ + public int getM_Warehouse_ID() + { + if (m_M_Warehouse_ID == 0) + { + MLocator loc = MLocator.get(getCtx(), getM_Locator_ID()); + m_M_Warehouse_ID = loc.getM_Warehouse_ID(); + } + return m_M_Warehouse_ID; + } // getM_Warehouse_ID + + /** + * String Representation + * @return info + */ + public String toString() + { + StringBuffer sb = new StringBuffer("MStorage[") + .append("M_Locator_ID=").append(getM_Locator_ID()) + .append(",M_Product_ID=").append(getM_Product_ID()) + .append(",M_AttributeSetInstance_ID=").append(getM_AttributeSetInstance_ID()) + .append(": OnHand=").append(getQtyOnHand()) + .append(",Reserved=").append(getQtyReserved()) + .append(",Ordered=").append(getQtyOrdered()) + .append("]"); + return sb.toString(); + } // toString + +} // MStorage diff --git a/base/src/org/compiere/model/MWarehouse.java b/base/src/org/compiere/model/MWarehouse.java new file mode 100644 index 0000000000..96535a4279 --- /dev/null +++ b/base/src/org/compiere/model/MWarehouse.java @@ -0,0 +1,236 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Warehouse Model + * + * @author Jorg Janke + * @version $Id: MWarehouse.java,v 1.3 2006/07/30 00:58:05 jjanke Exp $ + */ +public class MWarehouse extends X_M_Warehouse +{ + /** + * Get from Cache + * @param ctx context + * @param M_Warehouse_ID id + * @return warehouse + */ + public static MWarehouse get (Properties ctx, int M_Warehouse_ID) + { + Integer key = new Integer(M_Warehouse_ID); + MWarehouse retValue = (MWarehouse)s_cache.get(key); + if (retValue != null) + return retValue; + // + retValue = new MWarehouse (ctx, M_Warehouse_ID, null); + s_cache.put (key, retValue); + return retValue; + } // get + + /** + * Get Warehouses for Org + * @param ctx context + * @param AD_Org_ID id + * @return warehouse + */ + public static MWarehouse[] getForOrg (Properties ctx, int AD_Org_ID) + { + ArrayList list = new ArrayList(); + String sql = "SELECT * FROM M_Warehouse WHERE AD_Org_ID=? ORDER BY Created"; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, AD_Org_ID); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + list.add (new MWarehouse (ctx, rs, null)); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + s_log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + MWarehouse[] retValue = new MWarehouse[list.size ()]; + list.toArray (retValue); + return retValue; + } // get + + + /** Cache */ + private static CCache s_cache = new CCache("M_Warehouse", 5); + /** Static Logger */ + private static CLogger s_log = CLogger.getCLogger (MWarehouse.class); + + /** + * Standard Constructor + * @param ctx context + * @param M_Warehouse_ID id + * @param trxName transaction + */ + public MWarehouse (Properties ctx, int M_Warehouse_ID, String trxName) + { + super(ctx, M_Warehouse_ID, trxName); + if (M_Warehouse_ID == 0) + { + // setValue (null); + // setName (null); + // setC_Location_ID (0); + setSeparator ("*"); // * + } + } // MWarehouse + + /** + * Load Constructor + * @param ctx context + * @param rs result set + * @param trxName transaction + */ + public MWarehouse (Properties ctx, ResultSet rs, String trxName) + { + super(ctx, rs, trxName); + } // MWarehouse + + /** + * Organization Constructor + * @param org parent + */ + public MWarehouse (MOrg org) + { + this (org.getCtx(), 0, org.get_TrxName()); + setClientOrg(org); + setValue (org.getValue()); + setName (org.getName()); + if (org.getInfo() != null) + setC_Location_ID (org.getInfo().getC_Location_ID()); + } // MWarehouse + + /** Warehouse Locators */ + private MLocator[] m_locators = null; + + /** + * Get Locators + * @param reload if true reload + * @return array of locators + */ + public MLocator[] getLocators(boolean reload) + { + if (!reload && m_locators != null) + return m_locators; + // + String sql = "SELECT * FROM M_Locator WHERE M_Warehouse_ID=? ORDER BY X,Y,Z"; + ArrayList list = new ArrayList(); + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setInt (1, getM_Warehouse_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + list.add(new MLocator (getCtx(), rs, null)); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + // + m_locators = new MLocator[list.size()]; + list.toArray (m_locators); + return m_locators; + } // getLocators + + /** + * Get Default Locator + * @return (first) default locator + */ + public MLocator getDefaultLocator() + { + MLocator[] locators = getLocators(false); + for (int i = 0; i < locators.length; i++) + { + if (locators[i].isDefault() && locators[i].isActive()) + return locators[i]; + } + // No Default - first one + if (locators.length > 0) + { + log.warning("No default locator for " + getName()); + return locators[0]; + } + // No Locator - create one + MLocator loc = new MLocator (this, "Standard"); + loc.setIsDefault(true); + loc.save(); + log.info("Created default locator for " + getName()); + return loc; + } // getLocators + + /** + * After Save + * @param newRecord new + * @param success success + * @return success + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + if (newRecord && success) + insert_Accounting("M_Warehouse_Acct", "C_AcctSchema_Default", null); + + return success; + } // afterSave + + /** + * Before Delete + * @return true + */ + protected boolean beforeDelete () + { + return delete_Accounting("M_Warehouse_Acct"); + } // beforeDelete + +} // MWarehouse diff --git a/base/src/org/compiere/model/ModelValidator.java b/base/src/org/compiere/model/ModelValidator.java new file mode 100644 index 0000000000..9d480d1a4a --- /dev/null +++ b/base/src/org/compiere/model/ModelValidator.java @@ -0,0 +1,162 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + * Contributor(s) : Layda Salas - globalqss * + *****************************************************************************/ +package org.compiere.model; + +/** + * Model Validator + * + * @author Jorg Janke + * @version $Id: ModelValidator.java,v 1.2 2006/07/30 00:58:18 jjanke Exp $ + * + * 2007/02/26 laydasalasc - globalqss - Add new timings for all before/after events on documents + */ +public interface ModelValidator +{ + /** Model Change Type New */ + public static final int TYPE_BEFORE_NEW = 1; // teo_sarca [ 1675490 ] + public static final int TYPE_NEW = 1; + public static final int CHANGETYPE_NEW = 1; // Compatibility with Compiere 260c + public static final int TYPE_AFTER_NEW = 4; // teo_sarca [ 1675490 ] + /** Model Change Type Change */ + public static final int TYPE_BEFORE_CHANGE = 2; // teo_sarca [ 1675490 ] + public static final int TYPE_CHANGE = 2; + public static final int CHANGETYPE_CHANGE = 2; // Compatibility with Compiere 260c + public static final int TYPE_AFTER_CHANGE = 5; // teo_sarca [ 1675490 ] + /** Model Change Type Delete */ + public static final int TYPE_BEFORE_DELETE = 3; // teo_sarca [ 1675490 ] + public static final int TYPE_DELETE = 3; + public static final int CHANGETYPE_DELETE = 3; // Compatibility with Compiere 260c + public static final int TYPE_AFTER_DELETE = 6; // teo_sarca [ 1675490 ] + + // Correlation between constant events and list of event script model validators + public static String[] tableEventValidators = new String[] { + "", // 0 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_TableBeforeNew, // TYPE_BEFORE_NEW = 1 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_TableBeforeChange, // TYPE_BEFORE_CHANGE = 2 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_TableBeforeDelete, // TYPE_BEFORE_DELETE = 3 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_TableAfterNew, // TYPE_AFTER_NEW = 4 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_TableAfterChange, // TYPE_AFTER_CHANGE = 5 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_TableAfterDelete // TYPE_AFTER_DELETE = 6 + }; + + /** Called before document is prepared */ + public static final int TIMING_BEFORE_PREPARE = 1; + public static final int DOCTIMING_BEFORE_PREPARE = 1; // Compatibility with Compiere 260c + /** Called before document is void */ + public static final int TIMING_BEFORE_VOID = 2; + /** Called before document is close */ + public static final int TIMING_BEFORE_CLOSE = 3; + /** Called before document is reactivate */ + public static final int TIMING_BEFORE_REACTIVATE = 4; + /** Called before document is reversecorrect */ + public static final int TIMING_BEFORE_REVERSECORRECT = 5; + /** Called before document is reverseaccrual */ + public static final int TIMING_BEFORE_REVERSEACCRUAL = 6; + /** Called before document is completed */ + public static final int TIMING_BEFORE_COMPLETE = 7; + /** Called after document is prepared */ + public static final int TIMING_AFTER_PREPARE = 8; + /** Called after document is completed */ + public static final int TIMING_AFTER_COMPLETE = 9; + public static final int DOCTIMING_AFTER_COMPLETE = 9; // Compatibility with Compiere 260c + /** Called after document is void */ + public static final int TIMING_AFTER_VOID = 10; + /** Called after document is closed */ + public static final int TIMING_AFTER_CLOSE = 11; + /** Called after document is reactivated */ + public static final int TIMING_AFTER_REACTIVATE = 12; + /** Called after document is reversecorrect */ + public static final int TIMING_AFTER_REVERSECORRECT = 13; + /** Called after document is reverseaccrual */ + public static final int TIMING_AFTER_REVERSEACCRUAL = 14; + /** Called before document is posted */ + public static final int TIMING_BEFORE_POST = 15; + /** Called after document is posted */ + public static final int TIMING_AFTER_POST = 16; + + // Correlation between constant events and list of event script model validators + public static String[] documentEventValidators = new String[] { + "", // 0 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentBeforePrepare, // TIMING_BEFORE_PREPARE = 1 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentBeforeVoid, // TIMING_BEFORE_VOID = 2 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentBeforeClose, // TIMING_BEFORE_CLOSE = 3 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentBeforeReactivate, // TIMING_BEFORE_REACTIVATE = 4 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentBeforeReverseCorrect, // TIMING_BEFORE_REVERSECORRECT = 5 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentBeforeReverseAccrual, // TIMING_BEFORE_REVERSEACCRUAL = 6 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentBeforeComplete, // TIMING_BEFORE_COMPLETE = 7 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentAfterPrepare, // TIMING_AFTER_PREPARE = 8 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentAfterComplete, // TIMING_AFTER_COMPLETE = 9 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentAfterVoid, // TIMING_AFTER_VOID = 10 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentAfterClose, // TIMING_AFTER_CLOSE = 11 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentAfterReactivate, // TIMING_AFTER_REACTIVATE = 12 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentAfterReverseCorrect, // TIMING_AFTER_REVERSECORRECT = 13 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentAfterReverseAccrual, // TIMING_AFTER_REVERSEACCRUAL = 14 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentBeforePost, // TIMING_BEFORE_POST = 15 + X_AD_Table_ScriptValidator.EVENTMODELVALIDATOR_DocumentAfterPost // TIMING_AFTER_POST = 16 + }; + + /** + * Initialize Validation + * @param engine validation engine + * @param client client + */ + public void initialize (ModelValidationEngine engine, MClient client); + + /** + * Get Client to be monitored + * @return AD_Client_ID + */ + public int getAD_Client_ID(); + + /** + * User logged in + * Called before preferences are set + * @param AD_Org_ID org + * @param AD_Role_ID role + * @param AD_User_ID user + * @return error message or null + */ + public String login (int AD_Org_ID, int AD_Role_ID, int AD_User_ID); + + + /** + * Model Change of a monitored Table. + * Called after PO.beforeSave/PO.beforeDelete + * when you called addModelChange for the table + * @param po persistent object + * @param type TYPE_ + * @return error message or null + * @exception Exception if the recipient wishes the change to be not accept. + */ + public String modelChange (PO po, int type) throws Exception; + + + /** + * Validate Document. + * Called as first step of DocAction.prepareIt + * or at the end of DocAction.completeIt + * when you called addDocValidate for the table. + * Note that totals, etc. may not be correct before the prepare stage. + * @param po persistent object + * @param timing see TIMING_ constants + * @return error message or null - + * if not null, the pocument will be marked as Invalid. + */ + public String docValidate (PO po, int timing); + +} // ModelValidator diff --git a/base/src/org/compiere/model/PO.java b/base/src/org/compiere/model/PO.java new file mode 100644 index 0000000000..62bf027836 --- /dev/null +++ b/base/src/org/compiere/model/PO.java @@ -0,0 +1,3643 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.model; + +import java.io.Serializable; +import java.io.StringWriter; +import java.math.BigDecimal; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Properties; +import java.util.logging.Level; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.compiere.Adempiere; +import org.compiere.acct.Doc; +import org.compiere.util.CLogMgt; +import org.compiere.util.CLogger; +import org.compiere.util.CacheMgt; +import org.compiere.util.DB; +import org.compiere.util.DBException; +import org.compiere.util.DisplayType; +import org.compiere.util.Env; +import org.compiere.util.Evaluatee; +import org.compiere.util.Msg; +import org.compiere.util.SecureEngine; +import org.compiere.util.Trace; +import org.compiere.util.Trx; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Persistent Object. + * Superclass for actual implementations + * + * @author Jorg Janke + * @version $Id: PO.java,v 1.12 2006/08/09 16:38:47 jjanke Exp $ + * + * @author Teo Sarca - FR [ 1675490 ], BF [ 1704828 ] + */ +public abstract class PO + implements Serializable, Comparator, Evaluatee +{ + /** + * Set Document Value Workflow Manager + * @param docWFMgr mgr + */ + public static void setDocWorkflowMgr (DocWorkflowMgr docWFMgr) + { + s_docWFMgr = docWFMgr; + s_log.config (s_docWFMgr.toString()); + } // setDocWorkflowMgr + + /** Document Value Workflow Manager */ + private static DocWorkflowMgr s_docWFMgr = null; + + /** User Maintained Entity Type */ + static protected final String ENTITYTYPE_UserMaintained = "U"; + /** Dictionary Maintained Entity Type */ + static protected final String ENTITYTYPE_Dictionary = "D"; + + /************************************************************************** + * Create New Persisent Object + * @param ctx context + */ + public PO (Properties ctx) + { + this (ctx, 0, null, null); + } // PO + + /** + * Create & Load existing Persistent Object + * @param ID The unique ID of the object + * @param ctx context + * @param trxName transaction name + */ + public PO (Properties ctx, int ID, String trxName) + { + this (ctx, ID, trxName, null); + } // PO + + /** + * Create & Load existing Persistent Object. + * @param ctx context + * @param rs optional - load from current result set position (no navigation, not closed) + * if null, a new record is created. + * @param trxName transaction name + */ + public PO (Properties ctx, ResultSet rs, String trxName) + { + this (ctx, 0, trxName, rs); + } // PO + + /** + * Create & Load existing Persistent Object. + *
    +	 *  You load
    +	 * 		- an existing single key record with 	new PO (ctx, Record_ID)
    +	 * 			or									new PO (ctx, Record_ID, trxName)
    +	 * 			or									new PO (ctx, rs, get_TrxName())
    +	 * 		- a new single key record with			new PO (ctx, 0)
    +	 * 		- an existing multi key record with		new PO (ctx, rs, get_TrxName())
    +	 * 		- a new multi key record with			new PO (ctx, null)
    +	 *  The ID for new single key records is created automatically,
    +	 *  you need to set the IDs for multi-key records explicitly.
    +	 *	
    + * @param ctx context + * @param ID the ID if 0, the record defaults are applied - ignored if re exists + * @param trxName transaction name + * @param rs optional - load from current result set position (no navigation, not closed) + */ + public PO (Properties ctx, int ID, String trxName, ResultSet rs) + { + if (ctx == null) + throw new IllegalArgumentException ("No Context"); + p_ctx = ctx; + m_trxName = trxName; + + p_info = initPO(ctx); + if (p_info == null || p_info.getTableName() == null) + throw new IllegalArgumentException ("Invalid PO Info - " + p_info); + // + int size = p_info.getColumnCount(); + m_oldValues = new Object[size]; + m_newValues = new Object[size]; + + if (rs != null) + load(rs); // will not have virtual columns + else + load(ID, trxName); + } // PO + + /** + * Create New PO by Copying existing (key not copied). + * @param ctx context + * @param source souce object + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + public PO (Properties ctx, PO source, int AD_Client_ID, int AD_Org_ID) + { + this (ctx, 0, null, null); // create new + // + if (source != null) + copyValues (source, this); + setAD_Client_ID(AD_Client_ID); + setAD_Org_ID(AD_Org_ID); + } // PO + + + /** Logger */ + protected transient CLogger log = CLogger.getCLogger (getClass()); + /** Static Logger */ + private static CLogger s_log = CLogger.getCLogger (PO.class); + + /** Context */ + protected Properties p_ctx; + /** Model Info */ + protected volatile POInfo p_info = null; + + /** Original Values */ + private Object[] m_oldValues = null; + /** New Valies */ + private Object[] m_newValues = null; + + /** Record_IDs */ + private Object[] m_IDs = new Object[] {I_ZERO}; + /** Key Columns */ + private String[] m_KeyColumns = null; + /** Create New for Multi Key */ + private boolean m_createNew = false; + /** Attachment with entriess */ + private MAttachment m_attachment = null; + /** Deleted ID */ + private int m_idOld = 0; + /** Custom Columns */ + private HashMap m_custom = null; + + /** Zero Integer */ + protected static final Integer I_ZERO = new Integer(0); + /** Accounting Columns */ + private ArrayList s_acctColumns = null; + + + /** Access Level S__ 100 4 System info */ + public static final int ACCESSLEVEL_SYSTEM = 4; + /** Access Level _C_ 010 2 Client info */ + public static final int ACCESSLEVEL_CLIENT = 2; + /** Access Level __O 001 1 Organization info */ + public static final int ACCESSLEVEL_ORG = 1; + /** Access Level SCO 111 7 System shared info */ + public static final int ACCESSLEVEL_ALL = 7; + /** Access Level SC_ 110 6 System/Client info */ + public static final int ACCESSLEVEL_SYSTEMCLIENT = 6; + /** Access Level _CO 011 3 Client shared info */ + public static final int ACCESSLEVEL_CLIENTORG = 3; + + + /** + * Initialize and return PO_Info + * @param ctx context + * @return POInfo + */ + abstract protected POInfo initPO (Properties ctx); + + /** + * Get Table Access Level + * @return Access Level + */ + abstract protected int get_AccessLevel(); + + /** + * String representation + * @return String representation + */ + public String toString() + { + StringBuffer sb = new StringBuffer("PO[") + .append(get_WhereClause(true)).append("]"); + return sb.toString(); + } // toString + + /** + * Equals based on ID + * @param cmp comperator + * @return true if ID the same + */ + public boolean equals (Object cmp) + { + if (cmp == null) + return false; + if (!(cmp instanceof PO)) + return false; + if (cmp.getClass().equals(this.getClass())) + // if both ID's are zero they can't be compared by ID + if (((PO)cmp).get_ID() == 0 && get_ID() == 0) + return super.equals(cmp); + else + return ((PO)cmp).get_ID() == get_ID(); + return super.equals(cmp); + } // equals + + /** + * Compare based on DocumentNo, Value, Name, Description + * @param o1 Object 1 + * @param o2 Object 2 + * @return -1 if o1 < o2 + */ + public int compare (Object o1, Object o2) + { + if (o1 == null) + return -1; + else if (o2 == null) + return 1; + if (!(o1 instanceof PO)) + throw new ClassCastException ("Not PO -1- " + o1); + if (!(o2 instanceof PO)) + throw new ClassCastException ("Not PO -2- " + o2); + // same class + if (o1.getClass().equals(o2.getClass())) + { + int index = get_ColumnIndex("DocumentNo"); + if (index == -1) + index = get_ColumnIndex("Value"); + if (index == -1) + index = get_ColumnIndex("Name"); + if (index == -1) + index = get_ColumnIndex("Description"); + if (index != -1) + { + PO po1 = (PO)o1; + Object comp1 = po1.get_Value(index); + PO po2 = (PO)o2; + Object comp2 = po2.get_Value(index); + if (comp1 == null) + return -1; + else if (comp2 == null) + return 1; + return comp1.toString().compareTo(comp2.toString()); + } + } + return o1.toString().compareTo(o2.toString()); + } // compare + + /** + * Get TableName. + * @return table name + */ + public String get_TableName() + { + return p_info.getTableName(); + } // get_TableName + + /** + * Get Key Columns. + * @return table name + */ + public String[] get_KeyColumns() + { + return m_KeyColumns; + } // get_KeyColumns + + /** + * Get Table ID. + * @return table id + */ + public int get_Table_ID() + { + return p_info.getAD_Table_ID(); + } // get_TableID + + /** + * Return Single Key Record ID + * @return ID or 0 + */ + public int get_ID() + { + Object oo = m_IDs[0]; + if (oo != null && oo instanceof Integer) + return ((Integer)oo).intValue(); + return 0; + } // getID + + /** + * Return Deleted Single Key Record ID + * @return ID or 0 + */ + public int get_IDOld() + { + return m_idOld; + } // getID + + /** + * Get Context + * @return context + */ + public Properties getCtx() + { + return p_ctx; + } // getCtx + + /** + * Get Logger + * @return logger + */ + public CLogger get_Logger() + { + return log; + } // getLogger + + /************************************************************************** + * Get Value + * @param index index + * @return value + */ + public final Object get_Value (int index) + { + if (index < 0 || index >= get_ColumnCount()) + { + log.log(Level.WARNING, "Index invalid - " + index); + return null; + } + if (m_newValues[index] != null) + { + if (m_newValues[index].equals(Null.NULL)) + return null; + return m_newValues[index]; + } + return m_oldValues[index]; + } // get_Value + + /** + * Get Value as int + * @param index index + * @return int value or 0 + */ + protected int get_ValueAsInt (int index) + { + Object value = get_Value(index); + if (value == null) + return 0; + if (value instanceof Integer) + return ((Integer)value).intValue(); + try + { + return Integer.parseInt(value.toString()); + } + catch (NumberFormatException ex) + { + log.warning(p_info.getColumnName(index) + " - " + ex.getMessage()); + return 0; + } + } // get_ValueAsInt + + /** + * Get Value + * @param columnName column name + * @return value or null + */ + public final Object get_Value (String columnName) + { + int index = get_ColumnIndex(columnName); + if (index < 0) + { + log.log(Level.WARNING, "Column not found - " + columnName); + Trace.printStack(); + return null; + } + return get_Value (index); + } // get_Value + + /** + * Get Encrypted Value + * @param columnName column name + * @return value or null + */ + protected final Object get_ValueE (String columnName) + { + return get_Value (columnName); + } // get_ValueE + + /** + * Get Column Value + * @param variableName name + * @return value or "" + */ + public String get_ValueAsString (String variableName) + { + Object value = get_Value (variableName); + if (value == null) + return ""; + return value.toString(); + } // get_ValueAsString + + /** + * Get Value of Column + * @param AD_Column_ID column + * @return value or null + */ + public final Object get_ValueOfColumn (int AD_Column_ID) + { + int index = p_info.getColumnIndex(AD_Column_ID); + if (index < 0) + { + log.log(Level.WARNING, "Not found - AD_Column_ID=" + AD_Column_ID); + return null; + } + return get_Value (index); + } // get_ValueOfColumn + + /** + * Get Old Value + * @param index index + * @return value + */ + public final Object get_ValueOld (int index) + { + if (index < 0 || index >= get_ColumnCount()) + { + log.log(Level.WARNING, "Index invalid - " + index); + return null; + } + return m_oldValues[index]; + } // get_ValueOld + + /** + * Get Old Value + * @param columnName column name + * @return value or null + */ + public final Object get_ValueOld (String columnName) + { + int index = get_ColumnIndex(columnName); + if (index < 0) + { + log.log(Level.WARNING, "Column not found - " + columnName); + return null; + } + return get_ValueOld (index); + } // get_ValueOld + + /** + * Get Old Value as int + * @param columnName column name + * @return int value or 0 + */ + protected int get_ValueOldAsInt (String columnName) + { + Object value = get_ValueOld(columnName); + if (value == null) + return 0; + if (value instanceof Integer) + return ((Integer)value).intValue(); + try + { + return Integer.parseInt(value.toString()); + } + catch (NumberFormatException ex) + { + log.warning(columnName + " - " + ex.getMessage()); + return 0; + } + } // get_ValueOldAsInt + + /** + * Is Value Changed + * @param index index + * @return true if changed + */ + public final boolean is_ValueChanged (int index) + { + if (index < 0 || index >= get_ColumnCount()) + { + log.log(Level.WARNING, "Index invalid - " + index); + return false; + } + if (m_newValues[index] == null) + return false; + return !m_newValues[index].equals(m_oldValues[index]); + } // is_ValueChanged + + /** + * Is Value Changed + * @param columnName column name + * @return true if changed + */ + public final boolean is_ValueChanged (String columnName) + { + int index = get_ColumnIndex(columnName); + if (index < 0) + { + log.log(Level.WARNING, "Column not found - " + columnName); + return false; + } + return is_ValueChanged (index); + } // is_ValueChanged + + /** + * Return new - old. + * - New Value if Old Valus is null + * - New Value - Old Value if Number + * - otherwise null + * @param index index + * @return new - old or null if not appropiate or not changed + */ + public final Object get_ValueDifference (int index) + { + if (index < 0 || index >= get_ColumnCount()) + { + log.log(Level.WARNING, "Index invalid - " + index); + return null; + } + Object nValue = m_newValues[index]; + // No new Value or NULL + if (nValue == null || nValue == Null.NULL) + return null; + // + Object oValue = m_oldValues[index]; + if (oValue == null || oValue == Null.NULL) + return nValue; + if (nValue instanceof BigDecimal) + { + BigDecimal obd = (BigDecimal)oValue; + return ((BigDecimal)nValue).subtract(obd); + } + else if (nValue instanceof Integer) + { + int result = ((Integer)nValue).intValue(); + result -= ((Integer)oValue).intValue(); + return new Integer(result); + } + // + log.warning("Invalid type - New=" + nValue); + return null; + } // get_ValueDifference + + /** + * Return new - old. + * - New Value if Old Valus is null + * - New Value - Old Value if Number + * - otherwise null + * @param columnName column name + * @return new - old or null if not appropiate or not changed + */ + public final Object get_ValueDifference (String columnName) + { + int index = get_ColumnIndex(columnName); + if (index < 0) + { + log.log(Level.WARNING, "Column not found - " + columnName); + return null; + } + return get_ValueDifference (index); + } // get_ValueDifference + + + /************************************************************************** + * Set Value + * @param ColumnName column name + * @param value value + * @return true if value set + */ + protected final boolean set_Value (String ColumnName, Object value) + { + if (value instanceof String && ColumnName.equals("WhereClause") + && value.toString().toUpperCase().indexOf("=NULL") != -1) + log.warning("Invalid Null Value - " + ColumnName + "=" + value); + + int index = get_ColumnIndex(ColumnName); + if (index < 0) + { + log.log(Level.SEVERE, "Column not found - " + ColumnName); + return false; + } + if (ColumnName.endsWith("_ID") && value instanceof String ) + { + log.severe("Invalid Data Type for " + ColumnName + "=" + value); + value = Integer.parseInt((String)value); + } + + return set_Value (index, value); + } // setValue + + /** + * Set Encrypted Value + * @param ColumnName column name + * @param value value + * @return true if value set + */ + protected final boolean set_ValueE (String ColumnName, Object value) + { + return set_Value (ColumnName, value); + } // setValueE + + /** + * Set Value if updateable and correct class. + * (and to NULL if not mandatory) + * @param index index + * @param value value + * @return true if value set + */ + protected final boolean set_Value (int index, Object value) + { + if (index < 0 || index >= get_ColumnCount()) + { + log.log(Level.WARNING, "Index invalid - " + index); + return false; + } + String ColumnName = p_info.getColumnName(index); + String colInfo = " - " + ColumnName; + // + if (p_info.isVirtualColumn(index)) + { + log.log(Level.WARNING, "Virtual Column" + colInfo); + return false; + } + // + // globalqss -- Bug 1618469 - is throwing not updateable even on new records + // if (!p_info.isColumnUpdateable(index)) + if ( ( ! p_info.isColumnUpdateable(index) ) && ( ! is_new() ) ) + { + colInfo += " - NewValue=" + value + " - OldValue=" + get_Value(index); + log.log(Level.WARNING, "Column not updateable" + colInfo); + return false; + } + // + if (value == null) + { + if (p_info.isColumnMandatory(index)) + { + log.log(Level.WARNING, "Cannot set mandatory column to null " + colInfo); + // Trace.printStack(); + return false; + } + m_newValues[index] = Null.NULL; // correct + log.finer(ColumnName + " = null"); + } + else + { + // matching class or generic object + if (value.getClass().equals(p_info.getColumnClass(index)) + || p_info.getColumnClass(index) == Object.class) + m_newValues[index] = value; // correct + // Integer can be set as BigDecimal + else if (value.getClass() == BigDecimal.class + && p_info.getColumnClass(index) == Integer.class) + m_newValues[index] = new Integer (((BigDecimal)value).intValue()); + // Set Boolean + else if (p_info.getColumnClass(index) == Boolean.class + && ("Y".equals(value) || "N".equals(value)) ) + m_newValues[index] = new Boolean("Y".equals(value)); + // added by vpj-cd + // To solve BUG [ 1618423 ] Set Project Type button in Project window throws warning + // generated because C_Project.C_Project_Type_ID is defined as button in dictionary + // although is ID (integer) in database + else if (value.getClass() == Integer.class + && p_info.getColumnClass(index) == String.class) + m_newValues[index] = value; + else if (value.getClass() == String.class + && p_info.getColumnClass(index) == Integer.class) + try + { + m_newValues[index] = new Integer((String)value); + } + catch (NumberFormatException e) + { + log.log(Level.SEVERE, ColumnName + + " - Class invalid: " + value.getClass().toString() + + ", Should be " + p_info.getColumnClass(index).toString() + ": " + value); + return false; + } + else + { + log.log(Level.SEVERE, ColumnName + + " - Class invalid: " + value.getClass().toString() + + ", Should be " + p_info.getColumnClass(index).toString() + ": " + value); + return false; + } + // Validate (Min/Max) + String error = p_info.validate(index, value); + if (error != null) + { + log.log(Level.WARNING, ColumnName + "=" + value + " - " + error); + return false; + } + // Length for String + if (p_info.getColumnClass(index) == String.class) + { + String stringValue = value.toString(); + int length = p_info.getFieldLength(index); + if (stringValue.length() > length && length > 0) + { + log.warning(ColumnName + " - Value too long - truncated to length=" + length); + m_newValues[index] = stringValue.substring(0,length-1); + } + } + log.finest(ColumnName + " = " + m_newValues[index]); + } + set_Keys (ColumnName, m_newValues[index]); + return true; + } // setValue + + /** + * Set Value w/o check (update, r/o, ..). + * Used when Column is R/O + * Required for key and parent values + * @param ColumnName column name + * @param value value + * @return true if value set + */ + protected final boolean set_ValueNoCheck (String ColumnName, Object value) + { + int index = get_ColumnIndex(ColumnName); + if (index < 0) + { + log.log(Level.SEVERE, "Column not found - " + ColumnName); + return false; + } + if (value == null) + m_newValues[index] = Null.NULL; // write direct + else + { + // matching class or generic object + if (value.getClass().equals(p_info.getColumnClass(index)) + || p_info.getColumnClass(index) == Object.class) + m_newValues[index] = value; // correct + // Integer can be set as BigDecimal + else if (value.getClass() == BigDecimal.class + && p_info.getColumnClass(index) == Integer.class) + m_newValues[index] = new Integer (((BigDecimal)value).intValue()); + // Set Boolean + else if (p_info.getColumnClass(index) == Boolean.class + && ("Y".equals(value) || "N".equals(value)) ) + m_newValues[index] = new Boolean("Y".equals(value)); + else if (p_info.getColumnClass(index) == Integer.class + && value.getClass() == String.class) + { + try + { + int intValue = Integer.parseInt((String)value); + m_newValues[index] = Integer.valueOf(intValue); + } + catch (Exception e) + { + log.warning (ColumnName + + " - Class invalid: " + value.getClass().toString() + + ", Should be " + p_info.getColumnClass(index).toString() + ": " + value); + m_newValues[index] = null; + } + } + else + { + log.warning (ColumnName + + " - Class invalid: " + value.getClass().toString() + + ", Should be " + p_info.getColumnClass(index).toString() + ": " + value); + m_newValues[index] = value; // correct + } + // Validate (Min/Max) + String error = p_info.validate(index, value); + if (error != null) + log.warning(ColumnName + "=" + value + " - " + error); + // length for String + if (p_info.getColumnClass(index) == String.class) + { + String stringValue = value.toString(); + int length = p_info.getFieldLength(index); + if (stringValue.length() > length && length > 0) + { + log.warning(ColumnName + " - Value too long - truncated to length=" + length); + m_newValues[index] = stringValue.substring(0,length-1); + } + } + } + log.finest(ColumnName + " = " + m_newValues[index] + + " (" + (m_newValues[index]==null ? "-" : m_newValues[index].getClass().getName()) + ")"); + set_Keys (ColumnName, m_newValues[index]); + return true; + } // set_ValueNoCheck + + /** + * Set Encrypted Value w/o check (update, r/o, ..). + * Used when Column is R/O + * Required for key and parent values + * @param ColumnName column name + * @param value value + * @return true if value set + */ + protected final boolean set_ValueNoCheckE (String ColumnName, Object value) + { + return set_ValueNoCheckE (ColumnName, value); + } // set_ValueNoCheckE + + /** + * Set value of Column + * @param columnName + * @param value + */ + public final void set_ValueOfColumn(String columnName, Object value) + { + int AD_Column_ID = p_info.getAD_Column_ID(columnName); + if (AD_Column_ID > 0) + { + set_ValueOfColumn(AD_Column_ID, value); + } + } + + /** + * Set Value of Column + * @param AD_Column_ID column + * @param value value + */ + public final void set_ValueOfColumn (int AD_Column_ID, Object value) + { + int index = p_info.getColumnIndex(AD_Column_ID); + if (index < 0) + log.log(Level.SEVERE, "Not found - AD_Column_ID=" + AD_Column_ID); + String ColumnName = p_info.getColumnName(index); + if (ColumnName.equals("IsApproved")) + set_ValueNoCheck(ColumnName, value); + else + set_Value (index, value); + } // setValueOfColumn + + + /** + * Set Custom Column + * @param columnName column + * @param value value + */ + public final void set_CustomColumn (String columnName, Object value) + { + // [ 1845793 ] PO.set_CustomColumn not updating correctly m_newValues + // this is for columns not in PO - verify and call proper method if exists + int poIndex = get_ColumnIndex(columnName); + if (poIndex > 0) { + // is not custom column - it exists in the PO + set_Value(columnName, value); + return; + } + if (m_custom == null) + m_custom = new HashMap(); + String valueString = "NULL"; + if (value == null) + ; + else if (value instanceof Number) + valueString = value.toString(); + else if (value instanceof Boolean) + valueString = ((Boolean)value).booleanValue() ? "'Y'" : "'N'"; + else if (value instanceof Timestamp) + valueString = DB.TO_DATE((Timestamp)value, false); + else // if (value instanceof String) + valueString = DB.TO_STRING(value.toString()); + // Save it + log.log(Level.INFO, columnName + "=" + valueString); + m_custom.put(columnName, valueString); + } // set_CustomColumn + + + /** + * Set (numeric) Key Value + * @param ColumnName column name + * @param value value + */ + private void set_Keys (String ColumnName, Object value) + { + // Update if KeyColumn + for (int i = 0; i < m_IDs.length; i++) + { + if (ColumnName.equals (m_KeyColumns[i])) + { + m_IDs[i] = value; + } + } // for all key columns + } // setKeys + + + /************************************************************************** + * Get Column Count + * @return column count + */ + protected int get_ColumnCount() + { + return p_info.getColumnCount(); + } // getColumnCount + + /** + * Get Column Name + * @param index index + * @return ColumnName + */ + protected String get_ColumnName (int index) + { + return p_info.getColumnName (index); + } // getColumnName + + /** + * Get Column Label + * @param index index + * @return Column Label + */ + protected String get_ColumnLabel (int index) + { + return p_info.getColumnLabel (index); + } // getColumnLabel + + /** + * Get Column Description + * @param index index + * @return column description + */ + protected String get_ColumnDescription (int index) + { + return p_info.getColumnDescription (index); + } // getColumnDescription + + /** + * Is Column Mandatory + * @param index index + * @return true if column mandatory + */ + protected boolean isColumnMandatory (int index) + { + return p_info.isColumnMandatory(index); + } // isColumnNandatory + + /** + * Is Column Updateable + * @param index index + * @return true if column updateable + */ + protected boolean isColumnUpdateable (int index) + { + return p_info.isColumnUpdateable(index); + } // isColumnUpdateable + + /** + * Set Column Updateable + * @param index index + * @param updateable column updateable + */ + protected void set_ColumnUpdateable (int index, boolean updateable) + { + p_info.setColumnUpdateable(index, updateable); + } // setColumnUpdateable + + /** + * Set all columns updateable + * @param updateable updateable + */ + protected void setUpdateable (boolean updateable) + { + p_info.setUpdateable (updateable); + } // setUpdateable + + /** + * Get Column DisplayType + * @param index index + * @return display type + */ + protected int get_ColumnDisplayType (int index) + { + return p_info.getColumnDisplayType(index); + } // getColumnDisplayType + + /** + * Get Lookup + * @param index index + * @return Lookup or null + */ + protected Lookup get_ColumnLookup(int index) + { + return p_info.getColumnLookup(index); + } // getColumnLookup + + /** + * Get Column Index + * @param columnName column name + * @return index of column with ColumnName or -1 if not found + */ + public final int get_ColumnIndex (String columnName) + { + return p_info.getColumnIndex(columnName); + } // getColumnIndex + + /** + * Get Display Value of value + * @param columnName columnName + * @param currentValue current value + * @return String value with "./." as null + */ + protected String get_DisplayValue(String columnName, boolean currentValue) + { + Object value = currentValue ? get_Value(columnName) : get_ValueOld(columnName); + if (value == null) + return "./."; + String retValue = value.toString(); + int index = get_ColumnIndex(columnName); + if (index < 0) + return retValue; + int dt = get_ColumnDisplayType(index); + if (DisplayType.isText(dt) || DisplayType.YesNo == dt) + return retValue; + // Lookup + Lookup lookup = get_ColumnLookup(index); + if (lookup != null) + return lookup.getDisplay(value); + // Other + return retValue; + } // get_DisplayValue + + + /** + * Copy old values of From to new values of To. + * Does not copy Keys + * @param from old, existing & unchanged PO + * @param to new, not saved PO + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + protected static void copyValues (PO from, PO to, int AD_Client_ID, int AD_Org_ID) + { + copyValues (from, to); + to.setAD_Client_ID(AD_Client_ID); + to.setAD_Org_ID(AD_Org_ID); + } // copyValues + + /** + * Copy old values of From to new values of To. + * Does not copy Keys and AD_Client_ID/AD_Org_ID + * @param from old, existing & unchanged PO + * @param to new, not saved PO + */ + public static void copyValues (PO from, PO to) + { + s_log.fine("From ID=" + from.get_ID() + " - To ID=" + to.get_ID()); + // Different Classes + if (from.getClass() != to.getClass()) + { + for (int i1 = 0; i1 < from.m_oldValues.length; i1++) + { + if (from.p_info.isVirtualColumn(i1) + || from.p_info.isKey(i1)) // KeyColumn + continue; + String colName = from.p_info.getColumnName(i1); + // Ignore Standard Values + if (colName.startsWith("Created") + || colName.startsWith("Updated") + || colName.equals("IsActive") + || colName.equals("AD_Client_ID") + || colName.equals("AD_Org_ID") + || colName.equals("Processing") + ) + ; // ignore + else + { + for (int i2 = 0; i2 < to.m_oldValues.length; i2++) + { + if (to.p_info.getColumnName(i2).equals(colName)) + { + to.m_newValues[i2] = from.m_oldValues[i1]; + break; + } + } + } + } // from loop + } + else // same class + { + for (int i = 0; i < from.m_oldValues.length; i++) + { + if (from.p_info.isVirtualColumn(i) + || from.p_info.isKey(i)) // KeyColumn + continue; + String colName = from.p_info.getColumnName(i); + // Ignore Standard Values + if (colName.startsWith("Created") + || colName.startsWith("Updated") + || colName.equals("IsActive") + || colName.equals("AD_Client_ID") + || colName.equals("AD_Org_ID") + || colName.equals("Processing") + ) + ; // ignore + else + to.m_newValues[i] = from.m_oldValues[i]; + } + } // same class + } // copy + + + /************************************************************************** + * Load record with ID + * @param ID ID + * @param trxName transaction name + */ + protected void load (int ID, String trxName) + { + log.finest("ID=" + ID); + if (ID > 0) + { + m_IDs = new Object[] {new Integer(ID)}; + m_KeyColumns = new String[] {p_info.getTableName() + "_ID"}; + load(trxName); + } + else // new + { + loadDefaults(); + m_createNew = true; + setKeyInfo(); // sets m_IDs + loadComplete(true); + } + } // load + + + /** + * (re)Load record with m_ID[*] + * @param trxName transaction + * @return true if loaded + */ + public boolean load (String trxName) + { + m_trxName = trxName; + boolean success = true; + StringBuffer sql = new StringBuffer("SELECT "); + int size = get_ColumnCount(); + for (int i = 0; i < size; i++) + { + if (i != 0) + sql.append(","); + sql.append(p_info.getColumnSQL(i)); // Normal and Virtual Column + } + sql.append(" FROM ").append(p_info.getTableName()) + .append(" WHERE ") + .append(get_WhereClause(false)); + + // + // int index = -1; + if (CLogMgt.isLevelFinest()) + log.finest(get_WhereClause(true)); + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql.toString(), m_trxName); // local trx only + for (int i = 0; i < m_IDs.length; i++) + { + Object oo = m_IDs[i]; + if (oo instanceof Integer) + pstmt.setInt(i+1, ((Integer)m_IDs[i]).intValue()); + else + pstmt.setString(i+1, m_IDs[i].toString()); + } + rs = pstmt.executeQuery(); + if (rs.next()) + { + success = load(rs); + } + else + { + log.log(Level.SEVERE, "NO Data found for " + get_WhereClause(true), new Exception()); + m_IDs = new Object[] {I_ZERO}; + success = false; + // throw new DBException("NO Data found for " + get_WhereClause(true)); + } + m_createNew = false; + // reset new values + m_newValues = new Object[size]; + } + catch (Exception e) + { + String msg = ""; + if (m_trxName != null) + msg = "[" + m_trxName + "] - "; + msg += get_WhereClause(true) + // + ", Index=" + index + // + ", Column=" + get_ColumnName(index) + // + ", " + p_info.toString(index) + + ", SQL=" + sql.toString(); + success = false; + m_IDs = new Object[] {I_ZERO}; + log.log(Level.SEVERE, msg, e); + // throw new DBException(e); + } + // Finish + finally { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + loadComplete(success); + return success; + } // load + + + /** + * Load from the current position of a ResultSet + * @param rs result set + * @return true if loaded + */ + protected boolean load (ResultSet rs) + { + int size = get_ColumnCount(); + boolean success = true; + int index = 0; + log.finest("(rs)"); + // load column values + for (index = 0; index < size; index++) + { + String columnName = p_info.getColumnName(index); + Class clazz = p_info.getColumnClass(index); + int dt = p_info.getColumnDisplayType(index); + try + { + if (clazz == Integer.class) + m_oldValues[index] = decrypt(index, new Integer(rs.getInt(columnName))); + else if (clazz == BigDecimal.class) + m_oldValues[index] = decrypt(index, rs.getBigDecimal(columnName)); + else if (clazz == Boolean.class) + m_oldValues[index] = new Boolean ("Y".equals(decrypt(index, rs.getString(columnName)))); + else if (clazz == Timestamp.class) + m_oldValues[index] = decrypt(index, rs.getTimestamp(columnName)); + else if (DisplayType.isLOB(dt)) + m_oldValues[index] = get_LOB (rs.getObject(columnName)); + else if (clazz == String.class) + m_oldValues[index] = decrypt(index, rs.getString(columnName)); + else + m_oldValues[index] = loadSpecial(rs, index); + // NULL + if (rs.wasNull() && m_oldValues[index] != null) + m_oldValues[index] = null; + // + if (CLogMgt.isLevelAll()) + log.finest(String.valueOf(index) + ": " + p_info.getColumnName(index) + + "(" + p_info.getColumnClass(index) + ") = " + m_oldValues[index]); + } + catch (SQLException e) + { + if (p_info.isVirtualColumn(index)) // if rs constructor used + log.log(Level.FINER, "Virtual Column not loaded: " + columnName); + else + { + log.log(Level.SEVERE, "(rs) - " + String.valueOf(index) + + ": " + p_info.getTableName() + "." + p_info.getColumnName(index) + + " (" + p_info.getColumnClass(index) + ") - " + e); + success = false; + } + } + } + m_createNew = false; + setKeyInfo(); + loadComplete(success); + return success; + } // load + + /** + * Load from HashMap + * @param hmIn hash map + * @return true if loaded + */ + protected boolean load (HashMap hmIn) + { + int size = get_ColumnCount(); + boolean success = true; + int index = 0; + log.finest("(hm)"); + // load column values + for (index = 0; index < size; index++) + { + String columnName = p_info.getColumnName(index); + String value = (String)hmIn.get(columnName); + if (value == null) + continue; + Class clazz = p_info.getColumnClass(index); + int dt = p_info.getColumnDisplayType(index); + try + { + if (clazz == Integer.class) + m_oldValues[index] = new Integer(value); + else if (clazz == BigDecimal.class) + m_oldValues[index] = new BigDecimal(value); + else if (clazz == Boolean.class) + m_oldValues[index] = new Boolean ("Y".equals(value)); + else if (clazz == Timestamp.class) + m_oldValues[index] = Timestamp.valueOf(value); + else if (DisplayType.isLOB(dt)) + m_oldValues[index] = null; // get_LOB (rs.getObject(columnName)); + else if (clazz == String.class) + m_oldValues[index] = value; + else + m_oldValues[index] = null; // loadSpecial(rs, index); + // + if (CLogMgt.isLevelAll()) + log.finest(String.valueOf(index) + ": " + p_info.getColumnName(index) + + "(" + p_info.getColumnClass(index) + ") = " + m_oldValues[index]); + } + catch (Exception e) + { + if (p_info.isVirtualColumn(index)) // if rs constructor used + log.log(Level.FINER, "Virtual Column not loaded: " + columnName); + else + { + log.log(Level.SEVERE, "(ht) - " + String.valueOf(index) + + ": " + p_info.getTableName() + "." + p_info.getColumnName(index) + + " (" + p_info.getColumnClass(index) + ") - " + e); + success = false; + } + } + } + m_createNew = false; + // Overwrite + setStandardDefaults(); + setKeyInfo(); + loadComplete(success); + return success; + } // load + + /** + * Create Hashmap with data as Strings + * @return HashMap + */ + protected HashMap get_HashMap() + { + HashMap hmOut = new HashMap(); + int size = get_ColumnCount(); + for (int i = 0; i < size; i++) + { + Object value = get_Value(i); + // Don't insert NULL values (allows Database defaults) + if (value == null + || p_info.isVirtualColumn(i)) + continue; + // Display Type + int dt = p_info.getColumnDisplayType(i); + // Based on class of definition, not class of value + Class c = p_info.getColumnClass(i); + String stringValue = null; + if (c == Object.class) + ; // saveNewSpecial (value, i)); + else if (value == null || value.equals (Null.NULL)) + ; + else if (value instanceof Integer || value instanceof BigDecimal) + stringValue = value.toString(); + else if (c == Boolean.class) + { + boolean bValue = false; + if (value instanceof Boolean) + bValue = ((Boolean)value).booleanValue(); + else + bValue = "Y".equals(value); + stringValue = bValue ? "Y" : "N"; + } + else if (value instanceof Timestamp) + stringValue = value.toString(); + else if (c == String.class) + stringValue = (String)value; + else if (DisplayType.isLOB(dt)) + ; + else + ; // saveNewSpecial (value, i)); + // + if (stringValue != null) + hmOut.put(p_info.getColumnName(i), stringValue); + } + // Custom Columns + if (m_custom != null) + { + Iterator it = m_custom.keySet().iterator(); + while (it.hasNext()) + { + String column = (String)it.next(); +// int index = p_info.getColumnIndex(column); + String value = (String)m_custom.get(column); + if (value != null) + hmOut.put(column, value); + } + m_custom = null; + } + return hmOut; + } // get_HashMap + + /** + * Load Special data (images, ..). + * To be extended by sub-classes + * @param rs result set + * @param index zero based index + * @return value value + * @throws SQLException + */ + protected Object loadSpecial (ResultSet rs, int index) throws SQLException + { + log.finest("(NOP) - " + p_info.getColumnName(index)); + return null; + } // loadSpecial + + /** + * Load is complete + * @param success success + * To be extended by sub-classes + */ + protected void loadComplete (boolean success) + { + } // loadComplete + + + /** + * Load Defaults + */ + protected void loadDefaults() + { + setStandardDefaults(); + // + /** @todo defaults from Field */ + // MField.getDefault(p_info.getDefaultLogic(i)); + } // loadDefaults + + /** + * Set Default values. + * Client, Org, Created/Updated, *By, IsActive + */ + protected void setStandardDefaults() + { + int size = get_ColumnCount(); + for (int i = 0; i < size; i++) + { + if (p_info.isVirtualColumn(i)) + continue; + String colName = p_info.getColumnName(i); + // Set Standard Values + if (colName.endsWith("tedBy")) + m_newValues[i] = new Integer (Env.getContextAsInt(p_ctx, "#AD_User_ID")); + else if (colName.equals("Created") || colName.equals("Updated")) + m_newValues[i] = new Timestamp (System.currentTimeMillis()); + else if (colName.equals(p_info.getTableName() + "_ID")) // KeyColumn + m_newValues[i] = I_ZERO; + else if (colName.equals("IsActive")) + m_newValues[i] = new Boolean(true); + else if (colName.equals("AD_Client_ID")) + m_newValues[i] = new Integer(Env.getAD_Client_ID(p_ctx)); + else if (colName.equals("AD_Org_ID")) + m_newValues[i] = new Integer(Env.getAD_Org_ID(p_ctx)); + else if (colName.equals("Processed")) + m_newValues[i] = new Boolean(false); + else if (colName.equals("Processing")) + m_newValues[i] = new Boolean(false); + else if (colName.equals("Posted")) + m_newValues[i] = new Boolean(false); + } + } // setDefaults + + /** + * Set Key Info (IDs and KeyColumns). + */ + private void setKeyInfo() + { + // Search for Primary Key + for (int i = 0; i < p_info.getColumnCount(); i++) + { + if (p_info.isKey(i)) + { + String ColumnName = p_info.getColumnName(i); + m_KeyColumns = new String[] {ColumnName}; + if (p_info.getColumnName(i).endsWith("_ID")) + { + Integer ii = (Integer)get_Value(i); + if (ii == null) + m_IDs = new Object[] {I_ZERO}; + else + m_IDs = new Object[] {ii}; + log.finest("(PK) " + ColumnName + "=" + ii); + } + else + { + Object oo = get_Value(i); + if (oo == null) + m_IDs = new Object[] {null}; + else + m_IDs = new Object[] {oo}; + log.finest("(PK) " + ColumnName + "=" + oo); + } + return; + } + } // primary key search + + // Search for Parents + ArrayList columnNames = new ArrayList(); + for (int i = 0; i < p_info.getColumnCount(); i++) + { + if (p_info.isColumnParent(i)) + columnNames.add(p_info.getColumnName(i)); + } + // Set FKs + int size = columnNames.size(); + if (size == 0) + throw new IllegalStateException("No PK nor FK - " + p_info.getTableName()); + m_IDs = new Object[size]; + m_KeyColumns = new String[size]; + for (int i = 0; i < size; i++) + { + m_KeyColumns[i] = (String)columnNames.get(i); + if (m_KeyColumns[i].endsWith("_ID")) + { + Integer ii = null; + try + { + ii = (Integer)get_Value(m_KeyColumns[i]); + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } + if (ii != null) + m_IDs[i] = ii; + } + else + m_IDs[i] = get_Value(m_KeyColumns[i]); + log.finest("(FK) " + m_KeyColumns[i] + "=" + m_IDs[i]); + } + } // setKeyInfo + + + /************************************************************************** + * Are all mandatory Fields filled (i.e. can we save)?. + * Stops at first null mandatory field + * @return true if all mandatory fields are ok + */ + protected boolean isMandatoryOK() + { + int size = get_ColumnCount(); + for (int i = 0; i < size; i++) + { + if (p_info.isColumnMandatory(i)) + { + if (p_info.isVirtualColumn(i)) + continue; + if (get_Value(i) == null || get_Value(i).equals(Null.NULL)) + { + log.info(p_info.getColumnName(i)); + return false; + } + } + } + return true; + } // isMandatoryOK + + + /************************************************************************** + * Set AD_Client + * @param AD_Client_ID client + */ + final protected void setAD_Client_ID (int AD_Client_ID) + { + set_ValueNoCheck ("AD_Client_ID", new Integer(AD_Client_ID)); + } // setAD_Client_ID + + /** + * Get AD_Client + * @return AD_Client_ID + */ + public final int getAD_Client_ID() + { + Integer ii = (Integer)get_Value("AD_Client_ID"); + if (ii == null) + return 0; + return ii.intValue(); + } // getAD_Client_ID + + /** + * Set AD_Org + * @param AD_Org_ID org + */ + final public void setAD_Org_ID (int AD_Org_ID) + { + set_ValueNoCheck ("AD_Org_ID", new Integer(AD_Org_ID)); + } // setAD_Org_ID + + /** + * Get AD_Org + * @return AD_Org_ID + */ + public int getAD_Org_ID() + { + Integer ii = (Integer)get_Value("AD_Org_ID"); + if (ii == null) + return 0; + return ii.intValue(); + } // getAD_Org_ID + + /** + * Overwrite Client Org if different + * @param AD_Client_ID client + * @param AD_Org_ID org + */ + protected void setClientOrg (int AD_Client_ID, int AD_Org_ID) + { + if (AD_Client_ID != getAD_Client_ID()) + setAD_Client_ID(AD_Client_ID); + if (AD_Org_ID != getAD_Org_ID()) + setAD_Org_ID(AD_Org_ID); + } // setClientOrg + + /** + * Overwrite Client Org if different + * @param po persistent object + */ + protected void setClientOrg (PO po) + { + setClientOrg(po.getAD_Client_ID(), po.getAD_Org_ID()); + } // setClientOrg + + /** + * Set Active + * @param active active + */ + public final void setIsActive (boolean active) + { + set_Value("IsActive", new Boolean(active)); + } // setActive + + /** + * Is Active + * @return is active + */ + public final boolean isActive() + { + Boolean bb = (Boolean)get_Value("IsActive"); + if (bb != null) + return bb.booleanValue(); + return false; + } // isActive + + /** + * Get Created + * @return created + */ + final public Timestamp getCreated() + { + return (Timestamp)get_Value("Created"); + } // getCreated + + /** + * Get Updated + * @return updated + */ + final public Timestamp getUpdated() + { + return (Timestamp)get_Value("Updated"); + } // getUpdated + + /** + * Get CreatedBy + * @return AD_User_ID + */ + final public int getCreatedBy() + { + Integer ii = (Integer)get_Value("CreatedBy"); + if (ii == null) + return 0; + return ii.intValue(); + } // getCreateddBy + + /** + * Get UpdatedBy + * @return AD_User_ID + */ + final public int getUpdatedBy() + { + Integer ii = (Integer)get_Value("UpdatedBy"); + if (ii == null) + return 0; + return ii.intValue(); + } // getUpdatedBy + + /** + * Set UpdatedBy + * @param AD_User_ID user + */ + final protected void setUpdatedBy (int AD_User_ID) + { + set_ValueNoCheck ("UpdatedBy", new Integer(AD_User_ID)); + } // setAD_User_ID + + /** + * Get Translation of column + * @param columnName + * @param AD_Language + * @return translation or null if not found + */ + protected String get_Translation (String columnName, String AD_Language) + { + if (columnName == null || AD_Language == null + || m_IDs.length > 1 || m_IDs[0].equals(I_ZERO) + || !(m_IDs[0] instanceof Integer)) + { + log.severe ("Invalid Argument: ColumnName" + columnName + + ", AD_Language=" + AD_Language + + ", ID.length=" + m_IDs.length + ", ID=" + m_IDs[0]); + return null; + } + int ID = ((Integer)m_IDs[0]).intValue(); + String retValue = null; + StringBuffer sql = new StringBuffer ("SELECT ").append(columnName) + .append(" FROM ").append(p_info.getTableName()).append("_Trl WHERE ") + .append(m_KeyColumns[0]).append("=?") + .append(" AND AD_Language=?"); + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql.toString(), get_TrxName()); + pstmt.setInt (1, ID); + pstmt.setString (2, AD_Language); + rs = pstmt.executeQuery (); + if (rs.next ()) + retValue = rs.getString(1); + } + catch (Exception e) + { + log.log(Level.SEVERE, sql.toString(), e); + } + finally { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + return retValue; + } // get_Translation + + /** + * Is new record + * @return true if new + */ + public boolean is_new() + { + if (m_createNew) + return true; + // + for (int i = 0; i < m_IDs.length; i++) + { + if (m_IDs[i].equals(I_ZERO)) + continue; + return false; // one value is non-zero + } + return true; + } // is_new + + /* + * Classes which override save() method: + * org.compiere.process.DocActionTemplate + * org.compiere.model.MClient + * org.compiere.model.MClientInfo + * org.compiere.model.MSystem + */ + /************************************************************************** + * Update Value or create new record. + * To reload call load() - not updated + * @return true if saved + */ + public boolean save() + { + CLogger.resetLast(); + boolean newRecord = is_new(); // save locally as load resets + if (!newRecord && !is_Changed()) + { + log.fine("Nothing changed - " + p_info.getTableName()); + return true; + } + + // Organization Check + if (getAD_Org_ID() == 0 + && (get_AccessLevel() == ACCESSLEVEL_ORG + || (get_AccessLevel() == ACCESSLEVEL_CLIENTORG + && MClientShare.isOrgLevelOnly(getAD_Client_ID(), get_Table_ID())))) + { + log.saveError("FillMandatory", Msg.getElement(getCtx(), "AD_Org_ID")); + return false; + } + // Should be Org 0 + if (getAD_Org_ID() != 0) + { + boolean reset = get_AccessLevel() == ACCESSLEVEL_SYSTEM; + if (!reset && MClientShare.isClientLevelOnly(getAD_Client_ID(), get_Table_ID())) + { + reset = get_AccessLevel() == ACCESSLEVEL_CLIENT + || get_AccessLevel() == ACCESSLEVEL_SYSTEMCLIENT + || get_AccessLevel() == ACCESSLEVEL_CLIENTORG; + } + if (reset) + { + log.warning("Set Org to 0"); + setAD_Org_ID(0); + } + } + + Trx localTrx = null; + if (m_trxName == null) { + m_trxName = Trx.createTrxName("POSave"); + localTrx = Trx.get(m_trxName, true); + } + + // Before Save + try + { + if (!beforeSave(newRecord)) + { + log.warning("beforeSave failed - " + toString()); + if (localTrx != null) { + localTrx.rollback(); + localTrx.close(); + m_trxName = null; + } + return false; + } + } + catch (Exception e) + { + log.log(Level.WARNING, "beforeSave - " + toString(), e); + log.saveError("Error", e.toString(), false); + if (localTrx != null) { + localTrx.rollback(); + localTrx.close(); + m_trxName = null; + } + return false; + } + + try { + // Call ModelValidators TYPE_NEW/TYPE_CHANGE + String errorMsg = ModelValidationEngine.get().fireModelChange + (this, newRecord ? ModelValidator.TYPE_NEW : ModelValidator.TYPE_CHANGE); + if (errorMsg != null) + { + log.warning("Validation failed - " + errorMsg); + log.saveError("Error", errorMsg); + if (localTrx != null) { + localTrx.rollback(); + m_trxName = null; + } + return false; + } + // Save + if (newRecord) + { + boolean b = saveNew(); + if (b) + { + if (localTrx != null) + return localTrx.commit(); + else + return b; + } + else + { + if (localTrx != null) + localTrx.rollback(); + return b; + } + } + else + { + boolean b = saveUpdate(); + if (b) + { + if (localTrx != null) + return localTrx.commit(); + else + return b; + } + else + { + if (localTrx != null) + localTrx.rollback(); + return b; + } + } + } finally { + if (localTrx != null) + { + localTrx.close(); + m_trxName = null; + } + } + } // save + + /** + * Finish Save Process + * @param newRecord new + * @param success success + * @return true if saved + */ + private boolean saveFinish (boolean newRecord, boolean success) + { + // Translations + if (success) + { + if (newRecord) + insertTranslations(); + else + updateTranslations(); + } + // + try + { + success = afterSave (newRecord, success); + } + catch (Exception e) + { + log.log(Level.WARNING, "afterSave", e); + log.saveError("Error", e.toString(), false); + success = false; + // throw new DBException(e); + } + // Call ModelValidators TYPE_AFTER_NEW/TYPE_AFTER_CHANGE - teo_sarca [ 1675490 ] + if (success) { + String errorMsg = ModelValidationEngine.get().fireModelChange + (this, newRecord ? ModelValidator.TYPE_AFTER_NEW : ModelValidator.TYPE_AFTER_CHANGE); + if (errorMsg != null) { + log.saveError("Error", errorMsg); + success = false; + } + } + // OK + if (success) + { + if (s_docWFMgr == null) + { + try + { + Class.forName("org.compiere.wf.DocWorkflowManager"); + } + catch (Exception e) + { + } + } + if (s_docWFMgr != null) + s_docWFMgr.process (this, p_info.getAD_Table_ID()); + + // Copy to Old values + int size = p_info.getColumnCount(); + for (int i = 0; i < size; i++) + { + if (m_newValues[i] != null) + { + if (m_newValues[i] == Null.NULL) + m_oldValues[i] = null; + else + m_oldValues[i] = m_newValues[i]; + } + } + m_newValues = new Object[size]; + } + m_createNew = false; + if (!newRecord) + CacheMgt.get().reset(p_info.getTableName()); + return success; + } // saveFinish + + /** + * Update Value or create new record. + * To reload call load() - not updated + * @param trxName transaction + * @return true if saved + */ + public boolean save (String trxName) + { + set_TrxName(trxName); + return save(); + } // save + + /** + * Is there a Change to be saved? + * @return true if record changed + */ + public boolean is_Changed() + { + int size = get_ColumnCount(); + for (int i = 0; i < size; i++) + { + // Test if the column has changed - teo_sarca [ 1704828 ] + if (is_ValueChanged(i)) + return true; + } + if (m_custom != null && m_custom.size() > 0) + return true; // there are custom columns modified + return false; + } // is_Change + + /** + * Called before Save for Pre-Save Operation + * @param newRecord new record + * @return true if record can be saved + */ + protected boolean beforeSave(boolean newRecord) + { + /** Prevents saving + log.saveError("Error", Msg.parseTranslation(getCtx(), "@C_Currency_ID@ = @C_Currency_ID@")); + log.saveError("FillMandatory", Msg.getElement(getCtx(), "PriceEntered")); + /** Issues message + log.saveWarning(AD_Message, message); + log.saveInfo (AD_Message, message); + **/ + return true; + } // beforeSave + + /** + * Called after Save for Post-Save Operation + * @param newRecord new record + * @param success true if save operation was success + * @return if save was a success + */ + protected boolean afterSave (boolean newRecord, boolean success) + { + return success; + } // afterSave + + /** + * Update Record directly + * @return true if updated + */ + protected boolean saveUpdate() + { + String where = get_WhereClause(true); + // + boolean changes = false; + StringBuffer sql = new StringBuffer ("UPDATE "); + sql.append(p_info.getTableName()).append( " SET "); + boolean updated = false; + boolean updatedBy = false; + lobReset(); + + // Change Log + MSession session = MSession.get (p_ctx, false); + if (session == null) + log.fine("No Session found"); + int AD_ChangeLog_ID = 0; + + int size = get_ColumnCount(); + for (int i = 0; i < size; i++) + { + Object value = m_newValues[i]; + if (value == null + || p_info.isVirtualColumn(i)) + continue; + // we have a change + Class c = p_info.getColumnClass(i); + int dt = p_info.getColumnDisplayType(i); + String columnName = p_info.getColumnName(i); + // + // updated/by + if (columnName.equals("UpdatedBy")) + { + if (updatedBy) // explicit + continue; + updatedBy = true; + } + else if (columnName.equals("Updated")) + { + if (updated) + continue; + updated = true; + } + if (DisplayType.isLOB(dt)) + { + lobAdd (value, i, dt); + // If no changes set UpdatedBy explicitly to ensure commit of lob + if (!changes && !updatedBy) + { + int AD_User_ID = Env.getContextAsInt(p_ctx, "#AD_User_ID"); + set_ValueNoCheck("UpdatedBy", new Integer(AD_User_ID)); + sql.append("UpdatedBy=").append(AD_User_ID); + changes = true; + updatedBy = true; + } + continue; + } + // Update Document No + if (columnName.equals("DocumentNo")) + { + String strValue = (String)value; + if (strValue.startsWith("<") && strValue.endsWith(">")) + { + value = null; + int AD_Client_ID = getAD_Client_ID(); + int index = p_info.getColumnIndex("C_DocTypeTarget_ID"); + if (index == -1) + index = p_info.getColumnIndex("C_DocType_ID"); + if (index != -1) // get based on Doc Type (might return null) + value = DB.getDocumentNo(get_ValueAsInt(index), m_trxName, false, this); + if (value == null) // not overwritten by DocType and not manually entered + value = DB.getDocumentNo(AD_Client_ID, p_info.getTableName(), m_trxName, this); + } + else + log.warning("DocumentNo updated: " + m_oldValues[i] + " -> " + value); + } + + if (changes) + sql.append(", "); + changes = true; + sql.append(columnName).append("="); + + // values + if (value == Null.NULL) + sql.append("NULL"); + else if (value instanceof Integer || value instanceof BigDecimal) + sql.append(encrypt(i,value)); + else if (c == Boolean.class) + { + boolean bValue = false; + if (value instanceof Boolean) + bValue = ((Boolean)value).booleanValue(); + else + bValue = "Y".equals(value); + sql.append(encrypt(i,bValue ? "'Y'" : "'N'")); + } + else if (value instanceof Timestamp) + sql.append(DB.TO_DATE((Timestamp)encrypt(i,value),p_info.getColumnDisplayType(i) == DisplayType.Date)); + else { + if (value.toString().length() == 0) { + // [ 1722057 ] Encrypted columns throw error if saved as null + // don't encrypt NULL + sql.append(DB.TO_STRING(value.toString())); + } else { + sql.append(encrypt(i,DB.TO_STRING(value.toString()))); + } + } + + // Change Log - Only + if (session != null + && m_IDs.length == 1 + && !p_info.isEncrypted(i) // not encrypted + && !p_info.isVirtualColumn(i) // no virtual column + && !"Password".equals(columnName) + ) + { + Object oldV = m_oldValues[i]; + Object newV = value; + if (oldV != null && oldV == Null.NULL) + oldV = null; + if (newV != null && newV == Null.NULL) + newV = null; + // change log on update + MChangeLog cLog = session.changeLog ( + m_trxName, AD_ChangeLog_ID, + p_info.getAD_Table_ID(), p_info.getColumn(i).AD_Column_ID, + get_ID(), getAD_Client_ID(), getAD_Org_ID(), oldV, newV, MChangeLog.EVENTCHANGELOG_Update); + if (cLog != null) + AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); + } + } // for all fields + + // Custom Columns (cannot be logged as no column) + if (m_custom != null) + { + Iterator it = m_custom.keySet().iterator(); + while (it.hasNext()) + { + if (changes) + sql.append(", "); + changes = true; + // + String column = (String)it.next(); + String value = (String)m_custom.get(column); + int index = p_info.getColumnIndex(column); + sql.append(column).append("=").append(encrypt(index,value)); + } + m_custom = null; + } + + // Something changed + if (changes) + { + if (m_trxName == null) + log.fine(p_info.getTableName() + "." + where); + else + log.fine("[" + m_trxName + "] - " + p_info.getTableName() + "." + where); + if (!updated) // Updated not explicitly set + { + Timestamp now = new Timestamp(System.currentTimeMillis()); + set_ValueNoCheck("Updated", now); + sql.append(",Updated=").append(DB.TO_DATE(now, false)); + } + if (!updatedBy) // UpdatedBy not explicitly set + { + int AD_User_ID = Env.getContextAsInt(p_ctx, "#AD_User_ID"); + set_ValueNoCheck("UpdatedBy", new Integer(AD_User_ID)); + sql.append(",UpdatedBy=").append(AD_User_ID); + } + sql.append(" WHERE ").append(where); + /** @todo status locking goes here */ + + log.finest(sql.toString()); + int no = DB.executeUpdate(sql.toString(), m_trxName); + boolean ok = no == 1; + if (ok) + ok = lobSave(); + else + { + if (m_trxName == null) + log.log(Level.WARNING, "#" + no + + " - " + p_info.getTableName() + "." + where); + else + log.log(Level.WARNING, "#" + no + + " - [" + m_trxName + "] - " + p_info.getTableName() + "." + where); + } + return saveFinish (false, ok); + } + + // nothing changed, so OK + return saveFinish (false, true); + } // saveUpdate + + /** + * Create New Record + * @return true if new record inserted + */ + private boolean saveNew() + { + // Set ID for single key - Multi-Key values need explicitly be set previously + if (m_IDs.length == 1 && p_info.hasKeyColumn() + && m_KeyColumns[0].endsWith("_ID")) // AD_Language, EntityType + { + int no = saveNew_getID(); + if (no <= 0) + no = DB.getNextID(getAD_Client_ID(), p_info.getTableName(), m_trxName); + if (no <= 0) + { + log.severe("No NextID (" + no + ")"); + return saveFinish (true, false); + } + m_IDs[0] = new Integer(no); + set_ValueNoCheck(m_KeyColumns[0], m_IDs[0]); + } + if (m_trxName == null) + log.fine(p_info.getTableName() + " - " + get_WhereClause(true)); + else + log.fine("[" + m_trxName + "] - " + p_info.getTableName() + " - " + get_WhereClause(true)); + + // Set new DocumentNo + String columnName = "DocumentNo"; + int index = p_info.getColumnIndex(columnName); + if (index != -1) + { + String value = (String)get_Value(index); + if (value != null && value.startsWith("<") && value.endsWith(">")) + value = null; + if (value == null || value.length() == 0) + { + int dt = p_info.getColumnIndex("C_DocTypeTarget_ID"); + if (dt == -1) + dt = p_info.getColumnIndex("C_DocType_ID"); + if (dt != -1) // get based on Doc Type (might return null) + value = DB.getDocumentNo(get_ValueAsInt(dt), m_trxName, false, this); + if (value == null) // not overwritten by DocType and not manually entered + value = DB.getDocumentNo(getAD_Client_ID(), p_info.getTableName(), m_trxName, this); + set_ValueNoCheck(columnName, value); + } + } + // Set empty Value + columnName = "Value"; + index = p_info.getColumnIndex(columnName); + if (index != -1) + { + String value = (String)get_Value(index); + if (value == null || value.length() == 0) + { + value = DB.getDocumentNo (getAD_Client_ID(), p_info.getTableName(), m_trxName, this); + set_ValueNoCheck(columnName, value); + } + } + + lobReset(); + + // Change Log + MSession session = MSession.get (p_ctx, false); + if (session == null) + log.fine("No Session found"); + int AD_ChangeLog_ID = 0; + + // SQL + StringBuffer sqlInsert = new StringBuffer("INSERT INTO "); + sqlInsert.append(p_info.getTableName()).append(" ("); + StringBuffer sqlValues = new StringBuffer(") VALUES ("); + int size = get_ColumnCount(); + boolean doComma = false; + for (int i = 0; i < size; i++) + { + Object value = get_Value(i); + // Don't insert NULL values (allows Database defaults) + if (value == null + || p_info.isVirtualColumn(i)) + continue; + + // Display Type + int dt = p_info.getColumnDisplayType(i); + if (DisplayType.isLOB(dt)) + { + lobAdd (value, i, dt); + continue; + } + + // ** add column ** + if (doComma) + { + sqlInsert.append(","); + sqlValues.append(","); + } + else + doComma = true; + sqlInsert.append(p_info.getColumnName(i)); + // + // Based on class of definition, not class of value + Class c = p_info.getColumnClass(i); + try + { + if (c == Object.class) // may have need to deal with null values differently + sqlValues.append (saveNewSpecial (value, i)); + else if (value == null || value.equals (Null.NULL)) + sqlValues.append ("NULL"); + else if (value instanceof Integer || value instanceof BigDecimal) + sqlValues.append (encrypt(i,value)); + else if (c == Boolean.class) + { + boolean bValue = false; + if (value instanceof Boolean) + bValue = ((Boolean)value).booleanValue(); + else + bValue = "Y".equals(value); + sqlValues.append (encrypt(i,bValue ? "'Y'" : "'N'")); + } + else if (value instanceof Timestamp) + sqlValues.append (DB.TO_DATE ((Timestamp)encrypt(i,value), p_info.getColumnDisplayType (i) == DisplayType.Date)); + else if (c == String.class) + sqlValues.append (encrypt(i,DB.TO_STRING ((String)value))); + else if (DisplayType.isLOB(dt)) + sqlValues.append("null"); // no db dependent stuff here + else + sqlValues.append (saveNewSpecial (value, i)); + } + catch (Exception e) + { + String msg = ""; + if (m_trxName != null) + msg = "[" + m_trxName + "] - "; + msg += p_info.toString(i) + + " - Value=" + value + + "(" + (value==null ? "null" : value.getClass().getName()) + ")"; + log.log(Level.SEVERE, msg, e); + throw new DBException(e); // fini + } + + // Change Log - Only + String insertLog = MSysConfig.getValue("SYSTEM_INSERT_CHANGELOG", "Y", getAD_Client_ID()); + if ( session != null + && m_IDs.length == 1 + && !p_info.isEncrypted(i) // not encrypted + && !p_info.isVirtualColumn(i) // no virtual column + && !"Password".equals(columnName) + && (insertLog.equalsIgnoreCase("Y") + || (insertLog.equalsIgnoreCase("K") && p_info.getColumn(i).IsKey)) + ) + { + // change log on new + MChangeLog cLog = session.changeLog ( + m_trxName, AD_ChangeLog_ID, + p_info.getAD_Table_ID(), p_info.getColumn(i).AD_Column_ID, + get_ID(), getAD_Client_ID(), getAD_Org_ID(), null, value, MChangeLog.EVENTCHANGELOG_Insert); + if (cLog != null) + AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); + } + + } + // Custom Columns + if (m_custom != null) + { + Iterator it = m_custom.keySet().iterator(); + while (it.hasNext()) + { + String column = (String)it.next(); + index = p_info.getColumnIndex(column); + String value = (String)m_custom.get(column); + if (doComma) + { + sqlInsert.append(","); + sqlValues.append(","); + } + else + doComma = true; + sqlInsert.append(column); + //jz for ad_issue, some value may include ' in a string??? + sqlValues.append(encrypt(index, value)); + } + m_custom = null; + } + sqlInsert.append(sqlValues) + .append(")"); + // + int no = DB.executeUpdate(sqlInsert.toString(), m_trxName); + boolean ok = no == 1; + if (ok) + { + ok = lobSave(); + if (!load(m_trxName)) // re-read Info + { + if (m_trxName == null) + log.log(Level.SEVERE, "reloading"); + else + log.log(Level.SEVERE, "[" + m_trxName + "] - reloading"); + ok = false;; + } + } + else + { + String msg = "Not inserted - "; + if (CLogMgt.isLevelFiner()) + msg += sqlInsert.toString(); + else + msg += get_TableName(); + if (m_trxName == null) + log.log(Level.WARNING, msg); + else + log.log(Level.WARNING, "[" + m_trxName + "]" + msg); + } + return saveFinish (true, ok); + } // saveNew + + /** + * Get ID for new record during save. + * You can overwite this to explicitly set the ID + * @return ID to be used or 0 for fedault logic + */ + protected int saveNew_getID() + { + return 0; + } // saveNew_getID + + + /** + * Create Single/Multi Key Where Clause + * @param withValues if true uses actual values otherwise ? + * @return where clause + */ + public String get_WhereClause (boolean withValues) + { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < m_IDs.length; i++) + { + if (i != 0) + sb.append(" AND "); + sb.append(m_KeyColumns[i]).append("="); + if (withValues) + { + if (m_KeyColumns[i].endsWith("_ID")) + sb.append(m_IDs[i]); + else + sb.append("'").append(m_IDs[i]).append("'"); + } + else + sb.append("?"); + } + return sb.toString(); + } // getWhereClause + + + /** + * Save Special Data. + * To be extended by sub-classes + * @param value value + * @param index index + * @return SQL code for INSERT VALUES clause + */ + protected String saveNewSpecial (Object value, int index) + { + String colName = p_info.getColumnName(index); + String colClass = p_info.getColumnClass(index).toString(); + String colValue = value == null ? "null" : value.getClass().toString(); +// int dt = p_info.getColumnDisplayType(index); + + log.log(Level.SEVERE, "Unknown class for column " + colName + + " (" + colClass + ") - Value=" + colValue); + + if (value == null) + return "NULL"; + return value.toString(); + } // saveNewSpecial + + /** + * Encrypt data. + * Not: LOB, special values/Obkects + * @param index index + * @param xx data + * @return xx + */ + private Object encrypt (int index, Object xx) + { + if (xx == null) + return null; + if (index != -1 && p_info.isEncrypted(index)) + return SecureEngine.encrypt(xx); + return xx; + } // encrypt + + /** + * Decrypt data + * @param index index + * @param yy data + * @return yy + */ + private Object decrypt (int index, Object yy) + { + if (yy == null) + return null; + if (index != -1 && p_info.isEncrypted(index)) + return SecureEngine.decrypt(yy); + return yy; + } // decrypt + + /************************************************************************** + * Delete Current Record + * @param force delete also processed records + * @return true if deleted + */ + public boolean delete (boolean force) + { + CLogger.resetLast(); + if (is_new()) + return true; + + int AD_Table_ID = p_info.getAD_Table_ID(); + int Record_ID = get_ID(); + + if (!force) + { + int iProcessed = get_ColumnIndex("Processed"); + if (iProcessed != -1) + { + Boolean processed = (Boolean)get_Value(iProcessed); + if (processed != null && processed.booleanValue()) + { + log.warning("Record processed"); // CannotDeleteTrx + log.saveError("Processed", "Processed", false); + return false; + } + } // processed + } // force + + Trx localTrx = null; + boolean success = false; + try + { + + String localTrxName = m_trxName; + if (localTrxName == null) + { + localTrxName = Trx.createTrxName("POdel"); + localTrx = Trx.get(localTrxName, true); + m_trxName = localTrxName; + } + + try + { + if (!beforeDelete()) + { + log.warning("beforeDelete failed"); + return false; + } + } + catch (Exception e) + { + log.log(Level.WARNING, "beforeDelete", e); + log.saveError("Error", e.toString(), false); + // throw new DBException(e); + return false; + } + // Delete Restrict AD_Table_ID/Record_ID (Requests, ..) + String errorMsg = PO_Record.exists(AD_Table_ID, Record_ID, m_trxName); + if (errorMsg != null) + { + log.saveError("CannotDelete", errorMsg); + return false; + } + // Call ModelValidators TYPE_DELETE + errorMsg = ModelValidationEngine.get().fireModelChange + (this, ModelValidator.TYPE_DELETE); + if (errorMsg != null) + { + log.saveError("Error", errorMsg); + return false; + } + + // + deleteTranslations(localTrxName); + // Delete Cascade AD_Table_ID/Record_ID (Attachments, ..) + PO_Record.deleteCascade(AD_Table_ID, Record_ID, localTrxName); + + // The Delete Statement + StringBuffer sql = new StringBuffer ("DELETE FROM ") //jz why no FROM?? + .append(p_info.getTableName()) + .append(" WHERE ") + .append(get_WhereClause(true)); + int no = DB.executeUpdate(sql.toString(), localTrxName); + success = no == 1; + + // Save ID + m_idOld = get_ID(); + // + if (!success) + { + log.warning("Not deleted"); + if (localTrx != null) + localTrx.rollback(); + } + else + { + if (success) + { + // Change Log + MSession session = MSession.get (p_ctx, false); + if (session == null) + log.fine("No Session found"); + else if (m_IDs.length == 1) + { + int AD_ChangeLog_ID = 0; + int size = get_ColumnCount(); + for (int i = 0; i < size; i++) + { + Object value = m_oldValues[i]; + if (value != null + && !p_info.isEncrypted(i) // not encrypted + && !p_info.isVirtualColumn(i) // no virtual column + && !"Password".equals(p_info.getColumnName(i)) + ) + { + // change log on delete + MChangeLog cLog = session.changeLog ( + m_trxName != null ? m_trxName : localTrxName, AD_ChangeLog_ID, + AD_Table_ID, p_info.getColumn(i).AD_Column_ID, + Record_ID, getAD_Client_ID(), getAD_Org_ID(), value, null, MChangeLog.EVENTCHANGELOG_Delete); + if (cLog != null) + AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); + } + } // for all fields + } + + // Housekeeping + m_IDs[0] = I_ZERO; + if (m_trxName == null) + log.fine("complete"); + else + log.fine("[" + m_trxName + "] - complete"); + m_attachment = null; + } + else + { + log.warning("Not deleted"); + } + } + + try + { + success = afterDelete (success); + } + catch (Exception e) + { + log.log(Level.WARNING, "afterDelete", e); + log.saveError("Error", e.toString(), false); + success = false; + // throw new DBException(e); + } + + // Call ModelValidators TYPE_AFTER_DELETE - teo_sarca [ 1675490 ] + if (success) { + errorMsg = ModelValidationEngine.get().fireModelChange(this, ModelValidator.TYPE_AFTER_DELETE); + if (errorMsg != null) { + log.saveError("Error", errorMsg); + success = false; + } + } + + if (!success) + { + if (localTrx != null) + localTrx.rollback(); + } + else + { + if (localTrx != null) + { + try { + localTrx.commit(true); + } catch (SQLException e) { + log.saveError("Error", e); + success = false; + } + } + } + + // Reset + if (success) + { + m_idOld = 0; + int size = p_info.getColumnCount(); + m_oldValues = new Object[size]; + m_newValues = new Object[size]; + CacheMgt.get().reset(p_info.getTableName()); + } + } + finally + { + if (localTrx != null) + { + localTrx.close(); + m_trxName = null; + } + } + // log.info("" + success); + return success; + } // delete + + /** + * Delete Current Record + * @param force delete also processed records + * @param trxName transaction + * @return true if deleted + */ + public boolean delete (boolean force, String trxName) + { + set_TrxName(trxName); + return delete (force); + } // delete + + /** + * Executed before Delete operation. + * @return true if record can be deleted + */ + protected boolean beforeDelete () + { + // log.saveError("Error", Msg.getMsg(getCtx(), "CannotDelete")); + return true; + } // beforeDelete + + /** + * Executed after Delete operation. + * @param success true if record deleted + * @return true if delete is a success + */ + protected boolean afterDelete (boolean success) + { + return success; + } // afterDelete + + + /** + * Insert (missing) Translation Records + * @return false if error (true if no translation or success) + */ + private boolean insertTranslations() + { + // Not a translation table + if (m_IDs.length > 1 + || m_IDs[0].equals(I_ZERO) + || !p_info.isTranslated() + || !(m_IDs[0] instanceof Integer)) + return true; + // + StringBuffer iColumns = new StringBuffer(); + StringBuffer sColumns = new StringBuffer(); + for (int i = 0; i < p_info.getColumnCount(); i++) + { + if (p_info.isColumnTranslated(i)) + { + iColumns.append(p_info.getColumnName(i)) + .append(","); + sColumns.append("t.") + .append(p_info.getColumnName(i)) + .append(","); + } + } + if (iColumns.length() == 0) + return true; + + String tableName = p_info.getTableName(); + String keyColumn = m_KeyColumns[0]; + StringBuffer sql = new StringBuffer ("INSERT INTO ") + .append(tableName).append("_Trl (AD_Language,") + .append(keyColumn).append(", ") + .append(iColumns) + .append(" IsTranslated,AD_Client_ID,AD_Org_ID,Created,Createdby,Updated,UpdatedBy) ") + .append("SELECT l.AD_Language,t.") + .append(keyColumn).append(", ") + .append(sColumns) + .append(" 'N',t.AD_Client_ID,t.AD_Org_ID,t.Created,t.Createdby,t.Updated,t.UpdatedBy ") + .append("FROM AD_Language l, ").append(tableName).append(" t ") + .append("WHERE l.IsActive='Y' AND l.IsSystemLanguage='Y' AND l.IsBaseLanguage='N' AND t.") + .append(keyColumn).append("=").append(get_ID()) + /*jz since derby bug, rewrite the sql + .append(" AND NOT EXISTS (SELECT * FROM ").append(tableName) + .append("_Trl tt WHERE tt.AD_Language=l.AD_Language AND tt.") + .append(keyColumn).append("=t.").append(keyColumn).append(")"); + */ + .append(" AND EXISTS (SELECT * FROM ").append(tableName) + .append("_Trl tt WHERE tt.AD_Language!=l.AD_Language OR tt.") + .append(keyColumn).append("!=t.").append(keyColumn).append(")"); + int no = DB.executeUpdate(sql.toString(), m_trxName); + log.fine("#" + no); + return no > 0; + } // insertTranslations + + /** + * Update Translations. + * @return false if error (true if no translation or success) + */ + private boolean updateTranslations() + { + // Not a translation table + if (m_IDs.length > 1 + || m_IDs[0].equals(I_ZERO) + || !p_info.isTranslated() + || !(m_IDs[0] instanceof Integer)) + return true; + // + boolean trlColumnChanged = false; + for (int i = 0; i < p_info.getColumnCount(); i++) + { + if (p_info.isColumnTranslated(i) + && is_ValueChanged(p_info.getColumnName(i))) + { + trlColumnChanged = true; + break; + } + } + if (!trlColumnChanged) + return true; + // + MClient client = MClient.get(getCtx()); + // + String tableName = p_info.getTableName(); + String keyColumn = m_KeyColumns[0]; + StringBuffer sql = new StringBuffer ("UPDATE ") + .append(tableName).append("_Trl SET "); + // + if (client.isAutoUpdateTrl(tableName)) + { + for (int i = 0; i < p_info.getColumnCount(); i++) + { + if (p_info.isColumnTranslated(i)) + { + String columnName = p_info.getColumnName(i); + sql.append(columnName).append("="); + Object value = get_Value(columnName); + if (value == null) + sql.append("NULL"); + else if (value instanceof String) + sql.append(DB.TO_STRING((String)value)); + else if (value instanceof Boolean) + sql.append(((Boolean)value).booleanValue() ? "'Y'" : "'N'"); + else if (value instanceof Timestamp) + sql.append(DB.TO_DATE((Timestamp)value)); + else + sql.append(value.toString()); + sql.append(","); + } + } + sql.append("IsTranslated='Y'"); + } + else + sql.append("IsTranslated='N'"); + // + sql.append(" WHERE ") + .append(keyColumn).append("=").append(get_ID()); + int no = DB.executeUpdate(sql.toString(), m_trxName); + log.fine("#" + no); + return no >= 0; + } // updateTranslations + + /** + * Delete Translation Records + * @param trxName transaction + * @return false if error (true if no translation or success) + */ + private boolean deleteTranslations(String trxName) + { + // Not a translation table + if (m_IDs.length > 1 + || m_IDs[0].equals(I_ZERO) + || !p_info.isTranslated() + || !(m_IDs[0] instanceof Integer)) + return true; + // + String tableName = p_info.getTableName(); + String keyColumn = m_KeyColumns[0]; + StringBuffer sql = new StringBuffer ("DELETE FROM ") + .append(tableName).append("_Trl WHERE ") + .append(keyColumn).append("=").append(get_ID()); + int no = DB.executeUpdate(sql.toString(), trxName); + log.fine("#" + no); + return no >= 0; + } // deleteTranslations + + /** + * Insert Accounting Records + * @param acctTable accounting sub table + * @param acctBaseTable acct table to get data from + * @param whereClause optional where clause with alisa "p" for acctBaseTable + * @return true if records inserted + */ + protected boolean insert_Accounting (String acctTable, + String acctBaseTable, String whereClause) + { + if (s_acctColumns == null // cannot cache C_BP_*_Acct as there are 3 + || acctTable.startsWith("C_BP_")) + { + s_acctColumns = new ArrayList(); + String sql = "SELECT c.ColumnName " + + "FROM AD_Column c INNER JOIN AD_Table t ON (c.AD_Table_ID=t.AD_Table_ID) " + + "WHERE t.TableName=? AND c.IsActive='Y' AND c.AD_Reference_ID=25 ORDER BY 1"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, null); + pstmt.setString (1, acctTable); + rs = pstmt.executeQuery (); + while (rs.next ()) + s_acctColumns.add (rs.getString(1)); + } + catch (Exception e) + { + log.log(Level.SEVERE, acctTable, e); + } + finally { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + if (s_acctColumns.size() == 0) + { + log.severe ("No Columns for " + acctTable); + return false; + } + } + + // Create SQL Statement - INSERT + StringBuffer sb = new StringBuffer("INSERT INTO ") + .append(acctTable) + .append(" (").append(get_TableName()) + .append("_ID, C_AcctSchema_ID, AD_Client_ID,AD_Org_ID,IsActive, Created,CreatedBy,Updated,UpdatedBy "); + for (int i = 0; i < s_acctColumns.size(); i++) + sb.append(",").append(s_acctColumns.get(i)); + // .. SELECT + sb.append(") SELECT ").append(get_ID()) + .append(", p.C_AcctSchema_ID, p.AD_Client_ID,0,'Y', SysDate,") + .append(getUpdatedBy()).append(",SysDate,").append(getUpdatedBy()); + for (int i = 0; i < s_acctColumns.size(); i++) + sb.append(",p.").append(s_acctColumns.get(i)); + // .. FROM + sb.append(" FROM ").append(acctBaseTable) + .append(" p WHERE p.AD_Client_ID=").append(getAD_Client_ID()); + if (whereClause != null && whereClause.length() > 0) + sb.append (" AND ").append(whereClause); + sb.append(" AND NOT EXISTS (SELECT * FROM ").append(acctTable) + .append(" e WHERE e.C_AcctSchema_ID=p.C_AcctSchema_ID AND e.") + .append(get_TableName()).append("_ID=").append(get_ID()).append(")"); + // + int no = DB.executeUpdate(sb.toString(), get_TrxName()); + if (no > 0) + log.fine("#" + no); + else + log.warning("#" + no + + " - Table=" + acctTable + " from " + acctBaseTable); + return no > 0; + } // insert_Accounting + + /** + * Delete Accounting records. + * NOP - done by database constraints + * @param acctTable accounting sub table + * @return true + */ + protected boolean delete_Accounting(String acctTable) + { + return true; + } // delete_Accounting + + + /** + * Insert id data into Tree + * @param treeType MTree TREETYPE_* + * @return true if inserted + */ + protected boolean insert_Tree (String treeType) + { + return insert_Tree (treeType, 0); + } // insert_Tree + + /** + * Insert id data into Tree + * @param treeType MTree TREETYPE_* + * @param C_Element_ID element for accounting element values + * @return true if inserted + */ + protected boolean insert_Tree (String treeType, int C_Element_ID) + { + StringBuffer sb = new StringBuffer ("INSERT INTO ") + .append(MTree_Base.getNodeTableName(treeType)) + .append(" (AD_Client_ID,AD_Org_ID, IsActive,Created,CreatedBy,Updated,UpdatedBy, " + + "AD_Tree_ID, Node_ID, Parent_ID, SeqNo) " + + "SELECT t.AD_Client_ID,0, 'Y', SysDate, 0, SysDate, 0," + + "t.AD_Tree_ID, ").append(get_ID()).append(", 0, 999 " + + "FROM AD_Tree t " + + "WHERE t.AD_Client_ID=").append(getAD_Client_ID()).append(" AND t.IsActive='Y'"); + // Account Element Value handling + if (C_Element_ID != 0) + sb.append(" AND EXISTS (SELECT * FROM C_Element ae WHERE ae.C_Element_ID=") + .append(C_Element_ID).append(" AND t.AD_Tree_ID=ae.AD_Tree_ID)"); + else // std trees + sb.append(" AND t.IsAllNodes='Y' AND t.TreeType='").append(treeType).append("'"); + // Duplicate Check + sb.append(" AND NOT EXISTS (SELECT * FROM " + MTree_Base.getNodeTableName(treeType) + " e " + + "WHERE e.AD_Tree_ID=t.AD_Tree_ID AND Node_ID=").append(get_ID()).append(")"); + int no = DB.executeUpdate(sb.toString(), get_TrxName()); + if (no > 0) + log.fine("#" + no + " - TreeType=" + treeType); + else + log.warning("#" + no + " - TreeType=" + treeType); + return no > 0; + } // insert_Tree + + /** + * Delete ID Tree Nodes + * @param treeType MTree TREETYPE_* + * @return true if deleted + */ + protected boolean delete_Tree (String treeType) + { + int id = get_ID(); + if (id == 0) + id = get_IDOld(); + StringBuffer sb = new StringBuffer ("DELETE FROM ") + .append(MTree_Base.getNodeTableName(treeType)) + .append(" n WHERE Node_ID=").append(id) + .append(" AND EXISTS (SELECT * FROM AD_Tree t " + + "WHERE t.AD_Tree_ID=n.AD_Tree_ID AND t.TreeType='") + .append(treeType).append("')"); + int no = DB.executeUpdate(sb.toString(), get_TrxName()); + if (no > 0) + log.fine("#" + no + " - TreeType=" + treeType); + else + log.warning("#" + no + " - TreeType=" + treeType); + return no > 0; + } // delete_Tree + + /************************************************************************** + * Lock it. + * @return true if locked + */ + public boolean lock() + { + int index = get_ProcessingIndex(); + if (index != -1) + { + m_newValues[index] = Boolean.TRUE; // direct + String sql = "UPDATE " + p_info.getTableName() + + " SET Processing='Y' WHERE (Processing='N' OR Processing IS NULL) AND " + + get_WhereClause(true); + boolean success = DB.executeUpdate(sql, null) == 1; // outside trx + if (success) + log.fine("success"); + else + log.log(Level.WARNING, "failed"); + return success; + } + return false; + } // lock + + /** + * Get the Column Processing index + * @return index or -1 + */ + private int get_ProcessingIndex() + { + return p_info.getColumnIndex("Processing"); + } // getProcessingIndex + + /** + * UnLock it + * @param trxName transaction + * @return true if unlocked (false only if unlock fails) + */ + public boolean unlock (String trxName) + { + // log.warning(trxName); + int index = get_ProcessingIndex(); + if (index != -1) + { + m_newValues[index] = Boolean.FALSE; // direct + String sql = "UPDATE " + p_info.getTableName() + + " SET Processing='N' WHERE " + get_WhereClause(true); + boolean success = DB.executeUpdate(sql, trxName) == 1; + if (success) + log.fine("success" + (trxName == null ? "" : "[" + trxName + "]")); + else + log.log(Level.WARNING, "failed" + (trxName == null ? "" : " [" + trxName + "]")); + return success; + } + return true; + } // unlock + + /** Optional Transaction */ + private String m_trxName = null; + + /** + * Set Trx + * @param trxName transaction + */ + public void set_TrxName (String trxName) + { + m_trxName = trxName; + } // setTrx + + /** + * Get Trx + * @return transaction + */ + public String get_TrxName() + { + return m_trxName; + } // getTrx + + + /************************************************************************** + * Get Attachments. + * An attachment may have multiple entries + * @return Attachment or null + */ + public MAttachment getAttachment () + { + return getAttachment(false); + } // getAttachment + + /** + * Get Attachments + * @param requery requery + * @return Attachment or null + */ + public MAttachment getAttachment (boolean requery) + { + if (m_attachment == null || requery) + m_attachment = MAttachment.get (getCtx(), p_info.getAD_Table_ID(), get_ID()); + return m_attachment; + } // getAttachment + + /** + * Create/return Attachment for PO. + * If not exist, create new + * @return attachment + */ + public MAttachment createAttachment() + { + getAttachment (false); + if (m_attachment == null) + m_attachment = new MAttachment (getCtx(), p_info.getAD_Table_ID(), get_ID(), null); + return m_attachment; + } // createAttachment + + + /** + * Do we have a Attachment of type + * @param extension extension e.g. .pdf + * @return true if there is a attachment of type + */ + public boolean isAttachment (String extension) + { + getAttachment (false); + if (m_attachment == null) + return false; + for (int i = 0; i < m_attachment.getEntryCount(); i++) + { + if (m_attachment.getEntryName(i).endsWith(extension)) + { + log.fine("#" + i + ": " + m_attachment.getEntryName(i)); + return true; + } + } + return false; + } // isAttachment + + /** + * Get Attachment Data of type + * @param extension extension e.g. .pdf + * @return data or null + */ + public byte[] getAttachmentData (String extension) + { + getAttachment(false); + if (m_attachment == null) + return null; + for (int i = 0; i < m_attachment.getEntryCount(); i++) + { + if (m_attachment.getEntryName(i).endsWith(extension)) + { + log.fine("#" + i + ": " + m_attachment.getEntryName(i)); + return m_attachment.getEntryData(i); + } + } + return null; + } // getAttachmentData + + /** + * Do we have a PDF Attachment + * @return true if there is a PDF attachment + */ + public boolean isPdfAttachment() + { + return isAttachment(".pdf"); + } // isPdfAttachment + + /** + * Get PDF Attachment Data + * @return data or null + */ + public byte[] getPdfAttachment() + { + return getAttachmentData(".pdf"); + } // getPDFAttachment + + + /************************************************************************** + * Dump Record + */ + public void dump () + { + if (CLogMgt.isLevelFinest()) + { + log.finer(get_WhereClause (true)); + for (int i = 0; i < get_ColumnCount (); i++) + dump (i); + } + } // dump + + /** + * Dump column + * @param index index + */ + public void dump (int index) + { + StringBuffer sb = new StringBuffer(" ").append(index); + if (index < 0 || index >= get_ColumnCount()) + { + log.finest(sb.append(": invalid").toString()); + return; + } + sb.append(": ").append(get_ColumnName(index)) + .append(" = ").append(m_oldValues[index]) + .append(" (").append(m_newValues[index]).append(")"); + log.finest(sb.toString()); + } // dump + + + /************************************************************************* + * Get All IDs of Table. + * Used for listing all Entities + * + int[] IDs = PO.getAllIDs ("AD_PrintFont", null); + for (int i = 0; i < IDs.length; i++) + { + pf = new MPrintFont(Env.getCtx(), IDs[i]); + System.out.println(IDs[i] + " = " + pf.getFont()); + } + * + * @param TableName table name (key column with _ID) + * @param WhereClause optional where clause + * @return array of IDs or null + * @param trxName transaction + */ + public static int[] getAllIDs (String TableName, String WhereClause, String trxName) + { + ArrayList list = new ArrayList(); + StringBuffer sql = new StringBuffer("SELECT "); + sql.append(TableName).append("_ID FROM ").append(TableName); + if (WhereClause != null && WhereClause.length() > 0) + sql.append(" WHERE ").append(WhereClause); + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql.toString(), trxName); + rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new Integer(rs.getInt(1))); + } + catch (SQLException e) + { + s_log.log(Level.SEVERE, sql.toString(), e); + return null; + } + finally { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // Convert to array + int[] retValue = new int[list.size()]; + for (int i = 0; i < retValue.length; i++) + retValue[i] = ((Integer)list.get(i)).intValue(); + return retValue; + } // getAllIDs + + + /** + * Get Find parameter. + * Convert to upper case and add % at the end + * @param query in string + * @return out string + */ + protected static String getFindParameter (String query) + { + if (query == null) + return null; + if (query.length() == 0 || query.equals("%")) + return null; + if (!query.endsWith("%")) + query += "%"; + return query.toUpperCase(); + } // getFindParameter + + + /************************************************************************** + * Load LOB + * @param value LOB + * @return object + */ + private Object get_LOB (Object value) + { + log.fine("Value=" + value); + if (value == null) + return null; + // + Object retValue = null; + + long length = -99; + try + { + //[ 1643996 ] Chat not working in postgres port + if (value instanceof String || + value instanceof byte[]) + retValue = value; + else if (value instanceof Clob) // returns String + { + Clob clob = (Clob)value; + length = clob.length(); + retValue = clob.getSubString(1, (int)length); + } + else if (value instanceof Blob) // returns byte[] + { + Blob blob = (Blob)value; + length = blob.length(); + int index = 1; // correct + if (blob.getClass().getName().equals("oracle.jdbc.rowset.OracleSerialBlob")) + index = 0; // Oracle Bug Invalid Arguments + // at oracle.jdbc.rowset.OracleSerialBlob.getBytes(OracleSerialBlob.java:130) + retValue = blob.getBytes(index, (int)length); + } + else + log.log(Level.SEVERE, "Unknown: " + value); + } + catch (Exception e) + { + log.log(Level.SEVERE, "Length=" + length, e); + } + return retValue; + } // getLOB + + /** LOB Info */ + private ArrayList m_lobInfo = null; + + /** + * Reset LOB info + */ + private void lobReset() + { + m_lobInfo = null; + } // resetLOB + + /** + * Prepare LOB save + * @param value value + * @param index index + * @param displayType display type + */ + private void lobAdd (Object value, int index, int displayType) + { + log.finest("Value=" + value); + PO_LOB lob = new PO_LOB (p_info.getTableName(), get_ColumnName(index), + get_WhereClause(true), displayType, value); + if (m_lobInfo == null) + m_lobInfo = new ArrayList(); + m_lobInfo.add(lob); + } // lobAdd + + /** + * Save LOB + * @return true if saved or ok + */ + private boolean lobSave () + { + if (m_lobInfo == null) + return true; + boolean retValue = true; + for (int i = 0; i < m_lobInfo.size(); i++) + { + PO_LOB lob = (PO_LOB)m_lobInfo.get(i); + if (!lob.save(get_TrxName())) + { + retValue = false; + break; + } + } // for all LOBs + lobReset(); + return retValue; + } // saveLOB + + /** + * Get Object xml representation as string + * @param xml optional string buffer + * @return updated/new string buffer header is only added once + */ + public StringBuffer get_xmlString (StringBuffer xml) + { + if (xml == null) + xml = new StringBuffer(); + else + xml.append(Env.NL); + // + try + { + StringWriter writer = new StringWriter(); + StreamResult result = new StreamResult(writer); + DOMSource source = new DOMSource(get_xmlDocument(xml.length()!=0)); + TransformerFactory tFactory = TransformerFactory.newInstance(); + Transformer transformer = tFactory.newTransformer(); + transformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes"); + transformer.transform (source, result); + StringBuffer newXML = writer.getBuffer(); + // + if (xml.length() != 0) + { // // + int tagIndex = newXML.indexOf("?>"); + if (tagIndex != -1) + xml.append(newXML.substring(tagIndex+2)); + else + xml.append(newXML); + } + else + xml.append(newXML); + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } + return xml; + } // get_xmlString + + /** Table ID Attribute */ + protected final static String XML_ATTRIBUTE_AD_Table_ID = "AD_Table_ID"; + /** Record ID Attribute */ + protected final static String XML_ATTRIBUTE_Record_ID = "Record_ID"; + + /** + * Get XML Document representation + * @param noComment do not add comment + * @return XML document + */ + public Document get_xmlDocument(boolean noComment) + { + Document document = null; + try + { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = factory.newDocumentBuilder(); + document = builder.newDocument(); + if (!noComment) + document.appendChild(document.createComment(Adempiere.getSummaryAscii())); + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } + // Root + Element root = document.createElement(get_TableName()); + root.setAttribute(XML_ATTRIBUTE_AD_Table_ID, String.valueOf(get_Table_ID())); + root.setAttribute(XML_ATTRIBUTE_Record_ID, String.valueOf(get_ID())); + document.appendChild(root); + // Columns + int size = get_ColumnCount(); + for (int i = 0; i < size; i++) + { + if (p_info.isVirtualColumn(i)) + continue; + + Element col = document.createElement(p_info.getColumnName(i)); + // + Object value = get_Value(i); + // Display Type + int dt = p_info.getColumnDisplayType(i); + // Based on class of definition, not class of value + Class c = p_info.getColumnClass(i); + if (value == null || value.equals (Null.NULL)) + ; + else if (c == Object.class) + col.appendChild(document.createCDATASection(value.toString())); + else if (value instanceof Integer || value instanceof BigDecimal) + col.appendChild(document.createTextNode(value.toString())); + else if (c == Boolean.class) + { + boolean bValue = false; + if (value instanceof Boolean) + bValue = ((Boolean)value).booleanValue(); + else + bValue = "Y".equals(value); + col.appendChild(document.createTextNode(bValue ? "Y" : "N")); + } + else if (value instanceof Timestamp) + col.appendChild(document.createTextNode(value.toString())); + else if (c == String.class) + col.appendChild(document.createCDATASection((String)value)); + else if (DisplayType.isLOB(dt)) + col.appendChild(document.createCDATASection(value.toString())); + else + col.appendChild(document.createCDATASection(value.toString())); + // + root.appendChild(col); + } + // Custom Columns + if (m_custom != null) + { + Iterator it = m_custom.keySet().iterator(); + while (it.hasNext()) + { + String columnName = (String)it.next(); +// int index = p_info.getColumnIndex(columnName); + String value = (String)m_custom.get(columnName); + // + Element col = document.createElement(columnName); + if (value != null) + col.appendChild(document.createTextNode(value)); + root.appendChild(col); + } + m_custom = null; + } + return document; + } // getDocument + + /* Doc - To be used on ModelValidator to get the corresponding Doc from the PO */ + private Doc m_doc; + + /** + * Set the accounting document associated to the PO - for use in POST ModelValidator + * @param doc Document + */ + public void setDoc(Doc doc) { + m_doc = doc; + } + + /** + * Set the accounting document associated to the PO - for use in POST ModelValidator + * @return Doc Document + */ + public Doc getDoc() { + return m_doc; + } + + /** + * PO.setTrxName - set given trxName to an array of POs + * As suggested by teo in [ 1854603 ] + */ + public static void set_TrxName(PO[] lines, String trxName) { + for (PO line : lines) + line.set_TrxName(trxName); + } + +} // PO diff --git a/base/src/org/compiere/print/layout/LayoutEngine.java b/base/src/org/compiere/print/layout/LayoutEngine.java new file mode 100644 index 0000000000..000b56e895 --- /dev/null +++ b/base/src/org/compiere/print/layout/LayoutEngine.java @@ -0,0 +1,1838 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.print.layout; + +import java.awt.*; +import java.awt.geom.*; +import java.awt.print.*; +import java.io.*; +import java.net.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import javax.print.*; +import javax.print.attribute.*; +import org.compiere.*; +import org.compiere.model.*; +import org.compiere.print.*; +import org.compiere.util.*; + +/** + * Adempiere Print Engine. + * All coordinates are relative to the Page. + * The Language setting is maintained in the format + * + * @author Jorg Janke + * @version $Id: LayoutEngine.java,v 1.3 2006/07/30 00:53:02 jjanke Exp $ + * + * @author Teo Sarca, SC ARHIPAC SERVICE SRL + *
  • BF [ 1673505 ] BarCode/Image problem when print format is not form + *
  • BF [ 1673542 ] Can't add static image in report table cell + *
  • BF [ 1673548 ] Image is not scaled in a report table cell + *
  • BF [ 1807917 ] Layout positioning issue with m_maxHeightSinceNewLine + *
  • BF [ 1825876 ] Layout boxes with auto width not working + */ +public class LayoutEngine implements Pageable, Printable, Doc +{ + /** + * Detail Constructor + * @param format Print Format + * @param data Print Data + * @param query query for parameter info + */ + public LayoutEngine (MPrintFormat format, PrintData data, MQuery query) + { + this(format, data, query, null); + } // LayoutEngine + + /** + * Detail Constructor + * @param format Print Format + * @param data Print Data + * @param query query for parameter info + * @param trxName + */ + public LayoutEngine (MPrintFormat format, PrintData data, MQuery query, String trxName) + { + m_TrxName = trxName; + log.info(format + " - " + data + " - " + query); + // s_FASTDRAW = MClient.get(format.getCtx()).isUseBetaFunctions(); + // + setPrintFormat(format, false); + setPrintData(data, query, false); + layout(); + } // LayoutEngine + + + /*************************************************************************/ + + /** Logger */ + private static CLogger log = CLogger.getCLogger (LayoutEngine.class); + /** Existing Layout */ + private boolean m_hasLayout = false; + /** The Format */ + private MPrintFormat m_format; + /** Print Context */ + private Properties m_printCtx; + /** The Data */ + private PrintData m_data; + /** The Query (parameter */ + private MQuery m_query; + /** Default Color */ + private MPrintColor m_printColor; + /** Default Font */ + private MPrintFont m_printFont; + /** Printed Column Count */ + private int m_columnCount = -1; + /** Transaction name */ + private String m_TrxName = null; + + + /** Paper - default: standard portrait */ + private CPaper m_paper; + /** Header Area Height (1/4") */ + private int m_headerHeight = 18; // 1/4" => 72/4 + /** Footer Area Height (1/4") */ + private int m_footerHeight = 18; + + + /** Current Page Number */ + private int m_pageNo = 0; + /** Current Page */ + private Page m_currPage; + /** Pages */ + private ArrayList m_pages = new ArrayList(); + /** Header&Footer for all pages */ + private HeaderFooter m_headerFooter; + + + /** Header Coordinates */ + private Rectangle m_header = new Rectangle (); + /** Content Coordinates */ + private Rectangle m_content = new Rectangle(); + /** Footer Coordinates */ + private Rectangle m_footer = new Rectangle(); + /** Temporary NL Position */ + private int m_tempNLPositon = 0; + + /** Header Area */ + public static final int AREA_HEADER = 0; + /** Content Area */ + public static final int AREA_CONTENT = 1; + /** Footer Area */ + public static final int AREA_FOOTER = 2; + /** Area Pointer */ + private int m_area = AREA_CONTENT; + + /** Current Position in 1/72 inch */ + private Point2D.Double[] m_position = new Point2D.Double[] + {new Point2D.Double(0,0), new Point2D.Double(0,0), new Point2D.Double(0,0)}; + /** Max Height Since New Line */ + private float m_maxHeightSinceNewLine[] = new float[] {0f, 0f, 0f}; + + /** Primary Table Element for Page XY Info */ + private TableElement m_tableElement = null; + + /** Last Height by area */ + private float m_lastHeight[] = new float[] {0f, 0f, 0f}; + /** Last Width by area */ + private float m_lastWidth[] = new float[] {0f, 0f, 0f}; + + /** Draw using attributed String vs. Text Layout where possible */ + public static boolean s_FASTDRAW = true; + /** Print Copy (print interface) */ + private boolean m_isCopy = false; + + + /*************************************************************************/ + + /** True Image */ + public static Image IMAGE_TRUE = null; + /** False Image */ + public static Image IMAGE_FALSE = null; + /** Image Size */ + public static Dimension IMAGE_SIZE = new Dimension(10,10); + + static { + Toolkit tk = Toolkit.getDefaultToolkit(); + URL url = LayoutEngine.class.getResource("true10.gif"); + if (url != null) + IMAGE_TRUE = tk.getImage(url); + url = LayoutEngine.class.getResource("false10.gif"); + /** @todo load images via medialoader */ + if (url != null) + IMAGE_FALSE = tk.getImage(url); + } // static init + + + + /************************************************************************** + * Set Print Format + * Optionally re-calculate layout + * @param doLayout if layout exists, redo it + * @param format print Format + */ + public void setPrintFormat (MPrintFormat format, boolean doLayout) + { + m_format = format; + // Initial & Default Settings + m_printCtx = new Properties(format.getCtx()); + + // Set Paper + boolean tempHasLayout = m_hasLayout; + m_hasLayout = false; // do not start re-calculation + MPrintPaper mPaper = MPrintPaper.get(format.getAD_PrintPaper_ID()); + if (m_format.isStandardHeaderFooter()) + setPaper(mPaper.getCPaper()); + else + setPaper(mPaper.getCPaper(), + m_format.getHeaderMargin(), m_format.getFooterMargin()); + m_hasLayout = tempHasLayout; + // + m_printColor = MPrintColor.get(getCtx(), format.getAD_PrintColor_ID()); + m_printFont = MPrintFont.get (format.getAD_PrintFont_ID()); + + // Print Context + Env.setContext(m_printCtx, Page.CONTEXT_REPORTNAME, m_format.getName()); + Env.setContext(m_printCtx, Page.CONTEXT_HEADER, Env.getHeader(m_printCtx, 0)); + Env.setContext(m_printCtx, Env.LANGUAGE, m_format.getLanguage().getAD_Language()); + + if (m_hasLayout && doLayout) + layout(); // re-calculate + } // setPrintFormat + + /** + * Set PrintData. + * Optionally re-calculate layout + * @param data data + * @param doLayout if layout exists, redo it + * @param query query for parameter + */ + public void setPrintData (PrintData data, MQuery query, boolean doLayout) + { + m_data = data; + m_query = query; + if (m_hasLayout && doLayout) + layout(); // re-calculate + } // setPrintData + + public void setPrintData (PrintData data, MQuery query, boolean doLayout, String trxName) + { + m_data = data; + m_query = query; + m_TrxName = trxName; + if (m_hasLayout && doLayout) + layout(); // re-calculate + } // setPrintData + + + /************************************************************************** + * Set Paper + * @param paper Paper + */ + public void setPaper (CPaper paper) + { + setPaper(paper, m_headerHeight, m_footerHeight); + } // setPaper + + /** + * Set Paper + * Optionally re-calculate layout + * @param paper Paper + * @param headerHeight header height + * @param footerHeight footer height + */ + public void setPaper (CPaper paper, int headerHeight, int footerHeight) + { + if (paper == null) + return; + // + boolean paperChange = headerHeight != m_headerHeight || footerHeight != m_footerHeight; + if (!paperChange) + paperChange = !paper.equals(m_paper); + // + log.fine(paper + " - Header=" + headerHeight + ", Footer=" + footerHeight); + m_paper = paper; + m_headerHeight = headerHeight; + m_footerHeight = footerHeight; + calculatePageSize(); + // + if (m_hasLayout && paperChange) + layout(); // re-calculate + } // setPaper + + /** + * Show Dialog and Set Paper + * Optionally re-calculate layout + * @param job printer job + */ + public void pageSetupDialog (PrinterJob job) + { + log.info(""); + if (m_paper.pageSetupDialog(job)) + { + setPaper(m_paper); + layout(); + } + } // pageSetupDialog + + /** + * Set Paper from Page Format. + * PageFormat is derived from CPaper + * @param pf Optional PageFormat - if null standard paper Portrait + */ + protected void setPageFormat (PageFormat pf) + { + if (pf != null) + setPaper(new CPaper(pf)); + else + setPaper(null); + } // setPageFormat + + /** + * Get Page Format + * @return page format + */ + public PageFormat getPageFormat () + { + return m_paper.getPageFormat(); + } // getPageFormat + + + /** + * Calculate Page size based on Paper and header/footerHeight. + *
    +	 *  Paper: 8.5x11.0" Portrait x=32.0,y=32.0 w=548.0,h=728.0
    +	 *  +------------------------ Paper   612x792
    +	 *  |    non-imageable space          32x32
    +	 *  |  +--------------------- Header = printable area start
    +	 *  |  | headerHeight=32      =>  [x=32,y=32,width=548,height=32]
    +	 *  |  +--------------------- Content
    +	 *  |  |                      =>  [x=32,y=64,width=548,height=664]
    +	 *  |  |
    +	 *  |  |
    +	 *  |  |
    +	 *  |  +--------------------- Footer
    +	 *  |  | footerHeight=32      =>  [x=32,y=728,width=548,height=32]
    +	 *  |  +--------------------- Footer end = printable area end
    +	 *  |   non-imageable space
    +	 *  +------------------------
    +	 *  
    + */ + private void calculatePageSize() + { + int x = (int)m_paper.getImageableX (true); + int w = (int)m_paper.getImageableWidth (true); + // + int y = (int)m_paper.getImageableY (true); + int h = (int)m_paper.getImageableHeight (true); + + int height = m_headerHeight; + m_header.setBounds (x, y, w, height); + // + y += height; + height = h-m_headerHeight-m_footerHeight; + m_content.setBounds (x, y, w, height); + // + y += height; + height = m_footerHeight; + m_footer.setBounds (x, y, w, height); + + log.fine("Paper=" + m_paper + ",HeaderHeight=" + m_headerHeight + ",FooterHeight=" + m_footerHeight + + " => Header=" + m_header + ",Contents=" + m_content + ",Footer=" + m_footer); + } // calculatePageSize + + /** + * Set Paper + * @return Paper + */ + public CPaper getPaper() + { + return m_paper; + } // getPaper + + + + /************************************************************************** + * Create Layout + */ + private void layout() + { + // Header/Footer + m_headerFooter = new HeaderFooter(m_printCtx); + if (!m_format.isForm() && m_format.isStandardHeaderFooter()) + createStandardHeaderFooter(); + // + m_pageNo = 0; + m_pages.clear(); + m_tableElement = null; + newPage(true, false); // initialize + // + if (m_format.isForm()) + layoutForm(); + else + { + // Parameter + PrintElement element = layoutParameter(); + if (element != null) + { + m_currPage.addElement (element); + element.setLocation(m_position[AREA_CONTENT]); + m_position[AREA_CONTENT].y += element.getHeight() + 5; // GAP + } + // Table + if (m_data != null) + { + element = layoutTable(m_format, m_data, 0); + element.setLocation(m_content.getLocation()); + for (int p = 1; p <= element.getPageCount(); p++) + { + if (p != 1) + newPage(true, false); + m_currPage.addElement (element); + } + } + } + // + String pageInfo = String.valueOf(m_pages.size()) + getPageInfo(m_pages.size()); + Env.setContext(m_printCtx, Page.CONTEXT_PAGECOUNT, pageInfo); + Timestamp now = new Timestamp(System.currentTimeMillis()); + Env.setContext(m_printCtx, Page.CONTEXT_DATE, + DisplayType.getDateFormat(DisplayType.Date, m_format.getLanguage()).format(now)); + Env.setContext(m_printCtx, Page.CONTEXT_TIME, + DisplayType.getDateFormat(DisplayType.DateTime, m_format.getLanguage()).format(now)); + // Update Page Info + int pages = m_pages.size(); + for (int i = 0; i < pages; i++) + { + Page page = (Page)m_pages.get(i); + int pageNo = page.getPageNo(); + pageInfo = String.valueOf(pageNo) + getPageInfo(pageNo); + page.setPageInfo(pageInfo); + page.setPageCount(pages); + } + + m_hasLayout = true; + } // layout + + + /*************************************************************************** + * Get PrintLayout (Report) Context + * @return context + */ + public Properties getCtx() + { + return m_printCtx; + } // getCtx + + /** + * Get the number of printed Columns + * @return no of printed columns + */ + public int getColumnCount() + { + return m_columnCount; + } // getColumnCount + + /** + * Set the current Print Area + * @param area see HEADER_.. constants + */ + protected void setArea (int area) + { + if (m_area == area) + return; + if (area < 0 || area > 2) + throw new ArrayIndexOutOfBoundsException (area); + m_area = area; + } // setArea + + /** + * Get the current Print Area + * @return area see HEADER_.. constants + */ + public int getArea () + { + return m_area; + } // getArea + + /** + * Return bounds of current Area + * @return rectangle with bounds + */ + public Rectangle getAreaBounds() + { + Rectangle part = m_content; + if (m_area == AREA_HEADER) + part = m_header; + else if (m_area == AREA_FOOTER) + part = m_footer; + // + return part; + } // getAreaBounds + + + /************************************************************************** + * Create New Page, set position to top content + * @param force if false will check if nothing printed so far + * @param preserveXPos preserve X Position of content area + * @return new page no + */ + protected int newPage (boolean force, boolean preserveXPos) + { + // We are on a new page + if (!force + && m_position[AREA_CONTENT].getX() == m_content.x + && m_position[AREA_CONTENT].getY() == m_content.y) + { + log.fine("skipped"); + return m_pageNo; + } + + m_pageNo++; + m_currPage = new Page (m_printCtx, m_pageNo); + m_pages.add(m_currPage); + // + m_position[AREA_HEADER].setLocation(m_header.x, m_header.y); + if (preserveXPos) + m_position[AREA_CONTENT].setLocation(m_position[AREA_CONTENT].x, m_content.y); + else + m_position[AREA_CONTENT].setLocation(m_content.x, m_content.y); + m_position[AREA_FOOTER].setLocation(m_footer.x, m_footer.y); + m_maxHeightSinceNewLine = new float[] {0f, 0f, 0f}; + log.finer("Page=" + m_pageNo); + return m_pageNo; + } // newPage + + /** + * Move to New Line (may cause new page) + */ + protected void newLine () + { + Rectangle part = m_content; + if (m_area == AREA_HEADER) + part = m_header; + else if (m_area == AREA_FOOTER) + part = m_footer; + + // Temporary NL Position + int xPos = part.x; + if (m_tempNLPositon != 0) + xPos = m_tempNLPositon; + + if (isYspaceFor(m_maxHeightSinceNewLine[m_area])) + { + m_position[m_area].setLocation(xPos, m_position[m_area].y + m_maxHeightSinceNewLine[m_area]); + log.finest("Page=" + m_pageNo + " [" + m_area + "] " + m_position[m_area].x + "/" + m_position[m_area].y); + } + else if (m_area == AREA_CONTENT) + { + log.finest("Not enough Y space " + + m_lastHeight[m_area] + " - remaining " + getYspace() + " - Area=" + m_area); + newPage(true, false); + log.finest("Page=" + m_pageNo + " [" + m_area + "] " + m_position[m_area].x + "/" + m_position[m_area].y); + } + else // footer/header + { + m_position[m_area].setLocation(part.x, m_position[m_area].y + m_maxHeightSinceNewLine[m_area]); + log.log(Level.SEVERE, "Outside of Area(" + m_area + "): " + m_position[m_area]); + } + m_maxHeightSinceNewLine[m_area] = 0f; + } // newLine + + + /** + * Get current Page Number (not zero based) + * @return Page No + */ + public int getPageNo() + { + return m_pageNo; + } // getPageNo + + /** + * Get Page No + * @param pageNo page number (NOT zero based) + * @return Page + */ + public Page getPage (int pageNo) + { + if (pageNo <= 0 || pageNo > m_pages.size()) + { + log.log(Level.SEVERE, "No page #" + pageNo); + return null; + } + Page retValue = (Page)m_pages.get(pageNo-1); + return retValue; + } // getPage + + /** + * Get Pages + * @return Pages in ArrayList + */ + public ArrayList getPages() + { + return m_pages; + } // getPages + + /** + * Get Header & Footer info + * @return Header&Footer + */ + public HeaderFooter getHeaderFooter() + { + return m_headerFooter; + } // getPages + + /** + * Set Current page to Page No + * @param pageNo page number (NOT zero based) + */ + protected void setPage (int pageNo) + { + if (pageNo <= 0 || pageNo > m_pages.size()) + { + log.log(Level.SEVERE, "No page #" + pageNo); + return; + } + Page retValue = (Page)m_pages.get(pageNo-1); + m_currPage = retValue; + } // setPage + + /** + * Get Page Info for Multi-Page tables + * @param pageNo page + * @return info e.g. (1,1) + */ + public String getPageInfo(int pageNo) + { + if (m_tableElement == null || m_tableElement.getPageXCount() == 1) + return ""; + int pi = m_tableElement.getPageIndex(pageNo); + StringBuffer sb = new StringBuffer("("); + sb.append(m_tableElement.getPageYIndex(pi)+1).append(",") + .append(m_tableElement.getPageXIndex(pi)+1).append(")"); + return sb.toString(); + } // getPageInfo + + /** + * Get Max Page Info for Multi-Page tables + * @return info e.g. (3,2) + */ + public String getPageInfoMax() + { + if (m_tableElement == null || m_tableElement.getPageXCount() == 1) + return ""; + StringBuffer sb = new StringBuffer("("); + sb.append(m_tableElement.getPageYCount()).append(",") + .append(m_tableElement.getPageXCount()).append(")"); + return sb.toString(); + } // getPageInfoMax + + /** + * Get Format Model + * @return model + */ + public MPrintFormat getFormat() + { + return m_format; + } // getFormat + + /** + * Get Print Interface (Pageable, Printable, Doc) + * @param isCopy true if it is a document copy + * @return this if nothing to print + */ + public LayoutEngine getPageable (boolean isCopy) + { + setCopy(isCopy); + if (getNumberOfPages() == 0 + || !ArchiveEngine.isValid(this)) + { + log.warning("Nothing to print - " + toString()); + return null; + } + return this; + } // getPageable + + /************************************************************************** + * Set Position on current page (no check) + * @param p point relative in area + */ + protected void setRelativePosition (Point2D p) + { + if (p == null) + return; + Rectangle part = m_content; + if (m_area == AREA_HEADER) + part = m_header; + else if (m_area == AREA_FOOTER) + part = m_footer; + m_position[m_area].setLocation(part.x + p.getX(), part.y + p.getY()); + log.finest("Page=" + m_pageNo + " [" + m_area + "] " + m_position[m_area].x + "/" + m_position[m_area].y); + } // setPosition + + /** + * Set Position on current page (no check) + * @param x x position in 1/72 inch + * @param y y position in 1/72 inch + */ + protected void setRelativePosition (float x, float y) + { + setRelativePosition(new Point2D.Float(x, y)); + } // setPosition + + /** + * Get the current position on current page + * @return current position + */ + public Point2D getPosition () + { + return m_position[m_area]; + } // getPosition + + /** + * Set X Position on current page + * @param x x position in 1/72 inch + */ + protected void setX (float x) + { + m_position[m_area].x = x; + log.finest("Page=" + m_pageNo + " [" + m_area + "] " + m_position[m_area].x + "/" + m_position[m_area].y); + } // setX + + /** + * Add to X Position on current page + * @param xOffset add offset to x position in 1/72 inch + */ + protected void addX (float xOffset) + { + if (xOffset == 0f) + return; + m_position[m_area].x += xOffset; + log.finest("Page=" + m_pageNo + " [" + m_area + "] " + m_position[m_area].x + "/" + m_position[m_area].y); + } // addX + + /** + * Get X Position on current page + * @return x position in 1/72 inch + */ + public float getX () + { + return (float)m_position[m_area].x; + } // getX + + /** + * Set Y Position on current page + * @param y y position in 1/72 inch + */ + protected void setY (int y) + { + m_position[m_area].y = y; + log.finest("Page=" + m_pageNo + " [" + m_area + "] " + m_position[m_area].x + "/" + m_position[m_area].y); + } // setY + + /** + * Add to Y Position - may cause New Page + * @param yOffset add offset to y position in 1/72 inch + */ + protected void addY (int yOffset) + { + if (yOffset == 0f) + return; + if (isYspaceFor(yOffset)) + { + m_position[m_area].y += yOffset; + log.finest("Page=" + m_pageNo + " [" + m_area + "] " + m_position[m_area].x + "/" + m_position[m_area].y); + } + else if (m_area == AREA_CONTENT) + { + log.finest("Not enough Y space " + + m_lastHeight[m_area] + " - remaining " + getYspace() + " - Area=" + m_area); + newPage(true, true); + log.finest("Page=" + m_pageNo + " [" + m_area + "] " + m_position[m_area].x + "/" + m_position[m_area].y); + } + else + { + m_position[m_area].y += yOffset; + log.log(Level.SEVERE, "Outside of Area: " + m_position); + } + } // addY + + /** + * Get Y Position on current page + * @return y position in 1/72 inch + */ + public float getY () + { + return (float)m_position[m_area].y; + } // getY + + + /************************************************************************** + * Return remaining X dimension space _ on current page in Area + * @return space in 1/72 inch remaining in line + */ + public float getXspace() + { + Rectangle part = m_content; + if (m_area == AREA_HEADER) + part = m_header; + else if (m_area == AREA_FOOTER) + part = m_footer; + // + return (float)(part.x + part.width - m_position[m_area].x); + } // getXspace + + /** + * Remaining Space is OK for Width in Area + * @param width width + * @return true if width fits in area + */ + public boolean isXspaceFor (float width) + { + return (getXspace()-width) > 0f; + } // isXspaceFor + + /** + * Return remaining Y dimension space | on current page in Area + * @return space in 1/72 inch remaining on page + */ + public float getYspace() + { + Rectangle part = m_content; + if (m_area == AREA_HEADER) + part = m_header; + else if (m_area == AREA_FOOTER) + part = m_footer; + // + return (float)(part.y + part.height - m_position[m_area].y); + } // getYspace + + /** + * Remaining Space is OK for Height in Area + * @param height height + * @return true if height fits in area + */ + public boolean isYspaceFor (float height) + { + return (getYspace()-height) > 0f; + } // isYspaceFor + + /************************************************************************** + * Create Standard Header/Footer + *
    +	 *  title           C        Page x of x
    +	 *  Copyright      who         date&time
    +	 *  
    + */ + private void createStandardHeaderFooter() + { + PrintElement element = new ImageElement(org.compiere.Adempiere.getImageLogoSmall(true)); // 48x15 + // element = new ImageElement(org.compiere.Adempiere.getImageLogo()); // 100x30 + element.layout(48, 15, false, MPrintFormatItem.FIELDALIGNMENTTYPE_LeadingLeft); + element.setLocation(m_header.getLocation()); + m_headerFooter.addElement(element); + // + MPrintTableFormat tf = m_format.getTableFormat(); + Font font = tf.getPageHeader_Font(); + Color color = tf.getPageHeaderFG_Color(); + // + element = new StringElement("@*ReportName@", font, color, null, true); + element.layout (m_header.width, 0, true, MPrintFormatItem.FIELDALIGNMENTTYPE_Center); + element.setLocation(m_header.getLocation()); + m_headerFooter.addElement(element); + // + // + element = new StringElement("@Page@ @*Page@ @of@ @*PageCount@", font, color, null, true); + element.layout (m_header.width, 0, true, MPrintFormatItem.FIELDALIGNMENTTYPE_TrailingRight); + element.setLocation(m_header.getLocation()); + m_headerFooter.addElement(element); + + // Footer + font = tf.getPageFooter_Font(); + color = tf.getPageFooterFG_Color(); + // + element = new StringElement(Adempiere.ADEMPIERE_R, font, color, null, true); + /** You can use the following to customize reports for your product name */ + // element = new StringElement(Adempiere.NAME, font, color, null, true); + element.layout (m_footer.width, 0, true, MPrintFormatItem.FIELDALIGNMENTTYPE_LeadingLeft); + Point ft = m_footer.getLocation(); + ft.y += m_footer.height - element.getHeight() - 2; // 2pt above min + element.setLocation(ft); + m_headerFooter.addElement(element); + // + element = new StringElement("@*Header@", font, color, null, true); + element.layout (m_footer.width, 0, true, MPrintFormatItem.FIELDALIGNMENTTYPE_Center); + element.setLocation(ft); + m_headerFooter.addElement(element); + // + element = new StringElement("@*CurrentDateTime@", font, color, null, true); + element.layout (m_footer.width, 0, true, MPrintFormatItem.FIELDALIGNMENTTYPE_TrailingRight); + element.setLocation(ft); + m_headerFooter.addElement(element); + } // createStandardHeaderFooter + + + + /************************************************************************** + * Layout Form. + * For every Row, loop through the Format + * and calculate element size and position. + */ + private void layoutForm() + { + // log.info("layoutForm"); + m_columnCount = 0; + if (m_data == null) + return; + // for every row + for (int row = 0; row < m_data.getRowCount(); row++) + { + log.info("Row=" + row); + m_data.setRowIndex(row); + boolean somethingPrinted = true; // prevent NL of nothing printed and supress null + // for every item + for (int i = 0; i < m_format.getItemCount(); i++) + { + MPrintFormatItem item = m_format.getItem(i); + // log.fine("layoutForm - Row=" + row + " - #" + i + " - " + item); + if (!item.isPrinted()) + continue; + // log.fine("layoutForm - Row=" + row + " - #" + i + " - " + item); + m_columnCount++; + // Read Header/Footer just once + if (row > 0 && (item.isHeader() || item.isFooter())) + continue; + // Position + if (item.isHeader()) // Area + setArea(AREA_HEADER); + else if (item.isFooter()) + setArea(AREA_FOOTER); + else + setArea(AREA_CONTENT); + // + if (item.isSetNLPosition() && item.isRelativePosition()) + m_tempNLPositon = 0; + // New Page/Line + if (item.isNextPage()) // item.isPageBreak() // new page + newPage(false, false); + else if (item.isNextLine() && somethingPrinted) // new line + { + newLine (); + somethingPrinted = false; + } + else + addX(m_lastWidth[m_area]); + // Relative Position space + if (item.isRelativePosition()) + { + addX(item.getXSpace()); + addY(item.getYSpace()); + } + else // Absolute relative position + setRelativePosition(item.getXPosition(), item.getYPosition()); + // Temporary NL Position when absolute positioned + if (item.isSetNLPosition() && !item.isRelativePosition()) + m_tempNLPositon = (int)getPosition().getX(); + + // line alignment + String alignment = item.getFieldAlignmentType(); + int maxWidth = item.getMaxWidth(); + boolean lineAligned = false; + if (item.isRelativePosition()) + { + if (item.isLineAlignLeading()) + { + alignment = MPrintFormatItem.FIELDALIGNMENTTYPE_LeadingLeft; + maxWidth = getAreaBounds().width; + lineAligned = true; + } + else if (item.isLineAlignCenter()) + { + alignment = MPrintFormatItem.FIELDALIGNMENTTYPE_Center; + maxWidth = getAreaBounds().width; + lineAligned = true; + } + else if (item.isLineAlignTrailing()) + { + alignment = MPrintFormatItem.FIELDALIGNMENTTYPE_TrailingRight; + maxWidth = getAreaBounds().width; + lineAligned = true; + } + } + + // Type + PrintElement element = null; + if (item.isTypePrintFormat()) //** included PrintFormat + { + element = includeFormat (item, m_data); + } + else if (item.isBarcode()) + { + element = createBarcodeElement(item); + element.layout(maxWidth, item.getMaxHeight(), false, alignment); + } + else if (item.isTypeImage()) //** Image + { + if (item.isImageField()) + element = createImageElement (item); + else if (item.isImageIsAttached()) + element = ImageElement.get (item.get_ID()); + else + element = ImageElement.get (item.getImageURL()); + if (element != null) + element.layout(maxWidth, item.getMaxHeight(), false, alignment); + } + else if (item.isTypeField()) //** Field + { + if (maxWidth == 0 && item.isFieldAlignBlock()) + maxWidth = getAreaBounds().width; + element = createFieldElement (item, maxWidth, alignment, m_format.isForm()); + } + else if (item.isTypeBox()) //** Line/Box + { + if (m_format.isForm()) + element = createBoxElement(item); + // Auto detect width - teo_sarca, BF [ 1825876 ] + if (element != null && maxWidth == 0) { + maxWidth = getAreaBounds().width; + element.setMaxWidth(maxWidth); + } + } + else // (item.isTypeText()) //** Text + { + if (maxWidth == 0 && item.isFieldAlignBlock()) + maxWidth = getAreaBounds().width; + element = createStringElement (item.getPrintName (m_format.getLanguage ()), + item.getAD_PrintColor_ID (), item.getAD_PrintFont_ID (), + maxWidth, item.getMaxHeight (), item.isHeightOneLine (), alignment, true); + } + + // Printed - set last width/height + if (element != null) + { + somethingPrinted = true; + if (!lineAligned) + m_lastWidth[m_area] = element.getWidth(); + m_lastHeight[m_area] = element.getHeight(); + } + else + { + somethingPrinted = false; + m_lastWidth[m_area] = 0f; + m_lastHeight[m_area] = 0f; + } + + // Does it fit? + if (item.isRelativePosition() && !lineAligned) + { + if (!isXspaceFor(m_lastWidth[m_area])) + { + log.finest("Not enough X space for " + + m_lastWidth[m_area] + " - remaining " + getXspace() + " - Area=" + m_area); + newLine (); + } + if (m_area == AREA_CONTENT && !isYspaceFor(m_lastHeight[m_area])) + { + log.finest("Not enough Y space " + + m_lastHeight[m_area] + " - remaining " + getYspace() + " - Area=" + m_area); + newPage (true, true); + } + } + // We know Position and Size + // log.fine( "LayoutEngine.layoutForm", + // "Page=" + m_pageNo + " [" + m_area + "] " + m_position[m_area].x + "/" + m_position[m_area].y + // + " w=" + lastWidth[m_area] + ",h=" + lastHeight[m_area] + " " + item); + if (element != null) + element.setLocation(m_position[m_area]); + // Add to Area + if (m_area == AREA_CONTENT) + m_currPage.addElement (element); + else + m_headerFooter.addElement (element); + // + if (m_lastHeight[m_area] > m_maxHeightSinceNewLine[m_area]) + m_maxHeightSinceNewLine[m_area] = m_lastHeight[m_area]; + // Reset maxHeightSinceNewLine if we have an absolute position - teo_sarca BF [ 1807917 ] + if (!item.isRelativePosition()) + m_maxHeightSinceNewLine[m_area] = m_lastHeight[m_area]; + + } // for every item + } // for every row + } // layoutForm + + + /** + * Include Table Format + * @param item print format item + * @param data print data + * @return Print Element + */ + private PrintElement includeFormat (MPrintFormatItem item, PrintData data) + { + newLine(); + PrintElement element = null; + // + MPrintFormat format = MPrintFormat.get (getCtx(), item.getAD_PrintFormatChild_ID(), false); + format.setLanguage(m_format.getLanguage()); + if (m_format.isTranslationView()) + format.setTranslationLanguage(m_format.getLanguage()); + int AD_Column_ID = item.getAD_Column_ID(); + log.info(format + " - Item=" + item.getName() + " (" + AD_Column_ID + ")"); + // + Object obj = data.getNode(new Integer(AD_Column_ID)); + // Object obj = data.getNode(item.getColumnName()); // slower + if (obj == null) + { + data.dumpHeader(); + data.dumpCurrentRow(); + log.log(Level.SEVERE, "No Node - AD_Column_ID=" + + AD_Column_ID + " - " + item + " - " + data); + return null; + } + PrintDataElement dataElement = (PrintDataElement)obj; + String recordString = dataElement.getValueKey(); + if (recordString == null || recordString.length() == 0) + { + data.dumpHeader(); + data.dumpCurrentRow(); + log.log(Level.SEVERE, "No Record Key - " + dataElement + + " - AD_Column_ID=" + AD_Column_ID + " - " + item); + return null; + } + int Record_ID = 0; + try + { + Record_ID = Integer.parseInt(recordString); + } + catch (Exception e) + { + data.dumpCurrentRow(); + log.log(Level.SEVERE, "Invalid Record Key - " + recordString + + " (" + e.getMessage() + + ") - AD_Column_ID=" + AD_Column_ID + " - " + item); + return null; + } + MQuery query = new MQuery (format.getAD_Table_ID()); + query.addRestriction(item.getColumnName(), MQuery.EQUAL, new Integer(Record_ID)); + format.setTranslationViewQuery(query); + log.fine(query.toString()); + // + DataEngine de = new DataEngine(format.getLanguage(),m_TrxName); + PrintData includedData = de.getPrintData(data.getCtx(), format, query); + if (includedData == null) + return null; + log.fine(includedData.toString()); + // + element = layoutTable (format, includedData, item.getXSpace()); + // handle multi page tables + if (element.getPageCount() > 1) + { + Point2D.Double loc = m_position[m_area]; + element.setLocation(loc); + for (int p = 1; p < element.getPageCount(); p++) // don't add last one + { + m_currPage.addElement (element); + newPage(true, false); + } + m_position[m_area] = loc; + ((TableElement)element).setHeightToLastPage(); + } + + m_lastWidth[m_area] = element.getWidth(); + m_lastHeight[m_area] = element.getHeight(); + + if (!isXspaceFor(m_lastWidth[m_area])) + { + log.finest("Not enough X space for " + + m_lastWidth[m_area] + " - remaining " + getXspace() + " - Area=" + m_area); + newLine (); + } + if (m_area == AREA_CONTENT && !isYspaceFor(m_lastHeight[m_area])) + { + log.finest("Not enough Y space " + + m_lastHeight[m_area] + " - remaining " + getYspace() + " - Area=" + m_area); + newPage (true, false); + } + // + return element; + } // includeFormat + + /** + * Create String Element + * + * @param content string to be printed + * @param AD_PrintColor_ID color + * @param AD_PrintFont_ID font + * @param maxWidth max width + * @param maxHeight max height + * @param isHeightOneLine onle line only + * @param FieldAlignmentType alignment type (MPrintFormatItem.FIELD_ALIGN_*) + * @param isTranslated if true and content contaiins @variable@, it is dynamically translated during print + * @return Print Element + */ + private PrintElement createStringElement (String content, int AD_PrintColor_ID, int AD_PrintFont_ID, + int maxWidth, int maxHeight, boolean isHeightOneLine, String FieldAlignmentType, boolean isTranslated) + { + if (content == null || content.length() == 0) + return null; + // Color / Font + Color color = getColor(); // default + if (AD_PrintColor_ID != 0 && m_printColor.get_ID() != AD_PrintColor_ID) + { + MPrintColor c = MPrintColor.get (getCtx(), AD_PrintColor_ID); + if (c.getColor() != null) + color = c.getColor(); + } + Font font = m_printFont.getFont(); // default + if (AD_PrintFont_ID != 0 && m_printFont.get_ID() != AD_PrintFont_ID) + { + MPrintFont f = MPrintFont.get (AD_PrintFont_ID); + if (f.getFont() != null) + font = f.getFont(); + } + PrintElement e = new StringElement(content, font, color, null, isTranslated); + e.layout (maxWidth, maxHeight, isHeightOneLine, FieldAlignmentType); + return e; + } // createStringElement + + /** + * Create Field Element + * @param item Format Item + * @param maxWidth max width + * @param FieldAlignmentType alignment type (MPrintFormatItem.FIELD_ALIGN_*) + * @param isForm true if document + * @return Print Element or null if nothing to print + */ + private PrintElement createFieldElement (MPrintFormatItem item, int maxWidth, + String FieldAlignmentType, boolean isForm) + { + // Get Data + Object obj = m_data.getNode(new Integer(item.getAD_Column_ID())); + if (obj == null) + return null; + else if (obj instanceof PrintDataElement) + ; + else + { + log.log(Level.SEVERE, "Element not PrintDataElement " + obj.getClass()); + return null; + } + + // Convert DataElement to String + PrintDataElement data = (PrintDataElement)obj; + if (data.isNull() && item.isSuppressNull()) + return null; + String stringContent = data.getValueDisplay (m_format.getLanguage()); + if ((stringContent == null || stringContent.length() == 0) && item.isSuppressNull()) + return null; + // non-string + Object content = stringContent; + if (data.getValue() instanceof Boolean) + content = data.getValue(); + + // Convert AmtInWords Content to alpha + if (item.getColumnName().equals("AmtInWords")) + { + log.fine("AmtInWords: " + stringContent); + stringContent = Msg.getAmtInWords (m_format.getLanguage(), stringContent); + content = stringContent; + } + // Label + String label = item.getPrintName(m_format.getLanguage()); + String labelSuffix = item.getPrintNameSuffix(m_format.getLanguage()); + + // ID Type + NamePair ID = null; + if (data.isID()) + { // Record_ID/ColumnName + Object value = data.getValue(); + if (value instanceof KeyNamePair) + ID = new KeyNamePair(((KeyNamePair)value).getKey(), item.getColumnName()); + else if (value instanceof ValueNamePair) + ID = new ValueNamePair(((ValueNamePair)value).getValue(), item.getColumnName()); + } + else if (MPrintFormatItem.FIELDALIGNMENTTYPE_Default.equals(FieldAlignmentType)) + { + if (data.isNumeric()) + FieldAlignmentType = MPrintFormatItem.FIELDALIGNMENTTYPE_TrailingRight; + else + FieldAlignmentType = MPrintFormatItem.FIELDALIGNMENTTYPE_LeadingLeft; + } + + // Get Color/ Font + Color color = getColor(); // default + if (ID != null && !isForm) + ; // link color/underline handeled in PrintElement classes + else if (item.getAD_PrintColor_ID() != 0 && m_printColor.get_ID() != item.getAD_PrintColor_ID()) + { + MPrintColor c = MPrintColor.get (getCtx(), item.getAD_PrintColor_ID()); + if (c.getColor() != null) + color = c.getColor(); + } + + Font font = m_printFont.getFont(); // default + if (item.getAD_PrintFont_ID() != 0 && m_printFont.get_ID() != item.getAD_PrintFont_ID()) + { + MPrintFont f = MPrintFont.get (item.getAD_PrintFont_ID()); + if (f.getFont() != null) + font = f.getFont(); + } + + // Create String, HTML or Location + PrintElement e = null; + if (data.getDisplayType() == DisplayType.Location) + { + e = new LocationElement(m_printCtx, ((KeyNamePair)ID).getKey(), font, color, + item.isHeightOneLine(), label, labelSuffix); + e.layout (maxWidth, item.getMaxHeight(), item.isHeightOneLine(), FieldAlignmentType); + } + else + { + if (HTMLElement.isHTML(stringContent)) + e = new HTMLElement(stringContent); + else + e = new StringElement(content, font, color, isForm ? null : ID, label, labelSuffix); + e.layout (maxWidth, item.getMaxHeight(), item.isHeightOneLine(), FieldAlignmentType); + } + return e; + } // createFieldElement + + /** + * Create Box/Line Element + * @param item item + * @return box element + */ + private PrintElement createBoxElement (MPrintFormatItem item) + { + Color color = getColor(); // default + if (item.getAD_PrintColor_ID() != 0 + && m_printColor.get_ID() != item.getAD_PrintColor_ID()) + { + MPrintColor c = MPrintColor.get (getCtx(), item.getAD_PrintColor_ID()); + if (c.getColor() != null) + color = c.getColor(); + } + return new BoxElement(item, color); + } // createBoxElement + + /** + * Create Image Element from item + * @param item item + * @return image element + */ + private PrintElement createImageElement (MPrintFormatItem item) + { + Object obj = m_data.getNode(new Integer(item.getAD_Column_ID())); + if (obj == null) + return null; + else if (obj instanceof PrintDataElement) + ; + else + { + log.log(Level.SEVERE, "Element not PrintDataElement " + obj.getClass()); + return null; + } + + PrintDataElement data = (PrintDataElement)obj; + if (data.isNull() && item.isSuppressNull()) + return null; + String url = data.getValueDisplay (m_format.getLanguage()); + if ((url == null || url.length() == 0)) + { + if (item.isSuppressNull()) + return null; + else // should create an empty area + return null; + } + ImageElement element = null; + if (data.getDisplayType() == DisplayType.Image) { + element = ImageElement.get (data, url); + } else { + element = ImageElement.get (url); + } + return element; + } // createImageElement + + /** + * Create Barcode Element + * @param item item + * @return barcode element + */ + private PrintElement createBarcodeElement (MPrintFormatItem item) + { + // Get Data + Object obj = m_data.getNode(new Integer(item.getAD_Column_ID())); + if (obj == null) + return null; + else if (obj instanceof PrintDataElement) + ; + else + { + log.log(Level.SEVERE, "Element not PrintDataElement " + obj.getClass()); + return null; + } + + // Convert DataElement to String + PrintDataElement data = (PrintDataElement)obj; + if (data.isNull() && item.isSuppressNull()) + return null; + String stringContent = data.getValueDisplay (m_format.getLanguage()); + if ((stringContent == null || stringContent.length() == 0) && item.isSuppressNull()) + return null; + + BarcodeElement element = new BarcodeElement (stringContent, item); + if (element.isValid()) + return element; + return null; + } // createBarcodeElement + + /** + * Get default Color + * @return color + */ + public Color getColor() + { + if (m_printColor == null) + return Color.BLACK; + return m_printColor.getColor(); + } // getColor + + /************************************************************************** + * Layout Table. + * Convert PrintData into TableElement + * @param format format to use + * @param printData data to use + * @param xOffset X Axis - offset (start of table) i.e. indentation + * @return TableElement + */ + private PrintElement layoutTable (MPrintFormat format, PrintData printData, + int xOffset) + { + log.info(format.getName() + " - " + printData.getName()); + MPrintTableFormat tf = format.getTableFormat(); + // Initial Values + HashMap rowColFont = new HashMap(); + MPrintFont printFont = MPrintFont.get (format.getAD_PrintFont_ID()); + rowColFont.put(new Point(TableElement.ALL,TableElement.ALL), printFont.getFont()); + tf.setStandard_Font(printFont.getFont()); + rowColFont.put(new Point(TableElement.HEADER_ROW,TableElement.ALL), tf.getHeader_Font()); + // + HashMap rowColColor = new HashMap(); + MPrintColor printColor = MPrintColor.get (getCtx(), format.getAD_PrintColor_ID()); + rowColColor.put(new Point(TableElement.ALL,TableElement.ALL), printColor.getColor()); + rowColColor.put(new Point(TableElement.HEADER_ROW,TableElement.ALL), tf.getHeaderFG_Color()); + // + HashMap rowColBackground = new HashMap(); + rowColBackground.put(new Point(TableElement.HEADER_ROW,TableElement.ALL), tf.getHeaderBG_Color()); + // Sizes + boolean multiLineHeader = false; + int pageNoStart = m_pageNo; + int repeatedColumns = 1; + Rectangle firstPage = new Rectangle(m_content); + firstPage.x += xOffset; + firstPage.width -= xOffset; + int yOffset = (int)m_position[AREA_CONTENT].y - m_content.y; + firstPage.y += yOffset; + firstPage.height -= yOffset; + Rectangle nextPages = new Rectangle(m_content); + nextPages.x += xOffset; + nextPages.width -= xOffset; + // Column count + int columnCount = 0; + for (int c = 0; c < format.getItemCount(); c++) + { + if (format.getItem(c).isPrinted()) + columnCount++; + } + // System.out.println("Cols=" + cols); + + // Header & Column Setup + ValueNamePair[] columnHeader = new ValueNamePair[columnCount]; + int[] columnMaxWidth = new int[columnCount]; + int[] columnMaxHeight = new int[columnCount]; + boolean[] fixedWidth = new boolean [columnCount]; + String[] columnJustification = new String[columnCount]; + HashMap additionalLines = new HashMap(); + + int col = 0; + for (int c = 0; c < format.getItemCount(); c++) + { + MPrintFormatItem item = format.getItem(c); + if (item.isPrinted()) + { + if (item.isNextLine() && item.getBelowColumn() != 0) + { + additionalLines.put(new Integer(col), new Integer(item.getBelowColumn()-1)); + if (!item.isSuppressNull()) + { + item.setIsSuppressNull(true); // display size will be set to 0 in TableElement + item.save(); + } + } + columnHeader[col] = new ValueNamePair(item.getColumnName(), + item.getPrintName(format.getLanguage())); + columnMaxWidth[col] = item.getMaxWidth(); + fixedWidth[col] = (columnMaxWidth[col] != 0 && item.isFixedWidth()); + if (item.isSuppressNull()) + { + if (columnMaxWidth[col] == 0) + columnMaxWidth[col] = -1; // indication suppress if Null + else + columnMaxWidth[col] *= -1; + } + columnMaxHeight[col] = item.getMaxHeight(); + if (item.isHeightOneLine()) + columnMaxHeight[col] = -1; + columnJustification[col] = item.getFieldAlignmentType(); + if (columnJustification[col] == null || columnJustification[col].equals(MPrintFormatItem.FIELDALIGNMENTTYPE_Default)) + columnJustification[col] = MPrintFormatItem.FIELDALIGNMENTTYPE_LeadingLeft; // when generated sets correct alignment + // Column Fonts + if (item.getAD_PrintFont_ID() != 0 && item.getAD_PrintFont_ID() != format.getAD_PrintFont_ID()) + { + MPrintFont font = MPrintFont.get(item.getAD_PrintFont_ID()); + rowColFont.put(new Point(TableElement.ALL, col), font.getFont()); + } + if (item.getAD_PrintColor_ID() != 0 && item.getAD_PrintColor_ID() != format.getAD_PrintColor_ID()) + { + MPrintColor color = MPrintColor.get (getCtx(), item.getAD_PrintColor_ID()); + rowColColor.put(new Point(TableElement.ALL, col), color.getColor()); + } + // + col++; + } + } + + // The Data + int rows = printData.getRowCount(); + // System.out.println("Rows=" + rows); + Object[][] data = new Object [rows][columnCount]; + KeyNamePair[] pk = new KeyNamePair[rows]; + String pkColumnName = null; + ArrayList functionRows = new ArrayList(); + ArrayList pageBreak = new ArrayList(); + + // for all rows + for (int row = 0; row < rows; row++) + { + // System.out.println("row=" + row); + printData.setRowIndex(row); + if (printData.isFunctionRow()) + { + functionRows.add(new Integer(row)); + rowColFont.put(new Point(row, TableElement.ALL), tf.getFunct_Font()); + rowColColor.put(new Point(row, TableElement.ALL), tf.getFunctFG_Color()); + rowColBackground.put(new Point(row, TableElement.ALL), tf.getFunctBG_Color()); + if (printData.isPageBreak()) + { + pageBreak.add(new Integer(row)); + log.finer("PageBreak row=" + row); + } + } + // Summary/Line Levels for Finanial Reports + else + { + int levelNo = printData.getLineLevelNo(); + if (levelNo != 0) + { + if (levelNo < 0) + levelNo = -levelNo; + Font base = printFont.getFont(); + if (levelNo == 1) + rowColFont.put(new Point(row, TableElement.ALL), new Font (base.getName(), + Font.ITALIC, base.getSize()-levelNo)); + else if (levelNo == 2) + rowColFont.put(new Point(row, TableElement.ALL), new Font (base.getName(), + Font.PLAIN, base.getSize()-levelNo)); + } + } + // for all columns + col = 0; + for (int c = 0; c < format.getItemCount(); c++) + { + MPrintFormatItem item = format.getItem(c); + Object dataElement = null; + if (item.isPrinted()) // Text Columns + { + if (item.isTypePrintFormat()) + { + log.warning("Unsupported: PrintFormat in Table: " + item); + } + else if (item.isTypeImage()) + { + if (item.isImageField()) + { + Object obj = null; + if (item.getAD_Column_ID() > 0) // teo_sarca, [ 1673542 ] + obj = printData.getNode(new Integer(item.getAD_Column_ID())); + if (obj == null) + ; + else if (obj instanceof PrintDataElement) + { + PrintDataElement pde = (PrintDataElement)obj; + // Get the PrintDataElement string value - teo_sarca [ 1673505 ] + Object o = pde.getValue(); + String value = null; + if (o == null) + value = ""; + else if (o instanceof KeyNamePair) + value = ((KeyNamePair)o).getName(); + else + value = o.toString(); + + data[row][col] = ImageElement.get (value); + } + } + else if (item.isImageIsAttached()) + data[row][col] = ImageElement.get (item.get_ID()); + else + data[row][col] = ImageElement.get (item.getImageURL()); + // Image layout - teo_sarca, [ 1673548 ] + if (data[row][col] != null) + ((ImageElement)data[row][col]).layout(item.getMaxWidth(), item.getMaxHeight(), false, item.getFieldAlignmentType()); + } + else if (item.isBarcode()) + { + Object obj = null; + if (item.getAD_Column_ID() > 0) // teo_sarca, [ 1673542 ] + obj = printData.getNode(new Integer(item.getAD_Column_ID())); + if (obj == null) + ; + else if (obj instanceof PrintDataElement) + { + PrintDataElement pde = (PrintDataElement)obj; + // Get the PrintDataElement string value - teo_sarca [ 1673505 ] + String value = null; + Object o = pde.getValue(); + if (o == null) + value = ""; + if (o instanceof KeyNamePair) + value = ((KeyNamePair)o).getID(); + else + value = o.toString(); + BarcodeElement element = new BarcodeElement (value, item); + + if (element.isValid()) + data[row][col] = element; + } + } + else + { + Object obj = null; + if (item.getAD_Column_ID() > 0) // teo_sarca, [ 1673542 ] + obj = printData.getNode(new Integer(item.getAD_Column_ID())); + if (obj == null) + ; + else if (obj instanceof PrintDataElement) + { + PrintDataElement pde = (PrintDataElement)obj; + if (pde.isID() || pde.isYesNo()) + dataElement = pde.getValue(); + else + dataElement = pde.getValueDisplay(format.getLanguage()); + } + else + log.log(Level.SEVERE, "Element not PrintDataElement " + obj.getClass()); + // System.out.println(" row=" + row + ",col=" + col + " - " + item.getAD_Column_ID() + " => " + dataElement); + data[row][col] = dataElement; + } + col++; + } // printed + } // for all columns + + PrintDataElement pde = printData.getPKey(); + if (pde != null) // for FunctionRows + { + pk[row] = (KeyNamePair)pde.getValue(); + if (pkColumnName == null) + pkColumnName = pde.getColumnName(); + } + // else + // System.out.println("No PK " + printData); + } // for all rows + + // + TableElement table = new TableElement(columnHeader, + columnMaxWidth, columnMaxHeight, columnJustification, + fixedWidth, functionRows, multiLineHeader, + data, pk, pkColumnName, + pageNoStart, firstPage, nextPages, repeatedColumns, additionalLines, + rowColFont, rowColColor, rowColBackground, + tf, pageBreak); + table.layout(0,0,false, MPrintFormatItem.FIELDALIGNMENTTYPE_LeadingLeft); + if (m_tableElement == null) + m_tableElement = table; + return table; + } // layoutTable + + /** + * Layout Parameter based on MQuery + * @return PrintElement + */ + private PrintElement layoutParameter () + { + if (m_query == null || !m_query.isActive()) + return null; + // + ParameterElement pe = new ParameterElement(m_query, m_printCtx, m_format.getTableFormat()); + pe.layout(0, 0, false, null); + return pe; + } // layoutParameter + + + /************************************************************************** + * Get number of pages (Pageable Interface) + * @return number of pages + */ + public int getNumberOfPages() + { + return m_pages.size(); + } // getNumberOfPages + + /** + * Get Page Format (Pageable Interface) + * @param pageIndex page index + * @return Page Format + * @throws IndexOutOfBoundsException + */ + public PageFormat getPageFormat (int pageIndex) throws IndexOutOfBoundsException + { + if (!havePage(pageIndex)) + throw new IndexOutOfBoundsException("No page index=" + pageIndex); + return getPageFormat(); + } // getPageFormat + + /** + * Get Printable (PageableInterface) + * @param pageIndex page index + * @return this + * @throws IndexOutOfBoundsException + */ + public Printable getPrintable (int pageIndex) throws IndexOutOfBoundsException + { + if (!havePage(pageIndex)) + throw new IndexOutOfBoundsException("No page index=" + pageIndex); + return this; + } // getPrintable + + /** + * Print Page (Printable Interface) + * @param graphics graphics + * @param pageFormat page format (ignored) + * @param pageIndex page index + * @return PageExists/NoSuchPage + * @throws PrinterException + */ + public int print (Graphics graphics, PageFormat pageFormat, int pageIndex) + throws PrinterException + { + if (!havePage(pageIndex)) + return Printable.NO_SUCH_PAGE; + // + Rectangle r = new Rectangle (0, 0, (int)getPaper().getWidth(true), (int)getPaper().getHeight(true)); + Page page = getPage(pageIndex+1); + // + // log.fine("#" + m_id, "PageIndex=" + pageIndex + ", Copy=" + m_isCopy); + page.paint((Graphics2D)graphics, r, false, m_isCopy); // sets context + getHeaderFooter().paint((Graphics2D)graphics, r, false); + // + return Printable.PAGE_EXISTS; + } // print + + /** + * Do we have the page + * @param pageIndex page index + * @return true if page exists + */ + private boolean havePage (int pageIndex) + { + if (pageIndex < 0 || pageIndex >= getNumberOfPages()) + return false; + return true; + } // havePage + + /** + * Print Copy + * @return true if copy + */ + public boolean isCopy() + { + return m_isCopy; + } // isCopy + + /** + * Set Copy + * @param isCopy if true document is a copy + */ + public void setCopy (boolean isCopy) + { + m_isCopy = isCopy; + } // setCopy + + /************************************************************************** + * Get the doc flavor (Doc Interface) + * @return SERVICE_FORMATTED.PAGEABLE + */ + public DocFlavor getDocFlavor() + { + return DocFlavor.SERVICE_FORMATTED.PAGEABLE; + } // getDocFlavor + + /** + * Get Print Data (Doc Interface) + * @return this + * @throws IOException + */ + public Object getPrintData() throws IOException + { + return this; + } // getPrintData + + /** + * Get Document Attributes (Doc Interface) + * @return null to obtain all attribute values from the + * job's attribute set. + */ + public DocAttributeSet getAttributes() + { + return null; + } // getAttributes + + /** + * Obtains a reader for extracting character print data from this doc. + * (Doc Interface) + * @return null + * @exception IOException + */ + public Reader getReaderForText() throws IOException + { + return null; + } // getReaderForText + + /** + * Obtains an input stream for extracting byte print data from this doc. + * (Doc Interface) + * @return null + * @exception IOException + */ + public InputStream getStreamForBytes() throws IOException + { + return null; + } // getStreamForBytes + +} // LayoutEngine diff --git a/base/src/org/compiere/process/Aging.java b/base/src/org/compiere/process/Aging.java new file mode 100644 index 0000000000..7102b6d61b --- /dev/null +++ b/base/src/org/compiere/process/Aging.java @@ -0,0 +1,212 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.process; + +import java.math.*; +import java.sql.*; +import java.util.logging.*; +import org.compiere.model.*; +import org.compiere.util.*; + +/** + * Invoice Aging Report. + * Based on RV_Aging. + * @author Jorg Janke + * @version $Id: Aging.java,v 1.5 2006/10/07 00:58:44 jjanke Exp $ + */ +public class Aging extends SvrProcess +{ + /** The date to calculate the days due from */ + private Timestamp p_StatementDate = null; + private boolean p_IsSOTrx = false; + private int p_C_Currency_ID = 0; + private int p_C_BP_Group_ID = 0; + private int p_C_BPartner_ID = 0; + private boolean p_IsListInvoices = false; + /** Number of days between today and statement date */ + private int m_statementOffset = 0; + + /** + * Prepare - e.g., get Parameters. + */ + protected void prepare() + { + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) + { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null) + ; + else if (name.equals("StatementDate")) + p_StatementDate = (Timestamp)para[i].getParameter(); + else if (name.equals("IsSOTrx")) + p_IsSOTrx = "Y".equals(para[i].getParameter()); + else if (name.equals("C_Currency_ID")) + p_C_Currency_ID = ((BigDecimal)para[i].getParameter()).intValue(); + else if (name.equals("C_BP_Group_ID")) + p_C_BP_Group_ID = ((BigDecimal)para[i].getParameter()).intValue(); + else if (name.equals("C_BPartner_ID")) + p_C_BPartner_ID = ((BigDecimal)para[i].getParameter()).intValue(); + else if (name.equals("IsListInvoices")) + p_IsListInvoices = "Y".equals(para[i].getParameter()); + else + log.log(Level.SEVERE, "Unknown Parameter: " + name); + } + if (p_StatementDate == null) + p_StatementDate = new Timestamp (System.currentTimeMillis()); + else + m_statementOffset = TimeUtil.getDaysBetween( + new Timestamp(System.currentTimeMillis()), p_StatementDate); + } // prepare + + /** + * DoIt + * @return Message + * @throws Exception + */ + protected String doIt() throws Exception + { + log.info("StatementDate=" + p_StatementDate + ", IsSOTrx=" + p_IsSOTrx + + ", C_Currency_ID=" + p_C_Currency_ID + + ", C_BP_Group_ID=" + p_C_BP_Group_ID + ", C_BPartner_ID=" + p_C_BPartner_ID + + ", IsListInvoices=" + p_IsListInvoices); + // + StringBuffer sql = new StringBuffer(); + sql.append("SELECT bp.C_BP_Group_ID, oi.C_BPartner_ID,oi.C_Invoice_ID,oi.C_InvoicePaySchedule_ID, " + + "oi.C_Currency_ID, oi.IsSOTrx, " // 5..6 + + "oi.DateInvoiced, oi.NetDays,oi.DueDate,oi.DaysDue, "); // 7..10 + if (p_C_Currency_ID == 0) + sql.append("oi.GrandTotal, oi.PaidAmt, oi.OpenAmt "); // 11..13 + else + { + String s = ",oi.C_Currency_ID," + p_C_Currency_ID + ",oi.DateAcct,oi.C_ConversionType_ID,oi.AD_Client_ID,oi.AD_Org_ID)"; + sql.append("currencyConvert(oi.GrandTotal").append(s) // 11.. + .append(", currencyConvert(oi.PaidAmt").append(s) + .append(", currencyConvert(oi.OpenAmt").append(s); + } + sql.append(",oi.C_Activity_ID,oi.C_Campaign_ID,oi.C_Project_ID " // 14 + + "FROM RV_OpenItem oi" + + " INNER JOIN C_BPartner bp ON (oi.C_BPartner_ID=bp.C_BPartner_ID) " + + "WHERE oi.ISSoTrx=").append(p_IsSOTrx ? "'Y'" : "'N'"); + if (p_C_BPartner_ID > 0) + sql.append(" AND oi.C_BPartner_ID=").append(p_C_BPartner_ID); + else if (p_C_BP_Group_ID > 0) + sql.append(" AND bp.C_BP_Group_ID=").append(p_C_BP_Group_ID); + sql.append(" ORDER BY oi.C_BPartner_ID, oi.C_Currency_ID, oi.C_Invoice_ID"); + + log.finest(sql.toString()); + String finalSql = MRole.getDefault(getCtx(), false).addAccessSQL( + sql.toString(), "oi", MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO); + log.finer(finalSql); + + PreparedStatement pstmt = null; + // + MAging aging = null; + int counter = 0; + int rows = 0; + int AD_PInstance_ID = getAD_PInstance_ID(); + // + try + { + pstmt = DB.prepareStatement(finalSql, get_TrxName()); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + int C_BP_Group_ID = rs.getInt(1); + int C_BPartner_ID = rs.getInt(2); + int C_Invoice_ID = p_IsListInvoices ? rs.getInt(3) : 0; + int C_InvoicePaySchedule_ID = p_IsListInvoices ? rs.getInt(4) : 0; + int C_Currency_ID = rs.getInt(5); + boolean IsSOTrx = "Y".equals(rs.getString(6)); + // + Timestamp DateInvoiced = rs.getTimestamp(7); + int NetDays = rs.getInt(8); + Timestamp DueDate = rs.getTimestamp(9); + // Days Due + int DaysDue = rs.getInt(10) // based on today + + m_statementOffset; + // + BigDecimal GrandTotal = rs.getBigDecimal(11); + BigDecimal PaidAmt = rs.getBigDecimal(12); + BigDecimal OpenAmt = rs.getBigDecimal(13); + // + int C_Activity_ID = p_IsListInvoices ? rs.getInt(14) : 0; + int C_Campaign_ID = p_IsListInvoices ? rs.getInt(15) : 0; + int C_Project_ID = p_IsListInvoices ? rs.getInt(16) : 0; + + rows++; + // New Aging Row + if (aging == null // Key + || AD_PInstance_ID != aging.getAD_PInstance_ID() + || C_BPartner_ID != aging.getC_BPartner_ID() + || C_Currency_ID != aging.getC_Currency_ID() + || C_Invoice_ID != aging.getC_Invoice_ID() + || C_InvoicePaySchedule_ID != aging.getC_InvoicePaySchedule_ID()) + { + if (aging != null) + { + if (aging.save()) + log.fine("#" + ++counter + " - " + aging); + else + { + log.log(Level.SEVERE, "Not saved " + aging); + break; + } + } + aging = new MAging (getCtx(), AD_PInstance_ID, p_StatementDate, + C_BPartner_ID, C_Currency_ID, + C_Invoice_ID, C_InvoicePaySchedule_ID, + C_BP_Group_ID, DueDate, IsSOTrx, get_TrxName()); + aging.setC_Activity_ID(C_Activity_ID); + aging.setC_Campaign_ID(C_Campaign_ID); + aging.setC_Project_ID(C_Project_ID); + } + // Fill Buckets + aging.add (DueDate, DaysDue, GrandTotal, OpenAmt); + } + if (aging != null) + { + if (aging.save()) + log.fine("#" + ++counter + " - " + aging); + else + log.log(Level.SEVERE, "Not saved " + aging); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, finalSql, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + // + log.info("#" + counter + " - rows=" + rows); + return ""; + } // doIt + +} // Aging + diff --git a/base/src/org/compiere/process/DocumentEngine.java b/base/src/org/compiere/process/DocumentEngine.java new file mode 100644 index 0000000000..c7d42e827b --- /dev/null +++ b/base/src/org/compiere/process/DocumentEngine.java @@ -0,0 +1,1136 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.process; + +import java.io.File; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Properties; +import java.util.Vector; +import java.util.logging.Level; + +import javax.naming.InitialContext; + +import org.compiere.db.CConnection; +import org.compiere.interfaces.Server; +import org.compiere.interfaces.ServerHome; +import org.compiere.model.MAllocationHdr; +import org.compiere.model.MBankStatement; +import org.compiere.model.MClient; +import org.compiere.model.MInOut; +import org.compiere.model.MInventory; +import org.compiere.model.MInvoice; +import org.compiere.model.MJournal; +import org.compiere.model.MJournalBatch; +import org.compiere.model.MMovement; +import org.compiere.model.MOrder; +import org.compiere.model.MPayment; +import org.compiere.model.X_C_Order; +import org.compiere.util.CLogger; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.Ini; + +/** + * Document Action Engine + * + * @author Jorg Janke + * @author Karsten Thiemann FR [ 1782412 ] + * @version $Id: DocumentEngine.java,v 1.2 2006/07/30 00:54:44 jjanke Exp $ + */ +public class DocumentEngine implements DocAction +{ + /** + * Doc Engine (Drafted) + * @param po document + */ + public DocumentEngine (DocAction po) + { + this (po, STATUS_Drafted); + } // DocActionEngine + + /** + * Doc Engine + * @param po document + * @param docStatus initial document status + */ + public DocumentEngine (DocAction po, String docStatus) + { + m_document = po; + if (docStatus != null) + m_status = docStatus; + } // DocActionEngine + + /** Persistent Document */ + private DocAction m_document; + /** Document Status */ + private String m_status = STATUS_Drafted; + /** Process Message */ + private String m_message = null; + /** Actual Doc Action */ + private String m_action = null; + + /** Logger */ + private static CLogger log = CLogger.getCLogger(DocumentEngine.class); + + /** + * Get Doc Status + * @return document status + */ + public String getDocStatus() + { + return m_status; + } // getDocStatus + + /** + * Set Doc Status - Ignored + * @param ignored Status is not set directly + * @see org.compiere.process.DocAction#setDocStatus(String) + */ + public void setDocStatus(String ignored) + { + } // setDocStatus + + /** + * Document is Drafted + * @return true if drafted + */ + public boolean isDrafted() + { + return STATUS_Drafted.equals(m_status); + } // isDrafted + + /** + * Document is Invalid + * @return true if Invalid + */ + public boolean isInvalid() + { + return STATUS_Invalid.equals(m_status); + } // isInvalid + + /** + * Document is In Progress + * @return true if In Progress + */ + public boolean isInProgress() + { + return STATUS_InProgress.equals(m_status); + } // isInProgress + + /** + * Document is Approved + * @return true if Approved + */ + public boolean isApproved() + { + return STATUS_Approved.equals(m_status); + } // isApproved + + /** + * Document is Not Approved + * @return true if Not Approved + */ + public boolean isNotApproved() + { + return STATUS_NotApproved.equals(m_status); + } // isNotApproved + + /** + * Document is Waiting Payment or Confirmation + * @return true if Waiting Payment + */ + public boolean isWaiting() + { + return STATUS_WaitingPayment.equals(m_status) + || STATUS_WaitingConfirmation.equals(m_status); + } // isWaitingPayment + + /** + * Document is Completed + * @return true if Completed + */ + public boolean isCompleted() + { + return STATUS_Completed.equals(m_status); + } // isCompleted + + /** + * Document is Reversed + * @return true if Reversed + */ + public boolean isReversed() + { + return STATUS_Reversed.equals(m_status); + } // isReversed + + /** + * Document is Closed + * @return true if Closed + */ + public boolean isClosed() + { + return STATUS_Closed.equals(m_status); + } // isClosed + + /** + * Document is Voided + * @return true if Voided + */ + public boolean isVoided() + { + return STATUS_Voided.equals(m_status); + } // isVoided + + /** + * Document Status is Unknown + * @return true if unknown + */ + public boolean isUnknown() + { + return STATUS_Unknown.equals(m_status) || + !(isDrafted() || isInvalid() || isInProgress() || isNotApproved() + || isApproved() || isWaiting() || isCompleted() + || isReversed() || isClosed() || isVoided() ); + } // isUnknown + + + /** + * Process actual document. + * Checks if user (document) action is valid and then process action + * Calls the individual actions which call the document action + * @param processAction document action based on workflow + * @param docAction document action based on document + * @return true if performed + */ + public boolean processIt (String processAction, String docAction) + { + m_message = null; + m_action = null; + // Std User Workflows - see MWFNodeNext.isValidFor + + if (isValidAction(processAction)) // WF Selection first + m_action = processAction; + // + else if (isValidAction(docAction)) // User Selection second + m_action = docAction; + // Nothing to do + else if (processAction.equals(ACTION_None) + || docAction.equals(ACTION_None)) + { + if (m_document != null) + m_document.get_Logger().info ("**** No Action (Prc=" + processAction + "/Doc=" + docAction + ") " + m_document); + return true; + } + else + { + throw new IllegalStateException("Status=" + getDocStatus() + + " - Invalid Actions: Process=" + processAction + ", Doc=" + docAction); + } + if (m_document != null) + m_document.get_Logger().info ("**** Action=" + m_action + " (Prc=" + processAction + "/Doc=" + docAction + ") " + m_document); + boolean success = processIt (m_action); + if (m_document != null) + m_document.get_Logger().fine("**** Action=" + m_action + " - Success=" + success); + return success; + } // process + + /** + * Process actual document - do not call directly. + * Calls the individual actions which call the document action + * @param action document action + * @return true if performed + */ + public boolean processIt (String action) + { + m_message = null; + m_action = action; + // + if (ACTION_Unlock.equals(m_action)) + return unlockIt(); + if (ACTION_Invalidate.equals(m_action)) + return invalidateIt(); + if (ACTION_Prepare.equals(m_action)) + return STATUS_InProgress.equals(prepareIt()); + if (ACTION_Approve.equals(m_action)) + return approveIt(); + if (ACTION_Reject.equals(m_action)) + return rejectIt(); + if (ACTION_Complete.equals(m_action) || ACTION_WaitComplete.equals(m_action)) + { + String status = null; + if (isDrafted() || isInvalid()) // prepare if not prepared yet + { + status = prepareIt(); + if (!STATUS_InProgress.equals(status)) + return false; + } + status = completeIt(); + if (m_document != null + && !Ini.isClient()) // Post Immediate if on Server + { + MClient client = MClient.get(m_document.getCtx(), m_document.getAD_Client_ID()); + if (STATUS_Completed.equals(status) && client.isPostImmediate()) + { + m_document.save(); + postIt(); + } + } + return STATUS_Completed.equals(status) + || STATUS_InProgress.equals(status) + || STATUS_WaitingPayment.equals(status) + || STATUS_WaitingConfirmation.equals(status); + } + if (ACTION_ReActivate.equals(m_action)) + return reActivateIt(); + if (ACTION_Reverse_Accrual.equals(m_action)) + return reverseAccrualIt(); + if (ACTION_Reverse_Correct.equals(m_action)) + return reverseCorrectIt(); + if (ACTION_Close.equals(m_action)) + return closeIt(); + if (ACTION_Void.equals(m_action)) + return voidIt(); + if (ACTION_Post.equals(m_action)) + return postIt(); + // + return false; + } // processDocument + + /** + * Unlock Document. + * Status: Drafted + * @return true if success + * @see org.compiere.process.DocAction#unlockIt() + */ + public boolean unlockIt() + { + if (!isValidAction(ACTION_Unlock)) + return false; + if (m_document != null) + { + if (m_document.unlockIt()) + { + m_status = STATUS_Drafted; + m_document.setDocStatus(m_status); + return true; + } + return false; + } + m_status = STATUS_Drafted; + return true; + } // unlockIt + + /** + * Invalidate Document. + * Status: Invalid + * @return true if success + * @see org.compiere.process.DocAction#invalidateIt() + */ + public boolean invalidateIt() + { + if (!isValidAction(ACTION_Invalidate)) + return false; + if (m_document != null) + { + if (m_document.invalidateIt()) + { + m_status = STATUS_Invalid; + m_document.setDocStatus(m_status); + return true; + } + return false; + } + m_status = STATUS_Invalid; + return true; + } // invalidateIt + + /** + * Process Document. + * Status is set by process + * @return new status (In Progress or Invalid) + * @see org.compiere.process.DocAction#prepareIt() + */ + public String prepareIt() + { + if (!isValidAction(ACTION_Prepare)) + return m_status; + if (m_document != null) + { + m_status = m_document.prepareIt(); + m_document.setDocStatus(m_status); + } + return m_status; + } // processIt + + /** + * Approve Document. + * Status: Approved + * @return true if success + * @see org.compiere.process.DocAction#approveIt() + */ + public boolean approveIt() + { + if (!isValidAction(ACTION_Approve)) + return false; + if (m_document != null) + { + if (m_document.approveIt()) + { + m_status = STATUS_Approved; + m_document.setDocStatus(m_status); + return true; + } + return false; + } + m_status = STATUS_Approved; + return true; + } // approveIt + + /** + * Reject Approval. + * Status: Not Approved + * @return true if success + * @see org.compiere.process.DocAction#rejectIt() + */ + public boolean rejectIt() + { + if (!isValidAction(ACTION_Reject)) + return false; + if (m_document != null) + { + if (m_document.rejectIt()) + { + m_status = STATUS_NotApproved; + m_document.setDocStatus(m_status); + return true; + } + return false; + } + m_status = STATUS_NotApproved; + return true; + } // rejectIt + + /** + * Complete Document. + * Status is set by process + * @return new document status (Complete, In Progress, Invalid, Waiting ..) + * @see org.compiere.process.DocAction#completeIt() + */ + public String completeIt() + { + if (!isValidAction(ACTION_Complete)) + return m_status; + if (m_document != null) + { + m_status = m_document.completeIt(); + m_document.setDocStatus(m_status); + } + return m_status; + } // completeIt + + /** + * Post Document + * Does not change status + * @return true if success + */ + public boolean postIt() + { + if (!isValidAction(ACTION_Post) + || m_document == null) + return false; + try + { + // Should work on Client and Server + InitialContext ctx = CConnection.get().getInitialContext(true); + ServerHome serverHome = (ServerHome)ctx.lookup (ServerHome.JNDI_NAME); + if (serverHome != null) + { + Server server = serverHome.create(); + if (server != null) + { + String error = server.postImmediate(Env.getCtx(), + m_document.getAD_Client_ID(), + m_document.get_Table_ID(), m_document.get_ID(), + true, m_document.get_TrxName()); + m_document.get_Logger().config("Server: " + error == null ? "OK" : error); + return error == null; + } + } + else + m_document.get_Logger().config("NoServerHome"); + } + catch (Exception e) + { + m_document.get_Logger().config("(ex) " + e.getMessage()); + } + return false; + } // postIt + + /** + * Void Document. + * Status: Voided + * @return true if success + * @see org.compiere.process.DocAction#voidIt() + */ + public boolean voidIt() + { + if (!isValidAction(ACTION_Void)) + return false; + if (m_document != null) + { + if (m_document.voidIt()) + { + m_status = STATUS_Voided; + m_document.setDocStatus(m_status); + return true; + } + return false; + } + m_status = STATUS_Voided; + return true; + } // voidIt + + /** + * Close Document. + * Status: Closed + * @return true if success + * @see org.compiere.process.DocAction#closeIt() + */ + public boolean closeIt() + { + if (m_document != null // orders can be closed any time + && m_document.get_Table_ID() == X_C_Order.Table_ID) + ; + else if (!isValidAction(ACTION_Close)) + return false; + if (m_document != null) + { + if (m_document.closeIt()) + { + m_status = STATUS_Closed; + m_document.setDocStatus(m_status); + return true; + } + return false; + } + m_status = STATUS_Closed; + return true; + } // closeIt + + /** + * Reverse Correct Document. + * Status: Reversed + * @return true if success + * @see org.compiere.process.DocAction#reverseCorrectIt() + */ + public boolean reverseCorrectIt() + { + if (!isValidAction(ACTION_Reverse_Correct)) + return false; + if (m_document != null) + { + if (m_document.reverseCorrectIt()) + { + m_status = STATUS_Reversed; + m_document.setDocStatus(m_status); + return true; + } + return false; + } + m_status = STATUS_Reversed; + return true; + } // reverseCorrectIt + + /** + * Reverse Accrual Document. + * Status: Reversed + * @return true if success + * @see org.compiere.process.DocAction#reverseAccrualIt() + */ + public boolean reverseAccrualIt() + { + if (!isValidAction(ACTION_Reverse_Accrual)) + return false; + if (m_document != null) + { + if (m_document.reverseAccrualIt()) + { + m_status = STATUS_Reversed; + m_document.setDocStatus(m_status); + return true; + } + return false; + } + m_status = STATUS_Reversed; + return true; + } // reverseAccrualIt + + /** + * Re-activate Document. + * Status: In Progress + * @return true if success + * @see org.compiere.process.DocAction#reActivateIt() + */ + public boolean reActivateIt() + { + if (!isValidAction(ACTION_ReActivate)) + return false; + if (m_document != null) + { + if (m_document.reActivateIt()) + { + m_status = STATUS_InProgress; + m_document.setDocStatus(m_status); + return true; + } + return false; + } + m_status = STATUS_InProgress; + return true; + } // reActivateIt + + + /** + * Set Document Status to new Status + * @param newStatus new status + */ + void setStatus (String newStatus) + { + m_status = newStatus; + } // setStatus + + + /************************************************************************** + * Get Action Options based on current Status + * @return array of actions + */ + public String[] getActionOptions() + { + if (isInvalid()) + return new String[] {ACTION_Prepare, ACTION_Invalidate, + ACTION_Unlock, ACTION_Void}; + + if (isDrafted()) + return new String[] {ACTION_Prepare, ACTION_Invalidate, ACTION_Complete, + ACTION_Unlock, ACTION_Void}; + + if (isInProgress() || isApproved()) + return new String[] {ACTION_Complete, ACTION_WaitComplete, + ACTION_Approve, ACTION_Reject, + ACTION_Unlock, ACTION_Void, ACTION_Prepare}; + + if (isNotApproved()) + return new String[] {ACTION_Reject, ACTION_Prepare, + ACTION_Unlock, ACTION_Void}; + + if (isWaiting()) + return new String[] {ACTION_Complete, ACTION_WaitComplete, + ACTION_ReActivate, ACTION_Void, ACTION_Close}; + + if (isCompleted()) + return new String[] {ACTION_Close, ACTION_ReActivate, + ACTION_Reverse_Accrual, ACTION_Reverse_Correct, + ACTION_Post, ACTION_Void}; + + if (isClosed()) + return new String[] {ACTION_Post, ACTION_ReOpen}; + + if (isReversed() || isVoided()) + return new String[] {ACTION_Post}; + + return new String[] {}; + } // getActionOptions + + /** + * Is The Action Valid based on current state + * @param action action + * @return true if valid + */ + public boolean isValidAction (String action) + { + String[] options = getActionOptions(); + for (int i = 0; i < options.length; i++) + { + if (options[i].equals(action)) + return true; + } + return false; + } // isValidAction + + /** + * Get Process Message + * @return clear text error message + */ + public String getProcessMsg () + { + return m_message; + } // getProcessMsg + + /** + * Get Process Message + * @param msg clear text error message + */ + public void setProcessMsg (String msg) + { + m_message = msg; + } // setProcessMsg + + + /** Document Exception Message */ + private static String EXCEPTION_MSG = "Document Engine is no Document"; + + /************************************************************************* + * Get Summary + * @return throw exception + */ + public String getSummary() + { + throw new IllegalStateException(EXCEPTION_MSG); + } + + /** + * Get Document No + * @return throw exception + */ + public String getDocumentNo() + { + throw new IllegalStateException(EXCEPTION_MSG); + } + + /** + * Get Document Info + * @return throw exception + */ + public String getDocumentInfo() + { + throw new IllegalStateException(EXCEPTION_MSG); + } + + /** + * Get Document Owner + * @return throw exception + */ + public int getDoc_User_ID() + { + throw new IllegalStateException(EXCEPTION_MSG); + } + + /** + * Get Document Currency + * @return throw exception + */ + public int getC_Currency_ID() + { + throw new IllegalStateException(EXCEPTION_MSG); + } + + /** + * Get Document Approval Amount + * @return throw exception + */ + public BigDecimal getApprovalAmt() + { + throw new IllegalStateException(EXCEPTION_MSG); + } + + /** + * Get Document Client + * @return throw exception + */ + public int getAD_Client_ID() + { + throw new IllegalStateException(EXCEPTION_MSG); + } + + /** + * Get Document Organization + * @return throw exception + */ + public int getAD_Org_ID() + { + throw new IllegalStateException(EXCEPTION_MSG); + } + + /** + * Get Doc Action + * @return Document Action + */ + public String getDocAction() + { + return m_action; + } + + /** + * Save Document + * @return throw exception + */ + public boolean save() + { + throw new IllegalStateException(EXCEPTION_MSG); + } + + /** + * Get Context + * @return context + */ + public Properties getCtx() + { + if (m_document != null) + return m_document.getCtx(); + throw new IllegalStateException(EXCEPTION_MSG); + } // getCtx + + /** + * Get ID of record + * @return ID + */ + public int get_ID() + { + if (m_document != null) + return m_document.get_ID(); + throw new IllegalStateException(EXCEPTION_MSG); + } // get_ID + + /** + * Get AD_Table_ID + * @return AD_Table_ID + */ + public int get_Table_ID() + { + if (m_document != null) + return m_document.get_Table_ID(); + throw new IllegalStateException(EXCEPTION_MSG); + } // get_Table_ID + + /** + * Get Logger + * @return logger + */ + public CLogger get_Logger() + { + if (m_document != null) + return m_document.get_Logger(); + throw new IllegalStateException(EXCEPTION_MSG); + } // get_Logger + + /** + * Get Transaction + * @return trx name + */ + public String get_TrxName() + { + return null; + } // get_TrxName + + /** + * CreatePDF + * @return null + */ + public File createPDF () + { + return null; + } + + /** + * Get list of valid document action into the options arary parameter. + * Set default document action into the docAction array parameter. + * @param docStatus + * @param processing + * @param orderType + * @param isSOTrx + * @param AD_Table_ID + * @param docAction + * @param options + * @return Number of valid options + */ + public static int getValidActions(String docStatus, Object processing, + String orderType, String isSOTrx, int AD_Table_ID, String[] docAction, String[] options) + { + if (options == null) + throw new IllegalArgumentException("Option array parameter is null"); + if (docAction == null) + throw new IllegalArgumentException("Doc action array parameter is null"); + + int index = 0; + +// Locked + if (processing != null) + { + boolean locked = "Y".equals(processing); + if (!locked && processing instanceof Boolean) + locked = ((Boolean)processing).booleanValue(); + if (locked) + options[index++] = DocumentEngine.ACTION_Unlock; + } + + // Approval required .. NA + if (docStatus.equals(DocumentEngine.STATUS_NotApproved)) + { + options[index++] = DocumentEngine.ACTION_Prepare; + options[index++] = DocumentEngine.ACTION_Void; + } + // Draft/Invalid .. DR/IN + else if (docStatus.equals(DocumentEngine.STATUS_Drafted) + || docStatus.equals(DocumentEngine.STATUS_Invalid)) + { + options[index++] = DocumentEngine.ACTION_Complete; + // options[index++] = DocumentEngine.ACTION_Prepare; + options[index++] = DocumentEngine.ACTION_Void; + } + // In Process .. IP + else if (docStatus.equals(DocumentEngine.STATUS_InProgress) + || docStatus.equals(DocumentEngine.STATUS_Approved)) + { + options[index++] = DocumentEngine.ACTION_Complete; + options[index++] = DocumentEngine.ACTION_Void; + } + // Complete .. CO + else if (docStatus.equals(DocumentEngine.STATUS_Completed)) + { + options[index++] = DocumentEngine.ACTION_Close; + } + // Waiting Payment + else if (docStatus.equals(DocumentEngine.STATUS_WaitingPayment) + || docStatus.equals(DocumentEngine.STATUS_WaitingConfirmation)) + { + options[index++] = DocumentEngine.ACTION_Void; + options[index++] = DocumentEngine.ACTION_Prepare; + } + // Closed, Voided, REversed .. CL/VO/RE + else if (docStatus.equals(DocumentEngine.STATUS_Closed) + || docStatus.equals(DocumentEngine.STATUS_Voided) + || docStatus.equals(DocumentEngine.STATUS_Reversed)) + return 0; + + /******************** + * Order + */ + if (AD_Table_ID == MOrder.Table_ID) + { + // Draft .. DR/IP/IN + if (docStatus.equals(DocumentEngine.STATUS_Drafted) + || docStatus.equals(DocumentEngine.STATUS_InProgress) + || docStatus.equals(DocumentEngine.STATUS_Invalid)) + { + options[index++] = DocumentEngine.ACTION_Prepare; + options[index++] = DocumentEngine.ACTION_Close; + // Draft Sales Order Quote/Proposal - Process + if ("Y".equals(isSOTrx) + && ("OB".equals(orderType) || "ON".equals(orderType))) + docAction[0] = DocumentEngine.ACTION_Prepare; + } + // Complete .. CO + else if (docStatus.equals(DocumentEngine.STATUS_Completed)) + { + options[index++] = DocumentEngine.ACTION_Void; + options[index++] = DocumentEngine.ACTION_ReActivate; + } + else if (docStatus.equals(DocumentEngine.STATUS_WaitingPayment)) + { + options[index++] = DocumentEngine.ACTION_ReActivate; + options[index++] = DocumentEngine.ACTION_Close; + } + } + /******************** + * Shipment + */ + else if (AD_Table_ID == MInOut.Table_ID) + { + // Complete .. CO + if (docStatus.equals(DocumentEngine.STATUS_Completed)) + { + options[index++] = DocumentEngine.ACTION_Void; + options[index++] = DocumentEngine.ACTION_Reverse_Correct; + } + } + /******************** + * Invoice + */ + else if (AD_Table_ID == MInvoice.Table_ID) + { + // Complete .. CO + if (docStatus.equals(DocumentEngine.STATUS_Completed)) + { + options[index++] = DocumentEngine.ACTION_Void; + options[index++] = DocumentEngine.ACTION_Reverse_Correct; + } + } + /******************** + * Payment + */ + else if (AD_Table_ID == MPayment.Table_ID) + { + // Complete .. CO + if (docStatus.equals(DocumentEngine.STATUS_Completed)) + { + options[index++] = DocumentEngine.ACTION_Void; + options[index++] = DocumentEngine.ACTION_Reverse_Correct; + } + } + /******************** + * GL Journal + */ + else if (AD_Table_ID == MJournal.Table_ID || AD_Table_ID == MJournalBatch.Table_ID) + { + // Complete .. CO + if (docStatus.equals(DocumentEngine.STATUS_Completed)) + { + options[index++] = DocumentEngine.ACTION_Reverse_Correct; + options[index++] = DocumentEngine.ACTION_Reverse_Accrual; + } + } + /******************** + * Allocation + */ + else if (AD_Table_ID == MAllocationHdr.Table_ID) + { + // Complete .. CO + if (docStatus.equals(DocumentEngine.STATUS_Completed)) + { + options[index++] = DocumentEngine.ACTION_Void; + options[index++] = DocumentEngine.ACTION_Reverse_Correct; + } + } + /******************** + * Bank Statement + */ + else if (AD_Table_ID == MBankStatement.Table_ID) + { + // Complete .. CO + if (docStatus.equals(DocumentEngine.STATUS_Completed)) + { + options[index++] = DocumentEngine.ACTION_Void; + } + } + /******************** + * Inventory Movement, Physical Inventory + */ + else if (AD_Table_ID == MMovement.Table_ID + || AD_Table_ID == MInventory.Table_ID) + { + // Complete .. CO + if (docStatus.equals(DocumentEngine.STATUS_Completed)) + { + options[index++] = DocumentEngine.ACTION_Void; + options[index++] = DocumentEngine.ACTION_Reverse_Correct; + } + } + return index; + } + + /** + * Fill Vector with DocAction Ref_List(135) values + * @param v_value + * @param v_name + * @param v_description + */ + public static void readReferenceList(ArrayList v_value, ArrayList v_name, + ArrayList v_description) + { + if (v_value == null) + throw new IllegalArgumentException("v_value parameter is null"); + if (v_name == null) + throw new IllegalArgumentException("v_name parameter is null"); + if (v_description == null) + throw new IllegalArgumentException("v_description parameter is null"); + + String sql; + if (Env.isBaseLanguage(Env.getCtx(), "AD_Ref_List")) + sql = "SELECT Value, Name, Description FROM AD_Ref_List " + + "WHERE AD_Reference_ID=? ORDER BY Name"; + else + sql = "SELECT l.Value, t.Name, t.Description " + + "FROM AD_Ref_List l, AD_Ref_List_Trl t " + + "WHERE l.AD_Ref_List_ID=t.AD_Ref_List_ID" + + " AND t.AD_Language='" + Env.getAD_Language(Env.getCtx()) + "'" + + " AND l.AD_Reference_ID=? ORDER BY t.Name"; + + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, DocAction.AD_REFERENCE_ID); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + String value = rs.getString(1); + String name = rs.getString(2); + String description = rs.getString(3); + if (description == null) + description = ""; + // + v_value.add(value); + v_name.add(name); + v_description.add(description); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + } + + /** + * Checks the access rights of the given role/client for the given document actions. + * If no access rules can be found for a doctype/client/document action combination + * every role can access this combination (so no definition is needed for the default + * access rights). + * @param clientId + * @param roleId + * @param docTypeId + * @param options + * @param maxIndex + * @return number of valid actions in the String[] options + */ + public static int checkActionAccess(int clientId, int roleId, int docTypeId, String[] options, int maxIndex) { + final Vector validOptions = new Vector(); + String sql = "SELECT AD_Role_ID FROM AD_Document_Action_Access " + + "WHERE IsActive='Y' AND AD_Client_ID=? AND C_DocType_ID=? AND AD_Ref_List_ID=" + + "(SELECT AD_Ref_List_ID FROM AD_Ref_List WHERE AD_Reference_ID=135" + + " AND Value=?)"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + for (int i = 0; i < maxIndex; i++) { + pstmt.setInt(1, clientId); + pstmt.setInt(2, docTypeId); + pstmt.setString(3, options[i]); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + if(rs.getInt(1) == roleId){ + //is valid for role + validOptions.add(options[i]); + continue; + } + } + rs.close(); + } + validOptions.toArray(options); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + return validOptions.size(); + } +} // DocumentEnine diff --git a/base/src/org/compiere/process/M_Product_BOM_Check.java b/base/src/org/compiere/process/M_Product_BOM_Check.java new file mode 100644 index 0000000000..ac7cf671a9 --- /dev/null +++ b/base/src/org/compiere/process/M_Product_BOM_Check.java @@ -0,0 +1,181 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + * Portions created by Carlos Ruiz are Copyright (C) 2005 QSS Ltda. + * Contributor(s): Carlos Ruiz (globalqss) + *****************************************************************************/ +package org.compiere.process; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.logging.*; + +import org.compiere.model.X_M_Product; +import org.compiere.util.*; + +/** + * Title: Check BOM Structure (free of cycles) + * Description: + * Tree cannot contain BOMs which are already referenced + * + * @author Carlos Ruiz (globalqss) + * @version $Id: M_Product_BOM_Check.java,v 1.0 2005/09/17 13:32:00 globalqss Exp $ + * @author Carlos Ruiz (globalqss) + * Make T_Selection tables permanent + */ +public class M_Product_BOM_Check extends SvrProcess +{ + + /** The Record */ + private int p_Record_ID = 0; + + private int m_AD_PInstance_ID = 0; + + /** + * Prepare - e.g., get Parameters. + */ + protected void prepare() + { + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) + { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null) + ; + else + log.log(Level.SEVERE, "Unknown Parameter: " + name); + } + p_Record_ID = getRecord_ID(); + m_AD_PInstance_ID = getAD_PInstance_ID(); + } // prepare + + /** + * Process + * @return message + * @throws Exception + */ + protected String doIt() throws Exception + { + StringBuffer sql1 = null; + int no = 0; + + log.info("Check BOM Structure"); + + // Record ID is M_Product_ID of product to be tested + X_M_Product xp = new X_M_Product(Env.getCtx(), p_Record_ID, get_TrxName()); + + if (! xp.isBOM()) { + log.info("NOT BOM Product"); + // No BOM - should not happen, but no problem + xp.setIsVerified(true); + xp.save(get_TrxName()); + return "OK"; + } + + // Table to put all BOMs - duplicate will cause exception + sql1 = new StringBuffer("DELETE FROM T_Selection2 WHERE Query_ID = 0 AND AD_PInstance_ID="+ m_AD_PInstance_ID); + no = DB.executeUpdate(sql1.toString(), get_TrxName()); + sql1 = new StringBuffer("INSERT INTO T_Selection2 (AD_PInstance_ID, Query_ID, T_Selection_ID) VALUES (" + + m_AD_PInstance_ID + + ", 0, " + + p_Record_ID + ")"); + no = DB.executeUpdate(sql1.toString(), get_TrxName()); + // Table of root modes + sql1 = new StringBuffer("DELETE FROM T_Selection WHERE AD_PInstance_ID="+ m_AD_PInstance_ID); + no = DB.executeUpdate(sql1.toString(), get_TrxName()); + sql1 = new StringBuffer("INSERT INTO T_Selection (AD_PInstance_ID, T_Selection_ID) VALUES (" + + m_AD_PInstance_ID + + ", " + + p_Record_ID + ")"); + no = DB.executeUpdate(sql1.toString(), get_TrxName()); + + while (true) { + + // Get count remaining on t_selection + int countno = 0; + try + { + PreparedStatement pstmt = DB.prepareStatement + ("SELECT COUNT(*) FROM T_Selection WHERE AD_PInstance_ID="+ m_AD_PInstance_ID, get_TrxName()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + countno = rs.getInt(1); + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + throw new Exception ("count t_selection", e); + } + log.fine("Count T_Selection =" + countno); + + if (countno == 0) + break; + + try + { + // if any command fails (no==-1) break and inform failure + // Insert BOM Nodes into "All" table + sql1 = new StringBuffer("INSERT INTO T_Selection2 (AD_PInstance_ID, Query_ID, T_Selection_ID) " + + "SELECT " + m_AD_PInstance_ID + ", 0, p.M_Product_ID FROM M_Product p WHERE IsBOM='Y' AND EXISTS " + + "(SELECT * FROM M_Product_BOM b WHERE p.M_Product_ID=b.M_ProductBOM_ID AND b.M_Product_ID IN " + + "(SELECT T_Selection_ID FROM T_Selection WHERE AD_PInstance_ID="+ m_AD_PInstance_ID + "))"); + no = DB.executeUpdate(sql1.toString(), get_TrxName()); + if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString()); + // Insert BOM Nodes into temporary table + sql1 = new StringBuffer("DELETE FROM T_Selection2 WHERE Query_ID = 1 AND AD_PInstance_ID="+ m_AD_PInstance_ID); + no = DB.executeUpdate(sql1.toString(), get_TrxName()); + if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString()); + sql1 = new StringBuffer("INSERT INTO T_Selection2 (AD_PInstance_ID, Query_ID, T_Selection_ID) " + + "SELECT " + m_AD_PInstance_ID + ", 1, p.M_Product_ID FROM M_Product p WHERE IsBOM='Y' AND EXISTS " + + "(SELECT * FROM M_Product_BOM b WHERE p.M_Product_ID=b.M_ProductBOM_ID AND b.M_Product_ID IN " + + "(SELECT T_Selection_ID FROM T_Selection WHERE AD_PInstance_ID="+ m_AD_PInstance_ID + "))"); + no = DB.executeUpdate(sql1.toString(), get_TrxName()); + if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString()); + // Copy into root table + sql1 = new StringBuffer("DELETE FROM T_Selection WHERE AD_PInstance_ID="+ m_AD_PInstance_ID); + no = DB.executeUpdate(sql1.toString(), get_TrxName()); + if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString()); + sql1 = new StringBuffer("INSERT INTO T_Selection (AD_PInstance_ID, T_Selection_ID) " + + "SELECT " + m_AD_PInstance_ID + ", T_Selection_ID " + + "FROM T_Selection2 WHERE Query_ID = 1 AND AD_PInstance_ID="+ m_AD_PInstance_ID); + no = DB.executeUpdate(sql1.toString(), get_TrxName()); + if (no == -1) raiseError("InsertingRoot:ERROR", sql1.toString()); + } + catch (Exception e) + { + throw new Exception ("root insert", e); + } + + } + + // Finish process + xp.setIsVerified(true); + xp.save(get_TrxName()); + return "OK"; + } // doIt + + private void raiseError(String string, String sql) throws Exception { + DB.rollback(false, get_TrxName()); + String msg = string; + ValueNamePair pp = CLogger.retrieveError(); + if (pp != null) + msg = pp.getName() + " - "; + msg += sql; + throw new AdempiereUserError (msg); + } + +} // M_Product_BOM_Check diff --git a/base/src/org/compiere/process/OrderBatchProcess.java b/base/src/org/compiere/process/OrderBatchProcess.java new file mode 100644 index 0000000000..fee4ed4256 --- /dev/null +++ b/base/src/org/compiere/process/OrderBatchProcess.java @@ -0,0 +1,160 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.process; + +import java.sql.*; +import java.util.logging.*; +import org.compiere.model.*; +import org.compiere.util.*; + + +/** + * Order Batch Processing + * + * @author Jorg Janke + * @version $Id: OrderBatchProcess.java,v 1.2 2006/07/30 00:51:02 jjanke Exp $ + */ +public class OrderBatchProcess extends SvrProcess +{ + private int p_C_DocTypeTarget_ID = 0; + private String p_DocStatus = null; + private int p_C_BPartner_ID = 0; + private String p_IsSelfService = null; + private Timestamp p_DateOrdered_From = null; + private Timestamp p_DateOrdered_To = null; + private String p_DocAction = null; + + /** + * Prepare + */ + protected void prepare () + { + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) + { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null) + ; + else if (name.equals("C_DocTypeTarget_ID")) + p_C_DocTypeTarget_ID = para[i].getParameterAsInt(); + else if (name.equals("DocStatus")) + p_DocStatus = (String)para[i].getParameter(); + else if (name.equals("IsSelfService")) + p_IsSelfService = (String)para[i].getParameter(); + else if (name.equals("C_BPartner_ID")) + p_C_BPartner_ID = para[i].getParameterAsInt(); + else if (name.equals("DateOrdered")) + { + p_DateOrdered_From = (Timestamp)para[i].getParameter(); + p_DateOrdered_To = (Timestamp)para[i].getParameter_To(); + } + else if (name.equals("DocAction")) + p_DocAction = (String)para[i].getParameter(); + else + log.log(Level.SEVERE, "Unknown Parameter: " + name); + } + } // prepare + + /** + * Process + * @return msg + * @throws Exception + */ + protected String doIt () throws Exception + { + log.info("C_DocTypeTarget_ID=" + p_C_DocTypeTarget_ID + ", DocStatus=" + p_DocStatus + + ", IsSelfService=" + p_IsSelfService + ", C_BPartner_ID=" + p_C_BPartner_ID + + ", DateOrdered=" + p_DateOrdered_From + "->" + p_DateOrdered_To + + ", DocAction=" + p_DocAction); + + if (p_C_DocTypeTarget_ID == 0) + throw new AdempiereUserError("@NotFound@: @C_DocTypeTarget_ID@"); + if (p_DocStatus == null || p_DocStatus.length() != 2) + throw new AdempiereUserError("@NotFound@: @DocStatus@"); + if (p_DocAction == null || p_DocAction.length() != 2) + throw new AdempiereUserError("@NotFound@: @DocAction@"); + + // + StringBuffer sql = new StringBuffer("SELECT * FROM C_Order " + + "WHERE C_DocTypeTarget_ID=? AND DocStatus=?"); + if (p_IsSelfService != null && p_IsSelfService.length() == 1) + sql.append(" AND IsSelfService='").append(p_IsSelfService).append("'"); + if (p_C_BPartner_ID != 0) + sql.append(" AND C_BPartner_ID=").append(p_C_BPartner_ID); + if (p_DateOrdered_From != null) + sql.append(" AND TRUNC(DateOrdered) >= ").append(DB.TO_DATE(p_DateOrdered_From, true)); + if (p_DateOrdered_To != null) + sql.append(" AND TRUNC(DateOrdered) <= ").append(DB.TO_DATE(p_DateOrdered_To, true)); + + int counter = 0; + int errCounter = 0; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(sql.toString(), get_TrxName()); + pstmt.setInt(1, p_C_DocTypeTarget_ID); + pstmt.setString(2, p_DocStatus); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + if (process(new MOrder(getCtx(),rs, get_TrxName()))) + counter++; + else + errCounter++; + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql.toString(), e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + return "@Updated@=" + counter + ", @Errors@=" + errCounter; + } // doIt + + /** + * Process Order + * @param order order + * @return true if ok + */ + private boolean process (MOrder order) + { + log.info(order.toString()); + // + order.setDocAction(p_DocAction); + if (order.processIt(p_DocAction)) + { + order.save(); + addLog(0, null, null, order.getDocumentNo() + ": OK"); + return true; + } + addLog (0, null, null, order.getDocumentNo() + ": Error " + order.getProcessMsg()); + return false; + } // process + +} // OrderBatchProcess diff --git a/base/src/org/compiere/process/OrderOpen.java b/base/src/org/compiere/process/OrderOpen.java new file mode 100644 index 0000000000..3abd24fbd3 --- /dev/null +++ b/base/src/org/compiere/process/OrderOpen.java @@ -0,0 +1,72 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.process; + +import java.util.logging.*; +import org.compiere.model.*; + +/** + * Re-Open Order Process (from Closed to Completed) + * + * @author Jorg Janke + * @version $Id: OrderOpen.java,v 1.2 2006/07/30 00:51:02 jjanke Exp $ + */ +public class OrderOpen extends SvrProcess +{ + /** The Order */ + private int p_C_Order_ID = 0; + + /** + * Prepare - e.g., get Parameters. + */ + protected void prepare() + { + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) + { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null) + ; + else if (name.equals("C_Order_ID")) + p_C_Order_ID = para[i].getParameterAsInt(); + else + log.log(Level.SEVERE, "prepare - Unknown Parameter: " + name); + } + } // prepare + + /** + * Perrform process. + * @return Message + * @throws Exception if not successful + */ + protected String doIt() throws Exception + { + log.info("doIt - Open C_Order_ID=" + p_C_Order_ID); + if (p_C_Order_ID == 0) + throw new IllegalArgumentException("C_Order_ID == 0"); + // + MOrder order = new MOrder (getCtx(), p_C_Order_ID, get_TrxName()); + if (MOrder.DOCSTATUS_Closed.equals(order.getDocStatus())) + { + order.setDocStatus(MOrder.DOCSTATUS_Completed); + return order.save() ? "@OK@" : "@Error@"; + } + else + throw new IllegalStateException("Order is not closed"); + } // doIt + +} // OrderOpen diff --git a/base/src/org/compiere/process/OrderPOCreate.java b/base/src/org/compiere/process/OrderPOCreate.java new file mode 100644 index 0000000000..f355fa64ea --- /dev/null +++ b/base/src/org/compiere/process/OrderPOCreate.java @@ -0,0 +1,288 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.process; + +import java.awt.geom.IllegalPathStateException; +import java.math.BigDecimal; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Timestamp; +import java.util.logging.Level; + +import org.compiere.model.MBPartner; +import org.compiere.model.MOrder; +import org.compiere.model.MOrderLine; +import org.compiere.util.DB; + +/** + * Generate PO from Sales Order + * + * @author Jorg Janke + * @version $Id: OrderPOCreate.java,v 1.2 2006/07/30 00:51:01 jjanke Exp $ + */ +public class OrderPOCreate extends SvrProcess +{ + /** Order Date From */ + private Timestamp p_DateOrdered_From; + /** Order Date To */ + private Timestamp p_DateOrdered_To; + /** Customer */ + private int p_C_BPartner_ID; + /** Vendor */ + private int p_Vendor_ID; + /** Sales Order */ + private int p_C_Order_ID; + /** Drop Ship */ + private String p_IsDropShip; + + /** + * Prepare - e.g., get Parameters. + */ + protected void prepare() + { + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) + { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null) + ; + else if (name.equals("DateOrdered")) + { + p_DateOrdered_From = (Timestamp)para[i].getParameter(); + p_DateOrdered_To = (Timestamp)para[i].getParameter_To(); + } + else if (name.equals("C_BPartner_ID")) + p_C_BPartner_ID = ((BigDecimal)para[i].getParameter()).intValue(); + else if (name.equals("Vendor_ID")) + p_Vendor_ID = ((BigDecimal)para[i].getParameter()).intValue(); + else if (name.equals("C_Order_ID")) + p_C_Order_ID = ((BigDecimal)para[i].getParameter()).intValue(); + else if (name.equals("IsDropShip")) + p_IsDropShip = (String)para[i].getParameter(); + else + log.log(Level.SEVERE, "Unknown Parameter: " + name); + } + } // prepare + + /** + * Perrform process. + * @return Message + * @throws Exception if not successful + */ + protected String doIt() throws Exception + { + log.info("DateOrdered=" + p_DateOrdered_From + " - " + p_DateOrdered_To + + " - C_BPartner_ID=" + p_C_BPartner_ID + " - Vendor_ID=" + p_Vendor_ID + + " - IsDropShip=" + p_IsDropShip + " - C_Order_ID=" + p_C_Order_ID); + if (p_C_Order_ID == 0 && p_IsDropShip == null + && p_DateOrdered_From == null && p_DateOrdered_To == null + && p_C_BPartner_ID == 0 && p_Vendor_ID == 0) + throw new IllegalPathStateException("You need to restrict selection"); + // + String sql = "SELECT * FROM C_Order o " + + "WHERE o.IsSOTrx='Y'" + // No Duplicates + // " AND o.Ref_Order_ID IS NULL" + + " AND NOT EXISTS (SELECT * FROM C_OrderLine ol WHERE o.C_Order_ID=ol.C_Order_ID AND ol.Ref_OrderLine_ID IS NOT NULL)" + ; + if (p_C_Order_ID != 0) + sql += " AND o.C_Order_ID=?"; + else + { + if (p_C_BPartner_ID != 0) + sql += " AND o.C_BPartner_ID=?"; + if (p_IsDropShip != null) + sql += " AND o.IsDropShip=?"; + if (p_Vendor_ID != 0) + sql += " AND EXISTS (SELECT * FROM C_OrderLine ol" + + " INNER JOIN M_Product_PO po ON (ol.M_Product_ID=po.M_Product_ID) " + + "WHERE o.C_Order_ID=ol.C_Order_ID AND po.C_BPartner_ID=?)"; + if (p_DateOrdered_From != null && p_DateOrdered_To != null) + sql += "AND TRUNC(o.DateOrdered) BETWEEN ? AND ?"; + else if (p_DateOrdered_From != null && p_DateOrdered_To == null) + sql += "AND TRUNC(o.DateOrdered) >= ?"; + else if (p_DateOrdered_From == null && p_DateOrdered_To != null) + sql += "AND TRUNC(o.DateOrdered) <= ?"; + } + PreparedStatement pstmt = null; + ResultSet rs = null; + int counter = 0; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + if (p_C_Order_ID != 0) + pstmt.setInt (1, p_C_Order_ID); + else + { + int index = 1; + if (p_C_BPartner_ID != 0) + pstmt.setInt (index++, p_C_BPartner_ID); + if (p_IsDropShip != null) + pstmt.setString(index++, p_IsDropShip); + if (p_Vendor_ID != 0) + pstmt.setInt (index++, p_Vendor_ID); + if (p_DateOrdered_From != null && p_DateOrdered_To != null) + { + pstmt.setTimestamp(index++, p_DateOrdered_From); + pstmt.setTimestamp(index++, p_DateOrdered_To); + } + else if (p_DateOrdered_From != null && p_DateOrdered_To == null) + pstmt.setTimestamp(index++, p_DateOrdered_From); + else if (p_DateOrdered_From == null && p_DateOrdered_To != null) + pstmt.setTimestamp(index++, p_DateOrdered_To); + } + rs = pstmt.executeQuery (); + while (rs.next ()) + { + counter += createPOFromSO (new MOrder (getCtx(), rs, get_TrxName())); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + if (counter == 0) + log.fine(sql); + return "@Created@ " + counter; + } // doIt + + /** + * Create PO From SO + * @param so sales order + * @return number of POs created + */ + private int createPOFromSO (MOrder so) + { + log.info(so.toString()); + MOrderLine[] soLines = so.getLines(true, null); + if (soLines == null || soLines.length == 0) + { + log.warning("No Lines - " + so); + return 0; + } + // + int counter = 0; + // Order Lines with a Product which has a current vendor + String sql = "SELECT DISTINCT po.C_BPartner_ID, po.M_Product_ID " + + "FROM M_Product_PO po" + + " INNER JOIN C_OrderLine ol ON (po.M_Product_ID=ol.M_Product_ID) " + + "WHERE ol.C_Order_ID=? AND po.IsCurrentVendor='Y' " + + "ORDER BY 1"; + PreparedStatement pstmt = null; + ResultSet rs = null; + MOrder po = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, so.getC_Order_ID()); + rs = pstmt.executeQuery (); + while (rs.next ()) + { + // New Order + int C_BPartner_ID = rs.getInt(1); + if (po == null || po.getBill_BPartner_ID() != C_BPartner_ID) + { + po = createPOForVendor(rs.getInt(1), so); + addLog(0, null, null, po.getDocumentNo()); + counter++; + } + + // Line + int M_Product_ID = rs.getInt(2); + for (int i = 0; i < soLines.length; i++) + { + if (soLines[i].getM_Product_ID() == M_Product_ID) + { + MOrderLine poLine = new MOrderLine (po); + poLine.setRef_OrderLine_ID(soLines[i].getC_OrderLine_ID()); + poLine.setM_Product_ID(soLines[i].getM_Product_ID()); + poLine.setM_AttributeSetInstance_ID(soLines[i].getM_AttributeSetInstance_ID()); + poLine.setC_UOM_ID(soLines[i].getC_UOM_ID()); + poLine.setQtyEntered(soLines[i].getQtyEntered()); + poLine.setQtyOrdered(soLines[i].getQtyOrdered()); + poLine.setDescription(soLines[i].getDescription()); + poLine.setDatePromised(soLines[i].getDatePromised()); + poLine.setPrice(); + poLine.save(); + } + } + } + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + // Set Reference to PO + if (counter == 1 && po != null) + { + so.setRef_Order_ID(po.getC_Order_ID()); + so.save(); + } + return counter; + } // createPOFromSO + + /** + * Create PO for Vendor + * @param C_BPartner_ID vendor + * @param so sales order + */ + public MOrder createPOForVendor(int C_BPartner_ID, MOrder so) + { + MOrder po = new MOrder (getCtx(), 0, get_TrxName()); + po.setClientOrg(so.getAD_Client_ID(), so.getAD_Org_ID()); + po.setRef_Order_ID(so.getC_Order_ID()); + po.setIsSOTrx(false); + po.setC_DocTypeTarget_ID(); + // + po.setDescription(so.getDescription()); + po.setPOReference(so.getDocumentNo()); + po.setPriorityRule(so.getPriorityRule()); + po.setSalesRep_ID(so.getSalesRep_ID()); + po.setM_Warehouse_ID(so.getM_Warehouse_ID()); + // Set Vendor + MBPartner vendor = new MBPartner (getCtx(), C_BPartner_ID, get_TrxName()); + po.setBPartner(vendor); + // Drop Ship + po.setIsDropShip(so.isDropShip()); + if (so.isDropShip()) + { + po.setShip_BPartner_ID(so.getC_BPartner_ID()); + po.setShip_Location_ID(so.getC_BPartner_Location_ID()); + po.setShip_User_ID(so.getAD_User_ID()); + } + // References + po.setC_Activity_ID(so.getC_Activity_ID()); + po.setC_Campaign_ID(so.getC_Campaign_ID()); + po.setC_Project_ID(so.getC_Project_ID()); + po.setUser1_ID(so.getUser1_ID()); + po.setUser2_ID(so.getUser2_ID()); + // + po.save(); + return po; + } // createPOForVendor + +} // doIt diff --git a/base/src/org/compiere/process/ReplenishReport.java b/base/src/org/compiere/process/ReplenishReport.java new file mode 100644 index 0000000000..93ef3bd2c8 --- /dev/null +++ b/base/src/org/compiere/process/ReplenishReport.java @@ -0,0 +1,594 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + * Contributor(s): Chris Farley - northernbrewer * + *****************************************************************************/ +package org.compiere.process; + +import java.sql.*; +import java.util.*; +import java.math.*; + +import org.compiere.model.*; +import java.util.logging.*; +import org.compiere.util.*; + +/** + * Replenishment Report + * + * @author Jorg Janke + * @version $Id: ReplenishReport.java,v 1.2 2006/07/30 00:51:01 jjanke Exp $ + * + * Carlos Ruiz globalqss - integrate bug fixing from Chris Farley + * [ 1619517 ] Replenish report fails when no records in m_storage + */ +public class ReplenishReport extends SvrProcess +{ + /** Warehouse */ + private int p_M_Warehouse_ID = 0; + /** Optional BPartner */ + private int p_C_BPartner_ID = 0; + /** Create (POO)Purchse Order or (POR)Requisition or (MMM)Movements */ + private String p_ReplenishmentCreate = null; + /** Document Type */ + private int p_C_DocType_ID = 0; + /** Return Info */ + private String m_info = ""; + + /** + * Prepare - e.g., get Parameters. + */ + protected void prepare() + { + ProcessInfoParameter[] para = getParameter(); + for (int i = 0; i < para.length; i++) + { + String name = para[i].getParameterName(); + if (para[i].getParameter() == null) + ; + else if (name.equals("M_Warehouse_ID")) + p_M_Warehouse_ID = para[i].getParameterAsInt(); + else if (name.equals("C_BPartner_ID")) + p_C_BPartner_ID = para[i].getParameterAsInt(); + else if (name.equals("ReplenishmentCreate")) + p_ReplenishmentCreate = (String)para[i].getParameter(); + else if (name.equals("C_DocType_ID")) + p_C_DocType_ID = para[i].getParameterAsInt(); + else + log.log(Level.SEVERE, "Unknown Parameter: " + name); + } + } // prepare + + /** + * Perrform process. + * @return Message + * @throws Exception if not successful + */ + protected String doIt() throws Exception + { + log.info("M_Warehouse_ID=" + p_M_Warehouse_ID + + ", C_BPartner_ID=" + p_C_BPartner_ID + + " - ReplenishmentCreate=" + p_ReplenishmentCreate + + ", C_DocType_ID=" + p_C_DocType_ID); + if (p_ReplenishmentCreate != null && p_C_DocType_ID == 0) + throw new AdempiereUserError("@FillMandatory@ @C_DocType_ID@"); + + MWarehouse wh = MWarehouse.get(getCtx(), p_M_Warehouse_ID); + if (wh.get_ID() == 0) + throw new AdempiereSystemError("@FillMandatory@ @M_Warehouse_ID@"); + // + prepareTable(); + fillTable(wh); + // + if (p_ReplenishmentCreate == null) + return "OK"; + // + MDocType dt = MDocType.get(getCtx(), p_C_DocType_ID); + if (!dt.getDocBaseType().equals(p_ReplenishmentCreate)) + throw new AdempiereSystemError("@C_DocType_ID@=" + dt.getName() + " <> " + p_ReplenishmentCreate); + // + if (p_ReplenishmentCreate.equals("POO")) + createPO(); + else if (p_ReplenishmentCreate.equals("POR")) + createRequisition(); + else if (p_ReplenishmentCreate.equals("MMM")) + createMovements(); + return m_info; + } // doIt + + /** + * Prepare/Check Replenishment Table + */ + private void prepareTable() + { + // Level_Max must be >= Level_Max + String sql = "UPDATE M_Replenish" + + " SET Level_Max = Level_Min " + + "WHERE Level_Max < Level_Min"; + int no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Corrected Max_Level=" + no); + + // Minimum Order should be 1 + sql = "UPDATE M_Product_PO" + + " SET Order_Min = 1 " + + "WHERE Order_Min IS NULL OR Order_Min < 1"; + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Corrected Order Min=" + no); + + // Pack should be 1 + sql = "UPDATE M_Product_PO" + + " SET Order_Pack = 1 " + + "WHERE Order_Pack IS NULL OR Order_Pack < 1"; + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Corrected Order Pack=" + no); + + // Set Current Vendor where only one vendor + sql = "UPDATE M_Product_PO p" + + " SET IsCurrentVendor='Y' " + + "WHERE IsCurrentVendor<>'Y'" + + " AND EXISTS (SELECT pp.M_Product_ID FROM M_Product_PO pp " + + "WHERE p.M_Product_ID=pp.M_Product_ID " + + "GROUP BY pp.M_Product_ID " + + "HAVING COUNT(*) = 1)"; + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Corrected CurrentVendor(Y)=" + no); + + // More then one current vendor + sql = "UPDATE M_Product_PO p" + + " SET IsCurrentVendor='N' " + + "WHERE IsCurrentVendor = 'Y'" + + " AND EXISTS (SELECT pp.M_Product_ID FROM M_Product_PO pp " + + "WHERE p.M_Product_ID=pp.M_Product_ID AND pp.IsCurrentVendor='Y' " + + "GROUP BY pp.M_Product_ID " + + "HAVING COUNT(*) > 1)"; + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Corrected CurrentVendor(N)=" + no); + + // Just to be sure + sql = "DELETE T_Replenish WHERE AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Delete Existing Temp=" + no); + } // prepareTable + + /** + * Fill Table + * @param wh warehouse + */ + private void fillTable (MWarehouse wh) throws Exception + { + String sql = "INSERT INTO T_Replenish " + + "(AD_PInstance_ID, M_Warehouse_ID, M_Product_ID, AD_Client_ID, AD_Org_ID," + + " ReplenishType, Level_Min, Level_Max," + + " C_BPartner_ID, Order_Min, Order_Pack, QtyToOrder, ReplenishmentCreate) " + + "SELECT " + getAD_PInstance_ID() + + ", r.M_Warehouse_ID, r.M_Product_ID, r.AD_Client_ID, r.AD_Org_ID," + + " r.ReplenishType, r.Level_Min, r.Level_Max," + + " po.C_BPartner_ID, po.Order_Min, po.Order_Pack, 0, "; + if (p_ReplenishmentCreate == null) + sql += "null"; + else + sql += "'" + p_ReplenishmentCreate + "'"; + sql += " FROM M_Replenish r" + + " INNER JOIN M_Product_PO po ON (r.M_Product_ID=po.M_Product_ID) " + + "WHERE po.IsCurrentVendor='Y'" // Only Current Vendor + + " AND r.ReplenishType<>'0'" + + " AND po.IsActive='Y' AND r.IsActive='Y'" + + " AND r.M_Warehouse_ID=" + p_M_Warehouse_ID; + if (p_C_BPartner_ID != 0) + sql += " AND po.C_BPartner_ID=" + p_C_BPartner_ID; + int no = DB.executeUpdate(sql, get_TrxName()); + log.finest(sql); + log.fine("Insert (1) #" + no); + + if (p_C_BPartner_ID == 0) + { + sql = "INSERT INTO T_Replenish " + + "(AD_PInstance_ID, M_Warehouse_ID, M_Product_ID, AD_Client_ID, AD_Org_ID," + + " ReplenishType, Level_Min, Level_Max," + + " C_BPartner_ID, Order_Min, Order_Pack, QtyToOrder, ReplenishmentCreate) " + + "SELECT " + getAD_PInstance_ID() + + ", r.M_Warehouse_ID, r.M_Product_ID, r.AD_Client_ID, r.AD_Org_ID," + + " r.ReplenishType, r.Level_Min, r.Level_Max," + + " 0, 1, 1, 0, "; + if (p_ReplenishmentCreate == null) + sql += "null"; + else + sql += "'" + p_ReplenishmentCreate + "'"; + sql += " FROM M_Replenish r " + + "WHERE r.ReplenishType<>'0' AND r.IsActive='Y'" + + " AND r.M_Warehouse_ID=" + p_M_Warehouse_ID + + " AND NOT EXISTS (SELECT * FROM T_Replenish t " + + "WHERE r.M_Product_ID=t.M_Product_ID" + + " AND AD_PInstance_ID=" + getAD_PInstance_ID() + ")"; + no = DB.executeUpdate(sql, get_TrxName()); + log.fine("Insert (BP) #" + no); + } + + sql = "UPDATE T_Replenish t SET " + + "QtyOnHand = (SELECT COALESCE(SUM(QtyOnHand),0) FROM M_Storage s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID" + + " AND l.M_Locator_ID=s.M_Locator_ID AND l.M_Warehouse_ID=t.M_Warehouse_ID)," + + "QtyReserved = (SELECT COALESCE(SUM(QtyReserved),0) FROM M_Storage s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID" + + " AND l.M_Locator_ID=s.M_Locator_ID AND l.M_Warehouse_ID=t.M_Warehouse_ID)," + + "QtyOrdered = (SELECT COALESCE(SUM(QtyOrdered),0) FROM M_Storage s, M_Locator l WHERE t.M_Product_ID=s.M_Product_ID" + + " AND l.M_Locator_ID=s.M_Locator_ID AND l.M_Warehouse_ID=t.M_Warehouse_ID)"; + if (p_C_DocType_ID != 0) + sql += ", C_DocType_ID=" + p_C_DocType_ID; + sql += " WHERE AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Update #" + no); + + // Delete inactive products and replenishments + sql = "DELETE T_Replenish r " + + "WHERE (EXISTS (SELECT * FROM M_Product p " + + "WHERE p.M_Product_ID=r.M_Product_ID AND p.IsActive='N')" + + " OR EXISTS (SELECT * FROM M_Replenish rr " + + " WHERE rr.M_Product_ID=r.M_Product_ID AND rr.IsActive='N'))" + + " AND AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Delete Inactive=" + no); + + // Ensure Data consistency + sql = "UPDATE T_Replenish SET QtyOnHand = 0 WHERE QtyOnHand IS NULL"; + no = DB.executeUpdate(sql, get_TrxName()); + sql = "UPDATE T_Replenish SET QtyReserved = 0 WHERE QtyReserved IS NULL"; + no = DB.executeUpdate(sql, get_TrxName()); + sql = "UPDATE T_Replenish SET QtyOrdered = 0 WHERE QtyOrdered IS NULL"; + no = DB.executeUpdate(sql, get_TrxName()); + + // Set Minimum / Maximum Maintain Level + // X_M_Replenish.REPLENISHTYPE_ReorderBelowMinimumLevel + sql = "UPDATE T_Replenish" + + " SET QtyToOrder = CASE WHEN QtyOnHand - QtyReserved + QtyOrdered <= Level_Min " + + " THEN Level_Max - QtyOnHand + QtyReserved - QtyOrdered " + + " ELSE 0 END " + + "WHERE ReplenishType='1'" + + " AND AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Update Type-1=" + no); + // + // X_M_Replenish.REPLENISHTYPE_MaintainMaximumLevel + sql = "UPDATE T_Replenish" + + " SET QtyToOrder = Level_Max - QtyOnHand + QtyReserved - QtyOrdered " + + "WHERE ReplenishType='2'" + + " AND AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Update Type-2=" + no); + + + // Minimum Order Quantity + sql = "UPDATE T_Replenish" + + " SET QtyToOrder = Order_Min " + + "WHERE QtyToOrder < Order_Min" + + " AND QtyToOrder > 0" + + " AND AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Set MinOrderQty=" + no); + + // Even dividable by Pack + sql = "UPDATE T_Replenish" + + " SET QtyToOrder = QtyToOrder - MOD(QtyToOrder, Order_Pack) + Order_Pack " + + "WHERE MOD(QtyToOrder, Order_Pack) <> 0" + + " AND QtyToOrder > 0" + + " AND AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Set OrderPackQty=" + no); + + // Source from other warehouse + if (wh.getM_WarehouseSource_ID() != 0) + { + sql = "UPDATE T_Replenish" + + " SET M_WarehouseSource_ID=" + wh.getM_WarehouseSource_ID() + + " WHERE AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Set Source Warehouse=" + no); + } + // Check Source Warehouse + sql = "UPDATE T_Replenish" + + " SET M_WarehouseSource_ID = NULL " + + "WHERE M_Warehouse_ID=M_WarehouseSource_ID" + + " AND AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Set same Source Warehouse=" + no); + + // Custom Replenishment + String className = wh.getReplenishmentClass(); + if (className != null && className.length() > 0) + { + // Get Replenishment Class + ReplenishInterface custom = null; + try + { + Class clazz = Class.forName(className); + custom = (ReplenishInterface)clazz.newInstance(); + } + catch (Exception e) + { + throw new AdempiereUserError("No custom Replenishment class " + + className + " - " + e.toString()); + } + + X_T_Replenish[] replenishs = getReplenish("ReplenishType='9'"); + for (int i = 0; i < replenishs.length; i++) + { + X_T_Replenish replenish = replenishs[i]; + if (replenish.getReplenishType().equals(X_T_Replenish.REPLENISHTYPE_Custom)) + { + BigDecimal qto = null; + try + { + qto = custom.getQtyToOrder(wh, replenish); + } + catch (Exception e) + { + log.log(Level.SEVERE, custom.toString(), e); + } + if (qto == null) + qto = Env.ZERO; + replenish.setQtyToOrder(qto); + replenish.save(); + } + } + } + // Delete rows where nothing to order + sql = "DELETE T_Replenish " + + "WHERE QtyToOrder < 1" + + " AND AD_PInstance_ID=" + getAD_PInstance_ID(); + no = DB.executeUpdate(sql, get_TrxName()); + if (no != 0) + log.fine("Delete No QtyToOrder=" + no); + } // fillTable + + /** + * Create PO's + */ + private void createPO() + { + int noOrders = 0; + String info = ""; + // + MOrder order = null; + MWarehouse wh = null; + X_T_Replenish[] replenishs = getReplenish("M_WarehouseSource_ID IS NULL"); + for (int i = 0; i < replenishs.length; i++) + { + X_T_Replenish replenish = replenishs[i]; + if (wh == null || wh.getM_Warehouse_ID() != replenish.getM_Warehouse_ID()) + wh = MWarehouse.get(getCtx(), replenish.getM_Warehouse_ID()); + // + if (order == null + || order.getC_BPartner_ID() != replenish.getC_BPartner_ID() + || order.getM_Warehouse_ID() != replenish.getM_Warehouse_ID()) + { + order = new MOrder(getCtx(), 0, get_TrxName()); + order.setIsSOTrx(false); + order.setC_DocTypeTarget_ID(p_C_DocType_ID); + MBPartner bp = new MBPartner(getCtx(), replenish.getC_BPartner_ID(), get_TrxName()); + order.setBPartner(bp); + order.setSalesRep_ID(getAD_User_ID()); + order.setDescription(Msg.getMsg(getCtx(), "Replenishment")); + // Set Org/WH + order.setAD_Org_ID(wh.getAD_Org_ID()); + order.setM_Warehouse_ID(wh.getM_Warehouse_ID()); + if (!order.save()) + return; + log.fine(order.toString()); + noOrders++; + info += " - " + order.getDocumentNo(); + } + MOrderLine line = new MOrderLine (order); + line.setM_Product_ID(replenish.getM_Product_ID()); + line.setQty(replenish.getQtyToOrder()); + line.setPrice(); + line.save(); + } + m_info = "#" + noOrders + info; + log.info(m_info); + } // createPO + + /** + * Create Requisition + */ + private void createRequisition() + { + int noReqs = 0; + String info = ""; + // + MRequisition requisition = null; + MWarehouse wh = null; + X_T_Replenish[] replenishs = getReplenish("M_WarehouseSource_ID IS NULL"); + for (int i = 0; i < replenishs.length; i++) + { + X_T_Replenish replenish = replenishs[i]; + if (wh == null || wh.getM_Warehouse_ID() != replenish.getM_Warehouse_ID()) + wh = MWarehouse.get(getCtx(), replenish.getM_Warehouse_ID()); + // + if (requisition == null + || requisition.getM_Warehouse_ID() != replenish.getM_Warehouse_ID()) + { + requisition = new MRequisition (getCtx(), 0, get_TrxName()); + requisition.setAD_User_ID (getAD_User_ID()); + requisition.setC_DocType_ID(p_C_DocType_ID); + requisition.setDescription(Msg.getMsg(getCtx(), "Replenishment")); + // Set Org/WH + requisition.setAD_Org_ID(wh.getAD_Org_ID()); + requisition.setM_Warehouse_ID(wh.getM_Warehouse_ID()); + if (!requisition.save()) + return; + log.fine(requisition.toString()); + noReqs++; + info += " - " + requisition.getDocumentNo(); + } + // + MRequisitionLine line = new MRequisitionLine(requisition); + line.setM_Product_ID(replenish.getM_Product_ID()); + line.setC_BPartner_ID(replenish.getC_BPartner_ID()); + line.setQty(replenish.getQtyToOrder()); + line.setPrice(); + line.save(); + } + m_info = "#" + noReqs + info; + log.info(m_info); + } // createRequisition + + /** + * Create Inventory Movements + */ + private void createMovements() + { + int noMoves = 0; + String info = ""; + // + MClient client = null; + MMovement move = null; + int M_Warehouse_ID = 0; + int M_WarehouseSource_ID = 0; + MWarehouse whSource = null; + MWarehouse wh = null; + X_T_Replenish[] replenishs = getReplenish("M_WarehouseSource_ID IS NOT NULL"); + for (int i = 0; i < replenishs.length; i++) + { + X_T_Replenish replenish = replenishs[i]; + if (whSource == null || whSource.getM_WarehouseSource_ID() != replenish.getM_WarehouseSource_ID()) + whSource = MWarehouse.get(getCtx(), replenish.getM_WarehouseSource_ID()); + if (wh == null || wh.getM_Warehouse_ID() != replenish.getM_Warehouse_ID()) + wh = MWarehouse.get(getCtx(), replenish.getM_Warehouse_ID()); + if (client == null || client.getAD_Client_ID() != whSource.getAD_Client_ID()) + client = MClient.get(getCtx(), whSource.getAD_Client_ID()); + // + if (move == null + || M_WarehouseSource_ID != replenish.getM_WarehouseSource_ID() + || M_Warehouse_ID != replenish.getM_Warehouse_ID()) + { + M_WarehouseSource_ID = replenish.getM_WarehouseSource_ID(); + M_Warehouse_ID = replenish.getM_Warehouse_ID(); + + move = new MMovement (getCtx(), 0, get_TrxName()); + move.setC_DocType_ID(p_C_DocType_ID); + move.setDescription(Msg.getMsg(getCtx(), "Replenishment") + + ": " + whSource.getName() + "->" + wh.getName()); + // Set Org + move.setAD_Org_ID(whSource.getAD_Org_ID()); + if (!move.save()) + return; + log.fine(move.toString()); + noMoves++; + info += " - " + move.getDocumentNo(); + } + // To + int M_LocatorTo_ID = wh.getDefaultLocator().getM_Locator_ID(); + // From: Look-up Storage + MProduct product = MProduct.get(getCtx(), replenish.getM_Product_ID()); + String MMPolicy = product.getMMPolicy(); + MStorage[] storages = MStorage.getWarehouse(getCtx(), + whSource.getM_Warehouse_ID(), replenish.getM_Product_ID(), 0, 0, + true, null, + MClient.MMPOLICY_FiFo.equals(MMPolicy), get_TrxName()); + // + BigDecimal target = replenish.getQtyToOrder(); + for (int j = 0; j < storages.length; j++) + { + MStorage storage = storages[j]; + if (storage.getQtyOnHand().signum() <= 0) + continue; + BigDecimal moveQty = target; + if (storage.getQtyOnHand().compareTo(moveQty) < 0) + moveQty = storage.getQtyOnHand(); + // + MMovementLine line = new MMovementLine(move); + line.setM_Product_ID(replenish.getM_Product_ID()); + line.setMovementQty(moveQty); + if (replenish.getQtyToOrder().compareTo(moveQty) != 0) + line.setDescription("Total: " + replenish.getQtyToOrder()); + line.setM_Locator_ID(storage.getM_Locator_ID()); // from + line.setM_AttributeSetInstance_ID(storage.getM_AttributeSetInstance_ID()); + line.setM_LocatorTo_ID(M_LocatorTo_ID); // to + line.setM_AttributeSetInstanceTo_ID(storage.getM_AttributeSetInstance_ID()); + line.save(); + // + target = target.subtract(moveQty); + if (target.signum() == 0) + break; + } + } + if (replenishs.length == 0) + { + m_info = "No Source Warehouse"; + log.warning(m_info); + } + else + { + m_info = "#" + noMoves + info; + log.info(m_info); + } + } // createRequisition + + /** + * Get Replenish Records + * @return replenish + */ + private X_T_Replenish[] getReplenish (String where) + { + String sql = "SELECT * FROM T_Replenish " + + "WHERE AD_PInstance_ID=? AND C_BPartner_ID > 0 "; + if (where != null && where.length() > 0) + sql += " AND " + where; + sql += " ORDER BY M_Warehouse_ID, M_WarehouseSource_ID, C_BPartner_ID"; + ArrayList list = new ArrayList(); + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement (sql, get_TrxName()); + pstmt.setInt (1, getAD_PInstance_ID()); + ResultSet rs = pstmt.executeQuery (); + while (rs.next ()) + list.add (new X_T_Replenish (getCtx(), rs, get_TrxName())); + rs.close (); + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, sql, e); + } + try + { + if (pstmt != null) + pstmt.close (); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + X_T_Replenish[] retValue = new X_T_Replenish[list.size ()]; + list.toArray (retValue); + return retValue; + } // getReplenish + +} // Replenish diff --git a/client/src/org/compiere/apps/AEnv.java b/client/src/org/compiere/apps/AEnv.java new file mode 100644 index 0000000000..2abeb3ea49 --- /dev/null +++ b/client/src/org/compiere/apps/AEnv.java @@ -0,0 +1,1043 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.apps; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.rmi.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import javax.swing.*; +import org.compiere.db.*; +import org.compiere.grid.ed.Calculator; +import org.compiere.interfaces.*; +import org.compiere.model.*; +import org.compiere.swing.*; +import org.compiere.util.*; + +/** + * Windows Application Environment and utilities + * + * @author Jorg Janke + * @version $Id: AEnv.java,v 1.2 2006/07/30 00:51:27 jjanke Exp $ + * + * Colin Rooney (croo) & kstan_79 RFE#1670185 + */ +public final class AEnv +{ + /** + * Show window: de-iconify and bring it to front + * @author teo_sarca [ 1707221 ] + */ + public static void showWindow(Window window) { + window.setVisible(true); + if (window instanceof Frame) { + Frame f = (Frame)window; + int state = f.getExtendedState(); + if ((state & Frame.ICONIFIED) > 0) + f.setExtendedState(state & ~Frame.ICONIFIED); + } + window.toFront(); + } + + /** + * Show in the center of the screen. + * (pack, set location and set visibility) + * @param window Window to position + */ + public static void showCenterScreen(Window window) + { + positionCenterScreen(window); + showWindow(window); + } // showCenterScreen + + /** + * Show frame as maximized. + * @param frame + */ + public static void showMaximized(Frame frame) + { + frame.pack(); + frame.setExtendedState(Frame.MAXIMIZED_BOTH); + frame.setVisible(true); + frame.toFront(); + } + + /** + * Position window in center of the screen + * @param window Window to position + */ + public static void positionCenterScreen(Window window) + { + positionScreen (window, SwingConstants.CENTER); + } // positionCenterScreen + + /** + * Show in the center of the screen. + * (pack, set location and set visibility) + * @param window Window to position + * @param position SwingConstants + */ + public static void showScreen(Window window, int position) + { + positionScreen(window, position); + showWindow(window); + } // showScreen + + + /** + * Position window in center of the screen + * @param window Window to position + * @param position SwingConstants + */ + public static void positionScreen (Window window, int position) + { + window.pack(); + Dimension sSize = Toolkit.getDefaultToolkit().getScreenSize(); + // take into account task bar and other adornments + GraphicsConfiguration config = window.getGraphicsConfiguration(); + Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config); + sSize.width -= (insets.left + insets.right); + sSize.height -= (insets.top + insets.bottom); + + Dimension wSize = window.getSize(); + // fit on window + if (wSize.height > sSize.height) + wSize.height = sSize.height; + if (wSize.width > sSize.width) + wSize.width = sSize.width; + window.setSize(wSize); + // Center + int x = (sSize.width - wSize.width) / 2; + int y = (sSize.height - wSize.height) / 2; + if (position == SwingConstants.CENTER) + ; + else if (position == SwingConstants.NORTH_WEST) + { + x = 0; + y = 0; + } + else if (position == SwingConstants.NORTH) + { + y = 0; + } + else if (position == SwingConstants.NORTH_EAST) + { + x = (sSize.width - wSize.width); + y = 0; + } + else if (position == SwingConstants.WEST) + { + x = 0; + } + else if (position == SwingConstants.EAST) + { + x = (sSize.width - wSize.width); + } + else if (position == SwingConstants.SOUTH) + { + y = (sSize.height - wSize.height); + } + else if (position == SwingConstants.SOUTH_WEST) + { + x = 0; + y = (sSize.height - wSize.height); + } + else if (position == SwingConstants.SOUTH_EAST) + { + x = (sSize.width - wSize.width); + y = (sSize.height - wSize.height); + } + // + window.setLocation(x + insets.left, y + insets.top); + } // positionScreen + + /** + * Position in center of the parent window. + * (pack, set location and set visibility) + * @param parent Parent Window + * @param window Window to position + */ + public static void showCenterWindow(Window parent, Window window) + { + positionCenterWindow(parent, window); + showWindow(window); + } // showCenterWindow + + /** + * Position in center of the parent window + * + * @param parent Parent Window + * @param window Window to position + */ + public static void positionCenterWindow(Window parent, Window window) + { + if (parent == null) + { + positionCenterScreen(window); + return; + } + window.pack(); + // + Dimension sSize = Toolkit.getDefaultToolkit().getScreenSize(); + // take into account task bar and other adornments + GraphicsConfiguration config = window.getGraphicsConfiguration(); + Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(config); + sSize.width -= (insets.left + insets.right); + sSize.height -= (insets.top + insets.bottom); + + Dimension wSize = window.getSize(); + // fit on window + if (wSize.height > sSize.height) + wSize.height = sSize.height; + if (wSize.width > sSize.width) + wSize.width = sSize.width; + window.setSize(wSize); + // center in parent + Rectangle pBounds = parent.getBounds(); + // Parent is in upper left corner + if (pBounds.x == pBounds.y && pBounds.x == 0) + { + positionCenterScreen(window); + return; + } + // Find middle + int x = pBounds.x + ((pBounds.width-wSize.width)/2); + if (x < 0) + x = 0; + int y = pBounds.y + ((pBounds.height-wSize.height)/2); + if (y < 0) + y = 0; + + // Is it on Screen? + if (x + wSize.width > sSize.width) + x = sSize.width - wSize.width; + if (y + wSize.height > sSize.height) + y = sSize.height - wSize.height; + // + // System.out.println("Position: x=" + x + " y=" + y + " w=" + wSize.getWidth() + " h=" + wSize.getHeight() + // + " - Parent loc x=" + pLoc.x + " y=" + y + " w=" + pSize.getWidth() + " h=" + pSize.getHeight()); + window.setLocation(x + insets.left, y + insets.top); + } // positionCenterScreen + + + /************************************************************************* + * Get Button + * @param iconName + * @return button + */ + public static CButton getButton (String iconName) + { + CButton button = new CButton(Env.getImageIcon(iconName + "16.gif")); + button.setMargin(new Insets (0, 0, 0, 0)); + button.setToolTipText(Msg.getMsg(Env.getCtx(), iconName)); + button.setDefaultCapable(false); + return button; + } // getButton + + + /** + * Create Menu Title (translate it and set Mnemonics). + * Based on MS notation of &Help => H is Mnemonics + * + * @param AD_Message message + * @return JMenu + */ + public static JMenu getMenu (String AD_Message) + { + JMenu menu = new JMenu(); + String text = Msg.getMsg(Env.getCtx(), AD_Message); + int pos = text.indexOf('&'); + if (pos != -1 && text.length() > pos) // We have a nemonic + { + char ch = text.toUpperCase().charAt(pos+1); + if (ch != ' ') + { + text = text.substring(0, pos) + text.substring(pos+1); + menu.setMnemonic(ch); + } + } + menu.setText(text); + return menu; + } // getMenu + + /** + * Create Menu Item. + * @param actionName action command + * @param iconName optional name of the icon, defaults to action if null + * @param ks optional key stroke + * @param menu menu to add menu item to + * @param al action listener to register + * @return MenuItem + */ + public static JMenuItem addMenuItem (String actionName, String iconName, KeyStroke ks, + JMenu menu, ActionListener al) + { + if (iconName == null) + iconName = actionName; + String text = Msg.getMsg(Env.getCtx(), actionName); + ImageIcon icon = Env.getImageIcon2(iconName + "16"); + CMenuItem mi = new CMenuItem(text, icon); + mi.setActionCommand(actionName); + if (ks != null) + mi.setAccelerator(ks); + if (menu != null) + menu.add(mi); + if (al != null) + mi.addActionListener(al); + return mi; + } // addMenuItem + + /** + * Perform action command for common menu items. + * Created in AMenu.createMenu(), APanel.createMenu(), FormFrame.createMenu() + * @param actionCommand known action command + * @param WindowNo window no + * @param c Container parent + * @return true if actionCommand was found and performed + */ + public static boolean actionPerformed (String actionCommand, int WindowNo, Container c) + { + MRole role = MRole.getDefault(); + // File Menu ------------------------ + if (actionCommand.equals("PrintScreen")) + { + PrintScreenPainter.printScreen (Env.getFrame(c)); + } + else if (actionCommand.equals("ScreenShot")) + { + ScreenShot.createJPEG(Env.getFrame(c), null); + } + // else if (actionCommand.equals("Report")) + // { + // AEnv.showCenterScreen (new ProcessStart()); + // } + else if (actionCommand.equals("Exit")) + { + if (ADialog.ask(WindowNo, c, "ExitApplication?")) + { + AMenu aMenu = (AMenu)Env.getWindow(0); + aMenu.dispose() ; + } + } + else if (actionCommand.equals("Logout")) + { + AMenu aMenu = (AMenu)Env.getWindow(0); + aMenu.logout(); + } + + // View Menu ------------------------ + else if (actionCommand.equals("InfoProduct") && AEnv.canAccessInfo("PRODUCT")) + { + org.compiere.apps.search.Info.showProduct (Env.getFrame(c), WindowNo); + } + else if (actionCommand.equals("InfoBPartner") && AEnv.canAccessInfo("BPARTNER")) + { + org.compiere.apps.search.Info.showBPartner (Env.getFrame(c), WindowNo); + } + else if (actionCommand.equals("InfoAsset") && AEnv.canAccessInfo("ASSET")) + { + org.compiere.apps.search.Info.showAsset (Env.getFrame(c), WindowNo); + } + else if (actionCommand.equals("InfoAccount") && + MRole.getDefault().isShowAcct() && + AEnv.canAccessInfo("ACCOUNT")) + { + new org.compiere.acct.AcctViewer(); + } + else if (actionCommand.equals("InfoSchedule") && AEnv.canAccessInfo("SCHEDULE")) + { + new org.compiere.apps.search.InfoSchedule (Env.getFrame(c), null, false); + } + else if (actionCommand.equals("InfoOrder") && AEnv.canAccessInfo("ORDER")) + { + org.compiere.apps.search.Info.showOrder (Env.getFrame(c), WindowNo, ""); + } + else if (actionCommand.equals("InfoInvoice") && AEnv.canAccessInfo("INVOICE")) + { + org.compiere.apps.search.Info.showInvoice (Env.getFrame(c), WindowNo, ""); + } + else if (actionCommand.equals("InfoInOut") && AEnv.canAccessInfo("INOUT")) + { + org.compiere.apps.search.Info.showInOut (Env.getFrame(c), WindowNo, ""); + } + else if (actionCommand.equals("InfoPayment") && AEnv.canAccessInfo("PAYMENT")) + { + org.compiere.apps.search.Info.showPayment (Env.getFrame(c), WindowNo, ""); + } + else if (actionCommand.equals("InfoCashLine") && AEnv.canAccessInfo("CASHJOURNAL")) + { + org.compiere.apps.search.Info.showCashLine (Env.getFrame(c), WindowNo, ""); + } + else if (actionCommand.equals("InfoAssignment") && AEnv.canAccessInfo("RESOURCE")) + { + org.compiere.apps.search.Info.showAssignment (Env.getFrame(c), WindowNo, ""); + } + + // Go Menu ------------------------ + else if (actionCommand.equals("WorkFlow")) + { + startWorkflowProcess(0,0); + } + else if (actionCommand.equals("Home")) + { + showWindow(Env.getWindow(0)); + } + + // Tools Menu ------------------------ + else if (actionCommand.equals("Calculator")) + { + Calculator calc = new org.compiere.grid.ed.Calculator(Env.getFrame(c)); + calc.setDisposeOnEqual(false); + AEnv.showCenterScreen (calc); + } + else if (actionCommand.equals("Calendar")) + { + AEnv.showCenterScreen (new org.compiere.grid.ed.Calendar(Env.getFrame(c))); + } + else if (actionCommand.equals("Editor")) + { + AEnv.showCenterScreen (new org.compiere.grid.ed.Editor(Env.getFrame(c))); + } + else if (actionCommand.equals("Script")) + { + new BeanShellEditor(Env.getFrame(c)); + } + else if (actionCommand.equals("Preference")) + { + if (role.isShowPreference()) { + AEnv.showCenterScreen(new Preference (Env.getFrame(c), WindowNo)); + } + } + + // Help Menu ------------------------ + else if (actionCommand.equals("Online")) + { + Env.startBrowser(org.compiere.Adempiere.getOnlineHelpURL()); + } + else if (actionCommand.equals("EMailSupport")) + { + ADialog.createSupportEMail(Env.getFrame(c), Env.getFrame(c).getTitle(), "\n\n"); + } + else if (actionCommand.equals("About")) + { + AEnv.showCenterScreen(new AboutBox(Env.getFrame(c))); + } + else + return false; + // + return true; + } // actionPerformed + + /** + * Set Text and Mnemonic for Button. + * Create Mnemonics of text containing '&'. + * Based on MS notation of &Help => H is Mnemonics + * @param b The button + * @param text The text with optional Mnemonics + */ + public static void setTextMnemonic (JButton b, String text) + { + if (text == null || b == null) + return; + int pos = text.indexOf('&'); + if (pos != -1) // We have a nemonic + { + char ch = text.charAt(pos+1); + b.setMnemonic(ch); + b.setText(text.substring(0, pos) + text.substring(pos+1)); + } + b.setText(text); + } // setTextMnemonic + + /** + * Get Mnemonic character from text. + * @param text text with '&' + * @return Mnemonic or 0 + */ + public static char getMnemonic (String text) + { + int pos = text.indexOf('&'); + if (pos != -1) // We have a nemonic + return text.charAt(pos+1); + return 0; + } // getMnemonic + + + /************************************************************************* + * Zoom + * @param AD_Table_ID + * @param Record_ID + */ + public static void zoom (int AD_Table_ID, int Record_ID) + { + String TableName = null; + int AD_Window_ID = 0; + int PO_Window_ID = 0; + String sql = "SELECT TableName, AD_Window_ID, PO_Window_ID FROM AD_Table WHERE AD_Table_ID=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, AD_Table_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + TableName = rs.getString(1); + AD_Window_ID = rs.getInt(2); + PO_Window_ID = rs.getInt(3); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + // Nothing to Zoom to + if (TableName == null || AD_Window_ID == 0) + return; + + // PO Zoom ? + boolean isSOTrx = true; + if (PO_Window_ID != 0) + { + String whereClause = TableName + "_ID=" + Record_ID; + isSOTrx = DB.isSOTrx(TableName, whereClause); + if (!isSOTrx) + AD_Window_ID = PO_Window_ID; + } + + log.config(TableName + " - Record_ID=" + Record_ID + " (IsSOTrx=" + isSOTrx + ")"); + AWindow frame = new AWindow(); + if (!frame.initWindow(AD_Window_ID, MQuery.getEqualQuery(TableName + "_ID", Record_ID))) + return; + addToWindowManager(frame); + if (Ini.isPropertyBool(Ini.P_OPEN_WINDOW_MAXIMIZED)) + { + AEnv.showMaximized(frame); + } + else + { + AEnv.showCenterScreen(frame); + } + frame = null; + } // zoom + + /** + * Zoom + * @param query query + */ + public static void zoom (MQuery query) + { + if (query == null || query.getTableName() == null || query.getTableName().length() == 0) + return; + String TableName = query.getTableName(); + int AD_Window_ID = 0; + int PO_Window_ID = 0; + String sql = "SELECT AD_Window_ID, PO_Window_ID FROM AD_Table WHERE TableName=?"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setString(1, TableName); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + AD_Window_ID = rs.getInt(1); + PO_Window_ID = rs.getInt(2); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + // Nothing to Zoom to + if (AD_Window_ID == 0) + return; + + // PO Zoom ? + boolean isSOTrx = true; + if (PO_Window_ID != 0) + { + isSOTrx = DB.isSOTrx(TableName, query.getWhereClause(false)); + if (!isSOTrx) + AD_Window_ID = PO_Window_ID; + } + + log.config(query + " (IsSOTrx=" + isSOTrx + ")"); + AWindow frame = new AWindow(); + if (!frame.initWindow(AD_Window_ID, query)) + return; + addToWindowManager(frame); + if (Ini.isPropertyBool(Ini.P_OPEN_WINDOW_MAXIMIZED)) + { + AEnv.showMaximized(frame); + } + else + { + AEnv.showCenterScreen(frame); + } + frame = null; + } // zoom + + /** + * Track open frame in window manager + * @param frame + */ + public static void addToWindowManager(CFrame frame) + { + JFrame top = Env.getWindow(0); + if (top instanceof AMenu) + { + ((AMenu)top).getWindowManager().add(frame); + } + } + /** + * Exit System + * @param status System exit status (usually 0 for no error) + */ + public static void exit (int status) + { + if (s_server != null) + { + try + { + s_server.remove(); + } + catch (Exception ex) + { + } + } + Env.exitEnv(status); + } // exit + + public static void logout() + { + if (s_server != null) + { + try + { + s_server.remove(); + } + catch (Exception ex) + { + } + } + Env.logout(); + + Splash.getSplash().setVisible(true); + + //reload + new AMenu(); + } + + /** + * Is Workflow Process view enabled. + * @return true if enabled + */ + public static boolean isWorkflowProcess () + { + if (s_workflow == null) + { + s_workflow = Boolean.FALSE; + int AD_Table_ID = 645; // AD_WF_Process + if (MRole.getDefault().isTableAccess (AD_Table_ID, true)) // RO + s_workflow = Boolean.TRUE; + else + { + AD_Table_ID = 644; // AD_WF_Activity + if (MRole.getDefault().isTableAccess (AD_Table_ID, true)) // RO + s_workflow = Boolean.TRUE; + else + log.config(s_workflow.toString()); + } + // Get Window + if (s_workflow.booleanValue()) + { + s_workflow_Window_ID = DB.getSQLValue (null, + "SELECT AD_Window_ID FROM AD_Table WHERE AD_Table_ID=?", AD_Table_ID); + if (s_workflow_Window_ID == 0) + s_workflow_Window_ID = 297; // fallback HARDCODED + // s_workflow = Boolean.FALSE; + log.config(s_workflow + ", Window=" + s_workflow_Window_ID); + } + } + return s_workflow.booleanValue(); + } // isWorkflowProcess + + + /** + * Start Workflow Process Window + * @param AD_Table_ID optional table + * @param Record_ID optional record + */ + public static void startWorkflowProcess (int AD_Table_ID, int Record_ID) + { + if (s_workflow_Window_ID == 0) + return; + // + MQuery query = null; + if (AD_Table_ID != 0 && Record_ID != 0) + { + query = new MQuery("AD_WF_Process"); + query.addRestriction("AD_Table_ID", MQuery.EQUAL, AD_Table_ID); + query.addRestriction("Record_ID", MQuery.EQUAL, Record_ID); + } + // + AWindow frame = new AWindow(); + if (!frame.initWindow(s_workflow_Window_ID, query)) + return; + addToWindowManager(frame); + if (Ini.isPropertyBool(Ini.P_OPEN_WINDOW_MAXIMIZED) ) { + frame.pack(); + frame.setExtendedState(Frame.MAXIMIZED_BOTH); + frame.setVisible(true); + frame.toFront(); + } else + AEnv.showCenterScreen(frame); + frame = null; + } // startWorkflowProcess + + + /*************************************************************************/ + + /** Workflow Menu */ + private static Boolean s_workflow = null; + /** Workflow Menu */ + private static int s_workflow_Window_ID = 0; + + /** Server Re-tries */ + private static int s_serverTries = 0; + /** Server Session */ + private static Server s_server = null; + /** Logger */ + private static CLogger log = CLogger.getCLogger(AEnv.class); + + /** + * Is AppsServer Active ? + * @return true if active + */ + public static boolean isServerActive() + { + boolean contactAgain = s_server == null && s_serverTries == 0; + boolean ok = CConnection.get().isAppsServerOK(contactAgain); + if (ok) + { + s_serverTries = 0; + return true; + } + if (s_serverTries > 1) // try twice + return false; + + // Try to connect + CLogMgt.enable(false); + try + { + s_serverTries++; + log.config("try #" + s_serverTries); + ok = CConnection.get().isAppsServerOK(true); + if (ok) + s_serverTries = 0; + } + catch (Exception ex) + { + ok = false; + s_server = null; + } + CLogMgt.enable(true); + // + return ok; + } // isServerActive + + /** + * Get Server Version + * @return Apps Server Version + * @see ALogin#checkVersion + */ + public static String getServerVersion () + { + return CConnection.get().getServerVersion(); + } // getServerVersion + + /** Window Cache */ + private static CCache s_windows + = new CCache("AD_Window", 10); + + /** + * Get Window Model + * + * @param WindowNo Window No + * @param AD_Window_ID window + * @param AD_Menu_ID menu + * @return Model Window Value Obkect + */ + public static GridWindowVO getMWindowVO (int WindowNo, int AD_Window_ID, int AD_Menu_ID) + { + log.config("Window=" + WindowNo + ", AD_Window_ID=" + AD_Window_ID); + GridWindowVO mWindowVO = null; + if (AD_Window_ID != 0 && Ini.isCacheWindow()) // try cache + { + mWindowVO = s_windows.get(AD_Window_ID); + if (mWindowVO != null) + { + mWindowVO = mWindowVO.clone(WindowNo); + log.info("Cached=" + mWindowVO); + } + } + // try to get from Server when enabled + if (mWindowVO == null && DB.isRemoteObjects() && isServerActive()) + { + log.config("trying server"); + try + { + s_server = CConnection.get().getServer(); + if (s_server != null) + { + mWindowVO = s_server.getWindowVO(Env.getCtx(), WindowNo, AD_Window_ID, AD_Menu_ID); + log.config("from Server: success"); + } + } + catch (RemoteException e) + { + log.log(Level.SEVERE, "(RE)", e); + mWindowVO = null; + s_server = null; + } + catch (Exception e) + { + Throwable tt = e.getCause(); + if (tt != null && tt instanceof InvalidClassException) + log.log(Level.SEVERE, "(Server<>Client class) " + tt); + else if (tt != null && tt instanceof NotSerializableException) + log.log(Level.SEVERE, "Serialization: " + tt.getMessage(), e); + else + log.log(Level.SEVERE, "ex", e); + mWindowVO = null; + s_server = null; + } + catch (Throwable t) + { + log.log(Level.SEVERE, t.toString()); + mWindowVO = null; + s_server = null; + } + if (mWindowVO != null) + s_windows.put(AD_Window_ID, mWindowVO); + } // from Server + + // Create Window Model on Client + if (mWindowVO == null) + { + log.config("create local"); + mWindowVO = GridWindowVO.create (Env.getCtx(), WindowNo, AD_Window_ID, AD_Menu_ID); + if (mWindowVO != null) + s_windows.put(AD_Window_ID, mWindowVO); + } // from Client + if (mWindowVO == null) + return null; + + // Check (remote) context + if (!mWindowVO.ctx.equals(Env.getCtx())) + { + // Remote Context is called by value, not reference + // Add Window properties to context + Enumeration keyEnum = mWindowVO.ctx.keys(); + while (keyEnum.hasMoreElements()) + { + String key = (String)keyEnum.nextElement(); + if (key.startsWith(WindowNo+"|")) + { + String value = mWindowVO.ctx.getProperty (key); + Env.setContext(Env.getCtx(), key, value); + } + } + // Sync Context + mWindowVO.setCtx(Env.getCtx()); + } + return mWindowVO; + } // getWindow + + /** + * Post Immediate + * @param WindowNo window + * @param AD_Table_ID Table ID of Document + * @param AD_Client_ID Client ID of Document + * @param Record_ID Record ID of this document + * @param force force posting + * @return null if success, otherwise error + */ + public static String postImmediate (int WindowNo, int AD_Client_ID, + int AD_Table_ID, int Record_ID, boolean force) + { + log.config("Window=" + WindowNo + + ", AD_Table_ID=" + AD_Table_ID + "/" + Record_ID + + ", Force=" + force); + + String error = null; + // try to get from Server when enabled + if (isServerActive()) + { + log.config("trying server"); + try + { + s_server = CConnection.get().getServer(); + if (s_server != null) + { + error = s_server.postImmediate(Env.getCtx(), AD_Client_ID, + AD_Table_ID, Record_ID, force, null); + log.config("from Server: " + error== null ? "OK" : error); + } + else + { + ADialog.error(WindowNo, null, "NoAppsServer"); + return "NoAppsServer"; + } + } + catch (RemoteException e) + { + log.log(Level.WARNING, "(RE)", e); + error = e.getMessage(); + s_server = null; + } + catch (Exception e) + { + log.log(Level.WARNING, "ex", e); + error = e.getMessage(); + s_server = null; + } + } + else + { + ADialog.error(WindowNo, null, "NoAppsServer"); + return "NoAppsServer"; + } + return error; + } // postImmediate + + /** + * Cache Reset + * @param tableName table name + * @param Record_ID record id + */ + public static void cacheReset (String tableName, int Record_ID) + { + log.config("TableName=" + tableName + ", Record_ID=" + Record_ID); + + // try to get from Server when enabled + if (isServerActive()) + { + log.config("trying server"); + try + { + Server server = CConnection.get().getServer(); + if (server != null) + { + server.cacheReset(tableName, Record_ID); + } + } + catch (RemoteException e) + { + log.log(Level.SEVERE, "(RE)", e); + s_server = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, "ex", e); + s_server = null; + } + } + } // cacheReset + + /** + * Update all windows after look and feel changes. + * @since 2006-11-27 + */ + public static void updateUI() + { + Set updated = Env.updateUI(); + JFrame top = Env.getWindow(0); + if (top instanceof AMenu) + { + CFrame[] frames = ((AMenu)top).getWindowManager().getWindows(); + for (CFrame f : frames) + { + if (updated.contains(f)) continue; + SwingUtilities.updateComponentTreeUI(f); + f.validate(); + RepaintManager mgr = RepaintManager.currentManager(f); + Component childs[] = f.getComponents(); + for (Component c : childs) { + if (c instanceof JComponent) + mgr.markCompletelyDirty((JComponent)c); + } + f.repaint(); + updated.add(f); + } + } + } + + /** + * Validate permissions to access Info queries on the view menu + * @author kstan_79 + * @return true if access is allowed + */ + + public static boolean canAccessInfo(String infoWindowName) + { + boolean result=false; + int roleid= Env.getAD_Role_ID(Env.getCtx()); + String sqlRolePermission="Select COUNT(AD_ROLE_ID) AS ROWCOUNT FROM AD_ROLE WHERE AD_ROLE_ID=" + roleid + + " AND ALLOW_INFO_" + infoWindowName + "='Y'"; + + log.config(sqlRolePermission); + PreparedStatement prolestmt = null; + ResultSet rs = null; + try + { + prolestmt = DB.prepareStatement (sqlRolePermission, null); + + rs = prolestmt.executeQuery (); + + rs.next(); + + if (rs.getInt("ROWCOUNT")>0) + { + result=true; + } + else + { + return false; + } + } + catch (Exception e) + { + System.out.println(e); + log.log(Level.SEVERE, "(1)", e); + } + finally + { + DB.close(rs, prolestmt); + rs = null; prolestmt = null; + } + + return result; + } // canAccessInfo + +} // AEnv diff --git a/client/src/org/compiere/apps/AMenu.java b/client/src/org/compiere/apps/AMenu.java new file mode 100644 index 0000000000..5a8c5d227b --- /dev/null +++ b/client/src/org/compiere/apps/AMenu.java @@ -0,0 +1,788 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.apps; + +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Event; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.math.BigDecimal; +import java.text.MessageFormat; +import java.util.Properties; +import java.util.logging.Level; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JProgressBar; +import javax.swing.KeyStroke; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.adempiere.apps.graph.PAPanel; +import org.compiere.Adempiere; +import org.compiere.apps.wf.WFActivity; +import org.compiere.apps.wf.WFPanel; +import org.compiere.grid.tree.VTreePanel; +import org.compiere.model.MRole; +import org.compiere.model.MSession; +import org.compiere.model.MSysConfig; +import org.compiere.model.MSystem; +import org.compiere.model.MTreeNode; +import org.compiere.model.MUser; +import org.compiere.swing.CButton; +import org.compiere.swing.CFrame; +import org.compiere.swing.CPanel; +import org.compiere.swing.CScrollPane; +import org.compiere.swing.CTabbedPane; +import org.compiere.util.CLogger; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.Ini; +import org.compiere.util.Language; +import org.compiere.util.Msg; +import org.compiere.util.Splash; +/** + * Application Menu Controller + * + * @author Jorg Janke + * @version $Id: AMenu.java,v 1.2 2006/07/30 00:51:27 jjanke Exp $ + * + * Colin Rooney (croo) RFE#1670185 restrict access to info queries + */ +public final class AMenu extends CFrame + implements ActionListener, PropertyChangeListener, ChangeListener +{ + /** + * Application Start and Menu + */ + public AMenu () + { + super(); + log.info("CodeBase=" + Adempiere.getCodeBase()); + Splash splash = Splash.getSplash(); + // + m_WindowNo = Env.createWindowNo(this); + // Login + initSystem (splash); // login + splash.setText(Msg.getMsg(m_ctx, "Loading")); + splash.toFront(); + splash.paint(splash.getGraphics()); + + // + if (!Adempiere.startupEnvironment(true)) // Load Environment + System.exit(1); + MSession.get (Env.getCtx(), true); // Start Session + + // Setting close operation/listner - teo_sarca [ 1684168 ] + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + addWindowListener(new WindowListener() { + public void windowClosing(WindowEvent e) { + if (!ADialog.ask(0, null, "ExitApplication?")) + return; + dispose(); + } + public void windowActivated(WindowEvent e) {} + public void windowClosed(WindowEvent e) {} + public void windowDeactivated(WindowEvent e) {} + public void windowDeiconified(WindowEvent e) {} + public void windowIconified(WindowEvent e) {} + public void windowOpened(WindowEvent e) {} + }); + + // Preparation + wfActivity = new WFActivity(this); + wfPanel = new WFPanel(this); + treePanel = new VTreePanel (m_WindowNo, true, false); // !editable & hasBar + + try + { + jbInit(); + createMenu(); + } + catch(Exception ex) + { + log.log(Level.SEVERE, "AMenu", ex); + } + + // initialize & load tree + int AD_Role_ID = Env.getAD_Role_ID(Env.getCtx()); + int AD_Tree_ID = DB.getSQLValue(null, + "SELECT COALESCE(r.AD_Tree_Menu_ID, ci.AD_Tree_Menu_ID)" + + "FROM AD_ClientInfo ci" + + " INNER JOIN AD_Role r ON (ci.AD_Client_ID=r.AD_Client_ID) " + + "WHERE AD_Role_ID=?", AD_Role_ID); + if (AD_Tree_ID <= 0) + AD_Tree_ID = 10; // Menu + treePanel.initTree(AD_Tree_ID); + + // Translate + Env.setContext(m_ctx, m_WindowNo, "WindowName", Msg.getMsg(m_ctx, "Menu")); + setTitle(Env.getHeader(m_ctx, m_WindowNo)); + + progressBar.setString(Msg.getMsg(m_ctx, "SelectProgram")); + + // Finish UI + Point loc = Ini.getWindowLocation(0); + if (loc == null) + loc = new Point(0,0); + this.setLocation(loc); + this.pack(); + this.setVisible(true); + if (Ini.isPropertyBool(Ini.P_OPEN_WINDOW_MAXIMIZED)) + this.setExtendedState(Frame.MAXIMIZED_BOTH); + else + this.setState(Frame.NORMAL); + this.validate(); + m_AD_User_ID = Env.getContextAsInt(m_ctx, "#AD_User_ID"); + m_AD_Role_ID = Env.getContextAsInt(m_ctx, "#AD_Role_ID"); + updateInfo(); + // + infoUpdater = new InfoUpdater(); + infoUpdaterThread = new Thread(infoUpdater, "InfoUpdater"); + infoUpdaterThread.start(); + // + splash.dispose(); + splash = null; + } // AMenu + + private int m_WindowNo; + private Properties m_ctx = Env.getCtx(); + private boolean m_startingItem = false; + /** The User */ + private int m_AD_User_ID; + /** The Role */ + private int m_AD_Role_ID; + + /** Center Tabbed Pane index: Menu */ + private int m_tabMenu = 0; + /** Center Tabbed Pane index: Activities */ + private int m_tabActivities = 1; + /** Center Tabbed Pane index: Workflow */ + private int m_tabWorkflow = 2; + + // Links + private int m_request_Menu_ID = 0; + private int m_note_Menu_ID = 0; + private String m_requestSQL = null; +// private DecimalFormat m_memoryFormat = DisplayType.getNumberFormat(DisplayType.Integer); + /** Logger */ + private static CLogger log = CLogger.getCLogger(AMenu.class); + + /** The Info Update instance **/ + private InfoUpdater infoUpdater = null; + /** The Info Update thread **/ + private Thread infoUpdaterThread = null; + + private WindowManager windowManager = new WindowManager(); + + + /************************************************************************** + * Init System. + * -- do not get Msg as environment not initialized yet -- + *
    +	 *	- Login - in not successful, exit
    +	 *  
    + * @param splash splash window + */ + private void initSystem (Splash splash) + { + // Default Image + this.setIconImage(Adempiere.getImage16()); + + // Focus Traversal + KeyboardFocusManager.setCurrentKeyboardFocusManager(AKeyboardFocusManager.get()); + // FocusManager.getCurrentManager().setDefaultFocusTraversalPolicy(AFocusTraversalPolicy.get()); + // this.setFocusTraversalPolicy(AFocusTraversalPolicy.get()); + + /** + * Show Login Screen - if not successful - exit + */ + log.finer("Login"); + + ALogin login = new ALogin(splash); + if (!login.initLogin()) // no automatic login + { + // Center the window + try + { + AEnv.showCenterScreen(login); // HTML load errors + } + catch (Exception ex) + { + log.severe(ex.toString()); + } + if (!login.isConnected() || !login.isOKpressed()) + AEnv.exit(1); + } + + // Check Build + if (!DB.isBuildOK(m_ctx)) + AEnv.exit(1); + + // Check DB (AppsServer Version checked in Login) + DB.isDatabaseOK(m_ctx); + } // initSystem + + // UI + private CPanel mainPanel = new CPanel(); + private BorderLayout mainLayout = new BorderLayout(); + private CTabbedPane centerPane = new CTabbedPane(); + private CPanel southPanel = new CPanel(); + private BorderLayout southLayout = new BorderLayout(); + private JMenuBar menuBar = new JMenuBar(); + protected JProgressBar progressBar = new JProgressBar(0,100); + private CPanel infoPanel = new CPanel(); + private CButton bNotes = new CButton(); + private CButton bRequests = new CButton(); + private GridLayout infoLayout = new GridLayout(); + private JProgressBar memoryBar = new JProgressBar(); + // Tabs + private PAPanel paPanel = null; + private VTreePanel treePanel = null; + private WFActivity wfActivity = null; + private WFPanel wfPanel = null; + private WindowMenu m_WindowMenu; + + /** + * Static Init. + *
    +	 *  - mainPanel
    +	 * 		- centerPane
    +	 *      	- treePanel
    +	 * 			- wfActivity
    +	 * 			- wfPanel
    +	 *      - southPanel
    +	 *          - infoPanel
    +	 *              - bNotes
    +	 *              - bTask
    +	 *              - memoryBar
    +	 *          - wfPanel
    +	 *          - progressBar
    +	 *  
    + * @throws Exception + */ + void jbInit() throws Exception + { + this.setName("Menu"); + this.setLocale(Language.getLoginLanguage().getLocale()); + this.setJMenuBar(menuBar); + // + mainPanel.setLayout(mainLayout); + mainLayout.setHgap(0); + mainLayout.setVgap(2); + // + treePanel.addPropertyChangeListener(VTreePanel.NODE_SELECTION, this); + // + infoPanel.setLayout(infoLayout); + infoLayout.setColumns(2); + infoLayout.setHgap(4); + infoLayout.setVgap(0); + // bNotes.setRequestFocusEnabled(false); + bNotes.setToolTipText(""); + bNotes.setActionCommand("Notes"); + bNotes.addActionListener(this); + bNotes.setIcon(Env.getImageIcon("GetMail24.gif")); + bNotes.setMargin(new Insets(0, 0, 0, 0)); + // bRequests.setRequestFocusEnabled(false); + bRequests.setActionCommand("Requests"); + bRequests.addActionListener(this); + bRequests.setIcon(Env.getImageIcon("Request24.gif")); + bRequests.setMargin(new Insets(0, 0, 0, 0)); + // + southLayout.setHgap(0); + southLayout.setVgap(1); + // + memoryBar.setStringPainted(true); + memoryBar.setOpaque(false); + memoryBar.setBorderPainted(false); + memoryBar.addMouseListener(new AMenu_MouseAdapter()); + // + progressBar.setStringPainted(true); + progressBar.setOpaque(false); + // + getContentPane().add(mainPanel); + mainPanel.add(centerPane, BorderLayout.CENTER); + mainPanel.add(southPanel, BorderLayout.SOUTH); + mainPanel.add(Box.createHorizontalStrut(3), BorderLayout.EAST); + mainPanel.add(Box.createHorizontalStrut(3), BorderLayout.WEST); + + // Tabs + centerPane.setFont(centerPane.getFont().deriveFont(centerPane.getFont().getSize2D()+1)); + paPanel = PAPanel.get(); + if (paPanel != null) + { + //centerPane.add(paPanel, Msg.getMsg(m_ctx, "PAPanel")); + centerPane.addTab(Msg.getMsg(m_ctx, "PAPanel"), Env.getImageIcon2("InfoAccount16"), paPanel); + m_tabMenu++; + m_tabActivities++; + m_tabWorkflow++; + } + treePanel.setBorder(BorderFactory.createEmptyBorder(2,3,2,3)); + //centerPane.add(treePanel, Msg.getMsg(m_ctx, "Menu")); + centerPane.addTab(Msg.getMsg(m_ctx, "Menu"), Env.getImageIcon2("Home16"), treePanel); + //centerPane.add(new CScrollPane(wfActivity), Msg.getMsg (m_ctx, "WorkflowActivities") + ": 0"); + centerPane.addTab(Msg.getMsg (m_ctx, "WorkflowActivities") + ": 0", Env.getImageIcon2("Assignment16"), new CScrollPane(wfActivity)); + //centerPane.add(new CScrollPane(wfPanel), Msg.getMsg (m_ctx, "WorkflowPanel")); + centerPane.addTab(Msg.getMsg (m_ctx, "WorkflowPanel"), Env.getImageIcon2("WorkFlow16"), new CScrollPane(wfPanel)); + centerPane.addChangeListener (this); + // + southPanel.setLayout(southLayout); + southPanel.add(infoPanel, BorderLayout.NORTH); + southPanel.add(progressBar, BorderLayout.SOUTH); + // + infoPanel.add(bNotes, null); + infoPanel.add(bRequests, null); + infoPanel.add(memoryBar, null); + // + int loc = Ini.getDividerLocation(); + if (loc > 0) + treePanel.setDividerLocation(loc); + } // jbInit + + /** + * Get Preferred Size + * @return preferred Size + */ + public Dimension getPreferredSize() + { + Dimension dim = Ini.getWindowDimension(0); + if (dim == null) + dim = new Dimension (350, 500); + return dim; + } // getPreferredSize + + + /** + * Create Menu + */ + private void createMenu() + { + // File + JMenu mFile = AEnv.getMenu("File"); + menuBar.add(mFile); + AEnv.addMenuItem("PrintScreen", null, KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, 0), mFile, this); + AEnv.addMenuItem("ScreenShot", null, KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, KeyEvent.SHIFT_MASK), mFile, this); + mFile.addSeparator(); + AEnv.addMenuItem("Logout", null, KeyStroke.getKeyStroke(KeyEvent.VK_L, Event.SHIFT_MASK+Event.ALT_MASK), mFile, this); + AEnv.addMenuItem("Exit", null, KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.SHIFT_MASK+Event.ALT_MASK), mFile, this); + + // View + JMenu mView = AEnv.getMenu("View"); + menuBar.add(mView); + + if (MRole.getDefault().isAllow_Info_Product()) + { + AEnv.addMenuItem("InfoProduct", null, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK), mView, this); + } + if (MRole.getDefault().isAllow_Info_BPartner()) + { + AEnv.addMenuItem("InfoBPartner", null, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK+Event.CTRL_MASK), mView, this); + } + if (MRole.getDefault().isShowAcct() && MRole.getDefault().isAllow_Info_Account()) + { + AEnv.addMenuItem("InfoAccount", null, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK+Event.CTRL_MASK), mView, this); + } + if (MRole.getDefault().isAllow_Info_Schedule()) + { + AEnv.addMenuItem("InfoSchedule", null, null, mView, this); + } + mView.addSeparator(); + if (MRole.getDefault().isAllow_Info_Order()) + { + AEnv.addMenuItem("InfoOrder", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Invoice()) + { + AEnv.addMenuItem("InfoInvoice", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_InOut()) + { + AEnv.addMenuItem("InfoInOut", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Payment()) + { + AEnv.addMenuItem("InfoPayment", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_CashJournal()) + { + AEnv.addMenuItem("InfoCashLine", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Resource()) + { + AEnv.addMenuItem("InfoAssignment", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Asset()) + { + AEnv.addMenuItem("InfoAsset", "Info", null, mView, this); + } + + // Tools + JMenu mTools = AEnv.getMenu("Tools"); + menuBar.add(mTools); + AEnv.addMenuItem("Calculator", null, null, mTools, this); + AEnv.addMenuItem("Calendar", null, null, mTools, this); + AEnv.addMenuItem("Editor", null, null, mTools, this); + MUser user = MUser.get(Env.getCtx()); + if (user.isAdministrator()) + AEnv.addMenuItem("Script", null, null, mTools, this); + if (AEnv.isWorkflowProcess()) + AEnv.addMenuItem("WorkFlow", null, null, mTools, this); + if (MRole.getDefault().isShowPreference()) + { + mTools.addSeparator(); + AEnv.addMenuItem("Preference", null, null, mTools, this); + } + + //Window Menu + m_WindowMenu = new WindowMenu(windowManager, this); + menuBar.add(m_WindowMenu); + KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_MASK); + this.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, "ShowAllWindow"); + AppsAction action = new AppsAction("ShowAllWindow", ks, false); + this.getRootPane().getActionMap().put("ShowAllWindow", action); + action.setDelegate(this); + + // Help + JMenu mHelp = AEnv.getMenu("Help"); + menuBar.add(mHelp); + AEnv.addMenuItem("Online", null, null, mHelp, this); + AEnv.addMenuItem("EMailSupport", null, null, mHelp, this); + AEnv.addMenuItem("About", null, null, mHelp, this); + } // createMenu + + /** + * Dispose - end system + */ + public void dispose() + { + preDispose(); + // + super.dispose(); + AEnv.exit(0); + } // dispose + + private void preDispose() { + // clean up - save window state + Ini.setWindowDimension(0, getSize()); + Ini.setDividerLocation(treePanel.getDividerLocation()); + Ini.setWindowLocation(0, getLocation()); + Ini.saveProperties(true); + // + infoUpdater.stop = true; + try { + infoUpdaterThread.join(50); + } catch(InterruptedException ire) { + } finally { + infoUpdaterThread = null; + infoUpdater = null; + } + } + + public void logout() + { + windowManager.close(); + preDispose(); + super.dispose(); + AEnv.logout(); + } + + /** + * Window Events - requestFocus + * @param e event + */ + protected void processWindowEvent(WindowEvent e) + { + super.processWindowEvent(e); + if (e.getID() == WindowEvent.WINDOW_OPENED) + { + treePanel.getSearchField().requestFocusInWindow(); + // this.toFront(); + } + } // processWindowEvent + + /** + * Set Busy + * @param value true if buzy + */ + protected void setBusy (boolean value) + { + m_startingItem = value; + if (value) + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + else + setCursor(Cursor.getDefaultCursor()); + // setEnabled (!value); // causes flicker + } // setBusy + + /** + * Selection in tree - launch Application + * @param e PropertyChangeEvent + */ + public void propertyChange(PropertyChangeEvent e) + { + MTreeNode nd = (MTreeNode)e.getNewValue(); + log.info(nd.getNode_ID() + " - " + nd.toString()); + + // ignore summary items & when loading + if (m_startingItem || nd.isSummary()) + return; + + String sta = nd.toString(); + progressBar.setString(sta); + int cmd = nd.getNode_ID(); + + (new AMenuStartItem(cmd, true, sta, this)).start(); // async load + //hengsin, updateInfo is call again in AMenuStartItem + //updateInfo(); + } // propertyChange + + + /************************************************************************** + * ActionListener + * @param e ActionEvent + */ + public void actionPerformed(ActionEvent e) + { + // Buttons + if (e.getSource() == bNotes) + gotoNotes(); + else if (e.getSource() == bRequests) + gotoRequests(); + else if (e.getActionCommand().equals("ShowAllWindow")) + m_WindowMenu.expose(); + else if (!AEnv.actionPerformed(e.getActionCommand(), m_WindowNo, this)) + log.log(Level.SEVERE, "unknown action=" + e.getActionCommand()); + //updateInfo(); + } // actionPerformed + + /** + * Get number of open Notes + * @return number of notes + */ + private int getNotes() + { + String sql = "SELECT COUNT(1) FROM AD_Note " + + "WHERE AD_Client_ID=? AND AD_User_ID IN (0,?)" + + " AND Processed='N'"; + int retValue = DB.getSQLValue(null, sql, Env.getAD_Client_ID(Env.getCtx()), m_AD_User_ID); + return retValue; + } // getNotes + + /** + * Open Note Window + */ + private void gotoNotes() + { + // AD_Table_ID for AD_Note = 389 HARDCODED + if (m_note_Menu_ID == 0) + m_note_Menu_ID = DB.getSQLValue(null, "SELECT AD_Menu_ID " + + "FROM AD_Menu m" + + " INNER JOIN AD_TABLE t ON (t.AD_Window_ID=m.AD_Window_ID) " + + "WHERE t.AD_Table_ID=?", 389); + if (m_note_Menu_ID == 0) + m_note_Menu_ID = 233; // fallback HARDCODED + (new AMenuStartItem (m_note_Menu_ID, true, Msg.translate(m_ctx, "AD_Note_ID"), this)).start(); // async load + } // gotoMessage + + /** + * Ger Number of open Requests + * @return number of requests + */ + private int getRequests() + { + if (m_requestSQL == null) + m_requestSQL = MRole.getDefault().addAccessSQL ("SELECT COUNT(1) FROM R_Request " + + "WHERE (SalesRep_ID=? OR AD_Role_ID=?) AND Processed='N'" + + " AND (DateNextAction IS NULL OR TRUNC(DateNextAction) <= TRUNC(SysDate))" + + " AND (R_Status_ID IS NULL OR R_Status_ID IN (SELECT R_Status_ID FROM R_Status WHERE IsClosed='N'))", + "R_Request", false, true); // not qualified - RW + int retValue = DB.getSQLValue(null, m_requestSQL, m_AD_User_ID, m_AD_Role_ID); + return retValue; + } // getRequests + + /** + * Open Request Window + */ + private void gotoRequests() + { + // AD_Table_ID for R_Request = 417 HARDCODED + // if (m_request_Menu_ID == 0) // Goes to Request (all) + // m_request_Menu_ID = DB.getSQLValue (null, "SELECT AD_Menu_ID " + // + "FROM AD_Menu m" + // + " INNER JOIN AD_TABLE t ON (t.AD_Window_ID=m.AD_Window_ID) " + // + "WHERE t.AD_Table_ID=?", 417); + if (m_request_Menu_ID == 0) + m_request_Menu_ID = 237; // My Requests + (new AMenuStartItem (m_request_Menu_ID, true, Msg.translate(m_ctx, "R_Request_ID"), this)).start(); // async load + } // gotoRequests + + /** + * Show Memory Info - run GC if required - Update Requests/Memos/Activities + */ + public void updateInfo() + { + double total = Runtime.getRuntime().totalMemory() / 1024; + double free = Runtime.getRuntime().freeMemory() / 1024; + double used = total - free; + double percent = used * 100 / total; + // + memoryBar.setMaximum((int)total); + memoryBar.setValue((int)used); + String msg = MessageFormat.format("{0,number,integer} MB - {1,number,integer}%", + new Object[] {new BigDecimal(total / 1024), new BigDecimal(percent)}); + memoryBar.setString(msg); + // + // msg = MessageFormat.format("Total Memory {0,number,integer} kB - Free {1,number,integer} kB", + msg = Msg.getMsg(m_ctx, "MemoryInfo", + new Object[] {new BigDecimal(total), new BigDecimal(free)}); + memoryBar.setToolTipText(msg); + // progressBar.repaint(); + + // CarlosRuiz - globalqss - [ 1881285 ] Remove unnecessary calls to System.gc + // if (percent > 50) + // System.gc(); + + if (DB.isConnected()) + { + // Requests + int requests = getRequests(); + bRequests.setText(Msg.translate(m_ctx, "R_Request_ID") + ": " + requests); + // Memo + int notes = getNotes(); + bNotes.setText(Msg.translate(m_ctx, "AD_Note_ID") + ": " + notes); + // Activities + int activities = wfActivity.getActivitiesCount(); + centerPane.setTitleAt(m_tabActivities, Msg.getMsg (m_ctx, "WorkflowActivities") + ": " + activities); + /* + log.config(msg + + ", Processors=" + Runtime.getRuntime().availableProcessors() + + ", Requests=" + requests + ", Notes=" + notes + ", Activities=" + activities + + "," + CConnection.get().getStatus() + ); + */ + MSystem.get(m_ctx).info(); + } + } // updateInfo + + + /** + * Update Activities Label + */ + public void updateActivities(int act_length) + { + centerPane.setTitleAt(m_tabActivities, Msg.getMsg (m_ctx, "WorkflowActivities") + ": " + act_length); + } // updateInfo + + /************************************************************************* + * Start Workflow Activity + * @param AD_Workflow_ID id + */ + protected void startWorkFlow (int AD_Workflow_ID) + { + centerPane.setSelectedIndex(m_tabWorkflow); // switch + wfPanel.load(AD_Workflow_ID, false); + } // startWorkFlow + + + /** + * Change Listener (tab) + * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent) + * @param e event + */ + public void stateChanged (ChangeEvent e) + { + //updateInfo(); + // show activities + if (centerPane.getSelectedIndex() == m_tabActivities) + { + wfActivity.loadActivities(); + wfActivity.display(); + } + } // stateChanged + + public WindowManager getWindowManager() { + return windowManager; + } + + /************************************************************************** + * Mouse Listener + */ + class AMenu_MouseAdapter extends MouseAdapter + { + /** + * Invoked when the mouse has been clicked on a component. + * @param e evant + */ + public void mouseClicked(MouseEvent e) + { + if (e.getClickCount() > 1) + { + System.gc(); + //updateInfo(); + } + } + } // AMenu_MouseAdapter + + + /************************************************************************** + * OS Start + * @param args Array of String arguments (ignored) + */ + public static void main(String[] args) + { + Splash.getSplash(); + Adempiere.startup(true); // needs to be here for UI + new AMenu(); + } // main + + class InfoUpdater implements Runnable + { + boolean stop = false; + + public void run() + { + int sleep = MSysConfig.getIntValue("MENU_INFOUPDATER_SLEEP_MS", 60000, Env.getAD_Client_ID(Env.getCtx())); + while(stop == false) + { + updateInfo(); + + try { + Thread.sleep(sleep); + } catch(InterruptedException ire) { } + } + } + } + +} // AMenu diff --git a/client/src/org/compiere/apps/APanel.java b/client/src/org/compiere/apps/APanel.java new file mode 100644 index 0000000000..7008f22582 --- /dev/null +++ b/client/src/org/compiere/apps/APanel.java @@ -0,0 +1,2501 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + * @contributor Victor Perez , e-Evolution.SC FR [ 1757088 ] + *****************************************************************************/ +package org.compiere.apps; + +import java.awt.*; +import java.awt.event.*; +import java.beans.PropertyChangeListener; +import java.util.*; +import java.util.logging.*; + +import javax.swing.*; +import javax.swing.event.*; +import org.compiere.apps.search.*; +import org.compiere.grid.*; +import org.compiere.grid.ed.*; +import org.compiere.model.*; +import org.compiere.plaf.*; +import org.compiere.print.*; +import org.compiere.process.*; +import org.compiere.swing.*; +import org.compiere.util.*; + +/** + * Main Panel of application window. + *
    + *  Structure:
    + *      (MenuBar) -> to be added to owning window
    + *		northPanel  (ToolBar)
    + *		tabPanel
    + *		southPanel  (StatusBar)
    + *  
    + * + * @author Jorg Janke + * @version $Id: APanel.java,v 1.4 2006/07/30 00:51:27 jjanke Exp $ + * + * Colin Rooney 2007/03/20 RFE#1670185 & related BUG#1684142 - Extend Sec to Info Queries + * @contributor Victor Perez , e-Evolution.SC FR [ 1757088 ] + * @contributor fer_luck@centuryon.com , FR [ 1757088 ] + * @author Teo Sarca, SC ARHIPAC SERVICE SRL + *
  • BF [ 1824621 ] History button can't be canceled + *
  • BF [ 1941271 ] VTreePanel is modifying even if is save wasn't successfull + */ +public final class APanel extends CPanel + implements DataStatusListener, ChangeListener, ActionListener, ASyncProcess +{ + private boolean isNested = false; + + /** + * Constructs a new instance. + * Need to call initPanel for dynamic initialization + */ + //FR [ 1757088 ] + public APanel(GridController gc, int windowNo){ + super(); + isNested = true; + m_ctx = Env.getCtx(); + try{ + m_curGC = gc; + gc.addDataStatusListener(this); + m_curTab = gc.getMTab(); + + Component tabElement = null; + tabElement = gc; + VTabbedPane tabPane = new VTabbedPane(false); + tabPane.addTab(m_curTab.getName().toString(), m_curTab, tabElement); + m_curWinTab = tabPane; + m_curWindowNo = windowNo; + jbInit(); + initSwitchLineAction(); + } + catch(Exception e){ + log.log(Level.SEVERE, "", e); + } + + createMenu(); + + MRole role = MRole.getDefault(); + m_curGC.query (m_onlyCurrentRows, m_onlyCurrentDays, role.getMaxQueryRecords()); + m_curTab.navigateCurrent(); // updates counter + m_curGC.dynamicDisplay(0); + } + + public APanel(AWindow window) + { + super(); + m_window = window; + + m_ctx = Env.getCtx(); + // + try + { + jbInit(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } + createMenu(); + } // APanel + + /** Logger */ + private static CLogger log = CLogger.getCLogger(APanel.class); + + private AWindow m_window; + private boolean isCancel = false; //Goodwill + + /** + * Dispose + */ + public void dispose() + { + // log.config(""); + // ignore changes + m_disposing = true; + // + if (m_curAPanelTab != null) + { + m_curAPanelTab.unregisterPanel(); + m_curAPanelTab = null; + } + // close panels + tabPanel.dispose(this); + tabPanel = null; + // All Workbenches + for (int i = 0; i < m_mWorkbench.getWindowCount(); i++) + { + m_curWindowNo = m_mWorkbench.getWindowNo(i); + log.info("#" + m_curWindowNo); + Env.setAutoCommit(m_ctx, m_curWindowNo, false); + m_mWorkbench.dispose(i); + Env.clearWinContext(m_ctx, m_curWindowNo); + } // all Workbenchens + + // Get rid of remaining model + if (m_mWorkbench != null) + m_mWorkbench.dispose(); + m_mWorkbench = null; + // MenuBar + if (menuBar != null) + menuBar.removeAll(); + menuBar = null; + // ToolBar + if (toolBar != null) + toolBar.removeAll(); + toolBar = null; + // Prepare GC + this.removeAll(); + } // dispose + + /** + * The Layout. + */ + private BorderLayout mainLayout = new BorderLayout(); + private VTabbedPane tabPanel = new VTabbedPane(true); + private StatusBar statusBar = new StatusBar(); + private CPanel northPanel = new CPanel(); + private JToolBar toolBar = new JToolBar(); + private JMenuBar menuBar = new JMenuBar(); + private FlowLayout northLayout = new FlowLayout(); + + /** + * Initializes the state of this instance. + * @throws Exception + */ + private void jbInit() throws Exception + { + this.setLocale(Language.getLoginLanguage().getLocale()); + this.setLayout(mainLayout); + + // tabPanel + mainLayout.setHgap(2); + mainLayout.setVgap(2); + if (isNested) + this.add(m_curGC, BorderLayout.CENTER); + else + { + CPanel dummy = new CPanel(); + dummy.setLayout(new BorderLayout()); + dummy.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2)); + dummy.add(tabPanel, BorderLayout.CENTER); + this.add(dummy, BorderLayout.CENTER); + } + // southPanel + this.add(statusBar, BorderLayout.SOUTH); + // northPanel + this.add(northPanel, BorderLayout.NORTH); + northPanel.setLayout(northLayout); + northLayout.setAlignment(FlowLayout.LEFT); + toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE); + toolBar.setBorderPainted(false); + toolBar.setFloatable(false); // teo_sarca, [ 1666591 ] Toolbar should not be floatable + northPanel.add(toolBar, null); + } // jbInit + + private AppsAction aPrevious, aNext, aParent, aDetail, aFirst, aLast, + aNew, aCopy, aDelete, aPrint, aPrintPreview, + aRefresh, aHistory, aAttachment, aChat, aMulti, aFind, + aWorkflow, aZoomAcross, aRequest, aWinSize, aArchive; + /** Ignore Button */ + public AppsAction aIgnore; + /** Save Button */ + public AppsAction aSave; + /** Private Lock Button */ + public AppsAction aLock; + // Local (added to toolbar) + private AppsAction aReport, aEnd, aHome, aHelp, aProduct, aLogout, + aAccount, aCalculator, aCalendar, aEditor, aPreference, aScript, + aOnline, aMailSupport, aAbout, aPrintScr, aScrShot, aExit, aBPartner, + aDeleteSelection, aShowAllWindow; + + private SwitchAction aSwitchLinesDownAction, aSwitchLinesUpAction; + + private WindowMenu m_WindowMenu; + /************************************************************************** + * Create Menu and Toolbar and registers keyboard actions. + * - started from constructor + */ + private void createMenu() + { + /** + * Menu + */ + // menuBar.setHelpMenu(); + // File + JMenu mFile = AEnv.getMenu("File"); + menuBar.add(mFile); + aPrintScr = addAction("PrintScreen", mFile, KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, 0), false); + aScrShot = addAction("ScreenShot", mFile, KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, Event.SHIFT_MASK), false); + aReport = addAction("Report", mFile, KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0), false); + aPrint = addAction("Print", mFile, KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0), false); + aPrintPreview = addAction("PrintPreview", mFile, KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.SHIFT_MASK+Event.ALT_MASK), false); + mFile.addSeparator(); + aEnd = addAction("End", mFile, KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.ALT_MASK), false); + aLogout = addAction("Logout", mFile, KeyStroke.getKeyStroke(KeyEvent.VK_L, Event.SHIFT_MASK+Event.ALT_MASK), false); + aExit = addAction("Exit", mFile, KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.SHIFT_MASK+Event.ALT_MASK), false); + // Edit + JMenu mEdit = AEnv.getMenu("Edit"); + menuBar.add(mEdit); + aNew = addAction("New", mEdit, KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), false); + aSave = addAction("Save", mEdit, KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0), false); + mEdit.addSeparator(); + aCopy = addAction("Copy", mEdit, KeyStroke.getKeyStroke(KeyEvent.VK_F2, Event.SHIFT_MASK), false); + aDelete = addAction("Delete", mEdit, KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0), false); + aDeleteSelection = addAction("DeleteSelection", mEdit, KeyStroke.getKeyStroke(KeyEvent.VK_D, Event.CTRL_MASK), false); + aIgnore = addAction("Ignore", mEdit, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), false); + aRefresh = addAction("Refresh", mEdit, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), false); + mEdit.addSeparator(); + aFind = addAction("Find", mEdit, KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0), true); // toggle + if (m_isPersonalLock) + aLock = addAction("Lock", mEdit, null, true); // toggle + // View + JMenu mView = AEnv.getMenu("View"); + menuBar.add(mView); + if (MRole.getDefault().isAllow_Info_Product()) + { + aProduct = addAction("InfoProduct", mView, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK), false); + } + if (MRole.getDefault().isAllow_Info_BPartner()) + { + aBPartner = addAction("InfoBPartner", mView, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.SHIFT_MASK+Event.ALT_MASK), false); + } + if (MRole.getDefault().isShowAcct() && MRole.getDefault().isAllow_Info_Account()) + { + aAccount = addAction("InfoAccount",mView, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK+Event.CTRL_MASK), false); + } + if (MRole.getDefault().isAllow_Info_Schedule()) + { + AEnv.addMenuItem("InfoSchedule", null, null, mView, this); + } + mView.addSeparator(); + if (MRole.getDefault().isAllow_Info_Order()) + { + AEnv.addMenuItem("InfoOrder", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Invoice()) + { + AEnv.addMenuItem("InfoInvoice", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_InOut()) + { + AEnv.addMenuItem("InfoInOut", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Payment()) + { + AEnv.addMenuItem("InfoPayment", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_CashJournal()) + { + AEnv.addMenuItem("InfoCashLine", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Resource()) + { + AEnv.addMenuItem("InfoAssignment", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Asset()) + { + AEnv.addMenuItem("InfoAsset", "Info", null, mView, this); + } + + mView.addSeparator(); + aAttachment = addAction("Attachment", mView, KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0), true); // toggle + aChat = addAction("Chat", mView, null, true); // toggle + aHistory = addAction("History", mView, KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0), true); // toggle + mView.addSeparator(); + aMulti = addAction("Multi", mView, KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0), true); // toggle + // Go + JMenu mGo = AEnv.getMenu("Go"); + menuBar.add(mGo); + aFirst = addAction("First", mGo, KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, Event.ALT_MASK), false); + aPrevious = addAction("Previous", mGo, KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.ALT_MASK), false); + aNext = addAction("Next", mGo, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.ALT_MASK), false); + aLast = addAction("Last", mGo, KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, Event.ALT_MASK), false); + mGo.addSeparator(); + aParent = addAction("Parent", mGo, KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, Event.ALT_MASK), false); + aDetail = addAction("Detail", mGo, KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, Event.ALT_MASK), false); + mGo.addSeparator(); + aZoomAcross = addAction("ZoomAcross", mGo, null, false); + aRequest = addAction("Request", mGo, null, false); + aArchive = addAction("Archive", mGo, null, false); + aHome = addAction("Home", mGo, null, false); + // Tools + JMenu mTools = AEnv.getMenu("Tools"); + menuBar.add(mTools); + aCalculator = addAction("Calculator", mTools, null, false); + aCalendar = addAction("Calendar", mTools, null, false); + aEditor = addAction("Editor", mTools, null, false); + MUser user = MUser.get(Env.getCtx()); + if (user.isAdministrator()) + aScript = addAction("Script", mTools, null, false); + if ("Y".equals(Env.getContext(m_ctx, "#SysAdmin"))) // set in DB.loginDB + aWinSize = addAction("WinSize", mTools, null, false); + if (AEnv.isWorkflowProcess()) + aWorkflow = addAction("WorkFlow", mTools, null, false); + if (MRole.getDefault().isShowPreference()) + { + mTools.addSeparator(); + aPreference = addAction("Preference", mTools, null, false); + } + + //Window + AMenu aMenu = (AMenu)Env.getWindow(0); + m_WindowMenu = new WindowMenu(aMenu.getWindowManager(), m_window); + menuBar.add(m_WindowMenu); + aShowAllWindow = addAction("ShowAllWindow", null, KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_MASK), false); + + // Help + JMenu mHelp = AEnv.getMenu("Help"); + menuBar.add(mHelp); + aHelp = addAction("Help", mHelp, KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), false); + aOnline = addAction("Online", mHelp, null, false); + aMailSupport = addAction("EMailSupport", mHelp, null, false); + aAbout = addAction("About", mHelp, null, false); + + /** + * ToolBar + */ + toolBar.add(aIgnore.getButton()); // ESC + toolBar.addSeparator(); + toolBar.add(aHelp.getButton()); // F1 + toolBar.add(aNew.getButton()); + toolBar.add(aDelete.getButton()); + toolBar.add(aDeleteSelection.getButton()); + toolBar.add(aSave.getButton()); + toolBar.addSeparator(); + toolBar.add(aRefresh.getButton()); // F5 + if (!isNested()) + toolBar.add(aFind.getButton()); + toolBar.add(aAttachment.getButton()); + toolBar.add(aChat.getButton()); + toolBar.add(aMulti.getButton()); + toolBar.addSeparator(); + //FR [ 1757088 ] + if((m_curGC == null) || (m_curGC != null && !m_curGC.isDetailGrid())){ + toolBar.add(aHistory.getButton()); // F9 + toolBar.add(aHome.getButton()); // F10 is Windows Menu Key + toolBar.add(aParent.getButton()); + toolBar.add(aDetail.getButton()); + toolBar.addSeparator(); + } + + toolBar.add(aFirst.getButton()); + toolBar.add(aPrevious.getButton()); + toolBar.add(aNext.getButton()); + toolBar.add(aLast.getButton()); + toolBar.addSeparator(); + toolBar.add(aReport.getButton()); + toolBar.add(aArchive.getButton()); + toolBar.add(aPrintPreview.getButton()); + toolBar.add(aPrint.getButton()); + // FR [ 1757088 ] + if((m_curGC == null) || (m_curGC != null && !m_curGC.isDetailGrid())){ + toolBar.addSeparator(); + if (m_isPersonalLock) + toolBar.add(aLock.getButton()); + toolBar.add(aZoomAcross.getButton()); + if (aWorkflow != null) + toolBar.add(aWorkflow.getButton()); + toolBar.add(aRequest.getButton()); + if (MRole.getDefault().isAllow_Info_Product()) + { + toolBar.add(aProduct.getButton()); + } + toolBar.addSeparator(); + toolBar.add(aEnd.getButton()); + } + + // + if (CLogMgt.isLevelAll()) + Util.printActionInputMap(this); + } // createMenu + + + /** + * Add (Toggle) Action to Toolbar and Menu + * @param actionName action name + * @param menu manu + * @param accelerator accelerator + * @param toggle toggle button + * @return AppsAction + */ + private AppsAction addAction (String actionName, JMenu menu, KeyStroke accelerator, boolean toggle) + { + AppsAction action = new AppsAction(actionName, accelerator, toggle); + if (menu != null) + menu.add(action.getMenuItem()); + action.setDelegate(this); + // AbstractButton b = action.getButton(); + // String s = null; + // if (b != null) + // s = b.getToolTipText(); + + // Key Strokes + if (accelerator != null) + { + getInputMap(WHEN_IN_FOCUSED_WINDOW).put(accelerator, actionName); + getActionMap().put(actionName, action); + } + // + return action; + } // addAction + + /** + * Return MenuBar + * @return JMenuBar + */ + public JMenuBar getMenuBar() + { + return menuBar; + } // getMenuBar + + /** + * Get Title of Window + * @return String with Title + */ + public String getTitle() + { + if (m_mWorkbench != null && m_mWorkbench.getWindowCount() > 1) + { + StringBuffer sb = new StringBuffer(); + sb.append(m_mWorkbench.getName()).append(" ") + .append(Env.getContext(m_ctx, "#AD_User_Name")).append("@") + .append(Env.getContext(m_ctx, "#AD_Client_Name")).append(".") + .append(Env.getContext(m_ctx, "#AD_Org_Name")).append(" [") + .append(Env.getContext(m_ctx, "#DB_UID")).append("]"); + return sb.toString(); + } + return Env.getHeader(m_ctx, m_curWindowNo); + } // getTitle + + + private Properties m_ctx; + + /** Workbench Model */ + private GridWorkbench m_mWorkbench; + /** Current MTab */ + private GridTab m_curTab; + /** Current GridController */ + private GridController m_curGC; + /** Current Window Panel */ + private JTabbedPane m_curWinTab = null; + /** Current Window No */ + private int m_curWindowNo; + /** Current Window Panel Index */ + private int m_curTabIndex = -1; + /** Current Tab Order */ + private APanelTab m_curAPanelTab = null; + + /** Dispose active */ + private boolean m_disposing = false; + /** Save Error Message indicator */ + private boolean m_errorDisplayed = false; + /** Only current row flag */ + private boolean m_onlyCurrentRows = true; + /** Number of days to show 0=all */ + private int m_onlyCurrentDays = 0; + /** Process Info */ + private boolean m_isLocked = false; + /** Show Personal Lock */ + private boolean m_isPersonalLock = MRole.getDefault().isPersonalLock(); + /** Last Modifier of Action Event */ + private int m_lastModifiers; + + + /************************************************************************** + * Dynamic Panel Initialization - either single window or workbench. + *
    +	 *  either
    +	 *  - Workbench tabPanel    (VTabbedPane)
    +	 *      - Tab               (GridController)
    +	 *  or
    +	 *  - Workbench tabPanel    (VTabbedPane)
    +	 *      - Window            (VTabbedPane)
    +	 *          - Tab           (GridController)
    +	 *  
    + * tabPanel + * @param AD_Workbench_ID if > 0 this is a workbench, AD_Window_ID ignored + * @param AD_Window_ID if not a workbench, Window ID + * @param query if not a Workbench, Zoom Query - additional SQL where clause + * @return true if Panel is initialized successfully + */ + public boolean initPanel (int AD_Workbench_ID, int AD_Window_ID, MQuery query) + { + log.info("WB=" + AD_Workbench_ID + ", Win=" + AD_Window_ID + ", Query=" + query); + this.setName("APanel" + AD_Window_ID); + + // Single Window + if (AD_Workbench_ID == 0) + m_mWorkbench = new GridWorkbench(m_ctx, AD_Window_ID); + else + // Workbench + { + // m_mWorkbench = new MWorkbench(m_ctx); + // if (!m_mWorkbench.initWorkbench (AD_Workbench_ID)) + // { + // log.log(Level.SEVERE, "APanel.initWindow - No Workbench Model"); + // return false; + // } + // tabPanel.setWorkbench(true); + // tabPanel.addChangeListener(this); + ADialog.warn(0, this, "","Not implemented yet"); + return false; + } + + Dimension windowSize = m_mWorkbench.getWindowSize(); + + /** + * WorkBench Loop + */ + for (int wb = 0; wb < m_mWorkbench.getWindowCount(); wb++) + { + // Get/set WindowNo + m_curWindowNo = Env.createWindowNo (this); // Timing: ca. 1.5 sec + m_mWorkbench.setWindowNo(wb, m_curWindowNo); + // Set AutoCommit for this Window + Env.setAutoCommit(m_ctx, m_curWindowNo, Env.isAutoCommit(m_ctx)); + boolean autoNew = Env.isAutoNew(m_ctx); + Env.setAutoNew(m_ctx, m_curWindowNo, autoNew); + + // Workbench Window + VTabbedPane window = null; + // just one window + if (m_mWorkbench.getWindowCount() == 1) + { + window = tabPanel; + window.setWorkbench(false); + } + else + { + VTabbedPane tp = new VTabbedPane(false); + window = tp; + } + // Window Init + window.addChangeListener(this); + + /** + * Init Model + */ + int wbType = m_mWorkbench.getWindowType(wb); + + /** + * Window + */ + if (wbType == GridWorkbench.TYPE_WINDOW) + { + HashMap includedMap = new HashMap(4); + // + GridWindowVO wVO = AEnv.getMWindowVO(m_curWindowNo, m_mWorkbench.getWindowID(wb), 0); + if (wVO == null) + { + ADialog.error(0, null, "AccessTableNoView", "(No Window Model Info)"); + return false; + } + GridWindow mWindow = new GridWindow (wVO); // Timing: ca. 0.3-1 sec + // Set SO/AutoNew for Window + Env.setContext(m_ctx, m_curWindowNo, "IsSOTrx", mWindow.isSOTrx()); + if (!autoNew && mWindow.isTransaction()) + Env.setAutoNew(m_ctx, m_curWindowNo, true); + m_mWorkbench.setMWindow(wb, mWindow); + if (wb == 0) + m_onlyCurrentRows = mWindow.isTransaction(); // default = only current + if (windowSize == null) + windowSize = mWindow.getWindowSize(); + + /** + * Window Tabs + */ + int tabSize = mWindow.getTabCount(); + boolean goSingleRow = query != null; // Zoom Query + for (int tab = 0; tab < tabSize; tab++) + { + boolean included = false; + // MTab + if (tab == 0) mWindow.initTab(0); + GridTab gTab = mWindow.getTab(tab); + Env.setContext(m_ctx, m_curWindowNo, tab, "TabLevel", Integer.toString(gTab.getTabLevel())); + // Query first tab + if (tab == 0) + { + // initial user query for single workbench tab + if (m_mWorkbench.getWindowCount() == 1) + { + isCancel = false; //Goodwill + query = initialQuery (query, gTab); + if (isCancel) return false; //Cancel opening window + if (query != null && query.getRecordCount() <= 1) + goSingleRow = true; + } + else if (wb != 0) + // workbench dynamic query for dependent windows + { + query = m_mWorkbench.getQuery(); + } + // Set initial Query on first tab + if (query != null) + { + m_onlyCurrentRows = false; // Query might involve history + gTab.setQuery(query); + } + if (wb == 0) + m_curTab = gTab; + } // query on first tab + + Component tabElement = null; + // GridController + if (gTab.isSortTab()) + { + VSortTab st = new VSortTab(m_curWindowNo, gTab.getAD_Table_ID(), + gTab.getAD_ColumnSortOrder_ID(), gTab.getAD_ColumnSortYesNo_ID()); + st.setTabLevel(gTab.getTabLevel()); + tabElement = st; + } + else // normal tab + { + GridController gc = new GridController(); // Timing: ca. .1 sec + CompiereColor cc = mWindow.getColor(); + if (cc != null) + gc.setBackgroundColor(cc); // set color on Window level + gc.initGrid(gTab, false, m_curWindowNo, this, mWindow, (tab != 0)); // will set color on Tab level + // Timing: ca. 6-7 sec for first .2 for next + gc.addDataStatusListener(this); + gc.registerESCAction(aIgnore); // register Escape Key + // Set First Tab + if (wb == 0 && tab == 0) + { + m_curGC = gc; + Dimension size = gc.getPreferredSize(); // Screen Sizing + size.width += 4; + size.height += 4; + gc.setPreferredSize(size); + } + tabElement = gc; + // If we have a zoom query, switch to single row + if (tab == 0 && goSingleRow) + gc.switchSingleRow(); + + // FR [ 1757088 ] + GridField[] fields = gc.getMTab().getFields(); + int m_tab_id = 0; + for(int f =0 ; f < fields.length ; f ++) + { + m_tab_id = fields[f].getIncluded_Tab_ID(); + if ( m_tab_id != 0) + { + includedMap.put(m_tab_id, gc); + } + } + + // Is this tab included? + if (includedMap.size() > 0) + { + GridController parent = (GridController)includedMap.get(new Integer(gTab.getAD_Tab_ID())); + if (parent != null) + { + // FR [ 1757088 ] + gc.removeDataStatusListener(this); + GridSynchronizer synchronizer = new GridSynchronizer(mWindow, parent, gc); + if (parent == m_curGC) + synchronizer.activateChild(); + included = parent.includeTab(gc,this,synchronizer); + } + } + initSwitchLineAction(); + } // normal tab + + if (!included) // Add to TabbedPane + { + StringBuffer tabName = new StringBuffer (); + tabName.append (""); + if (gTab.isReadOnly()) + tabName.append(""); + int pos = gTab.getName ().indexOf (" "); + if (pos == -1) + tabName.append (gTab.getName ()).append ("
     "); + else + { + tabName.append (gTab.getName().substring (0, pos)) + .append ("
    ") + .append (gTab.getName().substring(pos + 1)); + } + if (gTab.isReadOnly()) + tabName.append("
    "); + tabName.append (""); + // Add Tab - sets ALT- and Shift-ALT- + window.addTab (tabName.toString(), gTab, tabElement); + } + } // Tab Loop + // Tab background + // window.setBackgroundColor(new AdempiereColor(Color.magenta, Color.green)); + } // Type-MWindow + + // Single Workbench Window Tab + if (m_mWorkbench.getWindowCount() == 1) + { + window.setToolTipText(m_mWorkbench.getDescription(wb)); + } + else + // Add Workbench Window Tab + { + tabPanel.addTab(m_mWorkbench.getName(wb), m_mWorkbench.getIcon(wb), window, m_mWorkbench.getDescription(wb)); + } + // Used for Env.getHeader + Env.setContext(m_ctx, m_curWindowNo, "WindowName", m_mWorkbench.getName(wb)); + + } // Workbench Loop + + // stateChanged (<->) triggered + toolBar.setName(getTitle()); + m_curTab.getTableModel().setChanged(false); + // Set Detail Button + aDetail.setEnabled(0 != m_curWinTab.getTabCount()-1); + + // Enable/Disable Tabs dynamically + if (m_curWinTab instanceof VTabbedPane) + ((VTabbedPane)m_curWinTab).evaluate(null); + // Size + if (windowSize != null) + setPreferredSize(windowSize); + else + revalidate(); + Dimension size = getPreferredSize(); + log.info( "fini - " + size); + m_curWinTab.requestFocusInWindow(); + return true; + } // initPanel + + /** + * Get Current Window No + * @return win no + */ + public int getWindowNo() + { + return m_curWindowNo; + } // getWindowNo + + /** + * Initial Query + * @param query initial query + * @param mTab tab + * @return query or null + */ + private MQuery initialQuery (MQuery query, GridTab mTab) + { + // We have a (Zoom) query + if (query != null && query.isActive() && query.getRecordCount() < 10) + return query; + // + StringBuffer where = new StringBuffer(); + // Query automatically if high volume and no query + boolean require = mTab.isHighVolume(); + if (!require && !m_onlyCurrentRows) // No Trx Window + { + String wh1 = mTab.getWhereExtended(); + if (wh1 == null || wh1.length() == 0) + wh1 = mTab.getWhereClause(); + if (wh1 != null && wh1.length() > 0) + where.append(wh1); + // + if (query != null) + { + String wh2 = query.getWhereClause(); + if (wh2.length() > 0) + { + if (where.length() > 0) + where.append (" AND "); + where.append(wh2); + } + } + // + StringBuffer sql = new StringBuffer("SELECT COUNT(*) FROM ") + .append(mTab.getTableName()); + if (where.length() > 0) + sql.append(" WHERE ").append(where); + // Does not consider security + int no = DB.getSQLValue(null, sql.toString()); + // + require = MRole.getDefault().isQueryRequire(no); + } + // Show Query + if (require) + { + GridField[] findFields = mTab.getFields(); + Find find = new Find (Env.getFrame(this), m_curWindowNo, mTab.getName(), + mTab.getAD_Tab_ID(), mTab.getAD_Table_ID(), mTab.getTableName(), + where.toString(), findFields, 10); // no query below 10 + query = find.getQuery(); + isCancel = (query == null);//Goodwill + find.dispose(); + find = null; + } + return query; + } // initialQuery + + + /** + * Get Window Index + * @return Window Index + */ + private int getWindowIndex() + { + // only one window + if (m_mWorkbench.getWindowCount() == 1) + return 0; + // workbench + return tabPanel.getSelectedIndex(); + } // getWindowIndex + + /** + * Is first Tab (on Window) + * @return true if the panel displays the first tab + */ + private boolean isFirstTab() + { + return m_curWinTab.getSelectedIndex() == 0; + } // isFirstTab + + /** + * Get Window Image + * @return image or null + */ + public Image getImage() + { + return m_mWorkbench.getImage(getWindowIndex()); + } // getImage + + + /************************************************************************** + * Data Status Listener (row change) ^ | v + * @param e event + */ + public void dataStatusChanged (DataStatusEvent e) + { + if (m_disposing) + return; + log.info(e.getMessage()); + String dbInfo = e.getMessage(); + if (m_curTab != null && m_curTab.isQueryActive()) + dbInfo = "[ " + dbInfo + " ]"; + statusBar.setStatusDB(dbInfo, e); + if (!isNested) + m_window.setTitle(getTitle()); + + // Set Message / Info + if (e.getAD_Message() != null || e.getInfo() != null) + { + StringBuffer sb = new StringBuffer(); + String msg = e.getMessage(); + if (msg != null && msg.length() > 0) + sb.append(Msg.getMsg(m_ctx, e.getAD_Message())); + String info = e.getInfo(); + if (info != null && info.length() > 0) + { + if (sb.length() > 0 && !sb.toString().trim().endsWith(":")) + sb.append(": "); + sb.append(info); + } + if (sb.length() > 0) + { + int pos = sb.indexOf("\n"); + if (pos != -1) // replace CR/NL + sb.replace(pos, pos+1, " - "); + setStatusLine (sb.toString (), e.isError ()); + } + } + + // Confirm Error + if (e.isError() && !e.isConfirmed()) + { + ADialog.error(m_curWindowNo, this, e.getAD_Message(), e.getInfo()); + e.setConfirmed(true); // show just once - if MTable.setCurrentRow is involved the status event is re-issued + m_errorDisplayed = true; + } + // Confirm Warning + else if (e.isWarning() && !e.isConfirmed()) + { + ADialog.warn(m_curWindowNo, this, e.getAD_Message(), e.getInfo()); + e.setConfirmed(true); // show just once - if MTable.setCurrentRow is involved the status event is re-issued + } + + // update Navigation + boolean firstRow = e.isFirstRow(); + aFirst.setEnabled(!firstRow); + aPrevious.setEnabled(!firstRow); + boolean lastRow = e.isLastRow(); + aNext.setEnabled(!lastRow); + aLast.setEnabled(!lastRow); + + // update Change + boolean changed = e.isChanged() || e.isInserting(); + boolean readOnly = m_curTab.isReadOnly(); + boolean insertRecord = !readOnly; + if (insertRecord) + insertRecord = m_curTab.isInsertRecord(); + aNew.setEnabled(!changed && insertRecord); + aCopy.setEnabled(!changed && insertRecord); + aRefresh.setEnabled(!changed); + aDelete.setEnabled(!changed && !readOnly); + // + if (readOnly && m_curTab.isAlwaysUpdateField()) + readOnly = false; + aIgnore.setEnabled(changed && !readOnly); + aSave.setEnabled(changed && !readOnly); + // + // No Rows + if (e.getTotalRows() == 0 && insertRecord) { + aNew.setEnabled(true); + aDelete.setEnabled(false); + aDeleteSelection.setEnabled(false); + } else { + aDeleteSelection.setEnabled(true); + } + + // Single-Multi + aMulti.setPressed(!m_curGC.isSingleRow()); + + // History (on first Tab only) + if (isFirstTab()) + aHistory.setPressed(!m_curTab.isOnlyCurrentRows()); + + // Transaction info + String trxInfo = m_curTab.getTrxInfo(); + if (trxInfo != null) + statusBar.setInfo(trxInfo); + + // Check Attachment + boolean canHaveAttachment = m_curTab.canHaveAttachment(); // not single _ID column + // + if (canHaveAttachment && e.isLoading() && m_curTab.getCurrentRow() > e.getLoadedRows()) + canHaveAttachment = false; + if (canHaveAttachment && m_curTab.getRecord_ID() == -1) // No Key + canHaveAttachment = false; + if (canHaveAttachment) + { + aAttachment.setEnabled(true); + aAttachment.setPressed(m_curTab.hasAttachment()); + aChat.setEnabled(true); + aChat.setPressed(m_curTab.hasChat()); + } + else + { + aAttachment.setEnabled(false); + aChat.setEnabled(false); + } + // Lock Indicator + if (m_isPersonalLock) + aLock.setPressed(m_curTab.isLocked()); + + if (m_curWinTab instanceof VTabbedPane) + ((VTabbedPane)m_curWinTab).evaluate(e); + // log.info("- fini", e.getMessage()); + } // dataStatusChanged + + /** + * Set Status Line to text + * @param text clear text + * @param error error flag + */ + public void setStatusLine (String text, boolean error) + { + log.fine(text); + statusBar.setStatusLine(text, error); + } // setStatusLine + + /** + * Indicate Busy + * @param busy busy + * @param focus request focus + */ + private void setBusy (boolean busy, boolean focus) + { + m_isLocked = busy; + // + JFrame frame = Env.getFrame(this); + if (frame == null) // during init + return; + if (frame instanceof AWindow) + ((AWindow)frame).setBusy(busy); + // String processing = Msg.getMsg(m_ctx, "Processing"); + if (busy) + { + // setStatusLine(processing); + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + } + else + { + this.setCursor(Cursor.getDefaultCursor()); + frame.setCursor(Cursor.getDefaultCursor()); + if (focus) + m_curGC.requestFocus(); + // if (statusBar.getStatusLine().equals(processing)) + // statusBar.setStatusLine(""); + } + } // set Busy + + + /************************************************************************** + * Change Listener - (tab change) <-> + * @param e event + */ + public void stateChanged (ChangeEvent e) + { + if (m_disposing) + return; + log.info(e.toString()); + setBusy(true, true); + + VTabbedPane tp = (VTabbedPane)e.getSource(); + boolean back = false; + boolean isAPanelTab = false; + + int previousIndex = 0; + + // Workbench Tab Change + if (tp.isWorkbench()) + { + int WBIndex = tabPanel.getSelectedIndex(); + m_curWindowNo = m_mWorkbench.getWindowNo(WBIndex); + // Window Change + log.info("curWin=" + m_curWindowNo + " - Win=" + tp); + if (tp.getSelectedComponent() instanceof JTabbedPane) + m_curWinTab = (JTabbedPane)tp.getSelectedComponent(); + else + throw new java.lang.IllegalArgumentException("Window does not contain Tabs"); + if (m_curWinTab.getSelectedComponent() instanceof GridController) { + m_curGC = (GridController)m_curWinTab.getSelectedComponent(); + initSwitchLineAction(); + } + // else if (m_curWinTab.getSelectedComponent() instanceof APanelTab) + // isAPanelTab = true; + else + throw new java.lang.IllegalArgumentException("Window-Tab does not contain GridControler"); + // change pointers + m_curTabIndex = m_curWinTab.getSelectedIndex(); + } + else + { + // Just a Tab Change + log.info("Tab=" + tp); + m_curWinTab = tp; + int tpIndex = m_curWinTab.getSelectedIndex(); + // detect no tab change + if (tpIndex == m_curTabIndex) return; + back = tpIndex < m_curTabIndex; + GridController gc = null; + if (m_curWinTab.getSelectedComponent() instanceof GridController) + gc = (GridController)m_curWinTab.getSelectedComponent(); + else if (m_curWinTab.getSelectedComponent() instanceof APanelTab) + isAPanelTab = true; + else + throw new java.lang.IllegalArgumentException("Tab does not contain GridControler"); + // Save old Tab + if (m_curGC != null) + { + m_curGC.stopEditor(true); + // has anything changed? + if (m_curTab.needSave(true, false)) + { // do we have real change + if (m_curTab.needSave(true, true)) + { + // Automatic Save + if (Env.isAutoCommit(m_ctx, m_curWindowNo)) + { + if (!m_curTab.dataSave(true)) + { // there is a problem, so we go back + m_curWinTab.setSelectedIndex(m_curTabIndex); + setBusy(false, true); + return; + } + } + // explicitly ask when changing tabs + else if (ADialog.ask(m_curWindowNo, this, "SaveChanges?", m_curTab.getCommitWarning())) + { // yes we want to save + if (!m_curTab.dataSave(true)) + { // there is a problem, so we go back + m_curWinTab.setSelectedIndex(m_curTabIndex); + setBusy(false, true); + return; + } + } + else // Don't save + m_curTab.dataIgnore(); + } + else // new record, but nothing changed + m_curTab.dataIgnore(); + } // there is a change + } + if (m_curAPanelTab != null) + { + m_curAPanelTab.saveData(); + m_curAPanelTab.unregisterPanel(); + m_curAPanelTab = null; + } + + // new tab + // if (m_curTabIndex >= 0) + // m_curWinTab.setForegroundAt(m_curTabIndex, AdempierePLAF.getTextColor_Normal()); + // m_curWinTab.setForegroundAt(tpIndex, AdempierePLAF.getTextColor_OK()); + previousIndex = m_curTabIndex; + m_curTabIndex = tpIndex; + if (!isAPanelTab) { + m_curGC = gc; + initSwitchLineAction(); + } + } + + // Sort Tab Handling + if (isAPanelTab) + { + m_curAPanelTab = (APanelTab)m_curWinTab.getSelectedComponent(); + m_curAPanelTab.registerAPanel(this); + m_curAPanelTab.loadData(); + // Consider that APanelTab (e.g. VSortTab) is not navigable - teo_sarca [ 1705444 ] + aFirst.setEnabled(false); + aPrevious.setEnabled(false); + aNext.setEnabled(false); + aLast.setEnabled(false); + } + else // Cur Tab Setting + { + int gwTabIndex = m_mWorkbench.getMWindow(0).getTabIndex(m_curGC.getMTab()); + //boolean needValidate = false; + if (m_mWorkbench.getMWindow(0).isTabInitialized(gwTabIndex) == false) + { + m_mWorkbench.getMWindow(0).initTab(gwTabIndex); + //needValidate = true; + } + m_curGC.activate(); + m_curTab = m_curGC.getMTab(); + + // Refresh only current row when tab is current + if (back && m_curTab.isCurrent()) + m_curTab.dataRefresh(); + else // Requery & autoSize + { + MRole role = MRole.getDefault(); + m_curGC.query (m_onlyCurrentRows, m_onlyCurrentDays, role.getMaxQueryRecords()); + /* + if (m_curGC.isNeedToSaveParent()) + { + // there is a problem, so we go back + ADialog.error(m_curWindowNo, this, "SaveParentFirst"); + m_curWinTab.setSelectedIndex(previousIndex); + setBusy(false, true); + return; + }*/ + } + // Set initial record + if (m_curTab.getRowCount() == 0) + { + // Automatically create New Record, if none & tab not RO + if (!m_curTab.isReadOnly() + && (Env.isAutoNew(m_ctx, m_curWindowNo) || m_curTab.isQueryNewRecord())) + { + log.config("No record - New - AutoNew=" + Env.isAutoNew(m_ctx, m_curWindowNo) + + " - QueryNew=" + m_curTab.isQueryNewRecord()); + m_curTab.dataNew(false); + } + else // No Records found + { + aSave.setEnabled(false); + aDelete.setEnabled(false); + aDeleteSelection.setEnabled(false); + } + m_curTab.navigateCurrent(); // updates counter + m_curGC.dynamicDisplay(0); + } + /* + if (needValidate) + { + JFrame frame = Env.getFrame(APanel.this); + if (frame != null) + { + //not sure why, the following lines is needed to make dynamic resize work + //tested on jdk1.5, 1.6 using jgoodies look and feel + frame.getPreferredSize(); + + if (frame.getExtendedState() != JFrame.MAXIMIZED_BOTH) + { + frame.setMinimumSize(frame.getSize()); + revalidate(); + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + JFrame frame = Env.getFrame(APanel.this); + frame.validate(); + AEnv.showCenterScreen(frame); + frame.setMinimumSize(null); + } + + }); + } + } + }*/ + // else ##CHANGE + // m_curTab.navigateCurrent(); + } + + // Update <-> Navigation + aDetail.setEnabled(m_curTabIndex != m_curWinTab.getTabCount()-1); + aParent.setEnabled(m_curTabIndex != 0 && m_curWinTab.getTabCount() > 1); + + // History (on first tab only) + if (m_mWorkbench.getMWindow(getWindowIndex()).isTransaction()) + aHistory.setEnabled(isFirstTab()); + else + { + aHistory.setPressed(false); + aHistory.setEnabled(false); + } + // Document Print + aPrint.setEnabled(m_curTab.isPrinted()); + aPrintPreview.setEnabled(m_curTab.isPrinted()); + // Query + aFind.setPressed(m_curTab.isQueryActive()); + + // Order Tab + if (isAPanelTab) + { + aMulti.setPressed(false); + aMulti.setEnabled(false); + aNew.setEnabled(false); + aDelete.setEnabled(false); + aDeleteSelection.setEnabled(false); + aFind.setEnabled(false); + aRefresh.setEnabled(false); + aAttachment.setEnabled(false); + aChat.setEnabled(false); + } + else // Grid Tab + { + aMulti.setEnabled(true); + aMulti.setPressed(!m_curGC.isSingleRow()); + aFind.setEnabled(true); + aRefresh.setEnabled(true); + aAttachment.setEnabled(true); + aChat.setEnabled(true); + } + // + m_curWinTab.requestFocusInWindow(); + setBusy(false, true); + log.config( "fini"); + } // stateChanged + + /** + * Navigate to Detail Tab -> + */ + private void cmd_detail() + { + int index = m_curWinTab.getSelectedIndex(); + if (index == m_curWinTab.getTabCount()-1) + return; + //hengsin, bug [ 1637763 ] + if (m_curWinTab instanceof VTabbedPane) + { + VTabbedPane tabPane = (VTabbedPane)m_curWinTab; + index++; + while ( index < tabPane.getTabCount() ) + { + if (tabPane.isEnabledAt(index)) + { + m_curGC.getTable().removeEditor(); + m_curGC.acceptEditorChanges(); + tabPane.setSelectedIndex(index); + break; + } + else + index++; + } + } + else + { + m_curGC.getTable().removeEditor(); + m_curGC.acceptEditorChanges(); + m_curWinTab.setSelectedIndex(index+1); + } + + } // navigateDetail + + /** + * Navigate to Parent Tab <- + */ + private void cmd_parent() + { + int index = m_curWinTab.getSelectedIndex(); + if (index == 0) + return; + //hengsin, bug [ 1637763 ] + if (m_curWinTab instanceof VTabbedPane) + { + VTabbedPane tabPane = (VTabbedPane)m_curWinTab; + index--; + while ( index >= 0 ) + { + if (tabPane.isEnabledAt(index)) + { + m_curGC.getTable().removeEditor(); + m_curGC.acceptEditorChanges(); + tabPane.setSelectedIndex(index); + break; + } + else + index--; + } + } + else + { + m_curGC.getTable().removeEditor(); + m_curGC.acceptEditorChanges(); + m_curWinTab.setSelectedIndex(index-1); + } + } // navigateParent + + + /************************************************************************** + * Action Listener + * @param e event + */ + public void actionPerformed (ActionEvent e) + { + log.info(e.getActionCommand() + " - " + e.getModifiers()); + // + " - " + new Timestamp(e.getWhen()) + " " + isUILocked()); + if (m_disposing || isUILocked()) + return; + + m_lastModifiers = e.getModifiers(); + String cmd = e.getActionCommand(); + // Do ScreenShot w/o busy + if (cmd.equals("ScreenShot")) + { + AEnv.actionPerformed (e.getActionCommand(), m_curWindowNo, this); + return; + } + + // Problem: doubleClick detection - can't disable button as clicking button may change button status + if (!cmd.equals(aShowAllWindow.getName())) + setBusy (true, true); + // Command Buttons + if (e.getSource() instanceof VButton) + { + setStatusLine(processButtonCallout((VButton)e.getSource()), true); + actionButton((VButton)e.getSource()); + setBusy(false, true); + return; + } + + try + { + // File + if (cmd.equals(aReport.getName())) + cmd_report(); + else if (cmd.equals(aPrint.getName())) + cmd_print(); + else if (cmd.equals(aPrintPreview.getName())) + cmd_print(true); + else if (cmd.equals(aEnd.getName())) + cmd_end(false); + else if (cmd.equals(aExit.getName())) + cmd_end(true); + // Edit + else if (cmd.equals(aNew.getName())) + cmd_new(false); + else if (cmd.equals(aSave.getName())) + cmd_save(true); + else if (cmd.equals(aCopy.getName())) + cmd_new(true); + else if (cmd.equals(aDelete.getName())) + cmd_delete(); + else if (cmd.equals(aDeleteSelection.getName())) + cmd_deleteSelection(); + else if (cmd.equals(aIgnore.getName())) + cmd_ignore(); + else if (cmd.equals(aRefresh.getName())) + cmd_refresh(); + else if (cmd.equals(aFind.getName())) + cmd_find(); + else if (m_isPersonalLock && cmd.equals(aLock.getName())) + cmd_lock(); + // View + else if (cmd.equals(aAttachment.getName())) + cmd_attachment(); + else if (cmd.equals(aChat.getName())) + cmd_chat(); + else if (cmd.equals(aHistory.getName())) + cmd_history(); + else if (cmd.equals(aMulti.getName())) + m_curGC.switchRowPresentation(); + // Go + else if (cmd.equals(aHome.getName())) { + // show main menu - teo_sarca [ 1706409, 1707221 ] + setBusy(false, false); + AEnv.showWindow(Env.getWindow(0)); + return; + } + else if (cmd.equals(aFirst.getName())) + { /*cmd_save(false);*/ + m_curGC.getTable().removeEditor(); + m_curGC.acceptEditorChanges(); + m_curTab.navigate(0); + } + else if (cmd.equals(aSwitchLinesUpAction.getName())) + { + //up-key + shift + m_curGC.getTable().removeEditor(); + m_curTab.switchRows(m_curTab.getCurrentRow(), m_curTab.getCurrentRow() - 1, m_curGC.getTable().getSortColumn(), m_curGC.getTable().isSortAscending()); + m_curGC.getTable().requestFocus(); + } + else if (cmd.equals(aPrevious.getName())) + { /* cmd_save(false); */ + //up-image + shift + m_curGC.getTable().removeEditor(); + m_curGC.acceptEditorChanges(); + if ((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0) { + m_curTab.switchRows(m_curTab.getCurrentRow(), m_curTab.getCurrentRow() - 1, m_curGC.getTable().getSortColumn(), m_curGC.getTable().isSortAscending()); + } else { + m_curTab.navigateRelative(-1); + } + } + else if (cmd.equals(aSwitchLinesDownAction.getName())) + { + //down-key + shift + m_curGC.getTable().removeEditor(); + m_curTab.switchRows(m_curTab.getCurrentRow(), m_curTab.getCurrentRow() + 1, m_curGC.getTable().getSortColumn(), m_curGC.getTable().isSortAscending()); + m_curGC.getTable().requestFocus(); + } + else if (cmd.equals(aNext.getName())) + { /* cmd_save(false); */ + //down-image + shift + m_curGC.getTable().removeEditor(); + m_curGC.acceptEditorChanges(); + if ((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0) { + m_curTab.switchRows(m_curTab.getCurrentRow(), m_curTab.getCurrentRow() + 1, m_curGC.getTable().getSortColumn(), m_curGC.getTable().isSortAscending()); + } else { + m_curTab.navigateRelative(+1); + } + } + else if (cmd.equals(aLast.getName())) + { /*cmd_save(false);*/ + m_curGC.getTable().removeEditor(); + m_curGC.acceptEditorChanges(); + m_curTab.navigate(m_curTab.getRowCount()-1); + } + else if (cmd.equals(aParent.getName())) + cmd_parent(); + else if (cmd.equals(aDetail.getName())) + cmd_detail(); + else if (cmd.equals(aZoomAcross.getName())) + cmd_zoomAcross(); + else if (cmd.equals(aRequest.getName())) + cmd_request(); + else if (cmd.equals(aArchive.getName())) + cmd_archive(); + // Tools + else if (aWorkflow != null && cmd.equals(aWorkflow.getName())) + { + if (m_curTab.getRecord_ID() <= 0) + ; + else if (m_curTab.getTabNo() == 0 && m_mWorkbench.getMWindow(getWindowIndex()).isTransaction()) + AEnv.startWorkflowProcess(m_curTab.getAD_Table_ID(), m_curTab.getRecord_ID()); + else + AEnv.startWorkflowProcess(m_curTab.getAD_Table_ID(), m_curTab.getRecord_ID()); + } + else if (aWinSize != null && cmd.equals(aWinSize.getName())) + cmd_winSize(); + // Help + else if (cmd.equals(aHelp.getName())) + cmd_help(); + // General Commands (Environment) + else if (cmd.equals(aLogout.getName())) + cmd_logout(); + else if (cmd.equals(aShowAllWindow.getName())) + m_WindowMenu.expose(); + else if (!AEnv.actionPerformed (e.getActionCommand(), m_curWindowNo, this)) + log.log(Level.SEVERE, "No action for: " + cmd); + } + catch (Exception ex) + { + log.log(Level.SEVERE, cmd, ex); + String msg = ex.getMessage(); + if (msg == null || msg.length() == 0) + msg = ex.toString(); + msg = Msg.parseTranslation(m_ctx, msg); + ADialog.error(m_curWindowNo, this, "Error", msg); + } + // + m_curWinTab.requestFocusInWindow(); + setBusy(false, true); + } // actionPerformed + + private void cmd_logout() { + JFrame top = Env.getWindow(0); + if (top instanceof AMenu) { + ((AMenu)top).logout(); + } + + } + + /************************************************************************** + * Process Callout(s). + *

    + * The Callout is in the string of + * "class.method;class.method;" + * If there is no class name, i.e. only a method name, the class is regarded + * as CalloutSystem. + * The class needs to comply with the Interface Callout. + * + * @param field field + * @return error message or "" + * @see org.compiere.model.Callout + */ + private String processButtonCallout (VButton button) + { + GridField field = m_curTab.getField(button.getColumnName()); + return m_curTab.processCallout(field); + } // processButtonCallout + + /** + * Create New Record + * @param copy true if current record is to be copied + */ + private void cmd_new (boolean copy) + { + log.config("copy=" + copy); + if (!m_curTab.isInsertRecord()) + { + log.warning("Insert Record disabled for Tab"); + return; + } + cmd_save(false); + m_curTab.dataNew (copy); + m_curGC.dynamicDisplay(0); + // m_curTab.getTableModel().setChanged(false); + } // cmd_new + + /** + * Confirm & delete record + */ + private void cmd_delete() + { + if (m_curTab.isReadOnly()) + return; + int keyID = m_curTab.getRecord_ID(); + if (ADialog.ask(m_curWindowNo, this, "DeleteRecord?")) + if (m_curTab.dataDelete()) + m_curGC.rowChanged(false, keyID); + m_curGC.dynamicDisplay(0); + } // cmd_delete + + /** + * Show a list to select one or more items to delete. + */ + private void cmd_deleteSelection(){ + if (m_curTab.isReadOnly()) + return; + //show table with deletion rows -> value, name... + JPanel messagePanel = new JPanel(); + JList list = new JList(); + JScrollPane scrollPane = new JScrollPane(list); + Vector data = new Vector(); + int noOfRows = m_curTab.getRowCount(); + for(int i=0; i parentColumnNames = m_curTab.getParentColumnNames(); + for (Iterator iter = parentColumnNames.iterator(); iter.hasNext();) { + String columnName = (String) iter.next(); + GridField field = m_curTab.getField(columnName); + if(field.isLookup()){ + Lookup lookup = field.getLookup(); + if (lookup != null){ + displayValue = displayValue.append(lookup.getDisplay(m_curTab.getValue(i,columnName))).append(" | "); + } else { + displayValue = displayValue.append(m_curTab.getValue(i,columnName)).append(" | "); + } + } else { + displayValue = displayValue.append(m_curTab.getValue(i,columnName)).append(" | "); + } + } + } else { + displayValue = displayValue.append(m_curTab.getValue(i,m_curTab.getKeyColumnName())); + } + if(m_curTab.getField("DocumentNo")!=null){ + displayValue = displayValue.append(" | ").append(m_curTab.getValue(i, "DocumentNo")); + } + if(m_curTab.getField("Line")!=null){ + displayValue = displayValue.append(" | ").append(m_curTab.getValue(i, "Line")); + } + if(m_curTab.getField("Value")!=null){ + displayValue = displayValue.append(" | ").append(m_curTab.getValue(i, "Value")); + } + if(m_curTab.getField("Name")!=null){ + displayValue = displayValue.append(" | ").append(m_curTab.getValue(i, "Name")); + } + data.add(displayValue.toString()); + } + list.setListData(data); + + + list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + messagePanel.add(scrollPane); + + final JOptionPane pane = new JOptionPane( + messagePanel, // message + JOptionPane.QUESTION_MESSAGE, // messageType + JOptionPane.OK_CANCEL_OPTION); // optionType + final JDialog deleteDialog = pane.createDialog(this.getParent(), Msg.getMsg(m_ctx, "DeleteSelection")); + deleteDialog.setVisible(true); + Integer okCancel = (Integer) pane.getValue(); + if(okCancel!=null && okCancel==JOptionPane.OK_OPTION){ + log.fine("ok"); + Object[] selectedValues = list.getSelectedValues(); + for (int i = 0; i < selectedValues.length; i++) { + log.fine(selectedValues[i].toString()); + } + int[] indices = list.getSelectedIndices(); + Arrays.sort(indices); + int offset = 0; + for (int i = 0; i < indices.length; i++) { + //m_curTab.setCurrentRow(indices[i]-offset); + m_curTab.navigate(indices[i]-offset); + int keyID = m_curTab.getRecord_ID(); + if (m_curTab.dataDelete()){ + m_curGC.rowChanged(false, keyID); + offset++; + } + } + m_curGC.dynamicDisplay(0); + } else { + log.fine("cancel"); + } + }//cmd_deleteSelection + + /** + * If required ask if you want to save and save it + * @param manualCmd true if invoked manually (i.e. force) + * @return true if saved + */ + public boolean cmd_save (boolean manualCmd) + { + if (m_curAPanelTab != null) + manualCmd = false; + log.config("Manual=" + manualCmd); + m_errorDisplayed = false; + m_curGC.stopEditor(true); + m_curGC.acceptEditorChanges(); + + if (m_curAPanelTab != null) + { + m_curAPanelTab.saveData(); + aSave.setEnabled(false); // set explicitly + } + + if (m_curTab.getCommitWarning().length() > 0 && m_curTab.needSave(true, false)) + if (!ADialog.ask(m_curWindowNo, this, "SaveChanges?", m_curTab.getCommitWarning())) + return false; + + // manually initiated + boolean retValue = m_curTab.dataSave(manualCmd); + // if there is no previous error + if (manualCmd && !retValue && !m_errorDisplayed) + { + ADialog.error(m_curWindowNo, this, "SaveIgnored"); + setStatusLine(Msg.getMsg(m_ctx, "SaveIgnored"), true); + } + if (retValue) + m_curGC.rowChanged(true, m_curTab.getRecord_ID()); + if (manualCmd) { + m_curGC.dynamicDisplay(0); + if (!isNested) + m_window.setTitle(getTitle()); + } + + //BEGIN - [FR 1953734] + if(m_curGC.isDetailGrid() && retValue){ + m_curGC.getGCParent().refreshMTab(m_curGC); + } + //END - [FR 1953734] + + return retValue; + } // cmd_save + + /** + * Ignore + */ + private void cmd_ignore() + { + m_curGC.stopEditor(false); + // Ignore changes in APanelTab (e.g. VSortTab) - teo_sarca [ 1705429 ] + if (m_curAPanelTab != null) + { + m_curAPanelTab.loadData(); + } + m_curTab.dataIgnore(); + m_curGC.dynamicDisplay(0); + + } // cmd_ignore + + /** + * Refresh + */ + private void cmd_refresh() + { + cmd_save(false); + m_curTab.dataRefreshAll(); + m_curGC.dynamicDisplay(0); + } // cmd_refresh + + /** + * Print standard Report + */ + private void cmd_report () + { + log.info(""); + if (!MRole.getDefault().isCanReport(m_curTab.getAD_Table_ID())) + { + ADialog.error(m_curWindowNo, this, "AccessCannotReport"); + return; + } + + cmd_save(false); + + // Query + MQuery query = new MQuery(m_curTab.getTableName()); + // Link for detail records + String queryColumn = m_curTab.getLinkColumnName(); + // Current row otherwise + if (queryColumn.length() == 0) + queryColumn = m_curTab.getKeyColumnName(); + // Find display + String infoName = null; + String infoDisplay = null; + for (int i = 0; i < m_curTab.getFieldCount(); i++) + { + GridField field = m_curTab.getField(i); + if (field.isKey()) + infoName = field.getHeader(); + if ((field.getColumnName().equals("Name") || field.getColumnName().equals("DocumentNo") ) + && field.getValue() != null) + infoDisplay = field.getValue().toString(); + if (infoName != null && infoDisplay != null) + break; + } + if (queryColumn.length() != 0) + { + if (queryColumn.endsWith("_ID")) + query.addRestriction(queryColumn, MQuery.EQUAL, + new Integer(Env.getContextAsInt(m_ctx, m_curWindowNo, queryColumn)), + infoName, infoDisplay); + else + query.addRestriction(queryColumn, MQuery.EQUAL, + Env.getContext(m_ctx, m_curWindowNo, queryColumn), + infoName, infoDisplay); + } + + new AReport (m_curTab.getAD_Table_ID(), aReport.getButton(), query, this, m_curWindowNo); + } // cmd_report + + + /** + * Zoom Across Menu + */ + private void cmd_zoomAcross() + { + int record_ID = m_curTab.getRecord_ID(); + log.info("ID=" + record_ID); + if (record_ID <= 0) + return; + + // Query + MQuery query = new MQuery(); + // Current row + String link = m_curTab.getKeyColumnName(); + // Link for detail records + if (link.length() == 0) + link = m_curTab.getLinkColumnName(); + if (link.length() != 0) + { + if (link.endsWith("_ID")) + query.addRestriction(link, MQuery.EQUAL, + new Integer(Env.getContextAsInt(m_ctx, m_curWindowNo, link))); + else + query.addRestriction(link, MQuery.EQUAL, + Env.getContext(m_ctx, m_curWindowNo, link)); + } + new AZoomAcross (aZoomAcross.getButton(), + m_curTab.getTableName(), query); + } // cmd_zoom + + /** + * Open/View Request + */ + private void cmd_request() + { + int record_ID = m_curTab.getRecord_ID(); + log.info("ID=" + record_ID); + if (record_ID <= 0) + return; + + int AD_Table_ID = m_curTab.getAD_Table_ID(); + int C_BPartner_ID = 0; + Object BPartner_ID = m_curTab.getValue("C_BPartner_ID"); + if (BPartner_ID != null) + C_BPartner_ID = ((Integer)BPartner_ID).intValue(); + new ARequest (aRequest.getButton(), AD_Table_ID, record_ID, C_BPartner_ID); + } // cmd_request + + /** + * Open/View Archive + */ + private void cmd_archive() + { + int record_ID = m_curTab.getRecord_ID(); + log.info("ID=" + record_ID); + if (record_ID <= 0) + return; + + int AD_Table_ID = m_curTab.getAD_Table_ID(); + new AArchive (aArchive.getButton(), AD_Table_ID, record_ID); + } // cmd_archive + + /** + * Print specific Report - or start default Report + */ + private void cmd_print() + { + cmd_print(false); + } + + /** + * Print specific Report - or start default Report + */ + private void cmd_print(boolean printPreview) + { + // Get process defined for this tab + int AD_Process_ID = m_curTab.getAD_Process_ID(); + log.info("ID=" + AD_Process_ID); + + // No report defined + if (AD_Process_ID == 0) + { + cmd_report(); + return; + } + + cmd_save(false); + // + int table_ID = m_curTab.getAD_Table_ID(); + int record_ID = m_curTab.getRecord_ID(); + ProcessInfo pi = new ProcessInfo (getTitle(), AD_Process_ID, table_ID, record_ID); + pi.setAD_User_ID (Env.getAD_User_ID(m_ctx)); + pi.setAD_Client_ID (Env.getAD_Client_ID(m_ctx)); + pi.setPrintPreview(printPreview); + + ProcessCtl.process(this, m_curWindowNo, pi, null); // calls lockUI, unlockUI + } // cmd_print + + /** + * Find - Set Query + */ + private void cmd_find() + { + if (m_curTab == null) + return; + cmd_save(false); + // Gets Fields from AD_Field_v + GridField[] findFields = GridField.createFields(m_ctx, m_curWindowNo, 0, m_curTab.getAD_Tab_ID()); + Find find = new Find (Env.getFrame(this), m_curWindowNo, m_curTab.getName(), + m_curTab.getAD_Tab_ID(), m_curTab.getAD_Table_ID(), m_curTab.getTableName(), + m_curTab.getWhereExtended(), findFields, 1); + MQuery query = find.getQuery(); + find.dispose(); + find = null; + + // Confirmed query + if (query != null) + { + m_onlyCurrentRows = false; // search history too + m_curTab.setQuery(query); + m_curGC.query(m_onlyCurrentRows, m_onlyCurrentDays, 0); // autoSize + } + aFind.setPressed(m_curTab.isQueryActive()); + } // cmd_find + + /** + * Attachment + */ + private void cmd_attachment() + { + int record_ID = m_curTab.getRecord_ID(); + log.info("Record_ID=" + record_ID); + if (record_ID == -1) // No Key + { + aAttachment.setEnabled(false); + return; + } + + // Attachment va = + new Attachment (Env.getFrame(this), m_curWindowNo, + m_curTab.getAD_AttachmentID(), m_curTab.getAD_Table_ID(), record_ID, null); + // + m_curTab.loadAttachments(); // reload + aAttachment.setPressed(m_curTab.hasAttachment()); + } // attachment + + /** + * Chat + */ + private void cmd_chat() + { + int record_ID = m_curTab.getRecord_ID(); + log.info("Record_ID=" + record_ID); + if (record_ID == -1) // No Key + { + aChat.setEnabled(false); + return; + } + // Find display + String infoName = null; + String infoDisplay = null; + for (int i = 0; i < m_curTab.getFieldCount(); i++) + { + GridField field = m_curTab.getField(i); + if (field.isKey()) + infoName = field.getHeader(); + if ((field.getColumnName().equals("Name") || field.getColumnName().equals("DocumentNo") ) + && field.getValue() != null) + infoDisplay = field.getValue().toString(); + if (infoName != null && infoDisplay != null) + break; + } + String description = infoName + ": " + infoDisplay; + // + // AChat va = + new AChat (Env.getFrame(this), m_curWindowNo, + m_curTab.getCM_ChatID(), m_curTab.getAD_Table_ID(), record_ID, + description, null); + // + m_curTab.loadChats(); // reload + aChat.setPressed(m_curTab.hasChat()); + } // chat + + /** + * Lock + */ + private void cmd_lock() + { + log.info("Modifiers=" + m_lastModifiers); + if (!m_isPersonalLock) + return; + int record_ID = m_curTab.getRecord_ID(); + if (record_ID == -1) // No Key + return; + // Control Pressed + if ((m_lastModifiers & InputEvent.CTRL_MASK) != 0) + { + new RecordAccessDialog(Env.getFrame(this), m_curTab.getAD_Table_ID(), record_ID); + } + else + { + m_curTab.lock (Env.getCtx(), record_ID, aLock.getButton().isSelected()); + m_curTab.loadAttachments(); // reload + } + aLock.setPressed(m_curTab.isLocked()); + } // lock + + /** + * Toggle History + */ + private void cmd_history() + { + log.info(""); + if (m_mWorkbench.getMWindow(getWindowIndex()).isTransaction()) + { + if (m_curTab.needSave(true, true) && !cmd_save(false)) + return; + + Point pt = new Point (0, aHistory.getButton().getBounds().height); + SwingUtilities.convertPointToScreen(pt, aHistory.getButton()); + VOnlyCurrentDays ocd = new VOnlyCurrentDays(Env.getFrame(this), pt); + if (!ocd.isCancel()) { + m_onlyCurrentDays = ocd.getCurrentDays(); + if (m_onlyCurrentDays == 1) // Day + { + m_onlyCurrentRows = true; + m_onlyCurrentDays = 0; // no Created restriction + } + else + m_onlyCurrentRows = false; + // + m_curTab.setQuery(null); // reset previous queries + MRole role = MRole.getDefault(); + int maxRows = role.getMaxQueryRecords(); + // + log.config("OnlyCurrent=" + m_onlyCurrentRows + + ", Days=" + m_onlyCurrentDays + + ", MaxRows=" + maxRows); + m_curGC.query(m_onlyCurrentRows, m_onlyCurrentDays, maxRows ); // autoSize + } + // Restore history button's pressed status + else { + if (isFirstTab()) + aHistory.setPressed(!m_curTab.isOnlyCurrentRows()); + } + } + } // cmd_history + + /** + * Help + */ + private void cmd_help() + { + log.info(""); + Help hlp = new Help (Env.getFrame(this), this.getTitle(), m_mWorkbench.getMWindow(getWindowIndex())); + hlp.setVisible(true); + } // cmd_help + + /** + * Close this screen - after save + * @param exit ask if user wants to exit application + */ + private void cmd_end (boolean exit) + { + boolean exitSystem = false; + if (!cmd_save(false)) + return; + if (exit && ADialog.ask(m_curWindowNo, this, "ExitApplication?")) + exitSystem = true; + + Env.getFrame(this).dispose(); // calls this dispose + + if (exitSystem) + AEnv.exit(0); + } // cmd_end + + /** + * Set Window Size + */ + private void cmd_winSize() + { + Dimension size = getSize(); + if (!ADialog.ask(m_curWindowNo, this, "WinSizeSet", + "x=" + size.width + " - y=" + size.height)) + { + setPreferredSize(null); + SwingUtilities.getWindowAncestor(this).pack(); + size = new Dimension (0,0); + } + // + MWindow win = new MWindow(m_ctx, m_curTab.getAD_Window_ID(), null); + win.setWindowSize(size); + win.save(); + } // cmdWinSize + + + + /************************************************************************** + * Start Button Process + * @param vButton button + */ + private void actionButton (VButton vButton) + { + log.info(vButton.toString()); + + boolean startWOasking = false; + boolean batch = false; + String col = vButton.getColumnName(); + + // Zoom + if (col.equals("Record_ID")) + { + int AD_Table_ID = Env.getContextAsInt (m_ctx, m_curWindowNo, "AD_Table_ID"); + int Record_ID = Env.getContextAsInt (m_ctx, m_curWindowNo, "Record_ID"); + AEnv.zoom(AD_Table_ID, Record_ID); + return; + } // Zoom + + // save first --------------- + if (m_curTab.needSave(true, false)) + if (!cmd_save(true)) + return; + // + int table_ID = m_curTab.getAD_Table_ID(); + // Record_ID + int record_ID = m_curTab.getRecord_ID(); + // Record_ID - Language Handling + if (record_ID == -1 && m_curTab.getKeyColumnName().equals("AD_Language")) + record_ID = Env.getContextAsInt (m_ctx, m_curWindowNo, "AD_Language_ID"); + // Record_ID - Change Log ID + if (record_ID == -1 + && (vButton.getProcess_ID() == 306 || vButton.getProcess_ID() == 307)) + { + Integer id = (Integer)m_curTab.getValue("AD_ChangeLog_ID"); + record_ID = id.intValue(); + } + // Ensure it's saved + if (record_ID == -1 && m_curTab.getKeyColumnName().endsWith("_ID")) + { + ADialog.error(m_curWindowNo, this, "SaveErrorRowNotFound"); + return; + } + + // Pop up Payment Rules + if (col.equals("PaymentRule")) + { + VPayment vp = new VPayment(m_curWindowNo, m_curTab, vButton); + if (vp.isInitOK()) // may not be allowed + vp.setVisible(true); + vp.dispose(); + if (vp.needSave()) + { + cmd_save(false); + cmd_refresh(); + } + } // PaymentRule + + // Pop up Document Action (Workflow) + else if (col.equals("DocAction")) + { + VDocAction vda = new VDocAction(m_curWindowNo, m_curTab, vButton, record_ID); + // Something to select from? + if (vda.getNumberOfOptions() == 0) + { + vda.dispose (); + log.info("DocAction - No Options"); + return; + } + else + { + vda.setVisible(true); + if (!vda.isStartProcess()) + return; + batch = vda.isBatch(); + startWOasking = true; + vda.dispose(); + } + } // DocAction + + // Pop up Create From + else if (col.equals("CreateFrom")) + { + // m_curWindowNo + VCreateFrom vcf = VCreateFrom.create (m_curTab); + if (vcf != null) + { + if (vcf.isInitOK()) + { + vcf.setVisible(true); + vcf.dispose(); + m_curTab.dataRefresh(); + } + else + vcf.dispose(); + return; + } + // else may start process + } // CreateFrom + + // Posting ----- + else if (col.equals("Posted") && MRole.getDefault().isShowAcct()) + { + // Check Doc Status + String processed = Env.getContext(m_ctx, m_curWindowNo, "Processed"); + if (!processed.equals("Y")) + { + String docStatus = Env.getContext(m_ctx, m_curWindowNo, "DocStatus"); + if (DocAction.STATUS_Completed.equals(docStatus) + || DocAction.STATUS_Closed.equals(docStatus) + || DocAction.STATUS_Reversed.equals(docStatus) + || DocAction.STATUS_Voided.equals(docStatus)) + ; + else + { + ADialog.error(m_curWindowNo, this, "PostDocNotComplete"); + return; + } + } + + // Check Post Status + Object ps = m_curTab.getValue("Posted"); + if (ps != null && ps.equals("Y")) + { + new org.compiere.acct.AcctViewer (Env.getContextAsInt (m_ctx, m_curWindowNo, "AD_Client_ID"), + m_curTab.getAD_Table_ID(), m_curTab.getRecord_ID()); + } + else + { + if (ADialog.ask(m_curWindowNo, this, "PostImmediate?")) + { + boolean force = ps != null && !ps.equals ("N"); // force when problems + String error = AEnv.postImmediate (m_curWindowNo, Env.getAD_Client_ID(m_ctx), + m_curTab.getAD_Table_ID(), m_curTab.getRecord_ID(), force); + m_curTab.dataRefresh(); + if (error != null) + ADialog.error(m_curWindowNo, this, "PostingError-N", error); + } + } + return; + } // Posted + + /** + * Start Process ---- + */ + + log.config("Process_ID=" + vButton.getProcess_ID() + ", Record_ID=" + record_ID); + if (vButton.getProcess_ID() == 0) + return; + // Save item changed + if (m_curTab.needSave(true, false)) + if (!cmd_save(true)) + return; + + // hengsin - [ 1639242 ] Inconsistent appearance of Process/Report Dialog + // globalqss - Add support for Don't ShowHelp option in Process + // this code must be changed if integrated the parameters and help in only one window + /* + MProcess pr = new MProcess(m_ctx, vButton.getProcess_ID(), null); + if (pr.getShowHelp() != null && pr.getShowHelp().equals("N")) { + startWOasking = true; + } + // end globalqss + + // Ask user to start process, if Description and Help is not empty + + if (!startWOasking && !(vButton.getDescription().equals("") && vButton.getHelp().equals(""))) + if (!ADialog.ask(m_curWindowNo, this, "StartProcess?", + // "" + vButton.getText() + "
    " + + vButton.getDescription() + "\n" + vButton.getHelp())) + return; + // + String title = vButton.getDescription(); + if (title == null || title.length() == 0) + title = vButton.getName(); + ProcessInfo pi = new ProcessInfo (title, vButton.getProcess_ID(), table_ID, record_ID); + pi.setAD_User_ID (Env.getAD_User_ID(m_ctx)); + pi.setAD_Client_ID (Env.getAD_Client_ID(m_ctx)); + pi.setIsBatch(batch); + + // Trx trx = Trx.get(Trx.createTrxName("AppsPanel"), true); + ProcessCtl.process(this, m_curWindowNo, pi, null); // calls lockUI, unlockUI + */ + + ProcessModalDialog dialog = new ProcessModalDialog(m_ctx, Env.getWindow(m_curWindowNo), Env.getHeader(m_ctx, m_curWindowNo), + this, m_curWindowNo, vButton.getProcess_ID(), table_ID, + record_ID, startWOasking); + if (dialog.isValid()) + { + dialog.validate(); + dialog.pack(); + AEnv.showCenterWindow(Env.getWindow(m_curWindowNo), dialog); + } + } // actionButton + + + /************************************************************************** + * Lock User Interface. + * Called from the Worker before processing + * @param pi process info + */ + public void lockUI (ProcessInfo pi) + { + // log.fine("" + pi); + setBusy(true, false); + } // lockUI + + /** + * Unlock User Interface. + * Called from the Worker when processing is done + * @param pi of execute ASync call + */ + public void unlockUI (ProcessInfo pi) + { + // log.fine("" + pi); + boolean notPrint = pi != null + && pi.getAD_Process_ID() != m_curTab.getAD_Process_ID() + && pi.isReportingProcess() == false; + // + setBusy(false, notPrint); + // Process Result + if (notPrint) // refresh if not print + { + // Refresh data + m_curTab.dataRefresh(); + // Timeout + if (pi.isTimeout()) // set temporarily to R/O + Env.setContext(m_ctx, m_curWindowNo, "Processed", "Y"); + m_curGC.dynamicDisplay(0); + // Update Status Line + setStatusLine(pi.getSummary(), pi.isError()); + // Get Log Info + ProcessInfoUtil.setLogFromDB(pi); + String logInfo = pi.getLogInfo(); + if (logInfo.length() > 0) + ADialog.info(m_curWindowNo, this, Env.getHeader(m_ctx, m_curWindowNo), + pi.getTitle(), logInfo); // clear text + } + } // unlockUI + + /** + * Is the UI locked (Internal method) + * @return true, if UI is locked + */ + public boolean isUILocked() + { + return m_isLocked; + } // isLoacked + + /** + * Method to be executed async. + * Called from the ASyncProcess worker + * @param pi process info + */ + public void executeASync (ProcessInfo pi) + { + log.config("-"); + } // executeASync + + /** + * Get Current Tab + * @return current tab + */ + public GridTab getCurrentTab() + { + return m_curTab; + } // getCurrentTab + + /** + * Get the number of tabs in the panels JTabbedPane. + * @return no of tabs in the JTabbedPane of the panel + */ + public int noOfTabs() { + return m_curWinTab.getTabCount(); + } + + /** + * Get the selected tab index of the panels JTabbedPane. + * @return selected index of JTabbedPane + */ + public int getSelectedTabIndex() { + return m_curWinTab.getSelectedIndex(); + } + + /** + * Set the tab index of the panels JTabbedPane. + */ + public void setSelectedTabIndex(int index) { + m_curWinTab.setSelectedIndex(index); + } + + /** + * Get the name of the selected tab in the panels JTabbedPane. + * @return name of selected tab + */ + public String getSelectedTabName() { + String title = m_curWinTab.getTitleAt(m_curWinTab.getSelectedIndex()); + title = title.substring(title.indexOf("")+6); + title = title.substring(0,title.indexOf('<')); + return title; + } + + /** + * String representation + * @return String representation + */ + public String toString() + { + String s = "APanel[curWindowNo=" + m_curWindowNo; + if (m_mWorkbench != null) + s += ",WB=" + m_mWorkbench.toString(); + s += "]"; + return s; + } // toString + + /** + * Simple action class for the resort of tablelines (switch line no). Delegates actionPerformed + * to APanel. + * + * @author Karsten Thiemann, kthiemann@adempiere.org + * + */ + class SwitchAction extends AbstractAction { + + /** the action listener - APanel */ + private ActionListener al; + + /** action name */ + private String name; + + /** + * Constructor. + * @param name + * @param accelerator + * @param al + */ + SwitchAction(String name, KeyStroke accelerator, ActionListener al) { + super(name); + putValue(Action.NAME, name); // Display + putValue(Action.SHORT_DESCRIPTION, name); // Tooltip + putValue(Action.ACCELERATOR_KEY, accelerator); // KeyStroke + putValue(Action.ACTION_COMMAND_KEY, name); // ActionCammand + this.al = al; + this.name = name; + } + + public void actionPerformed(ActionEvent e) { + al.actionPerformed(e); + } // actionPerformed + + public String getName() { + return name; + } + } + + /** + * Removes the default KeyStroke action for the up/down keys and adds switch + * line actions. + */ + private void initSwitchLineAction() { + aSwitchLinesDownAction = new SwitchAction("switchLinesDown", KeyStroke.getKeyStroke( + KeyEvent.VK_DOWN, Event.SHIFT_MASK), this); + aSwitchLinesUpAction = new SwitchAction("switchLinesUp", KeyStroke.getKeyStroke( + KeyEvent.VK_UP, Event.SHIFT_MASK), this); + + JTable table = m_curGC.getTable(); + table.getInputMap(CPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( + KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.SHIFT_MASK), "none"); + table.getInputMap(CPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put( + KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.SHIFT_MASK), "none"); + table.getInputMap(CPanel.WHEN_FOCUSED).put( + KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.SHIFT_MASK), "none"); + table.getInputMap(CPanel.WHEN_FOCUSED).put( + KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.SHIFT_MASK), "none"); + + getInputMap(CPanel.WHEN_IN_FOCUSED_WINDOW).put( + KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.SHIFT_MASK), + aSwitchLinesDownAction.getName()); + getActionMap().put(aSwitchLinesDownAction.getName(), aSwitchLinesDownAction); + getInputMap(CPanel.WHEN_IN_FOCUSED_WINDOW).put( + KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.SHIFT_MASK), + aSwitchLinesUpAction.getName()); + getActionMap().put(aSwitchLinesUpAction.getName(), aSwitchLinesUpAction); + } + + public boolean isNested() { + return isNested; + } + +} // APanel diff --git a/client/src/org/compiere/apps/AStart.java b/client/src/org/compiere/apps/AStart.java new file mode 100644 index 0000000000..332c5d9e92 --- /dev/null +++ b/client/src/org/compiere/apps/AStart.java @@ -0,0 +1,140 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.apps; + +import java.awt.*; +import javax.swing.*; + +/** + * Applet Start + * + * @author Jorg Janke + * @version $Id: AStart.java,v 1.2 2006/07/30 00:51:27 jjanke Exp $ + */ +public final class AStart extends JApplet +{ + boolean isStandalone = false; + + /** + * Get a parameter value + */ + public String getParameter(String key, String def) + { + return isStandalone ? System.getProperty(key, def) : + (getParameter(key) != null ? getParameter(key) : def); + } + + /** + * Construct the applet + */ + public AStart() + { + } + + /** + * Initialize the applet + */ + public void init() + { + try + { + jbInit(); + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + /** + * Component initialization + */ + private void jbInit() throws Exception + { + this.setSize(new Dimension(400,300)); + } + + /** + * Start the applet + */ + public void start() + { + } + + /** + * Stop the applet + */ + public void stop() + { + } + + /** + * Destroy the applet + */ + public void destroy() + { + } + + /** + * Get Applet information + */ + public String getAppletInfo() + { + return "Start Applet"; + } + + /** + * Get parameter info + */ + public String[][] getParameterInfo() + { + return null; + } + + /** + * Main method + */ + public static void main(String[] args) + { + AStart applet = new AStart(); + applet.isStandalone = true; + JFrame frame = new JFrame(); + //EXIT_ON_CLOSE == 3 + frame.setDefaultCloseOperation(3); + frame.setTitle("Start Applet"); + frame.getContentPane().add(applet, BorderLayout.CENTER); + applet.init(); + applet.start(); + frame.setSize(400,320); + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setLocation((d.width - frame.getSize().width) / 2, (d.height - frame.getSize().height) / 2); + frame.setVisible(true); + } + + //static initializer for setting look & feel + static + { + try + { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + //UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + } + catch(Exception e) + { + } + } +} // AStert diff --git a/client/src/org/compiere/apps/form/FormFrame.java b/client/src/org/compiere/apps/form/FormFrame.java new file mode 100644 index 0000000000..b0da153f43 --- /dev/null +++ b/client/src/org/compiere/apps/form/FormFrame.java @@ -0,0 +1,438 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.apps.form; + +import java.awt.Cursor; +import java.awt.Event; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Properties; +import java.util.logging.Level; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.KeyStroke; + +import org.compiere.apps.AEnv; +import org.compiere.apps.AGlassPane; +import org.compiere.apps.AMenu; +import org.compiere.apps.Help; +import org.compiere.apps.WindowMenu; +import org.compiere.model.MRole; +import org.compiere.model.MUser; +import org.compiere.swing.CFrame; +import org.compiere.util.CLogger; +import org.compiere.util.DB; +import org.compiere.util.Env; +import org.compiere.util.Trace; + +/** + * Form Framework + * + * @author Jorg Janke + * @version $Id: FormFrame.java,v 1.2 2006/07/30 00:51:28 jjanke Exp $ + * + * Colin Rooney 2007/03/20 RFE#1670185 & BUG#1684142 + * Extend security to Info Queries + */ +public class FormFrame extends CFrame + implements ActionListener +{ + /** + * Create Form. + * Need to call openForm + */ + public FormFrame () + { + super(); + addWindowListener(new java.awt.event.WindowAdapter() + { + public void windowOpened(java.awt.event.WindowEvent evt) + { + formWindowOpened(evt); + } + }); + + m_WindowNo = Env.createWindowNo (this); + setGlassPane(m_glassPane); + try + { + jbInit(); + createMenu(); + } + catch(Exception e) + { + log.log(Level.SEVERE, "", e); + } + } // FormFrame + + /** WindowNo */ + private int m_WindowNo; + /** The GlassPane */ + private AGlassPane m_glassPane = new AGlassPane(); + /** Description */ + private String m_Description = null; + /** Help */ + private String m_Help = null; + /** Menu Bar */ + private JMenuBar menuBar = new JMenuBar(); + /** The Panel to be displayed */ + private FormPanel m_panel = null; + /** Maximize Window */ + public boolean m_maximize = false; + /** Logger */ + private static CLogger log = CLogger.getCLogger(FormFrame.class); + + /** Form ID */ + private int p_AD_Form_ID = 0; + + /** + * Static Init + * @throws Exception + */ + private void jbInit() throws Exception + { + this.setIconImage(org.compiere.Adempiere.getImage16()); + this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + this.setJMenuBar(menuBar); + } // jbInit + + /** + * Create Menu + */ + private void createMenu() + { + // File + JMenu mFile = AEnv.getMenu("File"); + menuBar.add(mFile); + AEnv.addMenuItem("PrintScreen", null, KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, 0), mFile, this); + AEnv.addMenuItem("ScreenShot", null, KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, Event.SHIFT_MASK), mFile, this); + AEnv.addMenuItem("Report", null, KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.ALT_MASK), mFile, this); + mFile.addSeparator(); + AEnv.addMenuItem("End", null, KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.ALT_MASK), mFile, this); + AEnv.addMenuItem("Exit", null, KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.SHIFT_MASK+Event.ALT_MASK), mFile, this); + + // View + JMenu mView = AEnv.getMenu("View"); + menuBar.add(mView); + + if (MRole.getDefault().isAllow_Info_Product()) + { + AEnv.addMenuItem("InfoProduct", null, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK), mView, this); + } + if (MRole.getDefault().isAllow_Info_BPartner()) + { + AEnv.addMenuItem("InfoBPartner", null, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK+Event.CTRL_MASK), mView, this); + } + if (MRole.getDefault().isShowAcct() && MRole.getDefault().isAllow_Info_Account()) + { + AEnv.addMenuItem("InfoAccount", null, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK+Event.CTRL_MASK), mView, this); + } + if (MRole.getDefault().isAllow_Info_Schedule()) + { + AEnv.addMenuItem("InfoSchedule", null, null, mView, this); + } + mView.addSeparator(); + if (MRole.getDefault().isAllow_Info_Order()) + { + AEnv.addMenuItem("InfoOrder", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Invoice()) + { + AEnv.addMenuItem("InfoInvoice", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_InOut()) + { + AEnv.addMenuItem("InfoInOut", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Payment()) + { + AEnv.addMenuItem("InfoPayment", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_CashJournal()) + { + AEnv.addMenuItem("InfoCashLine", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Resource()) + { + AEnv.addMenuItem("InfoAssignment", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Asset()) + { + AEnv.addMenuItem("InfoAsset", "Info", null, mView, this); + } + // Tools + JMenu mTools = AEnv.getMenu("Tools"); + menuBar.add(mTools); + AEnv.addMenuItem("Calculator", null, null, mTools, this); + AEnv.addMenuItem("Calendar", null, null, mTools, this); + AEnv.addMenuItem("Editor", null, null, mTools, this); + MUser user = MUser.get(Env.getCtx()); + if (user.isAdministrator()) + AEnv.addMenuItem("Script", null, null, mTools, this); + if (MRole.getDefault().isShowPreference()) + { + mTools.addSeparator(); + AEnv.addMenuItem("Preference", null, null, mTools, this); + } + + // Window + AMenu aMenu = (AMenu)Env.getWindow(0); + JMenu mWindow = new WindowMenu(aMenu.getWindowManager(), this); + menuBar.add(mWindow); + + // Help + JMenu mHelp = AEnv.getMenu("Help"); + menuBar.add(mHelp); + AEnv.addMenuItem("Help", "Help", KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), mHelp, this); + AEnv.addMenuItem("Online", null, null, mHelp, this); + AEnv.addMenuItem("EMailSupport", null, null, mHelp, this); + AEnv.addMenuItem("About", null, null, mHelp, this); + } // createMenu + + /** + * Dispose + */ + public void dispose() + { + log.config(""); + // recursive calls + if (Trace.isCalledFrom("JFrame") && m_panel != null) // [x] close window pressed + m_panel.dispose(); + m_panel = null; + Env.clearWinContext(m_WindowNo); + super.dispose(); + } // dispose + + /** + * Open Form + * @param AD_Form_ID form + * @return true if form opened + */ + public boolean openForm (int AD_Form_ID) + { + Properties ctx = Env.getCtx(); + // + String name = null; + String className = null; + String sql = "SELECT Name, Description, ClassName, Help FROM AD_Form WHERE AD_Form_ID=?"; + boolean trl = !Env.isBaseLanguage(ctx, "AD_Form"); + if (trl) + sql = "SELECT t.Name, t.Description, f.ClassName, t.Help " + + "FROM AD_Form f INNER JOIN AD_Form_Trl t" + + " ON (f.AD_Form_ID=t.AD_Form_ID AND AD_Language=?)" + + "WHERE f.AD_Form_ID=?"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement (sql, null); + if (trl) + { + pstmt.setString(1, Env.getAD_Language(ctx)); + pstmt.setInt(2, AD_Form_ID); + } + else + pstmt.setInt(1, AD_Form_ID); + rs = pstmt.executeQuery(); + if (rs.next()) + { + name = rs.getString(1); + m_Description = rs.getString(2); + className = rs.getString(3); + m_Help = rs.getString(4); + } + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; pstmt = null; + } + if (className == null) + return false; + // + return openForm(AD_Form_ID, className, name); + } // openForm + + /** + * Open Form + * @param AD_Form_ID Form + * @param className class name + * @param name title + * @return true if started + */ + protected boolean openForm (int AD_Form_ID, String className, String name) + { + log.info("AD_Form_ID=" + AD_Form_ID + " - Class=" + className); + Properties ctx = Env.getCtx(); + Env.setContext(ctx, m_WindowNo, "WindowName", name); + setTitle(Env.getHeader(ctx, m_WindowNo)); + + try + { + // Create instance w/o parameters + m_panel = (FormPanel)Class.forName(className).newInstance(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "Class=" + className + ", AD_Form_ID=" + AD_Form_ID, e); + return false; + } + // + m_panel.init(m_WindowNo, this); + p_AD_Form_ID = AD_Form_ID; + return true; + } // openForm + + /** + * Get Form Panel + * @return form panel + */ + public FormPanel getFormPanel() + { + return m_panel; + } // getFormPanel + + /** + * Action Listener + * @param e event + */ + public void actionPerformed(ActionEvent e) + { + String cmd = e.getActionCommand(); + if (cmd.equals("End")) + dispose(); + else if (cmd.equals("Help")) + actionHelp(); + else if (!AEnv.actionPerformed(cmd, m_WindowNo, this)) + log.log(Level.SEVERE, "Not handeled=" + cmd); + } // actionPerformed + + /** + * Show Help + */ + private void actionHelp() + { + StringBuffer sb = new StringBuffer(); + if (m_Description != null && m_Description.length() > 0) + sb.append("

    ").append(m_Description).append("

    "); + if (m_Help != null && m_Help.length() > 0) + sb.append("

    ").append(m_Help); + Help hlp = new Help (Env.getFrame(this), this.getTitle(), sb.toString()); + hlp.setVisible(true); + } // actionHelp + + + /************************************************************************* + * Set Window Busy + * @param busy busy + */ + public void setBusy (boolean busy) + { + if (busy == m_glassPane.isVisible()) + return; + log.info("Busy=" + busy); + if (busy) + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + else + setCursor(Cursor.getDefaultCursor()); + m_glassPane.setMessage(null); + m_glassPane.setVisible(busy); + m_glassPane.requestFocus(); + } // setBusy + + /** + * Set Busy Message + * @param AD_Message message + */ + public void setBusyMessage (String AD_Message) + { + m_glassPane.setMessage(AD_Message); + } // setBusyMessage + + /** + * Set and start Busy Counter + * @param time in seconds + */ + public void setBusyTimer (int time) + { + m_glassPane.setBusyTimer (time); + } // setBusyTimer + + + /** + * Set Maximize Window + * @param max maximize + */ + public void setMaximize (boolean max) + { + m_maximize = max; + } // setMaximize + + + /** + * Form Window Opened. + * Maximize window if required + * @param evt event + */ + private void formWindowOpened(java.awt.event.WindowEvent evt) + { + if (m_maximize == true) + { + super.setVisible(true); + super.setExtendedState(JFrame.MAXIMIZED_BOTH); + } + } // formWindowOpened + + /** + * Start Batch + * @param process + * @return running thread + */ + public Thread startBatch (final Runnable process) + { + Thread worker = new Thread() + { + public void run() + { + setBusy(true); + process.run(); + setBusy(false); + } + }; + worker.start(); + return worker; + } // startBatch + + /** + * @return Returns the AD_Form_ID. + */ + public int getAD_Form_ID () + { + return p_AD_Form_ID; + } // getAD_Window_ID + +} // FormFrame diff --git a/client/src/org/compiere/apps/form/VAllocation.java b/client/src/org/compiere/apps/form/VAllocation.java new file mode 100644 index 0000000000..04b50af7d0 --- /dev/null +++ b/client/src/org/compiere/apps/form/VAllocation.java @@ -0,0 +1,1160 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.apps.form; + +import java.awt.*; +import java.awt.event.*; +import java.beans.*; +import java.math.*; +import java.sql.*; +import java.text.*; +import java.util.*; +import java.util.logging.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.table.*; + +import org.adempiere.plaf.AdempierePLAF; +import org.compiere.apps.*; +import org.compiere.grid.ed.*; +import org.compiere.minigrid.*; +import org.compiere.model.*; +import org.compiere.plaf.*; +import org.compiere.process.*; +import org.compiere.swing.*; +import org.compiere.util.*; + +/** + * Allocation Form + * + * @author Jorg Janke + * @version $Id: VAllocation.java,v 1.2 2006/07/30 00:51:28 jjanke Exp $ + * + * Contributor : Fabian Aguilar - OFBConsulting - Multiallocation + */ +public class VAllocation extends CPanel + implements FormPanel, ActionListener, TableModelListener, VetoableChangeListener +{ + + /** + * Initialize Panel + * @param WindowNo window + * @param frame frame + */ + public void init (int WindowNo, FormFrame frame) + { + m_WindowNo = WindowNo; + m_frame = frame; + Env.setContext(Env.getCtx(), m_WindowNo, "IsSOTrx", "Y"); // defaults to no + m_C_Currency_ID = Env.getContextAsInt(Env.getCtx(), "$C_Currency_ID"); // default + // + log.info("Currency=" + m_C_Currency_ID); + try + { + dynInit(); + jbInit(); + calculate(); + frame.getContentPane().add(mainPanel, BorderLayout.CENTER); + frame.getContentPane().add(statusBar, BorderLayout.SOUTH); + } + catch(Exception e) + { + log.log(Level.SEVERE, "", e); + } + } // init + + /** Window No */ + private int m_WindowNo = 0; + /** FormFrame */ + private FormFrame m_frame; + /** Logger */ + private static CLogger log = CLogger.getCLogger(VAllocation.class); + + private boolean m_calculating = false; + private int m_C_Currency_ID = 0; + private int m_C_BPartner_ID = 0; + private int m_noInvoices = 0; + private int m_noPayments = 0; + private BigDecimal totalInv = new BigDecimal(0.0); + private BigDecimal totalPay = new BigDecimal(0.0); + private BigDecimal totalDiff = new BigDecimal(0.0); + + // Index changed if multi-currency + private int i_payment = 7; + // + private int i_open = 6; + private int i_discount = 7; + private int i_writeOff = 8; + private int i_overUnder = 9; + private int i_applied = 10; +// private int i_multiplier = 10; + // + private CPanel mainPanel = new CPanel(); + private BorderLayout mainLayout = new BorderLayout(); + private CPanel parameterPanel = new CPanel(); + private CPanel allocationPanel = new CPanel(); + private GridBagLayout parameterLayout = new GridBagLayout(); + private JLabel bpartnerLabel = new JLabel(); + private VLookup bpartnerSearch = null; + private MiniTable invoiceTable = new MiniTable(); + private MiniTable paymentTable = new MiniTable(); + private JSplitPane infoPanel = new JSplitPane(); + private CPanel paymentPanel = new CPanel(); + private CPanel invoicePanel = new CPanel(); + private JLabel paymentLabel = new JLabel(); + private JLabel invoiceLabel = new JLabel(); + private BorderLayout paymentLayout = new BorderLayout(); + private BorderLayout invoiceLayout = new BorderLayout(); + private JLabel paymentInfo = new JLabel(); + private JLabel invoiceInfo = new JLabel(); + private JScrollPane paymentScrollPane = new JScrollPane(); + private JScrollPane invoiceScrollPane = new JScrollPane(); + private GridBagLayout allocationLayout = new GridBagLayout(); + private JLabel differenceLabel = new JLabel(); + private CTextField differenceField = new CTextField(); + private JButton allocateButton = new JButton(); + private JLabel currencyLabel = new JLabel(); + private VLookup currencyPick = null; + private JCheckBox multiCurrency = new JCheckBox(); + private JLabel allocCurrencyLabel = new JLabel(); + private StatusBar statusBar = new StatusBar(); + private JLabel dateLabel = new JLabel(); + private VDate dateField = new VDate(); + private JCheckBox autoWriteOff = new JCheckBox(); + + private ArrayList m_bpartnerCheck = new ArrayList(); + + /** + * Static Init + * @throws Exception + */ + private void jbInit() throws Exception + { + CompiereColor.setBackground(this); + // + mainPanel.setLayout(mainLayout); + dateLabel.setText(Msg.getMsg(Env.getCtx(), "Date")); + autoWriteOff.setSelected(false); + autoWriteOff.setText(Msg.getMsg(Env.getCtx(), "AutoWriteOff", true)); + autoWriteOff.setToolTipText(Msg.getMsg(Env.getCtx(), "AutoWriteOff", false)); + // + parameterPanel.setLayout(parameterLayout); + allocationPanel.setLayout(allocationLayout); + bpartnerLabel.setText(Msg.translate(Env.getCtx(), "C_BPartner_ID")); + paymentLabel.setRequestFocusEnabled(false); + paymentLabel.setText(" " + Msg.translate(Env.getCtx(), "C_Payment_ID")); + invoiceLabel.setRequestFocusEnabled(false); + invoiceLabel.setText(" " + Msg.translate(Env.getCtx(), "C_Invoice_ID")); + paymentPanel.setLayout(paymentLayout); + invoicePanel.setLayout(invoiceLayout); + invoiceInfo.setHorizontalAlignment(SwingConstants.RIGHT); + invoiceInfo.setHorizontalTextPosition(SwingConstants.RIGHT); + invoiceInfo.setText("."); + paymentInfo.setHorizontalAlignment(SwingConstants.RIGHT); + paymentInfo.setHorizontalTextPosition(SwingConstants.RIGHT); + paymentInfo.setText("."); + differenceLabel.setText(Msg.getMsg(Env.getCtx(), "Difference")); + differenceField.setBackground(AdempierePLAF.getFieldBackground_Inactive()); + differenceField.setEditable(false); + differenceField.setText("0"); + differenceField.setColumns(8); + differenceField.setHorizontalAlignment(SwingConstants.RIGHT); + allocateButton.setText(Msg.getMsg(Env.getCtx(), "Process")); + allocateButton.addActionListener(this); + currencyLabel.setText(Msg.translate(Env.getCtx(), "C_Currency_ID")); + multiCurrency.setText(Msg.getMsg(Env.getCtx(), "MultiCurrency")); + multiCurrency.addActionListener(this); + allocCurrencyLabel.setText("."); + invoiceScrollPane.setPreferredSize(new Dimension(200, 200)); + paymentScrollPane.setPreferredSize(new Dimension(200, 200)); + mainPanel.add(parameterPanel, BorderLayout.NORTH); + parameterPanel.add(bpartnerLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0)); + parameterPanel.add(bpartnerSearch, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 5, 5), 0, 0)); + parameterPanel.add(dateLabel, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0)); + parameterPanel.add(dateField, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 5, 5), 0, 0)); + parameterPanel.add(currencyLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0 + ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0)); + parameterPanel.add(currencyPick, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 5, 5), 0, 0)); + parameterPanel.add(multiCurrency, new GridBagConstraints(3, 1, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 5, 5), 0, 0)); + parameterPanel.add(autoWriteOff, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0)); + mainPanel.add(allocationPanel, BorderLayout.SOUTH); + allocationPanel.add(differenceLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(5, 5, 5, 0), 0, 0)); + allocationPanel.add(differenceField, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 5, 5), 0, 0)); + allocationPanel.add(allocateButton, new GridBagConstraints(5, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 0, 5, 5), 0, 0)); + allocationPanel.add(allocCurrencyLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0 + ,GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0)); + paymentPanel.add(paymentLabel, BorderLayout.NORTH); + paymentPanel.add(paymentInfo, BorderLayout.SOUTH); + paymentPanel.add(paymentScrollPane, BorderLayout.CENTER); + paymentScrollPane.getViewport().add(paymentTable, null); + invoicePanel.add(invoiceLabel, BorderLayout.NORTH); + invoicePanel.add(invoiceInfo, BorderLayout.SOUTH); + invoicePanel.add(invoiceScrollPane, BorderLayout.CENTER); + invoiceScrollPane.getViewport().add(invoiceTable, null); + // + mainPanel.add(infoPanel, BorderLayout.CENTER); + infoPanel.setOrientation(JSplitPane.VERTICAL_SPLIT); + infoPanel.setBorder(BorderFactory.createEtchedBorder()); + infoPanel.setTopComponent(paymentPanel); + infoPanel.setBottomComponent(invoicePanel); + infoPanel.add(paymentPanel, JSplitPane.TOP); + infoPanel.add(invoicePanel, JSplitPane.BOTTOM); + infoPanel.setContinuousLayout(true); + infoPanel.setPreferredSize(new Dimension(800,250)); + infoPanel.setDividerLocation(110); + } // jbInit + + /** + * Dispose + */ + public void dispose() + { + if (m_frame != null) + m_frame.dispose(); + m_frame = null; + } // dispose + + /** + * Dynamic Init (prepare dynamic fields) + * @throws Exception if Lookups cannot be initialized + */ + private void dynInit() throws Exception + { + // Currency + int AD_Column_ID = 3505; // C_Invoice.C_Currency_ID + MLookup lookupCur = MLookupFactory.get (Env.getCtx(), m_WindowNo, 0, AD_Column_ID, DisplayType.TableDir); + currencyPick = new VLookup("C_Currency_ID", true, false, true, lookupCur); + currencyPick.setValue(new Integer(m_C_Currency_ID)); + currencyPick.addVetoableChangeListener(this); + + // BPartner + AD_Column_ID = 3499; // C_Invoice.C_BPartner_ID + MLookup lookupBP = MLookupFactory.get (Env.getCtx(), m_WindowNo, 0, AD_Column_ID, DisplayType.Search); + bpartnerSearch = new VLookup("C_BPartner_ID", true, false, true, lookupBP); + bpartnerSearch.addVetoableChangeListener(this); + + // Translation + statusBar.setStatusLine(Msg.getMsg(Env.getCtx(), "AllocateStatus")); + statusBar.setStatusDB(""); + + // Date set to Login Date + dateField.setValue(Env.getContextAsDate(Env.getCtx(), "#Date")); + dateField.addVetoableChangeListener(this); + } // dynInit + + /** + * Load Business Partner Info + * - Payments + * - Invoices + */ + private void loadBPartner () + { + log.config("BPartner=" + m_C_BPartner_ID + ", Cur=" + m_C_Currency_ID); + // Need to have both values + if (m_C_BPartner_ID == 0 || m_C_Currency_ID == 0) + return; + + // Async BPartner Test + Integer key = new Integer(m_C_BPartner_ID); + if (!m_bpartnerCheck.contains(key)) + { + new Thread() + { + public void run() + { + MPayment.setIsAllocated (Env.getCtx(), m_C_BPartner_ID, null); + MInvoice.setIsPaid (Env.getCtx(), m_C_BPartner_ID, null); + } + }.start(); + m_bpartnerCheck.add(key); + } + + /******************************** + * Load unallocated Payments + * 1-TrxDate, 2-DocumentNo, (3-Currency, 4-PayAmt,) + * 5-ConvAmt, 6-ConvOpen, 7-Allocated + */ + Vector> data = new Vector>(); + StringBuffer sql = new StringBuffer("SELECT p.DateTrx,p.DocumentNo,p.C_Payment_ID," // 1..3 + + "c.ISO_Code,p.PayAmt," // 4..5 + + "currencyConvert(p.PayAmt,p.C_Currency_ID,?,p.DateTrx,p.C_ConversionType_ID,p.AD_Client_ID,p.AD_Org_ID),"// 6 #1 + + "currencyConvert(paymentAvailable(C_Payment_ID),p.C_Currency_ID,?,p.DateTrx,p.C_ConversionType_ID,p.AD_Client_ID,p.AD_Org_ID)," // 7 #2 + + "p.MultiplierAP " + + "FROM C_Payment_v p" // Corrected for AP/AR + + " INNER JOIN C_Currency c ON (p.C_Currency_ID=c.C_Currency_ID) " + + "WHERE p.IsAllocated='N' AND p.Processed='Y'" + + " AND p.C_Charge_ID IS NULL" // Prepayments OK + + " AND p.C_BPartner_ID=?"); // #3 + if (!multiCurrency.isSelected()) + sql.append(" AND p.C_Currency_ID=?"); // #4 + sql.append(" ORDER BY p.DateTrx,p.DocumentNo"); + log.fine("PaySQL=" + sql.toString()); + try + { + PreparedStatement pstmt = DB.prepareStatement(sql.toString(), null); + pstmt.setInt(1, m_C_Currency_ID); + pstmt.setInt(2, m_C_Currency_ID); + pstmt.setInt(3, m_C_BPartner_ID); + if (!multiCurrency.isSelected()) + pstmt.setInt(4, m_C_Currency_ID); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + Vector line = new Vector(); + line.add(new Boolean(false)); // 0-Selection + line.add(rs.getTimestamp(1)); // 1-TrxDate + KeyNamePair pp = new KeyNamePair(rs.getInt(3), rs.getString(2)); + line.add(pp); // 2-DocumentNo + if (multiCurrency.isSelected()) + { + line.add(rs.getString(4)); // 3-Currency + line.add(rs.getBigDecimal(5)); // 4-PayAmt + } + line.add(rs.getBigDecimal(6)); // 3/5-ConvAmt + BigDecimal available = rs.getBigDecimal(7); + if (available == null || available.signum() == 0) // nothing available + continue; + line.add(available); // 4/6-ConvOpen/Available + line.add(Env.ZERO); // 5/7-Payment +// line.add(rs.getBigDecimal(8)); // 6/8-Multiplier + // + data.add(line); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql.toString(), e); + } + // Remove previous listeners + paymentTable.getModel().removeTableModelListener(this); + // Header Info + Vector columnNames = new Vector(); + columnNames.add(Msg.getMsg(Env.getCtx(), "Select")); + columnNames.add(Msg.translate(Env.getCtx(), "Date")); + columnNames.add(Util.cleanAmp(Msg.translate(Env.getCtx(), "DocumentNo"))); + if (multiCurrency.isSelected()) + { + columnNames.add(Msg.getMsg(Env.getCtx(), "TrxCurrency")); + columnNames.add(Msg.translate(Env.getCtx(), "Amount")); + } + columnNames.add(Msg.getMsg(Env.getCtx(), "ConvertedAmount")); + columnNames.add(Msg.getMsg(Env.getCtx(), "OpenAmt")); + columnNames.add(Msg.getMsg(Env.getCtx(), "AppliedAmt")); +// columnNames.add(" "); // Multiplier + + // Set Model + DefaultTableModel modelP = new DefaultTableModel(data, columnNames); + modelP.addTableModelListener(this); + paymentTable.setModel(modelP); + // + int i = 0; + paymentTable.setColumnClass(i++, Boolean.class, false); // 0-Selection + paymentTable.setColumnClass(i++, Timestamp.class, true); // 1-TrxDate + paymentTable.setColumnClass(i++, String.class, true); // 2-Value + if (multiCurrency.isSelected()) + { + paymentTable.setColumnClass(i++, String.class, true); // 3-Currency + paymentTable.setColumnClass(i++, BigDecimal.class, true); // 4-PayAmt + } + paymentTable.setColumnClass(i++, BigDecimal.class, true); // 5-ConvAmt + paymentTable.setColumnClass(i++, BigDecimal.class, true); // 6-ConvOpen + paymentTable.setColumnClass(i++, BigDecimal.class, false); // 7-Allocated +// paymentTable.setColumnClass(i++, BigDecimal.class, true); // 8-Multiplier + + // + i_payment = multiCurrency.isSelected() ? 7 : 5; + + + // Table UI + paymentTable.autoSize(); + + + /******************************** + * Load unpaid Invoices + * 1-TrxDate, 2-Value, (3-Currency, 4-InvAmt,) + * 5-ConvAmt, 6-ConvOpen, 7-ConvDisc, 8-WriteOff, 9-Applied + * + SELECT i.DateInvoiced,i.DocumentNo,i.C_Invoice_ID,c.ISO_Code, + i.GrandTotal*i.MultiplierAP "GrandTotal", + currencyConvert(i.GrandTotal*i.MultiplierAP,i.C_Currency_ID,i.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID) "GrandTotal $", + invoiceOpen(C_Invoice_ID,C_InvoicePaySchedule_ID) "Open", + currencyConvert(invoiceOpen(C_Invoice_ID,C_InvoicePaySchedule_ID),i.C_Currency_ID,i.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)*i.MultiplierAP "Open $", + invoiceDiscount(i.C_Invoice_ID,SysDate,C_InvoicePaySchedule_ID) "Discount", + currencyConvert(invoiceDiscount(i.C_Invoice_ID,SysDate,C_InvoicePaySchedule_ID),i.C_Currency_ID,i.C_Currency_ID,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)*i.Multiplier*i.MultiplierAP "Discount $", + i.MultiplierAP, i.Multiplier + FROM C_Invoice_v i INNER JOIN C_Currency c ON (i.C_Currency_ID=c.C_Currency_ID) + WHERE -- i.IsPaid='N' AND i.Processed='Y' AND i.C_BPartner_ID=1000001 + */ + data = new Vector>(); + sql = new StringBuffer("SELECT i.DateInvoiced,i.DocumentNo,i.C_Invoice_ID," // 1..3 + + "c.ISO_Code,i.GrandTotal*i.MultiplierAP, " // 4..5 Orig Currency + + "currencyConvert(i.GrandTotal*i.MultiplierAP,i.C_Currency_ID,?,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID), " // 6 #1 Converted + + "currencyConvert(invoiceOpen(C_Invoice_ID,C_InvoicePaySchedule_ID),i.C_Currency_ID,?,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)*i.MultiplierAP, " // 7 #2 Converted Open + + "currencyConvert(invoiceDiscount" // 8 AllowedDiscount + + "(i.C_Invoice_ID,?,C_InvoicePaySchedule_ID),i.C_Currency_ID,?,i.DateInvoiced,i.C_ConversionType_ID,i.AD_Client_ID,i.AD_Org_ID)*i.Multiplier*i.MultiplierAP," // #3, #4 + + "i.MultiplierAP " + + "FROM C_Invoice_v i" // corrected for CM/Split + + " INNER JOIN C_Currency c ON (i.C_Currency_ID=c.C_Currency_ID) " + + "WHERE i.IsPaid='N' AND i.Processed='Y'" + + " AND i.C_BPartner_ID=?"); // #5 + if (!multiCurrency.isSelected()) + sql.append(" AND i.C_Currency_ID=?"); // #6 + sql.append(" ORDER BY i.DateInvoiced, i.DocumentNo"); + log.fine("InvSQL=" + sql.toString()); + try + { + PreparedStatement pstmt = DB.prepareStatement(sql.toString(), null); + pstmt.setInt(1, m_C_Currency_ID); + pstmt.setInt(2, m_C_Currency_ID); + pstmt.setTimestamp(3, (Timestamp)dateField.getValue()); + pstmt.setInt(4, m_C_Currency_ID); + pstmt.setInt(5, m_C_BPartner_ID); + if (!multiCurrency.isSelected()) + pstmt.setInt(6, m_C_Currency_ID); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + Vector line = new Vector(); + line.add(new Boolean(false)); // 0-Selection + line.add(rs.getTimestamp(1)); // 1-TrxDate + KeyNamePair pp = new KeyNamePair(rs.getInt(3), rs.getString(2)); + line.add(pp); // 2-Value + if (multiCurrency.isSelected()) + { + line.add(rs.getString(4)); // 3-Currency + line.add(rs.getBigDecimal(5)); // 4-Orig Amount + } + line.add(rs.getBigDecimal(6)); // 3/5-ConvAmt + BigDecimal open = rs.getBigDecimal(7); + if (open == null) // no conversion rate + open = Env.ZERO; + line.add(open); // 4/6-ConvOpen + BigDecimal discount = rs.getBigDecimal(8); + if (discount == null) // no concersion rate + discount = Env.ZERO; + line.add(discount); // 5/7-ConvAllowedDisc + line.add(Env.ZERO); // 6/8-WriteOff + line.add(Env.ZERO); // 7/9-OverUnder + line.add(Env.ZERO); // 8/10-Applied +// line.add(rs.getBigDecimal(9)); // 8/10-Multiplier + // Add when open <> 0 (i.e. not if no conversion rate) + if (Env.ZERO.compareTo(open) != 0) + data.add(line); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql.toString(), e); + } + + // Remove previous listeners + invoiceTable.getModel().removeTableModelListener(this); + // Header Info + columnNames = new Vector(); + columnNames.add(Msg.getMsg(Env.getCtx(), "Select")); + columnNames.add(Msg.translate(Env.getCtx(), "Date")); + columnNames.add(Util.cleanAmp(Msg.translate(Env.getCtx(), "DocumentNo"))); + if (multiCurrency.isSelected()) + { + columnNames.add(Msg.getMsg(Env.getCtx(), "TrxCurrency")); + columnNames.add(Msg.translate(Env.getCtx(), "Amount")); + } + columnNames.add(Msg.getMsg(Env.getCtx(), "ConvertedAmount")); + columnNames.add(Msg.getMsg(Env.getCtx(), "OpenAmt")); + columnNames.add(Msg.getMsg(Env.getCtx(), "Discount")); + columnNames.add(Msg.getMsg(Env.getCtx(), "WriteOff")); + columnNames.add(Msg.getMsg(Env.getCtx(), "OverUnderAmt")); + columnNames.add(Msg.getMsg(Env.getCtx(), "AppliedAmt")); +// columnNames.add(" "); // Multiplier + + // Set Model + DefaultTableModel modelI = new DefaultTableModel(data, columnNames); + modelI.addTableModelListener(this); + invoiceTable.setModel(modelI); + // + i = 0; + invoiceTable.setColumnClass(i++, Boolean.class, false); // 0-Selection + invoiceTable.setColumnClass(i++, Timestamp.class, true); // 1-TrxDate + invoiceTable.setColumnClass(i++, String.class, true); // 2-Value + if (multiCurrency.isSelected()) + { + invoiceTable.setColumnClass(i++, String.class, true); // 3-Currency + invoiceTable.setColumnClass(i++, BigDecimal.class, true); // 4-Amt + } + invoiceTable.setColumnClass(i++, BigDecimal.class, true); // 5-ConvAmt + invoiceTable.setColumnClass(i++, BigDecimal.class, true); // 6-ConvAmt Open + invoiceTable.setColumnClass(i++, BigDecimal.class, false); // 7-Conv Discount + invoiceTable.setColumnClass(i++, BigDecimal.class, false); // 8-Conv WriteOff + invoiceTable.setColumnClass(i++, BigDecimal.class, false); // 9-Conv OverUnder + invoiceTable.setColumnClass(i++, BigDecimal.class, false); // 10-Conv Applied +// invoiceTable.setColumnClass(i++, BigDecimal.class, true); // 10-Multiplier + // Table UI + invoiceTable.autoSize(); + + i_open = multiCurrency.isSelected() ? 6 : 4; + i_discount = multiCurrency.isSelected() ? 7 : 5; + i_writeOff = multiCurrency.isSelected() ? 8 : 6; + i_overUnder = multiCurrency.isSelected() ? 9 : 7; + i_applied = multiCurrency.isSelected() ? 10 : 8; +// i_multiplier = multiCurrency.isSelected() ? 10 : 8; + + // Calculate Totals + calculate(); + } // loadBPartner + + + + /************************************************************************** + * Action Listener. + * - MultiCurrency + * - Allocate + * @param e event + */ + public void actionPerformed(ActionEvent e) + { + log.config(""); + if (e.getSource().equals(multiCurrency)) + loadBPartner(); + // Allocate + else if (e.getSource().equals(allocateButton)) + { + allocateButton.setEnabled(false); + saveData(); + loadBPartner(); + allocateButton.setEnabled(true); + } + } // actionPerformed + + /** + * Table Model Listener. + * - Recalculate Totals + * @param e event + */ + public void tableChanged(TableModelEvent e) + { + boolean isUpdate = (e.getType() == TableModelEvent.UPDATE); + // Not a table update + if (!isUpdate) + { + calculate(); + return; + } + + + /** + * Setting defaults + */ + if (m_calculating) // Avoid recursive calls + return; + m_calculating = true; + int row = e.getFirstRow(); + int col = e.getColumn(); + boolean isInvoice = (e.getSource().equals(invoiceTable.getModel())); + log.config("Row=" + row + + ", Col=" + col + ", InvoiceTable=" + isInvoice); + + // Payments + if (!isInvoice) + { + TableModel payment = paymentTable.getModel(); + + BigDecimal open = (BigDecimal)payment.getValueAt(row, i_open); + BigDecimal applied = (BigDecimal)payment.getValueAt(row, i_payment); + + if (col == 0) + { + // selection of payment row + if (((Boolean)payment.getValueAt(row, 0)).booleanValue()) + { + applied = open; // Open Amount + if (totalDiff.abs().compareTo(applied.abs()) < 0 // where less is available to allocate than open + && totalDiff.signum() == -applied.signum() ) // and the available amount has the opposite sign + applied = totalDiff.negate(); // reduce the amount applied to what's available + + } + else // de-selected + applied = Env.ZERO; + } + + + if (col == i_payment) + { + if ( applied.signum() == -open.signum() ) + applied = applied.negate(); + if ( open.abs().compareTo( applied.abs() ) < 0 ) + applied = open; + } + + payment.setValueAt(applied, row, i_payment); + } + + // Invoice + else + { + TableModel invoice = invoiceTable.getModel(); + boolean selected = ((Boolean) invoice.getValueAt(row, 0)).booleanValue(); + BigDecimal open = (BigDecimal)invoice.getValueAt(row, i_open); + BigDecimal discount = (BigDecimal)invoice.getValueAt(row, i_discount); + BigDecimal applied = (BigDecimal)invoice.getValueAt(row, i_applied); + BigDecimal writeOff = (BigDecimal) invoice.getValueAt(row, i_writeOff); + BigDecimal overUnder = (BigDecimal) invoice.getValueAt(row, i_overUnder); + int openSign = open.signum(); + + if (col == 0) //selection + { + // selected - set applied amount + if ( selected ) + { + applied = open; // Open Amount + applied = applied.subtract(discount); + writeOff = Env.ZERO; // to be sure + overUnder = Env.ZERO; + + if (totalDiff.abs().compareTo(applied.abs()) < 0 // where less is available to allocate than open + && totalDiff.signum() == applied.signum() ) // and the available amount has the same sign + applied = totalDiff; // reduce the amount applied to what's available + + if ( autoWriteOff.isSelected() ) + writeOff = open.subtract(applied.add(discount)); + else + overUnder = open.subtract(applied.add(discount)); + } + else // de-selected + { + writeOff = Env.ZERO; + applied = Env.ZERO; + overUnder = Env.ZERO; + } + } + + // check entered values are sensible and possibly auto write-off + if ( selected && col != 0 ) + { + + // values should have same sign as open except over/under + if ( discount.signum() == -openSign ) + discount = discount.negate(); + if ( writeOff.signum() == -openSign) + writeOff = writeOff.negate(); + if ( applied.signum() == -openSign ) + applied = applied.negate(); + + // discount and write-off must be less than open amount + if ( discount.abs().compareTo(open.abs()) > 0) + discount = open; + if ( writeOff.abs().compareTo(open.abs()) > 0) + writeOff = open; + + // if overUnder has same sign as open it is an under payment -> less than open + if ( overUnder.signum() == openSign && overUnder.abs().compareTo(open.abs()) > 0) + overUnder = open; + + /* + * Three rules to maintain: + * 1) |overUnder + writeOff + discount| < open + * 2) |writeOff + discount| < open ( in case overUnder is 'negative') + * 3) discount + writeOff + overUnder + applied = 0 + * + * As only one column is edited at a time and the initial position was one of compliance + * with the rules, we only need to redistribute the increase/decrease in the edited column to + * the others. + */ + + // comply with rules 1 or 2 + BigDecimal amtOver; + if ( overUnder.signum() == -openSign ) + amtOver = (discount.add(writeOff)).subtract(open); + else + amtOver = (discount.add(writeOff.add(overUnder))).subtract(open); + + if ( amtOver.signum() == openSign ) + { + BigDecimal temp = Env.ZERO; + if ( col != i_overUnder && overUnder.signum() == openSign ) + { + temp = overUnder.subtract(amtOver); + if ( temp.signum() == -openSign ) + { + overUnder = Env.ZERO; + amtOver = temp.negate(); + } + else + { + overUnder = temp; + amtOver = Env.ZERO; + } + } + + if ( col != i_writeOff ) + { + temp = writeOff.subtract(amtOver); + if ( temp.signum() == -openSign ) + { + writeOff = Env.ZERO; + amtOver = temp.negate(); + } + else + { + writeOff = temp; + amtOver = Env.ZERO; + } + } + + if ( col != i_discount ) + { + temp = discount.subtract(amtOver); + if ( temp.signum() == -openSign ) + { + discount = Env.ZERO; + amtOver = temp.negate(); + } + else + { + discount = temp; + amtOver = Env.ZERO; + } + } + } + + + // make everything balance to open + BigDecimal remainder = open.subtract(discount.add(writeOff.add(overUnder.add(applied)))); + + // need to increase something + if ( remainder.signum() == openSign ) + { + BigDecimal temp = Env.ZERO; + BigDecimal amtUnder = amtOver.negate(); + + if ( autoWriteOff.isSelected() && col != i_writeOff ) + { + temp = writeOff.add(remainder); + + if ( temp.abs().compareTo(amtUnder.abs()) > 0 ) + { + writeOff = amtUnder; + remainder = temp.subtract(amtUnder); + } + else + { + writeOff = temp; + remainder = Env.ZERO; + } + } + + if ( col != i_overUnder ) + { + temp = overUnder.add(remainder); + if ( temp.abs().compareTo(amtUnder.abs()) > 0 ) + { + overUnder = amtUnder; + remainder = temp.subtract(amtUnder); + } + else + { + overUnder = temp; + remainder = Env.ZERO; + } + } + + if ( col != i_applied && remainder.signum() != 0 ) + { + applied = applied.add(remainder); + remainder = Env.ZERO; + } + } + + // need to decrease some amount/s + if ( remainder.signum() == -openSign ) + { + BigDecimal temp = Env.ZERO; + + + if ( autoWriteOff.isSelected() && col != i_writeOff ) + { + temp = writeOff.add(remainder); + + if ( temp.signum() == -openSign ) + { + writeOff = Env.ZERO; + remainder = temp; + } + else + { + writeOff = temp; + remainder = Env.ZERO; + } + } + + + + if ( col != i_overUnder && remainder.signum() != 0 ) + { + overUnder = overUnder.add(remainder); + remainder = Env.ZERO; + } + } + + } + + // Warning if write Off > 30% + if (autoWriteOff.isSelected() && writeOff.doubleValue()/open.doubleValue() > .30) + ADialog.warn(m_WindowNo, this, "AllocationWriteOffWarn"); + + + invoice.setValueAt(discount, row, i_discount); + invoice.setValueAt(applied, row, i_applied); + invoice.setValueAt(writeOff, row, i_writeOff); + invoice.setValueAt(overUnder, row, i_overUnder); + + invoiceTable.repaint(); // update r/o + + + + } + + m_calculating = false; + calculate(); + } // tableChanged + + /** + * Calculate Allocation info + */ + private void calculate () + { + log.config(""); + // + DecimalFormat format = DisplayType.getNumberFormat(DisplayType.Amount); + Timestamp allocDate = null; + + // Payment + TableModel payment = paymentTable.getModel(); + totalPay = new BigDecimal(0.0); + int rows = payment.getRowCount(); + m_noPayments = 0; + for (int i = 0; i < rows; i++) + { + if (((Boolean)payment.getValueAt(i, 0)).booleanValue()) + { + Timestamp ts = (Timestamp)payment.getValueAt(i, 1); + allocDate = TimeUtil.max(allocDate, ts); + BigDecimal bd = (BigDecimal)payment.getValueAt(i, i_payment); + totalPay = totalPay.add(bd); // Applied Pay + m_noPayments++; + log.fine("Payment_" + i + " = " + bd + " - Total=" + totalPay); + } + } + paymentInfo.setText(String.valueOf(m_noPayments) + " - " + + Msg.getMsg(Env.getCtx(), "Sum") + " " + format.format(totalPay) + " "); + + // Invoices + TableModel invoice = invoiceTable.getModel(); + totalInv = new BigDecimal(0.0); + rows = invoice.getRowCount(); + m_noInvoices = 0; + + for (int i = 0; i < rows; i++) + { + if (((Boolean)invoice.getValueAt(i, 0)).booleanValue()) + { + Timestamp ts = (Timestamp)invoice.getValueAt(i, 1); + allocDate = TimeUtil.max(allocDate, ts); + BigDecimal bd = (BigDecimal)invoice.getValueAt(i, i_applied); + totalInv = totalInv.add(bd); // Applied Inv + m_noInvoices++; + log.fine("Invoice_" + i + " = " + bd + " - Total=" + totalPay); + } + } + invoiceInfo.setText(String.valueOf(m_noInvoices) + " - " + + Msg.getMsg(Env.getCtx(), "Sum") + " " + format.format(totalInv) + " "); + + // Set AllocationDate + if (allocDate != null) + dateField.setValue(allocDate); + // Set Allocation Currency + allocCurrencyLabel.setText(currencyPick.getDisplay()); + // Difference + totalDiff = totalPay.subtract(totalInv); + differenceField.setText(format.format(totalDiff)); + + if (totalDiff.compareTo(new BigDecimal(0.0)) == 0) + allocateButton.setEnabled(true); + else + allocateButton.setEnabled(false); + + } // calculate + + /** + * Vetoable Change Listener. + * - Business Partner + * - Currency + * - Date + * @param e event + */ + public void vetoableChange (PropertyChangeEvent e) + { + String name = e.getPropertyName(); + Object value = e.getNewValue(); + log.config(name + "=" + value); + if (value == null) + return; + + // BPartner + if (name.equals("C_BPartner_ID")) + { + bpartnerSearch.setValue(value); + m_C_BPartner_ID = ((Integer)value).intValue(); + loadBPartner(); + } + // Currency + else if (name.equals("C_Currency_ID")) + { + m_C_Currency_ID = ((Integer)value).intValue(); + loadBPartner(); + } + // Date for Multi-Currency + else if (name.equals("Date") && multiCurrency.isSelected()) + loadBPartner(); + } // vetoableChange + + + /************************************************************************** + * Save Data + */ + private void saveData() + { + if (m_noInvoices + m_noPayments == 0) + return; + + // fixed fields + int AD_Client_ID = Env.getContextAsInt(Env.getCtx(), m_WindowNo, "AD_Client_ID"); + int AD_Org_ID = Env.getContextAsInt(Env.getCtx(), m_WindowNo, "AD_Org_ID"); + int C_BPartner_ID = m_C_BPartner_ID; + int C_Order_ID = 0; + int C_CashLine_ID = 0; + Timestamp DateTrx = (Timestamp)dateField.getValue(); + int C_Currency_ID = m_C_Currency_ID; // the allocation currency + // + if (AD_Org_ID == 0) + { + ADialog.error(m_WindowNo, this, "Org0NotAllowed", null); + return; + } + // + log.config("Client=" + AD_Client_ID + ", Org=" + AD_Org_ID + + ", BPartner=" + C_BPartner_ID + ", Date=" + DateTrx); + + Trx trx = Trx.get(Trx.createTrxName("AL"), true); + + // Payment - Loop and add them to paymentList/amountList + int pRows = paymentTable.getRowCount(); + TableModel payment = paymentTable.getModel(); + ArrayList paymentList = new ArrayList(pRows); + ArrayList amountList = new ArrayList(pRows); + BigDecimal paymentAppliedAmt = Env.ZERO; + for (int i = 0; i < pRows; i++) + { + // Payment line is selected + if (((Boolean)payment.getValueAt(i, 0)).booleanValue()) + { + KeyNamePair pp = (KeyNamePair)payment.getValueAt(i, 2); // Value + // Payment variables + int C_Payment_ID = pp.getKey(); + paymentList.add(new Integer(C_Payment_ID)); + // + BigDecimal PaymentAmt = (BigDecimal)payment.getValueAt(i, i_payment); // Applied Payment + amountList.add(PaymentAmt); + // + paymentAppliedAmt = paymentAppliedAmt.add(PaymentAmt); + // + log.fine("C_Payment_ID=" + C_Payment_ID + + " - PaymentAmt=" + PaymentAmt); // + " * " + Multiplier + " = " + PaymentAmtAbs); + } + } + log.config("Number of Payments=" + paymentList.size() + " - Total=" + paymentAppliedAmt); + + // Invoices - Loop and generate allocations + int iRows = invoiceTable.getRowCount(); + TableModel invoice = invoiceTable.getModel(); + + // Create Allocation + MAllocationHdr alloc = new MAllocationHdr (Env.getCtx(), true, // manual + DateTrx, C_Currency_ID, Env.getContext(Env.getCtx(), "#AD_User_Name"), trx.getTrxName()); + alloc.setAD_Org_ID(AD_Org_ID); + if (!alloc.save()) + { + log.log(Level.SEVERE, "Allocation not created"); + return; + } + + // For all invoices + int invoiceLines = 0; + BigDecimal unmatchedApplied = Env.ZERO; + for (int i = 0; i < iRows; i++) + { + // Invoice line is selected + if (((Boolean)invoice.getValueAt(i, 0)).booleanValue()) + { + invoiceLines++; + KeyNamePair pp = (KeyNamePair)invoice.getValueAt(i, 2); // Value + // Invoice variables + int C_Invoice_ID = pp.getKey(); + BigDecimal AppliedAmt = (BigDecimal)invoice.getValueAt(i, i_applied); + // semi-fixed fields (reset after first invoice) + BigDecimal DiscountAmt = (BigDecimal)invoice.getValueAt(i, i_discount); + BigDecimal WriteOffAmt = (BigDecimal)invoice.getValueAt(i, i_writeOff); + // OverUnderAmt needs to be in Allocation Currency + BigDecimal OverUnderAmt = ((BigDecimal)invoice.getValueAt(i, i_open)) + .subtract(AppliedAmt).subtract(DiscountAmt).subtract(WriteOffAmt); + + log.config("Invoice #" + i + " - AppliedAmt=" + AppliedAmt);// + " -> " + AppliedAbs); + // loop through all payments until invoice applied + + for (int j = 0; j < paymentList.size() && AppliedAmt.signum() != 0; j++) + { + int C_Payment_ID = ((Integer)paymentList.get(j)).intValue(); + BigDecimal PaymentAmt = (BigDecimal)amountList.get(j); + if (PaymentAmt.signum() == AppliedAmt.signum()) // only match same sign (otherwise appliedAmt increases) + { // and not zero (appliedAmt was checked earlier) + log.config(".. with payment #" + j + ", Amt=" + PaymentAmt); + + BigDecimal amount = AppliedAmt; + if (amount.abs().compareTo(PaymentAmt.abs()) > 0) // if there's more open on the invoice + amount = PaymentAmt; // than left in the payment + + // Allocation Line + MAllocationLine aLine = new MAllocationLine (alloc, amount, + DiscountAmt, WriteOffAmt, OverUnderAmt); + aLine.setDocInfo(C_BPartner_ID, C_Order_ID, C_Invoice_ID); + aLine.setPaymentInfo(C_Payment_ID, C_CashLine_ID); + if (!aLine.save()) + log.log(Level.SEVERE, "Allocation Line not written - Invoice=" + C_Invoice_ID); + + // Apply Discounts and WriteOff only first time + DiscountAmt = Env.ZERO; + WriteOffAmt = Env.ZERO; + // subtract amount from Payment/Invoice + AppliedAmt = AppliedAmt.subtract(amount); + PaymentAmt = PaymentAmt.subtract(amount); + log.fine("Allocation Amount=" + amount + " - Remaining Applied=" + AppliedAmt + ", Payment=" + PaymentAmt); + amountList.set(j, PaymentAmt); // update + } // for all applied amounts + } // loop through payments for invoice + + if ( AppliedAmt.signum() == 0 && DiscountAmt.signum() == 0 && WriteOffAmt.signum() == 0) + continue; + else { // remainder will need to match against other invoices + int C_Payment_ID = 0; + + // Allocation Line + MAllocationLine aLine = new MAllocationLine (alloc, AppliedAmt, + DiscountAmt, WriteOffAmt, OverUnderAmt); + aLine.setDocInfo(C_BPartner_ID, C_Order_ID, C_Invoice_ID); + aLine.setPaymentInfo(C_Payment_ID, C_CashLine_ID); + if (!aLine.save(trx.getTrxName())) + log.log(Level.SEVERE, "Allocation Line not written - Invoice=" + C_Invoice_ID); + + log.fine("Allocation Amount=" + AppliedAmt); + unmatchedApplied = unmatchedApplied.add(AppliedAmt); + } + } // invoice selected + } // invoice loop + + // check for unapplied payment amounts (eg from payment reversals) + for (int i = 0; i < paymentList.size(); i++) { + BigDecimal payAmt = (BigDecimal) amountList.get(i); + if ( payAmt.signum() == 0 ) + continue; + int C_Payment_ID = ((Integer)paymentList.get(i)).intValue(); + log.fine("Payment=" + C_Payment_ID + + ", Amount=" + payAmt); + + // Allocation Line + MAllocationLine aLine = new MAllocationLine (alloc, payAmt, + Env.ZERO, Env.ZERO, Env.ZERO); + aLine.setDocInfo(C_BPartner_ID, 0, 0); + aLine.setPaymentInfo(C_Payment_ID, 0); + if (!aLine.save(trx.getTrxName())) + log.log(Level.SEVERE, "Allocation Line not saved - Payment=" + C_Payment_ID); + unmatchedApplied = unmatchedApplied.subtract(payAmt); + } + + if ( unmatchedApplied.signum() != 0 ) + log.log(Level.SEVERE, "Allocation not balanced -- out by " + unmatchedApplied ); + + // Should start WF + if (alloc.get_ID() != 0) + { + alloc.processIt(DocAction.ACTION_Complete); + alloc.save(); + } + + // Test/Set IsPaid for Invoice - requires that allocation is posted + for (int i = 0; i < iRows; i++) + { + // Invoice line is selected + if (((Boolean)invoice.getValueAt(i, 0)).booleanValue()) + { + KeyNamePair pp = (KeyNamePair)invoice.getValueAt(i, 2); // Value + // Invoice variables + int C_Invoice_ID = pp.getKey(); + String sql = "SELECT invoiceOpen(C_Invoice_ID, 0) " + + "FROM C_Invoice WHERE C_Invoice_ID=?"; + BigDecimal open = DB.getSQLValueBD(trx.getTrxName(), sql, C_Invoice_ID); + if (open != null && open.signum() == 0) { + sql = "UPDATE C_Invoice SET IsPaid='Y' " + + "WHERE C_Invoice_ID=" + C_Invoice_ID; + int no = DB.executeUpdate(sql, trx.getTrxName()); + log.config("Invoice #" + i + " is paid - updated=" + no); + } else + log.config("Invoice #" + i + " is not paid - " + open); + } + } + // Test/Set Payment is fully allocated + for (int i = 0; i < paymentList.size(); i++) + { + int C_Payment_ID = ((Integer)paymentList.get(i)).intValue(); + MPayment pay = new MPayment (Env.getCtx(), C_Payment_ID, trx.getTrxName()); + if (pay.testAllocation()) + pay.save(); + log.config("Payment #" + i + (pay.isAllocated() ? " not" : " is") + + " fully allocated"); + } + paymentList.clear(); + amountList.clear(); + trx.commit(); + trx.close(); + + statusBar.setStatusLine(alloc.getDocumentNo()); + } // saveData + + +} // VAllocation diff --git a/client/src/org/compiere/grid/GridController.java b/client/src/org/compiere/grid/GridController.java new file mode 100644 index 0000000000..9a8b709683 --- /dev/null +++ b/client/src/org/compiere/grid/GridController.java @@ -0,0 +1,1397 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + * @contributor Victor Perez , e-Evolution.SC FR [ 1757088 ] + * @contributor fer_luck @ centuryon + *****************************************************************************/ +package org.compiere.grid; + +import java.awt.*; +import java.awt.event.*; +import java.beans.*; +import java.util.*; +import java.util.logging.*; +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.table.*; + +import org.adempiere.plaf.AdempiereLookAndFeel; +import org.adempiere.plaf.AdempierePLAF; +import org.compiere.apps.*; +import org.compiere.grid.ed.*; +import org.compiere.grid.tree.*; +import org.compiere.model.*; +import org.compiere.swing.*; +import org.compiere.util.*; + +/** + * The Grid Controller is the panel for single and multi-row presentation + * and links to the Model Tab. + * + *
    + *  UI Structure:
    + *  this    (BorderLayout)
    + *      splitPane (JSplitPane)
    + *          left
    + *              graphicPanel
    + *          right
    + *              cardPanel   JPanel  (CardLayout)
    + *                  srPane  JSplitPane
    + * 						vPane	JScrollPane
    + *              	        vPanel  VPanel (GridBagLayout)
    + * 						vIncludedGC	GridController
    + *                  mrPane  JScrollPane
    + *                      vTable  VTable
    + *
    + *  DataBinding:
    + *  - MultiRow - is automatic between VTable and MTable
    + *  - SingleRow
    + *		- from VEditors via fireVetoableChange(m_columnName, null, getText());
    + *			(vetoableChange)
    + *		- to VEditors via updateSingleRow -> Editor.setValue(object)
    + *
    + *  Event Chains
    + *  -- Navigation --
    + *  (VTable selection -> GridController.valueChanged)
    + *  (APanel selection)
    + *      + MTab.navivate
    + *          + MTab.setCurrentRow
    + *              + Update all MFields
    + *                  + MField.setValue
    + *                      + setContext
    + *                      + fire PropertyChange "Value"
    + *                          + VEditor.propertyChange
    + *                              + VEditor.setValue
    + *              + MTab.fireProperyChange "CurrentRow"
    + *                  + VTable.propertyChange (setRowSelectionInterval)
    + *                      + GridController.valueChange
    + *                          + GridController.dynamicDisplay(complete)
    + *              + MTab.fireDataStatusChanged
    + *                  + APanel.statusChanged
    + *
    + *  -- ValueChanges --
    + *  VEditor.fireVetoableChange
    + *      + (VCellEditor.vetoableChange/getCellEditorValue)   -- multi-row source
    + *      + (GridController.vetoableChange)                   -- single-row source
    + *          + MTable.setValueAt
    + *              + MField.setValue
    + *                  + setContext
    + *                  + fire PropertyChange "Value"
    + *                      + VEditor.setValue
    + *              + MTable.fireDataStatusChanged
    + *                  + MTab.dataStatusChanged
    + *                      + MTab.fireDataStatusChanged
    + *                          + APanel.statusChanged
    + *                  + GridController.dataStatusChanged
    + *                      + GridController.dynamicDisplay(selective)
    + *  
    + * @author Jorg Janke + * @version $Id: GridController.java,v 1.8 2006/09/25 00:59:52 jjanke Exp $ + * + * @author Teo Sarca - BF [ 1742159 ], BF [ 1707876 ] + * @contributor Victor Perez , e-Evolution.SC FR [ 1757088 ] + * @contributor fer_luck @ centuryon FR [ 1757088 ] + */ +public class GridController extends CPanel + implements DataStatusListener, ListSelectionListener, Evaluatee, + VetoableChangeListener, PropertyChangeListener, MouseListener +{ + /** + * Constructor - you need to call initGrid for instanciation + */ + public GridController() + { + try + { + jbInit(); + } + catch(Exception e) + { + log.log(Level.SEVERE, "", e); + } + } // GridController + + /** + * toString + * @return string representation + */ + public String toString() + { + return "GridController for " + m_mTab; + } // toString + + /** Logger */ + private static CLogger log = CLogger.getCLogger(GridController.class); + + /** + * The Layout + */ + private BorderLayout mainLayout = new BorderLayout(); + private JSplitPane splitPane = new JSplitPane(); + private CPanel graphPanel = new CPanel(); + private BorderLayout graphLayout = new BorderLayout(); + private CPanel cardPanel = new CPanel(); + private CardLayout cardLayout = new CardLayout(); + //private JSplitPane srPane = new JSplitPane(); + + private JScrollPane vPane = new JScrollPane(); + private CScrollPane mrPane = new CScrollPane(); + private CPanel xPanel = new CPanel(); + private BorderLayout xLayout = new BorderLayout(); + private VTable vTable = new VTable(); + //FR [ 1757088 ] + private VPanel vPanel = null; + private boolean detailGrid = false; + + /** + * Static Layout init + * @throws Exception + */ + private void jbInit() throws Exception + { + this.setLayout(mainLayout); + this.add(splitPane, BorderLayout.CENTER); + splitPane.setOpaque(false); + graphPanel.setLayout(graphLayout); + // + splitPane.add(graphPanel, JSplitPane.LEFT); + splitPane.add(cardPanel, JSplitPane.RIGHT); + splitPane.setBorder(null); + splitPane.setName("gc_splitPane"); + // + cardPanel.setLayout(cardLayout); + cardPanel.add(vPane, "vPane"); // Sequence Important! + cardPanel.add(mrPane, "mrPane"); + cardPanel.setBorder(null); + cardPanel.setName("gc_cardPanel"); + // single row (w/o xPane it would be centered) + /* + srPane.setBorder(null); + srPane.setName("gc_srPane"); + srPane.setOrientation(JSplitPane.VERTICAL_SPLIT); + srPane.add(vPane, JSplitPane.TOP); + srPane.setTopComponent(vPane); + srPane.setBottomComponent(null); // otherwise a button is created/displayed + */ + //FR [ 1757088 ] vPane.getViewport().add(xPanel, null); + //FR [ 1757088 ] xPanel.add(vPanel); + xPanel.setLayout(xLayout); + xPanel.setName("gc_xPanel"); + xPanel.setBorder(BorderFactory.createEmptyBorder()); + //xLayout.setAlignment(FlowLayout.LEFT); + xLayout.setHgap(0); + xLayout.setVgap(0); + // multi-row + mrPane.setBorder(null); + mrPane.getViewport().add(vTable, null); + mrPane.setName("gc_mrPane"); + // + graphPanel.setBorder(null); + graphPanel.setName("gc_graphPanel"); + //srPane.setDividerLocation(200); + + vPane.setBorder(BorderFactory.createEmptyBorder()); + } // jbInit + + /** + * Displose + */ + public void dispose() + { + log.config( "(" + m_mTab.toString() + ")"); + // clear info + stopEditor(false); + if (m_mTab.isLoadComplete()) + { + if (m_mTab.needSave(true, false)) + m_mTab.dataIgnore(); + } + //FR [ 1757088 ] vIncludedGC = null; + + // Listeners + if (m_mTab.isLoadComplete()) + { + m_mTab.getTableModel().removeDataStatusListener(this); + m_mTab.getTableModel().removeVetoableChangeListener(this); + } + vTable.getSelectionModel().removeListSelectionListener(this); + m_mTab.removePropertyChangeListener(vTable); + + // editors + Component[] comp = vPanel.getComponentsRecursive(); + for (int i = 0; i < comp.length; i++) + { + if (comp[i] instanceof VEditor) + { + VEditor vEditor = (VEditor)comp[i]; + vEditor.removeVetoableChangeListener(this); + String columnName = comp[i].getName(); + GridField mField = m_mTab.getField(columnName); + if (mField != null) + mField.removePropertyChangeListener(vEditor); + vEditor.dispose(); + } + } + /** @todo Remove APanel Button listeners */ + + vTable.removeAll(); + vTable.setModel(new DefaultTableModel()); // remove reference + vTable = null; + vPanel.removeAll(); + vPanel = null; + //srPane.removeAll(); + //srPane = null; + splitPane.removeAll(); + splitPane = null; + m_mTab = null; + m_tree = null; + this.removeAll(); + } // dispose + + /** Model Tab */ + private GridTab m_mTab = null; + /** Window */ + private int m_WindowNo; + /** Only Multi-Row exist */ + private boolean m_onlyMultiRow = false; + /** Single/Multi Row indicator */ + private boolean m_singleRow = true; + /** Veto Active */ + private boolean m_vetoActive = false; + /** Tree Panel (optional) */ + private VTreePanel m_tree; + + private APanel m_aPanel; + + private boolean init; + + private ArrayList synchronizerList = new ArrayList(); + + public boolean initGrid (GridTab mTab, boolean onlyMultiRow, + int WindowNo, APanel aPanel, GridWindow mWindow) + { + return initGrid(mTab, onlyMultiRow, WindowNo, aPanel, mWindow, false); + } + + /************************************************************************** + * Init Grid. + *
    +	 *  - Map table to model
    +	 *  - Update (multi-row) table info with renderers/editors
    +	 *  - build single-row panel
    +	 *  - initialize display
    +	 *  
    + * @param mTab tab + * @param onlyMultiRow only table + * @param WindowNo window no + * @param aPanel optional Application Panel for adding button listeners + * @param mWindow parent Window Model + * @return true if initialized + */ + public boolean initGrid (GridTab mTab, boolean onlyMultiRow, + int WindowNo, APanel aPanel, GridWindow mWindow, boolean lazy) + { + log.config( "(" + mTab.toString() + ")"); + m_mTab = mTab; + m_WindowNo = WindowNo; + m_onlyMultiRow = onlyMultiRow; + m_aPanel = aPanel; + setName("GC-" + mTab); + //FR [ 1757088 ] + vPanel = new VPanel(mTab.getName(), m_WindowNo); + vPanel.putClientProperty(AdempiereLookAndFeel.HIDE_IF_ONE_TAB, Boolean.TRUE); + if (this.isDetailGrid()) + { + vPanel.setBorder(BorderFactory.createLineBorder(AdempierePLAF.getPrimary2())); + } + vPane.getViewport().add(xPanel, null); + xPanel.add(vPanel, BorderLayout.CENTER); + + setTabLevel(m_mTab.getTabLevel()); + + if (!lazy) + init(); + else + { + //Load tab meta data, needed for includeTab to work + m_mTab.initTab(false); + } + + + // log.config( "GridController.dynInit (" + mTab.toString() + ") - fini"); + return true; + } // initGrid + + private void init() + { + // Set up Multi Row Table + vTable.setModel(m_mTab.getTableModel()); + + // Update Table Info ------------------------------------------------- + int size = setupVTable (m_aPanel, m_mTab, vTable); + + // Set Color on Tab Level + // this.setBackgroundColor (mTab.getColor()); + + // Single Row ------------------------------------------------------- + if (!m_onlyMultiRow) + { + // Set Softcoded Mnemonic &x + for (int i = 0; i < size; i++) + { + GridField mField = m_mTab.getField(i); + if (mField.isDisplayed()) + vPanel.setMnemonic(mField); + } // for all fields + + // Add Fields + for (int i = 0; i < size; i++) + { + GridField mField = m_mTab.getField(i); + if (mField.isDisplayed()) + { + VEditor vEditor = VEditorFactory.getEditor(m_mTab, mField, false); + if (vEditor == null) + { + log.warning("Editor not created for " + mField.getColumnName()); + continue; + } + // MField => VEditor - New Field value to be updated to editor + mField.addPropertyChangeListener(vEditor); + // VEditor => this - New Editor value to be updated here (MTable) + vEditor.addVetoableChangeListener(this); + // Add to VPanel + vPanel.addField(vEditor, mField); + // APanel Listen to buttons + if (mField.getDisplayType() == DisplayType.Button && m_aPanel != null) + ((JButton)vEditor).addActionListener (m_aPanel); + } + } // for all fields + + // No Included Grid Controller + /* + srPane.setResizeWeight(1); // top part gets all + srPane.setDividerSize (0); + srPane.setDividerLocation (9999); + */ + // Use SR to size MR + mrPane.setPreferredSize(vPanel.getPreferredSize()); + } // Single-Row + + // Tree Graphics Layout + int AD_Tree_ID = 0; + if (m_mTab.isTreeTab()) + AD_Tree_ID = MTree.getDefaultAD_Tree_ID ( + Env.getAD_Client_ID(Env.getCtx()), m_mTab.getKeyColumnName()); + if (m_mTab.isTreeTab() && AD_Tree_ID != 0) + { + m_tree = new VTreePanel(m_WindowNo, false, true); + if (m_mTab.getTabNo() == 0) // initialize other tabs later + m_tree.initTree(AD_Tree_ID); + m_tree.addPropertyChangeListener(VTreePanel.NODE_SELECTION, this); + graphPanel.add(m_tree, BorderLayout.CENTER); + splitPane.setDividerLocation(250); + // splitPane.resetToPreferredSizes(); + } + else // No Graphics - hide + { + graphPanel.setPreferredSize(new Dimension(0,0)); + splitPane.setDividerSize(0); + splitPane.setDividerLocation(0); + } + + // Receive DataStatusChanged info from MTab + m_mTab.addDataStatusListener(this); + // Receive vetoableChange info from MTable when saving + m_mTab.getTableModel().addVetoableChangeListener(this); + // Selection Listener -> valueChanged + vTable.getSelectionModel().addListSelectionListener(this); + // Navigation (RowChanged) + m_mTab.addPropertyChangeListener(vTable); + + // Update UI + vTable.autoSize(true); + + // Set initial presentation + if (m_onlyMultiRow || !m_mTab.isSingleRow()) + switchMultiRow(); + else + switchSingleRow(); + + init = true; + } + + /** + * + * @return boolean + */ + public boolean isInit() + { + return init; + } + + /** + * Include Tab + * @param gc grid controller to add + * @return GridSynchronizer + */ + + //FR [ 1757088 ] + public boolean includeTab (GridController gc , APanel aPanel, GridSynchronizer sync) + { + GridController detail = gc; + int screenWidth = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth() - 630; + // Set screen dimension + //detail.setPreferredSize(new Dimension(screenWidth, 250)); + detail.setDetailGrid(true); + detail.addMouseListener(detail); + detail.enableEvents(AWTEvent.HIERARCHY_EVENT_MASK + AWTEvent.MOUSE_EVENT_MASK); + + vPanel.includeTab(detail); + //BEGIN - [FR 1953734] + gc.setGCParent(this); + //END - [FR 1953734] + synchronizerList.add(sync); + return true; + + } // IncludeTab + + //FR [ 1757088 ] + public void setDetailGrid(boolean value){ + detailGrid = value; + if (detailGrid && vPanel != null) + vPanel.setBorder(BorderFactory.createLineBorder(AdempierePLAF.getPrimary2())); + } + + public boolean isDetailGrid(){ + return detailGrid; + } + /** + * Get Title + * @return title + */ + public String getTitle () + { + return m_mTab.getName(); + } // getTitle + + /** + * Setup Multi-Row Table (add fields) + * @param aPanel Panel + * @param mTab Model Tab + * @param table JTable + * @return size + */ + private int setupVTable (APanel aPanel, GridTab mTab, VTable table) + { + if (!mTab.isDisplayed()) + return 0; + int size = mTab.getFieldCount (); + TableColumnModel tcm = table.getColumnModel(); + if (size != tcm.getColumnCount()) + throw new IllegalStateException("TableColumn Size <> TableModel"); + + for (int i = 0; i < size; i++) + { + GridField mField = mTab.getField (i); + TableColumn tc = tcm.getColumn(i); + tc.setMinWidth(30); + // + if (mField.getColumnName().equals(tc.getIdentifier().toString())) + { + //don't show included tab field in grid + if (mField.getIncluded_Tab_ID() > 0) + { + TableCellNone tcn = new TableCellNone(mField.getColumnName()); + tc.setCellRenderer (tcn); + tc.setCellEditor (tcn); + tc.setHeaderValue (null); + tc.setMinWidth (0); + tc.setMaxWidth (0); + tc.setPreferredWidth (0); + } + else if (mField.getDisplayType () == DisplayType.RowID) + { + tc.setCellRenderer (new VRowIDRenderer (false)); + tc.setCellEditor (new VRowIDEditor (false)); + tc.setHeaderValue (""); + tc.setMaxWidth (2); + } + else + { + // need to set CellEditor explicitly as default editor based on class causes problem (YesNo-> Boolean) + if (mField.isDisplayed ()) + { + tc.setCellRenderer (new VCellRenderer (mField)); + VCellEditor ce = new VCellEditor (mField); + tc.setCellEditor (ce); + // + tc.setHeaderValue (mField.getHeader ()); + tc.setPreferredWidth (Math.max (mField.getDisplayLength (), 30)); + tc.setHeaderRenderer (new VHeaderRenderer (mField.getDisplayType ())); + + // Enable Button actions in grid + if (mField.getDisplayType () == DisplayType.Button) + { + ce.setActionListener(aPanel); + } + } + else // column not displayed + { + TableCellNone tcn = new TableCellNone(mField.getColumnName()); + tc.setCellRenderer (tcn); + tc.setCellEditor (tcn); + tc.setHeaderValue (null); + tc.setMinWidth (0); + tc.setMaxWidth (0); + tc.setPreferredWidth (0); + } + } + // System.out.println ("TableColumnID " + tc.getIdentifier () + // + " Renderer=" + tc.getCellRenderer () + // + mField.getHeader ()); + + } // found field + else + log.log(Level.SEVERE, "TableColumn " + tc.getIdentifier () + + " <> MField " + mField.getColumnName() + mField.getHeader()); + } // for all fields + return size; + } // setupVTable + + /** + * Activate Grid Controller. + * Called by APanel when GridController is displayed (foreground) + */ + public void activate () + { + if (!init) init(); + + // Tree to be initiated on second/.. tab + if (m_mTab.isTreeTab() && m_mTab.getTabNo() != 0) + { + String keyColumnName = m_mTab.getKeyColumnName(); + String treeName = "AD_Tree_ID"; + if (keyColumnName.startsWith("CM")) + { + if (keyColumnName.equals("CM_Container_ID")) + treeName = "AD_TreeCMC_ID"; + else if (keyColumnName.equals("CM_CStage_ID")) + treeName = "AD_TreeCMS_ID"; + else if (keyColumnName.equals("CM_Template_ID")) + treeName = "AD_TreeCMT_ID"; + else if (keyColumnName.equals("CM_Media_ID")) + treeName = "AD_TreeCMM_ID"; + } + int AD_Tree_ID = Env.getContextAsInt (Env.getCtx(), m_WindowNo, treeName); + log.config(keyColumnName + " -> " + treeName + " = " + AD_Tree_ID); + if (AD_Tree_ID == 0) + AD_Tree_ID = MTree.getDefaultAD_Tree_ID ( + Env.getAD_Client_ID(Env.getCtx()), m_mTab.getKeyColumnName()); + if (m_tree != null) + m_tree.initTree (AD_Tree_ID); + } + + activateChilds(); + } // activate + + /** + * activate child grid controller ( included tab ) + */ + private void activateChilds() + { + for (GridSynchronizer s : synchronizerList ) + { + s.activateChild(); + } + } + + /** + * Register ESC Actions + * - overwrite VTable's Keystroks assigment for ESC + * @param aIgnore ignore + */ + public void registerESCAction (AppsAction aIgnore) + { + int c = VTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT; + vTable.getInputMap(c).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), aIgnore.getName()); + vTable.getActionMap().put(aIgnore.getName(), aIgnore); + + // AEnv.printActionInputMap(vTable); + } // registerESCAction + + /** + * Query Tab and resize Table + * (called from APanel) + * @param onlyCurrentRows only current rows + * @param onlyCurrentDays how many days back + * @param maxRows maximim rows or 0 for all + */ + public void query (boolean onlyCurrentRows, int onlyCurrentDays, int maxRows) + { + // start loading while building screen + m_mTab.query(onlyCurrentRows, onlyCurrentDays, maxRows); + // Update UI + if (!isSingleRow()) + vTable.autoSize(true); + } // query + + /* + public boolean isNeedToSaveParent() + { + return m_mTab.isNeedToSaveParent(); + }*/ + + /************************************************************************** + * Switch from single to multi & vice versa + */ + public void switchRowPresentation() + { + stopEditor(true); + if (m_singleRow) + switchMultiRow(); + else + switchSingleRow(); + } // switchRowPresentation + + /** + * Switch to SingleRow Presentation + */ + public void switchSingleRow() + { + if (m_onlyMultiRow) + return; + cardLayout.first(cardPanel); + m_singleRow = true; + dynamicDisplay(0); + // vPanel.requestFocus(); + } // switchSingleRow + + /** + * Switch to MultiRow Presentation + */ + public void switchMultiRow() + { + cardLayout.last(cardPanel); + m_singleRow = false; + vTable.autoSize(true); // resizes + // vTable.requestFocus(); + } // switchSingleRow + + /** + * Is Single Row presentation + * @return true if Single Row is displayed + */ + public boolean isSingleRow() + { + return m_singleRow; + } // isSingleRow + + + /************************************************************************** + * Remove Listener - pass on to MTab + * @param l listener + */ + public synchronized void removeDataStatusListener(DataStatusListener l) + { + m_mTab.removeDataStatusListener(l); + } // removeDataStatusListener + + /** + * Add Data Status Listener - pass on to MTab + * @param l listener + */ + public synchronized void addDataStatusListener(DataStatusListener l) + { + m_mTab.addDataStatusListener(l); + } + + /** + * Data Status Listener - for MTab events. + *

    + * Callouts are processed here for GUI changes + * - same as in MTab.setValue for batch changes + *

    + * calls dynamicDisplay + * @param e event + */ + public void dataStatusChanged(DataStatusEvent e) + { + // if (e.getChangedColumn() == 0) + // return; + int col = e.getChangedColumn(); + log.config("(" + m_mTab + ") Col=" + col + ": " + e.toString()); + + // Process Callout + GridField mField = m_mTab.getField(col); + if (mField != null + && (mField.getCallout().length() > 0 || m_mTab.hasDependants(mField.getColumnName()))) + { + String msg = m_mTab.processFieldChange(mField); // Dependencies & Callout + if (msg.length() > 0) + ADialog.error(m_WindowNo, this, msg); + } + //if (col >= 0) + dynamicDisplay(col); + } // dataStatusChanged + + + /************************************************************************** + * List Selection Listener (VTable) - row changed + * @param e event + */ + public void valueChanged(ListSelectionEvent e) + { + // no rows + if (m_mTab.getRowCount() == 0) + return; + + // vTable.stopEditor(graphPanel); + int rowTable = vTable.getSelectedRow(); + int rowCurrent = m_mTab.getCurrentRow(); + log.config("(" + m_mTab.toString() + ") Row in Table=" + rowTable + ", in Model=" + rowCurrent); + // FR [ 1757088 ] + if(rowCurrent + 1 == vTable.getRowCount() && !isSingleRow() && Env.isAutoNew(Env.getCtx()) && m_mTab.getRecord_ID() != -1) + { + //stopEditor(true); + vTable.getSelectionModel().removeListSelectionListener(this); + m_mTab.dataNew(false); + dynamicDisplay(0); + vTable.getSelectionModel().addListSelectionListener(this); + return; + } + if (rowTable == -1) // nothing selected + { + if (rowCurrent >= 0) + { + vTable.setRowSelectionInterval(rowCurrent, rowCurrent); // causes this method to be called again + return; + } + } + else + { + if (rowTable != rowCurrent) { + //make sure table selection is consistent with model + int t = m_mTab.navigate(rowTable); + if (t != rowTable) { + rowTable = t; + vTable.setRowSelectionInterval(rowTable, rowTable); + } + } + dynamicDisplay(0); + } + + // TreeNavigation - Synchronize -- select node in tree + if (m_tree != null) + m_tree.setSelectedNode (m_mTab.getRecord_ID()); // ignores new (-1) + + // log.config( "GridController.valueChanged (" + m_mTab.toString() + ") - fini", + // "Row in Table=" + rowTable + ", in Model=" + rowCurrent); + + } // valueChanged + + /** + * PropertyChange Listener - Tree Panel - node selection + * @param e event + */ + public void propertyChange(PropertyChangeEvent e) + { + // System.out.println("propertyChange"); + // System.out.println(e); + if (e == null) + return; + Object value = e.getNewValue(); + if (value == null) + return; + log.config(e.getPropertyName() + "=" + value + + " - " + value.getClass().toString()); + if (!(value instanceof MTreeNode)) + return; + + // We Have a TreeNode + int nodeID = ((MTreeNode)value).getNode_ID(); + // root of tree selected - ignore + //if (nodeID == 0) + //return; + + // Search all rows for mode id + int size = m_mTab.getRowCount(); + int row = -1; + for (int i = 0; i < size; i++) + { + if (m_mTab.getKeyID(i) == nodeID) + { + row = i; + break; + } + } + if (row == -1) + { + if (nodeID > 0) + log.log(Level.WARNING, "Tab does not have ID with Node_ID=" + nodeID); + return; + } + + // Navigate to node row + m_mTab.navigate(row); + } // propertyChange + + /** + * Dynamic Display. + * - Single Row Screen layout and update of dynamic Lookups + *

    + * Single Row layout: + * the components's name is the ColumnName; if it matches, the + * MField.isDisplayed(true) is used to determine if it is visible + * if the component is a VEditor, setEnabled is set from the MField + *

    + * Multi Row layout is not changed: + * VCellRenderer calls JTable.isCellEditable -> checks MField.isEditable (Active, isDisplayed) + * VCellEditor.isCellEditable calls MField.isEditable(true)
    + * If a column is not displayed, the width is set to 0 in dynInit + *

    + * Dynamic update of data is handeled in VLookup.focusGained/Lost. + * When focus is gained the model is temporarily updated with the + * specific validated data, if lost, it is switched back to the + * unvalidated data (i.e. everything). This allows that the display + * methods have a lookup to display.
    + * Here: if the changed field has dependents and the dependent + * is a Lookup and this lookup has a dynamic dependence of the changed field, + * the value of that field is set to null (in MTab.processDependencies - + * otherwise it would show an invalid value). + * As Editors listen for value changed of their MField, the display is updated. + *

    + * Called from GridController.valueChanged/dataStatusChanged, APane;.stateChanged/unlock/cmd_... + * @param col selective column number or 0 if all + */ + public void dynamicDisplay (int col) + { + // log.config( "GridController.dynamicDisplay (" + m_mTab.toString() + ") SingleRow=" + isSingleRow() + ", OnlyMultiRow=" + m_onlyMultiRow); + // Don't update if multi-row + if (!isSingleRow() || m_onlyMultiRow) + return; + if (!m_mTab.isOpen()) + return; + // Selective + if (col > 0) + { + GridField changedField = m_mTab.getField(col); + String columnName = changedField.getColumnName(); + ArrayList dependants = m_mTab.getDependantFields(columnName); + log.config("(" + m_mTab.toString() + ") " + + columnName + " - Dependents=" + dependants.size()); + // No Dependents and no Callout - Set just Background + if (dependants.size() == 0 && changedField.getCallout().length() > 0) + { + Component[] comp = vPanel.getComponentsRecursive(); + for (int i = 0; i < comp.length; i++) + { + if (columnName.equals(comp[i].getName ()) && comp[i] instanceof VEditor) + { + VEditor ve = (VEditor)comp[i]; + boolean manMissing = false; + boolean noValue = changedField.getValue() == null || changedField.getValue().toString().length() == 0; + if (noValue && changedField.isEditable(true) && changedField.isMandatory(true)) // check context + manMissing = true; + ve.setBackground(manMissing || changedField.isError()); + break; + } + } + return; + } + } // selective + + + // complete single row re-display + boolean noData = m_mTab.getRowCount() == 0; + log.config(m_mTab.toString() + " - Rows=" + m_mTab.getRowCount()); + // All Components in vPanel (Single Row) + + Set hiddens = new HashSet(); + Component[] comps = vPanel.getComponentsRecursive(); + for (int i = 0; i < comps.length; i++) + { + Component comp = comps[i]; + String columnName = comp.getName(); + + if (columnName != null && columnName.length() > 0) + { + GridField mField = m_mTab.getField(columnName); + if (mField != null) + { + if (mField.isDisplayed(true)) // check context + { + if (!comp.isVisible()) + comp.setVisible(true); // visibility + /** + * Feature Request [1707462] + * Enable runtime change of VFormat + * @author fer_luck + */ + if (comp instanceof VString){ + VString vs = (VString)comp; + if ((vs.getVFormat() != null && vs.getVFormat().length() > 0 && mField.getVFormat() == null) + || (vs.getVFormat() == null && mField.getVFormat() != null && mField.getVFormat().length() > 0) + || (vs.getVFormat() != null && mField.getVFormat() != null && !vs.getVFormat().equals(mField.getVFormat()))) { + vs.setVFormat(mField.getVFormat()); + } + } + //End Feature Request [1707462] + if (comp instanceof VEditor) + { + VEditor ve = (VEditor)comp; + if (noData) + ve.setReadWrite(false); + else + { + boolean rw = mField.isEditable(true); // r/w - check Context + ve.setReadWrite(rw); + // log.log(Level.FINEST, "RW=" + rw + " " + mField); + boolean manMissing = false; + // least expensive operations first // missing mandatory + if (rw && mField.getValue() == null && mField.isMandatory(true)) // check context + manMissing = true; + ve.setBackground(manMissing || mField.isError()); + } + } + } + else + { + if (comp.isVisible()) + comp.setVisible(false); + hiddens.add(columnName); + } + } + } + } // all components + + // hide empty field group based on the environment + for (int i = 0; i < comps.length; i++) { + Component comp = comps[i]; + + if (comp instanceof CollapsiblePanel) + { + if (comp.getName() == null || comp.getName().startsWith("IncludedTab#")) + continue; + else + { + boolean hasVisible = false; + Component[] childs = ((CollapsiblePanel)comp).getCollapsiblePane().getContentPane().getComponents(); + for (int j = 0; j < childs.length; j++) { + if (childs[j].isVisible()) + { + String columnName = childs[j].getName(); + if (columnName != null && columnName.length() > 0) + { + GridField mField = m_mTab.getField(columnName); + if (mField != null) + { + hasVisible = true; + break; + } + } + } + } + if (comp.isVisible() != hasVisible) + comp.setVisible(hasVisible); + } + } + } + + // + + log.config(m_mTab.toString() + " - fini - " + + (col <= 0 ? "complete" : "seletive")); + } // dynamicDisplay + + /** + * Row Changed - synchronize with Tree + * + * @param save true the row was saved (changed/added), false if the row was deleted + * @param keyID the ID of the row changed + */ + public void rowChanged (boolean save, int keyID) + { + if (m_tree == null || keyID <= 0) + return; + String name = (String)m_mTab.getValue("Name"); + String description = (String)m_mTab.getValue("Description"); + Boolean IsSummary = (Boolean)m_mTab.getValue("IsSummary"); + boolean summary = IsSummary != null && IsSummary.booleanValue(); + String imageIndicator = (String)m_mTab.getValue("Action"); // Menu - Action + // + m_tree.nodeChanged(save, keyID, name, description, + summary, imageIndicator); + } // rowChanged + + + /************************************************************************** + * Save Multiple records - Clone a record and assign new values to each + * clone for a specific column. + * @param ctx context + * @param tableName Table Name + * @param columnName Column for which value need to be changed + * @param recordId Record to clone + * @param values Values to be assigned to clones for the specified column + * @param trxName Transaction + * @throws Exception If error is occured when loading the PO or saving clones + * + * @author ashley + */ + protected void saveMultipleRecords(Properties ctx, String tableName, + String columnName, int recordId, Integer[] values, + String trxName) throws Exception + { + if (values == null) + { + return ; + } + + int oldRow = m_mTab.getCurrentRow(); + GridField lineField = m_mTab.getField("Line"); + + for (int i = 0; i < values.length; i++) + { + if (!m_mTab.dataNew(true)) + { + throw new IllegalStateException("Could not clone tab"); + } + + m_mTab.setValue(columnName, values[i]); + + if (lineField != null) + { + m_mTab.setValue(lineField, 0); + } + + if (!m_mTab.dataSave(false)) + { + throw new IllegalStateException("Could not update tab"); + } + + m_mTab.setCurrentRow(oldRow); + } + } + + /************************************************************************** + * Vetoable Change Listener. + * Called from VEditor + *

    +	 *  - for Save Confirmation dialog
    +	 *  - for Single Row from VEditor: Update MTable
    +	 *  
    + * @param e event + * @throws PropertyVetoException + */ + public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException + { + if (m_mTab.isProcessed() || !m_mTab.isActive()) // only active records + { + Object source = e.getSource(); + if (source instanceof VEditor) + { + if (!((VEditor)source).isReadWrite()) + { + log.config("(" + m_mTab.toString() + ") " + e.getPropertyName()); + return; + } + } + else + { + log.config("(" + m_mTab.toString() + ") " + e.getPropertyName()); + return; + } + } // processed + log.config("(" + m_mTab.toString() + ") " + + e.getPropertyName() + "=" + e.getNewValue() + " (" + e.getOldValue() + ") " + + (e.getOldValue() == null ? "" : e.getOldValue().getClass().getName())); + + + // Save Confirmation dialog MTable-RowSave + if (e.getPropertyName().equals(GridTable.PROPERTY)) + { + // throw new PropertyVetoException calls this method (??) again + if (m_vetoActive) + { + m_vetoActive = false; + return; + } + if (!Env.isAutoCommit(Env.getCtx(), m_WindowNo) || m_mTab.getCommitWarning().length() > 0) + { + if (!ADialog.ask(m_WindowNo, this, "SaveChanges?", m_mTab.getCommitWarning())) + { + m_vetoActive = true; + throw new PropertyVetoException ("UserDeniedSave", e); + } + } + return; + } // saveConfirmation + + + // Get Row/Col Info + GridTable mTable = m_mTab.getTableModel(); + int row = m_mTab.getCurrentRow(); + int col = mTable.findColumn(e.getPropertyName()); + // + if (e.getNewValue() == null && e.getOldValue() != null + && e.getOldValue().toString().length() > 0) // some editors return "" instead of null + mTable.setChanged (true); + else + { + // mTable.setValueAt (e.getNewValue(), row, col, true); + /* + * Changes: Added the logic below to handle multiple values for a single field + * due to multiple selection in Lookup (Info). + * @author ashley + */ + Object newValue = e.getNewValue(); + Integer newValues[] = null; + + if (newValue instanceof Integer[]) + { + newValues = ((Integer[])newValue); + newValue = newValues[0]; + + if (newValues.length > 1) + { + Integer valuesCopy[] = new Integer[newValues.length - 1]; + System.arraycopy(newValues, 1, valuesCopy, 0, valuesCopy.length); + newValues = valuesCopy; + } + else + { + newValues = null; + } + } + else if (newValue instanceof Object[]) + { + log.severe("Multiple values can only be processed for IDs (Integer)"); + throw new PropertyVetoException("Multiple Selection values not available for this field", e); + } + + mTable.setValueAt (newValue, row, col); // -> dataStatusChanged -> dynamicDisplay + + // Force Callout + if (e.getPropertyName().equals("S_ResourceAssignment_ID")) + { + GridField mField = m_mTab.getField(col); + if (mField != null && mField.getCallout().length() > 0) + m_mTab.processFieldChange(mField); // Dependencies & Callout + } + + if (newValues != null && newValues.length > 0) + { + // Save data, since record need to be used for generating clones. + if (!m_mTab.dataSave(false)) + { + throw new PropertyVetoException("SaveError", e); + } + + // Retrieve the current record ID + int recordId = m_mTab.getKeyID(m_mTab.getCurrentRow()); + + Trx trx = Trx.get(Trx.createTrxName(), true); + trx.start(); + try + { + saveMultipleRecords(Env.getCtx(), mTable.getTableName(), e.getPropertyName(), recordId, newValues, trx.getTrxName()); + trx.commit(); + m_mTab.dataRefreshAll(); + } + catch(Exception ex) + { + trx.rollback(); + log.severe(ex.getMessage()); + throw new PropertyVetoException("SaveError", e); + } + finally + { + trx.close(); + } + } + } + + // log.config( "GridController.vetoableChange (" + m_mTab.toString() + ") - fini", e.getPropertyName() + "=" + e.getNewValue()); + } // vetoableChange + + + /************************************************************************** + * Get Model Tab + * @return Model Tab + */ + public GridTab getMTab() + { + return m_mTab; + } // getMTab + + /** + * Get Display Logic + * @return Display Logic + */ + public String getDisplayLogic() + { + return m_mTab.getDisplayLogic(); + } // getDisplayLogic + + /** + * Get VTable + * @return VTable + */ + public VTable getTable() + { + return vTable; + } // getTable + + + /** + * Set Window level Mnemonics + * @param set true if set otherwise unregiser + */ + public void setMnemonics (boolean set) + { + if (vPanel != null) + vPanel.setMnemonics(set); + } // setMnemonics + + /** + * Stop Table & SR Editors and move focus to graphPanel + * @param saveValue save value + */ + public void stopEditor (boolean saveValue) + { + log.config("(" + m_mTab.toString() + ") TableEditing=" + vTable.isEditing()); + + // MultiRow - remove editors + vTable.stopEditor(saveValue); + + // SingleRow - stop editors by changing focus + if (m_singleRow) + vPanel.transferFocus(); + // graphPanel.requestFocus(); + // + // log.config( "GridController.stopEditor (" + m_mTab.toString() + ") - fini", + // "Editing=" + vTable.isEditing()); + } // stopEditors + + /** + * Mouse Clicked + * @param e event + */ + public void mouseClicked(MouseEvent e) + { + if (CLogMgt.isLevelFinest()) + log.finest("" + this + " - " + e); + } + /** + * Mouse Pressed + * @param e event + */ + public void mousePressed(MouseEvent e) + { + if (CLogMgt.isLevelFinest()) + log.finest("" + this + " - " + e); + } + /** + * Mouse Released + * @param e event + */ + public void mouseReleased(MouseEvent e) + { + if (CLogMgt.isLevelFinest()) + log.finest("" + this + " - " + e); + } + /** + * Mouse Entered + * @param e event + */ + public void mouseEntered(MouseEvent e) + { + if (CLogMgt.isLevelFinest()) + log.finest("" + this + " - " + e); + } + /** + * Mouse Exited + * @param e event + */ + public void mouseExited(MouseEvent e) + { + if (CLogMgt.isLevelFinest()) + log.finest("" + this + " - " + e); + } + + /** + * Get Variable Value + * @param variableName name + * @return value + */ + public String get_ValueAsString (String variableName) + { + return Env.getContext(Env.getCtx(), m_WindowNo, variableName); + } // get_ValueAsString + + /** + * Is controller data not stale + * @return boolean + */ + public boolean isCurrent() + { + return m_mTab != null ? m_mTab.isCurrent() : false; + } + + //FR [ 1757088 ] + public VPanel getvPanel() + { + return vPanel; + } + + //BEGIN - [FR 1953734] + GridController m_Parent; + public void setGCParent(GridController gc){ + m_Parent = gc; + } + public GridController getGCParent(){ + return m_Parent; + } + public void refreshMTab(GridController includedTab){ + int m_CurrentRowBeforeSave = includedTab.m_mTab.getCurrentRow(); + m_mTab.dataRefresh(m_mTab.getCurrentRow()); + includedTab.m_mTab.setCurrentRow(m_CurrentRowBeforeSave); + } + //END - [FR 1953734] + + /** + * Accept pending editor changes. + */ + public void acceptEditorChanges() + { + if (isSingleRow()) + { + Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if (c != null && this.isAncestorOf(c)) + { + Component t = c; + while (t != null && t != this) + { + if (t instanceof VManagedEditor) + { + ((VManagedEditor)t).commitChanges(); + return; + } + t = t.getParent(); + } + } + } + } +} // GridController diff --git a/client/src/org/compiere/grid/ed/VLocationDialog.java b/client/src/org/compiere/grid/ed/VLocationDialog.java new file mode 100644 index 0000000000..b7c92223ee --- /dev/null +++ b/client/src/org/compiere/grid/ed/VLocationDialog.java @@ -0,0 +1,524 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.grid.ed; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.logging.*; +import javax.swing.*; +import org.compiere.apps.*; +import org.compiere.model.*; +import org.compiere.swing.*; +import org.compiere.util.*; + +import org.adempiere.interfaces.*; +import org.adempiere.model.*; + +/** + * Dialog to enter Location Info (Address) + * + * @author Jorg Janke + * @version $Id: VLocationDialog.java,v 1.2 2006/07/30 00:51:28 jjanke Exp $ + * + * @author Teo Sarca, SC ARHIPAC SERVICE SRL + *
  • BF [ 1831060 ] Location dialog should use Address1, Address2 ... elements + */ +public class VLocationDialog extends CDialog + implements ActionListener +{ + + /** Lookup result */ + //private Object[][] data = null; + + /** Lookup result header */ + private Object[] header = null; + + //private int m_WindowNo = 0; + + /** + * Constructor + * + * @param frame parent + * @param title title (field name) + * @param location Model Location + */ + public VLocationDialog (Frame frame, String title, MLocation location) + { + super(frame, title, true); + //m_WindowNo = WindowNo; + try + { + jbInit(); + } + catch(Exception ex) + { + log.log(Level.SEVERE, ex.getMessage()); + } + m_location = location; + if (m_location == null) + m_location = new MLocation (Env.getCtx(), 0, null); + // Overwrite title + if (m_location.getC_Location_ID() == 0) + setTitle(Msg.getMsg(Env.getCtx(), "LocationNew")); + else + setTitle(Msg.getMsg(Env.getCtx(), "LocationUpdate")); + + // Current Country + MCountry.setDisplayLanguage(Env.getAD_Language(Env.getCtx())); + fCountry = new CComboBox(MCountry.getCountries(Env.getCtx())); + fCountry.setSelectedItem(m_location.getCountry()); + m_origCountry_ID = m_location.getC_Country_ID(); + // Current Region + fRegion = new CComboBox(MRegion.getRegions(Env.getCtx(), m_origCountry_ID)); + if (m_location.getCountry().isHasRegion()) + lRegion.setText(m_location.getCountry().getRegionName()); // name for region + fRegion.setSelectedItem(m_location.getRegion()); + // + initLocation(); + fCountry.addActionListener(this); + fOnline.addActionListener(this); + AEnv.positionCenterWindow(frame, this); + + + } // VLocationDialog + + private boolean m_change = false; + private MLocation m_location; + private int m_origCountry_ID; + private int s_oldCountry_ID = 0; + /** Logger */ + private static CLogger log = CLogger.getCLogger(VLocationDialog.class); + + private CPanel panel = new CPanel(); + private CPanel mainPanel = new CPanel(); + private CPanel southPanel = new CPanel(); + private BorderLayout panelLayout = new BorderLayout(); + private GridBagLayout gridBagLayout = new GridBagLayout(); + private ConfirmPanel confirmPanel = new ConfirmPanel(true); + private BorderLayout southLayout = new BorderLayout(); + // + private CLabel lAddress1 = new CLabel(Msg.getElement(Env.getCtx(), "Address1")); + private CLabel lAddress2 = new CLabel(Msg.getElement(Env.getCtx(), "Address2")); + private CLabel lAddress3 = new CLabel(Msg.getElement(Env.getCtx(), "Address3")); + private CLabel lAddress4 = new CLabel(Msg.getElement(Env.getCtx(), "Address4")); + private CLabel lCity = new CLabel(Msg.getMsg(Env.getCtx(), "City")); + private CLabel lCountry = new CLabel(Msg.getMsg(Env.getCtx(), "Country")); + private CLabel lRegion = new CLabel(Msg.getMsg(Env.getCtx(), "Region")); + private CLabel lPostal = new CLabel(Msg.getMsg(Env.getCtx(), "Postal")); + private CLabel lPostalAdd = new CLabel(Msg.getMsg(Env.getCtx(), "PostalAdd")); + private CLabel lOnline = new CLabel(""); // dummy to use addLine without error.... + private CTextField fAddress1 = new CTextField(20); // length=60 + private CTextField fAddress2 = new CTextField(20); // length=60 + private CTextField fAddress3 = new CTextField(20); // length=60 + private CTextField fAddress4 = new CTextField(20); // length=60 + private CTextField fCity = new CTextField(15); // length=60 + private CComboBox fCountry; + private CComboBox fRegion; + private CTextField fPostal = new CTextField(5); // length=10 + private CTextField fPostalAdd = new CTextField(5); // length=10 + private CButton fOnline = new CButton(); + // + private GridBagConstraints gbc = new GridBagConstraints(); + private Insets labelInsets = new Insets(2,15,2,0); // top,left,bottom,right + private Insets fieldInsets = new Insets(2,5,2,10); + + /** + * Static component init + * @throws Exception + */ + void jbInit() throws Exception + { + panel.setLayout(panelLayout); + southPanel.setLayout(southLayout); + mainPanel.setLayout(gridBagLayout); + panelLayout.setHgap(5); + panelLayout.setVgap(10); + getContentPane().add(panel); + panel.add(mainPanel, BorderLayout.CENTER); + panel.add(southPanel, BorderLayout.SOUTH); + southPanel.add(confirmPanel, BorderLayout.NORTH); + // + confirmPanel.addActionListener(this); + } // jbInit + + /** + * Dynanmic Init & fill fields - Called when Country changes! + */ + private void initLocation() + { + MCountry country = m_location.getCountry(); + log.fine(country.getName() + ", Region=" + country.isHasRegion() + " " + country.getDisplaySequence() + + ", C_Location_ID=" + m_location.getC_Location_ID()); + // new Region + if (m_location.getC_Country_ID() != s_oldCountry_ID && country.isHasRegion()) + { + fRegion = new CComboBox(MRegion.getRegions(Env.getCtx(), country.getC_Country_ID())); + if (m_location.getRegion() != null) + fRegion.setSelectedItem(m_location.getRegion()); + lRegion.setText(country.getRegionName()); + s_oldCountry_ID = m_location.getC_Country_ID(); + } + + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.gridy = 0; // line + gbc.gridx = 0; + gbc.gridwidth = 1; + gbc.insets = fieldInsets; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 0; + gbc.weighty = 0; + + mainPanel.add(Box.createVerticalStrut(5), gbc); // top gap + + int line = 1; + addLine(line++, lAddress1, fAddress1); + addLine(line++, lAddress2, fAddress2); + addLine(line++, lAddress3, fAddress3); + addLine(line++, lAddress4, fAddress4); + + // sequence of City Postal Region - @P@ @C@ - @C@, @R@ @P@ + String ds = country.getDisplaySequence(); + if (ds == null || ds.length() == 0) + { + log.log(Level.SEVERE, "DisplaySequence empty - " + country); + ds = ""; // @C@, @P@ + } + StringTokenizer st = new StringTokenizer(ds, "@", false); + while (st.hasMoreTokens()) + { + String s = st.nextToken(); + if (s.startsWith("C")) + addLine(line++, lCity, fCity); + else if (s.startsWith("P")) + addLine(line++, lPostal, fPostal); + else if (s.startsWith("A")) + addLine(line++, lPostalAdd, fPostalAdd); + else if (s.startsWith("R") && m_location.getCountry().isHasRegion()) + addLine(line++, lRegion, fRegion); + } + + + addLine(line++, lOnline, fOnline); + + // Country Last + addLine(line++, lCountry, fCountry); + + // Fill it + if (m_location.getC_Location_ID() != 0) + { + fAddress1.setText(m_location.getAddress1()); + fAddress2.setText(m_location.getAddress2()); + fAddress3.setText(m_location.getAddress3()); + fAddress4.setText(m_location.getAddress4()); + fCity.setText(m_location.getCity()); + fPostal.setText(m_location.getPostal()); + fPostalAdd.setText(m_location.getPostal_Add()); + fOnline.setText(Msg.getMsg(Env.getCtx(), "Online")); + if (m_location.getCountry().isHasRegion()) + { + lRegion.setText(m_location.getCountry().getRegionName()); + fRegion.setSelectedItem(m_location.getRegion()); + } + + // disable online if this country doesn't have post code lookup + if (m_location.getCountry().isPostcodeLookup()) { + fOnline.setEnabled(true); + fOnline.setVisible(true); + } + else { + fOnline.setEnabled(false); + fOnline.setVisible(false); + } + + fCountry.setSelectedItem(country); + } + // Update UI + pack(); + } // initLocation + + /** + * Add Line to screen + * + * @param line line number (zero based) + * @param label label + * @param field field + */ + private void addLine(int line, JLabel label, JComponent field) + { + gbc.gridy = line; + // label + gbc.insets = labelInsets; + gbc.gridx = 0; + gbc.weightx = 0.0; + gbc.fill = GridBagConstraints.HORIZONTAL; + label.setHorizontalAlignment(SwingConstants.RIGHT); + mainPanel.add(label, gbc); + // Field + gbc.insets = fieldInsets; + gbc.gridx = 1; + gbc.weightx = 1.0; + gbc.fill = GridBagConstraints.NONE; + gbc.insets = fieldInsets; + mainPanel.add(field, gbc); + + } // addLine + + + /** + * ActionListener + * @param e ActionEvent + */ + public void actionPerformed(ActionEvent e) + { + if (e.getActionCommand().equals(ConfirmPanel.A_OK)) + { + action_OK(); + m_change = true; + dispose(); + } + else if (e.getActionCommand().equals(ConfirmPanel.A_CANCEL)) + { + m_change = false; + dispose(); + } + + // Country Changed - display in new Format + else if (e.getSource() == fCountry) + { + // Modifier for Mouse selection is 16 - for any key selection 0 + MCountry c = (MCountry)fCountry.getSelectedItem(); + m_location.setCountry(c); + + // refresh online button for new country + if (c.isPostcodeLookup()) { + fOnline.setEnabled(true); + fOnline.setVisible(true); + } + else { + fOnline.setEnabled(false); + fOnline.setVisible(false); + } + + // update the region name if regions are enabled for this country + if (c.isHasRegion()) + { + lRegion.setText(c.getRegionName()); + fRegion.setSelectedItem(m_location.getRegion()); + + // TODO: fix bug that occurs when the new region name is shorter than the old region name + } + + // refresh + mainPanel.removeAll(); + + initLocation(); + fCountry.requestFocus(); // allows to use Keybord selection + } + else if (e.getSource() == fOnline) + { + + // check to see if we have a postcode lookup plugin for this country + MCountry c = (MCountry)fCountry.getSelectedItem(); + if (c.isPostcodeLookup()) + { + lookupPostcode(c, fPostal.getText()); + } + } + } // actionPerformed + + /** + * OK - check for changes (save them) & Exit + */ + private void action_OK() + { + m_location.setAddress1(fAddress1.getText()); + m_location.setAddress2(fAddress2.getText()); + m_location.setAddress3(fAddress3.getText()); + m_location.setAddress4(fAddress4.getText()); + m_location.setCity(fCity.getText()); + m_location.setPostal(fPostal.getText()); + m_location.setPostal_Add(fPostalAdd.getText()); + // Country/Region + MCountry c = (MCountry)fCountry.getSelectedItem(); + m_location.setCountry(c); + if (m_location.getCountry().isHasRegion()) + { + MRegion r = (MRegion)fRegion.getSelectedItem(); + m_location.setRegion(r); + } + else + m_location.setC_Region_ID(0); + // Save changes + m_location.save(); + } // actionOK + + /** + * Get result + * @return true, if changed + */ + public boolean isChanged() + { + return m_change; + } // getChange + + /** + * Get edited Value (MLocation) + * @return location + */ + public MLocation getValue() + { + return m_location; + } // getValue + /** + * lookupPostcode + * + * + * @param country + * @param postcode + * @return + */ + private String lookupPostcode(MCountry country, String postcode) + { + // Initialise the lookup class. + PostcodeLookupInterface pcLookup = null; + try { + PostcodeLookupInterface pcLookupTmp = (PostcodeLookupInterface) Class + .forName(country.getLookupClassName()).newInstance(); + pcLookup = pcLookupTmp.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + return "lookupAddress(): " + e.getMessage(); + } + + // remove any spaces from the postcode and convert to upper case + postcode = postcode.replaceAll(" ", "").toUpperCase(); + log.fine("Looking up postcode: " + postcode); + + // Lookup postcode on server. + pcLookup.setServerUrl(country.getLookupUrl()); + pcLookup.setClientID(country.getLookupClientID()); + pcLookup.setPassword(country.getLookupPassword()); + if (pcLookup.lookupPostcode(postcode)==1){ + // Success + fillLocation(pcLookup.getPostCodeData(), country); + fAddress1.requestFocusInWindow(); + } else + return "Postcode Lookup Error"; + + return ""; + } + /** + * Fills the location field using the information retrieved from postcode + * servers. + * + * @param ctx + * Context + * @param pkeyData + * Lookup results + * @param windowNo + * Window No. + * @param tab + * Tab + * @param field + * Field + */ + private void fillLocation(HashMap postcodeData, MCountry country) { + + // If it's not empty warn the user. + if (fAddress1 != null || fAddress2 != null + || fAddress3 != null + || fAddress4 != null || fCity != null) { + String warningMsg = "Existing address information will be overwritten. Proceed?"; + String warningTitle = "Warning"; + int response = JOptionPane.showConfirmDialog(null, warningMsg, + warningTitle, JOptionPane.YES_NO_OPTION); + if (response == JOptionPane.NO_OPTION) + return; + } + + + Set pcodeKeys = postcodeData.keySet(); + Iterator iterator = pcodeKeys.iterator(); + header = null; + + // Allocate the header array + header = new Object[pcodeKeys.size()]; + + String headerStr = null; + + // need to check how many records returned + // TODO - check number of records returns - size() method is incorrect + if (pcodeKeys.size() > 2) + { + // TODO: Implement ResultData Grid and get return (for premises level data) + System.out.println("Too many postcodes returned from Postcode Lookup - need to Implement ResultData"); + } else + { + for (int i = 0; (headerStr = (iterator.hasNext() ? iterator.next() : null)) != null + || iterator.hasNext(); i++) { + header[i] = headerStr; + Postcode values = (Postcode) postcodeData.get(headerStr); + + // Overwrite the values in location field. + fAddress1.setText(values.getStreet1()); + fCity.setText(values.getCity()); + fPostal.setText(values.getPostcode()); + + // Do region lookup + if (country.isHasRegion()) + { + // get all regions for this country + MRegion[] regions = MRegion.getRegions(country.getCtx(), country.getC_Country_ID()); + + // If regions were loaded + if ( regions.length > 0) + { + // loop through regions array to attempt a region match - don't finish loop if region found + boolean found = false; + for (i = 0; i < regions.length && !found; i++) + { + + if (regions[i].getName().equals(values.getRegion()) ) + { + // found county + fRegion.setSelectedItem(regions[i]); + log.fine("Found region: " + regions[i].getName()); + found = true; + } + } + if (!found) + { + // add new region + MRegion region = new MRegion(country, values.getRegion()); + if (region.save()) + { + log.fine("Added new region from web service: " + values.getRegion()); + fRegion.setSelectedItem(values); + } else + log.severe("Error saving new region: " + region.getName()); + + } + } else + log.severe("Region lookup failed for Country: " + country.getName()); + + } + } + } + + } +} // VLocationDialog diff --git a/client/src/org/compiere/grid/ed/VLookup.java b/client/src/org/compiere/grid/ed/VLookup.java new file mode 100644 index 0000000000..db2ec54481 --- /dev/null +++ b/client/src/org/compiere/grid/ed/VLookup.java @@ -0,0 +1,1544 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.grid.ed; + +import java.awt.*; +import java.awt.event.*; +import java.beans.*; +import java.sql.*; +import java.util.logging.*; +import javax.swing.*; + +import org.compiere.apps.*; +import org.compiere.apps.search.*; +import org.compiere.model.*; +import org.compiere.swing.*; +import org.compiere.util.*; + +/** + * Lookup Visual Field. + *

    + * When r/o - display a Label + * When STABLE - display a ComboBox + * Otherwise show Selection Dialog + *

    + * Special handling of BPartner and Product + * + * @author Jorg Janke + * @version $Id: VLookup.java,v 1.5 2006/10/06 00:42:38 jjanke Exp $ + * + * @author Teo Sarca - BF [ 1740835 ] + * @author Michael Judd (MultiSelect) + */ +public class VLookup extends JComponent + implements VEditor, ActionListener, FocusListener +{ + @Override + protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, + int condition, boolean pressed) { + if (e.getSource() == m_combo || e.getSource() == m_text || e.getSource() == this) { + return super.processKeyBinding(ks, e, condition, pressed); + } + + JComponent editorComp = null; + if (m_lookup != null && m_lookup.getDisplayType() != DisplayType.Search) + editorComp = m_combo; + else + editorComp = m_text; + InputMap map = editorComp.getInputMap(condition); + ActionMap am = editorComp.getActionMap(); + + if(map!=null && am!=null && isEnabled()){ + Object binding = map.get(ks); + Action action = (binding==null) ? null : am.get(binding); + if(action!=null){ + return SwingUtilities.notifyAction(action, ks, e, editorComp, + e.getModifiers()); + } + } + return false; + } + + /** + * Create Optional BPartner Search Lookup + * @param WindowNo window + * @return VLookup + */ + public static VLookup createBPartner (int WindowNo) + { + int AD_Column_ID = 3499; // C_Invoice.C_BPartner_ID + try + { + Lookup lookup = MLookupFactory.get (Env.getCtx(), WindowNo, + 0, AD_Column_ID, DisplayType.Search); + return new VLookup ("C_BPartner_ID", false, false, true, lookup); + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } + return null; + } // createBPartner + + /** + * Create Optional Product Search Lookup + * @param WindowNo window + * @return VLookup + */ + public static VLookup createProduct (int WindowNo) + { + int AD_Column_ID = 3840; // C_InvoiceLine.M_Product_ID + try + { + Lookup lookup = MLookupFactory.get (Env.getCtx(), WindowNo, 0, + AD_Column_ID, DisplayType.Search); + return new VLookup ("M_Product_ID", false, false, true, lookup); + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } + return null; + } // createProduct + + /** + * Create Optional User Search Lookup + * @param WindowNo window + * @return VLookup + */ + public static VLookup createUser (int WindowNo) + { + int AD_Column_ID = 10443; // AD_WF_Activity.AD_User_UD + try + { + Lookup lookup = MLookupFactory.get (Env.getCtx(), WindowNo, 0, + AD_Column_ID, DisplayType.Search); + return new VLookup ("AD_User_ID", false, false, true, lookup); + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } + return null; + } // createProduct + + + /************************************************************************* + * Detail Constructor + * + * @param columnName column + * @param mandatory mandatory + * @param isReadOnly read only + * @param isUpdateable updateable + * @param lookup lookup + */ + public VLookup (String columnName, boolean mandatory, boolean isReadOnly, boolean isUpdateable, + Lookup lookup) + { + super(); + super.setName(columnName); + m_combo.setName(columnName); + m_columnName = columnName; + setMandatory(mandatory); + m_lookup = lookup; + if (m_lookup != null) + m_lookup.setMandatory(mandatory); + // + setLayout(new BorderLayout()); + mouseAdapter = new VLookup_mouseAdapter(this); // popup + + // *** Text & Button *** + m_text.addActionListener(this); + m_text.addFocusListener(this); + m_text.addMouseListener(mouseAdapter); + // Button + m_button.addActionListener(this); + m_button.addMouseListener(mouseAdapter); + m_button.setFocusable(false); // don't focus when tabbing + m_button.setMargin(new Insets(0, 0, 0, 0)); + if (columnName.equals("C_BPartner_ID")) + m_button.setIcon(Env.getImageIcon("BPartner10.gif")); + else if (columnName.equals("M_Product_ID")) + m_button.setIcon(Env.getImageIcon("Product10.gif")); + else + m_button.setIcon(Env.getImageIcon("PickOpen10.gif")); + + // *** VComboBox *** + if (m_lookup != null && m_lookup.getDisplayType() != DisplayType.Search) // No Search + { + // Don't have to fill up combobox if it is readonly + if (!isReadOnly && isUpdateable) + m_lookup.fillComboBox (isMandatory(), true, true, false); + m_combo.setModel(m_lookup); + // + AutoCompletion.enable(m_combo); + m_combo.addActionListener(this); // Selection + m_combo.getEditor().getEditorComponent().addMouseListener(mouseAdapter); // popup + // FocusListener to refresh selection before opening + m_combo.addFocusListener(this); + m_combo.getEditor().getEditorComponent().addFocusListener(this); + } + + setUI (true); + // ReadWrite - decides what components to show + if (isReadOnly || !isUpdateable || m_lookup == null) + setReadWrite(false); + else + setReadWrite(true); + + // Popup + if (m_lookup != null) + { + if ((m_lookup.getDisplayType() == DisplayType.List && Env.getContextAsInt(Env.getCtx(), "#AD_Role_ID") == 0) + || m_lookup.getDisplayType() != DisplayType.List) // only system admins can change lists, so no need to zoom for others + { + mZoom = new CMenuItem(Msg.getMsg(Env.getCtx(), "Zoom"), Env.getImageIcon("Zoom16.gif")); + mZoom.addActionListener(this); + popupMenu.add(mZoom); + } + mRefresh = new CMenuItem(Msg.getMsg(Env.getCtx(), "Refresh"), Env.getImageIcon("Refresh16.gif")); + mRefresh.addActionListener(this); + popupMenu.add(mRefresh); + } + // VBPartner quick entry link + if (columnName.equals("C_BPartner_ID")) + { + mBPartnerNew = new CMenuItem (Msg.getMsg(Env.getCtx(), "New"), Env.getImageIcon("InfoBPartner16.gif")); + mBPartnerNew.addActionListener(this); + mBPartnerNew.setVisible(isReadWrite()); // visible only if the field is editable - teo_sarca [ 1721710 ] + popupMenu.add(mBPartnerNew); + mBPartnerUpd = new CMenuItem (Msg.getMsg(Env.getCtx(), "Update"), Env.getImageIcon("InfoBPartner16.gif")); + mBPartnerUpd.addActionListener(this); + popupMenu.add(mBPartnerUpd); + } + // + if (m_lookup != null && m_lookup.getZoom() == 0) + mZoom.setEnabled(false); + } // VLookup + + /** + * Dispose + */ + public void dispose() + { + m_text = null; + m_button = null; + m_lookup = null; + m_mField = null; + // + m_combo.getEditor().getEditorComponent().removeFocusListener(this); + m_combo.getEditor().getEditorComponent().removeMouseListener(mouseAdapter); + m_combo.removeFocusListener(this); + m_combo.removeActionListener(this); + m_combo.setModel(new DefaultComboBoxModel()); // remove reference + // m_combo.removeAllItems(); + m_combo = null; + } // dispose + + /** Display Length for Lookups (15) */ + public final static int DISPLAY_LENGTH = 15; + /** Field Height */ + public static int FIELD_HIGHT = 0; + + /** Search: The Editable Text Field */ + private CTextField m_text = new CTextField (DISPLAY_LENGTH); + /** Search: The Button to open Editor */ + private CButton m_button = new CButton(); + /** The Combo Box if not a Search Lookup */ + private VComboBox m_combo = new VComboBox(); + /** Indicator that value is being set */ + private volatile boolean m_settingValue = false; + /** Indicator that docus is being set */ + private volatile boolean m_settingFocus = false; + /** Indicator that Lookup has focus */ + private volatile boolean m_haveFocus = false; + /** Indicator - inserting new value */ + private volatile boolean m_inserting = false; + /** Last Display */ + private String m_lastDisplay = ""; + /** Column Name */ + private String m_columnName; + /** Lookup */ + private Lookup m_lookup; + /** Conbo Box Active */ + private boolean m_comboActive = true; + /** The Value */ + private Object m_value; + + private boolean m_stopediting = false; + + // Popup + JPopupMenu popupMenu = new JPopupMenu(); + private CMenuItem mZoom; + private CMenuItem mRefresh; + private CMenuItem mBPartnerNew; + private CMenuItem mBPartnerUpd; + // Mouse Listener + private VLookup_mouseAdapter mouseAdapter; + + + // Field for Value Preference + private GridField m_mField = null; + /** Logger */ + private static CLogger log = CLogger.getCLogger(VLookup.class); + + /** + * Set Content and Size of Components + * @param initial if true, size and margins will be set + */ + private void setUI (boolean initial) + { + if (initial) + { + Dimension size = m_text.getPreferredSize(); + setPreferredSize(new Dimension(size)); // causes r/o to be the same length + m_combo.setPreferredSize(new Dimension(size)); + setMinimumSize(new Dimension (30, size.height)); + FIELD_HIGHT = size.height; + // + m_text.setBorder(null); + Dimension bSize = new Dimension(size.height, size.height); + m_button.setPreferredSize (bSize); + } + + // What to show + this.remove(m_combo); + this.remove(m_button); + this.remove(m_text); + // + if (!isReadWrite()) // r/o - show text only + { + LookAndFeel.installBorder(this, "TextField.border"); + this.add(m_text, BorderLayout.CENTER); + m_text.setReadWrite(false); + m_combo.setReadWrite(false); + m_comboActive = false; + } + else if (m_lookup != null && m_lookup.getDisplayType() != DisplayType.Search) // show combo if not Search + { + this.setBorder(null); + this.add(m_combo, BorderLayout.CENTER); + m_comboActive = true; + } + else // Search or unstable - show text & button + { + LookAndFeel.installBorder(this, "TextField.border"); + this.add(m_text, BorderLayout.CENTER); + this.add(m_button, BorderLayout.EAST); + m_text.setReadWrite (true); + m_comboActive = false; + } + } // setUI + + /** + * Set ReadWrite + * @param value ReadWrite + */ + public void setReadWrite (boolean value) + { + boolean rw = value; + if (m_lookup == null) + rw = false; + if (m_combo.isReadWrite() != value) + { + m_combo.setReadWrite(rw); + setUI (false); + if (value && m_comboActive) { + m_settingValue = true; // disable actions + refresh(); + m_settingValue = false; + } + if (m_comboActive) + setValue (m_value); + } + // If the field is readonly the BPartner new option should be hidden - teo_sarca [ 1721710 ] + if (mBPartnerNew != null) + mBPartnerNew.setVisible(value); + } // setReadWrite + + /** + * IsEditable + * @return is lookup ReadWrite + */ + public boolean isReadWrite() + { + return m_combo.isReadWrite(); + } // isReadWrite + + /** + * Set Mandatory (and back color) + * @param mandatory mandatory + */ + public void setMandatory (boolean mandatory) + { + m_combo.setMandatory(mandatory); + m_text.setMandatory(mandatory); + } // setMandatory + + /** + * Is it mandatory + * @return true if mandatory + */ + public boolean isMandatory() + { + return m_combo.isMandatory(); + } // isMandatory + + /** + * Set Background + * @param color color + */ + public void setBackground(Color color) + { + m_text.setBackground(color); + m_combo.setBackground(color); + } // setBackground + + /** + * Set Background + * @param error error + */ + public void setBackground (boolean error) + { + m_text.setBackground(error); + m_combo.setBackground(error); + } // setBackground + + /** + * Set Foreground + * @param fg Foreground color + */ + public void setForeground(Color fg) + { + m_text.setForeground(fg); + m_combo.setForeground(fg); + } // setForeground + + /** + * Request Focus + */ + public void requestFocus () + { + if (m_lookup != null && m_lookup.getDisplayType() != DisplayType.Search) + m_combo.requestFocus (); + else + m_text.requestFocus (); + } // requestFocus + + + /** + * Set Editor to value + * @param value new Value + */ + public void setValue (Object value) + { + log.fine(m_columnName + "=" + value); + m_settingValue = true; // disable actions + m_value = value; + + // Set both for switching + if (value == null) + { + m_combo.setValue (value); + m_text.setText (null); + m_lastDisplay = ""; + m_settingValue = false; + return; + } + if (m_lookup == null) + { + m_combo.setValue (value); + m_text.setText (value.toString()); + m_lastDisplay = value.toString(); + m_settingValue = false; + return; + } + + //must call m_combo.setvalue after m_lookup as + //loading of combo data might happen in m_lookup.getDisplay + m_lastDisplay = m_lookup.getDisplay(value); + m_combo.setValue (value); + + if (m_lastDisplay.equals("<-1>")) + { + m_lastDisplay = ""; + m_value = null; + } + boolean notFound = m_lastDisplay.startsWith("<") && m_lastDisplay.endsWith(">"); + m_text.setText (m_lastDisplay); + m_text.setCaretPosition (0); // show beginning + + // Nothing showing in Combo and should be showing + if (m_combo.getSelectedItem() == null + && (m_comboActive || (m_inserting && m_lookup.getDisplayType() != DisplayType.Search))) + { + // lookup found nothing too + if (notFound) + { + log.finest(m_columnName + "=" + value + ": Not found - " + m_lastDisplay); + // we may have a new value + m_lookup.refresh(); + m_combo.setValue (value); + m_lastDisplay = m_lookup.getDisplay(value); + m_text.setText (m_lastDisplay); + m_text.setCaretPosition (0); // show beginning + notFound = m_lastDisplay.startsWith("<") && m_lastDisplay.endsWith(">"); + } + if (notFound) // + { + m_value = null; + actionCombo (null); // data binding + log.fine(m_columnName + "=" + value + ": Not found"); + } + // we have lookup + else if (m_combo.getSelectedItem() == null) + { + NamePair pp = m_lookup.get(value); + if (pp != null) + { + log.fine(m_columnName + " added to combo - " + pp); + // Add to Combo + m_combo.addItem (pp); + m_combo.setValue (value); + } + } + // Not in Lookup - set to Null + if (m_combo.getSelectedItem() == null) + { + log.info(m_columnName + "=" + value + ": not in Lookup - set to NULL"); + actionCombo (null); // data binding (calls setValue again) + m_value = null; + } + } + m_settingValue = false; + } // setValue + + /** + * Property Change Listener + * @param evt PropertyChangeEvent + */ + public void propertyChange (PropertyChangeEvent evt) + { + if (m_stopediting) + return; + + // log.fine( "VLookup.propertyChange", evt); + if (evt.getPropertyName().equals(GridField.PROPERTY)) + { + m_inserting = GridField.INSERTING.equals(evt.getOldValue()); // MField.setValue + setValue(evt.getNewValue()); + m_inserting = false; + } + } // propertyChange + + /** + * Return Editor value (Integer) + * @return value + */ + public Object getValue() + { + if (m_comboActive) + return m_combo.getValue (); + return m_value; + } // getValue + + /** + * Return editor display + * @return display value + */ + public String getDisplay() + { + String retValue = null; + if (m_comboActive) + retValue = m_combo.getDisplay(); + // check lookup + else if (m_lookup == null) + retValue = m_value == null ? null : m_value.toString(); + else + retValue = m_lookup.getDisplay(m_value); + // log.fine( "VLookup.getDisplay - " + retValue, "ComboActive=" + m_comboActive); + return retValue; + } // getDisplay + + /** + * Set Field/WindowNo for ValuePreference + * @param mField Model Field for Lookup + */ + public void setField (GridField mField) + { + m_mField = mField; + if (m_mField != null + && MRole.getDefault().isShowPreference()) + ValuePreference.addMenu (this, popupMenu); + } // setField + + + /************************************************************************** + * Action Listener - data binding + * @param e ActionEvent + */ + public void actionPerformed (ActionEvent e) + { + if (m_settingValue || m_settingFocus || m_stopediting) + return; + log.config(m_columnName + " - " + e.getActionCommand() + ", ComboValue=" + m_combo.getSelectedItem()); + // log.fine("Hash=" + this.hashCode()); + + // Preference + if (e.getActionCommand().equals(ValuePreference.NAME)) + { + if (MRole.getDefault().isShowPreference()) + ValuePreference.start (m_mField, getValue(), getDisplay()); + return; + } + + // Combo Selection + else if (e.getSource() == m_combo) + { + Object value = getValue(); + Object o = m_combo.getSelectedItem(); + if (o != null) + { + String s = o.toString(); + // don't allow selection of inactive + if (s.startsWith(MLookup.INACTIVE_S) && s.endsWith(MLookup.INACTIVE_E)) + { + log.info(m_columnName + " - selection inactive set to NULL"); + value = null; + } + } + actionCombo (value); // data binding + } + // Button pressed + else if (e.getSource() == m_button) + actionButton (""); + // Text entered + else if (e.getSource() == m_text) + actionText(); + + // Popup Menu + else if (e.getSource() == mZoom) + actionZoom(m_combo.getSelectedItem()); + else if (e.getSource() == mRefresh) + actionRefresh(); + else if (e.getSource() == mBPartnerNew) + actionBPartner(true); + else if (e.getSource() == mBPartnerUpd) + actionBPartner(false); + } // actionPerformed + + /** + * Action Listener Interface + * @param listener listener + */ + public void addActionListener(ActionListener listener) + { + m_combo.addActionListener(listener); + m_text.addActionListener(listener); + } // addActionListener + + /** + * Action - Combo. + *
    + * == dataBinding == inform of new value + *

    +	 *  VLookup.actionCombo
    +	 *      GridController.vetoableChange
    +	 *          MTable.setValueAt
    +	 *              MField.setValue
    +	 *                  VLookup.setValue
    +	 *          MTab.dataStatusChanged
    +	 *  
    + * @param value new value + */ + private void actionCombo (Object value) + { + log.fine("Value=" + value); + try + { + // -> GridController.vetoableChange + fireVetoableChange (m_columnName, null, value); + } + catch (PropertyVetoException pve) + { + log.log(Level.SEVERE, m_columnName, pve); + } + // is the value updated ? + boolean updated = false; + + Object updatedValue = value; + + if (updatedValue instanceof Object[] && ((Object[])updatedValue).length > 0) + { + updatedValue = ((Object[])updatedValue)[0]; + } + + if (updatedValue == null && m_value == null) + updated = true; + else if (updatedValue != null && value.equals(m_value)) + updated = true; + if (!updated) + { + // happens if VLookup is used outside of APanel/GridController (no property listener) + log.fine(m_columnName + " - Value explicitly set - new=" + updatedValue + ", old=" + m_value); + if (getListeners(PropertyChangeListener.class).length <= 0) + setValue(updatedValue); + } + } // actionCombo + + + /** + * Action - Button. + * - Call Info + * @param queryValue initial query value + */ + private void actionButton (String queryValue) + { + m_button.setEnabled(false); // disable double click + if (m_lookup == null) + return; // leave button disabled + m_text.requestFocus(); // closes other editors + Frame frame = Env.getFrame(this); + + /** + * Three return options: + * - Value Selected & OK pressed => store result => result has value + * - Cancel pressed => store null => result == null && cancelled + * - Window closed -> ignore => result == null && !cancalled + */ + + Object result[] = null; + boolean cancelled = false; + boolean multipleSelection = false; + // + String col = m_lookup.getColumnName(); // fully qualified name + if (col.indexOf('.') != -1) + col = col.substring(col.indexOf('.')+1); + // Zoom / Validation + String whereClause = getWhereClause(); + // + log.fine(col + + ", Zoom=" + m_lookup.getZoom() + + " (" + whereClause + ")"); + // + boolean resetValue = false; // reset value so that is always treated as new entry + String infoFactoryClass = m_lookup.getInfoFactoryClass(); + if (infoFactoryClass != null && infoFactoryClass.trim().length() > 0) + { + try { + Class clazz = (Class)this.getClass().getClassLoader().loadClass(infoFactoryClass); + InfoFactory factory = clazz.newInstance(); + if (m_tableName == null) // sets table name & key column + getDirectAccessSQL("*"); + Info ig = factory.create (frame, true, m_lookup.getWindowNo(), + m_tableName, m_keyColumnName, queryValue, false, whereClause); + ig.setVisible(true); + cancelled = ig.isCancelled(); + result = ig.getSelectedKeys(); + } catch (Exception e) { + log.log(Level.SEVERE, "Failed to load custom InfoFactory - " + e.getLocalizedMessage(), e); + } + } + else if (col.equals("M_Product_ID")) + { + // Reset + Env.setContext(Env.getCtx(), Env.WINDOW_INFO, Env.TAB_INFO, "M_Product_ID", "0"); + Env.setContext(Env.getCtx(), Env.WINDOW_INFO, Env.TAB_INFO, "M_AttributeSetInstance_ID", "0"); + Env.setContext(Env.getCtx(), Env.WINDOW_INFO, Env.TAB_INFO, "M_Lookup_ID", "0"); + // Replace Value with name if no value exists + if (queryValue.length() == 0 && m_text.getText().length() > 0) + queryValue = "@" + m_text.getText() + "@"; // Name indicator - otherwise Value + int M_Warehouse_ID = Env.getContextAsInt(Env.getCtx(), m_lookup.getWindowNo(), "M_Warehouse_ID"); + int M_PriceList_ID = Env.getContextAsInt(Env.getCtx(), m_lookup.getWindowNo(), "M_PriceList_ID"); + + int AD_Table_ID = MColumn.getTable_ID(Env.getCtx(), m_mField.getAD_Column_ID(), null); + multipleSelection = (MOrderLine.Table_ID == AD_Table_ID) || (MInvoiceLine.Table_ID == AD_Table_ID); + + // Show Info + InfoProduct ip = new InfoProduct (frame, true, m_lookup.getWindowNo(), + M_Warehouse_ID, M_PriceList_ID, queryValue, multipleSelection, whereClause); + ip.setVisible(true); + cancelled = ip.isCancelled(); + result = ip.getSelectedKeys(); + resetValue = true; + } + else if (col.equals("C_BPartner_ID")) + { + // Replace Value with name if no value exists + if (queryValue.length() == 0 && m_text.getText().length() > 0) + queryValue = m_text.getText(); + boolean isSOTrx = true; // default + if (Env.getContext(Env.getCtx(), m_lookup.getWindowNo(), "IsSOTrx").equals("N")) + isSOTrx = false; + InfoBPartner ip = new InfoBPartner (frame, true, m_lookup.getWindowNo(), + queryValue, isSOTrx, multipleSelection, whereClause); + ip.setVisible(true); + cancelled = ip.isCancelled(); + result = ip.getSelectedKeys(); + } + else // General Info + { + if (m_tableName == null) // sets table name & key column + getDirectAccessSQL("*"); + Info ig = Info.create (frame, true, m_lookup.getWindowNo(), + m_tableName, m_keyColumnName, queryValue, multipleSelection, whereClause); + ig.setVisible(true); + cancelled = ig.isCancelled(); + result = ig.getSelectedKeys(); + } + + // Result + if (result != null && result.length > 0) + { + log.config(m_columnName + " - Result = " + result.toString() + " (" + result.getClass().getName() + ")"); + // make sure that value is in cache + m_lookup.getDirect(result[0], false, true); + if (resetValue) + actionCombo (null); + // juddm added logic for multi-select handling + if (result.length > 1) + actionCombo (result); // data binding + else + actionCombo (result[0]); + + } + else if (cancelled) + { + log.config(m_columnName + " - Result = null (cancelled)"); + actionCombo(null); + } + else + { + log.config(m_columnName + " - Result = null (not cancelled)"); + setValue(m_value); // to re-display value + } + // + m_button.setEnabled(true); + m_text.requestFocus(); + } // actionButton + + /** + * Get Where Clause + * @return where clause or "" + */ + private String getWhereClause() + { + String whereClause = ""; + if (m_lookup == null) + return ""; + if (m_lookup.getZoomQuery() != null) + whereClause = m_lookup.getZoomQuery().getWhereClause(); + String validation = m_lookup.getValidation(); + if (validation == null) + validation = ""; + if (whereClause.length() == 0) + whereClause = validation; + else if (validation.length() > 0) + whereClause += " AND " + validation; + // log.finest("ZoomQuery=" + (m_lookup.getZoomQuery()==null ? "" : m_lookup.getZoomQuery().getWhereClause()) + // + ", Validation=" + m_lookup.getValidation()); + if (whereClause.indexOf('@') != -1) + { + String validated = Env.parseContext(Env.getCtx(), m_lookup.getWindowNo(), whereClause, false); + if (validated.length() == 0) + log.severe(m_columnName + " - Cannot Parse=" + whereClause); + else + { + log.fine(m_columnName + " - Parsed: " + validated); + return validated; + } + } + return whereClause; + } // getWhereClause + + /** + * + * + * + */ + private String getExtraWhereClause (String text) + { + StringBuffer sql = new StringBuffer(); + m_tableName = m_columnName.substring(0, m_columnName.length()-3); + m_keyColumnName = m_columnName; + // + if (m_columnName.equals("M_Product_ID")) + { + // Reset + Env.setContext(Env.getCtx(), Env.WINDOW_INFO, Env.TAB_INFO, "M_Product_ID", "0"); + Env.setContext(Env.getCtx(), Env.WINDOW_INFO, Env.TAB_INFO, "M_AttributeSetInstance_ID", "0"); + Env.setContext(Env.getCtx(), Env.WINDOW_INFO, Env.TAB_INFO, "M_Locator_ID", "0"); + // + sql.append(" AND (UPPER(p.Value) LIKE ") + .append(DB.TO_STRING(text)) + .append(" OR UPPER(p.Name) LIKE ").append(DB.TO_STRING(text)) + .append(" OR p.SKU LIKE ").append(DB.TO_STRING(text)).append(")"); + //.append(" OR p.SKU LIKE ").append(DB.TO_STRING(text)) + //.append(" OR p.UPC LIKE ").append(DB.TO_STRING(text)).append(")"); + } + // Predefined + /* + if (sql.length() > 0) + { + String wc = getWhereClause(); + if (wc != null && wc.length() > 0) + sql.append(" AND ").append(wc); + sql.append(" AND IsActive='Y'"); + // *** + log.finest(m_columnName + " (predefined) " + sql.toString()); + return MRole.getDefault().addAccessSQL(sql.toString(), + m_tableName, MRole.SQL_NOTQUALIFIED, MRole.SQL_RO); + }*/ + + return sql.toString(); + } + /** + * Check, if data returns unique entry, otherwise involve Info via Button + */ + private void actionText() + { + String text = m_text.getText(); + // Nothing entered + if (text == null || text.length() == 0 || text.equals("%")) + { + actionButton(text); + return; + } + text = text.toUpperCase(); + log.config(m_columnName + " - " + text); + + // Exact first + PreparedStatement pstmt = null; + String finalSQL = Msg.parseTranslation(Env.getCtx(), getDirectAccessSQL(text)); + int id = -3; + try + { + pstmt = DB.prepareStatement(finalSQL, null); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + id = rs.getInt(1); // first + if (rs.next()) + id = -1; // only if unique + } + rs.close(); + pstmt.close(); + } + catch (Exception e) + { + log.log(Level.SEVERE, finalSQL, e); + id = -2; + } + // Try like + if (id == -3 && !text.endsWith("%")) + { + text += "%"; + finalSQL = Msg.parseTranslation(Env.getCtx(), getDirectAccessSQL(text)); + try + { + pstmt = DB.prepareStatement(finalSQL, null); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + id = rs.getInt(1); // first + if (rs.next()) + id = -1; // only if unique + } + rs.close(); + pstmt.close(); + } + catch (Exception e) + { + log.log(Level.SEVERE, finalSQL, e); + id = -2; + } + } + try + { + if (pstmt != null) + pstmt.close(); + } + catch (Exception e) + { + } + + // No (unique) result + if (id <= 0) + { + if (id == -3) + log.fine(m_columnName + " - Not Found - " + finalSQL); + else + log.fine(m_columnName + " - Not Unique - " + finalSQL); + m_value = null; // force re-display + actionButton(m_text.getText()); + return; + } + log.fine(m_columnName + " - Unique ID=" + id); + m_value = null; // forces re-display if value is unchanged but text updated and still unique + actionCombo (new Integer(id)); // data binding + m_text.requestFocus(); + } // actionText + + + private String m_tableName = null; + private String m_keyColumnName = null; + + /** + * Generate Access SQL for Search. + * The SQL returns the ID of the value entered + * Also sets m_tableName and m_keyColumnName + * @param text uppercase text for LIKE comparison + * @return sql or "" + * Example + * SELECT C_Payment_ID FROM C_Payment WHERE UPPER(DocumentNo) LIKE x OR ... + */ + private String getDirectAccessSQL (String text) + { + StringBuffer sql = new StringBuffer(); + m_tableName = m_columnName.substring(0, m_columnName.length()-3); + m_keyColumnName = m_columnName; + // + if (m_columnName.equals("M_Product_ID")) + { + // Reset + Env.setContext(Env.getCtx(), Env.WINDOW_INFO, Env.TAB_INFO, "M_Product_ID", "0"); + Env.setContext(Env.getCtx(), Env.WINDOW_INFO, Env.TAB_INFO, "M_AttributeSetInstance_ID", "0"); + Env.setContext(Env.getCtx(), Env.WINDOW_INFO, Env.TAB_INFO, "M_Locator_ID", "0"); + // + sql.append("SELECT M_Product_ID FROM M_Product WHERE (UPPER(Value) LIKE ") + .append(DB.TO_STRING(text)) + .append(" OR UPPER(Name) LIKE ").append(DB.TO_STRING(text)) + .append(" OR SKU LIKE ").append(DB.TO_STRING(text)) + .append(" OR UPC LIKE ").append(DB.TO_STRING(text)).append(")"); + } + else if (m_columnName.equals("C_BPartner_ID")) + { + sql.append("SELECT C_BPartner_ID FROM C_BPartner WHERE (UPPER(Value) LIKE ") + .append(DB.TO_STRING(text)) + .append(" OR UPPER(Name) LIKE ").append(DB.TO_STRING(text)).append(")"); + } + else if (m_columnName.equals("C_Order_ID")) + { + sql.append("SELECT C_Order_ID FROM C_Order WHERE UPPER(DocumentNo) LIKE ") + .append(DB.TO_STRING(text)); + } + else if (m_columnName.equals("C_Invoice_ID")) + { + sql.append("SELECT C_Invoice_ID FROM C_Invoice WHERE UPPER(DocumentNo) LIKE ") + .append(DB.TO_STRING(text)); + } + else if (m_columnName.equals("M_InOut_ID")) + { + sql.append("SELECT M_InOut_ID FROM M_InOut WHERE UPPER(DocumentNo) LIKE ") + .append(DB.TO_STRING(text)); + } + else if (m_columnName.equals("C_Payment_ID")) + { + sql.append("SELECT C_Payment_ID FROM C_Payment WHERE UPPER(DocumentNo) LIKE ") + .append(DB.TO_STRING(text)); + } + else if (m_columnName.equals("GL_JournalBatch_ID")) + { + sql.append("SELECT GL_JournalBatch_ID FROM GL_JournalBatch WHERE UPPER(DocumentNo) LIKE ") + .append(DB.TO_STRING(text)); + } + else if (m_columnName.equals("SalesRep_ID")) + { + sql.append("SELECT AD_User_ID FROM AD_User WHERE UPPER(Name) LIKE ") + .append(DB.TO_STRING(text)); + m_tableName = "AD_User"; + m_keyColumnName = "AD_User_ID"; + } + // Predefined + if (sql.length() > 0) + { + String wc = getWhereClause(); + if (wc != null && wc.length() > 0) + sql.append(" AND ").append(wc); + sql.append(" AND IsActive='Y'"); + // *** + log.finest(m_columnName + " (predefined) " + sql.toString()); + return MRole.getDefault().addAccessSQL(sql.toString(), + m_tableName, MRole.SQL_NOTQUALIFIED, MRole.SQL_RO); + } + + // Check if it is a Table Reference + if (m_lookup != null && m_lookup instanceof MLookup) + { + int AD_Reference_ID = ((MLookup)m_lookup).getAD_Reference_Value_ID(); + if (AD_Reference_ID != 0) + { + String query = "SELECT kc.ColumnName, dc.ColumnName, t.TableName " + + "FROM AD_Ref_Table rt" + + " INNER JOIN AD_Column kc ON (rt.AD_Key=kc.AD_Column_ID)" + + " INNER JOIN AD_Column dc ON (rt.AD_Display=dc.AD_Column_ID)" + + " INNER JOIN AD_Table t ON (rt.AD_Table_ID=t.AD_Table_ID) " + + "WHERE rt.AD_Reference_ID=?"; + String displayColumnName = null; + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(query, null); + pstmt.setInt(1, AD_Reference_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + m_keyColumnName = rs.getString(1); + displayColumnName = rs.getString(2); + m_tableName = rs.getString(3); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, query, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + if (displayColumnName != null) + { + sql = new StringBuffer(); + sql.append("SELECT ").append(m_keyColumnName) + .append(" FROM ").append(m_tableName) + .append(" WHERE UPPER(").append(displayColumnName) + .append(") LIKE ").append(DB.TO_STRING(text)) + .append(" AND IsActive='Y'"); + String wc = getWhereClause(); + if (wc != null && wc.length() > 0) + sql.append(" AND ").append(wc); + // *** + log.finest(m_columnName + " (Table) " + sql.toString()); + return MRole.getDefault().addAccessSQL(sql.toString(), + m_tableName, MRole.SQL_NOTQUALIFIED, MRole.SQL_RO); + } + } // Table Reference + } // MLookup + + /** Check Well Known Columns of Table - assumes TableDir **/ + String query = "SELECT t.TableName, c.ColumnName " + + "FROM AD_Column c " + + " INNER JOIN AD_Table t ON (c.AD_Table_ID=t.AD_Table_ID AND t.IsView='N') " + + "WHERE (c.ColumnName IN ('DocumentNo', 'Value', 'Name') OR c.IsIdentifier='Y')" + + " AND c.AD_Reference_ID IN (10,14)" + + " AND EXISTS (SELECT * FROM AD_Column cc WHERE cc.AD_Table_ID=t.AD_Table_ID" + + " AND cc.IsKey='Y' AND cc.ColumnName=?)"; + m_keyColumnName = m_columnName; + sql = new StringBuffer(); + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(query, null); + pstmt.setString(1, m_keyColumnName); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + if (sql.length() != 0) + sql.append(" OR "); + m_tableName = rs.getString(1); + sql.append("UPPER(").append(rs.getString(2)).append(") LIKE ").append(DB.TO_STRING(text)); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (SQLException ex) + { + log.log(Level.SEVERE, query, ex); + } + try + { + if (pstmt != null) + pstmt.close(); + } + catch (SQLException ex1) + { + } + pstmt = null; + // + if (sql.length() == 0) + { + log.log(Level.SEVERE, m_columnName + " (TableDir) - no standard/identifier columns"); + return ""; + } + // + StringBuffer retValue = new StringBuffer ("SELECT ") + .append(m_columnName).append(" FROM ").append(m_tableName) + .append(" WHERE ").append(sql) + .append(" AND IsActive='Y'"); + String wc = getWhereClause(); + if (wc != null && wc.length() > 0) + retValue.append(" AND ").append(wc); + // *** + log.finest(m_columnName + " (TableDir) " + sql.toString()); + return MRole.getDefault().addAccessSQL(retValue.toString(), + m_tableName, MRole.SQL_NOTQUALIFIED, MRole.SQL_RO); + } // getDirectAccessSQL + + + /** + * Action - Special BPartner Screen + * @param newRecord true if new record should be created + */ + private void actionBPartner (boolean newRecord) + { + VBPartner vbp = new VBPartner (Env.getFrame(this), m_lookup.getWindowNo()); + int BPartner_ID = 0; + // if update, get current value + if (!newRecord) + { + if (m_value instanceof Integer) + BPartner_ID = ((Integer)m_value).intValue(); + else if (m_value != null) + BPartner_ID = Integer.parseInt(m_value.toString()); + } + + vbp.loadBPartner (BPartner_ID); + vbp.setVisible(true); + // get result + int result = vbp.getC_BPartner_ID(); + if (result == 0 // 0 = not saved + && result == BPartner_ID) // the same + return; + // Maybe new BPartner - put in cache + m_lookup.getDirect(new Integer(result), false, true); + + actionCombo (new Integer(result)); // data binding + } // actionBPartner + + /** + * Action - Zoom + * @param selectedItem item + */ + private void actionZoom (Object selectedItem) + { + if (m_lookup == null) + return; + // + MQuery zoomQuery = m_lookup.getZoomQuery(); + Object value = getValue(); + if (value == null) + value = selectedItem; + // If not already exist or exact value + if (zoomQuery == null || value != null) + { + zoomQuery = new MQuery(); // ColumnName might be changed in MTab.validateQuery + String keyColumnName = null; + // Check if it is a Table Reference + if (m_lookup != null && m_lookup instanceof MLookup) + { + int AD_Reference_ID = ((MLookup)m_lookup).getAD_Reference_Value_ID(); + if (AD_Reference_ID != 0) + { + String query = "SELECT kc.ColumnName" + + " FROM AD_Ref_Table rt" + + " INNER JOIN AD_Column kc ON (rt.AD_Key=kc.AD_Column_ID)" + + "WHERE rt.AD_Reference_ID=?"; + + PreparedStatement pstmt = null; + try + { + pstmt = DB.prepareStatement(query, null); + pstmt.setInt(1, AD_Reference_ID); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) + { + keyColumnName = rs.getString(1); + } + rs.close(); + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + log.log(Level.SEVERE, query, e); + } + try + { + if (pstmt != null) + pstmt.close(); + pstmt = null; + } + catch (Exception e) + { + pstmt = null; + } + } // Table Reference + } // MLookup + + if(keyColumnName != null && keyColumnName.length() !=0) + zoomQuery.addRestriction(keyColumnName, MQuery.EQUAL, value); + else + zoomQuery.addRestriction(m_columnName, MQuery.EQUAL, value); + + zoomQuery.setRecordCount(1); // guess + } + int AD_Window_ID = m_lookup.getZoom(zoomQuery); + // + log.info(m_columnName + " - AD_Window_ID=" + AD_Window_ID + + " - Query=" + zoomQuery + " - Value=" + value); + // + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + // + AWindow frame = new AWindow(); + if (!frame.initWindow(AD_Window_ID, zoomQuery)) + { + setCursor(Cursor.getDefaultCursor()); + ValueNamePair pp = CLogger.retrieveError(); + String msg = pp==null ? "AccessTableNoView" : pp.getValue(); + ADialog.error(m_lookup.getWindowNo(), this, msg, pp==null ? "" : pp.getName()); + } + else + { + AEnv.addToWindowManager(frame); + if (Ini.isPropertyBool(Ini.P_OPEN_WINDOW_MAXIMIZED)) + { + AEnv.showMaximized(frame); + } + else + { + AEnv.showCenterScreen(frame); + } + } + // async window - not able to get feedback + frame = null; + // + setCursor(Cursor.getDefaultCursor()); + } // actionZoom + + /** + * Action - Refresh + */ + private void actionRefresh() + { + if (m_lookup == null) + return; + // + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + // + Object obj = m_combo.getSelectedItem(); + log.info(m_columnName + " #" + m_lookup.getSize() + ", Selected=" + obj); + //no need to refresh readonly lookup, just remove direct cache + if (!isReadWrite()) + { + m_settingValue = true; // disable actions + m_lookup.removeAllElements(); + m_lastDisplay = m_lookup.getDisplay(m_value); + m_text.setText(m_lastDisplay); + m_text.setCaretPosition(0); + m_settingValue = false; + } + else + { + m_lookup.refresh(); + m_lookup.fillComboBox(isMandatory(), true, true, false); + m_combo.setSelectedItem(obj); + //m_combo.revalidate(); + } + // + setCursor(Cursor.getDefaultCursor()); + log.info(m_columnName + " #" + m_lookup.getSize() + ", Selected=" + m_combo.getSelectedItem()); + } // actionRefresh + + + /************************************************************************** + * Focus Listener for ComboBoxes with missing Validation or invalid entries + * - Requery listener for updated list + * @param e FocusEvent + */ + public void focusGained (FocusEvent e) + { + if (m_combo == null || m_combo.getEditor() == null) + return; + if ((e.getSource() != m_combo && e.getSource() != m_combo.getEditor().getEditorComponent()) + || e.isTemporary() || m_haveFocus || m_lookup == null) + return; + + //avoid repeated query + if (m_lookup.isValidated() && m_lookup.isLoaded()) + { + m_haveFocus = true; + return; + } + // + m_haveFocus = true; // prevents calling focus gained twice + m_settingFocus = true; // prevents actionPerformed + // + Object obj = m_lookup.getSelectedItem(); + log.config(m_columnName + + " - Start Count=" + m_combo.getItemCount() + ", Selected=" + obj); + // log.fine( "VLookupHash=" + this.hashCode()); + boolean popupVisible = m_combo.isPopupVisible(); + m_lookup.fillComboBox(isMandatory(), true, true, false); // only validated & active + if (popupVisible) + { + //refresh + m_combo.hidePopup(); + m_combo.showPopup(); + } + log.config(m_columnName + + " - Update Count=" + m_combo.getItemCount() + ", Selected=" + m_lookup.getSelectedItem()); + m_lookup.setSelectedItem(obj); + log.config(m_columnName + + " - Selected Count=" + m_combo.getItemCount() + ", Selected=" + m_lookup.getSelectedItem()); + // + m_settingFocus = false; + } // focusGained + + /** + * Reset Selection List + * @param e FocusEvent + */ + public void focusLost(FocusEvent e) + { + if (e.isTemporary() + || m_lookup == null + || !m_button.isEnabled() ) // set by actionButton + return; + // Text Lost focus + if (e.getSource() == m_text) + { + String text = m_text.getText(); + log.config(m_columnName + " (Text) " + m_columnName + " = " + m_value + " - " + text); + m_haveFocus = false; + // Skip if empty + if ((m_value == null + && m_text.getText().length() == 0)) + return; + if (m_lastDisplay.equals(text)) + return; + // + actionText(); // re-display + return; + } + // Combo lost focus + if (e.getSource() != m_combo && e.getSource() != m_combo.getEditor().getEditorComponent()) + return; + if (m_lookup.isValidated() && !m_lookup.hasInactive()) + { + m_haveFocus = false; + return; + } + // + m_settingFocus = true; // prevents actionPerformed + // + log.config(m_columnName + " = " + m_combo.getSelectedItem()); + Object obj = m_combo.getSelectedItem(); + /* + // set original model + if (!m_lookup.isValidated()) + m_lookup.fillComboBox(true); // previous selection + */ + // Set value + if (obj != null) + { + m_combo.setSelectedItem(obj); + // original model may not have item + if (!m_combo.getSelectedItem().equals(obj)) + { + log.fine(m_columnName + " - added to combo - " + obj); + m_combo.addItem(obj); + m_combo.setSelectedItem(obj); + } + } + // actionCombo(getValue()); + m_settingFocus = false; + m_haveFocus = false; // can gain focus again + } // focusLost + + /** + * Set ToolTip + * @param text tool tip text + */ + public void setToolTipText(String text) + { + super.setToolTipText(text); + m_button.setToolTipText(text); + m_text.setToolTipText(text); + m_combo.setToolTipText(text); + } // setToolTipText + + /** + * Refresh Query + * @return count + */ + public int refresh() + { + if (m_lookup == null) + return -1; + + //no need to refresh readonly lookup, just remove direct cache + if (!isReadWrite()) { + m_lookup.removeAllElements(); + return 0; + } + + return m_lookup.refresh(); + } // refresh + + /** + * Use by vcelleditor to indicate editing is off and don't invoke databinding + * @param stopediting + */ + public void setStopEditing(boolean stopediting) { + m_stopediting = stopediting; + } + + +} // VLookup + +/***************************************************************************** + * Mouse Listener for Popup Menu + */ +final class VLookup_mouseAdapter extends java.awt.event.MouseAdapter +{ + /** + * Constructor + * @param adaptee adaptee + */ + VLookup_mouseAdapter(VLookup adaptee) + { + m_adaptee = adaptee; + } // VLookup_mouseAdapter + + private VLookup m_adaptee; + + /** + * Mouse Listener + * @param e MouseEvent + */ + public void mouseClicked(MouseEvent e) + { + // System.out.println("mouseClicked " + e.getID() + " " + e.getSource().getClass().toString()); + // popup menu + if (SwingUtilities.isRightMouseButton(e)) + m_adaptee.popupMenu.show((Component)e.getSource(), e.getX(), e.getY()); + // Hide the popup if not right click - teo_sarca [ 1734802 ] + else + m_adaptee.popupMenu.setVisible(false); + } // mouse Clicked + +} // VLookup_mouseAdapter \ No newline at end of file diff --git a/client/src/org/compiere/print/Viewer.java b/client/src/org/compiere/print/Viewer.java new file mode 100644 index 0000000000..9e00ec864b --- /dev/null +++ b/client/src/org/compiere/print/Viewer.java @@ -0,0 +1,1199 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + * Contributor: phib [ 1566335 ] Report View scrolling (Bug 1566328) * + * Teo Sarca [ 1619449 ] Minor typo problem * + *****************************************************************************/ +package org.compiere.print; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import javax.swing.*; +import javax.swing.event.*; +import org.compiere.apps.*; +import org.compiere.apps.search.*; +import org.compiere.model.*; +import org.compiere.swing.*; +import org.compiere.util.*; +import org.adempiere.pdf.*; + +/** + * Print View Frame + * + * @author Jorg Janke + * @version $Id: Viewer.java,v 1.2 2006/07/30 00:51:28 jjanke Exp $ + * globalqss: integrate phib contribution from + * http://sourceforge.net/tracker/index.php?func=detail&aid=1566335&group_id=176962&atid=879334 + * globalqss: integrate Teo Sarca bug fixing + * Colin Rooney 2007/03/20 RFE#1670185 & BUG#1684142 + * Extend security to Info queries + * + * @author Teo Sarca, SC ARHIPAC SERVICE SRL + *
  • FR [ 1762466 ] Add "Window" menu to report viewer. + *
  • FR [ 1894640 ] Report Engine: Excel Export support + */ +public class Viewer extends CFrame + implements ActionListener, ChangeListener, WindowStateListener +{ + /** + * Viewer Constructor + * @param re report engine + */ + public Viewer (ReportEngine re) + { + super(); + log.info(""); + m_WindowNo = Env.createWindowNo(this); + m_reportEngine = re; + m_AD_Table_ID = re.getPrintFormat().getAD_Table_ID(); + if (!MRole.getDefault().isCanReport(m_AD_Table_ID)) + { + ADialog.error(m_WindowNo, this, "AccessCannotReport", m_reportEngine.getName()); + this.dispose(); + } + m_isCanExport = MRole.getDefault().isCanExport(m_AD_Table_ID); + try + { + m_viewPanel = re.getView(); + m_ctx = m_reportEngine.getCtx(); + jbInit(); + dynInit(); + if (!m_viewPanel.isArchivable()) + log.warning("Cannot archive Document"); + AEnv.showCenterScreen(this); + } + catch(Exception e) + { + log.log(Level.SEVERE, "", e); + ADialog.error(m_WindowNo, this, "LoadError", e.getLocalizedMessage()); + this.dispose(); + } + } // Viewer + + /** Window No */ + private int m_WindowNo; + /** Print Context */ + private Properties m_ctx; + /** Page No */ + private int m_pageNo = 1; + /** Max Page Number */ + private int m_pageMax = 1; + /** View Pane */ + private View m_viewPanel; + /** Setting Values */ + private boolean m_setting = false; + /** Report Engine */ + private ReportEngine m_reportEngine; + /** Drill Down/Across */ + private boolean m_drillDown = true; + /** Table ID */ + private int m_AD_Table_ID = 0; + private boolean m_isCanExport; + + private MQuery m_ddQ = null; + private MQuery m_daQ = null; + private CMenuItem m_ddM = null; + private CMenuItem m_daM = null; + + /** Logger */ + private static CLogger log = CLogger.getCLogger(Viewer.class); + + // + private CPanel northPanel = new CPanel(); + private JScrollPane centerScrollPane = new JScrollPane(); + private StatusBar statusBar = new StatusBar(false); + private JMenuBar menuBar = new JMenuBar(); + private JToolBar toolBar = new JToolBar(); + private CButton bPrint = new CButton(); + private CButton bSendMail = new CButton(); + private CButton bPageSetup = new CButton(); + private CButton bArchive = new CButton(); + private BorderLayout northLayout = new BorderLayout(); + private CButton bCustomize = new CButton(); + private CButton bEnd = new CButton(); + private CButton bFind = new CButton(); + private CButton bExport = new CButton(); + private CComboBox comboReport = new CComboBox(); + private CButton bPrevious = new CButton(); + private CButton bNext = new CButton(); + private SpinnerNumberModel spinnerModel = new SpinnerNumberModel(1,1,100,1); + private JSpinner spinner = new JSpinner(spinnerModel); + private CLabel labelDrill = new CLabel(); + private CComboBox comboDrill = new CComboBox(); +// private CComboBox comboZoom = new CComboBox(View.ZOOM_OPTIONS); + + + /** + * Static Layout + * @throws Exception + */ + private void jbInit() throws Exception + { + this.setIconImage(Env.getImage("mReport.gif")); + this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + // + northPanel.setLayout(northLayout); + this.getContentPane().add(northPanel, BorderLayout.NORTH); + northPanel.add(toolBar, BorderLayout.EAST); + this.getContentPane().add(centerScrollPane, BorderLayout.CENTER); + centerScrollPane.getViewport().add(m_viewPanel, null); + // pb add: set scrolling with scrollbar buttons to move by 20 pixels + // each press + centerScrollPane.getVerticalScrollBar().setUnitIncrement(20); + centerScrollPane.getHorizontalScrollBar().setUnitIncrement(20); + // end pb + this.getContentPane().add(statusBar, BorderLayout.SOUTH); + + // ToolBar + this.setJMenuBar(menuBar); + // Page Control + toolBar.add(bPrevious); + toolBar.add(spinner); + spinner.setToolTipText(Msg.getMsg(m_ctx, "GoToPage")); + toolBar.add(bNext); + // Zoom Level + toolBar.addSeparator(); +// toolBar.add(comboZoom, null); +// comboZoom.setToolTipText(Msg.getMsg(m_ctx, "Zoom")); + // Drill + toolBar.addSeparator(); + labelDrill.setText(Msg.getMsg(m_ctx, "Drill") + ": "); + toolBar.add(labelDrill); + toolBar.add(comboDrill); + comboDrill.setToolTipText(Msg.getMsg(m_ctx, "Drill")); + // Format, Customize, Find + toolBar.addSeparator(); + toolBar.add(comboReport); + comboReport.setToolTipText(Msg.translate(m_ctx, "AD_PrintFormat_ID")); + toolBar.add(bCustomize); + bCustomize.setToolTipText(Msg.getMsg(m_ctx, "PrintCustomize")); + toolBar.add(bFind); + bFind.setToolTipText(Msg.getMsg(m_ctx, "Find")); + toolBar.addSeparator(); + // Print/Export + toolBar.add(bPrint); + toolBar.addSeparator(); + toolBar.add(bPageSetup); + bPageSetup.setToolTipText(Msg.getMsg(m_ctx, "PageSetup")); + toolBar.add(bSendMail); + toolBar.add(bArchive); + if (m_isCanExport) + { + bExport.setToolTipText(Msg.getMsg(m_ctx, "Export")); + toolBar.add(bExport); + } + // End + toolBar.addSeparator(); + toolBar.add(bEnd, null); + bEnd.setToolTipText(Msg.getMsg(m_ctx, "End")); + } // jbInit + + /** + * Dynamic Init + */ + private void dynInit() + { + createMenu(); +// comboZoom.addActionListener(this); + // Change Listener to set Page no + //pb comment this out so that scrolling works normally + //centerScrollPane.getViewport().addChangeListener(this); + // end pb + + // Max Page + m_pageMax = m_viewPanel.getPageCount(); + spinnerModel.setMaximum(new Integer(m_pageMax)); + spinner.addChangeListener(this); + + fillComboReport(m_reportEngine.getPrintFormat().get_ID()); + + // View Panel Mouse Listener + m_viewPanel.addMouseListener(new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + if (SwingUtilities.isRightMouseButton(e)) + mouse_clicked(e,true); + else if (e.getClickCount() > 1) + mouse_clicked(e,false); + } + }); + + // fill Drill Options (Name, TableName) + comboDrill.addItem(new ValueNamePair (null,"")); + String sql = "SELECT t.AD_Table_ID, t.TableName, e.PrintName, NULLIF(e.PO_PrintName,e.PrintName) " + + "FROM AD_Column c " + + " INNER JOIN AD_Column used ON (c.ColumnName=used.ColumnName)" + + " INNER JOIN AD_Table t ON (used.AD_Table_ID=t.AD_Table_ID AND t.IsView='N' AND t.AD_Table_ID <> c.AD_Table_ID)" + + " INNER JOIN AD_Column cKey ON (t.AD_Table_ID=cKey.AD_Table_ID AND cKey.IsKey='Y')" + + " INNER JOIN AD_Element e ON (cKey.ColumnName=e.ColumnName) " + + "WHERE c.AD_Table_ID=? AND c.IsKey='Y' " + + "ORDER BY 3"; + boolean trl = !Env.isBaseLanguage(Env.getCtx(), "AD_Element"); + if (trl) + sql = "SELECT t.AD_Table_ID, t.TableName, et.PrintName, NULLIF(et.PO_PrintName,et.PrintName) " + + "FROM AD_Column c" + + " INNER JOIN AD_Column used ON (c.ColumnName=used.ColumnName)" + + " INNER JOIN AD_Table t ON (used.AD_Table_ID=t.AD_Table_ID AND t.IsView='N' AND t.AD_Table_ID <> c.AD_Table_ID)" + + " INNER JOIN AD_Column cKey ON (t.AD_Table_ID=cKey.AD_Table_ID AND cKey.IsKey='Y')" + + " INNER JOIN AD_Element e ON (cKey.ColumnName=e.ColumnName)" + + " INNER JOIN AD_Element_Trl et ON (e.AD_Element_ID=et.AD_Element_ID) " + + "WHERE c.AD_Table_ID=? AND c.IsKey='Y'" + + " AND et.AD_Language=? " + + "ORDER BY 3"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, m_reportEngine.getPrintFormat().getAD_Table_ID()); + if (trl) + pstmt.setString(2, Env.getAD_Language(Env.getCtx())); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + String tableName = rs.getString(2); + String name = rs.getString(3); + String poName = rs.getString(4); + if (poName != null) + name += "/" + poName; + comboDrill.addItem(new ValueNamePair (tableName, name)); + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + if (comboDrill.getItemCount() == 1) + { + labelDrill.setVisible(false); + comboDrill.setVisible(false); + } + else + comboDrill.addActionListener(this); + + revalidate(); + } // dynInit + + /** + * Fill ComboBox comboReport (report options) + * @param AD_PrintFormat_ID item to be selected + */ + private void fillComboReport(int AD_PrintFormat_ID) + { + comboReport.removeActionListener(this); + comboReport.removeAllItems(); + KeyNamePair selectValue = null; + // fill Report Options + String sql = MRole.getDefault().addAccessSQL( + "SELECT AD_PrintFormat_ID, Name, Description " + + "FROM AD_PrintFormat " + + "WHERE AD_Table_ID=? " + //Added Lines by Armen + + "AND IsActive='Y' " + //End of Added Lines + + "ORDER BY Name", + "AD_PrintFormat", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO); + int AD_Table_ID = m_reportEngine.getPrintFormat().getAD_Table_ID(); + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, AD_Table_ID); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + KeyNamePair pp = new KeyNamePair(rs.getInt(1), rs.getString(2)); + comboReport.addItem(pp); + if (rs.getInt(1) == AD_PrintFormat_ID) + selectValue = pp; + } + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + StringBuffer sb = new StringBuffer("** ").append(Msg.getMsg(m_ctx, "NewReport")).append(" **"); + KeyNamePair pp = new KeyNamePair(-1, sb.toString()); + comboReport.addItem(pp); + if (selectValue != null) + comboReport.setSelectedItem(selectValue); + comboReport.addActionListener(this); + } // fillComboReport + + /** + * Revalidate settings after change of environment + */ + private void revalidate() + { + m_pageMax = m_viewPanel.getPageCount(); + spinnerModel.setMaximum(new Integer(m_pageMax)); + + // scroll area (page size dependent) + centerScrollPane.setPreferredSize(new Dimension + (m_viewPanel.getPaperWidth()+30, m_viewPanel.getPaperHeight()+15)); + centerScrollPane.getViewport().setViewSize(new Dimension + (m_viewPanel.getPaperWidth()+2*View.MARGIN, + m_viewPanel.getPaperHeight()+2*View.MARGIN)); + + // Report Info + setTitle(Msg.getMsg(m_ctx, "Report") + ": " + m_reportEngine.getName() + " " + Env.getHeader(m_ctx, 0)); + StringBuffer sb = new StringBuffer (); + sb.append(m_viewPanel.getPaper().toString(m_ctx)) + .append(" - ").append(Msg.getMsg(m_ctx, "DataCols")).append("=") + .append(m_reportEngine.getColumnCount()) + .append(", ").append(Msg.getMsg(m_ctx, "DataRows")).append("=") + .append(m_reportEngine.getRowCount()); + statusBar.setStatusLine(sb.toString()); + // + setPage(m_pageNo); + } // revalidate + + + /** + * Create Menu + */ + private void createMenu() + { + // File + JMenu mFile = AEnv.getMenu("File"); + menuBar.add(mFile); + AEnv.addMenuItem("PrintScreen", null, KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, 0), mFile, this); + AEnv.addMenuItem("ScreenShot", null, KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, Event.SHIFT_MASK), mFile, this); + AEnv.addMenuItem("Report", null, KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.ALT_MASK), mFile, this); + mFile.addSeparator(); + AEnv.addMenuItem("PrintCustomize", "Preference", null, mFile, this); + AEnv.addMenuItem("Translate", null, null, mFile, this); + AEnv.addMenuItem("Find", null, KeyStroke.getKeyStroke(KeyEvent.VK_F, Event.CTRL_MASK), mFile, this); + mFile.addSeparator(); + AEnv.addMenuItem("PageSetup", null, null, mFile, this); + AEnv.addMenuItem("Print", null, KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.CTRL_MASK), mFile, this); + if (m_isCanExport) + AEnv.addMenuItem("Export", null, null, mFile, this); + mFile.addSeparator(); + AEnv.addMenuItem("End", null, KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.ALT_MASK), mFile, this); + AEnv.addMenuItem("Logout", null, KeyStroke.getKeyStroke(KeyEvent.VK_L, Event.SHIFT_MASK+Event.ALT_MASK), mFile, this); + AEnv.addMenuItem("Exit", null, KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.SHIFT_MASK+Event.ALT_MASK), mFile, this); + + // View + JMenu mView = AEnv.getMenu("View"); + menuBar.add(mView); + + if (MRole.getDefault().isAllow_Info_Product()) + { + AEnv.addMenuItem("InfoProduct", null, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK), mView, this); + } + if (MRole.getDefault().isAllow_Info_BPartner()) + { + AEnv.addMenuItem("InfoBPartner", null, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK+Event.CTRL_MASK), mView, this); + } + if (MRole.getDefault().isShowAcct() && MRole.getDefault().isAllow_Info_Account()) + { + AEnv.addMenuItem("InfoAccount", null, KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK+Event.CTRL_MASK), mView, this); + } + if (MRole.getDefault().isAllow_Info_Schedule()) + { + AEnv.addMenuItem("InfoSchedule", null, null, mView, this); + } + mView.addSeparator(); + if (MRole.getDefault().isAllow_Info_Order()) + { + AEnv.addMenuItem("InfoOrder", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Invoice()) + { + AEnv.addMenuItem("InfoInvoice", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_InOut()) + { + AEnv.addMenuItem("InfoInOut", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Payment()) + { + AEnv.addMenuItem("InfoPayment", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_CashJournal()) + { + AEnv.addMenuItem("InfoCashLine", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Resource()) + { + AEnv.addMenuItem("InfoAssignment", "Info", null, mView, this); + } + if (MRole.getDefault().isAllow_Info_Asset()) + { + AEnv.addMenuItem("InfoAsset", "Info", null, mView, this); + } + // Go + JMenu mGo = AEnv.getMenu("Go"); + menuBar.add(mGo); + AEnv.addMenuItem("First", "First", KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, Event.ALT_MASK), mGo, this); + AEnv.addMenuItem("PreviousPage", "Previous", KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.ALT_MASK), mGo, this); + AEnv.addMenuItem("NextPage", "Next", KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.ALT_MASK), mGo, this); + AEnv.addMenuItem("Last", "Last", KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, Event.ALT_MASK), mGo, this); + + // Tools + JMenu mTools = AEnv.getMenu("Tools"); + menuBar.add(mTools); + AEnv.addMenuItem("Calculator", null, null, mTools, this); + AEnv.addMenuItem("Calendar", null, null, mTools, this); + MUser user = MUser.get(Env.getCtx()); + if (user.isAdministrator()) + AEnv.addMenuItem("Editor", null, null, mTools, this); + AEnv.addMenuItem("Script", null, null, mTools, this); + mTools.addSeparator(); + AEnv.addMenuItem("Preference", null, null, mTools, this); + + // Window + AMenu aMenu = (AMenu)Env.getWindow(0); + JMenu mWindow = new WindowMenu(aMenu.getWindowManager(), this); + menuBar.add(mWindow); + + // Help + JMenu mHelp = AEnv.getMenu("Help"); + menuBar.add(mHelp); + AEnv.addMenuItem("Online", null, null, mHelp, this); + AEnv.addMenuItem("SendMail", null, null, mHelp, this); + AEnv.addMenuItem("About", null, null, mHelp, this); + + // ---- ToolBar ---- + // + setButton(bPrint, "Print", "Print"); + setButton(bSendMail, "SendMail", "SendMail"); + setButton(bPageSetup, "PageSetup", "PageSetup"); + setButton(bArchive, "Archive", "Archive"); + if (m_isCanExport) + setButton(bExport, "Export", "Export"); + // + setButton(bNext, "NextPage", "Next"); + setButton(bPrevious, "PreviousPage", "Previous"); + // + setButton(bFind, "Find", "Find"); + setButton(bCustomize, "PrintCustomize", "Preference"); + // + setButton(bEnd, "End", "End"); + } // createMenu + + /** + * Set Button + * @param button button + * @param cmd command + * @param file fine mame + */ + private void setButton (AbstractButton button, String cmd, String file) + { + String text = Msg.getMsg(m_ctx, cmd); + button.setToolTipText(text); + button.setActionCommand(cmd); + // + ImageIcon ii24 = Env.getImageIcon(file+"24.gif"); + if (ii24 != null) + button.setIcon(ii24); + button.setMargin(AppsAction.BUTTON_INSETS); + button.setPreferredSize(AppsAction.BUTTON_SIZE); + button.addActionListener(this); + } // setButton + + /** + * Dispose + */ + public void dispose() + { + Env.clearWinContext(m_WindowNo); + m_reportEngine = null; + m_viewPanel = null; + m_ctx = null; + super.dispose(); + } // dispose + + + /************************************************************************** + * Action Listener + * @param e event + */ + public void actionPerformed (ActionEvent e) + { + if (m_setting) + return; + String cmd = e.getActionCommand(); + log.config(cmd); + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + // +// if (e.getSource() == comboZoom) +// cmd_zoom(); +// else + if (e.getSource() == comboReport) + cmd_report(); + else if (e.getSource() == comboDrill) + cmd_drill(); + else if (cmd.equals("First")) + setPage(1); + else if (cmd.equals("PreviousPage") || cmd.equals("Previous")) + setPage(m_pageNo-1); + else if (cmd.equals("NextPage") || cmd.equals("Next")) + setPage(m_pageNo+1); + else if (cmd.equals("Last")) + setPage(m_pageMax); + else if (cmd.equals("Find")) + cmd_find(); + else if (cmd.equals("Export")) + cmd_export(); + else if (cmd.equals("Print")) + cmd_print(); + else if (cmd.equals("SendMail")) + cmd_sendMail(); + else if (cmd.equals("Archive")) + cmd_archive(); + else if (cmd.equals("PrintCustomize")) + cmd_customize(); + else if (cmd.equals("PageSetup")) + cmd_pageSetup(); + else if (cmd.equals("Translate")) + cmd_translate(); + else if (cmd.equals("End")) + dispose(); + // + else if (e.getSource() == m_ddM) + cmd_window(m_ddQ); + else if (e.getSource() == m_daM) + cmd_window(m_daQ); + // + else if (!AEnv.actionPerformed(e.getActionCommand(), m_WindowNo, this)) + log.log(Level.SEVERE, "unknown action=" + e.getActionCommand()); + // + this.setCursor(Cursor.getDefaultCursor()); + } // actionPerformed + + /** + * Change Listener (spinner, viewpoint) + * @param e event + */ + public void stateChanged (ChangeEvent e) + { + if (m_setting) + return; + // log.config( "Viewer.stateChanged", e); + m_setting = true; + int newPage = 0; + if (e.getSource() == spinner) + { + newPage = ((Integer)spinnerModel.getValue()).intValue(); + } + // pb with the viewport change listener disabled the following is + // superfluous and should be removed + else // Viewpoint + { + Point p = centerScrollPane.getViewport().getViewPosition(); + newPage = Math.round(m_viewPanel.getPageNoAt(p)); + } + setPage(newPage); + m_setting = false; + } // stateChanged + + + /** + * Set Page No + * @param page page no + */ + private void setPage (int page) + { + m_setting = true; + m_pageNo = page; + if (m_pageNo < 1) + m_pageNo = 1; + if (page > m_pageMax) + m_pageNo = m_pageMax; + bPrevious.setEnabled (m_pageNo != 1); + bNext.setEnabled (m_pageNo != m_pageMax); + // + Rectangle pageRectangle = m_viewPanel.getRectangleOfPage(m_pageNo); + pageRectangle.x -= View.MARGIN; + pageRectangle.y -= View.MARGIN; + centerScrollPane.getViewport().setViewPosition(pageRectangle.getLocation()); + // System.out.println("scrollTo " + pageRectangle); + + // Set Page + spinnerModel.setValue(new Integer(m_pageNo)); + StringBuffer sb = new StringBuffer (Msg.getMsg(m_ctx, "Page")) + .append(" ").append(m_pageNo) + .append(m_viewPanel.getPageInfo(m_pageNo)) + .append(" ").append(Msg.getMsg(m_ctx, "of")).append(" ") + .append(m_pageMax) + .append(m_viewPanel.getPageInfoMax()); + statusBar.setStatusDB(sb.toString()); + m_setting = false; + } // setPage + + + /************************************************************************** + * (Re)Set Drill Accross Cursor + */ + private void cmd_drill() + { + m_drillDown = comboDrill.getSelectedIndex() < 1; // -1 or 0 + if (m_drillDown) + setCursor(Cursor.getDefaultCursor()); + else + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } // cmd_drill + + /** + * Mouse clicked + * @param e event + * @param rightClick true if right click + */ + private void mouse_clicked (MouseEvent e, boolean rightClick) + { + Point point = e.getPoint(); + log.info("Right=" + rightClick + " - " + point.toString()); + if (rightClick) + { + m_ddQ = m_viewPanel.getDrillDown(point); + m_daQ = m_viewPanel.getDrillAcross(point); + m_ddM = null; + m_daM = null; + if (m_ddQ == null && m_daQ == null) + return; + // Create Menu + JPopupMenu pop = new JPopupMenu(); + Icon wi = Env.getImageIcon("mWindow.gif"); + if (m_ddQ != null) + { + m_ddM = new CMenuItem(m_ddQ.getDisplayName(Env.getCtx()), wi); + m_ddM.setToolTipText(m_ddQ.toString()); + m_ddM.addActionListener(this); + pop.add(m_ddM); + } + if (m_daQ != null) + { + m_daM = new CMenuItem(m_daQ.getDisplayName(Env.getCtx()), wi); + m_daM.setToolTipText(m_daQ.toString()); + m_daM.addActionListener(this); + pop.add(m_daM); + } + Point pp = e.getPoint(); + pop.show((Component)e.getSource(), pp.x, pp.y); + return; + } + + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + if (m_drillDown) + { + MQuery query = m_viewPanel.getDrillDown(point); + if (query != null) + { + log.info("Drill Down: " + query.getWhereClause(true)); + executeDrill(query); + } + } + else if (comboDrill.getSelectedItem() != null && comboDrill.getSelectedIndex() > 0) + { + MQuery query = m_viewPanel.getDrillAcross(point); + if (query != null) + { + NamePair pp = (NamePair)comboDrill.getSelectedItem(); + query.setTableName(pp.getID()); + log.info("Drill Accross: " + query.getWhereClause(true)); + executeDrill(query); + } + } + cmd_drill(); // setCursor + } // mouse_clicked + + /** + * Execute Drill to Query + * @param query query + */ + private void executeDrill (MQuery query) + { + int AD_Table_ID = AReport.getAD_Table_ID(query.getTableName()); + if (!MRole.getDefault().isCanReport(AD_Table_ID)) + { + ADialog.error(m_WindowNo, this, "AccessCannotReport", query.getTableName()); + return; + } + if (AD_Table_ID != 0) + new AReport (AD_Table_ID, null, query); + else + log.warning("No Table found for " + query.getWhereClause(true)); + } // executeDrill + + /** + * Open Window + * @param query query + */ + private void cmd_window (MQuery query) + { + if (query == null) + return; + AEnv.zoom(query); + } // cmd_window + + /************************************************************************** + * Print Report + */ + private void cmd_print() + { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + m_reportEngine.getPrintInfo().setWithDialog(true); + m_reportEngine.print(); + cmd_drill(); // setCursor + } // cmd_print + + /** + * Send Mail + */ + private void cmd_sendMail() + { + String to = ""; + MUser from = MUser.get(Env.getCtx(), Env.getAD_User_ID(Env.getCtx())); + String subject = m_reportEngine.getName(); + String message = ""; + File attachment = null; + + try + { + attachment = File.createTempFile("mail", ".pdf"); + m_reportEngine.getPDF(attachment); + } + catch (Exception e) + { + log.log(Level.SEVERE, "", e); + } + + EMailDialog emd = new EMailDialog (this, + Msg.getMsg(Env.getCtx(), "SendMail"), + from, to, subject, message, attachment); + + } // cmd_sendMail + + /** + * Archive Report directly + */ + private void cmd_archive () + { + boolean success = false; + byte[] data = Document.getPDFAsArray(m_reportEngine.getLayout().getPageable(false)); // No Copy + if (data != null) + { + MArchive archive = new MArchive (Env.getCtx(), m_reportEngine.getPrintInfo(), null); + archive.setBinaryData(data); + success = archive.save(); + } + if (success) + ADialog.info(m_WindowNo, this, "Archived"); + else + ADialog.error(m_WindowNo, this, "ArchiveError"); + } // cmd_archive + + /** + * Print Setup Dialog + */ + private void cmd_pageSetup() + { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + m_reportEngine.pageSetupDialog(); + revalidate(); + cmd_drill(); // setCursor + } // cmd_pageSetup + + /** + * Export + */ + private void cmd_export() + { + log.config(""); + if (!m_isCanExport) + { + ADialog.error(m_WindowNo, this, "AccessCannotExport", getTitle()); + return; + } + + // + JFileChooser chooser = new JFileChooser(); + chooser.setDialogType(JFileChooser.SAVE_DIALOG); + chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + chooser.setDialogTitle(Msg.getMsg(m_ctx, "Export") + ": " + getTitle()); + // + chooser.addChoosableFileFilter(new ExtensionFileFilter("ps", Msg.getMsg(m_ctx, "FilePS"))); + chooser.addChoosableFileFilter(new ExtensionFileFilter("xml", Msg.getMsg(m_ctx, "FileXML"))); + chooser.addChoosableFileFilter(new ExtensionFileFilter("pdf", Msg.getMsg(m_ctx, "FilePDF"))); + chooser.addChoosableFileFilter(new ExtensionFileFilter("html", Msg.getMsg(m_ctx, "FileHTML"))); + chooser.addChoosableFileFilter(new ExtensionFileFilter("txt", Msg.getMsg(m_ctx, "FileTXT"))); + chooser.addChoosableFileFilter(new ExtensionFileFilter("ssv", Msg.getMsg(m_ctx, "FileSSV"))); + chooser.addChoosableFileFilter(new ExtensionFileFilter("csv", Msg.getMsg(m_ctx, "FileCSV"))); + chooser.addChoosableFileFilter(new ExtensionFileFilter("xls", Msg.getMsg(m_ctx, "FileXLS"))); + // + if (chooser.showSaveDialog(this) != JFileChooser.APPROVE_OPTION) + return; + + // Create File + File outFile = ExtensionFileFilter.getFile(chooser.getSelectedFile(), chooser.getFileFilter()); + try + { + outFile.createNewFile(); + } + catch (IOException e) + { + log.log(Level.SEVERE, "", e); + ADialog.error(m_WindowNo, this, "FileCannotCreate", e.getLocalizedMessage()); + return; + } + + String ext = outFile.getPath(); + // no extension + if (ext.lastIndexOf('.') == -1) + { + ADialog.error(m_WindowNo, this, "FileInvalidExtension"); + return; + } + ext = ext.substring(ext.lastIndexOf('.')+1).toLowerCase(); + log.config( "File=" + outFile.getPath() + "; Type=" + ext); + + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + if (ext.equals("pdf")) + m_reportEngine.createPDF(outFile); + else if (ext.equals("ps")) + m_reportEngine.createPS(outFile); + else if (ext.equals("xml")) + m_reportEngine.createXML(outFile); + else if (ext.equals("csv")) + m_reportEngine.createCSV(outFile, ',', m_reportEngine.getPrintFormat().getLanguage()); + else if (ext.equals("ssv")) + m_reportEngine.createCSV(outFile, ';', m_reportEngine.getPrintFormat().getLanguage()); + else if (ext.equals("txt")) + m_reportEngine.createCSV(outFile, '\t', m_reportEngine.getPrintFormat().getLanguage()); + else if (ext.equals("html") || ext.equals("htm")) + m_reportEngine.createHTML(outFile, false, m_reportEngine.getPrintFormat().getLanguage()); + else if (ext.equals("xls")) + m_reportEngine.createXLS(outFile, m_reportEngine.getPrintFormat().getLanguage()); + else + ADialog.error(m_WindowNo, this, "FileInvalidExtension"); + } + catch (Exception e) { + ADialog.error(m_WindowNo, this, "Error", e.getLocalizedMessage()); + if (CLogMgt.isLevelFinest()) + e.printStackTrace(); + } + cmd_drill(); // setCursor + } // cmd_export + + + /** + * Report Combo - Start other Report or create new one + */ + private void cmd_report() + { + KeyNamePair pp = (KeyNamePair)comboReport.getSelectedItem(); + if (pp == null) + return; + // + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + MPrintFormat pf = null; + int AD_PrintFormat_ID = pp.getKey(); + + // create new + if (AD_PrintFormat_ID == -1) + { + int AD_ReportView_ID = m_reportEngine.getPrintFormat().getAD_ReportView_ID(); + if (AD_ReportView_ID != 0) + { + String name = m_reportEngine.getName(); + int index = name.lastIndexOf('_'); + if (index != -1) + name = name.substring(0,index); + pf = MPrintFormat.createFromReportView(m_ctx, AD_ReportView_ID, name); + } + else + { + int AD_Table_ID = m_reportEngine.getPrintFormat().getAD_Table_ID(); + pf = MPrintFormat.createFromTable(m_ctx, AD_Table_ID); + } + if (pf != null) + fillComboReport(pf.get_ID()); + else + return; + } + else + pf = MPrintFormat.get (Env.getCtx(), AD_PrintFormat_ID, true); + + // Get Language from previous - thanks Gunther Hoppe + if (m_reportEngine.getPrintFormat() != null) + { + pf.setLanguage(m_reportEngine.getPrintFormat().getLanguage()); // needs to be re-set - otherwise viewer will be blank + pf.setTranslationLanguage(m_reportEngine.getPrintFormat().getLanguage()); + } + m_reportEngine.setPrintFormat(pf); + revalidate(); + + cmd_drill(); // setCursor + } // cmd_report + + /** + * Query Report + */ + private void cmd_find() + { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + int AD_Table_ID = m_reportEngine.getPrintFormat().getAD_Table_ID(); + + String title = null; + String tableName = null; + + // Get Find Tab Info + String sql = "SELECT t.AD_Tab_ID " + // ,w.Name, t.Name, w.IsDefault, t.SeqNo, ABS (tt.AD_Window_ID-t.AD_Window_ID) + + "FROM AD_Tab t" + + " INNER JOIN AD_Window w ON (t.AD_Window_ID=w.AD_Window_ID)" + + " INNER JOIN AD_Table tt ON (t.AD_Table_ID=tt.AD_Table_ID) " + + "WHERE tt.AD_Table_ID=? " + + "ORDER BY w.IsDefault DESC, t.SeqNo, ABS (tt.AD_Window_ID-t.AD_Window_ID)"; + int AD_Tab_ID = DB.getSQLValue(null, sql, AD_Table_ID); + // ASP + MClient client = MClient.get(Env.getCtx()); + String ASPFilter = ""; + if (client.isUseASP()) + ASPFilter = + " AND ( AD_Tab_ID IN ( " + // Just ASP subscribed tabs for client " + + " SELECT w.AD_Tab_ID " + + " FROM ASP_Tab w, ASP_Level l, ASP_ClientLevel cl " + + " WHERE w.ASP_Level_ID = l.ASP_Level_ID " + + " AND cl.AD_Client_ID = " + client.getAD_Client_ID() + + " AND cl.ASP_Level_ID = l.ASP_Level_ID " + + " AND w.IsActive = 'Y' " + + " AND l.IsActive = 'Y' " + + " AND cl.IsActive = 'Y' " + + " AND w.ASP_Status = 'S') " // Show + + " OR AD_Tab_ID IN ( " + // + show ASP exceptions for client + + " SELECT AD_Tab_ID " + + " FROM ASP_ClientException ce " + + " WHERE ce.AD_Client_ID = " + client.getAD_Client_ID() + + " AND ce.IsActive = 'Y' " + + " AND ce.AD_Tab_ID IS NOT NULL " + + " AND ce.AD_Field_ID IS NULL " + + " AND ce.ASP_Status = 'S') " // Show + + " ) " + + " AND AD_Tab_ID NOT IN ( " + // minus hide ASP exceptions for client + + " SELECT AD_Tab_ID " + + " FROM ASP_ClientException ce " + + " WHERE ce.AD_Client_ID = " + client.getAD_Client_ID() + + " AND ce.IsActive = 'Y' " + + " AND ce.AD_Tab_ID IS NOT NULL " + + " AND ce.AD_Field_ID IS NULL " + + " AND ce.ASP_Status = 'H')"; // Hide + // + sql = "SELECT Name, TableName FROM AD_Tab_v WHERE AD_Tab_ID=? " + ASPFilter; + if (!Env.isBaseLanguage(Env.getCtx(), "AD_Tab")) + sql = "SELECT Name, TableName FROM AD_Tab_vt WHERE AD_Tab_ID=?" + + " AND AD_Language='" + Env.getAD_Language(Env.getCtx()) + "' " + ASPFilter; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, AD_Tab_ID); + ResultSet rs = pstmt.executeQuery(); + // + if (rs.next()) + { + title = rs.getString(1); + tableName = rs.getString(2); + } + // + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + + GridField[] findFields = null; + if (tableName != null) + findFields = GridField.createFields(m_ctx, m_WindowNo, 0, AD_Tab_ID); + + if (findFields == null) // No Tab for Table exists + bFind.setEnabled(false); + else + { + Find find = new Find (this, m_WindowNo, title, + AD_Tab_ID, AD_Table_ID, tableName, "", findFields, 1); + m_reportEngine.setQuery(find.getQuery()); + find.dispose(); + find = null; + revalidate(); + } + cmd_drill(); // setCursor + } // cmd_find + + /** + * Call Customize + */ + private void cmd_customize() + { + AWindow win = new AWindow (); + new AWindowListener (win, this); // forwards Window Events + int AD_Window_ID = 240; // hardcoded + int AD_PrintFormat_ID = m_reportEngine.getPrintFormat().get_ID(); + win.initWindow(AD_Window_ID, MQuery.getEqualQuery("AD_PrintFormat_ID", AD_PrintFormat_ID)); + AEnv.addToWindowManager(win); + AEnv.showCenterScreen(win); + // see windowStateChanged for applying change + } // cmd_customize + + /** + * Window State Listener for Customize Window + * @param e event + */ + public void windowStateChanged (WindowEvent e) + { + // The Customize Window was closed + if (e.getID() == WindowEvent.WINDOW_CLOSED && m_reportEngine != null) + { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + log.info("Re-read PrintFormat"); + int AD_PrintFormat_ID = m_reportEngine.getPrintFormat().get_ID(); + Language language = m_reportEngine.getPrintFormat().getLanguage(); + MPrintFormat pf = MPrintFormat.get (Env.getCtx(), AD_PrintFormat_ID, true); + pf.setLanguage (language); // needs to be re-set - otherwise viewer will be blank + pf.setTranslationLanguage (language); + m_reportEngine.setPrintFormat(pf); + revalidate(); + cmd_drill(); // setCursor + } + } // windowStateChanged + + /** + * Set Zoom Level + */ + private void cmd_zoom() + { + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); +// m_viewPanel.setZoomLevel(comboZoom.getSelectedIndex()); + revalidate(); + cmd_drill(); // setCursor + } // cmd_zoom + + + /** + * Show Translation Dialog. + * Translate base table entry, will be copied to trl tables if not multi-lingual + */ + private void cmd_translate() + { + ArrayList list = new ArrayList(); + ValueNamePair pp = null; + String sql = "SELECT Name, AD_Language FROM AD_Language WHERE IsSystemLanguage='Y' ORDER BY 1"; + try + { + PreparedStatement pstmt = DB.prepareStatement(sql, null); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + list.add(new ValueNamePair (rs.getString(2), rs.getString(1))); + rs.close(); + pstmt.close(); + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + if (list.size() == 0) + { + ADialog.warn(m_WindowNo, this, "NoTranslation"); + return; + } + + // Dialog + String title = Msg.getMsg(Env.getCtx(), "PrintFormatTrl", true); + String message = Msg.getMsg(Env.getCtx(), "PrintFormatTrl", false); + int choice = JOptionPane.showOptionDialog + (this, message, title, + JOptionPane.OK_OPTION, JOptionPane.QUESTION_MESSAGE, null, + list.toArray(), null); + if (choice == JOptionPane.CLOSED_OPTION) + return; + // + pp = (ValueNamePair)list.get(choice); + String AD_Language = pp.getValue(); + int AD_PrintFormat_ID = m_reportEngine.getPrintFormat().get_ID(); + log.config(AD_Language + " - AD_PrintFormat_ID=" + AD_PrintFormat_ID); + StringBuffer sb = new StringBuffer(); + // English + if (Language.isBaseLanguage (AD_Language)) + { + sb.append("UPDATE AD_PrintFormatItem pfi " + + "SET Name = (SELECT e.Name FROM AD_Element e, AD_Column c" + + " WHERE e.AD_Element_ID=c.AD_Element_ID AND c.AD_Column_ID=pfi.AD_Column_ID)," + + "PrintName = (SELECT e.PrintName FROM AD_Element e, AD_Column c" + + " WHERE e.AD_Element_ID=c.AD_Element_ID AND c.AD_Column_ID=pfi.AD_Column_ID) " + + "WHERE AD_PrintFormat_ID=").append(AD_PrintFormat_ID).append( + " AND EXISTS (SELECT * FROM AD_Element e, AD_Column c" + + " WHERE e.AD_Element_ID=c.AD_Element_ID AND c.AD_Column_ID=pfi.AD_Column_ID)"); + } + else + { + AD_Language = "'" + AD_Language + "'"; + sb.append("UPDATE AD_PrintFormatItem pfi " + + "SET Name = (SELECT e.Name FROM AD_Element_Trl e, AD_Column c" + + " WHERE e.AD_Language=").append(AD_Language).append( + " AND e.AD_Element_ID=c.AD_Element_ID AND c.AD_Column_ID=pfi.AD_Column_ID), " + + "PrintName = (SELECT e.PrintName FROM AD_Element_Trl e, AD_Column c" + + " WHERE e.AD_Language=").append(AD_Language).append( + " AND e.AD_Element_ID=c.AD_Element_ID AND c.AD_Column_ID=pfi.AD_Column_ID) " + + "WHERE AD_PrintFormat_ID=").append(AD_PrintFormat_ID).append( + " AND EXISTS (SELECT * FROM AD_Element_Trl e, AD_Column c" + + " WHERE e.AD_Language=").append(AD_Language).append( + " AND e.AD_Element_ID=c.AD_Element_ID AND c.AD_Column_ID=pfi.AD_Column_ID)"); + } + int count = DB.executeUpdate(sb.toString(), null); + log.config("Count=" + count); + // + m_reportEngine.setPrintFormat(MPrintFormat.get (Env.getCtx(), AD_PrintFormat_ID, true)); + revalidate(); + } // cmd_translate + + /*************************************************************************/ + + /** + * Test + * @param args args + */ + static public void main (String[] args) + { + Login.initTest(true); + + MQuery q = new MQuery("C_Invoice"); + q.addRestriction("C_Invoice_ID", MQuery.EQUAL, new Integer(103)); + + // 102 = Invoice - 100 = Order + PrintInfo i = new PrintInfo("test", X_C_Invoice.Table_ID, 102, 0); + MPrintFormat f = MPrintFormat.get (Env.getCtx(), 102, false); + ReportEngine re = new ReportEngine(Env.getCtx(), f, q, i); + + // MPrintFormat f = new MPrintFormat(Env.getCtx(), 101); + // ReportEngine re = new ReportEngine(f, null); + + new Viewer(re); + } // main + +} // Viewer + diff --git a/db/ddlutils/postgresql/views/C_INVOICE_LINETAX_V.sql b/db/ddlutils/postgresql/views/C_INVOICE_LINETAX_V.sql new file mode 100644 index 0000000000..39ee90ec30 --- /dev/null +++ b/db/ddlutils/postgresql/views/C_INVOICE_LINETAX_V.sql @@ -0,0 +1,114 @@ +CREATE OR REPLACE VIEW C_INVOICE_LINETAX_V +(AD_CLIENT_ID, AD_ORG_ID, ISACTIVE, CREATED, CREATEDBY, + UPDATED, UPDATEDBY, AD_LANGUAGE, C_INVOICE_ID, C_INVOICELINE_ID, + C_TAX_ID, TAXAMT, LINETOTALAMT, TAXINDICATOR, LINE, + M_PRODUCT_ID, QTYINVOICED, QTYENTERED, UOMSYMBOL, NAME, + DESCRIPTION, DOCUMENTNOTE, UPC, SKU, PRODUCTVALUE, + RESOURCEDESCRIPTION, PRICELIST, PRICEENTEREDLIST, DISCOUNT, PRICEACTUAL, + PRICEENTERED, LINENETAMT, M_ATTRIBUTESETINSTANCE_ID, M_ATTRIBUTESET_ID, SERNO, + LOT, M_LOT_ID, GUARANTEEDATE, PRODUCTDESCRIPTION, IMAGEURL, + C_CAMPAIGN_ID, C_PROJECT_ID, C_ACTIVITY_ID, C_PROJECTPHASE_ID, C_PROJECTTASK_ID) +AS +SELECT il.AD_Client_ID, il.AD_Org_ID, il.IsActive, il.Created, il.CreatedBy, il.Updated, il.UpdatedBy, + 'en_US' AS AD_Language, + il.C_Invoice_ID, il.C_InvoiceLine_ID, + il.C_Tax_ID, il.TaxAmt, il.LineTotalAmt, t.TaxIndicator, + il.Line, p.M_Product_ID, + CASE WHEN il.QtyInvoiced<>0 OR il.M_Product_ID IS NOT NULL THEN il.QtyInvoiced END AS QtyInvoiced, + CASE WHEN il.QtyEntered<>0 OR il.M_Product_ID IS NOT NULL THEN il.QtyEntered END AS QtyEntered, + CASE WHEN il.QtyEntered<>0 OR il.M_Product_ID IS NOT NULL THEN uom.UOMSymbol END AS UOMSymbol, + COALESCE(c.Name,p.Name||productAttribute(il.M_AttributeSetInstance_ID), il.Description) AS Name, -- main line + CASE WHEN COALESCE(c.Name,p.Name) IS NOT NULL THEN il.Description END AS Description, -- second line + p.DocumentNote, -- third line + p.UPC, p.SKU, COALESCE(pp.VendorProductNo,p.Value) AS ProductValue, + ra.Description AS ResourceDescription, -- forth line + CASE WHEN i.IsDiscountPrinted='Y' AND il.PriceList<>0 + THEN il.PriceList END AS PriceList, + CASE WHEN i.IsDiscountPrinted='Y' AND il.PriceList<>0 AND il.QtyEntered<>0 + THEN il.PriceList*il.QtyInvoiced/il.QtyEntered END AS PriceEnteredList, + CASE WHEN i.IsDiscountPrinted='Y' AND il.PriceList>il.PriceActual AND il.PriceList<>0 + THEN (il.PriceList-il.PriceActual)/il.PriceList*100 END AS Discount, + CASE WHEN il.PriceActual<>0 OR il.M_Product_ID IS NOT NULL THEN il.PriceActual END AS PriceActual, + CASE WHEN il.PriceEntered<>0 OR il.M_Product_ID IS NOT NULL THEN il.PriceEntered END AS PriceEntered, + CASE WHEN il.LineNetAmt<>0 OR il.M_Product_ID IS NOT NULL THEN il.LineNetAmt END AS LineNetAmt, + il.M_AttributeSetInstance_ID, asi.M_AttributeSet_ID, + asi.SerNo, asi.Lot, asi.M_Lot_ID,asi.GuaranteeDate, + p.Description as ProductDescription, p.ImageURL, + il.C_Campaign_ID, il.C_Project_ID, il.C_Activity_ID, il.C_ProjectPhase_ID, il.C_ProjectTask_ID +FROM C_InvoiceLine il + INNER JOIN C_UOM uom ON (il.C_UOM_ID=uom.C_UOM_ID) + INNER JOIN C_Invoice i ON (il.C_Invoice_ID=i.C_Invoice_ID) + LEFT OUTER JOIN C_Tax t ON (il.C_Tax_ID=t.C_Tax_ID) + LEFT OUTER JOIN M_Product p ON (il.M_Product_ID=p.M_Product_ID) + LEFT OUTER JOIN C_Charge c ON (il.C_Charge_ID=c.C_Charge_ID) + LEFT OUTER JOIN C_BPartner_Product pp ON (il.M_Product_ID=pp.M_Product_ID AND i.C_BPartner_ID=pp.C_BPartner_ID) + LEFT OUTER JOIN S_ResourceAssignment ra ON (il.S_ResourceAssignment_ID=ra.S_ResourceAssignment_ID) + LEFT OUTER JOIN M_AttributeSetInstance asi ON (il.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) +UNION -- bom lines +SELECT il.AD_Client_ID, il.AD_Org_ID, il.IsActive, il.Created, il.CreatedBy, il.Updated, il.UpdatedBy, + 'en_US' AS AD_Language, + il.C_Invoice_ID, il.C_InvoiceLine_ID, + il.C_Tax_ID, il.TaxAmt, il.LineTotalAmt, t.TaxIndicator, + il.Line+(b.Line/100) AS Line, p.M_Product_ID, + il.QtyInvoiced*b.BOMQty AS QtyInvoiced, + il.QtyEntered*b.BOMQty AS QtyEntered, + uom.UOMSymbol, + p.Name, -- main + b.Description, + p.DocumentNote, p.UPC, p.SKU, p.Value AS ProductValue, + null, null, null, null, null, null, null, + il.M_AttributeSetInstance_ID, asi.M_AttributeSet_ID, asi.SerNo, asi.Lot, asi.M_Lot_ID,asi.GuaranteeDate, + p.Description as ProductDescription, p.ImageURL, + il.C_Campaign_ID, il.C_Project_ID, il.C_Activity_ID, il.C_ProjectPhase_ID, il.C_ProjectTask_ID +FROM M_Product_BOM b -- BOM lines + INNER JOIN C_InvoiceLine il ON (b.M_Product_ID=il.M_Product_ID) + INNER JOIN M_Product bp ON (bp.M_Product_ID=il.M_Product_ID -- BOM Product + AND bp.IsBOM='Y' AND bp.IsVerified='Y' AND bp.IsInvoicePrintDetails='Y') + INNER JOIN M_Product p ON (b.M_ProductBOM_ID=p.M_Product_ID) -- BOM line product + INNER JOIN C_UOM uom ON (p.C_UOM_ID=uom.C_UOM_ID) + LEFT OUTER JOIN C_Tax t ON (il.C_Tax_ID=t.C_Tax_ID) + LEFT OUTER JOIN M_AttributeSetInstance asi ON (il.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) +UNION -- comment lines +SELECT il.AD_Client_ID, il.AD_Org_ID, il.IsActive, il.Created, il.CreatedBy, il.Updated, il.UpdatedBy, + 'en_US', il.C_Invoice_ID, il.C_InvoiceLine_ID, + null, null, null, null, + il.Line, null, + null, null, null, + il.Description, + null, null, null, null, null, null, + null, null, null, null, null, null, + null, null, null, null, null, null, null, null, + null, null, null, null, null +FROM C_InvoiceLine il +WHERE il.C_UOM_ID IS NULL +UNION -- empty line +SELECT AD_Client_ID, AD_Org_ID, IsActive, Created, CreatedBy, Updated, UpdatedBy, + 'en_US', C_Invoice_ID, null, + null, null, null, null, + 9998, null, + null, null, null, + null, + null, null, null, null, null, null, + null, null, null, null, null, null, + null, null, null, null, null, null, null, null, + null, null, null, null, null +FROM C_Invoice +UNION -- tax lines +SELECT it.AD_Client_ID, it.AD_Org_ID, it.IsActive, it.Created, it.CreatedBy, it.Updated, it.UpdatedBy, + 'en_US', it.C_Invoice_ID, null, + it.C_Tax_ID, null, null, t.TaxIndicator, + 9999, null, + null, null, null, + t.Name, + null, null, null, null, null, null, + null, null, null, + CASE WHEN it.IsTaxIncluded='Y' THEN it.TaxAmt ELSE it.TaxBaseAmt END, + CASE WHEN it.IsTaxIncluded='Y' THEN it.TaxAmt ELSE it.TaxBaseAmt END, + CASE WHEN it.IsTaxIncluded='Y' THEN NULL ELSE it.TaxAmt END, + null, null, null, null, null, null, null, null, + null, null, null, null, null +FROM C_InvoiceTax it + INNER JOIN C_Tax t ON (it.C_Tax_ID=t.C_Tax_ID); + + + diff --git a/db/ddlutils/postgresql/views/C_INVOICE_LINETAX_VT.sql b/db/ddlutils/postgresql/views/C_INVOICE_LINETAX_VT.sql new file mode 100644 index 0000000000..84790d1450 --- /dev/null +++ b/db/ddlutils/postgresql/views/C_INVOICE_LINETAX_VT.sql @@ -0,0 +1,117 @@ +CREATE OR REPLACE VIEW C_INVOICE_LINETAX_VT +(AD_CLIENT_ID, AD_ORG_ID, ISACTIVE, CREATED, CREATEDBY, + UPDATED, UPDATEDBY, AD_LANGUAGE, C_INVOICE_ID, C_INVOICELINE_ID, + C_TAX_ID, TAXAMT, LINETOTALAMT, TAXINDICATOR, LINE, + M_PRODUCT_ID, QTYINVOICED, QTYENTERED, UOMSYMBOL, NAME, + DESCRIPTION, DOCUMENTNOTE, UPC, SKU, PRODUCTVALUE, + RESOURCEDESCRIPTION, PRICELIST, PRICEENTEREDLIST, DISCOUNT, PRICEACTUAL, + PRICEENTERED, LINENETAMT, M_ATTRIBUTESETINSTANCE_ID, M_ATTRIBUTESET_ID, SERNO, + LOT, M_LOT_ID, GUARANTEEDATE, PRODUCTDESCRIPTION, IMAGEURL, + C_CAMPAIGN_ID, C_PROJECT_ID, C_ACTIVITY_ID, C_PROJECTPHASE_ID, C_PROJECTTASK_ID) +AS +SELECT il.AD_Client_ID, il.AD_Org_ID, il.IsActive, il.Created, il.CreatedBy, il.Updated, il.UpdatedBy, + uom.AD_Language, + il.C_Invoice_ID, il.C_InvoiceLine_ID, + il.C_Tax_ID, il.TaxAmt, il.LineTotalAmt, t.TaxIndicator, + il.Line, p.M_Product_ID, + CASE WHEN il.QtyInvoiced<>0 OR il.M_Product_ID IS NOT NULL THEN il.QtyInvoiced END AS QtyInvoiced, + CASE WHEN il.QtyEntered<>0 OR il.M_Product_ID IS NOT NULL THEN il.QtyEntered END AS QtyEntered, + CASE WHEN il.QtyEntered<>0 OR il.M_Product_ID IS NOT NULL THEN uom.UOMSymbol END AS UOMSymbol, + COALESCE(c.Name,COALESCE(pt.Name,p.Name)||productAttribute(il.M_AttributeSetInstance_ID), il.Description) AS Name, -- main line + CASE WHEN COALESCE(c.Name,pt.Name,p.Name) IS NOT NULL THEN il.Description END AS Description, -- second line + COALESCE(pt.DocumentNote,p.DocumentNote) AS DocumentNote, -- third line + p.UPC, p.SKU, COALESCE(pp.VendorProductNo,p.Value) AS ProductValue, + ra.Description AS ResourceDescription, -- forth line + CASE WHEN i.IsDiscountPrinted='Y' AND il.PriceList<>0 + THEN il.PriceList END AS PriceList, + CASE WHEN i.IsDiscountPrinted='Y' AND il.PriceList<>0 AND il.QtyEntered<>0 + THEN il.PriceList*il.QtyInvoiced/il.QtyEntered END AS PriceEnteredList, + CASE WHEN i.IsDiscountPrinted='Y' AND il.PriceList>il.PriceActual AND il.PriceList<>0 + THEN (il.PriceList-il.PriceActual)/il.PriceList*100 END AS Discount, + CASE WHEN il.PriceActual<>0 OR il.M_Product_ID IS NOT NULL THEN il.PriceActual END AS PriceActual, + CASE WHEN il.PriceEntered<>0 OR il.M_Product_ID IS NOT NULL THEN il.PriceEntered END AS PriceEntered, + CASE WHEN il.LineNetAmt<>0 OR il.M_Product_ID IS NOT NULL THEN il.LineNetAmt END AS LineNetAmt, + il.M_AttributeSetInstance_ID, asi.M_AttributeSet_ID, asi.SerNo, asi.Lot, asi.M_Lot_ID,asi.GuaranteeDate, + pt.Description as ProductDescription, p.ImageURL, + il.C_Campaign_ID, il.C_Project_ID, il.C_Activity_ID, il.C_ProjectPhase_ID, il.C_ProjectTask_ID +FROM C_InvoiceLine il + INNER JOIN C_UOM_Trl uom ON (il.C_UOM_ID=uom.C_UOM_ID) + INNER JOIN C_Invoice i ON (il.C_Invoice_ID=i.C_Invoice_ID) + LEFT OUTER JOIN C_Tax_Trl t ON (il.C_Tax_ID=t.C_Tax_ID AND uom.AD_Language=t.AD_Language) + LEFT OUTER JOIN M_Product p ON (il.M_Product_ID=p.M_Product_ID) + LEFT OUTER JOIN C_Charge c ON (il.C_Charge_ID=c.C_Charge_ID) + LEFT OUTER JOIN C_BPartner_Product pp ON (il.M_Product_ID=pp.M_Product_ID AND i.C_BPartner_ID=pp.C_BPartner_ID) + LEFT OUTER JOIN M_Product_Trl pt ON (il.M_Product_ID=pt.M_Product_ID AND uom.AD_Language=pt.AD_Language) + LEFT OUTER JOIN S_ResourceAssignment ra ON (il.S_ResourceAssignment_ID=ra.S_ResourceAssignment_ID) + LEFT OUTER JOIN M_AttributeSetInstance asi ON (il.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) +UNION -- bom lines +SELECT il.AD_Client_ID, il.AD_Org_ID, il.IsActive, il.Created, il.CreatedBy, il.Updated, il.UpdatedBy, + uom.AD_Language, + il.C_Invoice_ID, il.C_InvoiceLine_ID, + il.C_Tax_ID, il.TaxAmt, il.LineTotalAmt, t.TaxIndicator, + il.Line+(b.Line/100) AS Line, p.M_Product_ID, + il.QtyInvoiced*b.BOMQty AS QtyInvoiced, + il.QtyEntered*b.BOMQty AS QtyEntered, + uom.UOMSymbol, + COALESCE(pt.Name,p.Name) AS Name, -- main + b.Description, + COALESCE(pt.DocumentNote,p.DocumentNote) AS DocumentNote, p.UPC, p.SKU, p.Value AS ProductValue, + null, null, null, null, null, null, null, + il.M_AttributeSetInstance_ID, asi.M_AttributeSet_ID, asi.SerNo, asi.Lot, asi.M_Lot_ID,asi.GuaranteeDate, + pt.Description as ProductDescription, p.ImageURL, + il.C_Campaign_ID, il.C_Project_ID, il.C_Activity_ID, il.C_ProjectPhase_ID, il.C_ProjectTask_ID +FROM M_Product_BOM b -- BOM lines + INNER JOIN C_InvoiceLine il ON (b.M_Product_ID=il.M_Product_ID) + INNER JOIN M_Product bp ON (bp.M_Product_ID=il.M_Product_ID -- BOM Product + AND bp.IsBOM='Y' AND bp.IsVerified='Y' AND bp.IsInvoicePrintDetails='Y') + INNER JOIN M_Product p ON (b.M_ProductBOM_ID=p.M_Product_ID) -- BOM line product + INNER JOIN C_UOM_Trl uom ON (p.C_UOM_ID=uom.C_UOM_ID) + INNER JOIN M_Product_Trl pt ON (b.M_ProductBOM_ID=pt.M_Product_ID AND uom.AD_Language=pt.AD_Language) + LEFT OUTER JOIN C_Tax t ON (il.C_Tax_ID=t.C_Tax_ID) + LEFT OUTER JOIN M_AttributeSetInstance asi ON (il.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) +UNION -- comment line +SELECT il.AD_Client_ID, il.AD_Org_ID, il.IsActive, il.Created, il.CreatedBy, il.Updated, il.UpdatedBy, + l.AD_Language, il.C_Invoice_ID, il.C_InvoiceLine_ID, + null, null, null, null, + il.Line, null, + null, null, null, + il.Description, + null, null, null, null, null, null, + null, null, null, null, null, null, + null, null, null, null, null, null, null, null, + null,null,null,null,null +FROM C_InvoiceLine il, AD_Language l +WHERE il.C_UOM_ID IS NULL + AND l.IsBaseLanguage='N' AND l.IsSystemLanguage='Y' +UNION -- empty line +SELECT i.AD_Client_ID, i.AD_Org_ID, i.IsActive, i.Created, i.CreatedBy, i.Updated, i.UpdatedBy, + AD_Language, i.C_Invoice_ID, null, + null, null, null, null, + 9998, null, + null, null, null, + null, + null, null, null, null, null, null, + null, null, null, null, null, null, + null, null, null, null, null, null, null, null, + null,null,null,null,null +FROM C_Invoice i, AD_Language l +WHERE l.IsBaseLanguage='N' AND l.IsSystemLanguage='Y' +UNION -- tax lines +SELECT it.AD_Client_ID, it.AD_Org_ID, it.IsActive, it.Created, it.CreatedBy, it.Updated, it.UpdatedBy, + t.AD_Language, it.C_Invoice_ID, null, + it.C_Tax_ID, null, null, t.TaxIndicator, + 9999, null, + null, null, null, + t.Name, + null, null, null, null, null, null, + null, null, null, + CASE WHEN it.IsTaxIncluded='Y' THEN it.TaxAmt ELSE it.TaxBaseAmt END, + CASE WHEN it.IsTaxIncluded='Y' THEN it.TaxAmt ELSE it.TaxBaseAmt END, + CASE WHEN it.IsTaxIncluded='Y' THEN NULL ELSE it.TaxAmt END, + null, null, null, null, null, null, null, null, + null,null,null,null,null +FROM C_InvoiceTax it + INNER JOIN C_Tax_Trl t ON (it.C_Tax_ID=t.C_Tax_ID); + + + diff --git a/db/ddlutils/postgresql/views/C_ORDER_LINETAX_V.sql b/db/ddlutils/postgresql/views/C_ORDER_LINETAX_V.sql new file mode 100644 index 0000000000..2dbe7f8464 --- /dev/null +++ b/db/ddlutils/postgresql/views/C_ORDER_LINETAX_V.sql @@ -0,0 +1,93 @@ +CREATE OR REPLACE VIEW C_ORDER_LINETAX_V +(AD_CLIENT_ID, AD_ORG_ID, ISACTIVE, CREATED, CREATEDBY, + UPDATED, UPDATEDBY, AD_LANGUAGE, C_ORDER_ID, C_ORDERLINE_ID, + C_TAX_ID, TAXINDICATOR, C_BPARTNER_ID, C_BPARTNER_LOCATION_ID, BPNAME, + C_LOCATION_ID, LINE, M_PRODUCT_ID, QTYORDERED, QTYENTERED, + UOMSYMBOL, NAME, DESCRIPTION, DOCUMENTNOTE, UPC, + SKU, PRODUCTVALUE, RESOURCEDESCRIPTION, PRICELIST, PRICEENTEREDLIST, + DISCOUNT, PRICEACTUAL, PRICEENTERED, LINENETAMT, PRODUCTDESCRIPTION, + IMAGEURL, C_CAMPAIGN_ID, C_PROJECT_ID, C_ACTIVITY_ID, C_PROJECTPHASE_ID, + C_PROJECTTASK_ID) +AS +SELECT ol.AD_Client_ID, ol.AD_Org_ID, ol.IsActive, ol.Created, ol.CreatedBy, ol.Updated, ol.UpdatedBy, + 'en_US' AS AD_Language, + ol.C_Order_ID, ol.C_OrderLine_ID, ol.C_Tax_ID, t.TaxIndicator, + ol.C_BPartner_ID, ol.C_BPartner_Location_ID, bp.Name AS BPName, bpl.C_Location_ID, + ol.Line, p.M_Product_ID, + CASE WHEN ol.QtyOrdered<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.QtyOrdered END AS QtyOrdered, + CASE WHEN ol.QtyEntered<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.QtyEntered END AS QtyEntered, + CASE WHEN ol.QtyEntered<>0 OR ol.M_Product_ID IS NOT NULL THEN uom.UOMSymbol END AS UOMSymbol, + COALESCE(c.Name,p.Name||productAttribute(ol.M_AttributeSetInstance_ID), ol.Description) AS Name, -- main line + CASE WHEN COALESCE(c.Name,p.Name) IS NOT NULL THEN ol.Description END AS Description, -- second line + p.DocumentNote, -- third line + p.UPC, p.SKU, COALESCE(pp.VendorProductNo,p.Value) AS ProductValue, + ra.Description AS ResourceDescription, -- forth line + CASE WHEN i.IsDiscountPrinted='Y' AND ol.PriceList<>0 + THEN ol.PriceList END AS PriceList, + CASE WHEN i.IsDiscountPrinted='Y' AND ol.PriceList<>0 AND ol.QtyEntered<>0 + THEN ol.PriceList*ol.QtyOrdered/ol.QtyEntered END AS PriceEnteredList, + CASE WHEN i.IsDiscountPrinted='Y' AND ol.PriceList>ol.PriceActual AND ol.PriceList<>0 + THEN (ol.PriceList-ol.PriceActual)/ol.PriceList*100 END AS Discount, + CASE WHEN ol.PriceActual<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.PriceActual END AS PriceActual, + CASE WHEN ol.PriceEntered<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.PriceEntered END AS PriceEntered, + CASE WHEN ol.LineNetAmt<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.LineNetAmt END AS LineNetAmt, + p.Description as ProductDescription, p.ImageURL, + ol.C_Campaign_ID, ol.C_Project_ID, ol.C_Activity_ID, ol.C_ProjectPhase_ID, ol.C_ProjectTask_ID +FROM C_OrderLine ol + INNER JOIN C_UOM uom ON (ol.C_UOM_ID=uom.C_UOM_ID) + INNER JOIN C_Order i ON (ol.C_Order_ID=i.C_Order_ID) + LEFT OUTER JOIN M_Product p ON (ol.M_Product_ID=p.M_Product_ID) + LEFT OUTER JOIN S_ResourceAssignment ra ON (ol.S_ResourceAssignment_ID=ra.S_ResourceAssignment_ID) + LEFT OUTER JOIN C_Charge c ON (ol.C_Charge_ID=c.C_Charge_ID) + LEFT OUTER JOIN C_BPartner_Product pp ON (ol.M_Product_ID=pp.M_Product_ID AND i.C_BPartner_ID=pp.C_BPartner_ID) + INNER JOIN C_BPartner bp ON (ol.C_BPartner_ID=bp.C_BPartner_ID) + INNER JOIN C_BPartner_Location bpl ON (ol.C_BPartner_Location_ID=bpl.C_BPartner_Location_ID) + LEFT OUTER JOIN C_Tax t ON (ol.C_Tax_ID=t.C_Tax_ID) +UNION +SELECT ol.AD_Client_ID, ol.AD_Org_ID, ol.IsActive, ol.Created, ol.CreatedBy, ol.Updated, ol.UpdatedBy, + 'en_US' AS AD_Language, + ol.C_Order_ID, ol.C_OrderLine_ID, ol.C_Tax_ID, null, + null, null, null, null, + ol.Line+(b.Line/100) AS Line, p.M_Product_ID, + ol.QtyOrdered*b.BOMQty AS QtyInvoiced, ol.QtyEntered*b.BOMQty AS QtyEntered, uom.UOMSymbol, + p.Name, -- main + b.Description, + p.DocumentNote, p.UPC, p.SKU, p.Value AS ProductValue, + null, null, null, null, null, null, null, p.Description as ProductDescription, p.ImageURL, + ol.C_Campaign_ID, ol.C_Project_ID, ol.C_Activity_ID, ol.C_ProjectPhase_ID, ol.C_ProjectTask_ID +FROM M_Product_BOM b -- BOM lines + INNER JOIN C_OrderLine ol ON (b.M_Product_ID=ol.M_Product_ID) + INNER JOIN M_Product bp ON (bp.M_Product_ID=ol.M_Product_ID -- BOM Product + AND bp.IsBOM='Y' AND bp.IsVerified='Y' AND bp.IsInvoicePrintDetails='Y') + INNER JOIN M_Product p ON (b.M_ProductBOM_ID=p.M_Product_ID) -- BOM line product + INNER JOIN C_UOM uom ON (p.C_UOM_ID=uom.C_UOM_ID) +UNION +SELECT AD_Client_ID, AD_Org_ID, IsActive, Created, CreatedBy, Updated, UpdatedBy, + 'en_US', C_Order_ID, null, null, null, + null, + null, null, null, + null, null, null, null, + null, null, + null, null, null, null, null, null, + null, null, null, null, null, null, null, null, + null,null,null,null,null +FROM C_Order +UNION +SELECT ot.AD_Client_ID, ot.AD_Org_ID, ot.IsActive, ot.Created, ot.CreatedBy, ot.Updated, ot.UpdatedBy, + 'en_US', ot.C_Order_ID, null, ot.C_Tax_ID, t.TaxIndicator, + null, null, null, null, + null, null, + null, null, null, + t.Name, + null, null, null, null, null, null, + null, null, null, + CASE WHEN ot.IsTaxIncluded='Y' THEN ot.TaxAmt ELSE ot.TaxBaseAmt END, + CASE WHEN ot.IsTaxIncluded='Y' THEN ot.TaxAmt ELSE ot.TaxBaseAmt END, + CASE WHEN ot.IsTaxIncluded='Y' THEN NULL ELSE ot.TaxAmt END, + null, null, + null,null,null,null,null +FROM C_OrderTax ot + INNER JOIN C_Tax t ON (ot.C_Tax_ID=t.C_Tax_ID); + + + diff --git a/db/ddlutils/postgresql/views/C_ORDER_LINETAX_VT.sql b/db/ddlutils/postgresql/views/C_ORDER_LINETAX_VT.sql new file mode 100644 index 0000000000..9d0137e281 --- /dev/null +++ b/db/ddlutils/postgresql/views/C_ORDER_LINETAX_VT.sql @@ -0,0 +1,96 @@ +CREATE OR REPLACE VIEW C_ORDER_LINETAX_VT +(AD_CLIENT_ID, AD_ORG_ID, ISACTIVE, CREATED, CREATEDBY, + UPDATED, UPDATEDBY, AD_LANGUAGE, C_ORDER_ID, C_ORDERLINE_ID, + C_TAX_ID, TAXINDICATOR, C_BPARTNER_ID, C_BPARTNER_LOCATION_ID, BPNAME, + C_LOCATION_ID, LINE, M_PRODUCT_ID, QTYORDERED, QTYENTERED, + UOMSYMBOL, NAME, DESCRIPTION, DOCUMENTNOTE, UPC, + SKU, PRODUCTVALUE, RESOURCEDESCRIPTION, PRICELIST, PRICEENTEREDLIST, + DISCOUNT, PRICEACTUAL, PRICEENTERED, LINENETAMT, PRODUCTDESCRIPTION, + IMAGEURL, C_CAMPAIGN_ID, C_PROJECT_ID, C_ACTIVITY_ID, C_PROJECTPHASE_ID, + C_PROJECTTASK_ID) +AS +SELECT ol.AD_Client_ID, ol.AD_Org_ID, ol.IsActive, ol.Created, ol.CreatedBy, ol.Updated, ol.UpdatedBy, + uom.AD_Language, + ol.C_Order_ID, ol.C_OrderLine_ID, ol.C_Tax_ID, t.TaxIndicator, + ol.C_BPartner_ID, ol.C_BPartner_Location_ID, bp.Name AS BPName, bpl.C_Location_ID, + ol.Line, p.M_Product_ID, + CASE WHEN ol.QtyOrdered<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.QtyOrdered END AS QtyOrdered, + CASE WHEN ol.QtyEntered<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.QtyEntered END AS QtyEntered, + CASE WHEN ol.QtyEntered<>0 OR ol.M_Product_ID IS NOT NULL THEN uom.UOMSymbol END AS UOMSymbol, + COALESCE(pt.Name, c.Name,p.Name||productAttribute(ol.M_AttributeSetInstance_ID), ol.Description) AS Name, -- main line + CASE WHEN COALESCE(c.Name,pt.Name, p.Name) IS NOT NULL THEN ol.Description END AS Description, -- second line + COALESCE(pt.DocumentNote, p.DocumentNote) AS DocumentNote, -- third line + p.UPC, p.SKU, COALESCE(pp.VendorProductNo,p.Value) AS ProductValue, + ra.Description AS ResourceDescription, -- forth line + CASE WHEN i.IsDiscountPrinted='Y' AND ol.PriceList<>0 + THEN ol.PriceList END AS PriceList, + CASE WHEN i.IsDiscountPrinted='Y' AND ol.PriceList<>0 AND ol.QtyEntered<>0 + THEN ol.PriceList*ol.QtyOrdered/ol.QtyEntered END AS PriceEnteredList, + CASE WHEN i.IsDiscountPrinted='Y' AND ol.PriceList>ol.PriceActual AND ol.PriceList<>0 + THEN (ol.PriceList-ol.PriceActual)/ol.PriceList*100 END AS Discount, + CASE WHEN ol.PriceActual<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.PriceActual END AS PriceActual, + CASE WHEN ol.PriceEntered<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.PriceEntered END AS PriceEntered, + CASE WHEN ol.LineNetAmt<>0 OR ol.M_Product_ID IS NOT NULL THEN ol.LineNetAmt END AS LineNetAmt, + pt.Description as ProductDescription, p.ImageURL, + ol.C_Campaign_ID, ol.C_Project_ID, ol.C_Activity_ID, ol.C_ProjectPhase_ID, ol.C_ProjectTask_ID +FROM C_OrderLine ol + INNER JOIN C_UOM_Trl uom ON (ol.C_UOM_ID=uom.C_UOM_ID) + INNER JOIN C_Order i ON (ol.C_Order_ID=i.C_Order_ID) + LEFT OUTER JOIN M_Product p ON (ol.M_Product_ID=p.M_Product_ID) + LEFT OUTER JOIN M_Product_Trl pt ON (ol.M_Product_ID=pt.M_Product_ID AND uom.AD_Language=pt.AD_Language) + LEFT OUTER JOIN S_ResourceAssignment ra ON (ol.S_ResourceAssignment_ID=ra.S_ResourceAssignment_ID) + LEFT OUTER JOIN C_Charge c ON (ol.C_Charge_ID=c.C_Charge_ID) + LEFT OUTER JOIN C_BPartner_Product pp ON (ol.M_Product_ID=pp.M_Product_ID AND i.C_BPartner_ID=pp.C_BPartner_ID) + INNER JOIN C_BPartner bp ON (ol.C_BPartner_ID=bp.C_BPartner_ID) + INNER JOIN C_BPartner_Location bpl ON (ol.C_BPartner_Location_ID=bpl.C_BPartner_Location_ID) + LEFT OUTER JOIN C_Tax_Trl t ON (ol.C_Tax_ID=t.C_Tax_ID AND uom.AD_Language=t.AD_Language) +UNION +SELECT ol.AD_Client_ID, ol.AD_Org_ID, ol.IsActive, ol.Created, ol.CreatedBy, ol.Updated, ol.UpdatedBy, + uom.AD_Language, + ol.C_Order_ID, ol.C_OrderLine_ID, ol.C_Tax_ID, null, + null, null, null, null, + ol.Line+(b.Line/100) AS Line, p.M_Product_ID, + ol.QtyOrdered*b.BOMQty AS QtyInvoiced, ol.QtyEntered*b.BOMQty AS QtyEntered, uom.UOMSymbol, + COALESCE(pt.Name, p.Name) AS Name, -- main + b.Description, + COALESCE(pt.DocumentNote, p.DocumentNote) AS DocumentNote, p.UPC, p.SKU, p.Value AS ProductValue, + null, null, null, null, null, null, null, pt.Description AS ProductDescription, p.ImageURL, + ol.C_Campaign_ID, ol.C_Project_ID, ol.C_Activity_ID, ol.C_ProjectPhase_ID, ol.C_ProjectTask_ID +FROM M_Product_BOM b -- BOM lines + INNER JOIN C_OrderLine ol ON (b.M_Product_ID=ol.M_Product_ID) + INNER JOIN M_Product bp ON (bp.M_Product_ID=ol.M_Product_ID -- BOM Product + AND bp.IsBOM='Y' AND bp.IsVerified='Y' AND bp.IsInvoicePrintDetails='Y') + INNER JOIN M_Product p ON (b.M_ProductBOM_ID=p.M_Product_ID) -- BOM line product + INNER JOIN C_UOM_Trl uom ON (p.C_UOM_ID=uom.C_UOM_ID) + INNER JOIN M_Product_Trl pt ON (b.M_ProductBOM_ID=pt.M_Product_ID AND uom.AD_Language=pt.AD_Language) +UNION +SELECT o.AD_Client_ID, o.AD_Org_ID, o.IsActive, o.Created, o.CreatedBy, o.Updated, o.UpdatedBy, + l.AD_Language, o.C_Order_ID, null, null, null, + null, + null, null, null, + null, null, null, null, + null, null, + null, null, null, null, null, null, + null, null, null, null, null, null, null, null, + null,null,null,null,null +FROM C_Order o, AD_Language l +WHERE l.IsBaseLanguage='N' AND l.IsSystemLanguage='Y' +UNION +SELECT ot.AD_Client_ID, ot.AD_Org_ID, ot.IsActive, ot.Created, ot.CreatedBy, ot.Updated, ot.UpdatedBy, + t.AD_Language, ot.C_Order_ID, null, ot.C_Tax_ID, t.TaxIndicator, + null, null, null, null, + null, null, + null, null, null, + t.Name, + null, null, null, null, null, null, + null, null, null, + CASE WHEN ot.IsTaxIncluded='Y' THEN ot.TaxAmt ELSE ot.TaxBaseAmt END, + CASE WHEN ot.IsTaxIncluded='Y' THEN ot.TaxAmt ELSE ot.TaxBaseAmt END, + CASE WHEN ot.IsTaxIncluded='Y' THEN NULL ELSE ot.TaxAmt END, + null, null, + null,null,null,null,null +FROM C_OrderTax ot + INNER JOIN C_Tax_Trl t ON (ot.C_Tax_ID=t.C_Tax_ID); + + + diff --git a/db/ddlutils/postgresql/views/M_INOUT_LINE_V.sql b/db/ddlutils/postgresql/views/M_INOUT_LINE_V.sql new file mode 100644 index 0000000000..f17173e114 --- /dev/null +++ b/db/ddlutils/postgresql/views/M_INOUT_LINE_V.sql @@ -0,0 +1,61 @@ +CREATE OR REPLACE VIEW M_INOUT_LINE_V +(AD_CLIENT_ID, AD_ORG_ID, ISACTIVE, CREATED, CREATEDBY, + UPDATED, UPDATEDBY, AD_LANGUAGE, M_INOUT_ID, M_INOUTLINE_ID, + LINE, M_PRODUCT_ID, MOVEMENTQTY, QTYENTERED, UOMSYMBOL, + QTYORDERED, QTYDELIVERED, QTYBACKORDERED, NAME, DESCRIPTION, + DOCUMENTNOTE, UPC, SKU, PRODUCTVALUE, M_LOCATOR_ID, + M_WAREHOUSE_ID, X, Y, Z, M_ATTRIBUTESETINSTANCE_ID, + M_ATTRIBUTESET_ID, SERNO, LOT, M_LOT_ID, GUARANTEEDATE, + PRODUCTDESCRIPTION, IMAGEURL, C_CAMPAIGN_ID, C_PROJECT_ID, C_ACTIVITY_ID, + C_PROJECTPHASE_ID, C_PROJECTTASK_ID) +AS +SELECT iol.AD_Client_ID, iol.AD_Org_ID, iol.IsActive, iol.Created, iol.CreatedBy, iol.Updated, iol.UpdatedBy, + 'en_US' AS AD_Language, + iol.M_InOut_ID, iol.M_InOutLine_ID, + iol.Line, p.M_Product_ID, + CASE WHEN iol.MovementQty<>0 OR iol.M_Product_ID IS NOT NULL THEN iol.MovementQty END AS MovementQty, + CASE WHEN iol.QtyEntered<>0 OR iol.M_Product_ID IS NOT NULL THEN iol.QtyEntered END AS QtyEntered, + CASE WHEN iol.MovementQty<>0 OR iol.M_Product_ID IS NOT NULL THEN uom.UOMSymbol END AS UOMSymbol, + ol.QtyOrdered, ol.QtyDelivered, + CASE WHEN iol.MovementQty<>0 OR iol.M_Product_ID IS NOT NULL THEN ol.QtyOrdered-ol.QtyDelivered END AS QtyBackOrdered, + COALESCE(p.Name||productAttribute(iol.M_AttributeSetInstance_ID), c.Name, iol.Description) AS Name, -- main line + CASE WHEN COALESCE(c.Name,p.Name) IS NOT NULL THEN iol.Description END AS Description, -- second line + p.DocumentNote, -- third line + p.UPC, p.SKU, p.Value AS ProductValue, + iol.M_Locator_ID, l.M_Warehouse_ID, l.X, l.Y, l.Z, + iol.M_AttributeSetInstance_ID, asi.M_AttributeSet_ID, asi.SerNo, asi.Lot, asi.M_Lot_ID,asi.GuaranteeDate, + p.Description AS ProductDescription, p.ImageURL, + iol.C_Campaign_ID, iol.C_Project_ID, iol.C_Activity_ID, iol.C_ProjectPhase_ID, iol.C_ProjectTask_ID +FROM M_InOutLine iol + INNER JOIN C_UOM uom ON (iol.C_UOM_ID=uom.C_UOM_ID) + LEFT OUTER JOIN M_Product p ON (iol.M_Product_ID=p.M_Product_ID) + LEFT OUTER JOIN M_AttributeSetInstance asi ON (iol.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) + LEFT OUTER JOIN M_Locator l ON (iol.M_Locator_ID=l.M_Locator_ID) + LEFT OUTER JOIN C_OrderLine ol ON (iol.C_OrderLine_ID=ol.C_OrderLine_ID) + LEFT OUTER JOIN C_Charge c ON (iol.C_Charge_ID=c.C_Charge_ID) +UNION -- BOM lines +SELECT iol.AD_Client_ID, iol.AD_Org_ID, iol.IsActive, iol.Created, iol.CreatedBy, iol.Updated, iol.UpdatedBy, + 'en_US' AS AD_Language, + iol.M_InOut_ID, iol.M_InOutLine_ID, + iol.Line+(b.Line/100) AS Line, p.M_Product_ID, + iol.MovementQty*b.BOMQty AS QtyInvoiced, iol.QtyEntered*b.BOMQty AS QtyEntered, uom.UOMSymbol, + null, null, null, + p.Name, -- main line + b.Description, -- second line + p.DocumentNote, -- third line + p.UPC, p.SKU, p.Value AS ProductValue, + iol.M_Locator_ID, l.M_Warehouse_ID, l.X, l.Y, l.Z, + iol.M_AttributeSetInstance_ID, asi.M_AttributeSet_ID, asi.SerNo, asi.Lot, asi.M_Lot_ID,asi.GuaranteeDate, + p.Description AS ProductDescription, p.ImageURL, + iol.C_Campaign_ID, iol.C_Project_ID, iol.C_Activity_ID, iol.C_ProjectPhase_ID, iol.C_ProjectTask_ID +FROM M_Product_BOM b -- BOM lines + INNER JOIN M_InOutLine iol ON (b.M_Product_ID=iol.M_Product_ID) + INNER JOIN M_Product bp ON (bp.M_Product_ID=iol.M_Product_ID -- BOM Product + AND bp.IsBOM='Y' AND bp.IsVerified='Y' AND bp.IsPickListPrintDetails='Y') + INNER JOIN M_Product p ON (b.M_ProductBOM_ID=p.M_Product_ID) -- BOM line product + INNER JOIN C_UOM uom ON (p.C_UOM_ID=uom.C_UOM_ID) + LEFT OUTER JOIN M_AttributeSetInstance asi ON (iol.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) + LEFT OUTER JOIN M_Locator l ON (iol.M_Locator_ID=l.M_Locator_ID); + + + diff --git a/db/ddlutils/postgresql/views/M_INOUT_LINE_VT.sql b/db/ddlutils/postgresql/views/M_INOUT_LINE_VT.sql new file mode 100644 index 0000000000..672c7e89d5 --- /dev/null +++ b/db/ddlutils/postgresql/views/M_INOUT_LINE_VT.sql @@ -0,0 +1,63 @@ +CREATE OR REPLACE VIEW M_INOUT_LINE_VT +(AD_CLIENT_ID, AD_ORG_ID, ISACTIVE, CREATED, CREATEDBY, + UPDATED, UPDATEDBY, AD_LANGUAGE, M_INOUT_ID, M_INOUTLINE_ID, + LINE, M_PRODUCT_ID, MOVEMENTQTY, QTYENTERED, UOMSYMBOL, + QTYORDERED, QTYDELIVERED, QTYBACKORDERED, NAME, DESCRIPTION, + DOCUMENTNOTE, UPC, SKU, PRODUCTVALUE, M_LOCATOR_ID, + M_WAREHOUSE_ID, X, Y, Z, M_ATTRIBUTESETINSTANCE_ID, + M_ATTRIBUTESET_ID, SERNO, LOT, M_LOT_ID, GUARANTEEDATE, + PRODUCTDESCRIPTION, IMAGEURL, C_CAMPAIGN_ID, C_PROJECT_ID, C_ACTIVITY_ID, + C_PROJECTPHASE_ID, C_PROJECTTASK_ID) +AS +SELECT iol.AD_Client_ID, iol.AD_Org_ID, iol.IsActive, iol.Created, iol.CreatedBy, iol.Updated, iol.UpdatedBy, + uom.AD_Language, + iol.M_InOut_ID, iol.M_InOutLine_ID, + iol.Line, p.M_Product_ID, + CASE WHEN iol.MovementQty<>0 OR iol.M_Product_ID IS NOT NULL THEN iol.MovementQty END AS MovementQty, + CASE WHEN iol.QtyEntered<>0 OR iol.M_Product_ID IS NOT NULL THEN iol.QtyEntered END AS QtyEntered, + CASE WHEN iol.MovementQty<>0 OR iol.M_Product_ID IS NOT NULL THEN uom.UOMSymbol END AS UOMSymbol, + ol.QtyOrdered, ol.QtyDelivered, + CASE WHEN iol.MovementQty<>0 OR iol.M_Product_ID IS NOT NULL THEN ol.QtyOrdered-ol.QtyDelivered END AS QtyBackOrdered, + COALESCE(COALESCE(pt.Name,p.Name)||productAttribute(iol.M_AttributeSetInstance_ID), c.Name, iol.Description) AS Name, -- main line + CASE WHEN COALESCE(pt.Name,p.Name,c.Name) IS NOT NULL THEN iol.Description END AS Description, -- second line + COALESCE(pt.DocumentNote, p.DocumentNote) AS DocumentNote, -- third line + p.UPC, p.SKU, p.Value AS ProductValue, + iol.M_Locator_ID, l.M_Warehouse_ID, l.X, l.Y, l.Z, + iol.M_AttributeSetInstance_ID, asi.M_AttributeSet_ID, asi.SerNo, asi.Lot, asi.M_Lot_ID,asi.GuaranteeDate, + pt.Description AS ProductDescription, p.ImageURL, + iol.C_Campaign_ID, iol.C_Project_ID, iol.C_Activity_ID, iol.C_ProjectPhase_ID, iol.C_ProjectTask_ID +FROM M_InOutLine iol + INNER JOIN C_UOM_Trl uom ON (iol.C_UOM_ID=uom.C_UOM_ID) + LEFT OUTER JOIN M_Product p ON (iol.M_Product_ID=p.M_Product_ID) + LEFT OUTER JOIN M_Product_Trl pt ON (iol.M_Product_ID=pt.M_Product_ID AND uom.AD_Language=pt.AD_Language) + LEFT OUTER JOIN M_AttributeSetInstance asi ON (iol.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) + LEFT OUTER JOIN M_Locator l ON (iol.M_Locator_ID=l.M_Locator_ID) + LEFT OUTER JOIN C_OrderLine ol ON (iol.C_OrderLine_ID=ol.C_OrderLine_ID) + LEFT OUTER JOIN C_Charge c ON (iol.C_Charge_ID=c.C_Charge_ID) +UNION +SELECT iol.AD_Client_ID, iol.AD_Org_ID, iol.IsActive, iol.Created, iol.CreatedBy, iol.Updated, iol.UpdatedBy, + uom.AD_Language, + iol.M_InOut_ID, iol.M_InOutLine_ID, + iol.Line+(b.Line/100) AS Line, p.M_Product_ID, + iol.MovementQty*b.BOMQty AS QtyInvoiced, iol.QtyEntered*b.BOMQty AS QtyEntered, uom.UOMSymbol, + null, null, null, + COALESCE (pt.Name, p.Name) AS Name, -- main line + b.Description, -- second line + COALESCE (pt.DocumentNote, p.DocumentNote) AS DocumentNote, -- third line + p.UPC, p.SKU, p.Value AS ProductValue, + iol.M_Locator_ID, l.M_Warehouse_ID, l.X, l.Y, l.Z, + iol.M_AttributeSetInstance_ID, asi.M_AttributeSet_ID, asi.SerNo, asi.Lot, asi.M_Lot_ID,asi.GuaranteeDate, + pt.Description AS ProductDescription, p.ImageURL, + iol.C_Campaign_ID, iol.C_Project_ID, iol.C_Activity_ID, iol.C_ProjectPhase_ID, iol.C_ProjectTask_ID +FROM M_Product_BOM b -- BOM lines + INNER JOIN M_InOutLine iol ON (b.M_Product_ID=iol.M_Product_ID) + INNER JOIN M_Product bp ON (bp.M_Product_ID=iol.M_Product_ID -- BOM Product + AND bp.IsBOM='Y' AND bp.IsVerified='Y' AND bp.IsPickListPrintDetails='Y') + INNER JOIN M_Product p ON (b.M_ProductBOM_ID=p.M_Product_ID) -- BOM line product + INNER JOIN C_UOM_Trl uom ON (p.C_UOM_ID=uom.C_UOM_ID) + INNER JOIN M_Product_Trl pt ON (iol.M_Product_ID=pt.M_Product_ID AND uom.AD_Language=pt.AD_Language) + LEFT OUTER JOIN M_AttributeSetInstance asi ON (iol.M_AttributeSetInstance_ID=asi.M_AttributeSetInstance_ID) + LEFT OUTER JOIN M_Locator l ON (iol.M_Locator_ID=l.M_Locator_ID); + + + diff --git a/db/ddlutils/postgresql/views/RV_M_TRANSACTION.sql b/db/ddlutils/postgresql/views/RV_M_TRANSACTION.sql new file mode 100644 index 0000000000..b4cffa7d80 --- /dev/null +++ b/db/ddlutils/postgresql/views/RV_M_TRANSACTION.sql @@ -0,0 +1,16 @@ +CREATE OR REPLACE VIEW RV_M_TRANSACTION +(AD_CLIENT_ID, AD_ORG_ID, MOVEMENTDATE, MOVEMENTQTY, M_PRODUCT_ID, + M_LOCATOR_ID, M_ATTRIBUTESETINSTANCE_ID, M_PRODUCT_CATEGORY_ID, VALUE, C_BPARTNER_ID, + PRICEPO, PRICELASTPO, PRICELIST) +AS +SELECT t.AD_Client_ID,t.AD_Org_ID, t.MovementDate, t.MovementQty, + t.M_Product_ID, t.M_Locator_ID, t.M_AttributeSetInstance_ID, + p.M_Product_Category_ID, p.Value, + po.C_BPartner_ID, po.PricePO, po.PriceLastPO, po.PriceList +FROM M_Transaction t + INNER JOIN M_Product p ON (t.M_Product_ID=p.M_Product_ID) + INNER JOIN M_Product_PO po ON (t.M_Product_ID=po.M_Product_ID) +WHERE po.IsCurrentVendor='Y'; + + + diff --git a/db/ddlutils/postgresql/views/RV_OPENITEM.sql b/db/ddlutils/postgresql/views/RV_OPENITEM.sql new file mode 100644 index 0000000000..ad3d594833 --- /dev/null +++ b/db/ddlutils/postgresql/views/RV_OPENITEM.sql @@ -0,0 +1,55 @@ +CREATE OR REPLACE VIEW RV_OPENITEM +(AD_ORG_ID, AD_CLIENT_ID, DOCUMENTNO, C_INVOICE_ID, C_ORDER_ID, + C_BPARTNER_ID, ISSOTRX, DATEINVOICED, DATEACCT, NETDAYS, + DUEDATE, DAYSDUE, DISCOUNTDATE, DISCOUNTAMT, GRANDTOTAL, + PAIDAMT, OPENAMT, C_CURRENCY_ID, C_CONVERSIONTYPE_ID, C_PAYMENTTERM_ID, + ISPAYSCHEDULEVALID, C_INVOICEPAYSCHEDULE_ID, INVOICECOLLECTIONTYPE, C_CAMPAIGN_ID, C_PROJECT_ID, + C_ACTIVITY_ID) +AS +SELECT i.AD_Org_ID, i.AD_Client_ID, + i.DocumentNo, i.C_Invoice_ID, i.C_Order_ID, i.C_BPartner_ID, i.IsSOTrx, + i.DateInvoiced, i.DateAcct, + p.NetDays, + paymentTermDueDate(i.C_PaymentTerm_ID, i.DateInvoiced) AS DueDate, + paymentTermDueDays(i.C_PaymentTerm_ID, i.DateInvoiced, getdate()) AS DaysDue, + addDays(i.DateInvoiced,p.DiscountDays) AS DiscountDate, + ROUND(i.GrandTotal*p.Discount/100,2) AS DiscountAmt, + i.GrandTotal, + invoicePaid(i.C_Invoice_ID, i.C_Currency_ID, 1) AS PaidAmt, + invoiceOpen(i.C_Invoice_ID,0) AS OpenAmt, + i.C_Currency_ID, i.C_ConversionType_ID, + i.C_PaymentTerm_ID, + i.IsPayScheduleValid, cast(null as numeric) AS C_InvoicePaySchedule_ID, i.InvoiceCollectionType, + i.C_Campaign_ID, i.C_Project_ID, i.C_Activity_ID +FROM RV_C_Invoice i + INNER JOIN C_PaymentTerm p ON (i.C_PaymentTerm_ID=p.C_PaymentTerm_ID) +WHERE -- i.IsPaid='N' + invoiceOpen(i.C_Invoice_ID,0) <> 0 + AND i.IsPayScheduleValid<>'Y' + AND i.DocStatus<>'DR' +UNION +SELECT i.AD_Org_ID, i.AD_Client_ID, + i.DocumentNo, i.C_Invoice_ID, i.C_Order_ID, i.C_BPartner_ID, i.IsSOTrx, + i.DateInvoiced, i.DateAcct, + daysBetween(ips.DueDate,i.DateInvoiced) AS NetDays, + ips.DueDate, + daysBetween(getdate(),ips.DueDate) AS DaysDue, + ips.DiscountDate, + ips.DiscountAmt, + ips.DueAmt AS GrandTotal, + invoicePaid(i.C_Invoice_ID, i.C_Currency_ID, 1) AS PaidAmt, + invoiceOpen(i.C_Invoice_ID, ips.C_InvoicePaySchedule_ID) AS OpenAmt, + i.C_Currency_ID, i.C_ConversionType_ID, + i.C_PaymentTerm_ID, + i.IsPayScheduleValid, ips.C_InvoicePaySchedule_ID, i.InvoiceCollectionType, + i.C_Campaign_ID, i.C_Project_ID, i.C_Activity_ID +FROM RV_C_Invoice i + INNER JOIN C_InvoicePaySchedule ips ON (i.C_Invoice_ID=ips.C_Invoice_ID) +WHERE -- i.IsPaid='N' + invoiceOpen(i.C_Invoice_ID,ips.C_InvoicePaySchedule_ID) <> 0 + AND i.IsPayScheduleValid='Y' + AND i.DocStatus<>'DR' + AND ips.IsValid='Y'; + + + diff --git a/serverRoot/src/main/server/org/compiere/server/AdempiereServer.java b/serverRoot/src/main/server/org/compiere/server/AdempiereServer.java new file mode 100644 index 0000000000..0472f50efb --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/AdempiereServer.java @@ -0,0 +1,388 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.server; + +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.ldap.*; +import org.compiere.model.*; +import org.compiere.util.*; +import org.compiere.wf.*; + +/** + * Adempiere Server Base + * + * @author Jorg Janke + * @version $Id: AdempiereServer.java,v 1.3 2006/10/09 00:23:26 jjanke Exp $ + */ +public abstract class AdempiereServer extends Thread +{ + /** + * Create New Server Thead + * @param model model + * @return server tread or null + */ + public static AdempiereServer create (AdempiereProcessor model) + { + if (model instanceof MRequestProcessor) + return new RequestProcessor ((MRequestProcessor)model); + if (model instanceof MWorkflowProcessor) + return new WorkflowProcessor ((MWorkflowProcessor)model); + if (model instanceof MAcctProcessor) + return new AcctProcessor ((MAcctProcessor)model); + if (model instanceof MAlertProcessor) + return new AlertProcessor ((MAlertProcessor)model); + if (model instanceof MScheduler) + return new Scheduler ((MScheduler)model); + if (model instanceof MLdapProcessor) + return new LdapProcessor((MLdapProcessor)model); + // + throw new IllegalArgumentException("Unknown Processor"); + } // create + + + /************************************************************************** + * Server Base Class + * @param model model + * @param initialNap delay time running in sec + */ + protected AdempiereServer (AdempiereProcessor model, int initialNap) + { + super (AdempiereServerGroup.get(), null, model.getName(), 0); + p_model = model; + m_ctx = new Properties(model.getCtx()); + if (p_system == null) + p_system = MSystem.get(m_ctx); + p_client = MClient.get(m_ctx); + Env.setContext(m_ctx, "#AD_Client_ID", p_client.getAD_Client_ID()); + m_initialNap = initialNap; + // log.info(model.getName() + " - " + getThreadGroup()); + } // ServerBase + + /** The Processor Model */ + protected AdempiereProcessor p_model; + /** Initial nap is seconds */ + private int m_initialNap = 0; + + /** Miliseconds to sleep - 10 Min default */ + private long m_sleepMS = 600000; + /** Sleeping */ + private volatile boolean m_sleeping = false; + /** Server start time */ + private long m_start = 0; + /** Number of Work executions */ + protected int p_runCount = 0; + /** Tine start of work */ + protected long p_startWork = 0; + /** Number MS of last Run */ + private long m_runLastMS = 0; + /** Number of MS total */ + private long m_runTotalMS = 0; + /** When to run next */ + private long m_nextWork = 0; + + /** Logger */ + protected CLogger log = CLogger.getCLogger(getClass()); + /** Context */ + private Properties m_ctx = null; + /** System */ + protected static MSystem p_system = null; + /** Client */ + protected MClient p_client = null; + + /** + * Get Server Context + * @return context + */ + public Properties getCtx() + { + return m_ctx; + } // getCtx + + /** + * @return Returns the sleepMS. + */ + public long getSleepMS () + { + return m_sleepMS; + } // getSleepMS + + + /** + * Sleep for set time + * @return true if not interrupted + */ + public boolean sleep() + { + if (isInterrupted()) + { + log.info (getName() + ": interrupted"); + return false; + } + log.fine(getName() + ": sleeping " + TimeUtil.formatElapsed(m_sleepMS)); + m_sleeping = true; + try + { + sleep (m_sleepMS); + } + catch (InterruptedException e) + { + log.info (getName() + ": interrupted"); + m_sleeping = false; + return false; + } + m_sleeping = false; + return true; + } // sleep + + /** + * Run Now + */ + public void runNow() + { + log.info(getName()); + p_startWork = System.currentTimeMillis(); + doWork(); + long now = System.currentTimeMillis(); + // --------------- + + p_runCount++; + m_runLastMS = now - p_startWork; + m_runTotalMS += m_runLastMS; + // + p_model.setDateLastRun(new Timestamp(now)); + p_model.save(); + // + log.fine(getName() + ": " + getStatistics()); + } // runNow + + /************************************************************************** + * Run async + */ + public void run () + { + try + { + log.fine(getName() + ": pre-nap - " + m_initialNap); + sleep (m_initialNap * 1000); + } + catch (InterruptedException e) + { + log.log(Level.SEVERE, getName() + ": pre-nap interrupted", e); + return; + } + + m_start = System.currentTimeMillis(); + while (true) + { + if (m_nextWork == 0) + { + Timestamp dateNextRun = getDateNextRun(true); + if (dateNextRun != null) + m_nextWork = dateNextRun.getTime(); + } + long now = System.currentTimeMillis(); + if (m_nextWork > now) + { + m_sleepMS = m_nextWork - now; + if (!sleep ()) + break; + } + if (isInterrupted()) + { + log.info (getName() + ": interrupted"); + break; + } + + // --------------- + p_startWork = System.currentTimeMillis(); + doWork(); + now = System.currentTimeMillis(); + // --------------- + + p_runCount++; + m_runLastMS = now - p_startWork; + m_runTotalMS += m_runLastMS; + // + m_sleepMS = calculateSleep(); + m_nextWork = now + m_sleepMS; + // + p_model.setDateLastRun(new Timestamp(now)); + p_model.setDateNextRun(new Timestamp(m_nextWork)); + p_model.save(); + // + log.fine(getName() + ": " + getStatistics()); + if (!sleep()) + break; + } + m_start = 0; + } // run + + /** + * Get Run Statistics + * @return Statistic info + */ + public String getStatistics() + { + return "Run #" + p_runCount + + " - Last=" + TimeUtil.formatElapsed(m_runLastMS) + + " - Total=" + TimeUtil.formatElapsed(m_runTotalMS) + + " - Next " + TimeUtil.formatElapsed(m_nextWork - System.currentTimeMillis()); + } // getStatistics + + /** + * Do the actual Work + */ + protected abstract void doWork(); + + /** + * Get Server Info + * @return info + */ + public abstract String getServerInfo(); + + /** + * Get Unique ID + * @return Unique ID + */ + public String getServerID() + { + return p_model.getServerID(); + } // getServerID + + /** + * Get the date Next run + * @param requery requery database + * @return date next run + */ + public Timestamp getDateNextRun (boolean requery) + { + return p_model.getDateNextRun(requery); + } // getDateNextRun + + /** + * Get the date Last run + * @return date lext run + */ + public Timestamp getDateLastRun () + { + return p_model.getDateLastRun(); + } // getDateLastRun + + /** + * Get Description + * @return Description + */ + public String getDescription() + { + return p_model.getDescription(); + } // getDescription + + /** + * Get Model + * @return Model + */ + public AdempiereProcessor getModel() + { + return p_model; + } // getModel + + /** + * Calculate Sleep ms + * @return miliseconds + */ + private long calculateSleep () + { + String frequencyType = p_model.getFrequencyType(); + int frequency = p_model.getFrequency(); + if (frequency < 1) + frequency = 1; + // + long typeSec = 600; // 10 minutes + if (frequencyType == null) + typeSec = 300; // 5 minutes + else if (X_R_RequestProcessor.FREQUENCYTYPE_Minute.equals(frequencyType)) + typeSec = 60; + else if (X_R_RequestProcessor.FREQUENCYTYPE_Hour.equals(frequencyType)) + typeSec = 3600; + else if (X_R_RequestProcessor.FREQUENCYTYPE_Day.equals(frequencyType)) + typeSec = 86400; + // + return typeSec * 1000 * frequency; // ms + } // calculateSleep + + /** + * Is Sleeping + * @return sleeping + */ + public boolean isSleeping() + { + return m_sleeping; + } // isSleeping + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer (getName()) + .append (",Prio=").append(getPriority()) + .append (",").append (getThreadGroup()) + .append (",Alive=").append(isAlive()) + .append (",Sleeping=").append(m_sleeping) + .append (",Last=").append(getDateLastRun()); + if (m_sleeping) + sb.append (",Next=").append(getDateNextRun(false)); + return sb.toString (); + } // toString + + /** + * Get Seconds Alive + * @return seconds alive + */ + public int getSecondsAlive() + { + if (m_start == 0) + return 0; + long now = System.currentTimeMillis(); + long ms = (now-m_start) / 1000; + return (int)ms; + } // getSecondsAlive + + /** + * Get Start Time + * @return start time + */ + public Timestamp getStartTime() + { + if (m_start == 0) + return null; + return new Timestamp (m_start); + } // getStartTime + + /** + * Get Processor Logs + * @return logs + */ + public AdempiereProcessorLog[] getLogs() + { + return p_model.getLogs(); + } // getLogs + +} // AdempiereServer diff --git a/serverRoot/src/main/server/org/compiere/server/AdempiereServerMgr.java b/serverRoot/src/main/server/org/compiere/server/AdempiereServerMgr.java new file mode 100644 index 0000000000..d79098a82d --- /dev/null +++ b/serverRoot/src/main/server/org/compiere/server/AdempiereServerMgr.java @@ -0,0 +1,534 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, Inc. 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.compiere.server; + +import java.sql.*; +import java.util.*; +import java.util.logging.*; +import org.compiere.*; +import org.compiere.model.*; +import org.compiere.util.*; +import org.compiere.wf.*; + +/** + * Adempiere Server Manager + * + * @author Jorg Janke + * @version $Id: AdempiereServerMgr.java,v 1.4 2006/10/09 00:23:26 jjanke Exp $ + */ +public class AdempiereServerMgr +{ + /** + * Get Adempiere Server Manager + * @return mgr + */ + public static AdempiereServerMgr get() + { + if (m_serverMgr == null) + { + // for faster subsequent calls + m_serverMgr = new AdempiereServerMgr(); + m_serverMgr.startServers(); + m_serverMgr.log.info(m_serverMgr.toString()); + } + return m_serverMgr; + } // get + + /** Singleton */ + private static AdempiereServerMgr m_serverMgr = null; + /** Logger */ + protected CLogger log = CLogger.getCLogger(getClass()); + + /************************************************************************** + * Adempiere Server Manager + */ + private AdempiereServerMgr () + { + super(); + startEnvironment(); + // m_serverMgr.startServers(); + } // AdempiereServerMgr + + /** The Servers */ + private ArrayList m_servers = new ArrayList(); + /** Context */ + private Properties m_ctx = Env.getCtx(); + /** Start */ + private Timestamp m_start = new Timestamp(System.currentTimeMillis()); + + /** + * Start Environment + * @return true if started + */ + private boolean startEnvironment() + { + Adempiere.startup(false); + log.info(""); + + // Set Session + MSession session = MSession.get(getCtx(), true); + session.setWebStoreSession(false); + session.setWebSession("Server"); + session.save(); + // + return true; + } // startEnvironment + + /** + * Start Environment + * @return true if started + */ + private boolean startServers() + { + log.info(""); + int noServers = 0; + // Accounting + MAcctProcessor[] acctModels = MAcctProcessor.getActive(m_ctx); + for (int i = 0; i < acctModels.length; i++) + { + MAcctProcessor pModel = acctModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // Request + MRequestProcessor[] requestModels = MRequestProcessor.getActive(m_ctx); + for (int i = 0; i < requestModels.length; i++) + { + MRequestProcessor pModel = requestModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // Workflow + MWorkflowProcessor[] workflowModels = MWorkflowProcessor.getActive(m_ctx); + for (int i = 0; i < workflowModels.length; i++) + { + MWorkflowProcessor pModel = workflowModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // Alert + MAlertProcessor[] alertModels = MAlertProcessor.getActive(m_ctx); + for (int i = 0; i < alertModels.length; i++) + { + MAlertProcessor pModel = alertModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // Scheduler + MScheduler[] schedulerModels = MScheduler.getActive(m_ctx); + for (int i = 0; i < schedulerModels.length; i++) + { + MScheduler pModel = schedulerModels[i]; + AdempiereServer server = AdempiereServer.create(pModel); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + m_servers.add(server); + } + // LDAP + MLdapProcessor[] ldapModels = MLdapProcessor.getActive(m_ctx); + for (int i = 0; i < ldapModels.length; i++) + { + MLdapProcessor lp = ldapModels[i]; + AdempiereServer server = AdempiereServer.create(lp); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-1); + m_servers.add(server); + } + + log.fine("#" + noServers); + return startAll(); + } // startEnvironment + + /** + * Get Server Context + * @return ctx + */ + public Properties getCtx() + { + return m_ctx; + } // getCtx + + /** + * Start all servers + * @return true if started + */ + public boolean startAll() + { + log.info (""); + AdempiereServer[] servers = getInActive(); + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + if (server.isAlive()) + continue; + // Wait until dead + if (server.isInterrupted()) + { + int maxWait = 10; // 10 iterations = 1 sec + while (server.isAlive()) + { + if (maxWait-- == 0) + { + log.severe ("Wait timeout for interruped " + server); + break; + } + try + { + Thread.sleep(100); // 1/10 sec + } + catch (InterruptedException e) + { + log.log(Level.SEVERE, "While sleeping", e); + } + } + } + // Do start + if (!server.isAlive()) + { + // replace + server = AdempiereServer.create (server.getModel()); + if (server == null) + m_servers.remove(i); + else + m_servers.set(i, server); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "Server: " + server, e); + } + } // for all servers + + // Final Check + int noRunning = 0; + int noStopped = 0; + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + if (server.isAlive()) + { + log.info("Alive: " + server); + noRunning++; + } + else + { + log.warning("Dead: " + server); + noStopped++; + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "(checking) - " + server, e); + noStopped++; + } + } + log.fine("Running=" + noRunning + ", Stopped=" + noStopped); + AdempiereServerGroup.get().dump(); + return noStopped == 0; + } // startAll + + /** + * Start Server if not started yet + * @param serverID server ID + * @return true if started + */ + public boolean start (String serverID) + { + AdempiereServer server = getServer(serverID); + if (server == null) + return false; + if (server.isAlive()) + return true; + + try + { + // replace + int index = m_servers.indexOf(server); + server = AdempiereServer.create (server.getModel()); + if (server == null) + m_servers.remove(index); + else + m_servers.set(index, server); + server.start(); + server.setPriority(Thread.NORM_PRIORITY-2); + Thread.yield(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "Server=" + serverID, e); + return false; + } + log.info(server.toString()); + AdempiereServerGroup.get().dump(); + if (server == null) + return false; + return server.isAlive(); + } // startIt + + /** + * Stop all Servers + * @return true if stopped + */ + public boolean stopAll() + { + log.info (""); + AdempiereServer[] servers = getActive(); + // Interrupt + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + if (server.isAlive() && !server.isInterrupted()) + { + server.setPriority(Thread.MAX_PRIORITY-1); + server.interrupt(); + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "(interrupting) - " + server, e); + } + } // for all servers + Thread.yield(); + + // Wait for death + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + int maxWait = 10; // 10 iterations = 1 sec + while (server.isAlive()) + { + if (maxWait-- == 0) + { + log.severe ("Wait timeout for interruped " + server); + break; + } + Thread.sleep(100); // 1/10 + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "(waiting) - " + server, e); + } + } // for all servers + + // Final Check + int noRunning = 0; + int noStopped = 0; + for (int i = 0; i < servers.length; i++) + { + AdempiereServer server = servers[i]; + try + { + if (server.isAlive()) + { + log.warning ("Alive: " + server); + noRunning++; + } + else + { + log.info ("Stopped: " + server); + noStopped++; + } + } + catch (Exception e) + { + log.log(Level.SEVERE, "(checking) - " + server, e); + noRunning++; + } + } + log.fine("Running=" + noRunning + ", Stopped=" + noStopped); + AdempiereServerGroup.get().dump(); + return noRunning == 0; + } // stopAll + + /** + * Stop Server if not stopped + * @param serverID server ID + * @return true if interrupted + */ + public boolean stop (String serverID) + { + AdempiereServer server = getServer(serverID); + if (server == null) + return false; + if (!server.isAlive()) + return true; + + try + { + server.interrupt(); + Thread.sleep(10); // 1/100 sec + } + catch (Exception e) + { + log.log(Level.SEVERE, "stop", e); + return false; + } + log.info(server.toString()); + AdempiereServerGroup.get().dump(); + return !server.isAlive(); + } // stop + + + /** + * Destroy + */ + public void destroy () + { + log.info (""); + stopAll(); + m_servers.clear(); + } // destroy + + /** + * Get Active Servers + * @return array of active servers + */ + protected AdempiereServer[] getActive() + { + ArrayList list = new ArrayList(); + for (int i = 0; i < m_servers.size(); i++) + { + AdempiereServer server = (AdempiereServer)m_servers.get(i); + if (server != null && server.isAlive() && !server.isInterrupted()) + list.add (server); + } + AdempiereServer[] retValue = new AdempiereServer[list.size ()]; + list.toArray (retValue); + return retValue; + } // getActive + + /** + * Get InActive Servers + * @return array of inactive servers + */ + protected AdempiereServer[] getInActive() + { + ArrayList list = new ArrayList(); + for (int i = 0; i < m_servers.size(); i++) + { + AdempiereServer server = (AdempiereServer)m_servers.get(i); + if (server != null && (!server.isAlive() || !server.isInterrupted())) + list.add (server); + } + AdempiereServer[] retValue = new AdempiereServer[list.size()]; + list.toArray (retValue); + return retValue; + } // getInActive + + /** + * Get all Servers + * @return array of servers + */ + public AdempiereServer[] getAll() + { + AdempiereServer[] retValue = new AdempiereServer[m_servers.size()]; + m_servers.toArray (retValue); + return retValue; + } // getAll + + /** + * Get Server with ID + * @param serverID server id + * @return server or null + */ + public AdempiereServer getServer (String serverID) + { + if (serverID == null) + return null; + for (int i = 0; i < m_servers.size(); i++) + { + AdempiereServer server = (AdempiereServer)m_servers.get(i); + if (serverID.equals(server.getServerID())) + return server; + } + return null; + } // getServer + + /** + * String Representation + * @return info + */ + public String toString () + { + StringBuffer sb = new StringBuffer ("AdempiereServerMgr["); + sb.append("Servers=").append(m_servers.size()) + .append(",ContextSize=").append(m_ctx.size()) + .append(",Started=").append(m_start) + .append ("]"); + return sb.toString (); + } // toString + + /** + * Get Description + * @return description + */ + public String getDescription() + { + return "$Revision: 1.4 $"; + } // getDescription + + /** + * Get Number Servers + * @return no of servers + */ + public String getServerCount() + { + int noRunning = 0; + int noStopped = 0; + for (int i = 0; i < m_servers.size(); i++) + { + AdempiereServer server = (AdempiereServer)m_servers.get(i); + if (server.isAlive()) + noRunning++; + else + noStopped++; + } + String info = String.valueOf(m_servers.size()) + + " - Running=" + noRunning + + " - Stopped=" + noStopped; + return info; + } // getServerCount + + /** + * Get start date + * @return start date + */ + public Timestamp getStartTime() + { + return m_start; + } // getStartTime + +} // AdempiereServerMgr diff --git a/sqlj/oracle/createSQLJ.sql b/sqlj/oracle/createSQLJ.sql new file mode 100644 index 0000000000..a27d33c517 --- /dev/null +++ b/sqlj/oracle/createSQLJ.sql @@ -0,0 +1,230 @@ +/** + * Create SQL Java Functions (Oracle) + * + * Author + Copyright 1999-2005 Jorg Janke + * $Header: /cvs/adempiere/sqlj/oracle/createSQLJ.sql,v 1.1 2006/04/21 18:04:47 jjanke Exp $ + */ + +CREATE OR REPLACE FUNCTION adempiereVersion + RETURN VARCHAR2 + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Adempiere.getVersion() return java.lang.String'; +/ +CREATE OR REPLACE FUNCTION adempiereProperties + RETURN VARCHAR2 + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Adempiere.getProperties() return java.lang.String'; +/ +CREATE OR REPLACE FUNCTION adempiereProperty(p_key VARCHAR2) + RETURN VARCHAR2 + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Adempiere.getProperty(java.lang.String) return java.lang.String'; +/ + +/** Product **/ +CREATE OR REPLACE FUNCTION productAttribute (M_AttributeSetInstance_ID NUMBER) + RETURN NVARCHAR2 + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Product.attributeName(int) return java.lang.String'; +/ + +CREATE OR REPLACE FUNCTION bomPriceLimit (M_Product_ID NUMBER, M_PriceList_Version_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Product.bomPriceLimit(int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION bomPriceList (M_Product_ID NUMBER, M_PriceList_Version_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Product.bomPriceList(int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION bomPriceStd (M_Product_ID NUMBER, M_PriceList_Version_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Product.bomPriceStd(int,int) return java.math.BigDecimal'; +/ + +CREATE OR REPLACE FUNCTION bomQtyAvailable (M_Product_ID NUMBER, M_Warehouse_ID NUMBER, + M_Locator_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Product.bomQtyAvailable(int,int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION bomQtyOnHand (M_Product_ID NUMBER, M_Warehouse_ID NUMBER, + M_Locator_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Product.bomQtyOnHand(int,int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION bomQtyOrdered (M_Product_ID NUMBER, M_Warehouse_ID NUMBER, + M_Locator_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Product.bomQtyOrdered(int,int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION bomQtyReserved (M_Product_ID NUMBER, M_Warehouse_ID NUMBER, + M_Locator_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Product.bomQtyReserved(int,int,int) return java.math.BigDecimal'; +/ + +/** Currency **/ +CREATE OR REPLACE FUNCTION currencyBase (Amount NUMBER, C_CurrencyFrom_ID NUMBER, + ConversionDate DATE, AD_Client_ID NUMBER, AD_Org_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Currency.base(java.math.BigDecimal,int,java.sql.Timestamp,int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION currencyConvert (Amount NUMBER, C_CurrencyFrom_ID NUMBER, + C_CurrencyTo_ID NUMBER, + ConversionDate DATE, C_ConversionType_ID NUMBER, AD_Client_ID NUMBER, AD_Org_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Currency.convert(java.math.BigDecimal,int,int,java.sql.Timestamp,int,int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION currencyRate (C_CurrencyFrom_ID NUMBER, C_CurrencyTo_ID NUMBER, + ConversionDate DATE, C_ConversionType_ID NUMBER, AD_Client_ID NUMBER, AD_Org_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Currency.rate(int,int,java.sql.Timestamp,int,int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION currencyRound (Amt NUMBER, C_CurrencyTo_ID NUMBER, IsCosting VARCHAR2) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Currency.round(java.math.BigDecimal,int,java.lang.String) return java.math.BigDecimal'; +/ + +/** BPartner **/ +CREATE OR REPLACE FUNCTION bpartnerRemitLocation (p_C_BPartner_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.BPartner.remitLocation(int) return int'; +/ + +/** Invoice **/ +CREATE OR REPLACE FUNCTION invoiceOpen (p_C_Invoice_ID NUMBER, p_C_InvoicePaySchedule_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Invoice.open(int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION invoicePaid (p_C_Invoice_ID NUMBER, p_C_Currency_ID NUMBER, + p_MultiplierAP NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Invoice.paid(int,int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION invoiceDiscount (p_C_Invoice_ID NUMBER, p_PayDate Date, + p_C_InvoicePaySchedule_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Invoice.discount(int,java.sql.Timestamp,int) return java.math.BigDecimal'; +/ + +/** Payment Term **/ +CREATE OR REPLACE FUNCTION paymentTermDueDays (p_C_PaymentTerm_ID NUMBER, p_DocDate DATE, + p_PayDate DATE) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.PaymentTerm.dueDays(int,java.sql.Timestamp,java.sql.Timestamp) return int'; +/ +CREATE OR REPLACE FUNCTION paymentTermDiscount (p_Amount NUMBER, p_C_Currency_ID NUMBER, + p_C_PaymentTerm_ID NUMBER, p_DocDate DATE, p_PayDate DATE) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.PaymentTerm.discount(java.math.BigDecimal,int,int,java.sql.Timestamp,java.sql.Timestamp) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION paymentTermDueDate (p_C_PaymentTerm_ID NUMBER, p_DocDate DATE) + RETURN DATE + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.PaymentTerm.dueDate(int,java.sql.Timestamp) return java.sql.Timestamp'; +/ + +/** Payment **/ +CREATE OR REPLACE FUNCTION paymentAllocated (p_C_Payment_ID NUMBER, p_C_Currency_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Payment.allocated(int,int) return java.math.BigDecimal'; +/ +CREATE OR REPLACE FUNCTION paymentAvailable (p_C_Payment_ID NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Payment.available(int) return java.math.BigDecimal'; +/ + +/** Account **/ +CREATE OR REPLACE FUNCTION acctBalance (p_Account_ID NUMBER, p_AmtDr NUMBER, p_AmtCr NUMBER) + RETURN NUMBER + AS LANGUAGE JAVA + NAME 'org.compiere.sqlj.Account.balance(int,java.math.BigDecimal,java.math.BigDecimal) return java.math.BigDecimal'; +/ + +/** General **/ +BEGIN + dbms_java.grant_permission('ADEMPIERE','SYS:java.util.PropertyPermission', '*', 'read,write'); +END; +/ + +/** Get Character at Position */ +CREATE OR REPLACE FUNCTION charAt +( + p_string VARCHAR2, + p_pos NUMBER +) + RETURN VARCHAR2 +AS +BEGIN + RETURN SUBSTR(p_string, p_pos, 1); +END; +/ +/** GetDate */ +CREATE OR REPLACE FUNCTION getdate + RETURN DATE +AS +BEGIN + RETURN SysDate; +END; +/ +/** First Of DD/DY/MM/Q */ +CREATE OR REPLACE FUNCTION firstOf +( + p_date DATE, + p_datePart VARCHAR2 +) + RETURN DATE +AS +BEGIN + RETURN TRUNC(p_date, p_datePart); +END; +/ +/** Add Number of Days */ +CREATE OR REPLACE FUNCTION addDays +( + p_date DATE, + p_days NUMBER +) + RETURN DATE +AS +BEGIN + RETURN TRUNC(p_date) + p_days; +END; +/ +/** Difference in Days */ +CREATE OR REPLACE FUNCTION daysBetween +( + p_date1 DATE, + p_date2 DATE +) + RETURN NUMBER +AS +BEGIN + RETURN TRUNC(p_date1) - TRUNC(p_date2); +END; +/ + + +SELECT --adempiereVersion(), adempiereProperty('java.vendor'), + TRUNC(getdate()) FROM DUAL +/ + +EXIT diff --git a/utils_dev/RUN_build.bat b/utils_dev/RUN_build.bat new file mode 100644 index 0000000000..5026fc1fa7 --- /dev/null +++ b/utils_dev/RUN_build.bat @@ -0,0 +1,31 @@ +@Title Build Adempiere Clean +@Rem $Header: /cvsroot/adempiere/utils_dev/RUN_build.bat,v 1.22 2005/09/08 21:56:11 jjanke Exp $ + +@Rem Check java home +@IF NOT EXIST "%JAVA_HOME%\bin" ECHO "** JAVA_HOME NOT found" +@SET PATH=%JAVA_HOME%\bin;%PATH% + +@Rem Check jdk +@IF NOT EXIST "%JAVA_HOME%\lib\tools.jar" ECHO "** Need Full Java SDK **" + +@Rem Set ant classpath +@SET ANT_CLASSPATH=%CLASSPATH%;..\tools\lib\ant.jar;..\tools\lib\ant-launcher.jar;..\tools\lib\ant-swing.jar;..\tools\lib\ant-commons-net.jar;..\tools\lib\commons-net-1.4.0.jar +@SET ANT_CLASSPATH=%ANT_CLASSPATH%;"%JAVA_HOME%\lib\tools.jar" + +@SET ANT_OPTS=-Xms128m -Xmx512m + +@echo Cleanup ... +@"%JAVA_HOME%\bin\java" %ANT_OPTS% -classpath %ANT_CLASSPATH% -Dant.home="." org.apache.tools.ant.Main clean + +@echo Building ... +@"%JAVA_HOME%\bin\java" %ANT_OPTS% -classpath %ANT_CLASSPATH% -Dant.home="." org.apache.tools.ant.Main -logger org.apache.tools.ant.listener.MailLogger complete +@IF ERRORLEVEL 1 goto ERROR + +@Echo Done ... +@Pause +@exit + +:ERROR +@Color fc + +@Pause \ No newline at end of file