/****************************************************************************** * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 2007 Low Heng Sin * * 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. * *****************************************************************************/ package org.adempiere.webui.apps; import static org.compiere.model.SystemIDs.PROCESS_C_INVOICE_GENERATE; import static org.compiere.model.SystemIDs.PROCESS_M_INOUT_GENERATE; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import org.adempiere.util.Callback; import org.adempiere.webui.LayoutUtils; import org.adempiere.webui.component.ConfirmPanel; import org.adempiere.webui.component.DocumentLink; import org.adempiere.webui.component.Mask; import org.adempiere.webui.component.Tabpanel; import org.adempiere.webui.component.Window; import org.adempiere.webui.desktop.IDesktop; import org.adempiere.webui.panel.IHelpContext; import org.adempiere.webui.panel.ITabOnCloseHandler; import org.adempiere.webui.part.WindowContainer; import org.adempiere.webui.process.WProcessInfo; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.util.ZKUpdateUtil; import org.adempiere.webui.window.Dialog; import org.adempiere.webui.window.SimplePDFViewer; import org.compiere.model.MInOut; import org.compiere.model.MInvoice; import org.compiere.model.MProcess; import org.compiere.model.MRole; import org.compiere.model.MSysConfig; import org.compiere.model.X_AD_CtxHelp; import org.compiere.print.ReportEngine; import org.compiere.process.ProcessInfo; import org.compiere.process.ProcessInfoLog; import org.compiere.process.ProcessInfoUtil; import org.compiere.util.CLogger; import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.Util; import org.zkoss.zhtml.Table; import org.zkoss.zhtml.Td; import org.zkoss.zhtml.Text; import org.zkoss.zhtml.Tr; import org.zkoss.zk.au.out.AuEcho; import org.zkoss.zk.au.out.AuScript; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.HtmlBasedComponent; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.KeyEvent; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zul.A; import org.zkoss.zul.Div; import org.zkoss.zul.Html; import org.zkoss.zul.Label; import org.zkoss.zul.Tab; import org.zkoss.zul.Vlayout; import com.lowagie.text.Document; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfImportedPage; import com.lowagie.text.pdf.PdfReader; import com.lowagie.text.pdf.PdfWriter; /** * Embedded window to start process or report.
*
 *  Displays information about the process
 *     and lets the user decide to start it
 *     and displays results (optionally print them)  
 *  Calls ProcessCtl to execute.
 *  
* @author Low Heng Sin * @author arboleda - globalqss * - Implement ShowHelp option on processes and reports */ public class ProcessDialog extends AbstractProcessDialog implements EventListener, IHelpContext, ITabOnCloseHandler { public static final String SAVED_PREDEFINED_CONTEXT_VARIABLES = "__PredefinedContextVariables__"; /** * generated serial id */ private static final long serialVersionUID = -6728929130788829223L; public static final String ON_INITIAL_FOCUS_EVENT = "onInitialFocus"; /** * Event echo form {@link #onOk()} to defer execution of {@link #onOk()}. * Execution is defer to happens after the dismiss of modal dialog (usually info window) blocking parameter panel. */ private static final String ON_OK_ECHO_EVENT = "onOkEcho"; /** Logger */ private static final CLogger log = CLogger.getCLogger(ProcessDialog.class); // /** message from {@link ProcessInfoLog} **/ private Table logMessageTable; /** record ids from {@link ProcessInfo} **/ private int[] m_ids = null; /** true if dialog is showing process parameters **/ private boolean isParameterPage = true; private Mask mask; /** layout for process execution result **/ private HtmlBasedComponent resultPanelLayout; /** process message content of {@link #resultPanelLayout} **/ private HtmlBasedComponent messageResultContent; /** process log content of {@link #resultPanelLayout}, host {@link #logMessageTable} **/ private HtmlBasedComponent infoResultContent; /** * SysConfig USE_ESC_FOR_TAB_CLOSING */ private boolean isUseEscForTabClosing = MSysConfig.getBooleanValue(MSysConfig.USE_ESC_FOR_TAB_CLOSING, false, Env.getAD_Client_ID(Env.getCtx())); /** * Dialog to start a process/report * @param AD_Process_ID * @param isSOTrx */ public ProcessDialog (int AD_Process_ID, boolean isSOTrx) { this(AD_Process_ID, isSOTrx, null); } /** * Dialog to start a process/report * @param AD_Process_ID * @param isSOTrx * @param predefinedContextVariables */ public ProcessDialog (int AD_Process_ID, boolean isSOTrx, String predefinedContextVariables) { if (log.isLoggable(Level.INFO)) log.info("Process=" + AD_Process_ID ); m_WindowNo = SessionManager.getAppDesktop().registerWindow(this); this.setAttribute(IDesktop.WINDOWNO_ATTRIBUTE, m_WindowNo); Env.setContext(Env.getCtx(), m_WindowNo, "IsSOTrx", isSOTrx ? "Y" : "N"); //save for rerun of report if (predefinedContextVariables != null && MProcess.get(AD_Process_ID).isReport()) { Env.setContext(Env.getCtx(), m_WindowNo, SAVED_PREDEFINED_CONTEXT_VARIABLES, predefinedContextVariables); } Env.setPredefinedVariables(Env.getCtx(), m_WindowNo, predefinedContextVariables); try { init(Env.getCtx(), m_WindowNo, AD_Process_ID, null, false, false); querySaved(); addEventListener(WindowContainer.ON_WINDOW_CONTAINER_SELECTION_CHANGED_EVENT, this); addEventListener(ON_INITIAL_FOCUS_EVENT, this); addEventListener(ON_OK_ECHO_EVENT, this); } catch(Exception ex) { log.log(Level.SEVERE, "", ex); } } // ProcessDialog @Override public void onPageAttached(Page newpage, Page oldpage) { super.onPageAttached(newpage, oldpage); try { SessionManager.getSessionApplication().getKeylistener().addEventListener(Events.ON_CTRL_KEY, this); addEventListener(IDesktop.ON_CLOSE_WINDOW_SHORTCUT_EVENT, this); Component parentTab = this.getParent(); if (parentTab != null && parentTab instanceof Tabpanel) { ((Tabpanel)parentTab).setOnCloseHandler(this); } } catch (Exception e) {} } @Override public void onPageDetached(Page page) { super.onPageDetached(page); try { SessionManager.getSessionApplication().getKeylistener().removeEventListener(Events.ON_CTRL_KEY, this); removeEventListener(IDesktop.ON_CLOSE_WINDOW_SHORTCUT_EVENT, this); SessionManager.getAppDesktop().unregisterWindow(m_WindowNo); } catch (Exception e) {} } @Override public boolean setVisible (boolean visible) { return super.setVisible(visible); } // setVisible @Override public void dispose() { super.dispose(); SessionManager.getAppDesktop().closeWindow(getWindowNo()); }// dispose @Override public void onEvent(Event event) { Component component = event.getTarget(); if(component instanceof A && event.getName().equals((Events.ON_CLICK))){ doOnClick((A)component); } else if (bOK.equals(component)) { super.onEvent(event); onOk(); } else if (event.getName().equals(ON_OK_ECHO_EVENT)) { onOk(); }else if (bCancel.equals(component)){ super.onEvent(event); cancelProcess(); } else if (event.getName().equals(WindowContainer.ON_WINDOW_CONTAINER_SELECTION_CHANGED_EVENT)) { SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Process, getAD_Process_ID()); } else if (event.getName().equals(ON_INITIAL_FOCUS_EVENT)) { if (!isUILocked()) { if (!getParameterPanel().focusToFirstEditor()) { bOK.focus(); } } } else if (event.getName().equals(Events.ON_CTRL_KEY)) { KeyEvent keyEvent = (KeyEvent) event; if (LayoutUtils.isReallyVisible(this)) this.onCtrlKeyEvent(keyEvent); } else if(IDesktop.ON_CLOSE_WINDOW_SHORTCUT_EVENT.equals(event.getName())) { IDesktop desktop = SessionManager.getAppDesktop(); if (m_WindowNo > 0 && desktop.isCloseTabWithShortcut()) desktop.closeWindow(m_WindowNo); else desktop.setCloseTabWithShortcut(true); } else { super.onEvent(event); } } /** * Handle ON_Click event from {@link #bOK} */ private void onOk() { if (isParameterPage) { if (getParameterPanel().isWaitingForDialog()) { Events.echoEvent(ON_OK_ECHO_EVENT, this, null); return; } startProcess(); } else restart(); } /** * Handle shortcut key event * @param keyEvent */ private void onCtrlKeyEvent(KeyEvent keyEvent) { if ((keyEvent.isAltKey() && keyEvent.getKeyCode() == 0x58) // Alt-X || (keyEvent.getKeyCode() == 0x1B && isUseEscForTabClosing)) { // ESC keyEvent.stopPropagation(); Events.echoEvent(new Event(IDesktop.ON_CLOSE_WINDOW_SHORTCUT_EVENT, this)); } } /** * Handle on click event for record link. * @param btn */ private void doOnClick(A btn) { int Record_ID = 0; int AD_Table_ID =0; try { Record_ID = Integer.valueOf((String)btn.getAttribute("Record_ID")); AD_Table_ID= Integer.valueOf((String)btn.getAttribute("AD_Table_ID")); } catch (Exception e) { } if (Record_ID > 0 && AD_Table_ID > 0) { AEnv.zoom(AD_Table_ID, Record_ID); } } @Override public void showBusyDialog() { BusyDialog progressWindow = createBusyDialog(); progressWindow.setStyle("position: absolute;"); showBusyMask(progressWindow); LayoutUtils.openOverlappedWindow(this, progressWindow, "middle_center"); } /** * Get in progress mask * @return in progress mask */ private Div getMask() { if (mask == null) { mask = new Mask(); } return mask; } /** * Show in progress mask * @param window */ private void showBusyMask(Window window) { if (getParent() != null) { getParent().appendChild(getMask()); //to prevent focus to components beneath the in progress mask (see canActivate in web/js/org/idempiere/commons/window.js) StringBuilder script = new StringBuilder("(function(){let w=zk.Widget.$('#"); script.append(getParent().getUuid()).append("');"); if (window != null) { script.append("let d=zk.Widget.$('#").append(window.getUuid()).append("');w.busy=d;"); } else { script.append("w.busy=true;"); } script.append("})()"); Clients.response(new AuScript(script.toString())); } } /** * Close in progress mask */ private void hideBusyMask() { if (mask != null && mask.getParent() != null) { mask.detach(); if(getParent() != null) { StringBuilder script = new StringBuilder("(function(){let w=zk.Widget.$('#"); script.append(getParent().getUuid()).append("');w.busy=false;"); script.append("})()"); Clients.response(new AuScript(script.toString())); } } } @Override public void hideBusyDialog() { hideBusyMask(); closeBusyDialog(); } @Override public void updateUI() { swithToFinishScreen(); } /** * Switch to process execution result panel. */ protected void swithToFinishScreen() { ProcessInfo pi = getProcessInfo(); ProcessInfoUtil.setLogFromDB(pi); layoutResultPanel (topParameterLayout); StringBuilder buildMsg = new StringBuilder(getInitialMessage()); buildMsg.append("

** ") .append(pi.getSummary()) .append("

"); ((Html)messageResultContent).setContent (buildMsg.toString()); // Add Log info with zoom on record id infoResultContent.getChildren().removeAll(infoResultContent.getChildren()); appendRecordLogInfo(pi.getLogs(), infoResultContent); bOK.setLabel(Msg.getMsg(Env.getCtx(), "Parameter")); bOK.setDisabled(false); bCancel.setLabel(Msg.getMsg(Env.getCtx(), "Close")); bCancel.setDisabled(false); if (ThemeManager.isUseFontIconForImage()) { String iconSclass = "z-icon-Reset"; bOK.setIconSclass(iconSclass); LayoutUtils.addSclass("font-icon-toolbar-button", bOK); } else { bOK.setImage(ThemeManager.getThemeResource("images/Reset16.png")); bCancel.setImage(ThemeManager.getThemeResource("images/Cancel16.png")); } isParameterPage = false; m_ids = pi.getIDs(); //move message div to center to give more space to display potentially very long log info replaceComponent (resultPanelLayout, topParameterLayout); invalidate(); Clients.response(new AuEcho(this, "onAfterProcess", null)); } /** * Layout process execution result panel * @param topParameterLayout */ private void layoutResultPanel (HtmlBasedComponent topParameterLayout){ if (resultPanelLayout == null){ resultPanelLayout = new Vlayout(); resultPanelLayout.setSclass("result-parameter-layout"); ZKUpdateUtil.setVflex(resultPanelLayout, "true"); // reference for update late messageResultContent = setHeadMessage(resultPanelLayout, null); infoResultContent = new Div(); resultPanelLayout.appendChild(infoResultContent); } } /** * Replace oldComponent with newComponent * @param newComponent * @param oldComponent */ protected void replaceComponent(HtmlBasedComponent newComponent, HtmlBasedComponent oldComponent) { oldComponent.getParent().insertBefore(newComponent, oldComponent); oldComponent.detach(); } /** * Append m_logs content to {@link #logMessageTable} * @param m_logs * @param infoResultContent */ private void appendRecordLogInfo(ProcessInfoLog[] m_logs, HtmlBasedComponent infoResultContent) { if (m_logs == null) return; SimpleDateFormat dateFormat = DisplayType .getDateFormat(DisplayType.Date); logMessageTable = new Table(); logMessageTable.setId("logrecords"); logMessageTable.setDynamicProperty("border", "1"); logMessageTable.setDynamicProperty("cellpadding", "0"); logMessageTable.setDynamicProperty("cellspacing", "0"); logMessageTable.setDynamicProperty("width", "100%"); infoResultContent.appendChild(logMessageTable); boolean datePresents = false; boolean numberPresents = false; boolean msgPresents = false; for (ProcessInfoLog log : m_logs) { if (log.getP_Date() != null) datePresents = true; if (log.getP_Number() != null) numberPresents = true; if (log.getP_Msg() != null) msgPresents = true; } for (int i = 0; i < m_logs.length; i++) { Tr tr = new Tr(); logMessageTable.appendChild(tr); ProcessInfoLog log = m_logs[i]; if (datePresents) { Td td = new Td(); if (log.getP_Date() != null) { Label label = new Label(dateFormat.format(log.getP_Date())); td.appendChild(label); // label.setStyle("padding-right:100px"); } tr.appendChild(td); } if (numberPresents) { Td td = new Td(); if (log.getP_Number() != null) { Label labelPno = new Label("" + log.getP_Number()); td.appendChild(labelPno); } tr.appendChild(td); } if (msgPresents) { Td td = new Td(); if (log.getP_Msg() != null) { if (log.getAD_Table_ID() > 0 && log.getRecord_ID() > 0) { DocumentLink recordLink = new DocumentLink(log.getP_Msg(), log.getAD_Table_ID(), log.getRecord_ID()); td.appendChild(recordLink); } else { Text t = new Text(); t.setEncode(false); t.setValue(log.getP_Msg()); td.appendChild(t); } } tr.appendChild(td); } } } /** * Move back from process execution result panel to process parameter panel */ private void restart() { replaceComponent (topParameterLayout, resultPanelLayout); isParameterPage = true; bOK.setLabel(Msg.getMsg(Env.getCtx(), "Start")); bCancel.setLabel(Util.cleanAmp(Msg.translate(Env.getCtx(), ConfirmPanel.A_CANCEL))); if (ThemeManager.isUseFontIconForImage()) { String iconSclass = "z-icon-Ok"; bOK.setIconSclass(iconSclass); LayoutUtils.addSclass("font-icon-toolbar-button", bOK); } else { bOK.setImage(ThemeManager.getThemeResource("images/Ok16.png")); bCancel.setImage(ThemeManager.getThemeResource("images/Cancel16.png")); } //recreate process info ProcessInfo m_pi = new WProcessInfo(getName(), getAD_Process_ID()); m_pi.setAD_User_ID (Env.getAD_User_ID(Env.getCtx())); m_pi.setAD_Client_ID(Env.getAD_Client_ID(Env.getCtx())); setProcessInfo(m_pi); getParameterPanel().setProcessInfo(m_pi); m_ids = null; if (fSavedName != null) querySaved(); invalidate(); } /** * Handle onAfterProcess event echo from {@link #swithToFinishScreen()} */ public void onAfterProcess() { // if (!afterProcessTask()) { // If the process is a silent one and no errors occurred, close the dialog if(getShowHelp() != null && MProcess.SHOWHELP_RunSilently_TakeDefaults.equals(getShowHelp())) this.dispose(); } } /** * Optional after/post process execution task */ private boolean afterProcessTask() { // something to do? if (m_ids != null && m_ids.length > 0) { if (log.isLoggable(Level.CONFIG)) log.config(""); // Print invoices if (getAD_Process_ID() == PROCESS_C_INVOICE_GENERATE && MRole.getDefault().isCanReport(MInvoice.Table_ID)) { printInvoices(); return true; } else if (getAD_Process_ID() == PROCESS_M_INOUT_GENERATE && MRole.getDefault().isCanReport(MInOut.Table_ID)) { printShipments(); return true; } else { return false; } } return false; } // afterProcessTask /** * Print Shipments */ private void printShipments() { if (m_ids == null) return; Dialog.ask(getWindowNo(), "PrintShipments", new Callback() { @Override public void onCallback(Boolean result) { if (result) { StringBuilder buildMsg = new StringBuilder(((Html)messageResultContent).getContent()); buildMsg.append("

").append(Msg.getMsg(Env.getCtx(), "PrintShipments")).append("

"); ((Html)messageResultContent).setContent(buildMsg.toString()); showBusyDialog(); Clients.response(new AuEcho(ProcessDialog.this, "onPrintShipments", null)); } } }); } /** * Handle onPrintShipments event echo by {@link #printShipments()} */ public void onPrintShipments() { // Loop through all items List pdfList = new ArrayList(); for (int i = 0; i < m_ids.length; i++) { int M_InOut_ID = m_ids[i]; ReportEngine re = ReportEngine.get (Env.getCtx(), ReportEngine.SHIPMENT, M_InOut_ID, getWindowNo()); pdfList.add(re.getPDF()); } if (pdfList.size() > 1) { List pdfReaders = new ArrayList(); try { File outFile = File.createTempFile("PrintShipments", ".pdf"); Document document = null; PdfWriter copy = null; for (File f : pdfList) { String fileName = f.getAbsolutePath(); PdfReader reader = new PdfReader(fileName); pdfReaders.add(reader); reader.consolidateNamedDestinations(); if (document == null) { document = new Document(reader.getPageSizeWithRotation(1)); copy = PdfWriter.getInstance(document, new FileOutputStream(outFile)); document.open(); } int pages = reader.getNumberOfPages(); PdfContentByte cb = copy.getDirectContent(); for (int i = 1; i <= pages; i++) { document.newPage(); PdfImportedPage page = copy.getImportedPage(reader, i); cb.addTemplate(page, 0, 0); } } document.close(); hideBusyDialog(); Window win = new SimplePDFViewer(getProcessInfo().getTitle(), new FileInputStream(outFile)); SessionManager.getAppDesktop().showWindow(win, "center"); } catch (Exception e) { log.log(Level.SEVERE, e.getLocalizedMessage(), e); } finally { for (PdfReader reader : pdfReaders) { if (reader != null) reader.close(); } //do no harm calling this twice hideBusyDialog(); } } else if (pdfList.size() > 0) { hideBusyDialog(); try { Window win = new SimplePDFViewer(getProcessInfo().getTitle(), new FileInputStream(pdfList.get(0))); SessionManager.getAppDesktop().showWindow(win, "center"); } catch (Exception e) { log.log(Level.SEVERE, e.getLocalizedMessage(), e); } } // If the process is a silent one and no errors occurred, close the dialog if(getShowHelp() != null && MProcess.SHOWHELP_RunSilently_TakeDefaults.equals(getShowHelp())) this.dispose(); } /** * Print Invoices */ private void printInvoices() { if (m_ids == null) return; Dialog.ask(getWindowNo(), "PrintInvoices", new Callback() { @Override public void onCallback(Boolean result) { if (result) { StringBuilder buildMsg = new StringBuilder(((Html)messageResultContent).getContent()); buildMsg.append("

").append(Msg.getMsg(Env.getCtx(), "PrintInvoices")).append("

"); ((Html)messageResultContent).setContent(buildMsg.toString()); showBusyDialog(); Clients.response(new AuEcho(ProcessDialog.this, "onPrintInvoices", null)); } } }); } /** * Handle onPrintInvoices event echo by {@link #printInvoices()} */ public void onPrintInvoices() { // Loop through all items List pdfList = new ArrayList(); for (int i = 0; i < m_ids.length; i++) { int C_Invoice_ID = m_ids[i]; ReportEngine re = ReportEngine.get (Env.getCtx(), ReportEngine.INVOICE, C_Invoice_ID, getWindowNo()); pdfList.add(re.getPDF()); } if (pdfList.size() > 1) { List pdfReaders = new ArrayList(); try { File outFile = File.createTempFile("PrintInvoices", ".pdf"); Document document = null; PdfWriter copy = null; for (File f : pdfList) { PdfReader reader = new PdfReader(f.getAbsolutePath()); pdfReaders.add(reader); if (document == null) { document = new Document(reader.getPageSizeWithRotation(1)); copy = PdfWriter.getInstance(document, new FileOutputStream(outFile)); document.open(); } PdfContentByte cb = copy.getDirectContent(); // Holds the PDF int pages = reader.getNumberOfPages(); for (int i = 1; i <= pages; i++) { document.newPage(); PdfImportedPage page = copy.getImportedPage(reader, i); cb.addTemplate(page, 0, 0); } } document.close(); hideBusyDialog(); Window win = new SimplePDFViewer(getProcessInfo().getTitle(), new FileInputStream(outFile)); SessionManager.getAppDesktop().showWindow(win, "center"); } catch (Exception e) { log.log(Level.SEVERE, e.getLocalizedMessage(), e); } finally { for (PdfReader reader : pdfReaders) { if (reader != null) reader.close(); } //do no harm calling this twice hideBusyDialog(); } } else if (pdfList.size() > 0) { hideBusyDialog(); try { Window win = new SimplePDFViewer(getProcessInfo().getTitle(), new FileInputStream(pdfList.get(0))); SessionManager.getAppDesktop().showWindow(win, "center"); } catch (Exception e) { log.log(Level.SEVERE, e.getLocalizedMessage(), e); } } // If the process is a silent one and no errors occurred, close the dialog if(getShowHelp() != null && MProcess.SHOWHELP_RunSilently_TakeDefaults.equals(getShowHelp())) this.dispose(); } @Override public void onClose(Tabpanel tabPanel) { if(!isUILocked()) { Tab tab = tabPanel.getLinkedTab(); if (tab != null) { tab.close(); cleanUp(); } } } private void cleanUp() { if (m_WindowNo >= 0) { SessionManager.getAppDesktop().unregisterWindow(m_WindowNo); m_WindowNo = -1; } } } // ProcessDialog