From 40cc40a9abfc1deb82735b5bee2158dd248db058 Mon Sep 17 00:00:00 2001 From: Yamel Senih Date: Sat, 8 Aug 2020 16:41:20 -0400 Subject: [PATCH] REfactory from Panjian changes, update gRPC library and components (#526) --- .env.development | 9 - .github/ISSUE_TEMPLATE/question.md | 3 + README.es.md | 19 +- README.md | 9 +- babel.config.js | 13 +- mock/article.js | 4 +- mock/index.js | 19 +- mock/mock-server.js | 5 +- mock/remote-search.js | 4 +- mock/role/index.js | 8 +- mock/role/routes.js | 9 +- mock/user.js | 2 +- mock/utils.js | 48 +++ package.json | 203 +++++----- plop-templates/utils.js | 11 +- src/api/ADempiere/field/location.js | 57 +++ src/api/ADempiere/form/point-of-sales.js | 261 +++++++++++++ src/api/ADempiere/persistence.js | 39 +- src/api/ADempiere/system-core.js | 100 ++++- src/api/user.js | 18 +- src/components/ADempiere/Badge/index.vue | 5 + .../ADempiere/ContainerInfo/chatEntries.vue | 4 +- .../ADempiere/ContainerInfo/mixinInfo.js | 5 +- .../ADempiere/ContainerInfo/recordLogs.vue | 4 +- .../ADempiere/ContainerInfo/workflowLogs.vue | 4 +- .../ADempiere/ContentLoader/index.vue | 26 ++ .../ContextMenu/contextMenuDesktop.vue | 6 +- .../ADempiere/ContextMenu/contextMenuMixin.js | 96 ++--- .../ContextMenu/contextMenuMobile.vue | 9 +- .../ADempiere/Dashboard/docstatus/index.vue | 47 +-- .../ADempiere/Dashboard/mixinDashboard.js | 75 ++++ .../ADempiere/Dashboard/recentItems/index.vue | 74 +--- .../Dashboard/userfavorites/index.vue | 97 ++--- src/components/ADempiere/DataTable/index.vue | 7 +- .../ADempiere/DataTable/menu/index.vue | 60 +-- .../DataTable/menu/menuTableMixin.js | 28 +- .../DataTable/menu/tableContextMenu.vue | 2 +- src/components/ADempiere/Dialog/index.vue | 9 +- src/components/ADempiere/Dropdown/index.vue | 32 +- .../ADempiere/Field/FieldAutocomplete.vue | 257 +++++++++++++ .../ADempiere/Field/FieldBinary.vue | 6 +- src/components/ADempiere/Field/FieldDate.vue | 8 +- src/components/ADempiere/Field/FieldImage.vue | 6 +- .../Field/FieldLocation/fieldsList.js | 94 +++++ .../ADempiere/Field/FieldLocation/index.vue | 111 ++++++ .../FieldLocation/locationAddressForm.vue | 359 ++++++++++++++++++ .../Field/FieldLocation/mixinLocation.js | 53 +++ .../ADempiere/Field/FieldNumber.vue | 36 +- .../ADempiere/Field/FieldSelect.vue | 171 +++++---- src/components/ADempiere/Field/FieldText.vue | 21 + .../ADempiere/Field/FieldTextLong.vue | 7 +- src/components/ADempiere/Field/FieldTime.vue | 6 +- src/components/ADempiere/Field/FieldYesNo.vue | 6 +- .../ADempiere/Field/chatTextLong.vue | 2 +- src/components/ADempiere/Field/index.vue | 20 +- .../ADempiere/Field/mixin/mixinField.js | 24 +- .../ADempiere/Field/mixin/mixinFieldRange.js | 2 + .../ADempiere/Field/popover/contextInfo.vue | 9 +- .../Form/PriceChecking/fieldsList.js | 64 +--- .../ADempiere/Form/PriceChecking/index.vue | 295 +++++++++++--- src/components/ADempiere/Form/formMixin.js | 160 ++++---- .../ADempiere/IconElement/index.vue | 8 +- src/components/ADempiere/Pagination/index.vue | 75 ++++ .../ADempiere/Panel/mainPanelDesktop.vue | 2 +- .../ADempiere/Panel/mainPanelMixin.js | 25 +- .../ADempiere/Panel/mainPanelMobile.vue | 2 +- src/components/ADempiere/Tab/index.vue | 18 +- src/components/ADempiere/Tab/tabChildren.vue | 4 +- src/components/ADempiere/Tab/tabMixin.js | 16 +- .../ADempiere/WorkflowStatusBar/index.vue | 150 ++++---- src/components/DragSelect/index.vue | 20 +- src/components/HeaderSearch/index.vue | 3 +- src/components/JsonEditor/index.vue | 29 +- .../Tinymce/components/EditorImage.vue | 2 +- src/components/Tinymce/index.vue | 15 +- src/directive/permission/permission.js | 23 +- .../price-checking-background.png | Bin 0 -> 402972 bytes src/lang/ADempiere/en.js | 72 +++- src/lang/ADempiere/es.js | 74 +++- src/layout/components/Navbar.vue | 6 +- src/layout/components/Sidebar/Item.vue | 14 +- src/layout/components/TagsView/ScrollPane.vue | 2 +- src/layout/components/TagsView/index.vue | 8 +- src/router/index.js | 4 +- src/router/modules/ADempiere/menu.js | 24 +- src/store/modules/ADempiere/browser.js | 1 - src/store/modules/ADempiere/data.js | 39 +- src/store/modules/ADempiere/field.js | 39 +- src/store/modules/ADempiere/fieldValue.js | 91 +++-- src/store/modules/ADempiere/formDefinition.js | 9 +- src/store/modules/ADempiere/lookup.js | 13 +- src/store/modules/ADempiere/panel.js | 72 +++- src/store/modules/ADempiere/preference.js | 14 +- src/store/modules/ADempiere/utils.js | 62 ++- src/store/modules/ADempiere/window.js | 8 +- .../modules/ADempiere/windowDefinition.js | 9 +- src/store/modules/permission.js | 4 +- src/store/modules/settings.js | 5 +- src/store/modules/user.js | 115 ++++-- src/styles/sidebar.scss | 17 + src/utils/ADempiere/contextUtils.js | 10 +- src/utils/ADempiere/criteria.js | 121 ++++++ src/utils/ADempiere/dictionaryUtils.js | 239 +++++++----- src/utils/ADempiere/location.js | 16 + src/utils/ADempiere/lookupFactory.js | 183 ++++----- src/utils/ADempiere/references.js | 4 +- src/utils/ADempiere/resource.js | 19 + src/utils/ADempiere/valueFormat.js | 101 ++++- src/utils/ADempiere/valueUtils.js | 28 +- src/utils/index.js | 22 +- src/views/ADempiere/Browser/index.vue | 23 +- src/views/ADempiere/Form/index.vue | 49 ++- src/views/ADempiere/Process/index.vue | 8 +- src/views/ADempiere/Test/fieldsList.js | 6 +- src/views/ADempiere/Test/index.vue | 5 +- src/views/ADempiere/Window/index.vue | 107 +++--- src/views/components-demo/tinymce.vue | 2 +- .../dashboard/admin/components/BoxCard.vue | 2 +- .../example/components/ArticleDetail.vue | 2 +- src/views/login/forgotPassword.vue | 27 +- src/views/login/index.vue | 9 +- src/views/login/loginMixin.js | 8 +- src/views/login/setPassword.vue | 34 +- src/views/login/userEnrollment.vue | 24 +- src/views/permission/directive.vue | 6 +- src/views/profile/components/Profile.vue | 104 ++--- src/views/profile/components/RolesNavbar.vue | 61 +-- src/views/profile/components/UserCard.vue | 12 +- src/views/profile/components/role.vue | 34 +- src/views/profile/index.vue | 5 +- tests/unit/utils/param2Obj.spec.js | 14 + vue.config.js | 17 +- 132 files changed, 3999 insertions(+), 1545 deletions(-) create mode 100644 mock/utils.js create mode 100644 src/api/ADempiere/field/location.js create mode 100644 src/api/ADempiere/form/point-of-sales.js create mode 100644 src/components/ADempiere/ContentLoader/index.vue create mode 100644 src/components/ADempiere/Dashboard/mixinDashboard.js create mode 100644 src/components/ADempiere/Field/FieldAutocomplete.vue create mode 100644 src/components/ADempiere/Field/FieldLocation/fieldsList.js create mode 100644 src/components/ADempiere/Field/FieldLocation/index.vue create mode 100644 src/components/ADempiere/Field/FieldLocation/locationAddressForm.vue create mode 100644 src/components/ADempiere/Field/FieldLocation/mixinLocation.js create mode 100644 src/components/ADempiere/Pagination/index.vue create mode 100644 src/image/ADempiere/priceChecking/price-checking-background.png create mode 100644 src/utils/ADempiere/criteria.js create mode 100644 src/utils/ADempiere/location.js create mode 100644 src/utils/ADempiere/resource.js create mode 100644 tests/unit/utils/param2Obj.spec.js diff --git a/.env.development b/.env.development index 8f5856db..de583d09 100644 --- a/.env.development +++ b/.env.development @@ -3,12 +3,3 @@ ENV = 'development' # base api VUE_APP_BASE_API = '/dev-api' - -# vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, -# to control whether the babel-plugin-dynamic-import-node plugin is enabled. -# It only does one thing by converting all import() to require(). -# This configuration can significantly increase the speed of hot updates, -# when you have a large number of pages. -# Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js - -VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 0f4f8d28..f31e4c8f 100755 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -19,8 +19,10 @@ Asking questions about use #### Steps to reproduce 1. [xxx] + #### Screenshot or Gif @@ -33,6 +35,7 @@ Please only use Codepen, JSFiddle, CodeSandbox or a github repo #### Other relevant information - Your OS: +- Web Browser: - Node.js version: - vue-element-admin version: diff --git a/README.es.md b/README.es.md index f3fb1190..413d5d44 100644 --- a/README.es.md +++ b/README.es.md @@ -74,7 +74,7 @@ Sea un patrocinante y coloque su logo en nuestro LEEME en GitHub con un enlace d ``` - Iniciar / Cerrar Sesión -- Permisos de Authentication +- Permisos de Autenticación - Permisos basado en ADempiere - Página de Permisos - Directivas de permisos @@ -82,18 +82,21 @@ Sea un patrocinante y coloque su logo en nuestro LEEME en GitHub con un enlace d - Autenticación por dos pasos - Construcción Multi-entorno - - dev sit stage producción + - Desarrollo (dev) + - sit + - Escenario de pruebas (stage), + - Producción (prod) - Características Globales - I18n - Temas dinámicos - - Dynamic sidebar (soporte a rutas multi-nivel) + - Menu lateral dinámico (soporte a rutas multi-nivel) - Barra de rutas dinámica - - Tags-view (Tab page Support right-click operation) + - Tags-view (Pestañas de página, Soporta operación de clic derecho) - Svg Sprite - Datos de simulación con Mock - Pantalla completa - - Responsive Sidebar + - Menu lateral responsivo - Editor - Editor de Texto Enriquecido @@ -104,7 +107,7 @@ Sea un patrocinante y coloque su logo en nuestro LEEME en GitHub con un enlace d - Exportación a Excel - Carga de Excel - Visualización de Excel - - Exportación como zip + - Exportación como ZIP - Tabla - Tabla Dinámica @@ -139,7 +142,7 @@ Sea un patrocinante y coloque su logo en nuestro LEEME en GitHub con un enlace d - Página de Guías - ECharts (Gráficos) - Portapapeles -- Convertidor de Markdown a html +- Convertidor de Markdown a HTML ``` ## Iniciando @@ -210,7 +213,7 @@ Navegadores modernos e Internet Explorer 10+. | [IE / Edge](https://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](https://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](https://godban.github.io/browsers-support-badges/)
Chrome | [Safari](https://godban.github.io/browsers-support-badges/)
Safari | | --------- | --------- | --------- | --------- | -| IE10, IE11, Edge| últimas 2 versiones| últimas 2 versiones| últimas 2 versiones +| IE10, IE11, Edge | últimas 2 versiones | últimas 2 versiones | últimas 2 versiones | ## Licencia diff --git a/README.md b/README.md index 4d4d4bf6..bc865155 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Understanding and learning this knowledge in advance will greatly help the use o [![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/PanJiaChen/vue-element-admin/tree/CodeSandbox) -

+

@@ -83,7 +83,10 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s - Permission configuration page - Multi-environment build - - dev sit stage prod + - Develop (dev) + - sit + - Stage Test (stage) + - Production (prod) - Global Features - I18n @@ -209,7 +212,7 @@ Modern browsers and Internet Explorer 10+. | [IE / Edge](https://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](https://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](https://godban.github.io/browsers-support-badges/)
Chrome | [Safari](https://godban.github.io/browsers-support-badges/)
Safari | | --------- | --------- | --------- | --------- | -| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions +| IE10, IE11, Edge | last 2 versions | last 2 versions | last 2 versions | ## License diff --git a/babel.config.js b/babel.config.js index ba179669..fb82b271 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,5 +1,14 @@ module.exports = { presets: [ - '@vue/app' - ] + // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app + '@vue/cli-plugin-babel/preset' + ], + 'env': { + 'development': { + // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). + // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. + // https://panjiachen.github.io/vue-element-admin-site/guide/advanced/lazy-loading.html + 'plugins': ['dynamic-import-node'] + } + } } diff --git a/mock/article.js b/mock/article.js index 50218ae4..23d8ba51 100644 --- a/mock/article.js +++ b/mock/article.js @@ -1,4 +1,4 @@ -import Mock from 'mockjs' +const Mock = require('mockjs') const List = [] const count = 100 @@ -27,7 +27,7 @@ for (let i = 0; i < count; i++) { })) } -export default [ +module.exports = [ { url: '/vue-element-admin/article/list', type: 'get', diff --git a/mock/index.js b/mock/index.js index 196e2927..2eed65db 100644 --- a/mock/index.js +++ b/mock/index.js @@ -1,10 +1,10 @@ -import Mock from 'mockjs' -import { param2Obj } from '../src/utils' +const Mock = require('mockjs') +const { param2Obj } = require('./utils') -import user from './user' -import role from './role' -import article from './article' -import search from './remote-search' +const user = require('./user') +const role = require('./role') +const article = require('./article') +const search = require('./remote-search') const mocks = [ ...user, @@ -16,7 +16,7 @@ const mocks = [ // for front mock // please use it cautiously, it will redefine XMLHttpRequest, // which will cause many of your third-party libraries to be invalidated(like progress event). -export function mockXHR() { +function mockXHR() { // mock patch // https://github.com/nuysoft/Mock/issues/300 Mock.XHR.prototype.proxy_send = Mock.XHR.prototype.send @@ -54,4 +54,7 @@ export function mockXHR() { } } -export default mocks +module.exports = { + mocks, + mockXHR +} diff --git a/mock/mock-server.js b/mock/mock-server.js index 806fdacc..8941ec0f 100644 --- a/mock/mock-server.js +++ b/mock/mock-server.js @@ -8,7 +8,7 @@ const mockDir = path.join(process.cwd(), 'mock') function registerRoutes(app) { let mockLastIndex - const { default: mocks } = require('./index.js') + const { mocks } = require('./index.js') const mocksForServer = mocks.map(route => { return responseFake(route.url, route.type, route.response) }) @@ -44,9 +44,6 @@ const responseFake = (url, type, respond) => { } module.exports = app => { - // es6 polyfill - require('@babel/register') - // parse app.body // https://expressjs.com/en/4x/api.html#req.body app.use(bodyParser.json()) diff --git a/mock/remote-search.js b/mock/remote-search.js index 60809cb8..8fc49267 100644 --- a/mock/remote-search.js +++ b/mock/remote-search.js @@ -1,4 +1,4 @@ -import Mock from 'mockjs' +const Mock = require('mockjs') const NameList = [] const count = 100 @@ -10,7 +10,7 @@ for (let i = 0; i < count; i++) { } NameList.push({ name: 'mock-Pan' }) -export default [ +module.exports = [ // username search { url: '/vue-element-admin/search/user', diff --git a/mock/role/index.js b/mock/role/index.js index d957493b..4643f006 100644 --- a/mock/role/index.js +++ b/mock/role/index.js @@ -1,6 +1,6 @@ -import Mock from 'mockjs' -import { deepClone } from '../../src/utils/index.js' -import { asyncRoutes, constantRoutes } from './routes.js' +const Mock = require('mockjs') +const { deepClone } = require('../utils') +const { asyncRoutes, constantRoutes } = require('./routes.js') const routes = deepClone([...constantRoutes, ...asyncRoutes]) @@ -35,7 +35,7 @@ const roles = [ } ] -export default [ +module.exports = [ // mock get all routes form server { url: '/vue-element-admin/routes', diff --git a/mock/role/routes.js b/mock/role/routes.js index d718919c..d33f1624 100644 --- a/mock/role/routes.js +++ b/mock/role/routes.js @@ -1,6 +1,6 @@ // Just a mock data -export const constantRoutes = [ +const constantRoutes = [ { path: '/redirect', component: 'layout/Layout', @@ -72,7 +72,7 @@ export const constantRoutes = [ } ] -export const asyncRoutes = [ +const asyncRoutes = [ { path: '/permission', component: 'layout/Layout', @@ -523,3 +523,8 @@ export const asyncRoutes = [ { path: '*', redirect: '/404', hidden: true } ] + +module.exports = { + constantRoutes, + asyncRoutes +} diff --git a/mock/user.js b/mock/user.js index 859bd6f3..d82e079d 100644 --- a/mock/user.js +++ b/mock/user.js @@ -23,7 +23,7 @@ const users = { } } -export default [ +module.exports = [ // user login { url: '/vue-element-admin/user/login', diff --git a/mock/utils.js b/mock/utils.js new file mode 100644 index 00000000..f909a293 --- /dev/null +++ b/mock/utils.js @@ -0,0 +1,48 @@ +/** + * @param {string} url + * @returns {Object} + */ +function param2Obj(url) { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') + if (!search) { + return {} + } + const obj = {} + const searchArr = search.split('&') + searchArr.forEach(v => { + const index = v.indexOf('=') + if (index !== -1) { + const name = v.substring(0, index) + const val = v.substring(index + 1, v.length) + obj[name] = val + } + }) + return obj +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone') + } + const targetObj = source.constructor === Array ? [] : {} + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + }) + return targetObj +} + +module.exports = { + param2Obj, + deepClone +} diff --git a/package.json b/package.json index 7d656e8d..e76028fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "adempiere-vue", - "version": "4.2.1", + "version": "4.3.1", "description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features", "author": "Pan ", "contributors": [ @@ -9,29 +9,101 @@ "url": "https://github.com/EdwinBetanc0urt/" } ], - "license": "GPL-3.0-or-later", "scripts": { "start": "vue-cli-service serve", "dev": "vue-cli-service serve", + "lint": "eslint --ext .js,.vue src", "build:prod": "vue-cli-service build", "build:stage": "vue-cli-service build --mode staging", "preview": "node build/index.js --preview", - "lint": "eslint --ext .js,.vue src", - "test:unit": "jest --clearCache && vue-cli-service test:unit", - "test:ci": "npm run lint && npm run test:unit", + "new": "plop", "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml", - "new": "plop" + "test:unit": "jest --clearCache && vue-cli-service test:unit", + "test:ci": "npm run lint && npm run test:unit" }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } + "dependencies": { + "@adempiere/grpc-access-client": "^1.2.3", + "@adempiere/grpc-core-client": "^1.2.3", + "@adempiere/grpc-data-client": "^2.4.9", + "@adempiere/grpc-dictionary-client": "^1.4.5", + "@adempiere/grpc-enrollment-client": "^1.1.2", + "@adempiere/grpc-pos-client": "^1.4.1", + "axios": "0.19.2", + "clipboard": "2.0.6", + "codemirror": "5.56.0", + "core-js": "3.6.5", + "driver.js": "0.9.8", + "dropzone": "5.7.2", + "echarts": "4.8.0", + "element-ui": "2.13.2", + "file-saver": "2.0.2", + "fuse.js": "3.4.4", + "js-cookie": "2.2.1", + "jsonlint": "1.6.3", + "jszip": "3.5.0", + "moment": "^2.27.0", + "normalize.css": "8.0.1", + "nprogress": "0.2.0", + "path-to-regexp": "2.4.0", + "pinyin": "2.9.1", + "screenfull": "5.0.2", + "script-loader": "0.7.2", + "sortablejs": "1.10.2", + "tui-editor": "1.4.10", + "v-markdown": "^1.0.2", + "vue": "2.6.11", + "vue-content-loading": "^1.6.0", + "vue-count-to": "1.0.13", + "vue-i18n": "8.19.0", + "vue-multipane": "^0.9.5", + "vue-resize": "^0.5.0", + "vue-router": "3.3.4", + "vue-shortkey": "^3.1.7", + "vue-split-panel": "^1.0.4", + "vue-splitpane": "1.0.6", + "vuedraggable": "^2.24.0", + "vuex": "3.5.1", + "xlsx": "0.16.4" }, - "lint-staged": { - "src/**/*.{js,vue}": [ - "eslint --fix", - "git add" - ] + "devDependencies": { + "@vue/cli-plugin-babel": "4.4.6", + "@vue/cli-plugin-eslint": "4.4.6", + "@vue/cli-plugin-unit-jest": "4.4.6", + "@vue/cli-service": "4.4.6", + "@vue/test-utils": "1.0.3", + "autoprefixer": "9.8.5", + "babel-eslint": "10.1.0", + "babel-jest": "26.1.0", + "babel-plugin-dynamic-import-node": "2.3.3", + "chalk": "4.1.0", + "chokidar": "3.4.1", + "connect": "3.7.0", + "eslint": "7.5.0", + "eslint-plugin-vue": "6.2.2", + "html-webpack-plugin": "4.3.0", + "husky": "4.2.5", + "lint-staged": "10.2.11", + "mockjs": "1.1.0", + "plop": "2.7.3", + "runjs": "4.4.2", + "sass": "1.26.10", + "sass-loader": "8.0.2", + "script-ext-html-webpack-plugin": "2.1.4", + "serve-static": "1.14.1", + "svg-sprite-loader": "5.0.0", + "svgo": "1.3.2", + "vue-template-compiler": "2.6.11" + }, + "browserslist": [ + "> 1%", + "last 2 versions" + ], + "bugs": { + "url": "https://github.com/PanJiaChen/vue-element-admin/issues" + }, + "engines": { + "node": ">=8.9", + "npm": ">= 3.0.0" }, "keywords": [ "vue", @@ -42,95 +114,20 @@ "admin-template", "management-system" ], + "license": "GPL-3.0-or-later", + "lint-staged": { + "src/**/*.{js,vue}": [ + "eslint --fix", + "git add" + ] + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, "repository": { "type": "git", "url": "git+https://github.com/PanJiaChen/vue-element-admin.git" - }, - "bugs": { - "url": "https://github.com/PanJiaChen/vue-element-admin/issues" - }, - "dependencies": { - "@adempiere/grpc-access-client": "^1.2.2", - "@adempiere/grpc-core-client": "^1.1.8", - "@adempiere/grpc-data-client": "^2.4.6", - "@adempiere/grpc-dictionary-client": "^1.4.4", - "@adempiere/grpc-enrollment-client": "^1.1.1", - "@adempiere/grpc-pos-client": "^1.3.4", - "axios": "0.19.2", - "clipboard": "2.0.6", - "codemirror": "5.53.2", - "core-js": "^3.6.5", - "driver.js": "0.9.8", - "dropzone": "5.7.0", - "echarts": "4.7.0", - "element-ui": "2.13.2", - "file-saver": "2.0.2", - "fuse.js": "3.4.4", - "js-cookie": "2.2.1", - "jsonlint": "1.6.3", - "jszip": "3.4.0", - "mime-type": "^3.0.7", - "moment": "^2.24.0", - "normalize.css": "8.0.1", - "nprogress": "0.2.0", - "path-to-regexp": "2.4.0", - "pinyin": "2.9.0", - "screenfull": "5.0.2", - "script-loader": "0.7.2", - "showdown": "1.9.1", - "sortablejs": "1.10.2", - "tui-editor": "1.4.10", - "v-markdown": "^1.0.2", - "vue": "2.6.11", - "vue-count-to": "1.0.13", - "vue-i18n": "8.17.4", - "vue-multipane": "^0.9.5", - "vue-resize": "^0.5.0", - "vue-router": "3.3.2", - "vue-shortkey": "^3.1.7", - "vue-split-panel": "^1.0.4", - "vue-splitpane": "1.0.6", - "vuedraggable": "^2.23.2", - "vuex": "3.4.0", - "xlsx": "0.15.6" - }, - "devDependencies": { - "@babel/core": "7.9.0", - "@babel/register": "7.9.0", - "@vue/cli-plugin-babel": "4.3.1", - "@vue/cli-plugin-eslint": "4.3.1", - "@vue/cli-plugin-unit-jest": "^4.3.1", - "@vue/cli-service": "4.3.1", - "@vue/test-utils": "1.0.0-beta.33", - "autoprefixer": "^9.7.6", - "babel-core": "7.0.0-bridge.0", - "babel-eslint": "^10.1.0", - "babel-jest": "^25.5.1", - "chalk": "4.0.0", - "chokidar": "3.4.0", - "connect": "3.7.0", - "eslint": "^6.8.0", - "eslint-plugin-vue": "6.2.2", - "html-webpack-plugin": "4.2.1", - "husky": "^4.2.5", - "lint-staged": "10.2.0", - "mockjs": "1.1.0", - "node-sass": "^4.14.0", - "plop": "2.6.0", - "runjs": "^4.4.2", - "sass-loader": "^8.0.2", - "script-ext-html-webpack-plugin": "2.1.4", - "serve-static": "^1.14.1", - "svg-sprite-loader": "4.2.7", - "svgo": "1.3.2", - "vue-template-compiler": "2.6.11" - }, - "engines": { - "node": ">=8.9", - "npm": ">= 3.0.0" - }, - "browserslist": [ - "> 1%", - "last 2 versions" - ] + } } diff --git a/plop-templates/utils.js b/plop-templates/utils.js index 0310ca02..04987539 100644 --- a/plop-templates/utils.js +++ b/plop-templates/utils.js @@ -1,9 +1,2 @@ -exports.notEmpty = name => { - return v => { - if (!v || v.trim === '') { - return `${name} is required` - } else { - return true - } - } -} +exports.notEmpty = name => v => + !v || v.trim() === '' ? `${name} is required` : true diff --git a/src/api/ADempiere/field/location.js b/src/api/ADempiere/field/location.js new file mode 100644 index 00000000..82291df9 --- /dev/null +++ b/src/api/ADempiere/field/location.js @@ -0,0 +1,57 @@ +const tableName = 'C_Location' + +/** + * Create a location and return the created entity + * @param {array} attributes + */ +export function requestCreateLocationAddress({ + attributes +}) { + const { createEntity } = require('@/api/ADempiere/persistence.js') + + return createEntity({ + tableName, + attributes, + formatReturn: 'object' + }) +} + +/** + * Get location entity by identifier + * @param {number} id as C_Location_ID + * @param {string} uuid + */ +export function requestGetLocationAddress({ + id, + uuid +}) { + const { getEntity } = require('@/api/ADempiere/persistence.js') + + return getEntity({ + tableName, + recordId: id, + recordUuid: uuid + }) +} + +/** + * Update an existing location by id or uuid + * @param {number} id as C_Location_ID + * @param {string} uuid + * @param {array} attributes, all attributes (including empty values) + */ +export function requestUpdateLocationAddress({ + id, + uuid, + attributes +}) { + const { updateEntity } = require('@/api/ADempiere/persistence.js') + + return updateEntity({ + tableName, + recordId: id, + recordUuid: uuid, + attributes, + formatReturn: 'object' + }) +} diff --git a/src/api/ADempiere/form/point-of-sales.js b/src/api/ADempiere/form/point-of-sales.js new file mode 100644 index 00000000..13282a22 --- /dev/null +++ b/src/api/ADempiere/form/point-of-sales.js @@ -0,0 +1,261 @@ +import { POSInstance as Instance } from '@/api/ADempiere/instances.js' +import Criteria from '@/utils/ADempiere/criteria.js' + +/** + * method in price-checking.js as getProductPrice + */ +export { getProductPrice as findProduct } from '@/api/ADempiere/form/price-checking.js' + +// List Point of sales +export function requestlistPointOfSales({ + userUuid, + pageSize, + pageToken +}) { + return Instance.call(this).listPointOfSales({ + userUuid, + pageSize, + pageToken + }) +} + +// Create order from POS +export function createOrder({ + posUuid, + customerUuid, + documentTypeUuid +}) { + return Instance.call(this).createOrder({ + posUuid, + customerUuid, + documentTypeUuid + }) +} + +// Update order from POS +export function updateOrder({ + orderUuid, + posUuid, + customerUuid, + description +}) { + return Instance.call(this).updateOrder({ + orderUuid, + posUuid, + customerUuid, + description + }) +} + +// Create order line from order uuid and product +export function createOrderLine({ + orderUuid, + warehouseUuid, + productUuid, + chargeUuid, + description, + quantity, + price, + discountRate +}) { + return Instance.call(this).createOrderLine({ + orderUuid, + warehouseUuid, + productUuid, + chargeUuid, + description, + quantity, + price, + discountRate + }) +} + +// Create order line from order uuid and product +export function getOrder(orderUuid) { + return Instance.call(this).getOrder({ orderUuid }) +} + +// List orders from pos uuid +export function requestListOrders({ + posUuid, + documentNo, + businessPartnerUuid, + grandTotal, + openAmount, + isPaid, + isProcessed, + isAisleSeller, + isInvoiced, + dateOrderedFrom, + dateOrderedTo, + salesRepresentativeUuid, + pageSize, + pageToken +}) { + const criteria = new Criteria({ tableName: 'C_Order' }) + + /* + criteria.addCondition({ + columnName: 'DocumentNo', + value: documentNo + }).addCondition({ + columnName: 'C_BPartner_ID_UUID', + value: businessPartnerUuid + }).addCondition({ + columnName: 'GrandTotal', + value: grandTotal + }).addCondition({ + columnName: 'OpenAmt', + value: openAmount + }).addCondition({ + columnName: 'IsPaid', + value: isPaid + }).addCondition({ + columnName: 'Processed', + value: isProcessed + }).addCondition({ + columnName: 'IsAisleSeller', + value: isAisleSeller + }).addCondition({ + columnName: 'IsInvoiced', + value: isInvoiced + }).addCondition({ + columnName: 'DateOrderedFrom', + value: dateOrderedFrom + }).addCondition({ + columnName: 'DateOrderedTo', + value: dateOrderedTo + }).addCondition({ + columnName: 'SalesRep_ID_UUID', + value: salesRepresentativeId + }) + */ + + return Instance.call(this).listOrders({ + posUuid, + documentNo, + businessPartnerUuid, + grandTotal, + openAmount, + isPaid, + isProcessed, + isAisleSeller, + isInvoiced, + dateOrderedFrom, + dateOrderedTo, + salesRepresentativeUuid, + criteria: criteria.getCriteria(), + pageSize, + pageToken + }) +} + +// updateOrderLine orders from pos uuid +export function updateOrderLine({ + orderLineUuid, + description, + quantity, + price, + discountRate +}) { + return Instance.call(this).updateOrderLine({ + orderLineUuid, + description, + quantity, + price, + discountRate + }) +} + +// delete Order Line +export function deleteOrderLine({ + orderLineUuid +}) { + return Instance.call(this).deleteOrderLine({ + orderLineUuid + }) +} + +export function listOrderLines({ + orderUuid +}) { + return Instance.call(this).listOrderLines({ + orderUuid + }) +} + +export function getKeyLayout({ keyLayoutUuid }) { + return Instance.call(this).getKeyLayout({ + keyLayoutUuid + }) +} + +// ListProductPrice +export function requestListProductPrice({ + searchValue, + priceListUuid, + businessPartnerUuid, + warehouseUuid, + validFrom, + // Query + criteria, + pageSize, + pageToken +}) { + return Instance.call(this).requestListProductPrice({ + searchValue, + priceListUuid, + businessPartnerUuid, + warehouseUuid, + validFrom, + // Query + criteria, + pageSize, + pageToken + }) +} + +export function requestPrintOrder({ + orderUuid +}) { + console.info(`Print order ${orderUuid}`) +} + +export function requestGenerateImmediateInvoice({ + posId, + posUuid +}) { + console.info(`Generate imediate invoice with POS id ${posId}, and uuid ${posUuid}`) +} + +export function requestCompletePreparedOrder({ + orderUuid +}) { + console.info(`Complete prepared order ${orderUuid}`) +} + +export function requestReverseSalesTransaction({ + orderUuid +}) { + console.info(`Reverse sales transaction ${orderUuid}`) +} + +export function requestCreateWithdrawal({ + posId, + posUuid +}) { + console.info(`Withdrall cash with POS id ${posId}, and uuid ${posUuid}`) +} + +export function requestCreateNewCustomerReturnOrder({ + orderUuid +}) { + console.info(`New Customer Return Order ${orderUuid}`) +} + +export function requestCashClosing({ + posId, + posUuid +}) { + console.info(`Cash closing with POS id ${posId}, and uuid ${posUuid}`) +} diff --git a/src/api/ADempiere/persistence.js b/src/api/ADempiere/persistence.js index 9bb0e890..b0ba7752 100644 --- a/src/api/ADempiere/persistence.js +++ b/src/api/ADempiere/persistence.js @@ -6,10 +6,15 @@ import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js' * @param {string} tableName * @param {array} attributesList */ -export function createEntity({ tableName, attributes }) { +export function createEntity({ + tableName, + attributes, + formatReturn = 'array' +}) { return Instance.call(this).requestCreateEntity({ tableName, - attributesList: attributes + attributesList: attributes, + formatToConvert: formatReturn }) } @@ -20,12 +25,19 @@ export function createEntity({ tableName, attributes }) { * @param {string} recordUuid * @param {array} attributesList */ -export function updateEntity({ tableName, recordId, recordUuid, attributes }) { +export function updateEntity({ + tableName, + recordId, + recordUuid, + attributes, + formatReturn = 'array' +}) { return Instance.call(this).requestUpdateEntity({ tableName, recordId, recordUuid, - attributesList: attributes + attributesList: attributes, + formatToConvert: formatReturn }) } @@ -104,7 +116,7 @@ export function getEntitiesList({ * @param {string} tableName * @param {string} language * @param {string} recordUuid - * @param {integer} recordId + * @param {number} recordId */ export function requestTranslations({ tableName, language, recordUuid, recordId, pageToken, pageSize }) { return Instance.call(this).requestListTranslations({ @@ -116,3 +128,20 @@ export function requestTranslations({ tableName, language, recordUuid, recordId, pageSize }) } + +// Download a resource from file name +export function getResource({ resourceUuid }, callBack = { + onData: () => {}, + onStatus: () => {}, + onEnd: () => {} +}) { + const stream = Instance.call(this).getResource({ + resourceUuid + }) + + stream.on('data', (response) => callBack.onData(response)) + stream.on('status', (status) => callBack.onStatus(status)) + stream.on('end', (end) => callBack.onEnd(end)) + + return stream +} diff --git a/src/api/ADempiere/system-core.js b/src/api/ADempiere/system-core.js index 0cff82bf..e425addd 100644 --- a/src/api/ADempiere/system-core.js +++ b/src/api/ADempiere/system-core.js @@ -43,11 +43,15 @@ export function getWarehousesList({ }) } -// Get Country definition from server using id or uuid for record -export function getCountryDefinition({ countryUuid, countryId }) { +/** + * Get Country definition from server using id or uuid for record + * @param {string} uuid + * @param {number} id + */ +export function getCountryDefinition({ uuid, id }) { return SystemCoreInstance.call(this).requestGetCountry({ - countryUuid, - countryId + uuid, + id }) } @@ -55,3 +59,91 @@ export function getCountryDefinition({ countryUuid, countryId }) { export function listLanguages({ pageToken, pageSize }) { return Instance.call(this).requestListLanguages({ pageToken, pageSize }) } + +export function requestCreateBusinessPartner({ + value, + taxId, + duns, + naics, + name, + lastName, + description, + contactName, + eMail, + phone, + businessPartnerGroupUuid, + // Location + address1, + address2, + address3, + address4, + cityUuid, + cityName, + postalCode, + regionUuid, + regionName, + countryUuid, + posUuid +}) { + return SystemCoreInstance.call(this).requestCreateBusinessPartner({ + value, + taxId, + duns, + naics, + name, + lastName, + description, + contactName, + eMail, + phone, + businessPartnerGroupUuid, + // Location + address1, + address2, + address3, + address4, + cityUuid, + cityName, + postalCode, + regionUuid, + regionName, + countryUuid, + posUuid + }) +} + +export function requestGetBusinessPartner({ + searchValue +}) { + return SystemCoreInstance.call(this).requestGetBusinessPartner({ + searchValue + }) +} + +export function requestListBusinessPartner({ + searchValue, + value, + name, + contactName, + eMail, + postalCode, + phone, + // Query + criteria, + pageSize, + pageToken +}) { + return SystemCoreInstance.call(this).requestListBusinessPartner({ + searchValue, + value, + name, + contactName, + eMail, + postalCode, + phone, + // Query + criteria, + pageSize, + pageToken + }) +} diff --git a/src/api/user.js b/src/api/user.js index a5ba3956..a0970318 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -13,12 +13,11 @@ export function login({ userPass, role }) - } else { - return Instance.call(this).requestLoginDefault({ - userName, - userPass - }) } + return Instance.call(this).requestLoginDefault({ + userName, + userPass + }) } // Get User Info from session Uuid or token @@ -39,11 +38,6 @@ export function logout(sessionUuid) { return Instance.call(this).requestLogOut(sessionUuid) } -// Get User menu from server -export function getMenu(sessionUuid) { - return Instance.call(this).requestUserMenuFromSession(sessionUuid) -} - /** * * @param {string} attributes.sessionUuid @@ -51,6 +45,10 @@ export function getMenu(sessionUuid) { * @param {string} attributes.organizationUuid * @param {string} attributes.warehouseUuid */ +// Get User menu from server +export function getMenu(sessionUuid) { + return Instance.call(this).requestUserMenuFromSession(sessionUuid) +} export function changeRole(attributes) { return Instance.call(this).requestChangeRole(attributes) } diff --git a/src/components/ADempiere/Badge/index.vue b/src/components/ADempiere/Badge/index.vue index b32b878a..fb61baeb 100644 --- a/src/components/ADempiere/Badge/index.vue +++ b/src/components/ADempiere/Badge/index.vue @@ -44,6 +44,7 @@ + + + diff --git a/src/components/ADempiere/ContextMenu/contextMenuDesktop.vue b/src/components/ADempiere/ContextMenu/contextMenuDesktop.vue index 8beba7bb..18dae85e 100644 --- a/src/components/ADempiere/ContextMenu/contextMenuDesktop.vue +++ b/src/components/ADempiere/ContextMenu/contextMenuDesktop.vue @@ -2,7 +2,7 @@
- + @@ -112,7 +112,7 @@ + diff --git a/src/components/ADempiere/DataTable/menu/menuTableMixin.js b/src/components/ADempiere/DataTable/menu/menuTableMixin.js index 1b234ebb..eae18e2e 100644 --- a/src/components/ADempiere/DataTable/menu/menuTableMixin.js +++ b/src/components/ADempiere/DataTable/menu/menuTableMixin.js @@ -1,9 +1,9 @@ -import { supportedTypes, exportFileFromJson, exportFileZip } from '@/utils/ADempiere/exportUtil' -import { showNotification } from '@/utils/ADempiere/notification' -import { recursiveTreeSearch } from '@/utils/ADempiere/valueUtils' +import { supportedTypes, exportFileFromJson, exportFileZip } from '@/utils/ADempiere/exportUtil.js' +import { recursiveTreeSearch } from '@/utils/ADempiere/valueUtils.js' import { FIELDS_QUANTITY } from '@/utils/ADempiere/references' -export const menuTableMixin = { +export default { + name: 'MixinMenuTable', props: { parentUuid: { type: String, @@ -155,7 +155,6 @@ export const menuTableMixin = { } }, methods: { - showNotification, sortTab(actionSequence) { // TODO: Refactor and remove redundant dispatchs this.$store.dispatch('setShowDialog', { @@ -260,6 +259,7 @@ export const menuTableMixin = { }) this.$message({ message: this.$t('notifications.mandatoryFieldMissing') + fieldsEmpty, + showClose: true, type: 'info' }) }, @@ -305,18 +305,22 @@ export const menuTableMixin = { }) }, formatJson(filterVal, jsonData) { - return jsonData.map(rowData => filterVal.map(j => rowData[j])) + return jsonData.map(row => { + return filterVal.map(column => { + return row[column] + }) + }) }, zoomRecord() { const browserMetadata = this.$store.getters.getBrowser(this.$route.meta.uuid) const { elementName } = browserMetadata.fieldList.find(field => field.columnName === browserMetadata.keyColumn) const records = [] - this.getDataSelection.forEach(record => { - if (isNaN(record[browserMetadata.keyColumn])) { - records.push(record[browserMetadata.keyColumn]) - } else { - records.push(Number(record[browserMetadata.keyColumn])) + this.getDataSelection.forEach(recordItem => { + let record = recordItem[browserMetadata.keyColumn] + if (!isNaN(record)) { + record = Number(record) } + records.push(record) }) const viewSearch = recursiveTreeSearch({ @@ -333,6 +337,8 @@ export const menuTableMixin = { action: 'advancedQuery', [elementName]: records } + }).catch(error => { + console.info(`Table Menu Mixin: ${error.name}, ${error.message}`) }) } } diff --git a/src/components/ADempiere/DataTable/menu/tableContextMenu.vue b/src/components/ADempiere/DataTable/menu/tableContextMenu.vue index 4de96b04..28d60af2 100644 --- a/src/components/ADempiere/DataTable/menu/tableContextMenu.vue +++ b/src/components/ADempiere/DataTable/menu/tableContextMenu.vue @@ -39,7 +39,7 @@ diff --git a/src/components/ADempiere/Field/FieldBinary.vue b/src/components/ADempiere/Field/FieldBinary.vue index 6848c02f..25c2f69d 100644 --- a/src/components/ADempiere/Field/FieldBinary.vue +++ b/src/components/ADempiere/Field/FieldBinary.vue @@ -28,7 +28,11 @@ export default { mixins: [fieldMixin], computed: { cssClassStyle() { - return this.metadata.cssClassName + ' image-uploader' + let styleClass = ' image-uploader ' + if (!this.isEmptyValue(this.metadata.cssClassName)) { + styleClass += this.metadata.cssClassName + } + return styleClass } }, methods: { diff --git a/src/components/ADempiere/Field/FieldDate.vue b/src/components/ADempiere/Field/FieldDate.vue index 5795e6d5..dc03648a 100644 --- a/src/components/ADempiere/Field/FieldDate.vue +++ b/src/components/ADempiere/Field/FieldDate.vue @@ -112,7 +112,11 @@ export default { return picker }, cssClassStyle() { - return this.metadata.cssClassName + ' custom-field-date' + let styleClass = ' custom-field-date ' + if (!this.isEmptyValue(this.metadata.cssClassName)) { + styleClass += this.metadata.cssClassName + } + return styleClass }, /** * Parse the date format to be compatible with element-ui @@ -150,6 +154,7 @@ export default { value: { get() { let value = this.$store.getters.getValueOfField({ + parentUuid: this.metadata.parentUuid, containerUuid: this.metadata.containerUuid, columnName: this.metadata.columnName }) @@ -158,6 +163,7 @@ export default { } const valueTo = this.$store.getters.getValueOfField({ + parentUuid: this.metadata.parentUuid, containerUuid: this.metadata.containerUuid, columnName: this.metadata.columnNameTo }) diff --git a/src/components/ADempiere/Field/FieldImage.vue b/src/components/ADempiere/Field/FieldImage.vue index 15062cda..67db4c1c 100644 --- a/src/components/ADempiere/Field/FieldImage.vue +++ b/src/components/ADempiere/Field/FieldImage.vue @@ -21,7 +21,11 @@ export default { mixins: [fieldMixin], computed: { cssClassStyle() { - return this.metadata.cssClassName + ' custom-field-image' + let styleClass = ' custom-field-image ' + if (!this.isEmptyValue(this.metadata.cssClassName)) { + styleClass += this.metadata.cssClassName + } + return styleClass } }, methods: { diff --git a/src/components/ADempiere/Field/FieldLocation/fieldsList.js b/src/components/ADempiere/Field/FieldLocation/fieldsList.js new file mode 100644 index 00000000..e21d1abd --- /dev/null +++ b/src/components/ADempiere/Field/FieldLocation/fieldsList.js @@ -0,0 +1,94 @@ +const fieldBase = { + tableName: 'C_Location', + isFromDictionary: true, + overwriteDefinition: { + size: 24, + index: 0 + } +} + +export default [ + { + ...fieldBase, + columnName: 'C_Location_ID', + overwriteDefinition: { + size: 24, + isDisplayed: false, + index: 1 + } + }, + { + ...fieldBase, + columnName: 'C_Country_ID', + overwriteDefinition: { + isActiveLogics: true, // enable logics + defaultValue: '@#C_Country_ID@', + size: 24, + sequenceFields: 'CO', + index: 2 + } + }, + { + ...fieldBase, + columnName: 'C_Region_ID', + overwriteDefinition: { + size: 24, + sequenceFields: 'R', + index: 3 + } + }, + { + ...fieldBase, + columnName: 'C_City_ID', + overwriteDefinition: { + size: 24, + sequenceFields: 'C', + index: 4 + } + }, + { + ...fieldBase, + columnName: 'Address1', + overwriteDefinition: { + size: 24, + sequenceFields: 'A1', + index: 5 + } + }, + { + ...fieldBase, + columnName: 'Address2', + overwriteDefinition: { + size: 24, + sequenceFields: 'A2', + index: 6 + } + }, + { + ...fieldBase, + columnName: 'Address3', + overwriteDefinition: { + size: 24, + sequenceFields: 'A3', + index: 7 + } + }, + { + ...fieldBase, + columnName: 'Address4', + overwriteDefinition: { + size: 24, + sequenceFields: 'A4', + index: 8 + } + }, + { + ...fieldBase, + columnName: 'Postal', + overwriteDefinition: { + size: 24, + sequenceFields: 'P', + index: 9 + } + } +] diff --git a/src/components/ADempiere/Field/FieldLocation/index.vue b/src/components/ADempiere/Field/FieldLocation/index.vue new file mode 100644 index 00000000..ac75505c --- /dev/null +++ b/src/components/ADempiere/Field/FieldLocation/index.vue @@ -0,0 +1,111 @@ + + + diff --git a/src/components/ADempiere/Field/FieldLocation/locationAddressForm.vue b/src/components/ADempiere/Field/FieldLocation/locationAddressForm.vue new file mode 100644 index 00000000..7d8db74a --- /dev/null +++ b/src/components/ADempiere/Field/FieldLocation/locationAddressForm.vue @@ -0,0 +1,359 @@ + + + + + + diff --git a/src/components/ADempiere/Field/FieldLocation/mixinLocation.js b/src/components/ADempiere/Field/FieldLocation/mixinLocation.js new file mode 100644 index 00000000..1019b4fc --- /dev/null +++ b/src/components/ADempiere/Field/FieldLocation/mixinLocation.js @@ -0,0 +1,53 @@ +import { getCountryDefinition } from '@/api/ADempiere/system-core.js' +import { requestGetLocationAddress } from '@/api/ADempiere/field/location.js' + +export default { + name: 'MixinLocationField', + computed: { + currentCountryDefinition() { + return this.$store.getters['user/getCountry'] + }, + isShowedLocationForm: { + get() { + return this.$store.getters.getIsShowedLocation + }, + set() { + // empty + } + } + }, + methods: { + requestGetLocationAddress, + getCountryDefinition, + toggleShowedLocationForm() { + this.$store.commit('setShowedLocation', !this.isShowedLocationForm) + }, + setShowedLocationForm(isShow) { + this.$store.commit('setShowedLocation', isShow) + }, + /** + * TODO: Add support with sequence to displayed + * @param {object} entityValues + */ + getDisplayedValue(entityValues) { + let value = '' + + if (!this.isEmptyValue(entityValues)) { + if (!this.isEmptyValue(entityValues.Address1)) { + value = entityValues.Address1 + } + if (!this.isEmptyValue(entityValues.City)) { + value += ', ' + entityValues.City + } + if (!this.isEmptyValue(entityValues.RegionName)) { + value += ', ' + entityValues.RegionName + } + if (!this.isEmptyValue(entityValues.Postal)) { + value += ', ' + entityValues.Postal + } + } + + return value + } + } +} diff --git a/src/components/ADempiere/Field/FieldNumber.vue b/src/components/ADempiere/Field/FieldNumber.vue index a08e1a7f..9f395752 100644 --- a/src/components/ADempiere/Field/FieldNumber.vue +++ b/src/components/ADempiere/Field/FieldNumber.vue @@ -8,6 +8,7 @@ > @@ -60,7 +61,11 @@ export default { }, computed: { cssClassStyle() { - return this.metadata.cssClassName + ' custom-field-number' + let styleClass = ' custom-field-number ' + if (!this.isEmptyValue(this.metadata.cssClassName)) { + styleClass += this.metadata.cssClassName + } + return styleClass }, maxValue() { if (this.isEmptyValue(this.metadata.valueMax)) { @@ -144,17 +149,6 @@ export default { return this.$store.getters['user/getCurrency'] } }, - watch: { - isFocus(value) { - if (value) { - // focus into input number - this.$nextTick() - .then(() => { - this.$refs[this.metadata.columnName].$el.children[2].firstElementChild.focus() - }) - } - } - }, methods: { parseValue(value) { if (this.isEmptyValue(value)) { @@ -165,6 +159,10 @@ export default { customFocusGained(event) { this.isFocus = true // this.focusGained(event) + + this.$nextTick(() => { + this.$refs[this.metadata.columnName].focus() + }) }, customFocusLost(event) { this.isFocus = false @@ -230,12 +228,4 @@ export default { .el-input-number, .el-input { width: 100% !important; /* ADempiere Custom */ } - - /** Align text in right input **/ - .custom-field-number { - text-align: right !important; - input, .el-input__inner { - text-align: right !important; - } - } diff --git a/src/components/ADempiere/Field/FieldSelect.vue b/src/components/ADempiere/Field/FieldSelect.vue index 9da5a4fb..1939055f 100644 --- a/src/components/ADempiere/Field/FieldSelect.vue +++ b/src/components/ADempiere/Field/FieldSelect.vue @@ -5,7 +5,7 @@ :filterable="!isMobile" :placeholder="metadata.help" :loading="isLoading" - value-key="key" + value-key="id" :class="cssClassStyle" clearable :multiple="isSelectMultiple" @@ -19,7 +19,7 @@ @@ -27,7 +27,7 @@ + + diff --git a/src/components/ADempiere/Field/FieldTextLong.vue b/src/components/ADempiere/Field/FieldTextLong.vue index f08ed1a3..90ed5312 100644 --- a/src/components/ADempiere/Field/FieldTextLong.vue +++ b/src/components/ADempiere/Field/FieldTextLong.vue @@ -40,9 +40,12 @@ export default { }, computed: { cssClassStyle() { - let styleClass = this.metadata.cssClassName + let styleClass = ' custom-field-text-long ' if (this.isDisabled) { - styleClass += ' custom-field-text-long-disable' + styleClass += ' custom-field-text-long-disable ' + } + if (!this.isEmptyValue(this.metadata.cssClassName)) { + styleClass += this.metadata.cssClassName } return styleClass }, diff --git a/src/components/ADempiere/Field/FieldTime.vue b/src/components/ADempiere/Field/FieldTime.vue index 89d709b8..3a57ee2d 100644 --- a/src/components/ADempiere/Field/FieldTime.vue +++ b/src/components/ADempiere/Field/FieldTime.vue @@ -46,7 +46,11 @@ export default { return -Infinity }, cssClassStyle() { - return this.metadata.cssClassName + ' custom-field-time' + let styleClass = ' custom-field-time ' + if (!this.isEmptyValue(this.metadata.cssClassName)) { + styleClass += this.metadata.cssClassName + } + return styleClass } }, methods: { diff --git a/src/components/ADempiere/Field/FieldYesNo.vue b/src/components/ADempiere/Field/FieldYesNo.vue index d9775460..381c0a42 100644 --- a/src/components/ADempiere/Field/FieldYesNo.vue +++ b/src/components/ADempiere/Field/FieldYesNo.vue @@ -35,7 +35,11 @@ export default { }, computed: { cssClassStyle() { - return this.metadata.cssClassName + ' custom-field-yes-no' + let styleClass = ' custom-field-yes-no ' + if (!this.isEmptyValue(this.metadata.cssClassName)) { + styleClass += this.metadata.cssClassName + } + return styleClass } }, methods: { diff --git a/src/components/ADempiere/Field/chatTextLong.vue b/src/components/ADempiere/Field/chatTextLong.vue index 67cf1d2e..350f1f75 100644 --- a/src/components/ADempiere/Field/chatTextLong.vue +++ b/src/components/ADempiere/Field/chatTextLong.vue @@ -118,7 +118,7 @@ export default { }) }, preHandleChange(value) { - if (this.clean) { + if (this.isEmptyValue(value)) { this.$store.dispatch('setchatText', value) .then(responseComment => { this.$store.dispatch('setMarkDown', false) diff --git a/src/components/ADempiere/Field/index.vue b/src/components/ADempiere/Field/index.vue index 296f7dd0..7f1923dc 100644 --- a/src/components/ADempiere/Field/index.vue +++ b/src/components/ADempiere/Field/index.vue @@ -38,17 +38,20 @@ v-if="isDocuemntStatus" :field="fieldAttributes" /> + + + import('@/components/ADempiere/Field/FieldAutocomplete') + break case 'FieldBinary': field = () => import('@/components/ADempiere/Field/FieldBinary') break @@ -145,6 +151,9 @@ export default { case 'FieldImage': field = () => import('@/components/ADempiere/Field/FieldImage') break + case 'FieldLocation': + field = () => import('@/components/ADempiere/Field/FieldLocation') + break case 'FieldLocator': field = () => import('@/components/ADempiere/Field/FieldLocator') break @@ -187,7 +196,8 @@ export default { if (this.isAdvancedQuery) { return this.field.isShowedFromUser } - return fieldIsDisplayed(this.field) && (this.isMandatory || this.field.isShowedFromUser || this.inTable) + return fieldIsDisplayed(this.field) && + (this.isMandatory || this.field.isShowedFromUser || this.inTable) }, isMandatory() { if (this.isAdvancedQuery) { @@ -235,7 +245,7 @@ export default { return this.field.isReadOnlyFromLogic } // other type of panels (process/report) - return isUpdateableAllFields + return Boolean(isUpdateableAllFields) }, isFieldOnly() { if (this.inTable || this.field.isFieldOnly) { @@ -338,11 +348,11 @@ export default { return false }, isContextInfo() { - if (!this.isAdvancedQuery) { + if (this.field.panelType !== 'window') { return false } - return (this.field.contextInfo && this.field.contextInfo.isActive) || - (this.field.reference && this.field.reference.windowsList.length) + return Boolean(this.field.contextInfo && this.field.contextInfo.isActive) || + Boolean(this.field.reference && this.field.reference.windowsList.length) } }, watch: { diff --git a/src/components/ADempiere/Field/mixin/mixinField.js b/src/components/ADempiere/Field/mixin/mixinField.js index 97588a70..a09d22be 100644 --- a/src/components/ADempiere/Field/mixin/mixinField.js +++ b/src/components/ADempiere/Field/mixin/mixinField.js @@ -28,11 +28,16 @@ export default { return Boolean(this.metadata.readonly || this.metadata.disabled) }, cssClassStyle() { - return this.metadata.cssClassName + let styleClass = '' + if (!this.isEmptyValue(this.metadata.cssClassName)) { + styleClass = this.metadata.cssClassName + } + return styleClass }, value: { get() { return this.$store.getters.getValueOfField({ + parentUuid: this.metadata.parentUuid, containerUuid: this.metadata.containerUuid, columnName: this.metadata.columnName }) @@ -63,18 +68,6 @@ export default { this.requestFocus() } }, - watch: { - // valueModel(value) { - // if (this.metadata.inTable) { - // this.value = this.parseValue(value) - // } - // }, - // 'metadata.value'(value) { - // if (!this.metadata.inTable) { - // this.value = this.parseValue(value) - // } - // } - }, methods: { /** * Parse the value to a new value if required for element-ui component @@ -177,6 +170,11 @@ export default { // if is custom field, set custom handle change value if (this.metadata.isCustomField) { + if (this.metadata.isActiveLogics) { + this.$store.dispatch('changeDependentFieldsList', { + field: this.metadata + }) + } return } this.$store.dispatch('notifyFieldChange', { diff --git a/src/components/ADempiere/Field/mixin/mixinFieldRange.js b/src/components/ADempiere/Field/mixin/mixinFieldRange.js index 8febf31a..85be94a4 100644 --- a/src/components/ADempiere/Field/mixin/mixinFieldRange.js +++ b/src/components/ADempiere/Field/mixin/mixinFieldRange.js @@ -3,10 +3,12 @@ export default { value: { get() { const value = this.$store.getters.getValueOfField({ + parentUuid: this.metadata.parentUuid, containerUuid: this.metadata.containerUuid, columnName: this.metadata.columnName }) const valueTo = this.$store.getters.getValueOfField({ + parentUuid: this.metadata.parentUuid, containerUuid: this.metadata.containerUuid, columnName: this.metadata.columnName }) diff --git a/src/components/ADempiere/Field/popover/contextInfo.vue b/src/components/ADempiere/Field/popover/contextInfo.vue index d26c958e..daba6dbd 100644 --- a/src/components/ADempiere/Field/popover/contextInfo.vue +++ b/src/components/ADempiere/Field/popover/contextInfo.vue @@ -34,8 +34,7 @@ + + diff --git a/src/components/ADempiere/Form/formMixin.js b/src/components/ADempiere/Form/formMixin.js index c795151f..438c9b8f 100644 --- a/src/components/ADempiere/Form/formMixin.js +++ b/src/components/ADempiere/Form/formMixin.js @@ -4,25 +4,38 @@ import { createFieldFromDefinition, createFieldFromDictionary } from '@/utils/AD export default { name: 'FormMixn', components: { - Field + Field, + FieldDefinition: Field }, props: { metadata: { type: Object, - required: true + default: () => {} } }, data() { + let containerUuid = this.$route.meta.uuid + if (!this.isEmptyValue(this.metadata)) { + containerUuid = this.metadata.containerUuid + if (this.isEmptyValue(containerUuid)) { + containerUuid = this.metadata.uuid + } + } + return { + formUuid: this.$route.meta.uuid, + containerUuid, fieldsList: [], panelMetadata: {}, isLoaded: false, + isCustomForm: false, + unsubscribe: () => {}, panelType: 'form' } }, computed: { getterPanel() { - return this.$store.getters.getPanel(this.metadata.containerUuid) + return this.$store.getters.getPanel(this.containerUuid) } }, created() { @@ -31,16 +44,26 @@ export default { methods: { createFieldFromDefinition, createFieldFromDictionary, + /** + * Using forms and events with the enter key prevents the page from reloading + * with @submit.native.prevent="notSubmitForm" in el-form component + */ + notSubmitForm(event) { + event.preventDefault() + return false + }, async getPanel() { const panel = this.getterPanel - if (panel) { + if (!this.isEmptyValue(panel)) { this.fieldsList = panel.fieldList this.isLoaded = true + this.panelMetadata = panel } else { await this.generateFieldsList() this.$store.dispatch('addPanel', { ...this.metadata, - uuid: this.metadata.containerUuid, + isCustomForm: this.isCustomForm, + uuid: this.containerUuid, panelType: this.panelType, fieldList: this.fieldsList }) @@ -48,16 +71,21 @@ export default { this.fieldsList = responsePanel.fieldList this.$store.dispatch('changeFormAttribute', { - containerUuid: this.metadata.containerUuid, + containerUuid: this.containerUuid, attributeName: 'fieldList', attributeValue: this.fieldsList }) + this.panelMetadata = responsePanel + this.runAfterLoadPanel() }) .finally(() => { this.isLoaded = true }) } }, + runAfterLoadPanel() { + // some actions after load form panel + }, generateFieldsList() { let sequence = 0 const incrementSequence = (newValue) => { @@ -68,73 +96,75 @@ export default { return sequence } - return new Promise(resolve => { - const additionalAttributes = { - containerUuid: this.metadata.containerUuid, - isEvaluateValueChanges: false, - panelType: this.panelType - } + if (this.metadata) { + return new Promise(resolve => { + const additionalAttributes = { + containerUuid: this.containerUuid, + isEvaluateValueChanges: false, + panelType: this.panelType + } - const fieldsListFromDictionary = [] - const fieldsListFromMetadata = [] + const fieldsListFromDictionary = [] + const fieldsListFromMetadata = [] - this.fieldsList.forEach(fieldElement => { - if (fieldElement.isFromDictionary) { - // set sequence - if (fieldElement.overwriteDefinition) { - if (this.isEmptyValue(fieldElement.overwriteDefinition.sequence)) { + this.fieldsList.forEach(fieldElement => { + if (fieldElement.isFromDictionary) { + // set sequence + if (fieldElement.overwriteDefinition) { + if (this.isEmptyValue(fieldElement.overwriteDefinition.sequence)) { + fieldElement.overwriteDefinition.sequence = incrementSequence() + } else { + incrementSequence(fieldElement.overwriteDefinition.sequence) + } + } else { + fieldElement.overwriteDefinition = {} fieldElement.overwriteDefinition.sequence = incrementSequence() - } else { - incrementSequence(fieldElement.overwriteDefinition.sequence) } - } else { - fieldElement.overwriteDefinition = {} - fieldElement.overwriteDefinition.sequence = incrementSequence() - } - fieldsListFromDictionary.push( - this.createFieldFromDictionary({ - ...fieldElement, - ...additionalAttributes - }) - ) - } else { - // set sequence - if (fieldElement.definition) { - if (this.isEmptyValue(fieldElement.definition.sequence)) { + fieldsListFromDictionary.push( + this.createFieldFromDictionary({ + ...fieldElement, + ...additionalAttributes + }) + ) + } else { + // set sequence + if (fieldElement.definition) { + if (this.isEmptyValue(fieldElement.definition.sequence)) { + fieldElement.definition.sequence = incrementSequence() + } else { + incrementSequence(fieldElement.definition.sequence) + } + } else { + fieldElement.definition = {} fieldElement.definition.sequence = incrementSequence() - } else { - incrementSequence(fieldElement.definition.sequence) } - } else { - fieldElement.definition = {} - fieldElement.definition.sequence = incrementSequence() - } - fieldsListFromMetadata.push( - this.createFieldFromDefinition({ - ...fieldElement, - ...additionalAttributes + fieldsListFromMetadata.push( + this.createFieldFromDefinition({ + ...fieldElement, + ...additionalAttributes + }) + ) + } + }) + let fieldsList = fieldsListFromMetadata + + if (this.isEmptyValue(fieldsListFromDictionary)) { + this.fieldsList = fieldsList + resolve(fieldsList) + this.isLoaded = true + } else { + Promise.all(fieldsListFromDictionary) + .then(responsefields => { + fieldsList = fieldsList.concat(responsefields) + resolve(fieldsList) + this.fieldsList = fieldsList + this.isLoaded = true }) - ) } }) - let fieldsList = fieldsListFromMetadata - - if (this.isEmptyValue(fieldsListFromDictionary)) { - this.fieldsList = fieldsList - resolve(fieldsList) - this.isLoaded = true - } else { - Promise.all(fieldsListFromDictionary) - .then(responsefields => { - fieldsList = fieldsList.concat(responsefields) - resolve(fieldsList) - this.fieldsList = fieldsList - this.isLoaded = true - }) - } - }) + } }, // Set value for one field from panel // use example: setValue('ProductName', 'Patio Fun') @@ -150,9 +180,9 @@ export default { // Use example: setValues(values) setValues({ values = {}, withOutColumnNames = [] }) { this.$store.dispatch('notifyPanelChange', { - containerUuid: this.metadata.containerUuid, + containerUuid: this.containerUuid, panelType: this.metadata.panelType, - newValues: values, + attributes: values, withOutColumnNames, isChangedAllValues: true }) @@ -161,7 +191,7 @@ export default { this.$store.dispatch('addAction', { name: action.name, action: action.action, - containerUuid: this.metadata.containerUuid + containerUuid: this.containerUuid }) } } diff --git a/src/components/ADempiere/IconElement/index.vue b/src/components/ADempiere/IconElement/index.vue index e15a48f7..6241bb4e 100644 --- a/src/components/ADempiere/IconElement/index.vue +++ b/src/components/ADempiere/IconElement/index.vue @@ -72,7 +72,7 @@ export default { display: inline-block; vertical-align: middle; - /deep/ .el-input__inner { + ::v-deep .el-input__inner { border-radius: 0; border: 0; padding-left: 0; @@ -93,7 +93,7 @@ export default { vertical-align: middle; height: 28px; - /deep/ .el-input__inner { + ::v-deep .el-input__inner { border-radius: 0; border: 0; padding-left: 0; @@ -117,7 +117,7 @@ export default { display: inline-block; vertical-align: middle; - /deep/ .el-input__inner { + ::v-deep .el-input__inner { border-radius: 0; border: 0; padding-left: 0; @@ -136,7 +136,7 @@ export default { display: inline-block; vertical-align: middle; - /deep/ .el-input__inner { + ::v-deep .el-input__inner { border-radius: 0; border: 0; padding-left: 0; diff --git a/src/components/ADempiere/Pagination/index.vue b/src/components/ADempiere/Pagination/index.vue new file mode 100644 index 00000000..e9f8e60b --- /dev/null +++ b/src/components/ADempiere/Pagination/index.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/components/ADempiere/Panel/mainPanelDesktop.vue b/src/components/ADempiere/Panel/mainPanelDesktop.vue index e39146db..e95cde6a 100644 --- a/src/components/ADempiere/Panel/mainPanelDesktop.vue +++ b/src/components/ADempiere/Panel/mainPanelDesktop.vue @@ -121,7 +121,7 @@ - diff --git a/src/components/HeaderSearch/index.vue b/src/components/HeaderSearch/index.vue index cdff74ff..424143c1 100644 --- a/src/components/HeaderSearch/index.vue +++ b/src/components/HeaderSearch/index.vue @@ -202,7 +202,8 @@ export default { border-radius: 0; display: inline-block; vertical-align: middle; - /deep/ .el-input__inner { + + ::v-deep .el-input__inner { border-radius: 0; border: 0; padding-left: 0; diff --git a/src/components/JsonEditor/index.vue b/src/components/JsonEditor/index.vue index 07b282e4..c05b090c 100644 --- a/src/components/JsonEditor/index.vue +++ b/src/components/JsonEditor/index.vue @@ -54,19 +54,24 @@ export default { } - diff --git a/src/components/Tinymce/components/EditorImage.vue b/src/components/Tinymce/components/EditorImage.vue index d4a98e84..07d48e6c 100644 --- a/src/components/Tinymce/components/EditorImage.vue +++ b/src/components/Tinymce/components/EditorImage.vue @@ -104,7 +104,7 @@ export default { diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue index a33f5e7d..47d75ec6 100644 --- a/src/layout/components/TagsView/ScrollPane.vue +++ b/src/layout/components/TagsView/ScrollPane.vue @@ -94,7 +94,7 @@ export default { position: relative; overflow: hidden; width: 100%; - /deep/ { + ::v-deep { .el-scrollbar__bar { bottom: 0px; } diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue index 22f34620..61954a94 100644 --- a/src/layout/components/TagsView/index.vue +++ b/src/layout/components/TagsView/index.vue @@ -13,7 +13,13 @@ ref="tag" :key="tag.path" :class="isActive(tag)?'active':''" - :to="{ name: tag.name, path: tag.path, query: tag.query, fullPath: tag.fullPath, params: tag.params }" + :to="{ + name: tag.name, + path: tag.path, + query: tag.query, + fullPath: tag.fullPath, + params: tag.params + }" tag="span" class="tags-view-item" @click.middle.native="!isAffix(tag) ? closeSelectedTag(tag) : ''" diff --git a/src/router/index.js b/src/router/index.js index ddd17216..8ac95d12 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -25,7 +25,7 @@ import nestedRouter from './modules/nested' * meta : { roles: ['admin','editor'] control the page roles (you can set multiple roles) title: 'title' the name show in sidebar and breadcrumb (recommend set) - icon: 'svg-name' the icon show in the sidebar + icon: 'svg-name'/'el-icon-x' the icon show in the sidebar noCache: true if set true, the page will no be cached(default is false) affix: true if set true, the tag will affix in the tags-view breadcrumb: false if set false, the item will hidden in breadcrumb(default is true) @@ -223,7 +223,7 @@ export const asyncRoutes = [ name: 'Example', meta: { title: 'example', - icon: 'example' + icon: 'el-icon-s-help' }, children: [ { diff --git a/src/router/modules/ADempiere/menu.js b/src/router/modules/ADempiere/menu.js index 3e1eada6..cbbaee78 100644 --- a/src/router/modules/ADempiere/menu.js +++ b/src/router/modules/ADempiere/menu.js @@ -1,9 +1,11 @@ import { getMenu } from '@/api/user' import { getToken } from '@/utils/auth' import { convertAction } from '@/utils/ADempiere/dictionaryUtils' +import store from '@/store' /* Layout */ import Layout from '@/layout' +import { isEmptyValue } from '@adempiere/grpc-core-client' const staticRoutes = [ { @@ -71,22 +73,22 @@ const staticRoutes = [ ] // Get Menu from server -export function loadMainMenu() { +export function loadMainMenu(organizationId = 0) { return new Promise(resolve => { + organizationId = isEmptyValue(store.getters['user/getOrganization']) ? 0 : store.getters['user/getOrganization'].id getMenu(getToken()).then(menuResponse => { const asyncRoutesMap = [] menuResponse.childsList.forEach(menuElement => { - const optionMenu = getRouteFromMenuItem(menuElement) + const optionMenu = getRouteFromMenuItem(menuElement, organizationId) if (menuElement.isSummary) { menuElement.childsList.forEach(menu => { - const childsSumaryConverted = getChildFromAction(menu, 0) - + const childsSumaryConverted = getChildFromAction({ menu, index: 0, organizationId }) optionMenu.children.push(childsSumaryConverted) optionMenu.children[0].meta.childs.push(childsSumaryConverted) optionMenu.meta.childs.push(childsSumaryConverted) }) } else { - const childsConverted = getChildFromAction(menuElement) + const childsConverted = getChildFromAction({ menu: menuElement, index: undefined, organizationId }) optionMenu.children.push(childsConverted) optionMenu.meta.childs.push(childsConverted) @@ -101,16 +103,17 @@ export function loadMainMenu() { } // Get Only Child -function getChildFromAction(menu, index) { +function getChildFromAction({ menu, index, organizationId }) { const action = menu.action const actionAttributes = convertAction(action) + const clientId = store.getters['user/getRole'].clientId let routeIdentifier = actionAttributes.name + '/' + menu.id if (menu.isSummary) { routeIdentifier = '/' + menu.id } const option = { - path: routeIdentifier, + path: '/' + clientId + '/' + organizationId + '/' + routeIdentifier, component: actionAttributes.component, name: menu.uuid, hidden: index > 0, @@ -136,7 +139,7 @@ function getChildFromAction(menu, index) { if (actionAttributes.isIndex || actionAttributes.name === 'summary') { option['children'] = [] menu.childsList.forEach(child => { - const menuConverted = getChildFromAction(child, 1) + const menuConverted = getChildFromAction({ menu: child, index: 1, organizationId }) option.children.push(menuConverted) option.meta.childs.push(menuConverted) }) @@ -145,11 +148,12 @@ function getChildFromAction(menu, index) { } // Convert menu item from server to Route -function getRouteFromMenuItem(menu) { +function getRouteFromMenuItem(menu, organizationId) { const action = menu.action const actionAttributes = convertAction(action) + const clientId = store.getters['user/getRole'].clientId const optionMenu = { - path: '/' + menu.id, + path: '/' + clientId + '/' + organizationId + '/' + menu.id, redirect: '/' + menu.id + '/index', component: Layout, name: menu.uuid, diff --git a/src/store/modules/ADempiere/browser.js b/src/store/modules/ADempiere/browser.js index e1bdaf7d..a7f24c64 100644 --- a/src/store/modules/ADempiere/browser.js +++ b/src/store/modules/ADempiere/browser.js @@ -118,7 +118,6 @@ const browserControl = { // datatables attributes isNew: false, isEdit: false, - isSelected: false, isReadOnlyFromRow: false } }) diff --git a/src/store/modules/ADempiere/data.js b/src/store/modules/ADempiere/data.js index dc00e321..833e886c 100644 --- a/src/store/modules/ADempiere/data.js +++ b/src/store/modules/ADempiere/data.js @@ -9,9 +9,11 @@ import { unlockPrivateAccessFromServer } from '@/api/ADempiere/private-access' import { - isEmptyValue, - convertArrayPairsToObject + extractPagingToken, + isEmptyValue } from '@/utils/ADempiere/valueUtils.js' +import { convertArrayKeyValueObject } from '@/utils/ADempiere/valueFormat.js' +import { typeValue } from '@/utils/ADempiere/valueUtils.js' import { parseContext } from '@/utils/ADempiere/contextUtils' import { showMessage } from '@/utils/ADempiere/notification' import { TABLE, TABLE_DIRECT } from '@/utils/ADempiere/references' @@ -210,14 +212,14 @@ const data = { // TODO: Evaluate if is field is read only and FieldSelect .filter(itemField => { return itemField.componentPath === 'FieldSelect' || - String(values[itemField.columnName]) === '[object Object]' || + typeValue(values[itemField.columnName]) === 'OBJECT' || itemField.isSQLValue }) .map(async itemField => { const { columnName, componentPath } = itemField let valueGetDisplayColumn = values[columnName] - if (String(values[columnName]) === '[object Object]') { + if (typeValue(values[columnName]) === 'OBJECT') { if (componentPath === 'FieldSelect') { values[columnName] = ' ' values[itemField.displayColumnName] = ' ' @@ -248,7 +250,7 @@ const data = { } if (!isEmptyValue(valueGetDisplayColumn) && - String(valueGetDisplayColumn) === '[object Object]' && + typeValue(valueGetDisplayColumn) === 'OBJECT' && valueGetDisplayColumn.isSQL) { // get value from Query valueGetDisplayColumn = await dispatch('getValueBySQL', { @@ -532,14 +534,10 @@ const data = { const recordsList = dataResponse.recordsList.map(itemRecord => { const values = itemRecord.values - // datatables attributes - values.isNew = false - values.isEdit = false - values.isSelected = false - values.isReadOnlyFromRow = false + let isEdit = false if (isAddDefaultValues) { if (inEdited.find(itemEdit => itemEdit.UUID === values.UUID)) { - values.isEdit = true + isEdit = true } } @@ -547,7 +545,11 @@ const data = { // server (empty fields are not brought from the server) return { ...defaultValues, - ...values + ...values, + // datatables attributes + isNew: false, + isEdit, + isReadOnlyFromRow: false } }) @@ -556,10 +558,7 @@ const data = { if (isEmptyValue(token)) { token = dataStore.nextPageToken } else { - token = token.slice(0, -2) - if (token.substr(-1, 1) === '-') { - token = token.slice(0, -1) - } + token = extractPagingToken(token) } if (isShowNotification) { let searchMessage = 'searchWithOutRecords' @@ -579,7 +578,7 @@ const data = { selection: dataStore.selection, recordCount: dataResponse.recordCount, nextPageToken: token, - originalNextPageToken: originalNextPageToken, + originalNextPageToken, isAddRecord, pageNumber: dataStore.pageNumber, tableName, @@ -670,7 +669,7 @@ const data = { }) } if (Array.isArray(values)) { - values = convertArrayPairsToObject({ + values = convertArrayKeyValueObject({ arrayToConvert: values }) } @@ -702,7 +701,7 @@ const data = { displayColumn, withOutColumnNames = [] }) { - dispatch('setContext', { + dispatch('setPreferenceContext', { parentUuid, containerUuid, columnName, @@ -985,7 +984,7 @@ const data = { */ getSelectionToServer: (state, getters, rootState, rootGetters) => ({ containerUuid, selection = [] }) => { const selectionToServer = [] - const withOut = ['isEdit', 'isSelected', 'isSendToServer'] + const withOut = ['isEdit', 'isSendToServer'] if (selection.length <= 0) { selection = getters.getDataRecordSelection(containerUuid) diff --git a/src/store/modules/ADempiere/field.js b/src/store/modules/ADempiere/field.js index af99becc..5da14c42 100644 --- a/src/store/modules/ADempiere/field.js +++ b/src/store/modules/ADempiere/field.js @@ -3,7 +3,9 @@ import { getField as getFieldFromDictionary } from '@/api/ADempiere/dictionary' const initStateLookup = { referenceList: [], fieldList: [], - validationRuleList: [] + validationRuleList: [], + fieldListLocation: [], + isShowedLocation: false } const field = { @@ -20,11 +22,17 @@ const field = { }, resetStateLookup(state) { state = initStateLookup + }, + setShowedLocation(state, isShowed) { + state.isShowedLocation = isShowed + }, + setfieldListLocation(state, fieldListLocation) { + state.fieldListLocation = fieldListLocation } }, actions: { // Get Reference from Server based on criteria - getFieldFromServer({ commit, rootGetters }, { + getFieldFromServer({ commit }, { fieldUuid, columnUuid, elementUuid, @@ -52,35 +60,41 @@ const field = { fieldResponse.tableName = tableName fieldResponse.columnName = columnName } - commit('addField', { - fieldResponse - }) + + commit('addField', fieldResponse) + return fieldResponse }) .catch(error => { - console.warn(`Get Field, Select Base - Error ${error.code}: ${error.message}.`) + console.warn(`Get Field - Error ${error.code}: ${error.message}.`) }) + }, + changeSequence({ commit }, params) { + commit('setfieldListLocation', params) } }, getters: { + getIsShowedLocation: (state) => { + return state.isShowedLocation + }, getFieldFromUuid: (state) => (uuid) => { return state.fieldList.find(fieldItem => { - return fieldItem.fieldResponse.uuid === uuid + return fieldItem.uuid === uuid }) }, getFieldFromColumnUuid: (state) => (columnUuid) => { return state.fieldList.find(fieldItem => { - return fieldItem.fieldResponse.columnUuid === columnUuid + return fieldItem.columnUuid === columnUuid }) }, getFieldFromElementUuid: (state) => (elementUuid) => { return state.fieldList.find(fieldItem => { - return fieldItem.fieldResponse.elementUuid === elementUuid + return fieldItem.elementUuid === elementUuid }) }, getFieldFromElementColumnName: (state) => (elementColumnName) => { return state.fieldList.find(fieldItem => { - return fieldItem.fieldResponse.elementColumnName === elementColumnName + return fieldItem.elementColumnName === elementColumnName }) }, getFieldFromTableNameAndColumnName: (state) => ({ @@ -88,8 +102,11 @@ const field = { columnName }) => { return state.fieldList.find(fieldItem => { - return fieldItem.fieldResponse.tableName === tableName && fieldItem.fieldResponse.columnName === columnName + return fieldItem.tableName === tableName && fieldItem.columnName === columnName }) + }, + getFieldLocation: (state) => { + return state.fieldListLocation } } } diff --git a/src/store/modules/ADempiere/fieldValue.js b/src/store/modules/ADempiere/fieldValue.js index c0e38cdd..8693ab38 100644 --- a/src/store/modules/ADempiere/fieldValue.js +++ b/src/store/modules/ADempiere/fieldValue.js @@ -14,19 +14,41 @@ const value = { field: {} } }, - updateValueOfField(state, payload) { + /** + * + * @param {string} parentUuid + * @param {string} containerUuid + * @param {string} columnName + * @param {mixed} value + * @param {boolean} isOverWriteParent // overwite parent context values + */ + updateValueOfField(state, { + parentUuid, + containerUuid, + columnName, + value, + isOverWriteParent = true + }) { // Only Parent - if (payload.parentUuid) { - const keyParent = payload.parentUuid + '_' + payload.columnName - if (payload.value !== state.field[keyParent]) { - Vue.set(state.field, keyParent, payload.value) + if (parentUuid) { + const keyParent = parentUuid + '_' + columnName + const valueParent = state.field[keyParent] + if (value !== valueParent) { + if (isOverWriteParent) { + Vue.set(state.field, keyParent, value) + } else { + if (isEmptyValue(value)) { + // tab child no replace parent context with empty + Vue.set(state.field, keyParent, value) + } + } } } // Only Container - if (payload.containerUuid) { - const keyContainer = payload.containerUuid + '_' + payload.columnName - if (payload.value !== state.field[keyContainer]) { - Vue.set(state.field, keyContainer, payload.value) + if (containerUuid) { + const keyContainer = containerUuid + '_' + columnName + if (value !== state.field[keyContainer]) { + Vue.set(state.field, keyContainer, value) } } }, @@ -65,16 +87,26 @@ const value = { } }, getters: { - getValueOfField: (state) => ({ containerUuid, columnName }) => { - return state.field[containerUuid + '_' + columnName] - }, - getValueOfContainer: (state) => ({ parentUuid, containerUuid, columnName }) => { - // get in tab level - let value = state.field[containerUuid + '_' + columnName] - if (isEmptyValue(value) && parentUuid) { + getValueOfField: (state) => ({ + parentUuid, + containerUuid, + columnName + }) => { + let key = '' + let value + if (containerUuid) { + // get in tab level + key += containerUuid + '_' + } + key += columnName + value = state.field[key] + + if (parentUuid && isEmptyValue(value)) { // get in window level + key = parentUuid + '_' + columnName value = state.field[parentUuid + '_' + columnName] } + return value }, /** @@ -87,9 +119,9 @@ const value = { getValuesView: (state) => ({ parentUuid, containerUuid, + isOnlyColumns = true, format = 'array' }) => { - console.log(parentUuid, containerUuid) // generate context with parent uuid or container uuid associated const contextAllContainers = {} Object.keys(state.field).forEach(key => { @@ -102,26 +134,15 @@ const value = { const objectValues = {} const pairsValues = Object.keys(contextAllContainers).map(key => { const value = contextAllContainers[key] - if (isEmptyValue(value)) { - return - } - let columnName - if (parentUuid) { - if (!key.includes(containerUuid)) { - columnName = key - .replace(`${parentUuid}_`, '') - .replace(`${containerUuid}_`, '') - // set window parent context - objectValues[columnName] = value - } - // next if is tab context - return { - columnName, - value - } + if (isOnlyColumns) { + key = key + .replace(`${parentUuid}_`, '') + .replace(`${containerUuid}_`, '') } + // TODO: Verify if overwrite key with empty value + const columnName = key + // set container context (smart browser, process/report, form) - columnName = key.replace(`${containerUuid}_`, '') objectValues[columnName] = value return { columnName, diff --git a/src/store/modules/ADempiere/formDefinition.js b/src/store/modules/ADempiere/formDefinition.js index cc93e554..8d2b6ec7 100644 --- a/src/store/modules/ADempiere/formDefinition.js +++ b/src/store/modules/ADempiere/formDefinition.js @@ -20,10 +20,17 @@ const form = { if (payload.attributeNameControl) { value = payload.form[payload.attributeNameControl] } - payload.form[payload.attributeName] = value + if (isEmptyValue(payload.attributeName)) { + payload.form[payload.attributeName] = value + } } }, actions: { + addForm({ commit, getters }, metadataForm) { + if (!getters.getForm(metadataForm.uuid)) { + commit('addForm', metadataForm) + } + }, getFormFromServer({ commit, dispatch }, { id, containerUuid, diff --git a/src/store/modules/ADempiere/lookup.js b/src/store/modules/ADempiere/lookup.js index 3c6793f8..4543d604 100644 --- a/src/store/modules/ADempiere/lookup.js +++ b/src/store/modules/ADempiere/lookup.js @@ -64,7 +64,8 @@ const lookup = { const label = lookupItemResponse.values.DisplayColumn const option = { label: isEmptyValue(label) ? ' ' : label, - key: value // lookupItemResponse.values.KeyColumn + uuid: lookupItemResponse.uuid, + id: value // lookupItemResponse.values.KeyColumn } commit('addLoockupItem', { @@ -121,21 +122,23 @@ const lookup = { const list = [] lookupListResponse.recordsList.forEach(itemLookup => { const { - KeyColumn: key, + KeyColumn: id, DisplayColumn: label } = itemLookup.values - if (!isEmptyValue(key)) { + if (!isEmptyValue(id)) { list.push({ label, - key + id, + uuid: itemLookup.uuid }) } }) if (isAddBlankValue) { list.unshift({ label: ' ', - key: blankValue + id: blankValue, + uuid: undefined }) } commit('addLoockupList', { diff --git a/src/store/modules/ADempiere/panel.js b/src/store/modules/ADempiere/panel.js index 728f1529..4ba332ee 100644 --- a/src/store/modules/ADempiere/panel.js +++ b/src/store/modules/ADempiere/panel.js @@ -5,7 +5,11 @@ // - Window: Just need storage tab and fields // - Process & Report: Always save a panel and parameters // - Smart Browser: Can have a search panel, table panel and process panel -import { isEmptyValue, parsedValueComponent } from '@/utils/ADempiere/valueUtils.js' +import { + isEmptyValue, + parsedValueComponent, + typeValue +} from '@/utils/ADempiere/valueUtils.js' import { convertObjectToKeyValue } from '@/utils/ADempiere/valueFormat.js' import evaluator, { getContext, parseContext, specialColumns } from '@/utils/ADempiere/contextUtils.js' import { showMessage } from '@/utils/ADempiere/notification.js' @@ -25,7 +29,7 @@ const panel = { payload.panel = payload.newPanel }, changeFieldLogic(state, payload) { - if (payload.isDisplayedFromLogic !== undefined && payload.isDisplayedFromLogic !== null) { + if (!isEmptyValue(payload.isDisplayedFromLogic)) { payload.field.isDisplayedFromLogic = Boolean(payload.isDisplayedFromLogic) } payload.field.isMandatoryFromLogic = Boolean(payload.isMandatoryFromLogic) @@ -57,7 +61,12 @@ const panel = { }, actions: { addPanel({ commit, dispatch, getters }, params) { - const { panelType, uuid: containerUuid } = params + const { + panelType, + // isParentTab, + // parentUuid, + uuid: containerUuid + } = params let keyColumn = '' let selectionColumn = [] let identifierColumns = [] @@ -86,13 +95,16 @@ const panel = { } } // For all - if (['browser', 'process', 'report', 'form', 'table'].includes(panelType) || (panelType === 'window' && params.isParentTab)) { - dispatch('setContext', { - parentUuid: params.parentUuid, - containerUuid, - columnName: itemField.columnName, - value: itemField.value - }) + if (['browser', 'process', 'report', 'form', 'table'].includes(panelType) || + (panelType === 'window' && params.isParentTab)) { + // TODO: Verity with updateValueOfField, setContext, setPreferenceContext + // commit('updateValueOfField', { + // parentUuid, + // containerUuid, + // // isOverWriteParent: Boolean(isParentTab), + // columnName: itemField.columnName, + // value: itemField.value + // }) } // Get dependent fields if (!isEmptyValue(itemField.parentFieldsList) && itemField.isActive) { @@ -138,13 +150,17 @@ const panel = { commit('addPanel', params) - if (!['form', 'table'].includes(panelType)) { + if (!['table'].includes(panelType)) { dispatch('setDefaultValues', { parentUuid: params.parentUuid, containerUuid, + // isOverWriteParent: Boolean(isParentTab), panelType }) } + if (params.isCustomForm) { + dispatch('addForm', params) + } return params }, @@ -317,6 +333,7 @@ const panel = { parentUuid, containerUuid, panelType = 'window', + isOverWriteParent = true, isNewRecord = false }) { return new Promise(resolve => { @@ -346,6 +363,8 @@ const panel = { ...oldRoute.query, action: 'create-new' } + }).catch(error => { + console.info(`Panel Store: ${error.message}`) }) } showMessage({ @@ -381,6 +400,7 @@ const panel = { dispatch('updateValuesOfContainer', { parentUuid, containerUuid, + isOverWriteParent, attributes: defaultAttributes }) // .then(() => { @@ -421,11 +441,16 @@ const panel = { }) }, // Change all values of panel and dispatch actions for each field - notifyPanelChange({ commit }, { + notifyPanelChange({ commit, dispatch }, { parentUuid, containerUuid, attributes = [] }) { + if (typeValue(attributes) === 'OBJECT') { + attributes = convertObjectToKeyValue({ + object: attributes + }) + } // Update field commit('updateValuesOfContainer', { parentUuid, @@ -517,6 +542,10 @@ const panel = { // } // } // }) + + dispatch('setIsloadContext', { + containerUuid + }) }, /** * Handle all trigger for a field: @@ -539,6 +568,7 @@ const panel = { field = fieldsList.find(fieldItem => fieldItem.columnName === columnName) } const value = getters.getValueOfField({ + parentUuid: field.parentUuid, containerUuid: field.containerUuid, columnName: field.columnName }) @@ -596,6 +626,11 @@ const panel = { }) }) }, + /** + * Change dependent fields (default value, logics displayed, mandatory and read only) + * @param {object} field, definition and attributes + * TODO: Not working with fields generated on lookupFactory + */ changeDependentFieldsList({ commit, dispatch, getters }, { field }) { @@ -669,7 +704,7 @@ const panel = { columnName: fieldDependent.columnName, value: newValue }) - // + // dispatch('notifyFieldChange', { // parentUuid, // containerUuid, @@ -841,6 +876,7 @@ const panel = { // all optionals (not mandatory) fields fieldsList.forEach(fieldItem => { const value = getters.getValueOfField({ + parentUuid: fieldItem.parentUuid, containerUuid, columnName: fieldItem.columnName }) @@ -1023,7 +1059,7 @@ const panel = { } } - if (String(valueToReturn) === '[object Object]') { + if (typeValue(valueToReturn) === 'OBJECT') { if (!valueToReturn.isSQL) { valueToReturn = valueToReturn.value } @@ -1066,7 +1102,7 @@ const panel = { isSQL: isSQLTo }) } - + // TODO: Check with typeValue function if (typeof valueTo === 'object') { valueTo = { ...valueTo, @@ -1074,7 +1110,7 @@ const panel = { } } } - if (String(valueTo) === '[object Object]') { + if (typeValue(valueTo) === 'OBJECT') { if (!valueTo.isSQL) { valueTo = valueTo.value } @@ -1251,12 +1287,14 @@ const panel = { // from field value const value = rootGetters.getValueOfField({ + parentUuid: fieldItem.parentUuid, containerUuid, columnName }) let valueTo if (fieldItem.isRange && fieldItem.componentPath !== 'FieldNumber') { valueTo = rootGetters.getValueOfField({ + parentUuid: fieldItem.parentUuid, containerUuid, columnName: fieldItem.columnNameTo }) @@ -1281,6 +1319,7 @@ const panel = { valueTo = row[parameterItem.columnNameTo] } else { value = rootGetters.getValueOfField({ + parentUuid: parameterItem.parentUuid, containerUuid, columnName: columnName }) @@ -1315,6 +1354,7 @@ const panel = { // manage as Array = [value, valueTo] if (isRange && parameterItem.componentPath !== 'FieldNumber') { valueTo = rootGetters.getValueOfField({ + parentUuid: parameterItem.parentUuid, containerUuid, columnName: parameterItem.columnNameTo }) diff --git a/src/store/modules/ADempiere/preference.js b/src/store/modules/ADempiere/preference.js index e5203e62..356ff78b 100644 --- a/src/store/modules/ADempiere/preference.js +++ b/src/store/modules/ADempiere/preference.js @@ -1,6 +1,6 @@ import Vue from 'vue' // Delete when get global context and account context -import { isEmptyValue } from '@/utils/ADempiere/valueUtils.js' +import { isEmptyValue, typeValue } from '@/utils/ADempiere/valueUtils.js' const preference = { state: { @@ -14,7 +14,7 @@ const preference = { * @param {string} payload.columnName * @param {mixed} payload.value */ - setContext(state, payload) { + setPreferenceContext(state, payload) { let key = '' if (payload.parentUuid) { key += payload.parentUuid + '|' @@ -51,8 +51,8 @@ const preference = { } }, actions: { - setContext({ commit }, objectValue) { - commit('setContext', objectValue) + setPreferenceContext({ commit }, objectValue) { + commit('setPreferenceContext', objectValue) }, setMultiplePreference({ dispatch }, { parentUuid, @@ -60,7 +60,7 @@ const preference = { values }) { let actionToDispatch = 'setMultiplePreferenceObject' - if (Object.prototype.toString.call(values) === '[object Map]') { + if (typeValue(values) === 'MAP') { actionToDispatch = 'setMultiplePreferenceMap' } return dispatch(actionToDispatch, { @@ -77,7 +77,7 @@ const preference = { return new Promise(resolve => { if (!isEmptyValue(containerUuid) || !isEmptyValue(parentUuid)) { Object.keys(values).forEach(key => { - commit('setContext', { + commit('setPreferenceContext', { parentUuid, containerUuid, columnName: key, @@ -99,7 +99,7 @@ const preference = { return new Promise(resolve => { if (!isEmptyValue(containerUuid) || !isEmptyValue(parentUuid)) { values.forEach((value, key) => { - commit('setContext', { + commit('setPreferenceContext', { parentUuid, containerUuid, columnName: key, diff --git a/src/store/modules/ADempiere/utils.js b/src/store/modules/ADempiere/utils.js index 6ffd8a6a..ee9c16c7 100644 --- a/src/store/modules/ADempiere/utils.js +++ b/src/store/modules/ADempiere/utils.js @@ -29,7 +29,13 @@ const initStateUtils = { isLoaded: false }, panelRight: '', - currentRecord: {} + currentRecord: {}, + showOrder: false, + showTitleFrom: false, + showPanelRightPos: false, + showCollectionPos: false, + splitWidthRight: 3, + splitWidthLeft: 3 } const utils = { @@ -101,6 +107,24 @@ const utils = { }, setCurrentRecor(state, payload) { state.currentRecord = payload + }, + setShowOrder(state, showOrder) { + state.showOrder = showOrder + }, + setShowTitleFrom(state, showTitleFrom) { + state.showTitleFrom = showTitleFrom + }, + setShowPanelRightPos(state, showPanelRightPos) { + state.showPanelRightPos = showPanelRightPos + }, + setShowCollectionPos(state, showCollectionPos) { + state.showCollectionPos = showCollectionPos + }, + setSplitWidthRight(state, splitWidthRight) { + state.splitWidthRight = splitWidthRight + }, + setSplitWidthLeft(state, splitWidthLeft) { + state.splitWidthLeft = splitWidthLeft } }, actions: { @@ -167,6 +191,24 @@ const utils = { }, currentRecord({ commit }, record) { commit('setCurrentRecor', record) + }, + showOrder({ commit }, isOrder) { + commit('setShowOrder', isOrder) + }, + showTitleFrom({ commit }, isTitleFrom) { + commit('setShowTitleFrom', isTitleFrom) + }, + showPanelRightPos({ commit }, isPanelRight) { + commit('setShowPanelRightPos', isPanelRight) + }, + showCollectionPos({ commit }, isCollection) { + commit('setShowCollectionPos', isCollection) + }, + changeWidthRight({ commit }, newWidthRight) { + commit('setSplitWidthRight', newWidthRight) + }, + changeWidthLeft({ commit }, newWidthLeft) { + commit('setSplitWidthLeft', newWidthLeft) } }, getters: { @@ -242,6 +284,24 @@ const utils = { }, getCurrentRecord: (state) => { return state.currentRecord + }, + getShowPanelLeft: (state) => { + return state.showOrder + }, + getShowTitleFrom: (state) => { + return state.showTitleFrom + }, + getShowPanelRightPos: (state) => { + return state.showPanelRightPos + }, + getShowCollectionPos: (state) => { + return state.showCollectionPos + }, + getWidthRight: (state) => { + return state.splitWidthRight + }, + getWidthLeft: (state) => { + return state.splitWidthLeft } } } diff --git a/src/store/modules/ADempiere/window.js b/src/store/modules/ADempiere/window.js index b7a95061..72b36466 100644 --- a/src/store/modules/ADempiere/window.js +++ b/src/store/modules/ADempiere/window.js @@ -5,7 +5,7 @@ import { rollbackEntity } from '@/api/ADempiere/persistence' import { getReferencesList } from '@/api/ADempiere/values' -import { isEmptyValue } from '@/utils/ADempiere/valueUtils' +import { isEmptyValue, typeValue } from '@/utils/ADempiere/valueUtils.js' import { fieldIsDisplayed } from '@/utils/ADempiere/dictionaryUtils' import { parseContext } from '@/utils/ADempiere/contextUtils' import { showMessage } from '@/utils/ADempiere/notification' @@ -72,8 +72,9 @@ const windowControl = { value }) { // get value from store - if (!value) { + if (isEmptyValue(value)) { value = getters.getValueOfField({ + parentUuid: field.parentUuid, containerUuid: field.containerUuid, columnName: field.columnName }) @@ -157,6 +158,7 @@ const windowControl = { }) { // get value from store const value = getters.getValueOfField({ + parentUuid: field.parentUuid, containerUuid: field.containerUuid, columnName: field.columnName }) @@ -175,7 +177,7 @@ const windowControl = { value: sqlStatement, isSQL }).value - if (isSQL && String(sqlStatement) === '[object Object]') { + if (isSQL && typeValue(sqlStatement) === 'OBJECT') { sqlStatement = sqlStatement.query } } diff --git a/src/store/modules/ADempiere/windowDefinition.js b/src/store/modules/ADempiere/windowDefinition.js index 9e7349fd..2923dacd 100644 --- a/src/store/modules/ADempiere/windowDefinition.js +++ b/src/store/modules/ADempiere/windowDefinition.js @@ -66,6 +66,9 @@ const window = { const tabsListChildren = [] const tabsSequence = [] + // indexes related to visualization + let tabParentIndex = 0 + let tabChildrenIndex = 0 // TODO Add source tab on the server for tabs Translation and Sort const tabs = responseWindow.tabsList.filter(itemTab => { if (itemTab.isSortTab) { @@ -103,7 +106,7 @@ const window = { isAssociatedTabSequence: false, // show modal with order tab isShowedRecordNavigation: !(tabItem.isSingleRow), isLoadFieldsList: false, - index + index // this index is not related to the index in which the tabs are displayed } delete tab.processesList @@ -213,10 +216,14 @@ const window = { }) if (tab.isParentTab) { + tab.tabParentIndex = tabParentIndex + tabParentIndex++ tabsListParent.push(tab) return tab } if (!tab.isSortTab) { + tab.tabChildrenIndex = tabChildrenIndex + tabChildrenIndex++ tabsListChildren.push(tab) } return tab diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js index f6e105b0..e1d767cf 100644 --- a/src/store/modules/permission.js +++ b/src/store/modules/permission.js @@ -14,9 +14,9 @@ const mutations = { } const actions = { - generateRoutes({ commit }) { + generateRoutes({ commit }, organizationId) { return new Promise(resolve => { - loadMainMenu().then(menuResponse => { + loadMainMenu(organizationId).then(menuResponse => { commit('SET_ROUTES', menuResponse) resolve(menuResponse) }) diff --git a/src/store/modules/settings.js b/src/store/modules/settings.js index 33b5ee25..bb66f97a 100644 --- a/src/store/modules/settings.js +++ b/src/store/modules/settings.js @@ -1,7 +1,7 @@ import variables from '@/styles/element-variables.scss' import defaultSettings from '@/settings' -const { showSettings, tagsView, fixedHeader, sidebarLogo, supportPinyinSearch } = defaultSettings +const { showSettings, tagsView, fixedHeader, sidebarLogo, supportPinyinSearch, showContextMenu } = defaultSettings const state = { theme: variables.theme, @@ -9,7 +9,8 @@ const state = { tagsView, fixedHeader, sidebarLogo, - supportPinyinSearch + supportPinyinSearch, + showContextMenu } const mutations = { diff --git a/src/store/modules/user.js b/src/store/modules/user.js index c1d90fe7..e8537b94 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -23,7 +23,7 @@ import router, { resetRouter } from '@/router' import { showMessage } from '@/utils/ADempiere/notification' import { isEmptyValue } from '@/utils/ADempiere/valueUtils' import { convertDateFormat } from '@/utils/ADempiere/valueFormat' -import language, { getLanguage } from '@/lang' +import language from '@/lang' const state = { token: getToken(), @@ -116,19 +116,22 @@ const actions = { }) }, getCountryFormServer({ commit }, { - countryId, - countryUuid + id, + uuid }) { return new Promise(resolve => { getCountryDefinition({ - countryId, - countryUuid + id, + uuid }) .then(responseCountry => { commit('setCountry', responseCountry) resolve(responseCountry) }) + .catch(error => { + console.warn(`Error getting Country Definition: ${error.message}. Code: ${error.code}.`) + }) }) }, // user login @@ -162,9 +165,8 @@ const actions = { return new Promise((resolve, reject) => { getSessionInfo(sessionUuid) - .then(responseGetInfo => { + .then(async responseGetInfo => { const { role } = responseGetInfo - commit('setIsSession', true) commit('setSessionInfo', { id: responseGetInfo.id, @@ -196,10 +198,9 @@ const actions = { commit('SET_ROLE', role) setCurrentRole(role.uuid) + await dispatch('getOrganizationsList', role.uuid) resolve(sessionResponse) - dispatch('getOrganizationsList', role.uuid) - const countryId = parseInt( responseGetInfo.defaultContextMap.get('#C_Country_ID'), 10 @@ -209,7 +210,7 @@ const actions = { } else { // get country and currency dispatch('getCountryFormServer', { - countryId + id: countryId }) } @@ -278,30 +279,30 @@ const actions = { }, // user logout logout({ commit, state, dispatch }) { + const token = state.token return new Promise((resolve, reject) => { - logout(state.token).then(() => { - commit('SET_TOKEN', '') - commit('SET_ROLES', []) - commit('setIsSession', false) - dispatch('resetStateBusinessData', null, { - root: true - }) - dispatch('dictionaryResetCache', null, { - root: true - }) + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + removeToken() - dispatch('tagsView/delAllViews', null, { root: true }) - removeToken() - removeCurrentRole() - resetRouter() + commit('setIsSession', false) + dispatch('resetStateBusinessData', null, { + root: true + }) + dispatch('dictionaryResetCache', null, { + root: true + }) - // reset visited views and cached views - // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485 - dispatch('tagsView/delAllViews', null, { root: true }) + // reset visited views and cached views + // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2485 + dispatch('tagsView/delAllViews', null, { root: true }) + removeCurrentRole() + resetRouter() + logout(token).catch(error => { + console.warn(error) + }).finally(() => { resolve() - }).catch(error => { - reject(error) }) }) }, @@ -318,11 +319,14 @@ const actions = { if (isEmptyValue(roleUuid)) { roleUuid = getCurrentRole() } - return getOrganizationsList({ roleUuid }) .then(response => { commit('SET_ORGANIZATIONS_LIST', response.organizationsList) - let organization = response.organizationsList.find(item => item.uuid === getCurrentOrganization()) + let organization = response.organizationsList.find(item => { + if (item.uuid === getCurrentOrganization()) { + return item + } + }) if (isEmptyValue(organization)) { organization = response.organizationsList[0] } @@ -340,19 +344,57 @@ const actions = { console.warn(`Error ${error.code} getting Organizations list: ${error.message}.`) }) }, - changeOrganization({ dispatch }, { - organizationUuid + changeOrganization({ commit, dispatch, getters }, { + organizationUuid, + organizationId, + isCloseAllViews = true }) { setCurrentOrganization(organizationUuid) + const organization = getters.getOrganizations.find(org => org.uuid === organizationUuid) + commit('SET_ORGANIZATION', organization) + dispatch('getWarehousesList', organizationUuid) + + // TODO: Check if there are no tagViews in the new routes to close them, and + // if they exist, reload with the new route using name (uuid) + const route = router.app._route + const selectedTag = { + fullPath: route.fullPath, + hash: route.hash, + matched: route.matched, + meta: route.meta, + name: route.name, + params: route.params, + path: route.path, + query: route.query, + title: route.meta.title + } + + let actionToDispatch = 'tagsView/delOthersViews' + if (isCloseAllViews) { + actionToDispatch = 'tagsView/delAllViews' + } + dispatch(actionToDispatch, selectedTag, { root: true }) + + resetRouter() + dispatch('permission/generateRoutes', organizationId, { + root: true + }) + .then(response => { + router.addRoutes(response) + }) }, getWarehousesList({ commit }, organizationUuid) { if (isEmptyValue(organizationUuid)) { organizationUuid = getCurrentOrganization() } - return getWarehousesList({ organizationUuid }) + + return getWarehousesList({ + organizationUuid + }) .then(response => { commit('SET_WAREHOUSES_LIST', response.warehousesList) + let warehouse = response.warehousesList.find(item => item.uuid === getCurrentWarehouse()) if (isEmptyValue(warehouse)) { warehouse = response.warehousesList[0] @@ -409,6 +451,7 @@ const actions = { }) .then(changeRoleResponse => { const { role } = changeRoleResponse + commit('SET_ROLE', role) setCurrentRole(role.uuid) commit('SET_TOKEN', changeRoleResponse.uuid) @@ -466,14 +509,14 @@ const getters = { } return currency }, - getCountryLanguage(state) { + getCountryLanguage: (state) => { return state.country.language.replace('_', '-') }, getLanguagesList: (state) => { return state.languagesList }, getCurrentLanguageDefinition: (state) => { - return state.languagesList.find(definition => definition.languageISO === getLanguage()) + return state.languagesList.find(definition => definition.language === state.country.language) }, getRoles: (state) => { return state.rolesList diff --git a/src/styles/sidebar.scss b/src/styles/sidebar.scss index 5ad6c04e..450d8b97 100644 --- a/src/styles/sidebar.scss +++ b/src/styles/sidebar.scss @@ -57,6 +57,11 @@ margin-right: 16px; } + .sub-el-icon { + margin-right: 12px; + margin-left: -2px; + } + .el-menu { border: none; height: 100%; @@ -109,6 +114,10 @@ .svg-icon { margin-left: 20px; } + + .sub-el-icon { + margin-left: 19px; + } } } @@ -122,6 +131,10 @@ margin-left: 20px; } + .sub-el-icon { + margin-left: 19px; + } + .el-submenu__icon-arrow { display: none; } @@ -182,6 +195,10 @@ .svg-icon { margin-right: 16px; } + .sub-el-icon { + margin-right: 12px; + margin-left: -2px; + } } .nest-menu .el-submenu>.el-submenu__title, diff --git a/src/utils/ADempiere/contextUtils.js b/src/utils/ADempiere/contextUtils.js index ce016012..fc6841f9 100644 --- a/src/utils/ADempiere/contextUtils.js +++ b/src/utils/ADempiere/contextUtils.js @@ -12,8 +12,10 @@ export const getContext = ({ columnName }) => { let value - const isPreferenceValue = columnName.startsWith('$') || columnName.startsWith('#') - if (isPreferenceValue || columnName.startsWith(`P|${parentUuid}`) || columnName.startsWith(`P|${columnName}`)) { + const isPreferenceValue = columnName.startsWith('$') || + columnName.startsWith('#') || + columnName.startsWith(`P|`) + if (isPreferenceValue) { value = store.getters.getPreference({ parentUuid, containerUuid, @@ -21,7 +23,7 @@ export const getContext = ({ }) } if (!isPreferenceValue && isEmptyValue(value)) { - value = store.getters.getValueOfContainer({ + value = store.getters.getValueOfField({ parentUuid, containerUuid, columnName @@ -169,7 +171,7 @@ export function parseContext({ errorsList } } - value = String(value).replace('@SQL=') + value = String(value).replace('@SQL=', '') // const instances = value.length - value.replace('@', '').length // if ((instances > 0) && (instances % 2) !== 0) { // could be an email address // return value diff --git a/src/utils/ADempiere/criteria.js b/src/utils/ADempiere/criteria.js new file mode 100644 index 00000000..eabdf798 --- /dev/null +++ b/src/utils/ADempiere/criteria.js @@ -0,0 +1,121 @@ +import { isEmptyValue } from '@/utils/ADempiere/valueUtils.js' + +class Criteria { + constructor({ + tableName, + query, + whereClause, + referenceUuid, + conditionsList = [], + orderByClause, + valuesList = [], + orderByColumnsList = [], + limit + }) { + this.tableName = tableName + this.query = query + this.whereClause = whereClause + this.referenceUuid = referenceUuid + this.conditionsList = conditionsList + this.orderByClause = orderByClause + this.valuesList = valuesList + this.orderByColumnsList = orderByColumnsList + this.limit = limit + } + + setTableName(tableName) { + this.tableName = tableName + return this + } + + setQuery(query) { + this.query = query + return this + } + + setWhereClause(whereClause) { + this.whereClause = whereClause + return this + } + + setReferenceUuid(referenceUuid) { + this.referenceUuid = referenceUuid + return this + } + + addCondition({ columnName, condition, value }) { + if (isEmptyValue(value)) { + return this + } + + this.conditionsList.push({ + columnName, + condition, + value + }) + return this + } + + setConditionsList(conditionsList) { + this.conditionsList = conditionsList + return this + } + + setOrderByClause(orderByClause) { + this.orderByClause = orderByClause + return this + } + + addValue(value) { + if (isEmptyValue(value)) { + return this + } + + this.valuesList.push(value) + return this + } + + setValuesList(valuesList) { + this.valuesList = valuesList + return this + } + + addOrderByColumn(orderByColumn) { + if (isEmptyValue(orderByColumn)) { + return this + } + + this.orderByColumnsList.push(orderByColumn) + return this + } + + setOrderByColumnsList(orderByColumnsList) { + this.orderByColumnsList = orderByColumnsList + return this + } + + setLimit(limit) { + this.limit = limit + return this + } + + getCriteria() { + return { + tableName: this.tableName, + query: this.query, + whereClause: this.whereClause, + referenceUuid: this.referenceUuid, + conditionsList: this.conditionsList, + orderByClause: this.orderByClause, + valuesList: this.valuesList, + orderByColumnsList: this.orderByColumnsList, + limit: this.limit + } + } + + get criteria() { + return this.getCriteria() + } +} + +export default Criteria diff --git a/src/utils/ADempiere/dictionaryUtils.js b/src/utils/ADempiere/dictionaryUtils.js index 8c078dd3..45174433 100644 --- a/src/utils/ADempiere/dictionaryUtils.js +++ b/src/utils/ADempiere/dictionaryUtils.js @@ -25,9 +25,12 @@ export function generateField({ } const componentReference = evalutateTypeField(fieldToGenerate.displayType) - let isDisplayedFromLogic = fieldToGenerate.isDisplayed - let isMandatoryFromLogic = false - let isReadOnlyFromLogic = false + let evaluatedLogics = { + isDisplayedFromLogic: fieldToGenerate.isDisplayed, + isMandatoryFromLogic: false, + isReadOnlyFromLogic: false + } + let parentFieldsList = [] let parsedDefaultValue = fieldToGenerate.defaultValue let parsedDefaultValueTo = fieldToGenerate.defaultValueTo @@ -57,43 +60,12 @@ export function generateField({ operator = 'LIKE' } } else { - if (String(parsedDefaultValue).includes('@') && - String(parsedDefaultValue).trim() !== '-1') { - parsedDefaultValue = parseContext({ - ...moreAttributes, - columnName: fieldToGenerate.columnName, - value: parsedDefaultValue, - isSOTrxMenu - }).value - } - - if (isEmptyValue(parsedDefaultValue) && - !(fieldToGenerate.isKey || fieldToGenerate.isParent) && - String(parsedDefaultValue).trim() !== '-1') { - parsedDefaultValue = getPreference({ - parentUuid: fieldToGenerate.parentUuid, - containerUuid: fieldToGenerate.containerUuid, - columnName: fieldToGenerate.columnName - }) - - // search value preference with elementName - if (!isEmptyValue(fieldToGenerate.elementName) && - isEmptyValue(parsedDefaultValue)) { - parsedDefaultValue = getPreference({ - parentUuid: fieldToGenerate.parentUuid, - containerUuid: fieldToGenerate.containerUuid, - columnName: fieldToGenerate.elementName - }) - } - } - - parsedDefaultValue = parsedValueComponent({ + parsedDefaultValue = getDefaultValue({ + ...fieldToGenerate, + parentUuid: moreAttributes.parentUuid, + containerUuid: moreAttributes.containerUuid, componentPath: componentReference.componentPath, - columnName: fieldToGenerate.columnName, - value: parsedDefaultValue, - displayType: fieldToGenerate.displayType, - isMandatory: fieldToGenerate.isMandatory, - isIdentifier: fieldToGenerate.columnName.includes('_ID') + isSOTrxMenu }) if (String(fieldToGenerate.defaultValue).includes('@SQL=')) { @@ -103,70 +75,26 @@ export function generateField({ // VALUE TO if (fieldToGenerate.isRange) { - if (String(parsedDefaultValueTo).includes('@') && - String(parsedDefaultValueTo).trim() !== '-1') { - parsedDefaultValueTo = parseContext({ - ...moreAttributes, - columnName: `${fieldToGenerate.columnName}_To`, - value: parsedDefaultValueTo - }).value - } - - if (isEmptyValue(parsedDefaultValueTo) && - !(fieldToGenerate.isKey || fieldToGenerate.isParent) && - String(parsedDefaultValueTo).trim() !== '-1') { - parsedDefaultValueTo = getPreference({ - parentUuid: fieldToGenerate.parentUuid, - containerUuid: fieldToGenerate.containerUuid, - columnName: `${fieldToGenerate.columnName}_To` - }) - - // search value preference with elementName - if (!isEmptyValue(fieldToGenerate.elementName) && - isEmptyValue(parsedDefaultValueTo)) { - parsedDefaultValueTo = getPreference({ - parentUuid: fieldToGenerate.parentUuid, - containerUuid: fieldToGenerate.containerUuid, - columnName: `${fieldToGenerate.elementName}_To` - }) - } - } - - parsedDefaultValueTo = parsedValueComponent({ + parsedDefaultValueTo = getDefaultValue({ + ...fieldToGenerate, + parentUuid: moreAttributes.parentUuid, + containerUuid: moreAttributes.containerUuid, componentPath: componentReference.componentPath, - columnName: fieldToGenerate.columnName, - value: parsedDefaultValueTo, - displayType: fieldToGenerate.displayType, - isMandatory: fieldToGenerate.isMandatory, - isIdentifier: fieldToGenerate.columnName.includes('_ID') + defaultValue: fieldToGenerate.defaultValueTo, + columnName: `${fieldToGenerate.columnName}_To`, + elementName: `${fieldToGenerate.elementName}_To`, + isSOTrxMenu }) } + parentFieldsList = getParentFields(fieldToGenerate) - // evaluate logics - const setEvaluateLogics = { + // evaluate logics (diplayed, mandatory, readOnly) + evaluatedLogics = getEvaluatedLogics({ parentUuid: moreAttributes.parentUuid, containerUuid: moreAttributes.containerUuid, - context: getContext - } - if (!isEmptyValue(fieldToGenerate.displayLogic)) { - isDisplayedFromLogic = evaluator.evaluateLogic({ - ...setEvaluateLogics, - logic: fieldToGenerate.displayLogic - }) - } - if (!isEmptyValue(fieldToGenerate.mandatoryLogic)) { - isMandatoryFromLogic = evaluator.evaluateLogic({ - ...setEvaluateLogics, - logic: fieldToGenerate.mandatoryLogic - }) - } - if (!isEmptyValue(fieldToGenerate.readOnlyLogic)) { - isReadOnlyFromLogic = evaluator.evaluateLogic({ - ...setEvaluateLogics, - logic: fieldToGenerate.readOnlyLogic - }) - } + ...fieldToGenerate + }) } const field = { @@ -179,6 +107,7 @@ export function generateField({ componentPath: componentReference.componentPath, isSupported: componentReference.isSupported, size: componentReference.size || DEFAULT_SIZE, + // TODO: Property 'displayColumn' is @depecated displayColumn: undefined, // link to value from selects and table displayColumnName: `DisplayColumn_${fieldToGenerate.columnName}`, // key to display column // value attributes @@ -187,10 +116,8 @@ export function generateField({ valueTo: parsedDefaultValueTo, parsedDefaultValue, parsedDefaultValueTo, - // logics to app - isDisplayedFromLogic, - isReadOnlyFromLogic, - isMandatoryFromLogic, + // logics to app (isDisplayedFromLogic, isMandatoryFromLogic, isReadOnlyFromLogic) + ...evaluatedLogics, // parentFieldsList, dependentFieldsList: [], @@ -242,6 +169,118 @@ export function generateField({ return field } +/** + * Evaluate logics to definition field + * @param {object} + */ +export function getEvaluatedLogics({ + parentUuid, + containerUuid, + isDisplayed = true, + displayLogic, + mandatoryLogic, + readOnlyLogic +}) { + // evaluate logics + const commonParameters = { + parentUuid, + containerUuid, + context: getContext + } + + let isDisplayedFromLogic = isDisplayed + if (!isEmptyValue(displayLogic)) { + isDisplayedFromLogic = evaluator.evaluateLogic({ + ...commonParameters, + logic: displayLogic + }) + } + + let isMandatoryFromLogic = false + if (!isEmptyValue(mandatoryLogic)) { + isMandatoryFromLogic = evaluator.evaluateLogic({ + ...commonParameters, + logic: mandatoryLogic + }) + } + + let isReadOnlyFromLogic = false + if (!isEmptyValue(readOnlyLogic)) { + isReadOnlyFromLogic = evaluator.evaluateLogic({ + ...commonParameters, + logic: readOnlyLogic + }) + } + + return { + isDisplayedFromLogic, + isMandatoryFromLogic, + isReadOnlyFromLogic + } +} + +/** + * Get parsed default value to set into field + * @param {*} param0 + */ +export function getDefaultValue({ + parentUuid, + containerUuid, + isSOTrxMenu, + columnName, + elementName, + componentPath, + displayType, + defaultValue, + isMandatory, + isParent, + isKey +}) { + let parsedDefaultValue = defaultValue + + if (String(parsedDefaultValue).includes('@') && + String(parsedDefaultValue).trim() !== '-1') { + parsedDefaultValue = parseContext({ + parentUuid, + containerUuid, + columnName, + value: parsedDefaultValue, + isSOTrxMenu + }).value + } + + if (isEmptyValue(parsedDefaultValue) && + !(isKey || isParent) && + String(parsedDefaultValue).trim() !== '-1') { + parsedDefaultValue = getPreference({ + parentUuid, + containerUuid, + columnName + }) + + // search value preference with elementName + if (!isEmptyValue(elementName) && + isEmptyValue(parsedDefaultValue)) { + parsedDefaultValue = getPreference({ + parentUuid, + containerUuid, + columnName: elementName + }) + } + } + + parsedDefaultValue = parsedValueComponent({ + componentPath, + columnName, + value: parsedDefaultValue, + displayType, + isMandatory, + isIdentifier: columnName.includes('_ID') + }) + + return parsedDefaultValue +} + /** * Generate the actions and the associated process to store in the vuex store, * avoiding additional requests diff --git a/src/utils/ADempiere/location.js b/src/utils/ADempiere/location.js new file mode 100644 index 00000000..1bd93ed3 --- /dev/null +++ b/src/utils/ADempiere/location.js @@ -0,0 +1,16 @@ +// This file can be used for all related to location util like capture sequence and others values. +// Please add here all locations util methods + +// Use this function for get a list of sequence of capture for locations +export function getSequenceAsList(captureSequence) { + if (!captureSequence) { + return undefined + } + // Split it + return captureSequence + .replace('@@', '@') + .replace(',', '') + .trim() + .split('@') + .filter(value => value.trim()) +} diff --git a/src/utils/ADempiere/lookupFactory.js b/src/utils/ADempiere/lookupFactory.js index 591b0792..3227ce20 100644 --- a/src/utils/ADempiere/lookupFactory.js +++ b/src/utils/ADempiere/lookupFactory.js @@ -47,10 +47,10 @@ // - displayColumn // - defaultValue -import { CHAR, DEFAULT_SIZE, TEXT, TABLE_DIRECT } from '@/utils/ADempiere/references' -import { evalutateTypeField } from '@/utils/ADempiere/dictionaryUtils' -import { isEmptyValue } from '@/utils/ADempiere/valueUtils' -import evaluator, { getContext, getParentFields } from '@/utils/ADempiere/contextUtils' +import { CHAR, DEFAULT_SIZE, TABLE_DIRECT } from '@/utils/ADempiere/references.js' +import { evalutateTypeField, getDefaultValue, getEvaluatedLogics } from '@/utils/ADempiere/dictionaryUtils.js' +import { isEmptyValue } from '@/utils/ADempiere/valueUtils.js' +import { getParentFields } from '@/utils/ADempiere/contextUtils.js' import store from '@/store' // Create a Field from UUID based on server meta-data @@ -65,20 +65,27 @@ export function createFieldFromDictionary({ overwriteDefinition }) { let field + let valueToMatch if (fieldUuid) { field = store.getters.getFieldFromUuid(fieldUuid) + valueToMatch = fieldUuid } else if (columnUuid) { field = store.getters.getFieldFromColumnUuid(columnUuid) + valueToMatch = columnUuid } else if (elementUuid) { field = store.getters.getFieldFromElementUuid(elementUuid) + valueToMatch = elementUuid } if (elementColumnName) { field = store.getters.getFieldFromElementColumnName(elementColumnName) + valueToMatch = elementColumnName } else if (tableName && columnName) { field = store.getters.getFieldFromElementColumnName({ tableName, columnName }) + valueToMatch = columnName } + if (isEmptyValue(field)) { return new Promise(resolve => { store.dispatch('getFieldFromServer', { @@ -90,17 +97,43 @@ export function createFieldFromDictionary({ columnName }) .then(response => { - resolve(getFactoryFromField({ + const newField = getFactoryFromField({ containerUuid, field: response, overwriteDefinition - })) + }) + + resolve(newField) }).catch(error => { - console.warn(`LookupFactory: Get Field From Server (State) - Error ${error.code}: ${error.message}.`) + console.warn(`LookupFactory: Get Field (match: ${valueToMatch}) From Server (State) - Error ${error.code}: ${error.message}.`) + + const templateField = createFieldFromDefinition({ + containerUuid, + columnName, + definition: { + fieldUuid, + columnUuid, + elementUuid, + elementColumnName, + tableName, + columnName, + ...overwriteDefinition + } + }) + + resolve(templateField) }) }) } - return new Promise(resolve => { resolve(getFactoryFromField({ containerUuid, field })) }) + return new Promise(resolve => { + const fieldWithStore = getFactoryFromField({ + containerUuid, + field, + overwriteDefinition + }) + + resolve(fieldWithStore) + }) } // Convert field getted from server to factory @@ -109,50 +142,11 @@ function getFactoryFromField({ field, overwriteDefinition = {} }) { - const fieldAttributes = isEmptyValue(field.fieldResponse) ? field : field.fieldResponse - const fieldDefinition = { - displayType: fieldAttributes.displayType, - tableName: fieldAttributes.reference.tableName, - directQuery: fieldAttributes.directQuery, - query: fieldAttributes.reference.query, - keyColumnName: fieldAttributes.reference.keyColumnName, - validationCode: fieldAttributes.reference.validationCode, - windowsList: fieldAttributes.reference.windowsList, - id: fieldAttributes.id, - uuid: fieldAttributes.uuid, - name: fieldAttributes.name, - description: fieldAttributes.description, - help: fieldAttributes.help, - fieldGroup: fieldAttributes.fieldGroup, - isFieldOnly: fieldAttributes.isFieldOnly, - isRange: fieldAttributes.isRange, - isSameLine: fieldAttributes.isSameLine, - sequence: fieldAttributes.sequence, - seqNoGrid: fieldAttributes.seqNoGrid, - isIdentifier: fieldAttributes.isIdentifier, - isKey: fieldAttributes.isKey, - isSelectionColumn: fieldAttributes.isSelectionColumn, - isUpdateable: fieldAttributes.isUpdateable, - formatPattern: fieldAttributes.formatPattern, - vFormat: fieldAttributes.vFormat, - defaultValue: fieldAttributes.defaultValue, - defaultValueTo: fieldAttributes.defaultValueTo, - valueMin: fieldAttributes.valueMin, - valueMax: fieldAttributes.valueMax, - isActive: fieldAttributes.isActive, - isMandatory: fieldAttributes.isMandatory, - isReadOnly: fieldAttributes.isReadOnly, - isDisplayedFromLogic: fieldAttributes.isDisplayedFromLogic, - isReadOnlyFromLogic: fieldAttributes.isReadOnlyFromLogic, - isMandatoryFromLogic: fieldAttributes.isMandatoryFromLogic, - callout: fieldAttributes.callout, - isQueryCriteria: fieldAttributes.isQueryCriteria, - displayLogic: fieldAttributes.displayLogic, - mandatoryLogic: fieldAttributes.mandatoryLogic, - readOnlyLogic: fieldAttributes.readOnlyLogic, - parentFieldsList: fieldAttributes.parentFieldsList, - dependentFieldsList: fieldAttributes.dependentFieldsList, - contextInfo: fieldAttributes.contextInfo, + const definition = { + parentFieldsList: field.parentFieldsList || [], + dependentFieldsList: field.dependentFieldsList || [], + ...field, + isDisplayed: true, // Overwrite definition ...overwriteDefinition } @@ -160,12 +154,15 @@ function getFactoryFromField({ // Convert it return createFieldFromDefinition({ containerUuid, - columnName: field.columnName, - definition: fieldDefinition + columnName: definition.columnName, + definition }) } -// Create a field, it assumed that you define all behavior from source code +/** + * Create a field, it assumed that you define all behavior from source code + * TODO: Join with generateField function + */ export function createFieldFromDefinition({ parentUuid, containerUuid, @@ -175,7 +172,7 @@ export function createFieldFromDefinition({ }) { if (!isEmptyValue(definition)) { if (isEmptyValue(definition.displayType)) { - definition.displayType = TEXT.id + definition.displayType = CHAR.id } else if (definition.displayType === TABLE_DIRECT.id && isEmptyValue(definition.tableName) && columnName.indexOf('_ID') > 0) { @@ -187,9 +184,6 @@ export function createFieldFromDefinition({ if (isEmptyValue(definition.isDisplayed)) { definition.isDisplayed = true } - if (isEmptyValue(definition.isDisplayedFromLogic)) { - definition.isDisplayedFromLogic = true - } if (isEmptyValue(definition.isReadOnly)) { definition.isReadOnly = false } @@ -203,25 +197,8 @@ export function createFieldFromDefinition({ definition.sequence = 10 } } - - let reference = {} - if (!isEmptyValue(definition.directQuery) || !isEmptyValue(definition.query)) { - reference = { - directQuery: definition.directQuery, - query: definition.query, - tableName: definition.tableName || undefined, - keyColumnName: definition.keyColumnName || undefined, - validationCode: definition.validationCode || undefined, - windowsList: definition.windowsList || [] - } - delete definition.directQuery - delete definition.query - delete definition.tableName - delete definition.validationCode - delete definition.windowsList - } - definition.reference = reference } + return getFieldTemplate({ panelType, ...definition, @@ -269,6 +246,7 @@ export function getFieldTemplate(overwriteDefinition) { description: '', help: '', columnName: '', + displayColumnName: `DisplayColumn_${overwriteDefinition.columnName}`, // key to display column fieldGroup: { name: '', fieldGroupType: '' @@ -311,7 +289,6 @@ export function getFieldTemplate(overwriteDefinition) { displayLogic: undefined, mandatoryLogic: undefined, readOnlyLogic: undefined, - parentFieldsList: undefined, handleFocusGained: false, handleFocusLost: false, handleKeyPressed: false, @@ -332,33 +309,35 @@ export function getFieldTemplate(overwriteDefinition) { isFixedTableColumn: false, ...overwriteDefinition } + // get parsed parent fields list - fieldTemplateMetadata.parentFieldsList = getParentFields(fieldTemplateMetadata) + const parentFieldsList = getParentFields(fieldTemplateMetadata) - // evaluate logics - const setEvaluateLogics = { - parentUuid: fieldTemplateMetadata.parentUuid, - containerUuid: fieldTemplateMetadata.containerUuid, - context: getContext - } - if (!isEmptyValue(fieldTemplateMetadata.displayLogic)) { - fieldTemplateMetadata.isDisplayedFromLogic = evaluator.evaluateLogic({ - ...setEvaluateLogics, - logic: fieldTemplateMetadata.displayLogic - }) - } - if (!isEmptyValue(fieldTemplateMetadata.mandatoryLogic)) { - fieldTemplateMetadata.isMandatoryFromLogic = evaluator.evaluateLogic({ - ...setEvaluateLogics, - logic: fieldTemplateMetadata.mandatoryLogic - }) - } - if (!isEmptyValue(fieldTemplateMetadata.readOnlyLogic)) { - fieldTemplateMetadata.isReadOnlyFromLogic = evaluator.evaluateLogic({ - ...setEvaluateLogics, - logic: fieldTemplateMetadata.readOnlyLogic + // TODO: Add support to isSOTrxMenu + const parsedDefaultValue = getDefaultValue({ + ...fieldTemplateMetadata + }) + + let parsedDefaultValueTo + if (fieldTemplateMetadata.isRange) { + parsedDefaultValueTo = getDefaultValue({ + ...fieldTemplateMetadata, + defaultValue: fieldTemplateMetadata.defaultValueTo, + columnName: `${fieldTemplateMetadata.columnName}_To`, + elementName: `${fieldTemplateMetadata.elementName}_To` }) } - return fieldTemplateMetadata + // evaluate logics (diplayed, mandatory, readOnly) + const evaluatedLogics = getEvaluatedLogics({ + ...fieldTemplateMetadata + }) + + return { + ...fieldTemplateMetadata, + ...evaluatedLogics, + parsedDefaultValue, + parsedDefaultValueTo, + parentFieldsList + } } diff --git a/src/utils/ADempiere/references.js b/src/utils/ADempiere/references.js index a0824466..3b03ff7c 100644 --- a/src/utils/ADempiere/references.js +++ b/src/utils/ADempiere/references.js @@ -267,9 +267,9 @@ export const LIST = { // Location Address export const LOCATION_ADDRESS = { id: 21, - isSupported: false, + isSupported: true, valueType: 'INTEGER', - componentPath: 'FieldText', + componentPath: 'FieldLocation', size: { xs: 24, sm: 12, diff --git a/src/utils/ADempiere/resource.js b/src/utils/ADempiere/resource.js new file mode 100644 index 00000000..2ceec923 --- /dev/null +++ b/src/utils/ADempiere/resource.js @@ -0,0 +1,19 @@ +// This file allows generate util functions for handle arrays, resources and all related to upload to server side +// and downdload from server side to client side. Please add the necessary functions here: + +// Merge two arrays and return merged array +export function mergeByteArray(currentArray, arrayToMerge) { + const mergedArray = new currentArray.constructor(currentArray.length + arrayToMerge.length) + mergedArray.set(currentArray) + mergedArray.set(arrayToMerge, currentArray.length) + return mergedArray +} + +// Build a base 64 image from array +export function buildImageFromArray(resource, byteArray) { + return 'data:' + resource.contentType + ';base64,' + btoa( + byteArray.reduce( + (data, byte) => data + String.fromCharCode(byte), '' + ) + ) +} diff --git a/src/utils/ADempiere/valueFormat.js b/src/utils/ADempiere/valueFormat.js index 4688a32b..ca2aeeb3 100644 --- a/src/utils/ADempiere/valueFormat.js +++ b/src/utils/ADempiere/valueFormat.js @@ -3,14 +3,21 @@ import moment from 'moment' import { isEmptyValue } from '@/utils/ADempiere/valueUtils.js' import store from '@/store' -import { DATE, DATE_PLUS_TIME, TIME } from '@/utils/ADempiere/references' +import { DATE, DATE_PLUS_TIME, TIME, AMOUNT, COSTS_PLUS_PRICES, NUMBER, QUANTITY } from '@/utils/ADempiere/references.js' +/** + * Convert string values ('Y' or 'N') to component compatible Boolean values + * @param {mixed} valueToParsed + */ export const convertStringToBoolean = (valueToParsed) => { const valueString = String(valueToParsed).trim() if (valueString === 'N' || valueString === 'false') { return false + } else if (valueString === 'Y' || valueString === 'true') { + return true } - return Boolean(valueToParsed) + + return valueToParsed } export const convertBooleanToString = (booleanValue) => { @@ -43,8 +50,8 @@ export function convertObjectToKeyValue({ /** * Convert array pairs of object to literal object { key: value } * @param {array} array, Array to convert - * @param {string} nameKey, name from key in pairs - * @param {string} nameValue, name from value in pairs + * @param {string} keyName, name from key in pairs + * @param {string} valueName, name from value in pairs * @returns {object} { key: value, key2: value2 } */ export function convertArrayKeyValueObject({ @@ -125,6 +132,56 @@ export function formatDate(date, isTime = false) { })) } +// Get Formatted Price +export function formatPrice(number, currency) { + if (this.isEmptyValue(number)) { + return undefined + } + if (this.isEmptyValue(currency)) { + currency = getCurrency() + } + // Get formatted number + return new Intl.NumberFormat(getCountryCode(), { + style: 'currency', + currency + }).format(number) +} + +// Format Quantity +export function formatQuantity(number) { + if (this.isEmptyValue(number)) { + return undefined + } + if (!Number.isInteger(number)) { + return number + } + return Number.parseFloat(number).toFixed(2) + // Get formatted number +} + +// Format percentage based on Intl library +export function formatPercent(number) { + if (this.isEmptyValue(number)) { + return undefined + } + // Get formatted number + return new Intl.NumberFormat(getCountryCode(), { + style: 'percent' + }).format(number) +} + +// Get country code from store +function getCountryCode() { + const languageDefinition = store.getters['user/getCurrentLanguageDefinition'] + return languageDefinition.languageISO + '-' + languageDefinition.countryCode +} + +// Get Default country +function getCurrency() { + const currencyDefinition = store.getters['user/getCurrency'] + return currencyDefinition.iSOCode +} + // Return a format for field depending of reference for him export function formatField(value, reference, optionalFormat) { if (isEmptyValue(value)) { @@ -151,6 +208,18 @@ export function formatField(value, reference, optionalFormat) { isTime: true })) break + case AMOUNT.id: + formattedValue = formatPrice(value) + break + case COSTS_PLUS_PRICES.id: + formattedValue = formatPrice(value) + break + case NUMBER.id: + formattedValue = formatQuantity(value) + break + case QUANTITY.id: + formattedValue = formatQuantity(value) + break default: formattedValue = value } @@ -163,6 +232,7 @@ export function getDefaultFormat(isTime) { isTime }) } + // Get default format or optional function getDateFormat({ format, @@ -177,3 +247,26 @@ function getDateFormat({ return isTime ? languageDefinition.timePattern : languageDefinition.datePattern } } + +/** + * Removes the % of a text string, only from the beginning and end if they exist, + * this in case you need to use a match or local search to find matches between + * text strings. + * @param {string} stringToParsed ej: '%qwerty asd%' | '%zxc 123' + * @returns {string} ej: 'qwerty asd' | 'zxc 123' + */ +export function trimPercentage(stringToParsed) { + if (!isEmptyValue(stringToParsed) && String(stringToParsed).includes('%')) { + let parsedValue = stringToParsed + if (parsedValue[0] === '%') { + parsedValue = parsedValue.slice(1) + } + + const wordSize = parsedValue.length - 1 + if (parsedValue[wordSize] === '%') { + parsedValue = parsedValue.slice(0, wordSize) + } + return parsedValue + } + return stringToParsed +} diff --git a/src/utils/ADempiere/valueUtils.js b/src/utils/ADempiere/valueUtils.js index d92e8c80..19fab6fb 100644 --- a/src/utils/ADempiere/valueUtils.js +++ b/src/utils/ADempiere/valueUtils.js @@ -22,27 +22,13 @@ export function extractPagingToken(token) { } export function typeValue(value) { - if (typeof value === 'undefined' || value == null) { - return value - } else if (typeof value === 'string') { - return 'STRING' - } else if (typeof value === 'function') { - return 'FUNCTION' - } else if (typeof value === 'number') { - if (value.isInteger()) { - return 'INTEGER' - } - return 'DOUBLE' - } else if (typeof value === 'boolean') { - return 'BOOLEAN' - } else if (Object.prototype.toString.call(value) === '[object Date]') { - return 'DATE' - } else if (Array.isArray(value)) { - return 'ARRAY' - } else if (typeof value === 'object') { - return 'OBJECT' - } - return value + const typeOfValue = Object.prototype + .toString + .call(value) + .match(/^\[object\s(.*)\]$/)[1] + .toUpperCase() + + return typeOfValue } /** diff --git a/src/utils/index.js b/src/utils/index.js index 96ee6e7f..3225d3c4 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -162,19 +162,21 @@ export function param(json) { * @returns {Object} */ export function param2Obj(url) { - const search = url.split('?')[1] + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') if (!search) { return {} } - return JSON.parse( - '{"' + - decodeURIComponent(search) - .replace(/"/g, '\\"') - .replace(/&/g, '","') - .replace(/=/g, '":"') - .replace(/\+/g, ' ') + - '"}' - ) + const obj = {} + const searchArr = search.split('&') + searchArr.forEach(v => { + const index = v.indexOf('=') + if (index !== -1) { + const name = v.substring(0, index) + const val = v.substring(index + 1, v.length) + obj[name] = val + } + }) + return obj } /** diff --git a/src/views/ADempiere/Browser/index.vue b/src/views/ADempiere/Browser/index.vue index 27280eee..93a723a8 100644 --- a/src/views/ADempiere/Browser/index.vue +++ b/src/views/ADempiere/Browser/index.vue @@ -152,6 +152,10 @@ export default { }, created() { this.getBrowser() + this.$store.dispatch('settings/changeSetting', { + key: 'showContextMenu', + value: true + }) }, methods: { handleChange(value) { @@ -203,15 +207,16 @@ export default { if (this.isLoadedRecords) { // not research return - } else { - if (this.isReadyToSearch) { - // first search by default - this.$store.dispatch('getBrowserSearch', { - containerUuid: this.browserUuid - }) - return - } } + + if (this.isReadyToSearch) { + // first search by default + this.$store.dispatch('getBrowserSearch', { + containerUuid: this.browserUuid + }) + return + } + // set default values into data this.$store.dispatch('setRecordSelection', { containerUuid: this.browserUuid, @@ -281,7 +286,7 @@ export default { } .content-help { width: 100%; - height: 200%; + height: 100%; padding-left: 15px !important; } .content-help-mobile { diff --git a/src/views/ADempiere/Form/index.vue b/src/views/ADempiere/Form/index.vue index c6ad467e..729a3d7a 100644 --- a/src/views/ADempiere/Form/index.vue +++ b/src/views/ADempiere/Form/index.vue @@ -15,14 +15,14 @@ :panel-type="panelType" /> - + -

+

{{ formTitle }} - +

- +
diff --git a/src/views/dashboard/admin/components/BoxCard.vue b/src/views/dashboard/admin/components/BoxCard.vue index dff8462a..4bd0532d 100644 --- a/src/views/dashboard/admin/components/BoxCard.vue +++ b/src/views/dashboard/admin/components/BoxCard.vue @@ -101,7 +101,7 @@ export default { background-color: #fff; margin: auto; box-shadow: none!important; - /deep/ .pan-info { + ::v-deep .pan-info { box-shadow: none!important; } } diff --git a/src/views/example/components/ArticleDetail.vue b/src/views/example/components/ArticleDetail.vue index 616fd42b..5d74d1ce 100644 --- a/src/views/example/components/ArticleDetail.vue +++ b/src/views/example/components/ArticleDetail.vue @@ -279,7 +279,7 @@ export default { } } -.article-textarea /deep/ { +.article-textarea ::v-deep { textarea { padding-right: 40px; resize: none; diff --git a/src/views/login/forgotPassword.vue b/src/views/login/forgotPassword.vue index 854fe03c..e4485bb0 100644 --- a/src/views/login/forgotPassword.vue +++ b/src/views/login/forgotPassword.vue @@ -48,11 +48,8 @@ diff --git a/src/views/profile/components/RolesNavbar.vue b/src/views/profile/components/RolesNavbar.vue index 99654be6..88bb07db 100644 --- a/src/views/profile/components/RolesNavbar.vue +++ b/src/views/profile/components/RolesNavbar.vue @@ -8,7 +8,7 @@ @change="changeRole" >