diff --git a/migration/i6.2/oracle/201903281500_IDEMPIERE-3562.sql b/migration/i6.2/oracle/201903281500_IDEMPIERE-3562.sql new file mode 100644 index 0000000000..896699d2b1 --- /dev/null +++ b/migration/i6.2/oracle/201903281500_IDEMPIERE-3562.sql @@ -0,0 +1,11 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-3562 Timeline view of record changes +-- Mar 28, 2019, 3:57:53 PM MYT +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Time Line',0,0,'Y',TO_DATE('2019-03-28 15:57:52','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2019-03-28 15:57:52','YYYY-MM-DD HH24:MI:SS'),100,200491,'TimeLine','D','744a1b33-8052-457e-b763-73a75d7b4d52') +; + +SELECT register_migration_script('201903281500_IDEMPIERE-3562.sql') FROM dual +; + diff --git a/migration/i6.2/postgresql/201903281500_IDEMPIERE-3562.sql b/migration/i6.2/postgresql/201903281500_IDEMPIERE-3562.sql new file mode 100644 index 0000000000..de42b45aef --- /dev/null +++ b/migration/i6.2/postgresql/201903281500_IDEMPIERE-3562.sql @@ -0,0 +1,8 @@ +-- IDEMPIERE-3562 Timeline view of record changes +-- Mar 28, 2019, 3:57:53 PM MYT +INSERT INTO AD_Message (MsgType,MsgText,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Message_ID,Value,EntityType,AD_Message_UU) VALUES ('I','Time Line',0,0,'Y',TO_TIMESTAMP('2019-03-28 15:57:52','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2019-03-28 15:57:52','YYYY-MM-DD HH24:MI:SS'),100,200491,'TimeLine','D','744a1b33-8052-457e-b763-73a75d7b4d52') +; + +SELECT register_migration_script('201903281500_IDEMPIERE-3562.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/compiere/model/SystemIDs.java b/org.adempiere.base/src/org/compiere/model/SystemIDs.java index 9fd0c43545..686c738a80 100644 --- a/org.adempiere.base/src/org/compiere/model/SystemIDs.java +++ b/org.adempiere.base/src/org/compiere/model/SystemIDs.java @@ -131,6 +131,7 @@ public class SystemIDs public final static int REFERENCE_AD_USER = 110; public final static int REFERENCE_DOCUMENTACTION = 135; + public final static int REFERENCE_DOCUMENTSTATUS = 131; public final static int REFERENCE_PAYMENTRULE = 195; public final static int REFERENCE_POSTING_TYPE = 125; public final static int REFERENCE_POSTED = 234; diff --git a/org.adempiere.base/src/org/compiere/process/DocumentEngine.java b/org.adempiere.base/src/org/compiere/process/DocumentEngine.java index 4773229560..4c26ef0a70 100644 --- a/org.adempiere.base/src/org/compiere/process/DocumentEngine.java +++ b/org.adempiere.base/src/org/compiere/process/DocumentEngine.java @@ -51,6 +51,7 @@ import org.compiere.model.MRMA; import org.compiere.model.MRole; import org.compiere.model.MTable; import org.compiere.model.PO; +import org.compiere.model.SystemIDs; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; @@ -1354,4 +1355,62 @@ public class DocumentEngine implements DocAction return success; } + + /** + * Fill Vector with DocAction Ref_List(131) values + * @param v_value + * @param v_name + * @param v_description + */ + public static void readStatusReferenceList(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"; + PreparedStatement pstmt = null; + ResultSet rs = null; + try + { + pstmt = DB.prepareStatement(sql, null); + pstmt.setInt(1, SystemIDs.REFERENCE_DOCUMENTSTATUS); + 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); + } + } + catch (SQLException e) + { + log.log(Level.SEVERE, sql, e); + } + finally + { + DB.close(rs, pstmt); + rs = null; + pstmt = null; + } + } } // DocumentEnine diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java index b4221e07e4..7fcf504f31 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java @@ -1390,7 +1390,7 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements if (logger.isLoggable(Level.INFO)) logger.info(dbInfo); if (adTabbox.getSelectedGridTab() != null && adTabbox.getSelectedGridTab().isQueryActive()) dbInfo = "[ " + dbInfo + " ]"; - breadCrumb.setStatusDB(dbInfo, e); + breadCrumb.setStatusDB(dbInfo, e, adTabbox.getSelectedGridTab()); String adInfo = e.getAD_Message(); if ( adInfo == null diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/BreadCrumb.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/BreadCrumb.java index c22cf32fc7..e6f17ded04 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/BreadCrumb.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/BreadCrumb.java @@ -32,6 +32,7 @@ import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.util.ZKUpdateUtil; import org.adempiere.webui.window.WRecordInfo; import org.compiere.model.DataStatusEvent; +import org.compiere.model.GridTab; import org.compiere.model.MRole; import org.compiere.util.Env; import org.compiere.util.Msg; @@ -88,6 +89,8 @@ public class BreadCrumb extends Div implements EventListener { protected Menupopup linkPopup; + private GridTab m_gridTab; + /** * */ @@ -300,7 +303,7 @@ public class BreadCrumb extends Div implements EventListener { return; String title = Msg.getMsg(Env.getCtx(), "Who") + m_text; - new WRecordInfo (title, m_dse); + new WRecordInfo (title, m_dse, m_gridTab); } else if (event.getTarget() == btnFirst) { if (toolbarListener != null) toolbarListener.onFirst(); @@ -409,14 +412,15 @@ public class BreadCrumb extends Div implements EventListener { */ public void setStatusDB (String text) { - setStatusDB(text, null); + setStatusDB(text, null, null); } /** * @param text * @param dse + * @param gridTab */ - public void setStatusDB (String text, DataStatusEvent dse) + public void setStatusDB (String text, DataStatusEvent dse, GridTab gridTab) { if (text == null || text.length() == 0) { @@ -435,6 +439,7 @@ public class BreadCrumb extends Div implements EventListener { enableFirstNavigation(m_dse.getCurrentRow() > 0); enableLastNavigation(m_dse.getTotalRows() > m_dse.getCurrentRow()+1); } + m_gridTab = gridTab; } @Override diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/StatusBarPanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/StatusBarPanel.java index c11c732735..b246acb0d3 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/StatusBarPanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/StatusBarPanel.java @@ -322,7 +322,7 @@ public class StatusBarPanel extends Panel implements EventListener, IStat return; String title = Msg.getMsg(Env.getCtx(), "Who") + m_text; - new WRecordInfo (title, m_dse); + new WRecordInfo (title, m_dse, null); } else if (Events.ON_CLICK.equals(event.getName()) && event.getTarget() == popup) { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/RecordTimeLinePanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/RecordTimeLinePanel.java new file mode 100644 index 0000000000..faaed5f223 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/RecordTimeLinePanel.java @@ -0,0 +1,283 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.webui.window; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; + +import org.compiere.model.GridField; +import org.compiere.model.GridTab; +import org.compiere.model.MUser; +import org.compiere.process.DocAction; +import org.compiere.process.DocumentEngine; +import org.compiere.util.DB; +import org.compiere.util.DisplayType; +import org.compiere.util.Env; +import org.compiere.util.Msg; +import org.zkoss.util.Pair; +import org.zkoss.zul.Hbox; +import org.zkoss.zul.Html; +import org.zkoss.zul.Vlayout; + +/** + * @author hengsin + * + */ +public class RecordTimeLinePanel extends Vlayout { + + /** + * generated serial id + */ + private static final long serialVersionUID = 3420422470180313180L; + + /** + * + */ + public RecordTimeLinePanel() { + setStyle("height:100%;width:100%;overflow:auto"); + } + + public void render(GridTab gridTab) { + getChildren().clear(); + if (gridTab == null) { + return; + } + + if (gridTab.getRowCount() == 0 || gridTab.isNew()) { + return; + } + int recordId = gridTab.getRecord_ID(); + int tableId = gridTab.getAD_Table_ID(); + ArrayList docActionValues = new ArrayList(); + ArrayList docActionNames = new ArrayList(); + DocumentEngine.readReferenceList(docActionValues, docActionNames, new ArrayList()); + + ArrayList docStatusValues = new ArrayList(); + ArrayList docStatusNames = new ArrayList(); + DocumentEngine.readStatusReferenceList(docStatusValues, docStatusNames, new ArrayList()); + String reversedStatusName = null; + for(int i = 0; i < docStatusValues.size(); i++) { + if (DocAction.STATUS_Reversed.equals(docStatusValues.get(i))) + reversedStatusName = docStatusNames.get(i); + } + StringBuilder sql = new StringBuilder("SELECT u.AD_User_ID, l.created, c.columnName, l.oldValue, l.newValue, l.trxname ") + .append("FROM AD_ChangeLog l ") + .append("JOIN AD_Column c ON l.ad_column_id=c.ad_column_id ") + .append("JOIN AD_User u ON l.createdby=u.ad_user_id ") + .append("WHERE l.AD_Table_ID=? ") + .append("AND l.Record_ID=? ") + .append("ORDER BY l.created desc, l.trxName "); + PreparedStatement stmt = null; + ResultSet rs = null; + try { + stmt = DB.prepareStatement(sql.toString(), (String)null); + stmt.setInt(1, tableId); + stmt.setInt(2, recordId); + rs = stmt.executeQuery(); + List columns = null; + List> changes = null; + String currentTrx = null; + String currentDocStatusOld = null; + String currentDocStatusNew = null; + Timestamp updated = null; + int userId = -1; + while (rs.next()) { + String columnName = rs.getString(3); + String trxName = rs.getString(6); + String oldValue = rs.getString(4); + String newValue = rs.getString(5); + if (columns == null) { + columns = new ArrayList(); + changes = new ArrayList<>(); + } + if (currentTrx == null || currentTrx.equals(trxName)) { + if (currentTrx == null) + currentTrx = trxName; + if (columnName.equals("DocAction")) { + continue; + } else if (columnName.equals("DocStatus")) { + if (currentDocStatusNew == null) + currentDocStatusNew = newValue; + currentDocStatusOld = oldValue; + } else { + GridField field = gridTab.getField(columnName); + if (field != null && field.isDisplayed(true)) { + columns.add(field.getHeader()); + changes.add(new Pair(oldValue, newValue)); + } + } + } else { + buildChangeLogMessage(gridTab, docActionValues, + docActionNames, reversedStatusName, columns, + currentDocStatusOld, currentDocStatusNew, + updated, userId, changes); + currentTrx = trxName; + currentDocStatusOld = null; + currentDocStatusNew = null; + columns = new ArrayList(); + changes = new ArrayList<>(); + if (columnName.equals("DocAction")) { + continue; + } else if (columnName.equals("DocStatus")) { + if (currentDocStatusOld == null) + currentDocStatusOld = oldValue; + currentDocStatusNew = newValue; + } else { + GridField field = gridTab.getField(columnName); + if (field != null && field.isDisplayed(true)) { + columns.add(field.getHeader()); + changes.add(new Pair(oldValue, newValue)); + } + } + } + userId = rs.getInt(1); + updated = rs.getTimestamp(2); + } + buildChangeLogMessage(gridTab, docActionValues, docActionNames, + reversedStatusName, columns, currentDocStatusOld, + currentDocStatusNew, updated, userId, changes); + if (gridTab != null && gridTab.getValue("CreatedBy") != null) { + MUser createdBy = MUser.get(Env.getCtx(), (int) gridTab.getValue("CreatedBy")); + StringBuilder sb = new StringBuilder(" ") + .append(Msg.getMsg(Env.getCtx(), "Created")).append(" "); + if (gridTab.getTabNo() == 0) { + sb.append(Env.getContext(Env.getCtx(), gridTab.getWindowNo(), "_WinInfo_WindowName", true)); + } else { + sb.append(gridTab.getName()); + } + buildActivityMessage((Timestamp) gridTab.getValue("Created"), sb.toString(), createdBy); + } + } catch (SQLException e) { + e.printStackTrace(); + } finally { + DB.close(rs, stmt); + } + } + + public void buildChangeLogMessage(GridTab gridTab, + ArrayList docActionValues, + ArrayList docActionNames, String reversedStatusName, + List columns, String currentDocStatusOld, + String currentDocStatusNew, Timestamp updated, int userId, List> changes) { + if (currentDocStatusOld != null && currentDocStatusNew != null) { + buildDocActionMessage(docActionValues, docActionNames, reversedStatusName, updated, new MUser(Env.getCtx(), userId, (String)null), + currentDocStatusOld, currentDocStatusNew, gridTab.getWindowNo()); + } else if (columns != null && columns.size() > 0) { + StringBuilder sb = new StringBuilder(" "); + sb.append(Msg.getMsg(Env.getCtx(), "Updated")).append(" "); + for(int i = 0; i < columns.size(); i++) { + if (i > 0) { + if (i+1 == columns.size()) { + sb.append(" ") + .append(Msg.getMsg(Env.getCtx(), "AND").toLowerCase()) + .append(" "); + } else { + sb.append(", "); + } + } + Pair change = changes.get(i); + sb.append("") + .append(columns.get(i)); + sb.append(" (") + .append(change.getX() != null && !"NULL".equals(change.getX())? change.getX() : "") + .append(" > ") + .append(change.getY() != null && !"NULL".equals(change.getY()) ? change.getY() : "") + .append(")"); + sb.append(""); + } + buildActivityMessage(updated, sb.toString(), new MUser(Env.getCtx(), userId, (String)null)); + } + } + + private void buildDocActionMessage(ArrayList docActionValues, + ArrayList docActionNames, String reversedStatusName, Timestamp updated, + MUser user, String fromStatus, String toStatus, int windowNo) { + String docAction = null; + if (DocAction.STATUS_Completed.equals(toStatus)) { + docAction = DocAction.ACTION_Complete; + } else if (DocAction.STATUS_Approved.equals(toStatus)) { + docAction = DocAction.ACTION_Approve; + } else if (DocAction.STATUS_Voided.equals(toStatus)) { + docAction = DocAction.ACTION_Void; + } else if (DocAction.STATUS_Reversed.equals(toStatus)) { + docAction = DocAction.ACTION_Reverse_Accrual; + } else if (DocAction.STATUS_NotApproved.equals(toStatus)) { + docAction = DocAction.ACTION_Reject; + } else if (DocAction.STATUS_InProgress.equals(toStatus)) { + if (DocAction.STATUS_Completed.equals(fromStatus)) { + docAction = DocAction.ACTION_ReActivate; + } else if (DocAction.STATUS_Drafted.equals(fromStatus) || DocAction.STATUS_Invalid.equals(fromStatus)) { + docAction = DocAction.ACTION_Prepare; + } + } else if (DocAction.STATUS_Closed.equals(toStatus)) { + docAction = DocAction.ACTION_Close; + } else if (DocAction.STATUS_Invalid.equals(toStatus)) { + docAction = DocAction.ACTION_Invalidate; + } + + if (docAction == null) + return; + String docActionName = null; + if (DocAction.ACTION_Reverse_Accrual.equals(docAction) || DocAction.ACTION_Reverse_Correct.equals(docAction)) { + docActionName = reversedStatusName; + } else { + for(int i = 0; i < docActionValues.size(); i++) { + if (docActionValues.get(i).equals(docAction)) { + docActionName = docActionNames.get(i); + break; + } + } + } + StringBuilder sb = new StringBuilder("") + .append(docActionName).append("") + .append(" ").append(Env.getContext(Env.getCtx(), windowNo, "_WinInfo_WindowName", true)); + buildActivityMessage(updated, sb.toString(), user); + } + + private void buildActivityMessage(Timestamp activityDate, String activityMessage, MUser user) { + + SimpleDateFormat dateFormat = DisplayType.getDateFormat(DisplayType.DateTime); + StringBuilder sb = new StringBuilder(); + sb.append("
\n"); + sb.append("").append(user.getName()).append(" ").append(activityMessage); + sb.append("
 
").append(dateFormat.format(activityDate)).append("
"); + sb.append("
"); + Hbox hlayout = new Hbox(); + hlayout.setHflex("1"); + String hlayoutClass= "activity-card"; + if (getChildren().size() > 0) + hlayoutClass = hlayoutClass + " activity-card-spacing"; + hlayout.setSclass(hlayoutClass); + appendChild(hlayout); + Html contents = new Html(); + contents.setContent(sb.toString()); + hlayout.appendChild(contents); + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WRecordInfo.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WRecordInfo.java index b4e0ca569e..593ea9f443 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WRecordInfo.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WRecordInfo.java @@ -63,9 +63,12 @@ import org.zkoss.zul.Borderlayout; import org.zkoss.zul.Center; import org.zkoss.zul.Div; import org.zkoss.zul.Hbox; +import org.zkoss.zul.Hlayout; import org.zkoss.zul.Listhead; import org.zkoss.zul.Listheader; import org.zkoss.zul.North; +import org.zkoss.zul.Radio; +import org.zkoss.zul.Radiogroup; import org.zkoss.zul.South; /** @@ -92,15 +95,16 @@ public class WRecordInfo extends Window implements EventListener * Record Info * @param title title * @param dse data status event + * @param gridTab */ - public WRecordInfo (String title, DataStatusEvent dse) + public WRecordInfo (String title, DataStatusEvent dse, GridTab gridTab) { super (); this.setTitle(title); if (!ThemeManager.isUseCSSForWindowSize()) { - ZKUpdateUtil.setWindowWidthX(this, 500); - ZKUpdateUtil.setWindowHeightX(this, 400); + ZKUpdateUtil.setWindowWidthX(this, 800); + ZKUpdateUtil.setWindowHeightX(this, 600); } else { @@ -118,7 +122,7 @@ public class WRecordInfo extends Window implements EventListener try { - init ( dynInit(dse, title) ); + init ( dynInit(gridTab, dse, title) ); } catch (Exception e) { @@ -130,6 +134,7 @@ public class WRecordInfo extends Window implements EventListener private Listbox table = new Listbox(); + private RecordTimeLinePanel timeLinePanel = new RecordTimeLinePanel(); private ConfirmPanel confirmPanel = new ConfirmPanel (false); /** Logger */ @@ -169,7 +174,7 @@ public class WRecordInfo extends Window implements EventListener Pre pre = new Pre(); Text text = new Text(m_info.toString()); text.setParent(pre); - pre.setParent(div); + pre.setParent(div); // Borderlayout layout = new Borderlayout(); @@ -181,15 +186,41 @@ public class WRecordInfo extends Window implements EventListener center.setParent(layout); if (showTable) { + table.setSclass("record-info-changelog-table"); ZKUpdateUtil.setHflex(table, "true"); ZKUpdateUtil.setVflex(table, "true"); North north = new North(); north.setParent(layout); - north.appendChild(div); - + north.appendChild(div); center.appendChild(table); - ZKUpdateUtil.setWidth(table, "100%"); - ZKUpdateUtil.setVflex(table, true); + + Radiogroup group = new Radiogroup(); + div.appendChild(group); + Hlayout hlayout = new Hlayout(); + hlayout.setSclass("record-info-radiogroup"); + Radio radio = new Radio(Msg.getElement(Env.getCtx(), "AD_ChangeLog_ID")); + radio.setRadiogroup(group); + hlayout.appendChild(radio); + radio = new Radio(Msg.getMsg(Env.getCtx(), "TimeLine")); + radio.setRadiogroup(group); + hlayout.appendChild(radio); + div.appendChild(hlayout); + group.setSelectedIndex(0); + + group.addEventListener(Events.ON_CHECK, evt -> { + int index = group.getSelectedIndex(); + if (index == 0) { + if (table.getParent() == null && timeLinePanel.getParent() != null) { + timeLinePanel.detach(); + center.appendChild(table); + } + } else if (index == 1) { + if (table.getParent() != null && timeLinePanel.getParent() == null) { + table.detach(); + center.appendChild(timeLinePanel); + } + } + }); } else { @@ -197,6 +228,7 @@ public class WRecordInfo extends Window implements EventListener ZKUpdateUtil.setVflex(div, "true"); center.appendChild(div); } + // South south = new South(); south.setSclass("dialog-footer"); @@ -219,11 +251,12 @@ public class WRecordInfo extends Window implements EventListener /** * Dynamic Init + * @param gridTab * @param dse data status event * @param title title * @return true if table initialized */ - private boolean dynInit(DataStatusEvent dse, String title) + private boolean dynInit(GridTab gridTab, DataStatusEvent dse, String title) { if (dse.CreatedBy == null) return false; @@ -250,10 +283,14 @@ public class WRecordInfo extends Window implements EventListener //get uuid GridTable gridTable = null; String tabName = null; - if (dse.getSource() instanceof GridTab) + if (gridTab != null) { - GridTab gridTab = (GridTab) dse.getSource(); gridTable = gridTab.getTableModel(); + } + else if (dse.getSource() instanceof GridTab) + { + gridTab = (GridTab) dse.getSource(); + gridTable = gridTab.getTableModel(); tabName = gridTab.getName(); } else if (dse.getSource() instanceof GridTable) @@ -300,6 +337,10 @@ public class WRecordInfo extends Window implements EventListener m_permalink.setVisible(po.get_KeyColumns().length == 1); } } + if (gridTab != null) + { + timeLinePanel.render(gridTab); + } // Title if (tabName == null && dse.AD_Table_ID != 0) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WRecordTimeLine.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WRecordTimeLine.java new file mode 100644 index 0000000000..cc3d8a75d0 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/window/WRecordTimeLine.java @@ -0,0 +1,53 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.webui.window; + +import org.adempiere.webui.component.Window; +import org.compiere.model.GridTab; + +/** + * @author hengsin + * + */ +public class WRecordTimeLine extends Window { + + /** + * generated serial id + */ + private static final long serialVersionUID = -7887774385203335178L; + private RecordTimeLinePanel activityLayout; + + /** + * + */ + public WRecordTimeLine() { + activityLayout = new RecordTimeLinePanel(); + appendChild(activityLayout); + } + + public void render(GridTab gridTab) { + activityLayout.render(gridTab); + } +} diff --git a/org.adempiere.ui.zk/theme/default/css/fragment/adwindow.css.dsp b/org.adempiere.ui.zk/theme/default/css/fragment/adwindow.css.dsp index 09c799ea7c..d5dd3e430a 100644 --- a/org.adempiere.ui.zk/theme/default/css/fragment/adwindow.css.dsp +++ b/org.adempiere.ui.zk/theme/default/css/fragment/adwindow.css.dsp @@ -277,3 +277,27 @@ height: 80% !important; } } + +.activity-card { + border: 1px solid #d0cdc8; + border-top-left-radius: 2px; + border-top-right-radius: 2px; + border-bottom: 2px solid #d0cdc8; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + margin-left: 3px; + margin-right: 3px; +} +.activity-card-spacing { + margin-top: 8px; +} +.activity-card .help-content { + font-size: 13px; +} +.record-info-dialog .record-info-radiogroup { + padding: 4px 4px 8px 4px; +} +.record-info-dialog .record-info-changelog-table { + margin-left: 3px; + margin-right: 3px; +} diff --git a/org.adempiere.ui.zk/theme/default/css/fragment/window-size.css.dsp b/org.adempiere.ui.zk/theme/default/css/fragment/window-size.css.dsp index 17c8a6ccc6..041f190ae1 100644 --- a/org.adempiere.ui.zk/theme/default/css/fragment/window-size.css.dsp +++ b/org.adempiere.ui.zk/theme/default/css/fragment/window-size.css.dsp @@ -89,15 +89,15 @@ } .record-info-dialog { - width: 500px; - height: 400px; + width: 800px; + height: 600px; } -@media screen and (max-width: 500px) { +@media screen and (max-width: 800px) { .record-info-dialog { width: 100%; } } -@media screen and (max-height: 400px) { +@media screen and (max-height: 600px) { .record-info-dialog { height: 100%; }