From a8444588f3158f2d72163a097a307350f770f205 Mon Sep 17 00:00:00 2001 From: EdwinBetanc0urt Date: Sun, 21 Nov 2021 22:49:09 -0400 Subject: [PATCH] feat: Add run process or report panel. --- .../ADempiere/ActionMenu/Actions.vue | 2 +- .../ADempiere/FileRender/EmptyFile/index.vue | 1 + .../ADempiere/FileRender/ExcelFile/index.vue | 49 +++--- .../ADempiere/FileRender/HtmlFile/index.vue | 5 + .../ADempiere/FileRender/PdfFile/index.vue | 3 + src/lang/ADempiere/en/actionMenu.js | 2 + src/lang/ADempiere/es/actionMenu.js | 2 + .../ADempiere/dictionary/process/actions.js | 11 ++ .../ADempiere/dictionary/process/getters.js | 60 +++++++ src/store/modules/ADempiere/processManager.js | 149 ++++++++++++++++++ src/store/modules/ADempiere/reportManager.js | 83 ++++++++++ .../ADempiere/constants/actionsMenuList.js | 13 ++ src/utils/ADempiere/dictionary/process.js | 44 ++++++ src/utils/ADempiere/resource.js | 50 ++++-- src/views/ADempiere/Process/index.vue | 53 ++++--- 15 files changed, 467 insertions(+), 60 deletions(-) create mode 100644 src/store/modules/ADempiere/processManager.js create mode 100644 src/store/modules/ADempiere/reportManager.js diff --git a/src/components/ADempiere/ActionMenu/Actions.vue b/src/components/ADempiere/ActionMenu/Actions.vue index 71e10fd5db1..5289a4cd66f 100644 --- a/src/components/ADempiere/ActionMenu/Actions.vue +++ b/src/components/ADempiere/ActionMenu/Actions.vue @@ -172,7 +172,7 @@ export default defineComponent({ } return root.$t('data.undo') } - return root.$t('components.RunProcess') + return root.$t('actionMenu.runProcessOrReport') }) const defaultActionToRun = computed(() => { diff --git a/src/components/ADempiere/FileRender/EmptyFile/index.vue b/src/components/ADempiere/FileRender/EmptyFile/index.vue index 10e75046feb..8861deca0a8 100644 --- a/src/components/ADempiere/FileRender/EmptyFile/index.vue +++ b/src/components/ADempiere/FileRender/EmptyFile/index.vue @@ -20,6 +20,7 @@ diff --git a/src/components/ADempiere/FileRender/HtmlFile/index.vue b/src/components/ADempiere/FileRender/HtmlFile/index.vue index d6676ad09fd..c737c2933f2 100644 --- a/src/components/ADempiere/FileRender/HtmlFile/index.vue +++ b/src/components/ADempiere/FileRender/HtmlFile/index.vue @@ -29,14 +29,17 @@ @@ -46,6 +49,7 @@ export default defineComponent({ height: inherit; padding-left: 10px; padding-right: 10px; + .sub-content-html { min-height: inherit; height: inherit; @@ -55,5 +59,6 @@ export default defineComponent({ width: 100%; padding-bottom: 4%; } + } diff --git a/src/components/ADempiere/FileRender/PdfFile/index.vue b/src/components/ADempiere/FileRender/PdfFile/index.vue index f9608f4af63..5cdc524bfe7 100644 --- a/src/components/ADempiere/FileRender/PdfFile/index.vue +++ b/src/components/ADempiere/FileRender/PdfFile/index.vue @@ -25,14 +25,17 @@ diff --git a/src/lang/ADempiere/en/actionMenu.js b/src/lang/ADempiere/en/actionMenu.js index af4a3a7d805..df19fea2409 100644 --- a/src/lang/ADempiere/en/actionMenu.js +++ b/src/lang/ADempiere/en/actionMenu.js @@ -22,6 +22,8 @@ const actionMenu = { shareLink: 'Share Link', withoutActions: 'Without Actions', zoomWindow: 'Zoom Window', + // process + runProcessOrReport: 'Run', // relations relations: 'Relations', withoutRelations: 'Without Relations', diff --git a/src/lang/ADempiere/es/actionMenu.js b/src/lang/ADempiere/es/actionMenu.js index 665eba4e1a7..4e03a39e743 100644 --- a/src/lang/ADempiere/es/actionMenu.js +++ b/src/lang/ADempiere/es/actionMenu.js @@ -22,6 +22,8 @@ const actionMenu = { shareLink: 'Compartir Enlace', withoutActions: 'Sin Actiones', zoomWindow: 'Acercar Ventana', + // process + runProcessOrReport: 'Ejecutar', // relations relations: 'Relaciones', withoutRelations: 'Sin Relaciones', diff --git a/src/store/modules/ADempiere/dictionary/process/actions.js b/src/store/modules/ADempiere/dictionary/process/actions.js index 55872a6af7d..c1d068153cb 100644 --- a/src/store/modules/ADempiere/dictionary/process/actions.js +++ b/src/store/modules/ADempiere/dictionary/process/actions.js @@ -32,6 +32,10 @@ export default { }) }, + /** + * Get process dictionary definition + * @param {string} uuid of dictionary + */ getProcessDefinitionFromServer({ dispatch }, { uuid }) { @@ -51,6 +55,13 @@ export default { dispatch('addProcessToList', processDefinition) resolve(processDefinition) + + if (processDefinition.isReport) { + dispatch('getListPrintFormats', { + processUuid: uuid, + processId: processDefinition.id + }) + } }) .catch(error => { reject(error) diff --git a/src/store/modules/ADempiere/dictionary/process/getters.js b/src/store/modules/ADempiere/dictionary/process/getters.js index 5a3c7f23984..fb419fcaf79 100644 --- a/src/store/modules/ADempiere/dictionary/process/getters.js +++ b/src/store/modules/ADempiere/dictionary/process/getters.js @@ -15,6 +15,8 @@ // along with this program. If not, see . import { isEmptyValue } from '@/utils/ADempiere/valueUtils' +import { isDisplayedField, isMandatoryField } from '@/utils/ADempiere/dictionary/process' +import { isNumberField } from '@/utils/ADempiere/references' /** * Dictionary Process Getters @@ -34,5 +36,63 @@ export default { return process.fieldsList } return undefined + }, + + /** + * Getter converter params with value format + * @param {String} containerUuid + * @param {Array} fieldsList + * @returns {Array} [{ columnName: name key, value: value to send }] + */ + getProcessParameters: (state, getters, rootState, rootGetters) => ({ + containerUuid, + fieldsList = [] + }) => { + if (isEmptyValue(fieldsList)) { + fieldsList = getters.getStoredFieldsFromProcess(containerUuid) + } + + const processParameters = [] + + fieldsList.forEach(fieldItem => { + const { columnName } = fieldItem + const isMandatory = isMandatoryField(fieldItem) + // evaluate displayed fields + const isDisplayed = isDisplayedField(fieldItem) && + (fieldItem.isShowedFromUser || isMandatory) + + if (!isDisplayed) { + return + } + + const value = rootGetters.getValueOfField({ + containerUuid, + columnName + }) + + if (fieldItem.isRange && isNumberField(fieldItem.displayType)) { + const valueTo = rootGetters.getValueOfField({ + containerUuid, + columnName: fieldItem.columnNameTo + }) + if (!isEmptyValue(valueTo)) { + processParameters.push({ + columnName: fieldItem.columnNameTo, + value: valueTo + }) + } + } + + if (isEmptyValue(value)) { + return + } + processParameters.push({ + columnName, + value + }) + }) + + return processParameters } + } diff --git a/src/store/modules/ADempiere/processManager.js b/src/store/modules/ADempiere/processManager.js new file mode 100644 index 00000000000..4fe93dfff67 --- /dev/null +++ b/src/store/modules/ADempiere/processManager.js @@ -0,0 +1,149 @@ +// ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution +// Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A. +// Contributor(s): Edwin Betancourt EdwinBetanc0urt@outlook.com www.erpya.com +// 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 3 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, see . + +import Vue from 'vue' +import router from '@/router' +import language from '@/lang' + +// api request methods +import { + requestRunProcess +} from '@/api/ADempiere/process' + +// utils and helper methods +import { getToken } from '@/utils/auth' +import { isEmptyValue } from '@/utils/ADempiere/valueUtils' +import { showNotification } from '@/utils/ADempiere/notification' + +const initState = { + printFormatList: {} +} + +const processManager = { + state: initState, + + mutations: { + setPrintFormatsList(state, { containerUuid, printFormatList }) { + Vue.set(state.printFormatList, containerUuid, printFormatList) + }, + resetStateProcessManager(state) { + state = initState + } + }, + + actions: { + startProcess({ dispatch, rootGetters }, { + containerUuid + }) { + return new Promise(resolve => { + const processDefinition = rootGetters.getStoredProcess(containerUuid) + + const parametersList = rootGetters.getProcessParameters({ + containerUuid: processDefinition.uuid + }) + + const isSession = !isEmptyValue(getToken()) + let procesingNotification = { + close: () => false + } + if (isSession) { + procesingNotification = showNotification({ + title: language.t('notifications.processing'), + message: processDefinition.name, + summary: processDefinition.description, + type: 'info' + }) + } + + let isProcessedError = false + let summary = '' + requestRunProcess({ + uuid: containerUuid, + parametersList + }) + .then(runProcessRepsonse => { + isProcessedError = runProcessRepsonse.isError + summary = runProcessRepsonse.summary + + resolve(runProcessRepsonse) + }) + .catch(error => { + isProcessedError = true + console.warn(`Error getting print formats: ${error.message}. Code: ${error.code}.`) + }) + .finally(() => { + const currentRoute = router.app._route + // close view if is process or report panel + router.push({ + path: '/dashboard' + }, () => {}) + dispatch('tagsView/delView', currentRoute) + + dispatch('finishProcess', { + summary, + name: processDefinition.name, + isError: isProcessedError + }) + .then(() => { + // close runing process notification + if (!isEmptyValue(procesingNotification)) { + setTimeout(() => { + procesingNotification.close() + }, 1000) + } + }) + }) + }) + }, + + finishProcess({ commit }, { + name, + summary, + isError + }) { + let processMessage = { + name, + title: language.t('notifications.succesful'), + message: language.t('notifications.processExecuted'), + type: 'success', + summary + } + + if (isError) { + const errorMessage = !isEmptyValue(summary) + ? summary + : language.t('notifications.error') + + processMessage = { + name, + title: language.t('notifications.error'), + message: errorMessage, + type: 'error' + } + } + + const isSession = !isEmptyValue(getToken()) + if (isSession) { + showNotification(processMessage) + } + } + }, + + getters: { + } +} + +export default processManager diff --git a/src/store/modules/ADempiere/reportManager.js b/src/store/modules/ADempiere/reportManager.js new file mode 100644 index 00000000000..2aa03de9966 --- /dev/null +++ b/src/store/modules/ADempiere/reportManager.js @@ -0,0 +1,83 @@ +// ADempiere-Vue (Frontend) for ADempiere ERP & CRM Smart Business Solution +// Copyright (C) 2017-Present E.R.P. Consultores y Asociados, C.A. +// Contributor(s): Edwin Betancourt EdwinBetanc0urt@outlook.com www.erpya.com +// 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 3 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, see . + +import Vue from 'vue' + +import { + requestListPrintFormats +} from '@/api/ADempiere/report' + +const initState = { + printFormatList: {} +} + +const reportManager = { + state: initState, + + mutations: { + setPrintFormatsList(state, { containerUuid, printFormatList }) { + Vue.set(state.printFormatList, containerUuid, printFormatList) + }, + resetStateReportManager(state) { + state = initState + } + }, + + actions: { + getListPrintFormats({ commit }, { + processId, + processUuid, + instanceUuid + }) { + return new Promise(resolve => { + requestListPrintFormats({ processUuid }) + .then(printFormatResponse => { + const printFormatList = printFormatResponse.records.map(printFormatItem => { + return { + ...printFormatItem, + // type: 'updateReport', + // option: 'printFormat', + instanceUuid, + processUuid, + processId + } + }) + + commit('setPrintFormatsList', { + containerUuid: processUuid, + printFormatList + }) + + resolve(printFormatList) + }) + .catch(error => { + console.warn(`Error getting print formats: ${error.message}. Code: ${error.code}.`) + }) + }) + } + }, + + getters: { + getPrintFormatList: (state) => (containerUuid) => { + return state.printFormatList[containerUuid] || [] + }, + getReportManager: (state) => { + return state.reportManager + } + } +} + +export default reportManager diff --git a/src/utils/ADempiere/constants/actionsMenuList.js b/src/utils/ADempiere/constants/actionsMenuList.js index afe4f01d2cb..016200ba7f1 100644 --- a/src/utils/ADempiere/constants/actionsMenuList.js +++ b/src/utils/ADempiere/constants/actionsMenuList.js @@ -210,6 +210,19 @@ export const recordAccess = { } } +export const runProcessOrReport = { + name: language.t('actionMenu.runProcessOrReport'), + enabled: true, + svg: false, + icon: 'el-icon-setting', + actionName: 'runProcessOrReport', + runProcessOrReport: ({ root, containerUuid }) => { + root.$store.dispatch('startProcess', { + containerUuid + }) + } +} + export const windowActions = [ createNewRecord, refreshRecords, diff --git a/src/utils/ADempiere/dictionary/process.js b/src/utils/ADempiere/dictionary/process.js index 8fa13282849..e7e9c4bea12 100644 --- a/src/utils/ADempiere/dictionary/process.js +++ b/src/utils/ADempiere/dictionary/process.js @@ -16,6 +16,50 @@ import { generateField } from '@/utils/ADempiere/dictionaryUtils' import { sortFields } from '@/utils/ADempiere/dictionary/panel' +import { isHiddenField } from '@/utils/ADempiere/references' + +/** + * Is displayed field parameter in process/report panel + * @param {number} displayType + * @param {boolean} isActive + * @param {boolean} isDisplayed + * @param {boolean} isMandatory + * @param {boolean} isMandatoryFromLogic + * @returns {boolean} + */ +export function isDisplayedField({ displayType, isActive, isDisplayed, isDisplayedFromLogic }) { + // button field not showed + if (isHiddenField(displayType)) { + return false + } + + // verify if field is active + if (!isActive) { + return false + } + + return isDisplayed && isDisplayedFromLogic +} + +/** + * Process manager mandatory logic + * @param {boolean} isMandatory + * @param {boolean} isMandatoryFromLogic + * @returns {boolean} + */ +export function isMandatoryField({ isMandatory, isMandatoryFromLogic }) { + return isMandatory || isMandatoryFromLogic +} + +/** + * Process is read only field + * @param {boolean} isReadOnly + * @param {boolean} isReadOnlyFromLogic + * @returns {boolean} + */ +export function isReadOnlyField({ isReadOnly, isReadOnlyFromLogic }) { + return isReadOnly && isReadOnlyFromLogic +} /** * Generate the actions and the associated process to store in the vuex store, diff --git a/src/utils/ADempiere/resource.js b/src/utils/ADempiere/resource.js index baadc8306be..d3ea5214e1d 100644 --- a/src/utils/ADempiere/resource.js +++ b/src/utils/ADempiere/resource.js @@ -86,6 +86,34 @@ export function getImagePath({ } } +/** + * Generate blob file and data values + * @param {string} mimeType + * @param {array} outputStream + * @returns {object} + */ +export function buildBlobAndValues({ + mimeType, + outputStream +}) { + const dataValues = Object.values(outputStream) + const blobFile = new Blob([ + Uint8Array.from(dataValues) + ], { + type: mimeType + }) + + // const blobFile = new Blob( + // [outputStream], + // { type: mimeType } + // ) + + return { + dataValues, + blobFile + } +} + /** * Build link from ouput report * @param {string} fileName @@ -100,24 +128,14 @@ export function buildLinkHref({ outputStream, isDownload = false }) { + const { blobFile } = buildBlobAndValues({ + mimeType, + outputStream + }) + const link = document.createElement('a') + link.href = window.URL.createObjectURL(blobFile) link.download = fileName - link.href = window.URL.createObjectURL(blob) - - // const blob = new Blob( - // [outputStream], - // { type: mimeType } - // ) - - const reportObject = Object.values(outputStream) - const blob = new Blob( - [ - Uint8Array.from(reportObject) - ], - { - type: mimeType - } - ) // download report file if (isDownload) { diff --git a/src/views/ADempiere/Process/index.vue b/src/views/ADempiere/Process/index.vue index bacea93bab1..4d4d845a4a5 100644 --- a/src/views/ADempiere/Process/index.vue +++ b/src/views/ADempiere/Process/index.vue @@ -59,6 +59,7 @@