IDEMPIERE-3562 Timeline view of record changes.
This commit is contained in:
parent
6265a1bc5c
commit
e65edd9e51
|
@ -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
|
||||||
|
;
|
||||||
|
|
|
@ -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
|
||||||
|
;
|
||||||
|
|
|
@ -131,6 +131,7 @@ public class SystemIDs
|
||||||
|
|
||||||
public final static int REFERENCE_AD_USER = 110;
|
public final static int REFERENCE_AD_USER = 110;
|
||||||
public final static int REFERENCE_DOCUMENTACTION = 135;
|
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_PAYMENTRULE = 195;
|
||||||
public final static int REFERENCE_POSTING_TYPE = 125;
|
public final static int REFERENCE_POSTING_TYPE = 125;
|
||||||
public final static int REFERENCE_POSTED = 234;
|
public final static int REFERENCE_POSTED = 234;
|
||||||
|
|
|
@ -51,6 +51,7 @@ import org.compiere.model.MRMA;
|
||||||
import org.compiere.model.MRole;
|
import org.compiere.model.MRole;
|
||||||
import org.compiere.model.MTable;
|
import org.compiere.model.MTable;
|
||||||
import org.compiere.model.PO;
|
import org.compiere.model.PO;
|
||||||
|
import org.compiere.model.SystemIDs;
|
||||||
import org.compiere.util.CLogger;
|
import org.compiere.util.CLogger;
|
||||||
import org.compiere.util.DB;
|
import org.compiere.util.DB;
|
||||||
import org.compiere.util.Env;
|
import org.compiere.util.Env;
|
||||||
|
@ -1354,4 +1355,62 @@ public class DocumentEngine implements DocAction
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill Vector with DocAction Ref_List(131) values
|
||||||
|
* @param v_value
|
||||||
|
* @param v_name
|
||||||
|
* @param v_description
|
||||||
|
*/
|
||||||
|
public static void readStatusReferenceList(ArrayList<String> v_value, ArrayList<String> v_name,
|
||||||
|
ArrayList<String> 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
|
} // DocumentEnine
|
||||||
|
|
|
@ -1390,7 +1390,7 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements
|
||||||
if (logger.isLoggable(Level.INFO)) logger.info(dbInfo);
|
if (logger.isLoggable(Level.INFO)) logger.info(dbInfo);
|
||||||
if (adTabbox.getSelectedGridTab() != null && adTabbox.getSelectedGridTab().isQueryActive())
|
if (adTabbox.getSelectedGridTab() != null && adTabbox.getSelectedGridTab().isQueryActive())
|
||||||
dbInfo = "[ " + dbInfo + " ]";
|
dbInfo = "[ " + dbInfo + " ]";
|
||||||
breadCrumb.setStatusDB(dbInfo, e);
|
breadCrumb.setStatusDB(dbInfo, e, adTabbox.getSelectedGridTab());
|
||||||
|
|
||||||
String adInfo = e.getAD_Message();
|
String adInfo = e.getAD_Message();
|
||||||
if ( adInfo == null
|
if ( adInfo == null
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.adempiere.webui.theme.ThemeManager;
|
||||||
import org.adempiere.webui.util.ZKUpdateUtil;
|
import org.adempiere.webui.util.ZKUpdateUtil;
|
||||||
import org.adempiere.webui.window.WRecordInfo;
|
import org.adempiere.webui.window.WRecordInfo;
|
||||||
import org.compiere.model.DataStatusEvent;
|
import org.compiere.model.DataStatusEvent;
|
||||||
|
import org.compiere.model.GridTab;
|
||||||
import org.compiere.model.MRole;
|
import org.compiere.model.MRole;
|
||||||
import org.compiere.util.Env;
|
import org.compiere.util.Env;
|
||||||
import org.compiere.util.Msg;
|
import org.compiere.util.Msg;
|
||||||
|
@ -88,6 +89,8 @@ public class BreadCrumb extends Div implements EventListener<Event> {
|
||||||
|
|
||||||
protected Menupopup linkPopup;
|
protected Menupopup linkPopup;
|
||||||
|
|
||||||
|
private GridTab m_gridTab;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -300,7 +303,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
String title = Msg.getMsg(Env.getCtx(), "Who") + m_text;
|
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) {
|
} else if (event.getTarget() == btnFirst) {
|
||||||
if (toolbarListener != null)
|
if (toolbarListener != null)
|
||||||
toolbarListener.onFirst();
|
toolbarListener.onFirst();
|
||||||
|
@ -409,14 +412,15 @@ public class BreadCrumb extends Div implements EventListener<Event> {
|
||||||
*/
|
*/
|
||||||
public void setStatusDB (String text)
|
public void setStatusDB (String text)
|
||||||
{
|
{
|
||||||
setStatusDB(text, null);
|
setStatusDB(text, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param text
|
* @param text
|
||||||
* @param dse
|
* @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)
|
if (text == null || text.length() == 0)
|
||||||
{
|
{
|
||||||
|
@ -435,6 +439,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
|
||||||
enableFirstNavigation(m_dse.getCurrentRow() > 0);
|
enableFirstNavigation(m_dse.getCurrentRow() > 0);
|
||||||
enableLastNavigation(m_dse.getTotalRows() > m_dse.getCurrentRow()+1);
|
enableLastNavigation(m_dse.getTotalRows() > m_dse.getCurrentRow()+1);
|
||||||
}
|
}
|
||||||
|
m_gridTab = gridTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -322,7 +322,7 @@ public class StatusBarPanel extends Panel implements EventListener<Event>, IStat
|
||||||
return;
|
return;
|
||||||
|
|
||||||
String title = Msg.getMsg(Env.getCtx(), "Who") + m_text;
|
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)
|
else if (Events.ON_CLICK.equals(event.getName()) && event.getTarget() == popup)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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<String> docActionValues = new ArrayList<String>();
|
||||||
|
ArrayList<String> docActionNames = new ArrayList<String>();
|
||||||
|
DocumentEngine.readReferenceList(docActionValues, docActionNames, new ArrayList<String>());
|
||||||
|
|
||||||
|
ArrayList<String> docStatusValues = new ArrayList<String>();
|
||||||
|
ArrayList<String> docStatusNames = new ArrayList<String>();
|
||||||
|
DocumentEngine.readStatusReferenceList(docStatusValues, docStatusNames, new ArrayList<String>());
|
||||||
|
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<String> columns = null;
|
||||||
|
List<Pair<String, String>> 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<String>();
|
||||||
|
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<String, String>(oldValue, newValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildChangeLogMessage(gridTab, docActionValues,
|
||||||
|
docActionNames, reversedStatusName, columns,
|
||||||
|
currentDocStatusOld, currentDocStatusNew,
|
||||||
|
updated, userId, changes);
|
||||||
|
currentTrx = trxName;
|
||||||
|
currentDocStatusOld = null;
|
||||||
|
currentDocStatusNew = null;
|
||||||
|
columns = new ArrayList<String>();
|
||||||
|
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<String, String>(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<String> docActionValues,
|
||||||
|
ArrayList<String> docActionNames, String reversedStatusName,
|
||||||
|
List<String> columns, String currentDocStatusOld,
|
||||||
|
String currentDocStatusNew, Timestamp updated, int userId, List<Pair<String,String>> 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<String, String> change = changes.get(i);
|
||||||
|
sb.append("<i>")
|
||||||
|
.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("</i>");
|
||||||
|
}
|
||||||
|
buildActivityMessage(updated, sb.toString(), new MUser(Env.getCtx(), userId, (String)null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildDocActionMessage(ArrayList<String> docActionValues,
|
||||||
|
ArrayList<String> 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("<i>")
|
||||||
|
.append(docActionName).append("</i>")
|
||||||
|
.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("<div class=\"help-content\">\n");
|
||||||
|
sb.append("<strong>").append(user.getName()).append("</strong> ").append(activityMessage);
|
||||||
|
sb.append("<div> </div><div>").append(dateFormat.format(activityDate)).append("</div>");
|
||||||
|
sb.append("</div>");
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -63,9 +63,12 @@ import org.zkoss.zul.Borderlayout;
|
||||||
import org.zkoss.zul.Center;
|
import org.zkoss.zul.Center;
|
||||||
import org.zkoss.zul.Div;
|
import org.zkoss.zul.Div;
|
||||||
import org.zkoss.zul.Hbox;
|
import org.zkoss.zul.Hbox;
|
||||||
|
import org.zkoss.zul.Hlayout;
|
||||||
import org.zkoss.zul.Listhead;
|
import org.zkoss.zul.Listhead;
|
||||||
import org.zkoss.zul.Listheader;
|
import org.zkoss.zul.Listheader;
|
||||||
import org.zkoss.zul.North;
|
import org.zkoss.zul.North;
|
||||||
|
import org.zkoss.zul.Radio;
|
||||||
|
import org.zkoss.zul.Radiogroup;
|
||||||
import org.zkoss.zul.South;
|
import org.zkoss.zul.South;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -92,15 +95,16 @@ public class WRecordInfo extends Window implements EventListener<Event>
|
||||||
* Record Info
|
* Record Info
|
||||||
* @param title title
|
* @param title title
|
||||||
* @param dse data status event
|
* @param dse data status event
|
||||||
|
* @param gridTab
|
||||||
*/
|
*/
|
||||||
public WRecordInfo (String title, DataStatusEvent dse)
|
public WRecordInfo (String title, DataStatusEvent dse, GridTab gridTab)
|
||||||
{
|
{
|
||||||
super ();
|
super ();
|
||||||
this.setTitle(title);
|
this.setTitle(title);
|
||||||
if (!ThemeManager.isUseCSSForWindowSize())
|
if (!ThemeManager.isUseCSSForWindowSize())
|
||||||
{
|
{
|
||||||
ZKUpdateUtil.setWindowWidthX(this, 500);
|
ZKUpdateUtil.setWindowWidthX(this, 800);
|
||||||
ZKUpdateUtil.setWindowHeightX(this, 400);
|
ZKUpdateUtil.setWindowHeightX(this, 600);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -118,7 +122,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
init ( dynInit(dse, title) );
|
init ( dynInit(gridTab, dse, title) );
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
@ -130,6 +134,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
|
||||||
|
|
||||||
|
|
||||||
private Listbox table = new Listbox();
|
private Listbox table = new Listbox();
|
||||||
|
private RecordTimeLinePanel timeLinePanel = new RecordTimeLinePanel();
|
||||||
private ConfirmPanel confirmPanel = new ConfirmPanel (false);
|
private ConfirmPanel confirmPanel = new ConfirmPanel (false);
|
||||||
|
|
||||||
/** Logger */
|
/** Logger */
|
||||||
|
@ -181,15 +186,41 @@ public class WRecordInfo extends Window implements EventListener<Event>
|
||||||
center.setParent(layout);
|
center.setParent(layout);
|
||||||
if (showTable)
|
if (showTable)
|
||||||
{
|
{
|
||||||
|
table.setSclass("record-info-changelog-table");
|
||||||
ZKUpdateUtil.setHflex(table, "true");
|
ZKUpdateUtil.setHflex(table, "true");
|
||||||
ZKUpdateUtil.setVflex(table, "true");
|
ZKUpdateUtil.setVflex(table, "true");
|
||||||
North north = new North();
|
North north = new North();
|
||||||
north.setParent(layout);
|
north.setParent(layout);
|
||||||
north.appendChild(div);
|
north.appendChild(div);
|
||||||
|
|
||||||
center.appendChild(table);
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -197,6 +228,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
|
||||||
ZKUpdateUtil.setVflex(div, "true");
|
ZKUpdateUtil.setVflex(div, "true");
|
||||||
center.appendChild(div);
|
center.appendChild(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
South south = new South();
|
South south = new South();
|
||||||
south.setSclass("dialog-footer");
|
south.setSclass("dialog-footer");
|
||||||
|
@ -219,11 +251,12 @@ public class WRecordInfo extends Window implements EventListener<Event>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamic Init
|
* Dynamic Init
|
||||||
|
* @param gridTab
|
||||||
* @param dse data status event
|
* @param dse data status event
|
||||||
* @param title title
|
* @param title title
|
||||||
* @return true if table initialized
|
* @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)
|
if (dse.CreatedBy == null)
|
||||||
return false;
|
return false;
|
||||||
|
@ -250,9 +283,13 @@ public class WRecordInfo extends Window implements EventListener<Event>
|
||||||
//get uuid
|
//get uuid
|
||||||
GridTable gridTable = null;
|
GridTable gridTable = null;
|
||||||
String tabName = 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();
|
gridTable = gridTab.getTableModel();
|
||||||
tabName = gridTab.getName();
|
tabName = gridTab.getName();
|
||||||
}
|
}
|
||||||
|
@ -300,6 +337,10 @@ public class WRecordInfo extends Window implements EventListener<Event>
|
||||||
m_permalink.setVisible(po.get_KeyColumns().length == 1);
|
m_permalink.setVisible(po.get_KeyColumns().length == 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (gridTab != null)
|
||||||
|
{
|
||||||
|
timeLinePanel.render(gridTab);
|
||||||
|
}
|
||||||
|
|
||||||
// Title
|
// Title
|
||||||
if (tabName == null && dse.AD_Table_ID != 0)
|
if (tabName == null && dse.AD_Table_ID != 0)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -277,3 +277,27 @@
|
||||||
height: 80% !important;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -89,15 +89,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.record-info-dialog {
|
.record-info-dialog {
|
||||||
width: 500px;
|
width: 800px;
|
||||||
height: 400px;
|
height: 600px;
|
||||||
}
|
}
|
||||||
@media screen and (max-width: 500px) {
|
@media screen and (max-width: 800px) {
|
||||||
.record-info-dialog {
|
.record-info-dialog {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media screen and (max-height: 400px) {
|
@media screen and (max-height: 600px) {
|
||||||
.record-info-dialog {
|
.record-info-dialog {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue