1
0
mirror of https://github.com/PanJiaChen/vue-element-admin.git synced 2025-08-10 12:01:57 +08:00

Add API REST support based on proxy-adempiere-api and backend implementation (#529)

This commit is contained in:
Yamel Senih 2020-10-28 17:19:53 -04:00 committed by GitHub
parent 62742995a2
commit 6e54d93b9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
202 changed files with 16030 additions and 6156 deletions

View File

@ -39,7 +39,7 @@ Este es una gran UI para [ADempiere ERP, CRM & SCM](https://github.com/adempiere
- [Canal de Gitter](https://gitter.im/adempiere/adempiere-vue)
- [Para Donaciones](https://www.paypal.me/?)
- [Para Donaciones](https://www.paypal.me/YamelSenih)
- [Enlace de Wiki](http://wiki.adempiere.net/ADempiere_ERP)
@ -129,6 +129,7 @@ Sea un patrocinante y coloque su logo en nuestro LEEME en GitHub con un enlace d
- Componente para soltar archivos
- Adhesión de objetos
- Contador hasta
- Soporte a ADempiere
- Ventana
- Proceso
@ -205,7 +206,7 @@ Los cambios detallados por cada liberación se encuentran en [notas de liberaci
Si este proyecto es de mucha ayuda para ti, puedes ayudar a hacer una mejor UI
[dona por Paypal](https://www.paypal.me/?)
[dona por Paypal](https://www.paypal.me/YamelSenih)
## Navegadores Soportados

View File

@ -62,12 +62,12 @@ Understanding and learning this knowledge in advance will greatly help the use o
## Sponsors
<a href="https://erpya.com/"><img width="250px" src="https://i0.wp.com/spin-suite.com/erpya/wp-content/uploads/sites/28/2017/11/ERP-logotipo-H-color.png" /></a>
<a href="http://erpya.com/"><img width="250px" src="https://i0.wp.com/spin-suite.com/erpya/wp-content/uploads/sites/28/2017/11/ERP-logotipo-H-color.png" /></a>
<a href="http://www.e-evolution.com/"><img width="250px" src="https://i0.wp.com/mysmart.business/evolution/wp-content/uploads/sites/29/2017/10/e-evolution-logo-v2.png?fit=150%2C92" /></a>
<a href="http://westfalia-it.com/"><img width="150px" src="https://i1.wp.com/spin-suite.com/westfalia/wp-content/uploads/sites/30/2017/12/logo_copy.jpg?fit=265%2C357" /></a>
<a href="http://openupsolutions.com/"><img width="250px" src="https://i0.wp.com/spin-suite.com/openupsolutions/wp-content/uploads/sites/32/2017/05/Openup-Solutions-Logo-2017-80x200px.png" /></a>
Become a sponsor and get your logo on our README on GitHub with a link to your site. [Become a sponsor](https://www.paypal.me/?)
Become a sponsor and get your logo on our README on GitHub with a link to your site. [Become a sponsor](https://www.paypal.me/YamelSenih)
## Features
@ -129,6 +129,7 @@ Become a sponsor and get your logo on our README on GitHub with a link to your s
- Dropzone
- Sticky
- CountTo
- ADempiere supported
- Window
- Process
@ -204,7 +205,7 @@ Detailed changes for each release are documented in the [release notes](https://
If you find this project useful, you can help this make a better UI
[Paypal Me](https://www.paypal.me/?)
[Paypal Me](https://www.paypal.me/YamelSenih)
## Browsers support

View File

@ -22,12 +22,7 @@
"test:ci": "npm run lint && npm run test:unit"
},
"dependencies": {
"@adempiere/grpc-access-client": "^1.2.3",
"@adempiere/grpc-core-client": "^1.2.4",
"@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",
"@toast-ui/vue-editor": "^2.4.0",
"axios": "0.19.2",
"clipboard": "2.0.6",
"codemirror": "5.56.0",
@ -57,7 +52,7 @@
"vue-i18n": "8.19.0",
"vue-multipane": "^0.9.5",
"vue-resize": "^0.5.0",
"vue-router": "3.3.4",
"vue-router": "3.4.3",
"vue-shortkey": "^3.1.7",
"vue-split-panel": "^1.0.4",
"vue-splitpane": "1.0.6",
@ -68,6 +63,7 @@
"devDependencies": {
"@vue/cli-plugin-babel": "4.4.6",
"@vue/cli-plugin-eslint": "4.4.6",
"@vue/cli-plugin-pwa": "4.5.6",
"@vue/cli-plugin-unit-jest": "4.4.6",
"@vue/cli-service": "4.4.6",
"@vue/test-utils": "1.0.3",
@ -80,6 +76,7 @@
"connect": "3.7.0",
"eslint": "7.5.0",
"eslint-plugin-vue": "6.2.2",
"fs": "0.0.1-security",
"html-webpack-plugin": "4.3.0",
"husky": "4.2.5",
"lint-staged": "10.2.11",

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

View File

@ -3,6 +3,7 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="manifest" href="<%= BASE_URL %>manifest.json">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
@ -11,5 +12,16 @@
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('<%= htmlWebpackPlugin.options.pwaPath %>sw.js').then(function(registration) {
// Registration Success
console.log('[serviceWorker]: registration successful with scope: ', registration.scope)
}).catch(function(err) {
// Registration failed :(
console.log('[serviceWorker] registration failed', err)
})
}
</script>
</body>
</html>

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

15
public/manifest.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "ADempiere-Vue",
"short_name": "AD-Vue",
"icons": [
{
"src": "./logo.png",
"sizes": "200x200",
"type": "image/png"
}
],
"start_url": "/adempiere-vue/",
"display": "standalone",
"background_color": "#2d3a4b",
"theme_color": "#3d5165"
}

View File

@ -1,28 +1,69 @@
// Get Instance for connection
import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
/**
* Request a browser search
* @param {string} uuid
* @param {string} tableName
* @param {array} parametersList, This allows follow structure:
* [{
* columnName,
* key
* }]
* @param {array} filters
* @param {array} columns
* @param {string} query
* @param {string} whereClause
* @param {number} limit
* @param {string} orderByClause
* @param {string} nextPageToken
* @param {array} parametersList, This allows follow structure:
* [{
* columnName,
* value
* }]
*/
export function getBrowserSearch({ uuid, parametersList = [], query, whereClause, orderByClause, nextPageToken: pageToken, pageSize }) {
// Run browser
return Instance.call(this).requestListBrowserSearch({
uuid,
parametersList,
query,
whereClause,
orderByClause,
pageToken,
pageSize
export function requestBrowserSearch({
uuid,
parametersList = [],
tableName,
query,
whereClause,
orderByClause,
limit,
nextPageToken: pageToken,
pageSize
}) {
const filters = parametersList.map(parameter => {
return {
key: parameter.columnName,
value: parameter.value,
values: parameter.values
}
})
return requestRest({
url: '/ui/list-browser-items',
data: {
// Running Parameters
uuid,
table_name: tableName,
// DSL Query
filters,
// Custom Query
query,
where_clause: whereClause,
order_by_clause: orderByClause,
limit
},
params: {
// Page Data
page_token: pageToken,
page_size: pageSize
}
})
.then(evaluateResponse)
.then(responseBrowserSearch => {
const { convertEntityList } = require('@/utils/ADempiere/apiConverts/persistence.js')
return convertEntityList(responseBrowserSearch)
})
}

View File

@ -1,13 +1,5 @@
const proxyAddress = process.env.VUE_APP_PROXY_ADDRESS || 'localhost'
const proxyPort = process.env.VUE_APP_PROXY_PORT || '8989'
const apiRestAddress = process.env.VUE_APP_API_REST_ADDRESS || '0.0.0.0'
const apiRestPort = process.env.VUE_APP_API_REST_PORT || '8085'
export const API_ADDRESS = `http://${proxyAddress}:${proxyPort}`
export const ACCESS_ADDRESS = `${API_ADDRESS}/access`
export const DICTIONARY_ADDRESS = `${API_ADDRESS}/dictionary`
export const BUSINESS_DATA_ADDRESS = `${API_ADDRESS}/businessdata`
export const ENROLLMENT_ADDRESS = `${API_ADDRESS}/enrollment`
export const API_REST_ADDRESS = `http://${apiRestAddress}:${apiRestPort}/adempiere-api`

View File

@ -2,36 +2,141 @@
// please if you want to implement a custom dashboard create a new fielwith api definition
// Get Instance for connection
import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
// Get Recent Items based on selection option
export function getRecentItems({ pageToken, pageSize }) {
return Instance.call(this).requestListRecentItems({ pageToken, pageSize })
export function requestListRecentItems({
userUuid,
roleUuid,
pageToken,
pageSize
}) {
return requestRest({
url: '/logs/list-recent-items',
data: {
user_uuid: userUuid,
role_uuid: roleUuid,
current_session: true
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(recentItmesReponse => {
const { convertRecentItemsList } = require('@/utils/ADempiere/apiConverts/dashboard.js')
return convertRecentItemsList(recentItmesReponse)
})
}
/**
* Request Favorites List
* @param {string} userUuid
*/
export function getFavoritesFromServer({ userUuid, pageToken, pageSize }) {
return Instance.call(this).requestListFavorites({ userUuid, pageToken, pageSize })
export function getFavoritesFromServer({
userId,
userUuid,
pageToken,
pageSize
}) {
return requestRest({
url: '/dashboard/list-favorites',
data: {
user_id: userId,
user_uuid: userUuid
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(favoritesListReponse => {
const { convertFavorite } = require('@/utils/ADempiere/apiConverts/dashboard.js')
return {
recordCount: favoritesListReponse.record_count,
favoritesList: favoritesListReponse.records.map(favorite => {
return convertFavorite(favorite)
}),
nextPageToken: favoritesListReponse.next_page_token
}
})
}
// Get pending documents
export function getPendingDocumentsFromServer({ userUuid, roleUuid, pageToken, pageSize }) {
return Instance.call(this).requestListPendingDocuments({
userUuid,
roleUuid,
pageToken,
pageSize
export function getPendingDocumentsFromServer({
userId,
userUuid,
roleId,
roleUuid,
pageToken,
pageSize
}) {
return requestRest({
url: '/dashboard/list-pending-documents',
data: {
user_id: userId,
user_uuid: userUuid,
role_id: roleId,
role_uuid: roleUuid
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(pendingDocumentsListResponse => {
const { convertPendingDocument } = require('@/utils/ADempiere/apiConverts/dashboard.js')
return {
recordCount: pendingDocumentsListResponse.record_count,
pendingDocumentsList: pendingDocumentsListResponse.records.map(pendingDocument => {
return convertPendingDocument(pendingDocument)
}),
nextPageToken: pendingDocumentsListResponse.next_page_token
}
})
}
// List all dashboard for role
export function requestLisDashboards({ roleUuid, pageToken, pageSize }) {
return Instance.call(this).requestListDashboards({
roleUuid,
pageToken,
pageSize
export function requestLisDashboards({
roleId,
roleUuid,
pageToken,
pageSize
}) {
return requestRest({
url: '/dashboard/list-dashboards',
data: {
role_id: roleId,
role_uuid: roleUuid
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(dashboardsListResponse => {
const { convertDashboard } = require('@/utils/ADempiere/apiConverts/dashboard.js')
return {
recordCount: dashboardsListResponse.record_count,
dashboardsList: dashboardsListResponse.records.map(favorite => {
return convertDashboard(favorite)
}),
nextPageToken: dashboardsListResponse.next_page_token
}
})
}

View File

@ -1,78 +1,177 @@
// Get Instance for connection
import { DictionaryInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
export function getWindow({ uuid, id, isWithTabs = true }) {
return Instance.call(this).requestWindow({
uuid,
id,
isWithTabs
/**
* Request dictionary Window metadata
* @param {string} uuid universally unique identifier
* @param {number} id, identifier
*/
export function requestWindowMetadata({
uuid,
id
}) {
return requestRest({
url: '/dictionary/window',
method: 'get',
params: {
uuid,
id
}
})
.then(evaluateResponse)
.then(windowResponse => {
const { convertWindow } = require('@/utils/ADempiere/apiConverts/dictionary.js')
return convertWindow(windowResponse)
})
}
export function getProcess({ uuid, id }) {
return Instance.call(this).requestProcess({
uuid,
id,
isWithFields: true
/**
* Request dictionary Process/Report metadata
* @param {string} uuid universally unique identifier
* @param {number} id, identifier
*/
export function requestProcessMetadata({
uuid,
id
}) {
return requestRest({
url: '/dictionary/process',
method: 'get',
params: {
uuid,
id
}
})
.then(evaluateResponse)
.then(processResponse => {
const { convertProcess } = require('@/utils/ADempiere/apiConverts/dictionary.js')
return convertProcess(processResponse)
})
}
export function getBrowser({ uuid, id }) {
return Instance.call(this).requestBrowser({
uuid,
id
/**
* Request dictionary Smart Browser metadata
* @param {string} uuid universally unique identifier
* @param {number} id, identifier
*/
export function requestBrowserMetadata({
uuid,
id
}) {
return requestRest({
url: '/dictionary/browser',
method: 'get',
params: {
uuid,
id
}
})
.then(evaluateResponse)
.then(browserResponse => {
const { convertBrowser } = require('@/utils/ADempiere/apiConverts/dictionary.js')
return convertBrowser(browserResponse)
})
}
export function getTab({ uuid, id, isWithFields = true }) {
return Instance.call(this).requestTab({
uuid,
id,
isWithFields
/**
* Request dictionary Form metadata
* @param {string} uuid universally unique identifier
* @param {number} id, integer identifier
*/
export function requestForm({
uuid,
id
}) {
return requestRest({
url: '/dictionary/form',
method: 'get',
params: {
uuid,
id
}
})
.then(evaluateResponse)
.then(formResponse => {
const { convertForm } = require('@/utils/ADempiere/apiConverts/dictionary.js')
return convertForm(formResponse)
})
}
export function getField({
fieldUuid,
export function requestFieldMetadata({
uuid,
columnUuid,
elementUuid,
fieldUuid,
// TableName + ColumnName
tableName,
columnName,
elementColumnName
}) {
return Instance.call(this).requestField({
fieldUuid,
columnUuid,
elementUuid,
// TableName + ColumnName
tableName,
columnName,
elementColumnName
return requestRest({
url: '/dictionary/field',
method: 'get',
params: {
uuid,
column_uuid: columnUuid,
element_uuid: elementUuid,
field_uuid: fieldUuid,
// TableName + ColumnName
table_name: tableName,
column_name: columnName,
element_column_name: elementColumnName
}
})
.then(evaluateResponse)
.then(fieldResponse => {
const { convertField } = require('@/utils/ADempiere/apiConverts/field.js')
return convertField(fieldResponse)
})
}
/**
* Request Form
* @param {string} uuid
* @param {number} id, integer identifier
*/
export function requestForm({ uuid, id }) {
return Instance.call(this).requestForm({
uuid,
id
export function requestReference({
uuid,
columnName
}) {
return requestRest({
url: '/dictionary/reference',
method: 'get',
params: {
uuid,
column_name: columnName
}
})
.then(evaluateResponse)
.then(validationResponse => {
const { convertReference } = require('@/utils/ADempiere/apiConverts/field.js')
return convertReference(validationResponse)
})
}
export function requestReference({ referenceUuid, columnName }) {
return Instance.call(this).requestReference({
referenceUuid,
columnName
export function requestValidationRule({
uuid,
id
}) {
return requestRest({
url: '/dictionary/validation',
method: 'get',
params: {
uuid,
id
}
})
}
.then(evaluateResponse)
.then(validationResponse => {
const { convertValidationRule } = require('@/utils/ADempiere/apiConverts/dictionary.js')
export function requestValidationRule({ validationRuleUuid }) {
return Instance.call(this).requestValidationRule({
validationRuleUuid
})
return convertValidationRule(validationResponse)
})
}

View File

@ -1,5 +1,11 @@
// Get Instance for connection
import { EnrollmentInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
const clientVersion = '1.0.0'
const applicationType = 'ADempiere-Vue'
/**
* enroll User
@ -7,21 +13,61 @@ import { EnrollmentInstance as Instance } from '@/api/ADempiere/instances.js'
* @param {string} userData.userName
* @param {string} userData.password
*/
export function enrollmentUser({ name, userName, password, eMail }) {
return Instance.call(this).enrollUser({
name,
userName,
password,
eMail
export function requestEnrollUser({
name,
userName,
password,
eMail
}) {
return requestRest({
url: '/enrollment/enroll',
data: {
user_name: userName,
name,
email: eMail,
password,
client_version: clientVersion,
application_type: applicationType
}
})
.then(evaluateResponse)
.then(enrollResponse => {
return {
userName: enrollResponse.user_name,
name: enrollResponse.name,
eMail: enrollResponse.email
}
})
}
/**
* Request from forgot password
* @param {string} eMailOrUserName
*/
export function forgotPassword(eMailOrUserName) {
return Instance.call(this).requestResetPassword(eMailOrUserName)
export function requestForgotPassword(eMailOrUserName) {
let userName, eMail
if (String(eMailOrUserName).includes('@')) {
eMail = eMailOrUserName
} else {
userName = eMailOrUserName
}
return requestRest({
url: '/enrollment/reset-password',
data: {
user_name: userName,
email: eMail,
client_version: clientVersion,
application_type: applicationType
}
})
.then(evaluateResponse)
.then(forgotResponse => {
return {
responseType: forgotResponse.response_type,
responseTypeStatus: forgotResponse.response_type_status
}
})
}
/**
@ -29,6 +75,49 @@ export function forgotPassword(eMailOrUserName) {
* @param {string} token
* @param {string} password
*/
export function resetPasswordFromToken({ token, password }) {
return Instance.call(this).resetPasswordFromToken({ token, password })
export function requestChangePassword({
token,
password
}) {
return requestRest({
url: '/enrollment/change-password',
data: {
token,
password,
client_version: clientVersion,
application_type: applicationType
}
})
.then(evaluateResponse)
.then(changePasswordResponse => {
return {
responseType: changePasswordResponse.response_type,
responseTypeStatus: changePasswordResponse.response_type_status
}
})
}
/**
* Request from reset password with token
* @param {string} token
* @param {string} password
*/
export function requestActivateUser({
token
}) {
return requestRest({
url: '/enrollment/activate-user',
data: {
token,
client_version: clientVersion,
application_type: applicationType
}
})
.then(evaluateResponse)
.then(activateUserResponse => {
return {
responseType: activateUserResponse.response_type,
responseTypeStatus: activateUserResponse.response_type_status
}
})
}

View File

@ -2,17 +2,16 @@ const tableName = 'C_Location'
/**
* Create a location and return the created entity
* @param {array} attributes
* @param {array} attributesList
*/
export function requestCreateLocationAddress({
attributes
attributesList
}) {
const { createEntity } = require('@/api/ADempiere/persistence.js')
const { requestCreateEntity } = require('@/api/ADempiere/persistence.js')
return createEntity({
return requestCreateEntity({
tableName,
attributes,
formatReturn: 'object'
attributesList
})
}
@ -25,9 +24,9 @@ export function requestGetLocationAddress({
id,
uuid
}) {
const { getEntity } = require('@/api/ADempiere/persistence.js')
const { requestGetEntity } = require('@/api/ADempiere/persistence.js')
return getEntity({
return requestGetEntity({
tableName,
recordId: id,
recordUuid: uuid
@ -38,20 +37,19 @@ export function requestGetLocationAddress({
* 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)
* @param {array} attributesList, all attributes (including empty values)
*/
export function requestUpdateLocationAddress({
id,
uuid,
attributes
attributesList
}) {
const { updateEntity } = require('@/api/ADempiere/persistence.js')
const { requestUpdateEntity } = require('@/api/ADempiere/persistence.js')
return updateEntity({
return requestUpdateEntity({
tableName,
recordId: id,
recordUuid: uuid,
attributes,
formatReturn: 'object'
attributesList
})
}

View File

@ -1,10 +1,12 @@
export function getLocatorList({
const tableName = 'M_Locator'
export function requestLocatorList({
warehouseId
}) {
const { getEntitiesList } = require('@/api/ADempiere/persistence')
const { requestListEntities } = require('@/api/ADempiere/persistence.js')
return getEntitiesList({
tableName: 'M_Locator',
return requestListEntities({
tableName,
whereClause: `M_Warehouse_ID = ${warehouseId}`
})
}

View File

@ -1,78 +1,144 @@
import { POSInstance as Instance } from '@/api/ADempiere/instances.js'
import Criteria from '@/utils/ADempiere/criteria.js'
// Get Instance for connection
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
/**
* method in price-checking.js as getProductPrice
* method in api/price-checking.js as requestGetProductPrice
*/
export { getProductPrice as findProduct } from '@/api/ADempiere/form/price-checking.js'
export { requestGetProductPrice as findProduct } from '@/api/ADempiere/form/price-checking.js'
export { requestGetConversionRate } from '@/api/ADempiere/system-core.js'
// List Point of sales
export function requestlistPointOfSales({
export function requestGetPointOfSales({
posUuid
}) {
return requestRest({
url: '/pos/get-point-of-sales',
data: {
point_of_sales_uuid: posUuid
}
})
.then(evaluateResponse)
.then(posResponse => {
const { convertPointOfSales } = require('@/utils/ADempiere/apiConverts/pos.js')
return convertPointOfSales(posResponse)
})
}
// List Point of sales
export function requestListPointOfSales({
userUuid,
pageSize,
pageToken
}) {
return Instance.call(this).listPointOfSales({
userUuid,
pageSize,
pageToken
return requestRest({
url: '/pos/list-point-of-sales',
data: {
user_uuid: userUuid
},
params: {
page_size: pageSize,
page_token: pageToken
}
})
.then(evaluateResponse)
.then(posListResponse => {
const { convertPointOfSales } = require('@/utils/ADempiere/apiConverts/pos.js')
return {
nextPageToken: posListResponse.next_page_token,
recordCount: posListResponse.record_count,
sellingPointsList: posListResponse.records.map(pos => {
return convertPointOfSales(pos)
})
}
})
}
// Create order from POS
export function createOrder({
export function requestCreateOrder({
posUuid,
customerUuid,
documentTypeUuid
documentTypeUuid,
salesRepresentativeUuid
}) {
return Instance.call(this).createOrder({
posUuid,
customerUuid,
documentTypeUuid
return requestRest({
url: '/pos/create-order',
data: {
pos_uuid: posUuid,
customer_uuid: customerUuid,
document_type_uuid: documentTypeUuid,
sales_representative_uuid: salesRepresentativeUuid
}
})
.then(evaluateResponse)
.then(createOrderResponse => {
const { convertOrder } = require('@/utils/ADempiere/apiConverts/pos.js')
return convertOrder(createOrderResponse)
})
}
// Update order from POS
export function updateOrder({
export function requestUpdateOrder({
orderUuid,
posUuid,
customerUuid,
description
}) {
return Instance.call(this).updateOrder({
orderUuid,
posUuid,
customerUuid,
description
return requestRest({
url: '/pos/update-order',
data: {
order_uuid: orderUuid,
pos_uuid: posUuid,
customer_uuid: customerUuid,
description
}
})
.then(evaluateResponse)
.then(updateOrderResponse => {
const { convertOrder } = require('@/utils/ADempiere/apiConverts/pos.js')
return convertOrder(updateOrderResponse)
})
}
// Create order line from order uuid and product
export function createOrderLine({
orderUuid,
warehouseUuid,
productUuid,
chargeUuid,
description,
quantity,
price,
discountRate
// Get order from uuid
export function requestGetOrder(orderUuid) {
return requestRest({
url: '/pos/update-order',
data: {
order_uuid: orderUuid
}
})
.then(evaluateResponse)
.then(getOrderResponse => {
const { convertOrder } = require('@/utils/ADempiere/apiConverts/pos.js')
return convertOrder(getOrderResponse)
})
}
// Create order from POS
export function requestDeleteOrder({
posUuid,
customerUuid,
documentTypeUuid,
salesRepresentativeUuid
}) {
return Instance.call(this).createOrderLine({
orderUuid,
warehouseUuid,
productUuid,
chargeUuid,
description,
quantity,
price,
discountRate
return requestRest({
url: '/pos/delete-order',
data: {
pos_uuid: posUuid,
customer_uuid: customerUuid,
document_type_uuid: documentTypeUuid,
sales_representative_uuid: salesRepresentativeUuid
}
})
}
// Create order line from order uuid and product
export function getOrder(orderUuid) {
return Instance.call(this).getOrder({ orderUuid })
.then(evaluateResponse)
}
// List orders from pos uuid
@ -92,9 +158,12 @@ export function requestListOrders({
pageSize,
pageToken
}) {
const criteria = new Criteria({ tableName: 'C_Order' })
/*
const Criteria = require('@/utils/ADempiere/criteria.js')
const criteria = new Criteria({
tableName: 'C_Order'
})
criteria.addCondition({
columnName: 'DocumentNo',
value: documentNo
@ -131,23 +200,71 @@ export function requestListOrders({
})
*/
return Instance.call(this).listOrders({
posUuid,
documentNo,
businessPartnerUuid,
grandTotal,
openAmount,
isPaid,
isProcessed,
isAisleSeller,
isInvoiced,
dateOrderedFrom,
dateOrderedTo,
salesRepresentativeUuid,
criteria: criteria.getCriteria(),
pageSize,
pageToken
return requestRest({
url: '/pos/list-orders',
data: {
pos_uuid: posUuid,
document_no: documentNo,
business_partner_uuid: businessPartnerUuid,
sales_representative_uuid: salesRepresentativeUuid,
grand_total: grandTotal,
open_amount: openAmount,
is_paid: isPaid,
is_processed: isProcessed,
is_aisle_seller: isAisleSeller,
is_invoiced: isInvoiced,
date_ordered_from: dateOrderedFrom,
date_ordered_to: dateOrderedTo
},
params: {
page_size: pageSize,
page_token: pageToken
}
})
.then(evaluateResponse)
.then(ordersListResponse => {
const { convertOrder } = require('@/utils/ADempiere/apiConverts/core.js')
return {
nextPageToken: ordersListResponse.next_page_token,
recordCount: ordersListResponse.record_count,
ordersList: ordersListResponse.records.map(productPrice => {
return convertOrder(productPrice)
})
}
})
}
// Create order line from order uuid and product
export function requestCreateOrderLine({
orderUuid,
warehouseUuid,
productUuid,
chargeUuid,
description,
quantity,
price,
discountRate
}) {
return requestRest({
url: '/pos/create-order-line',
data: {
order_uuid: orderUuid,
product_uuid: productUuid,
description,
quantity,
price,
discount_rate: discountRate,
charge_uuid: chargeUuid,
warehouse_uuid: warehouseUuid
}
})
.then(evaluateResponse)
.then(createOrderLineResponse => {
const { convertOrderLine } = require('@/utils/ADempiere/apiConverts/pos.js')
return convertOrderLine(createOrderLineResponse)
})
}
// updateOrderLine orders from pos uuid
@ -158,36 +275,80 @@ export function updateOrderLine({
price,
discountRate
}) {
return Instance.call(this).updateOrderLine({
orderLineUuid,
description,
quantity,
price,
discountRate
return requestRest({
url: '/pos/update-order-line',
data: {
is_add_quantity: true,
order_line_uuid: orderLineUuid,
description,
quantity,
price,
discount_rate: discountRate
}
})
.then(evaluateResponse)
.then(createOrderLineResponse => {
const { convertOrderLine } = require('@/utils/ADempiere/apiConverts/pos.js')
return convertOrderLine(createOrderLineResponse)
})
}
// delete Order Line
export function deleteOrderLine({
export function requestDeleteOrderLine({
orderLineUuid
}) {
return Instance.call(this).deleteOrderLine({
orderLineUuid
return requestRest({
url: '/pos/create-order-line',
data: {
order_line_uuid: orderLineUuid
}
})
.then(evaluateResponse)
}
export function listOrderLines({
orderUuid
export function requestListOrderLines({
orderUuid,
pageSize,
pageToken
}) {
return Instance.call(this).listOrderLines({
orderUuid
return requestRest({
url: '/pos/list-order-lines',
data: {
order_uuid: orderUuid
},
params: {
page_size: pageSize,
page_token: pageToken
}
})
.then(evaluateResponse)
.then(ordersLineListResponse => {
const { convertOrderLine } = require('@/utils/ADempiere/apiConverts/pos.js')
return {
nextPageToken: ordersLineListResponse.next_page_token,
recordCount: ordersLineListResponse.record_count,
orderLineList: ordersLineListResponse.records.map(productPrice => {
return convertOrderLine(productPrice)
})
}
})
}
export function getKeyLayout({ keyLayoutUuid }) {
return Instance.call(this).getKeyLayout({
keyLayoutUuid
return requestRest({
url: '/pos/get-key-layout',
data: {
key_layout_uuid: keyLayoutUuid
}
})
.then(evaluateResponse)
.then(keyLayoutResponse => {
const { convertKeyLayout } = require('@/utils/ADempiere/apiConverts/pos.js')
return convertKeyLayout(keyLayoutResponse)
})
}
// ListProductPrice
@ -198,21 +359,36 @@ export function requestListProductPrice({
warehouseUuid,
validFrom,
// Query
criteria,
// criteria,
pageSize,
pageToken
}) {
return Instance.call(this).requestListProductPrice({
searchValue,
priceListUuid,
businessPartnerUuid,
warehouseUuid,
validFrom,
// Query
criteria,
pageSize,
pageToken
return requestRest({
url: '/pos/list-product-prices',
data: {
price_list_uuid: priceListUuid,
search_value: searchValue,
valid_from: validFrom,
business_partner_uuid: businessPartnerUuid,
warehouse_uuid: warehouseUuid
},
params: {
page_size: pageSize,
page_token: pageToken
}
})
.then(evaluateResponse)
.then(productPriceListResponse => {
const { convertProductPrice } = require('@/utils/ADempiere/apiConverts/core.js')
return {
nextPageToken: productPriceListResponse.next_page_token,
recordCount: productPriceListResponse.record_count,
productPricesList: productPriceListResponse.records.map(productPrice => {
return convertProductPrice(productPrice)
})
}
})
}
export function requestPrintOrder({

View File

@ -1,7 +1,11 @@
// Get Instance for connection
import { POSInstance as Instance } from '@/api/ADempiere/instances.js'
// Get Instance for connectionimport {
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
export function getProductPrice({
// List Point of sales
export function requestGetProductPrice({
searchValue,
upc,
value,
@ -11,14 +15,23 @@ export function getProductPrice({
warehouseUuid,
validFrom
}) {
return Instance.call(this).getProductPrice({
searchValue,
upc,
value,
name,
priceListUuid,
businessPartnerUuid,
warehouseUuid,
validFrom
return requestRest({
url: '/pos/get-product-price',
data: {
search_value: searchValue,
upc,
value,
name,
price_list_uuid: priceListUuid,
business_partner_uuid: businessPartnerUuid,
warehouse_uuid: warehouseUuid,
valid_from: validFrom
}
})
.then(evaluateResponse)
.then(productPriceResponse => {
const { convertProductPrice } = require('@/utils/ADempiere/apiConverts/core.js')
return convertProductPrice(productPriceResponse)
})
}

View File

@ -1,85 +1,72 @@
// Get Instance for connection System Core
export const SystemCoreInstance = () => {
const SystemCore = require('@adempiere/grpc-core-client')
const { BUSINESS_DATA_ADDRESS } = require('@/api/ADempiere/constants')
const { getLanguage } = require('@/lang/index')
const { getToken, getCurrentOrganization, getCurrentWarehouse } = require('@/utils/auth')
/**
* Instance for connection to API RESTful with axios
* @author EdwinBetanc0urt <EdwinBetanc0urt@oulook.com>
* @param {string} url to resource request
* @param {string} method rest, 'get' and 'post' (as default)
* @param {object} data body to send post request
* @param {object} params to send get uri
* @param {string} responseType default is 'json'
* @returns {function}
*/
export function ApiRest({
url,
method = 'post',
data = {},
params = {},
responseType = 'json'
}) {
const setInterceptor = (request) => {
request.interceptors.response.use(response => {
return response.data
}, error => {
return Promise.reject(error)
})
return request.interceptors
}
const { API_REST_ADDRESS } = require('@/api/ADempiere/constants.js')
const axios = require('axios')
return new SystemCore({
host: BUSINESS_DATA_ADDRESS,
sessionUuid: getToken(),
organizationUuid: getCurrentOrganization(),
warehouseUuid: getCurrentWarehouse(),
language: getLanguage() || 'en_US'
const request = axios.create({
baseURL: API_REST_ADDRESS,
// timeout: 10000, // 10s
headers: {
'Content-Type': 'application/json;charset=UTF-8'
},
responseType
})
}
request.interceptors = setInterceptor(request)
// Instance for connection Access (or Security)
export const AccessInstance = () => {
const Access = require('@adempiere/grpc-access-client')
const { ACCESS_ADDRESS } = require('@/api/ADempiere/constants')
const { getLanguage } = require('@/lang/index')
return new Access({
host: ACCESS_ADDRESS,
version: 'Version Epale',
language: getLanguage() || 'en_US'
})
}
// Instance for connection Business Data
export const BusinessDataInstance = () => {
const BusinessData = require('@adempiere/grpc-data-client')
const { BUSINESS_DATA_ADDRESS } = require('@/api/ADempiere/constants')
const { getLanguage } = require('@/lang/index')
const { getToken, getCurrentOrganization, getCurrentWarehouse } = require('@/utils/auth')
return new BusinessData({
host: BUSINESS_DATA_ADDRESS,
sessionUuid: getToken(),
organizationUuid: getCurrentOrganization(),
warehouseUuid: getCurrentWarehouse(),
language: getLanguage() || 'en_US'
})
}
// Get Instance for connection
export const DictionaryInstance = () => {
const Dictionary = require('@adempiere/grpc-dictionary-client')
const { DICTIONARY_ADDRESS } = require('@/api/ADempiere/constants')
const { getLanguage } = require('@/lang/index')
const { getToken } = require('@/utils/auth')
return new Dictionary({
host: DICTIONARY_ADDRESS,
sessionUuid: getToken(),
language: getLanguage() || 'en_US'
})
}
// Instance for connection Enrollment
export const EnrollmentInstance = () => {
const Enrollment = require('@adempiere/grpc-enrollment-client')
const { ENROLLMENT_ADDRESS } = require('@/api/ADempiere/constants')
return new Enrollment(
ENROLLMENT_ADDRESS,
3.9,
'ADempiere-Vue'
)
}
export const POSInstance = () => {
const POS = require('@adempiere/grpc-pos-client')
const { BUSINESS_DATA_ADDRESS } = require('@/api/ADempiere/constants')
const { getLanguage } = require('@/lang/index')
const { getToken, getCurrentOrganization, getCurrentWarehouse } = require('@/utils/auth')
const language = getLanguage() || 'en_US'
params = {
token: getToken(),
language,
...params
}
return new POS({
host: BUSINESS_DATA_ADDRESS,
sessionUuid: getToken(),
organizationUuid: getCurrentOrganization(),
warehouseUuid: getCurrentWarehouse(),
language: getLanguage() || 'en_US'
return request({
url,
method,
data,
params
})
}
/**
* Evaluate the response if is a success or error
* @author EdwinBetanc0urt <EdwinBetanc0urt@oulook.com>
* @param {object} response
* @returns {mixed}
*/
export const evaluateResponse = (response) => {
if (response.code >= 400) {
const error = {
code: response.code,
message: response.result
}
throw error
}
return response.result
}

View File

@ -1,21 +1,38 @@
// Get Instance for connection
import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
/**
* Create entity
* @param {string} tableName
* @param {array} attributesList
*/
export function createEntity({
export function requestCreateEntity({
tableName,
attributes,
formatReturn = 'array'
attributesList
}) {
return Instance.call(this).requestCreateEntity({
tableName,
attributesList: attributes,
formatToConvert: formatReturn
attributesList = attributesList.map(parameter => {
return {
key: parameter.columnName,
value: parameter.value
}
})
return requestRest({
url: '/data/create',
data: {
table_name: tableName,
attributes: attributesList
}
})
.then(evaluateResponse)
.then(entityCreateResponse => {
const { convertEntity } = require('@/utils/ADempiere/apiConverts/persistence.js')
return convertEntity(entityCreateResponse)
})
}
/**
@ -25,20 +42,34 @@ export function createEntity({
* @param {string} recordUuid
* @param {array} attributesList
*/
export function updateEntity({
export function requestUpdateEntity({
tableName,
recordId,
recordUuid,
attributes,
formatReturn = 'array'
attributesList
}) {
return Instance.call(this).requestUpdateEntity({
tableName,
recordId,
recordUuid,
attributesList: attributes,
formatToConvert: formatReturn
attributesList = attributesList.map(parameter => {
return {
key: parameter.columnName,
value: parameter.value
}
})
return requestRest({
url: '/data/update',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid,
attributes: attributesList
}
})
.then(evaluateResponse)
.then(entityUpdateResponse => {
const { convertEntity } = require('@/utils/ADempiere/apiConverts/persistence.js')
return convertEntity(entityUpdateResponse)
})
}
/**
@ -47,12 +78,19 @@ export function updateEntity({
* @param {number} recordId
* @param {string} recordUuid
*/
export function deleteEntity({ tableName, recordId, recordUuid }) {
return Instance.call(this).requestDeleteEntity({
tableName,
recordId,
recordUuid
})
export function requestDeleteEntity({
tableName,
recordId,
recordUuid
}) {
return requestRest({
url: '/data/delete',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid
}
}).then(evaluateResponse)
}
/**
@ -64,22 +102,47 @@ export function deleteEntity({ tableName, recordId, recordUuid }) {
export function rollbackEntity({
tableName,
recordId,
recordUuid,
eventType
}) {
return Instance.call(this).requestRollbackEntity({
tableName,
recordId,
eventTypeExecuted: eventType
return requestRest({
url: '/data/rollback-entity',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid,
event_type: eventType
}
})
.then(evaluateResponse)
.then(entityResponse => {
const { convertEntity } = require('@/utils/ADempiere/apiConverts/persistence.js')
return convertEntity(entityResponse)
})
}
// Get entity from table name and record id or record uuid
export function getEntity({ tableName, recordId, recordUuid }) {
return Instance.call(this).requestGetEntity({
tableName,
recordId,
recordUuid
export function requestGetEntity({
tableName,
recordId,
recordUuid
}) {
return requestRest({
url: '/data/entity',
method: 'get',
params: {
table_name: tableName,
uuid: recordUuid,
id: recordId
}
})
.then(evaluateResponse)
.then(entityResponse => {
const { convertEntity } = require('@/utils/ADempiere/apiConverts/persistence.js')
return convertEntity(entityResponse)
})
}
/**
@ -88,27 +151,57 @@ export function getEntity({ tableName, recordId, recordUuid }) {
* @param {string} query
* @param {string} whereClause
* @param {array} conditionsList
* @param {array} columnsList // TODO: Add support on adempiere-vue
* @param {string} orderByClause
* @param {string} pageToken
*/
export function getEntitiesList({
export function requestListEntities({
tableName,
query,
whereClause,
conditionsList = [],
columnsList = [],
orderByClause,
limit,
pageToken,
pageSize
}) {
return Instance.call(this).requestListEntities({
tableName,
query,
whereClause,
conditionsList,
orderByClause,
pageToken,
pageSize
const filters = conditionsList.map(condition => {
const { value, operator, columnName, valueTo, values } = condition
return {
column_name: columnName,
value,
operator,
value_to: valueTo,
values
}
})
return requestRest({
url: '/data/list',
data: {
table_name: tableName,
// DSL Query
filters,
columns: columnsList,
// Custom Query
query,
where_clause: whereClause,
order_by_clause: orderByClause,
limit
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(entitiesListResponse => {
const { convertEntityList } = require('@/utils/ADempiere/apiConverts/persistence.js')
return convertEntityList(entitiesListResponse)
})
}
/**
@ -118,25 +211,54 @@ export function getEntitiesList({
* @param {string} recordUuid
* @param {number} recordId
*/
export function requestTranslations({ tableName, language, recordUuid, recordId, pageToken, pageSize }) {
return Instance.call(this).requestListTranslations({
tableName,
recordUuid,
recordId,
language,
pageToken,
pageSize
export function requestTranslations({
tableName,
language,
recordUuid,
recordId,
pageToken,
pageSize
}) {
return requestRest({
url: '/ui/list-translations',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid
},
params: {
language,
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(languageListResponse => {
const { convertTranslation } = require('@/utils/ADempiere/apiConverts/persistence.js')
return {
nextPageToken: languageListResponse.next_page_token,
recordCount: languageListResponse.record_count,
translationsList: languageListResponse.records.map(record => {
return convertTranslation(record)
})
}
})
}
// Download a resource from file name
export function getResource({ resourceUuid }, callBack = {
export function requestResource({ resourceUuid }, callBack = {
onData: () => {},
onStatus: () => {},
onEnd: () => {}
}) {
const stream = Instance.call(this).getResource({
resourceUuid
const stream = requestRest({
url: '/resource',
method: 'get',
params: {
resource_uuid: resourceUuid
}
})
stream.on('data', (response) => callBack.onData(response))
@ -145,3 +267,34 @@ export function getResource({ resourceUuid }, callBack = {
return stream
}
/**
* Get image with uri request
* @author EdwinBetanc0urt <EdwinBetanc0urt@oulook.com>
* @param {string} file
* @param {number} width
* @param {number} height
* @param {string} operation fit, resize
* @returns {promise} with array buffer in response
*/
export function requestImage({
file,
width = 300,
height = 300,
operation = 'fit'
}) {
const { getImagePath } = require('@/utils/ADempiere/resource.js')
const { urn } = getImagePath({
file,
width,
height,
operation
})
return requestRest({
url: urn,
method: 'get',
responseType: 'arraybuffer'
})
}

View File

@ -1,29 +1,66 @@
// Get Instance for connection
import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
import { convertPrivateAccess } from '@/utils/ADempiere/apiConverts/privateAccess.js'
// Get private access for a record
export function getPrivateAccessFromServer({ tableName, recordId, userUuid }) {
return Instance.call(this).requestGetPrivateAccess({
tableName,
recordId,
userUuid
export function requestGetPrivateAccess({
tableName,
recordId,
recordUuid
}) {
return requestRest({
url: '/ui/get-private-access',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid
}
})
.then(evaluateResponse)
.then(responsePrivateAccess => {
return convertPrivateAccess(responsePrivateAccess)
})
}
// Lock a record for a user
export function lockPrivateAccessFromServer({ tableName, recordId, userUuid }) {
return Instance.call(this).requestLockPrivateAccess({
tableName,
recordId,
userUuid
export function requestLockPrivateAccess({
tableName,
recordId,
recordUuid
}) {
return requestRest({
url: '/ui/lock-private-access',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid
}
})
.then(evaluateResponse)
.then(responsePrivateAccess => {
return convertPrivateAccess(responsePrivateAccess)
})
}
// Unlock a record from a user
export function unlockPrivateAccessFromServer({ tableName, recordId, userUuid }) {
return Instance.call(this).requestUnlockPrivateAccess({
tableName,
recordId,
userUuid
export function requestUnlockPrivateAccess({
tableName,
recordId,
recordUuid
}) {
return requestRest({
url: '/ui/unlock-private-access',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid
}
})
.then(evaluateResponse)
.then(responsePrivateAccess => {
return convertPrivateAccess(responsePrivateAccess)
})
}

View File

@ -1,13 +1,17 @@
// Get Instance for connection
import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
/**
* Request a process
* This function allows follow structure:
* @param {string} uuid, uuid from process to run
* @param {number} reportType
* @param {number} tableName, table name of tab, used only window
* @param {string} reportType, format to output report (pdf, html, csv, ...)
* @param {string} tableName, table name of tab, used only window
* @param {number} recordId, record identifier, used only window
* @param {string} recordUuid, record universal unique identifier, used only window
* @param {array} parametersList, parameters from process [{ columnName, value }]
* @param {array} selectionsList, selection records, used only browser
[{
@ -15,22 +19,89 @@ import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
selectionValues: [{ columnName, value }]
}]
* @param {string} printFormatUuid
* @param {boolean} isSummary
* @param {number} tableSelectedId, used only browser // TODO: Add support on adempiere-vue
* @param {string} reportViewUuid
*/
export function runProcess({ uuid, reportType, tableName, recordId, parametersList = [], selectionsList = [], printFormatUuid }) {
// Run Process
return Instance.call(this).requestRunProcess({
uuid,
reportType,
tableName,
recordId,
parametersList,
selectionsList,
printFormatUuid
export function requestRunProcess({
uuid,
reportType,
tableName,
recordId,
recordUuid,
parametersList = [],
selectionsList = [],
isSummary,
tableSelectedId,
printFormatUuid,
reportViewUuid
}) {
parametersList = parametersList.map(parameter => {
return {
key: parameter.columnName,
value: parameter.value
}
})
return requestRest({
url: '/data/process',
data: {
process_uuid: uuid,
table_name: tableName,
id: recordId,
uuid: recordUuid,
is_summary: isSummary,
report_type: reportType,
table_selected_id: tableSelectedId,
report_view_uuid: reportViewUuid,
parameters: parametersList,
selections: selectionsList,
print_format_uuid: printFormatUuid
}
})
.then(evaluateResponse)
.then(processRunResponse => {
const { convertProcessLog } = require('@/utils/ADempiere/apiConverts/process.js')
return convertProcessLog(processRunResponse)
})
}
// Request a Process Activity list
export function requestListProcessesLogs({ pageToken, pageSize }) {
export function requestListProcessesLogs({
tableName,
instanceUuid,
userUuid,
recordId,
recordUuid,
pageToken,
pageSize
}) {
// Get Process Activity
return Instance.call(this).requestListProcessesLogs({ pageToken, pageSize })
return requestRest({
url: '/logs/list-process-logs',
data: {
instance_uuid: instanceUuid,
user_uuid: userUuid,
table_name: tableName,
id: recordId,
uuid: recordUuid
},
params: {
page_size: pageSize,
page_token: pageToken
}
})
.then(evaluateResponse)
.then(processLogResponse => {
const { convertProcessLog } = require('@/utils/ADempiere/apiConverts/process.js')
return {
recordCount: processLogResponse.record_count,
processLogsList: processLogResponse.records.map(itemProcess => {
return convertProcessLog(itemProcess)
}),
nextPageToken: processLogResponse.next_page_token
}
})
}

View File

@ -1,57 +1,139 @@
// Get Instance for connection
import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
/**
* Request Pending Documents List
* @param {string} tableName
* @param {string} processUuid
*/
export function requestReportViews({ tableName, processUuid, pageToken, pageSize }) {
return Instance.call(this).requestListReportViews({
tableName,
processUuid,
pageToken,
pageSize
export function requestListReportsViews({
tableName,
processUuid,
pageToken,
pageSize
}) {
return requestRest({
url: '/ui/list-report-views',
data: {
table_name: tableName,
process_uuid: processUuid
},
params: {
page_token: pageToken,
page_size: pageSize
}
})
.then(evaluateResponse)
.then(reportViewResponse => {
const { convertReportView } = require('@/utils/ADempiere/apiConverts/report.js')
return {
nextPageToken: reportViewResponse.next_page_token,
recordCount: reportViewResponse.record_count,
reportViewsList: reportViewResponse.records.map(drill => {
return convertReportView(drill)
})
}
})
}
// Get print formats from table name, report view uuid or process uuid
export function requestPrintFormats({ tableName, reportViewUuid, processUuid, pageToken, pageSize }) {
return Instance.call(this).requestListPrintFormats({
tableName,
reportViewUuid,
processUuid,
pageToken,
pageSize
export function requestListPrintFormats({
tableName,
reportViewUuid,
processUuid,
pageToken,
pageSize
}) {
return requestRest({
url: '/ui/list-print-formats',
data: {
table_name: tableName,
report_view_uuid: reportViewUuid,
process_uuid: processUuid
},
params: {
page_token: pageToken,
page_size: pageSize
}
})
.then(evaluateResponse)
.then(responseListPrintFormats => {
const { convertListPrintFormats } = require('@/utils/ADempiere/apiConverts/report.js')
return convertListPrintFormats(responseListPrintFormats)
})
}
// Get drill tables for a report
export function requestDrillTables({ tableName, pageToken, pageSize }) {
return Instance.call(this).requestListDrillTables({
tableName,
pageToken,
pageSize
export function requestListDrillTables({
tableName,
pageToken,
pageSize
}) {
requestRest({
url: '/ui/list-drill-tables',
data: {
table_name: tableName
},
params: {
page_token: pageToken,
page_size: pageSize
}
})
.then(evaluateResponse)
.then(drillTablesResponse => {
const { convertDrillTables } = require('@/utils/ADempiere/apiConverts/report.js')
return {
drillTablesList: drillTablesResponse.records.map(drill => {
return convertDrillTables(drill)
}),
nextPageToken: drillTablesResponse.next_page_token,
recordCount: drillTablesResponse.record_count
}
})
}
// Get report output from parameters
export function getReportOutput({
parametersList,
export function requestGetReportOutput({
tableName,
printFormatUuid,
reportViewUuid,
isSummary,
reportName,
reportType
reportType,
parametersList,
// query criteria
query,
whereClause,
orderByClause
}) {
return Instance.call(this).requestGetReportOutput({
parametersList,
tableName,
printFormatUuid,
reportViewUuid,
isSummary,
reportName,
reportType
return requestRest({
url: '/ui/get-report-output',
data: {
table_name: tableName,
// reference
print_format_uuid: printFormatUuid,
report_view_uuid: reportViewUuid,
is_summary: isSummary,
report_name: reportName,
report_type: reportType,
// DSL Query
filters: parametersList,
// Custom Query
query,
where_clause: whereClause,
order_by_clause: orderByClause
}
})
.then(evaluateResponse)
.then(reportOutpuResponse => {
const { convertReportOutput } = require('@/utils/ADempiere/apiConverts/report.js')
return convertReportOutput(reportOutpuResponse)
})
}

View File

@ -1,5 +1,8 @@
// Get Instance for connection
import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
/**
* Run callout request
@ -14,17 +17,30 @@ import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
* @param {array} attributesList
* @returns {Map} Entity
*/
export function runCallOutRequest({ windowUuid, windowNo, tabUuid, tableName, columnName, value, oldValue, valueType, callout, attributesList = [] }) {
return Instance.call(this).requestRunCallout({
windowUuid,
windowNo,
tabUuid,
tableName,
columnName,
value,
oldValue,
valueType,
callout,
attributesList
export function runCallOutRequest({
windowUuid,
windowNo,
tabUuid,
tableName,
columnName,
value,
oldValue,
callout,
attributesList = []
}) {
return requestRest({
url: '/ui/run-callout',
data: {
table_name: tableName,
window_uuid: windowUuid,
tab_uuid: tabUuid,
callout,
column_name: columnName,
old_value: oldValue,
value,
window_no: windowNo,
attributes: attributesList
}
})
.then(evaluateResponse)
}

View File

@ -1,46 +1,69 @@
// Get Instance for connection
import { BusinessDataInstance as Instance, SystemCoreInstance } from '@/api/ADempiere/instances.js'
/**
* Checks if value is empty. Deep-checks arrays and objects
* Note: isEmpty([]) == true, isEmpty({}) == true, isEmpty([{0:false},"",0]) == true, isEmpty({0:1}) == false
* @param {boolean|array|object|number|string|date|map|set|function} value
* @returns {boolean}
*/
export function isEmptyValue(value) {
const { isEmptyValue } = require('@adempiere/grpc-core-client/src/convertValues.js')
return isEmptyValue(value)
}
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
// Get Organization list from role
export function getOrganizationsList({
export function requestOrganizationsList({
roleUuid,
roleId,
pageToken,
pageSize
}) {
return Instance.call(this).requestListOrganizations({
roleUuid,
roleId,
pageToken,
pageSize
return requestRest({
url: '/core/list-organizations',
data: {
role_id: roleId,
role_uuid: roleUuid
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(organizationsListResponse => {
const { convertOrganization } = require('@/utils/ADempiere/apiConverts/core.js')
return {
nextPageToken: organizationsListResponse.next_page_token,
recordCount: organizationsListResponse.record_count,
organizationsList: organizationsListResponse.records.map(organization => {
return convertOrganization(organization)
})
}
})
}
// Get Warehouses of Organization
export function getWarehousesList({
export function requestWarehousesList({
organizationUuid,
organizationId,
pageToken,
pageSize
}) {
return Instance.call(this).requestListWarehouses({
organizationUuid,
organizationId,
pageToken,
pageSize
return requestRest({
url: '/core/list-warehouses',
data: {
organization_id: organizationId,
organization_uuid: organizationUuid
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(warehousesListResponse => {
return {
nextPageToken: warehousesListResponse.next_page_token,
recordCount: warehousesListResponse.record_count,
warehousesList: warehousesListResponse.records
}
})
}
/**
@ -48,16 +71,51 @@ export function getWarehousesList({
* @param {string} uuid
* @param {number} id
*/
export function getCountryDefinition({ uuid, id }) {
return SystemCoreInstance.call(this).requestGetCountry({
uuid,
id
export function requestGetCountryDefinition({
id,
uuid
}) {
return requestRest({
url: '/core/country',
method: 'get',
params: {
id,
uuid
}
})
.then(evaluateResponse)
.then(countryResponse => {
const { convertCountry } = require('@/utils/ADempiere/apiConverts/core.js')
return convertCountry(countryResponse)
})
}
// Get languages from api
export function listLanguages({ pageToken, pageSize }) {
return Instance.call(this).requestListLanguages({ pageToken, pageSize })
export function requestLanguagesList({
pageToken,
pageSize
}) {
return requestRest({
url: '/core/list-languages',
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(languagesListResponse => {
const { convertLanguage } = require('@/utils/ADempiere/apiConverts/core.js')
return {
nextPageToken: languagesListResponse.next_page_token,
recordCount: languagesListResponse.record_count,
languagesList: languagesListResponse.records.map(language => {
return convertLanguage(language)
})
}
})
}
export function requestCreateBusinessPartner({
@ -85,39 +143,58 @@ export function requestCreateBusinessPartner({
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
return requestRest({
url: '/core/create-business-partner',
data: {
value,
tax_id: taxId,
duns,
naics,
name,
last_name: lastName,
description,
contact_name: contactName,
e_mail: eMail,
phone,
business_partner_group_uid: businessPartnerGroupUuid,
// Location
address1,
address2,
address3,
address4,
city_uuid: cityUuid,
city_name: cityName,
postal_code: postalCode,
region_uuid: regionUuid,
region_name: regionName,
country_uuid: countryUuid,
pos_uuid: posUuid
}
})
.then(evaluateResponse)
.then(businessPartnerResponse => {
const { convertBusinessPartner } = require('@/utils/ADempiere/apiConverts/core.js')
return convertBusinessPartner(businessPartnerResponse)
})
}
export function requestGetBusinessPartner({
searchValue
}) {
return SystemCoreInstance.call(this).requestGetBusinessPartner({
searchValue
return requestRest({
url: '/core/get-business-partner',
method: 'get',
params: {
search_value: searchValue
}
})
.then(evaluateResponse)
.then(businessPartnerResponse => {
const { convertBusinessPartner } = require('@/utils/ADempiere/apiConverts/core.js')
return convertBusinessPartner(businessPartnerResponse)
})
}
export function requestListBusinessPartner({
@ -129,21 +206,68 @@ export function requestListBusinessPartner({
postalCode,
phone,
// Query
criteria,
// criteria,
pageSize,
pageToken
}) {
return SystemCoreInstance.call(this).requestListBusinessPartner({
searchValue,
value,
name,
contactName,
eMail,
postalCode,
phone,
// Query
criteria,
pageSize,
pageToken
return requestRest({
url: '/core/list-business-partner',
data: {
search_value: searchValue,
value,
name,
contact_name: contactName,
e_mail: eMail,
phone,
// Location
postal_code: postalCode
},
params: {
page_size: pageSize,
page_token: pageToken
}
})
.then(evaluateResponse)
.then(businessPartnerResponse => {
const { convertBusinessPartner } = require('@/utils/ADempiere/apiConverts/core.js')
return {
nextPageToken: businessPartnerResponse.next_page_token,
recordCount: businessPartnerResponse.record_count,
businessPartnersList: businessPartnerResponse.records.map(businessPartner => {
return convertBusinessPartner(businessPartner)
})
}
})
}
/**
* TODO: Add uuid support
* @param {string} conversionTypeUuid
* @param {string} currencyFromUuid
* @param {string} currencyToUuid
* @param {date} conversionDate
* @returns {promise}
*/
export function requestGetConversionRate({
conversionTypeUuid,
currencyFromUuid,
currencyToUuid,
conversionDate
}) {
return requestRest({
url: '/core/get-conversion-rate',
data: {
conversion_type_uuid: conversionTypeUuid,
currency_from_uuid: currencyFromUuid,
currency_to_uuid: currencyToUuid,
conversion_date: conversionDate
}
})
.then(evaluateResponse)
.then(conversionRateResponse => {
const { convertConversionRate } = require('@/utils/ADempiere/apiConverts/core.js')
return convertConversionRate(conversionRateResponse)
})
}

View File

@ -0,0 +1,48 @@
// Get Instance for connection
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
/**
* Get Attachment
* @param {number} recordId
* @param {string} recordUuid // TODO: Add suppport to record uuid on backend
*/
export function requestResourceReference({
recordId,
recordUuid
}) {
return requestRest({
url: '/ui/resource-reference',
method: 'get',
params: {
image_id: recordId,
image_uuid: recordUuid
}
})
.then(evaluateResponse)
}
/**
* Get Attachment
* @param {string} tableName
* @param {number} recordId
* @param {string} recordUuid
*/
export function requestAttachment({
tableName,
recordId,
recordUuid
}) {
return requestRest({
url: '/ui/attachment',
method: 'get',
params: {
table_name: tableName,
id: recordId,
uuid: recordUuid
}
})
.then(evaluateResponse)
}

View File

@ -1,23 +1,40 @@
// Get Instance for connection
import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
import { isEmptyValue } from '@/utils/ADempiere/valueUtils.js'
/**
* Request a Lookup data from Reference
* The main attributes that function hope are:
* @param {string} columnName
* @param {string} tableName
* @param {string} directQuery
* @param {string|number} value
*/
export function requestLookup({
columnName,
tableName,
directQuery,
value
}) {
return Instance.call(this).requestLookupFromReference({
tableName,
directQuery,
value
let filters = []
if (!isEmptyValue(value)) {
filters = [{
column_name: columnName,
value
}]
}
return requestRest({
url: '/ui/get-lookup-item',
data: {
table_name: tableName,
query: directQuery,
filters
}
})
.then(evaluateResponse)
}
/**
@ -25,24 +42,50 @@ export function requestLookup({
* The main attributes that function hope are:
* @param {string} tableName
* @param {string} query
* @param {Array<String>|<Number>} valuesList
* @param {string} whereClause
* @param {array} valuesList // TODO: Add support
* @param {string} pageToken
* @param {number} pageSize
*/
export function requestLookupList({
tableName,
query,
whereClause,
columnName,
valuesList = [],
pageToken,
pageSize
}) {
return Instance.call(this).requestListLookupFromReference({
tableName,
query,
valuesList,
pageToken,
pageSize
let filters = []
if (!isEmptyValue(valuesList)) {
filters = [{
column_name: columnName,
values: valuesList
}]
}
return requestRest({
url: '/ui/list-lookup-items',
data: {
table_name: tableName,
query,
where_clause: whereClause,
filters
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(lookupListResponse => {
return {
nextPageToken: lookupListResponse.next_page_token,
recordCount: lookupListResponse.record_count,
recordsList: lookupListResponse.records
}
})
}
/**
@ -52,23 +95,71 @@ export function requestLookupList({
* @param {string} recordUuid
* @param {number} recordId
*/
export function getReferencesList({ windowUuid, tableName, recordId, recordUuid, pageToken, pageSize }) {
return Instance.call(this).requestListReferences({
windowUuid,
tableName,
recordId,
recordUuid,
pageToken,
pageSize
export function requestReferencesList({
windowUuid,
tableName,
recordId,
recordUuid,
pageToken,
pageSize
}) {
return requestRest({
url: '/ui/list-references',
data: {
id: recordId,
uuid: recordUuid,
window_uuid: windowUuid,
table_name: tableName
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(referencesListResposnse => {
const { convertReferencesList } = require('@/utils/ADempiere/apiConverts/values.js')
return convertReferencesList(referencesListResposnse)
})
}
// Get default value for a field
export function getDefaultValueFromServer(query) {
return Instance.call(this).requestGetDefaultValue(query)
export function requestDefaultValue(query) {
return requestRest({
url: '/ui/get-default-value',
data: {
query
}
})
.then(evaluateResponse)
}
// Get context information for a window, tab or field
export function getContextInfoValueFromServer({ uuid, query }) {
return Instance.call(this).requestGetContextInfoValue({ uuid, query })
/**
* Get context information for a window, tab or field
* @param {string} query
* @param {string} uuid
* @param {number} id
*/
export function requestGetContextInfoValue({
uuid,
id,
query
}) {
return requestRest({
url: '/ui/get-context-info-value',
data: {
query,
uuid,
id
}
})
.then(evaluateResponse)
.then(contextInfoValueResponse => {
return {
messageText: contextInfoValueResponse.message_text,
messageTip: contextInfoValueResponse.message_tip
}
})
}

View File

@ -1,34 +1,77 @@
// Get Instance for connection
import { BusinessDataInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
// Get list of log for a records
export function requestListRecordsLogs({
export function requestListEntityLogs({
tableName,
recordId,
recordUuid,
pageToken,
pageSize
}) {
return Instance.call(this).requestListRecordsLogs({
tableName,
recordId,
pageToken,
pageSize
return requestRest({
url: '/logs/list-entity-logs',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(entityLogsListResponse => {
const { convertEntityLog } = require('@/utils/ADempiere/apiConverts/window.js')
return {
nextPageToken: entityLogsListResponse.next_page_token,
recordCount: entityLogsListResponse.record_count,
entityLogsList: entityLogsListResponse.records.map(entityLog => {
return convertEntityLog(entityLog)
})
}
})
}
// Get workflow log for a record
export function requestListWorkflowsLogs({
tableName,
recordId,
recordUuid,
pageToken,
pageSize
}) {
return Instance.call(this).requestListWorkflowsLogs({
tableName,
recordId,
pageToken,
pageSize
return requestRest({
url: '/logs/list-workflow-logs',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(workflowLogsListResponse => {
const { convertWorkflowProcess } = require('@/utils/ADempiere/apiConverts/window.js')
return {
nextPageToken: workflowLogsListResponse.next_page_token,
recordCount: workflowLogsListResponse.record_count,
workflowLogsList: workflowLogsListResponse.records.map(workflowLog => {
return convertWorkflowProcess(workflowLog)
})
}
})
}
// Get workflow list for a document
@ -37,11 +80,29 @@ export function requestListWorkflows({
pageToken,
pageSize
}) {
return Instance.call(this).requestListWorkflows({
tableName,
pageToken,
pageSize
return requestRest({
url: '/workflow/list-workflow',
data: {
table_name: tableName
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(workflowListResponse => {
const { convertWorkflowDefinition } = require('@/utils/ADempiere/apiConverts/window.js')
return {
nextPageToken: workflowListResponse.next_page_token,
recordCount: workflowListResponse.record_count,
workflowsList: workflowListResponse.records.map(workflowDefinition => {
return convertWorkflowDefinition(workflowDefinition)
})
}
})
}
/**
@ -50,13 +111,38 @@ export function requestListWorkflows({
* @param {string} pageToken
* @param {string} pageSize
*/
export function requestListRecordChats({ tableName, recordId, pageToken, pageSize }) {
return Instance.call(this).requestListRecordChats({
tableName,
recordId,
pageToken,
pageSize
export function requestListEntityChats({
tableName,
recordId,
recordUuid,
pageToken,
pageSize
}) {
return requestRest({
url: '/logs/list-entity-chats',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(entityChatListResponse => {
const { convertEntityChat } = require('@/utils/ADempiere/apiConverts/window.js')
return {
nextPageToken: entityChatListResponse.next_page_token,
recordCount: entityChatListResponse.record_count,
entityChatsList: entityChatListResponse.records.map(entityChat => {
return convertEntityChat(entityChat)
})
}
})
}
/**
@ -64,25 +150,65 @@ export function requestListRecordChats({ tableName, recordId, pageToken, pageSiz
* @param {string} pageToken
* @param {string} pageSize
*/
export function requestListChatEntries({ uuid, pageToken, pageSize }) {
return Instance.call(this).requestListChatEntries({
uuid,
pageToken,
pageSize
export function requestListChatsEntries({
id,
uuid,
pageToken,
pageSize
}) {
return requestRest({
url: '/logs/list-chat-entries',
data: {
id,
uuid
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(chatEntriesListResponse => {
const { convertChatEntry } = require('@/utils/ADempiere/apiConverts/window.js')
return {
nextPageToken: chatEntriesListResponse.next_page_token,
recordCount: chatEntriesListResponse.record_count,
chatEntriesList: chatEntriesListResponse.records.map(chatEntry => {
return convertChatEntry(chatEntry)
})
}
})
}
/**
* @param {string} tableName
* @param {string} recordId
* @param {string} recordUuid
* @param {string} comment
*/
export function requestCreateChatEntry({ tableName, recordId, comment }) {
return Instance.call(this).requestCreateChatEntry({
tableName,
recordId,
comment
export function requestCreateChatEntry({
tableName,
recordId,
recordUuid,
comment
}) {
return requestRest({
url: '/ui/create-chat-entry',
data: {
table_name: tableName,
id: recordId,
uuid: recordUuid,
comment: comment
}
})
.then(evaluateResponse)
.then(chatEntryResponse => {
const { convertChatEntry } = require('@/utils/ADempiere/apiConverts/window.js')
return convertChatEntry(chatEntryResponse)
})
}
/**
@ -95,27 +221,74 @@ export function requestCreateChatEntry({ tableName, recordId, comment }) {
* @param {number} pageSize
* @param {string} pageToken
*/
export function requestListDocumentStatuses({ tableName, recordId, recordUuid, documentStatus, documentAction, pageSize, pageToken }) {
return Instance.call(this).requestListDocumentStatuses({
tableName,
recordId,
recordUuid,
documentStatus,
documentAction,
pageSize,
pageToken
export function requestListDocumentStatuses({
tableName,
recordId,
recordUuid,
documentStatus,
documentAction,
pageSize,
pageToken
}) {
return requestRest({
url: '/workflow/list-document-actions',
data: {
id: recordId,
uuid: recordUuid,
table_name: tableName,
document_action: documentAction,
document_status: documentStatus
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(listDocumentsActionsResponse => {
return {
nextPageToken: listDocumentsActionsResponse.next_page_token,
recordCount: listDocumentsActionsResponse.record_count,
documentStatusesList: listDocumentsActionsResponse.records
}
})
}
// Request a document action list from current status of document
export function requestListDocumentActions({ tableName, recordId, recordUuid, documentStatus, documentAction, pageSize, pageToken }) {
return Instance.call(this).requestListDocumentActions({
tableName,
recordId,
recordUuid,
documentStatus,
documentAction,
pageSize,
pageToken
export function requestListDocumentActions({
tableName,
recordId,
recordUuid,
documentStatus,
documentAction,
pageSize,
pageToken
}) {
return requestRest({
url: '/workflow/list-document-actions',
data: {
id: recordId,
uuid: recordUuid,
table_name: tableName,
document_action: documentAction,
document_status: documentStatus
},
params: {
// Page Data
pageToken,
pageSize
}
})
.then(evaluateResponse)
.then(listDocumentsActionsResponse => {
return {
nextPageToken: listDocumentsActionsResponse.next_page_token,
recordCount: listDocumentsActionsResponse.record_count,
defaultDocumentAction: {
...listDocumentsActionsResponse.default_document_action
},
documentActionsList: listDocumentsActionsResponse.records
}
})
}

View File

@ -1,4 +1,8 @@
import request from '@/utils/request'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
export function getRoutes() {
return request({
@ -14,6 +18,25 @@ export function getRoles() {
})
}
export function requestRolesList(token) {
return requestRest({
url: 'user/roles',
method: 'get',
params: {
token
}
})
.then(evaluateResponse)
.then(responseRoles => {
const { convertRole } = require('@/utils/ADempiere/apiConverts/user.js')
const rolesList = responseRoles.map(itemRol => {
return convertRole(itemRol)
})
return rolesList
})
}
export function addRole(data) {
return request({
url: '/vue-element-admin/role',
@ -36,3 +59,31 @@ export function deleteRole(id) {
method: 'delete'
})
}
/**
* Change role of access
* @param {string} roleUuid
* @param {string} organizationUuid
* @param {string} warehouseUuid
*/
export function requestChangeRole({
roleUuid,
organizationUuid,
warehouseUuid
}) {
return requestRest({
url: 'user/change-role',
method: 'post',
data: {
role: roleUuid,
organization: organizationUuid,
warehouse: warehouseUuid
}
})
.then(evaluateResponse)
.then(responseChangeRole => {
const { convertSession } = require('@/utils/ADempiere/apiConverts/user.js')
return convertSession(responseChangeRole)
})
}

View File

@ -1,54 +1,89 @@
// Instance for connection
import { AccessInstance as Instance } from '@/api/ADempiere/instances.js'
import {
ApiRest as requestRest,
evaluateResponse
} from '@/api/ADempiere/instances.js'
// Make login by UserName and password, this function can return user data for show
/**
* Make login by UserName and password, this function can return user data for show
* @param {string} userName
* @param {string} password
*/
export function login({
userName,
password: userPass,
role
password
}) {
if (role && role.trim() !== '') {
return Instance.call(this).requestLogin({
userName,
userPass,
role
})
}
return Instance.call(this).requestLoginDefault({
userName,
userPass
return requestRest({
url: '/user/login',
method: 'post',
data: {
username: userName,
password
}
})
}
// Get User Info from session Uuid or token
/**
* Get User Info
* @param {string} token or session UUID
*/
export function requestUserInfoFromSession(token) {
return Instance.call(this).requestUserInfoFromSession(token)
return requestRest({
url: '/user/info',
method: 'get',
params: {
token
}
})
.then(evaluateResponse)
}
/**
* Get session info
* @param {string} sessionUuid
* @param {string} token or session UUID
*/
export function getSessionInfo(sessionUuid) {
return Instance.call(this).getSession(sessionUuid)
}
export function requestSessionInfo(token) {
return requestRest({
url: '/user/session',
method: 'get',
params: {
token
}
})
.then(evaluateResponse)
.then(responseSession => {
const { convertSession } = require('@/utils/ADempiere/apiConverts/user.js')
// Logout from server
export function logout(sessionUuid) {
return Instance.call(this).requestLogOut(sessionUuid)
return convertSession(responseSession)
})
}
/**
*
* @param {string} attributes.sessionUuid
* @param {string} attributes.roleUuid
* @param {string} attributes.organizationUuid
* @param {string} attributes.warehouseUuid
* Logout from server
* @param {string} token or session UUID
*/
// Get User menu from server
export function getMenu(sessionUuid) {
return Instance.call(this).requestUserMenuFromSession(sessionUuid)
export function logout(token) {
return requestRest({
url: '/user/logout',
data: {
token
}
})
}
export function changeRole(attributes) {
return Instance.call(this).requestChangeRole(attributes)
/**
* Get User menu from server
* @param {string} sessionUuid
*/
export function requestMenu({
sessionUuid
}) {
return requestRest({
url: '/user/menu',
method: 'get',
params: {
token: sessionUuid
}
})
.then(evaluateResponse)
}

View File

@ -75,24 +75,21 @@ export default {
},
handleCurrentChange(getRecordNotification, val, index, rows) {
if (val !== null) {
let options = {
name: 'ProcessActivity'
}
if (getRecordNotification && getRecordNotification.isReport && val.className !== 'procesActivity') {
this.$router.push({
options = {
name: 'Report Viewer',
params: {
processId: getRecordNotification.processId,
instanceUuid: getRecordNotification.instanceUuid,
fileName: getRecordNotification.download
}
}).catch(error => {
console.info(`${this.name} Component: ${error.name}, ${error.message}`)
})
} else {
this.$router.push({
name: 'ProcessActivity'
}).catch(error => {
console.info(`${this.name} Component: ${error.name}, ${error.message}`)
})
}
}
this.$router.push(options, () => {})
}
},
deleteRow(index, rows) {

View File

@ -0,0 +1,157 @@
<template>
<div>
<el-card
v-if="isNote"
class="box-card chat-entries-list-card"
:style="{ 'height': getHeightPanelBottom + 'vh' }"
>
<span
slot="header"
class="clearfix chat-entries-card-title"
>
{{ $t('window.containerInfo.notes') }}
</span>
<el-scrollbar wrap-class="scroll-window-log-chat">
<el-timeline>
<el-timeline-item
v-for="(chats, key) in chatList"
:key="key"
:timestamp="translateDate(chats.logDate)"
placement="top"
>
<el-card shadow="hover">
<div v-markdown="chats.characterData" />
</el-card>
</el-timeline-item>
</el-timeline>
</el-scrollbar>
</el-card>
<el-card
class="box-card chat-entry-create-card"
>
<span slot="header" class="clearfix chat-entries-card-title">
{{ $t('window.containerInfo.logWorkflow.addNote') }}
</span>
<el-scrollbar>
<input-chat />
<el-button
icon="el-icon-success"
style="background: #008fd3; float: right"
type="primary"
circle
@click="sendComment()"
/>
</el-scrollbar>
</el-card>
</div>
</template>
<script>
import inputChat from './inputChat'
export default {
name: 'ChatEntries',
components: {
inputChat
},
props: {
tableName: {
type: String,
default: undefined
},
recordId: {
type: Number,
default: undefined
}
},
computed: {
chatList() {
const commentLogs = this.$store.getters.getChatEntries
if (this.isEmptyValue(commentLogs)) {
return commentLogs
}
commentLogs.sort((a, b) => {
const c = new Date(a.logDate)
const d = new Date(b.logDate)
return c - d
})
return commentLogs
},
language() {
return this.$store.getters.language
},
tableNameToSend() {
if (this.isEmptyValue(this.tableName)) {
return this.$route.params.tableName
}
return this.tableName
},
recordIdToSend() {
if (this.isEmptyValue(this.recordId)) {
return this.$route.params.recordId
}
return this.recordId
},
isNote() {
return this.$store.getters.getIsNote
},
getHeightPanelBottom() {
return this.$store.getters.getSplitHeight - 14
}
},
methods: {
sendComment() {
const comment = this.$store.getters.getChatTextLong
if (!this.isEmptyValue(comment)) {
this.$store.dispatch('createChatEntry', {
tableName: this.tableNameToSend,
recordId: this.recordIdToSend,
comment
})
}
},
translateDate(value) {
return this.$d(new Date(value), 'long', this.language)
}
}
}
</script>
<style lang="scss">
.chat-entries-list-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
// brings the card space closer to the timerline
.el-card__body {
padding-left: 0px !important;
}
.el-timeline-item__content {
.el-card {
// remove the right spacing so that it does not overlap with the scroll
margin-right: 20px !important;
// removes excessive card content space from chat logs
.el-card__body {
padding-left: 20px !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
}
}
}
}
.chat-entry-create-card {
// small title of the card
.el-card__header {
max-height: 35px !important;
padding: 10px 20px !important;
}
}
</style>

View File

@ -0,0 +1,68 @@
<template>
<toast-editor
ref="editor"
:initial-value="value"
height="150px"
:options="editorOptions"
@change="onContentChanged"
/>
</template>
<script>
import 'codemirror/lib/codemirror.css'
import '@toast-ui/editor/dist/toastui-editor.css'
import { Editor } from '@toast-ui/vue-editor'
import '@toast-ui/editor/dist/i18n/es-es'
import { getLanguage } from '@/lang'
export default {
components: {
toastEditor: Editor
},
computed: {
editorInstance() {
return this.$refs.editor
},
editorOptions() {
return {
language: this.language,
minHeight: '100px',
usageStatistics: false, // send hostname to google analytics
hideModeSwitch: true
}
},
language() {
const langInCookie = getLanguage()
// https://github.com/nhn/tui.editor/tree/master/apps/editor/src/js/i18n
if (this.isEmptyValue(langInCookie)) {
return 'en-US'
}
return langInCookie.replace('_', '-')
},
value: {
get() {
return this.$store.getters.getChatTextLong
},
set(newValue) {
this.$store.commit('setChatText', newValue)
}
},
markdownValue() {
return this.editorInstance.invoke('getMarkdown')
}
},
watch: {
value(newValue) {
if (this.isEmptyValue(newValue)) {
this.editorInstance.invoke('setMarkdown', '')
}
}
},
methods: {
onContentChanged() {
this.value = this.markdownValue
}
}
}
</script>

View File

@ -1,51 +0,0 @@
<template>
<div>
<el-card
v-if="isNote"
class="box-card"
>
<div slot="header" class="clearfix">
<span>{{ $t('window.containerInfo.notes') }}</span>
</div>
<el-scrollbar wrap-class="scroll-window-log-chat">
<el-timeline>
<el-timeline-item
v-for="(chats, key) in gettersLischat"
:key="key"
:timestamp="translateDate(chats.logDate)"
placement="top"
>
<!-- <field-text-long /> -->
<el-card shadow="hover">
<div>
<div v-markdown="chats.characterData" />
</div>
</el-card>
</el-timeline-item>
</el-timeline>
</el-scrollbar>
</el-card>
<el-card class="box-card">
<div slot="header" class="clearfix">
{{ $t('window.containerInfo.logWorkflow.addNote') }}
</div>
<chat-text-long
v-model="chatNote"
/>
<el-button icon="el-icon-success" style="background: #008fd3; float: right" type="primary" circle @click="sendComment(chatNote)" />
</el-card>
</div>
</template>
<script>
import MixinInfo from './mixinInfo.js'
import chatTextLong from '@/components/ADempiere/Field/chatTextLong'
export default {
name: 'ChatEntries',
components: {
chatTextLong
},
mixins: [MixinInfo]
}
</script>

View File

@ -1,80 +1,11 @@
export default {
name: 'MixinContainerInfo',
data() {
return {
currentKey: 100,
typeAction: 0,
chatNote: ''
}
},
computed: {
gettersLischat() {
const commentLogs = this.$store.getters.getChatEntries
if (this.isEmptyValue(commentLogs)) {
return commentLogs
}
commentLogs.sort((a, b) => {
const c = new Date(a.logDate)
const d = new Date(b.logDate)
return c - d
})
return commentLogs
},
gettersListRecordLogs() {
const changeLog = this.$store.getters.getRecordLogs.recorLogs
return changeLog
},
getIsChangeLog() {
if (this.isEmptyValue(this.gettersListRecordLogs)) {
return false
}
return true
},
getIsChat() {
return this.$store.getters.getIsNote
},
gettersListWorkflow() {
return this.$store.getters.getWorkflow
},
getIsWorkflowLog() {
if (this.isEmptyValue(this.gettersListWorkflow)) {
return false
}
return true
},
language() {
return this.$store.getters.language
},
isNote() {
return this.$store.getters.getIsNote
}
},
methods: {
sendComment() {
const chatTextLong = this.$store.getters.getChatTextLong
if (!this.isEmptyValue(chatTextLong)) {
this.$store.dispatch('createChatEntry', {
tableName: this.$route.params.tableName,
recordId: this.$route.params.recordId,
comment: chatTextLong
})
.then(() => {
this.$store.dispatch('setMarkDown', true)
this.$store.dispatch('listChatEntries', {
tableName: this.$route.params.tableName,
recordId: this.$route.params.recordId
})
})
}
},
showkey(key, index) {
if (key === this.currentKey && index === this.typeAction) {
this.currentKey = 1000
} else {
this.currentKey = key
this.typeAction = index
}
},
translateDate(value) {
return this.$d(new Date(value), 'long', this.language)
}

View File

@ -16,13 +16,39 @@
<el-card shadow="hover" class="clearfix">
<div>
{{ listLogs.userName }}
<el-link type="primary" style="float: right;" @click="showkey(key)"> {{ $t('window.containerInfo.changeDetail') }} </el-link>
<el-link
type="primary"
style="float: right;"
@click="showkey(key)"
>
{{ $t('window.containerInfo.changeDetail') }}
</el-link>
</div>
<el-collapse-transition>
<div v-show="(currentKey === key)">
<span v-for="(list, index) in listLogs.changeLogs" :key="index">
<p v-if="list.columnName === 'DocStatus'"><b> {{ list.displayColumnName }} :</b> <strike> <el-tag :type="tagStatus(list.oldValue)"> {{ list.oldDisplayValue }} </el-tag> </strike> <el-tag :type="tagStatus(list.newValue)"> {{ list.newDisplayValue }} </el-tag> </p>
<p v-else><b> {{ list.displayColumnName }} :</b> <strike> <el-link type="danger"> {{ list.oldDisplayValue }} </el-link> </strike> <el-link type="success"> {{ list.newDisplayValue }} </el-link> </p>
<p v-if="list.columnName === 'DocStatus'">
<b> {{ list.displayColumnName }} :</b>
<strike>
<el-tag :type="tagStatus(list.oldValue)">
{{ list.oldDisplayValue }}
</el-tag>
</strike>
<el-tag :type="tagStatus(list.newValue)">
{{ list.newDisplayValue }}
</el-tag>
</p>
<p v-else>
<b> {{ list.displayColumnName }} :</b>
<strike>
<el-link type="danger">
{{ list.oldDisplayValue }}
</el-link>
</strike>
<el-link type="success">
{{ list.newDisplayValue }}
</el-link>
</p>
</span>
</div>
</el-collapse-transition>
@ -39,7 +65,15 @@ import MixinInfo from './mixinInfo.js'
export default {
name: 'RecordLogs',
mixins: [MixinInfo],
mixins: [
MixinInfo
],
data() {
return {
currentKey: 100,
typeAction: 0
}
},
computed: {
isMobile() {
return this.$store.state.app.device === 'mobile'
@ -55,6 +89,25 @@ export default {
return 'panel-mobile'
}
return 'panel'
},
gettersListRecordLogs() {
return this.$store.getters.getRecordLogs.entityLogs
},
getIsChangeLog() {
if (this.isEmptyValue(this.gettersListRecordLogs)) {
return false
}
return true
}
},
methods: {
showkey(key, index) {
if (key === this.currentKey && index === this.typeAction) {
this.currentKey = 1000
} else {
this.currentKey = key
this.typeAction = index
}
}
}
}

View File

@ -32,7 +32,10 @@
width="400"
trigger="hover"
>
<p><b> {{ $t('login.userName') }}:</b> {{ nodeList.userName }} </p>
<p>
<b> {{ $t('login.userName') }}:</b>
{{ nodeList.userName }}
</p>
<p v-if="!isEmptyValue(nodeList.textMessage)">
<b> {{ $t('window.containerInfo.logWorkflow.message') }}:</b>
{{ nodeList.textMessage }}
@ -70,7 +73,18 @@ import MixinInfo from './mixinInfo.js'
export default {
name: 'WorkflowLogs',
mixins: [MixinInfo]
mixins: [MixinInfo],
computed: {
gettersListWorkflow() {
return this.$store.getters.getWorkflow
},
getIsWorkflowLog() {
if (this.isEmptyValue(this.gettersListWorkflow)) {
return false
}
return true
}
}
}
</script>

View File

@ -69,14 +69,27 @@
<template slot="title">
{{ $t('data.exportRecord') }}
</template>
<el-menu-item v-for="(format, keyFormat) in supportedTypes" :key="keyFormat" :index="keyFormat" @click.native="exportRecord(keyFormat)">
<el-menu-item
v-for="(format, keyFormat) in supportedTypes"
:key="keyFormat"
:index="keyFormat"
@click.native="exportRecord(keyFormat)"
>
{{ format }}
</el-menu-item>
</el-submenu>
<el-menu-item v-show="$route.name === 'Report Viewer'" index="printFormat" @click="redirect">
<el-menu-item
v-show="$route.name === 'Report Viewer'"
index="printFormat"
@click="redirect"
>
{{ $t('components.contextMenuPrintFormatSetup') }}
</el-menu-item>
<el-menu-item v-if="isManageDataRecords" index="refreshData" @click="refreshData">
<el-menu-item
v-if="isManageDataRecords"
index="refreshData"
@click="refreshData"
>
{{ $t('components.contextMenuRefresh') }}
</el-menu-item>
<el-menu-item index="shareLink" @click="setShareLink">
@ -87,7 +100,11 @@
{{ $t('components.contextMenuActions') }}
</el-menu-item>
<el-submenu :disabled="!(isReferecesContent && isLoadedReferences)" class="el-menu-item" index="references">
<el-submenu
:disabled="!(isReferecesContent && isLoadedReferences)"
class="el-menu-item"
index="references"
>
<template slot="title">
{{ $t('components.contextMenuReferences') }}
</template>

View File

@ -1,6 +1,6 @@
import { showNotification } from '@/utils/ADempiere/notification.js'
import Item from './items'
import { convertFieldListToShareLink, recursiveTreeSearch } from '@/utils/ADempiere/valueUtils.js'
import { convertFieldsListToShareLink, recursiveTreeSearch } from '@/utils/ADempiere/valueUtils.js'
import { supportedTypes, exportFileFromJson } from '@/utils/ADempiere/exportUtil.js'
import ROUTES from '@/utils/ADempiere/zoomWindow'
@ -67,9 +67,7 @@ export default {
downloads: this.$store.getters.getProcessResult.url,
metadataMenu: {},
recordUuid: this.$route.query.action,
isLoadedReferences: false,
exportDefault: 'xls',
ROUTES
isLoadedReferences: false
}
},
computed: {
@ -111,17 +109,21 @@ export default {
return this.$store.getters.permission_routes
},
valuesPanelToShare() {
let containerUuid = this.containerUuid
if (this.$route.query.action === 'advancedQuery') {
containerUuid = 'table_' + containerUuid
}
return this.$store.getters.getParametersToShare({
containerUuid: this.containerUuid,
isOnlyDisplayed: true,
isAdvancedQuery: this.$route.query.action === 'advancedQuery'
containerUuid,
isOnlyDisplayed: true
})
},
getterFieldList() {
getterFieldsList() {
return this.$store.getters.getFieldsListFromPanel(this.containerUuid)
},
getterFieldListHeader() {
const header = this.getterFieldList.filter(fieldItem => {
getterFieldsListHeader() {
const header = this.getterFieldsList.filter(fieldItem => {
const isDisplayed = fieldItem.isDisplayed || fieldItem.isDisplayedFromLogic
if (fieldItem.isActive && isDisplayed && !fieldItem.isKey) {
return fieldItem.name
@ -131,8 +133,8 @@ export default {
return fieldItem.name
})
},
getterFieldListValue() {
const value = this.getterFieldList.filter(fieldItem => {
getterFieldsListValue() {
const value = this.getterFieldsList.filter(fieldItem => {
const isDisplayed = fieldItem.isDisplayed || fieldItem.isDisplayedFromLogic
if (fieldItem.isActive && isDisplayed && !fieldItem.isKey) {
return fieldItem
@ -275,14 +277,15 @@ export default {
console.warn(`Error getting data list tab. Message: ${error.message}, code ${error.code}.`)
})
} else if (this.panelType === 'browser') {
const fieldsEmpty = this.$store.getters.getFieldListEmptyMandatory({
const fieldsEmpty = this.$store.getters.getFieldsListEmptyMandatory({
containerUuid: this.containerUuid,
fieldsList: this.getterFieldList
fieldsList: this.getterFieldsList
})
if (fieldsEmpty.length) {
this.$message({
message: this.$t('notifications.mandatoryFieldMissing') + fieldsEmpty,
type: 'info'
type: 'info',
showClose: true
})
} else {
this.$store.dispatch('getBrowserSearch', {
@ -319,8 +322,8 @@ export default {
}
},
exportRecord(fotmatToExport) {
const tHeader = this.getterFieldListHeader
const filterVal = this.getterFieldListValue
const tHeader = this.getterFieldsListHeader
const filterVal = this.getterFieldsListValue
let list = []
if (this.panelType === 'window') {
list = this.getDataRecord
@ -355,7 +358,9 @@ export default {
this.actions = this.metadataMenu.actions
// TODO: Add store attribute to avoid making repeated requests
if (this.panelType === 'window') {
let isChangePrivateAccess = true
if (this.isReferecesContent) {
isChangePrivateAccess = false
if (!this.isEmptyValue(this.$route.params.tableName)) {
this.$store.dispatch('getPrivateAccessFromServer', {
tableName: this.$route.params.tableName,
@ -371,6 +376,7 @@ export default {
}
const processAction = this.actions.find(item => {
// TODO: Compare with 'action' attribute and not with 'name' (this change with language)
if (item.name === 'Procesar Orden' || (item.name === 'Process Order')) {
return item
}
@ -380,24 +386,32 @@ export default {
if (this.actions && this.actions.length) {
this.actions.forEach(itemAction => {
if (this.$route.meta.type === 'report' && itemAction.action === 'startProcess') {
const { action } = itemAction
if (this.$route.meta.type === 'report' && action === 'startProcess') {
itemAction.reportExportType = 'html'
}
// if no exists set prop with value
itemAction.disabled = false
if ((this.$route.name !== 'Report Viewer' && itemAction.action === 'changeParameters') ||
if ((this.$route.name !== 'Report Viewer' && action === 'changeParameters') ||
(this.$route.meta.type === 'process' && itemAction.type === 'summary')) {
itemAction.disabled = true
}
if (this.$route.meta.type === 'window') {
if (this.recordUuid === 'create-new' || !this.isInsertRecord) {
itemAction.disabled = true
if (isChangePrivateAccess) {
if (action === 'lockRecord') {
itemAction.hidden = false
} else if (action === 'unlockRecord') {
itemAction.hidden = true
}
}
// rollback
if (itemAction.action === 'undoModifyData') {
itemAction.disabled = Boolean(!this.getDataLog && !this.getOldRouteOfWindow)
} else if (this.recordUuid === 'create-new' || !this.isInsertRecord) {
itemAction.disabled = true
}
}
})
@ -444,10 +458,12 @@ export default {
query: {
...this.getOldRouteOfWindow.query
}
}).catch(error => {
console.info(`Context Menu Mixin: ${error.name}, ${error.message}`)
})
}, () => {})
} else {
if (action.action === 'setDefaultValues' && this.$route.query.action === 'create-new') {
return
}
this.$store.dispatch(action.action, {
parentUuid: this.parentUuid,
containerUuid: this.containerUuid,
@ -472,7 +488,7 @@ export default {
if (this.lastParameter !== undefined) {
containerParams = this.lastParameter
}
const fieldsNotReady = this.$store.getters.getFieldListEmptyMandatory({
const fieldsNotReady = this.$store.getters.getFieldsListEmptyMandatory({
containerUuid: containerParams
})
@ -590,13 +606,12 @@ export default {
// windowUuid: this.parentUuid,
tabParent: 0
}
}).catch(error => {
console.info(`Context Menu Mixin: ${error.name}, ${error.message}`)
})
}, () => {})
} else {
this.$message({
type: 'error',
message: this.$t('notifications.noRoleAccess')
message: this.$t('notifications.noRoleAccess'),
showClose: true
})
}
}
@ -604,7 +619,7 @@ export default {
setShareLink() {
let shareLink = this.panelType === 'window' || window.location.href.includes('?') ? `${window.location.href}&` : `${window.location.href}?`
if (this.$route.name === 'Report Viewer') {
const processParameters = convertFieldListToShareLink(this.processParametersExecuted)
const processParameters = convertFieldsListToShareLink(this.processParametersExecuted)
const reportFormat = this.$store.getters.getReportType
shareLink = this.$store.getters.getTempShareLink
if (String(processParameters).length) {
@ -661,15 +676,14 @@ export default {
})
},
redirect() {
const { uuid: name, tabParent } = ROUTES.PRINT_FORMAT_SETUP_WINDOW
this.$router.push({
name: ROUTES.PRINT_FORMAT_SETUP_WINDOW.uuid,
name,
query: {
action: this.getReportDefinition.output.printFormatUuid,
tabParent: ROUTES.PRINT_FORMAT_SETUP_WINDOW.tabParent
tabParent
}
}).catch(error => {
console.info(`Context Menu Mixin: ${error.name}, ${error.message}`)
})
}, () => {})
},
validatePrivateAccess({ isLocked, tableName, recordId }) {
if (this.isPersonalLock) {

View File

@ -54,7 +54,7 @@ export default {
query: {
tabParent: 0
}
})
}, () => {})
}
}
}

View File

@ -107,9 +107,7 @@ export default {
action: 'criteria',
tabParent
}
}).catch(error => {
console.info(`Dashboard/docstatus Component: ${error.name}, ${error.message}`)
})
}, () => {})
} else {
this.$message({
type: 'error',

View File

@ -24,7 +24,7 @@ export default {
handleClick(row) {
const viewSearch = this.recursiveTreeSearch({
treeData: this.permissionRoutes,
attributeValue: row.windowUuid,
attributeValue: row.referenceUuid,
attributeName: 'meta',
secondAttribute: 'uuid',
attributeChilds: 'children'
@ -46,9 +46,7 @@ export default {
action: recordUuid,
tabParent
}
}).catch(error => {
console.info(`Dashboard ${this.name}: ${error.name}, ${error.message}`)
})
}, () => {})
} else {
this.$message({
type: 'error',

View File

@ -35,13 +35,15 @@
</template>
<script>
import { getRecentItems as getRecentItemsFromServer } from '@/api/ADempiere/dashboard/dashboard'
import { requestListRecentItems } from '@/api/ADempiere/dashboard/dashboard'
import { convertAction } from '@/utils/ADempiere/dictionaryUtils'
import mixinDashboard from '@/components/ADempiere/Dashboard/mixinDashboard.js'
export default {
name: 'RecentItems',
mixins: [mixinDashboard],
mixins: [
mixinDashboard
],
data() {
return {
recentItems: [],
@ -54,6 +56,12 @@ export default {
return this.filterResult(this.search)
}
return this.recentItems
},
userUuid() {
return this.$store.getters['user/getUserUuid']
},
roleUuid() {
return this.$store.getters['user/getRole'].uuid
}
},
mounted() {
@ -67,10 +75,16 @@ export default {
methods: {
getRecentItems({ pageToken, pageSize }) {
return new Promise(resolve => {
getRecentItemsFromServer({ pageToken, pageSize })
requestListRecentItems({
userUuid: this.userUuid,
roleUuid: this.roleUuid,
pageToken,
pageSize
})
.then(response => {
const recentItems = response.recentItemsList.map(item => {
const actionConverted = convertAction(item.action)
return {
...item,
action: actionConverted.name,

View File

@ -87,7 +87,9 @@ export default {
getFavoritesList() {
const userUuid = this.$store.getters['user/getUserUuid']
return new Promise(resolve => {
getFavoritesFromServer({ userUuid })
getFavoritesFromServer({
userUuid
})
.then(response => {
const favorites = response.favoritesList.map(favoriteElement => {
const actionConverted = convertAction(favoriteElement.action)
@ -131,9 +133,7 @@ export default {
action: param,
tabParent: 0
}
}).catch(error => {
console.info(`Dashboard/userfavorites Component: ${error.name}, ${error.message}`)
})
}, () => {})
} else {
this.$message({
type: 'error',

View File

@ -0,0 +1,720 @@
import FieldDefinition from '@/components/ADempiere/Field'
import FilterColumns from '@/components/ADempiere/DataTable/filterColumns'
import FixedColumns from '@/components/ADempiere/DataTable/fixedColumns'
import TableContextMenu from '@/components/ADempiere/DataTable/menu/tableContextMenu'
import TableMainMenu from '@/components/ADempiere/DataTable/menu'
import IconElement from '@/components/ADempiere/IconElement'
import { formatField } from '@/utils/ADempiere/valueFormat'
import MainPanel from '@/components/ADempiere/Panel'
import { sortFields } from '@/utils/ADempiere/dictionaryUtils'
import { FIELDS_DECIMALS, FIELDS_QUANTITY, FIELDS_READ_ONLY_FORM } from '@/utils/ADempiere/references'
import { fieldIsDisplayed } from '@/utils/ADempiere'
import evaluator from '@/utils/ADempiere/evaluator'
import TableMixin from './mixin/tableMixin.js'
import TableMixinSort from './mixin/mixinTableSort.js'
import CustomPagination from '@/components/ADempiere/Pagination'
export default {
name: 'DataTable',
components: {
CustomPagination,
FieldDefinition,
FilterColumns,
FixedColumns,
IconElement,
MainPanel,
TableContextMenu,
TableMainMenu
},
mixins: [
TableMixin,
TableMixinSort
],
props: {
// Show check from selection row
isTableSelection: {
type: Boolean,
default: true
},
// Show check from selection row, send to panel form
isShowedPanelRecord: {
type: Boolean,
default: false
}
},
data() {
const activeName = []
// TODO: Manage attribute with vuex store in window module
if (this.isParent && this.$route.query.action && this.$route.query.action === 'advancedQuery') {
activeName.push('PanelAdvancedQuery')
}
return {
topContextualMenu: 0,
leftContextualMenu: 0,
currentRowMenu: {},
currentRow: null,
currentTable: 0,
visible: this.getShowContextMenuTable,
searchTable: '', // text from search
defaultMaxPagination: 50,
activeName,
rowStyle: {
height: '52px'
},
uuidCurrentRecordSelected: '',
showTableSearch: false
}
},
computed: {
isShowedContextMenu() {
if (this.isParent) {
return this.getShowContextMenuTable
}
return this.getShowContextMenuTabChildren
},
getMenuTable() {
const process = this.$store.getters.getContextMenu(this.containerUuid)
if (process && !this.isEmptyValue(process.actions)) {
return process.actions.filter(menu => {
if (menu.type === 'process' || menu.type === 'application') {
return menu
}
})
}
return []
},
getShowContextMenuTable() {
return this.$store.getters.getShowContextMenuTable
},
getShowContextMenuTabChildren() {
return this.$store.getters.getShowContextMenuTabChildren
},
panelMetadata() {
return this.$store.getters.getPanel(this.containerUuid)
},
isLoadedPanel() {
const panelMetadata = this.$store.getters.getPanel('table_' + this.containerUuid)
if (!this.isEmptyValue(panelMetadata)) {
return true
}
return false
},
isShowedTotals() {
return this.panelMetadata.isShowedTotals
},
isShowOptionalColumns() {
return this.panelMetadata.isShowedTableOptionalColumns
},
totalRecords() {
return this.getterDataRecordsAndSelection.recordCount
},
pageNumber() {
return this.getterDataRecordsAndSelection.pageNumber
},
isLoaded() {
return !this.getterDataRecordsAndSelection.isLoaded
},
fieldsIsDisplayed() {
return this.$store.getters.getFieldsIsDisplayed(this.containerUuid)
},
getterIsShowedCriteria() {
const browser = this.$store.getters.getBrowser(this.containerUuid)
if (browser) {
return browser.isShowedCriteria
}
return false
},
getHeightPanelBottom() {
return this.$store.getters.getSplitHeight - 25
},
getterHeight() {
return this.$store.getters.getHeigth
},
tableHeaderStyle() {
if (this.isParent) {
if (!this.isEmptyValue(this.activeName)) {
return {
height: '55%',
overflow: 'auto'
}
}
return {
height: '17%',
overflow: 'hidden'
}
}
return {
height: '35px'
}
},
getHeigthTable() {
let totalRow = 0
// to refresh height table if changed isShowedTotals
if (this.isShowedTotals) {
totalRow = 5
}
if (this.isPanelWindow) {
// table record navigation
if (this.isParent) {
if (this.isEmptyValue(this.activeName)) {
return this.getterHeight - 210 - totalRow
}
// panel advanced query is showed
return this.getterHeight - 420 - totalRow
}
// tabs children
if (totalRow) {
totalRow = 1
}
return (this.getHeightPanelBottom - 5 - totalRow) + 'vh'
} else if (this.panelType === 'browser') {
// open browser criteria
if (this.getterIsShowedCriteria) {
// showed some field in panel query criteria
if (this.fieldsIsDisplayed.isDisplayed) {
return this.getterHeight - 495 - totalRow
}
return this.getterHeight - 415 - totalRow
}
return this.getterHeight - 290 - totalRow
}
return this.getterHeight - 300 - totalRow
},
fieldsList() {
const panelMetadata = this.panelMetadata
if (panelMetadata && panelMetadata.fieldsList) {
if ((this.panelType === 'window' && this.isParent) || this.panelType === 'browser') {
let orderBy = 'seqNoGrid'
if (this.panelType === 'browser') {
orderBy = 'sequence'
}
return this.sortFields({
fieldsList: panelMetadata.fieldsList,
orderBy
})
}
return panelMetadata.fieldsList
}
return []
},
isLoadPanel() {
const panelMetadata = this.panelMetadata
if (panelMetadata && panelMetadata.fieldsList) {
return true
}
return false
},
preferenceClientId() {
if (this.isPanelWindow) {
return this.$store.getters.getPreferenceClientId
}
return undefined
},
shorcutKey() {
return {
f6: ['f6'],
ctrlf: ['ctrl', 'f']
}
},
keyUp() {
if (this.currentTable < 1) {
return this.currentTable
}
return this.currentTable - 1
},
keyDow() {
const maxDown = this.recordsData.length - 1
if (maxDown === this.currentTable) {
return this.currentTable
}
return this.currentTable + 1
}
},
watch: {
visible(value) {
if (value) {
document.body.addEventListener('click', this.closeMenu)
} else {
document.body.removeEventListener('click', this.closeMenu)
}
}
},
created() {
this.getPanel()
},
mounted() {
if (this.isTableSelection) {
this.toggleSelection(this.getDataSelection)
}
},
methods: {
sortFields,
actionAdvancedQuery() {
const activeNames = []
if (!this.activeName.length) {
activeNames.push('PanelAdvancedQuery')
if (this.isParent) {
const { isShowedRecordNavigation } = this.$store.getters.getWindow(this.parentUuid)
if (!isShowedRecordNavigation) {
this.$store.dispatch('changeWindowAttribute', {
parentUuid: this.parentUuid, // act as parentUuid
attributeName: 'isShowedRecordNavigation',
attributeValue: true
})
}
}
}
this.activeName = activeNames
},
setCurrent(row) {
this.$refs.multipleTable.setCurrentRow(row)
},
theAction(event) {
switch (event.srcKey) {
case 'up':
this.currentTable = this.keyUp
break
case 'down':
this.currentTable = this.keyDow
break
}
this.handleRowClick(this.recordsData[this.currentTable])
return this.setCurrent(this.recordsData[this.currentTable])
},
block() {
return false
},
rowMenu(row, column, event) {
const menuMinWidth = 105
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
const offsetWidth = this.$el.offsetWidth // container width
const maxLeft = offsetWidth - menuMinWidth // left boundary
const left = event.clientX - offsetLeft + 15 // 15: margin right
this.leftContextualMenu = left
if (left > maxLeft) {
this.leftContextualMenu = maxLeft
}
const offsetTop = this.$el.getBoundingClientRect().top
let top = event.clientY - offsetTop
if (this.panelType === 'browser' && this.panelMetadata.isShowedCriteria) {
top = event.clientY - 200
}
this.topContextualMenu = top
this.currentRowMenu = row
this.visible = true
// TODO: Verify use
this.$store.dispatch('showMenuTable', {
isShowedTable: this.isParent
})
this.$store.dispatch('showMenuTabChildren', {
isShowedTabChildren: !this.isParent
})
},
headerLabel(field) {
if (field.isMandatory || field.isMandatoryFromLogic) {
return '* ' + field.name
}
return field.name
},
/**
* @param {object} row, row data
* @param {object} field, field with attributes
*/
displayedValue(row, field) {
const { columnName, componentPath, displayColumnName, displayType } = field
let valueToShow
switch (componentPath) {
case 'FieldDate':
case 'FieldTime': {
let cell = row[columnName]
if (this.typeValue(cell) === 'DATE') {
cell = cell.getTime()
}
// replace number timestamp value for date
valueToShow = formatField(cell, displayType)
break
}
case 'FieldNumber':
if (this.isEmptyValue(row[columnName])) {
valueToShow = undefined
break
}
valueToShow = this.formatNumber({
displayType,
number: row[columnName]
})
break
case 'FieldSelect':
valueToShow = row[displayColumnName]
if (this.isEmptyValue(valueToShow) && row[columnName] === 0) {
valueToShow = field.defaultValue
break
}
break
case 'FieldYesNo':
// replace boolean true-false value for 'Yes' or 'Not' ('Si' or 'No' for spanish)
valueToShow = row[columnName]
? this.$t('components.switchActiveText')
: this.$t('components.switchInactiveText')
break
default:
valueToShow = row[columnName]
break
}
return valueToShow
},
rowCanBeEdited(record, fieldAttributes) {
if (!this.isParent) {
if (this.isPanelWindow) {
// getter with context
if (this.isReadOnlyParent) {
return false
}
// if record is IsActive, Processed, Processing
if (this.isReadOnlyRow(record, fieldAttributes)) {
return false
}
}
// if isReadOnly, isReadOnlyFromLogic
if (this.isReadOnlyCell(record, fieldAttributes)) {
return false
}
if (record.isEdit) {
return true
}
}
return false
},
isReadOnlyRow(row, field) {
// evaluate context
if (this.preferenceClientId !== parseInt(row.AD_Client_ID, 10)) {
return true
}
if (fieldIsDisplayed(field)) {
// columnName: IsActive
const fieldReadOnlyForm = FIELDS_READ_ONLY_FORM.find(item => {
return !item.isChangedAllForm &&
// columnName: IsActive, Processed, Processing
Object.prototype.hasOwnProperty.call(row, item.columnName)
})
if (fieldReadOnlyForm) {
const { columnName, valueIsReadOnlyForm } = fieldReadOnlyForm
// compare if is same key
return field.columnName !== columnName &&
// compare if is same value
row[columnName] === valueIsReadOnlyForm
}
}
return false
},
isReadOnlyCell(row, field) {
// TODO: Add support to its type fields
if (['FieldImage', 'FieldBinary'].includes(field.componentPath)) {
return true
}
const isUpdateableAllFields = field.isReadOnly || field.isReadOnlyFromLogic
if (this.isPanelWindow) {
const panelMetadata = this.panelMetadata
if (field.columnName === panelMetadata.linkColumnName ||
field.columnName === panelMetadata.fieldLinkColumnName) {
return true
}
// edit mode is diferent to create new
const editMode = !this.isEmptyValue(row.UUID)
return (!field.isUpdateable && editMode) || (isUpdateableAllFields || field.isReadOnlyFromForm)
} else if (this.panelType === 'browser') {
// browser result
return field.isReadOnly
}
// other type of panels (process/reports/forms)
return isUpdateableAllFields
},
callOffNewRecord() {
this.recordsData.shift()
},
tableRowClassName({ row, rowIndex }) {
if (row.isNew && rowIndex === 0) {
return 'warning-row'
}
return ''
},
addNewRow() {
if (this.newRecordsQuantity <= 0) {
this.$store.dispatch('addNewRow', {
parentUuid: this.parentUuid,
containerUuid: this.containerUuid,
fieldsList: this.fieldsList,
isEdit: true,
isSendServer: false
})
this.$refs.multipleTable.$refs.bodyWrapper.scrollTop = 0
} else {
const fieldsEmpty = this.$store.getters.getFieldsListEmptyMandatory({
containerUuid: this.containerUuid
})
this.$message({
message: this.$t('notifications.mandatoryFieldMissing') + fieldsEmpty,
type: 'info'
})
}
},
async setFocus() {
return new Promise(resolve => {
const fieldFocus = this.fieldsList.find(itemField => {
if (Object.prototype.hasOwnProperty.call(this.$refs, itemField.columnName)) {
if (fieldIsDisplayed(itemField) && !itemField.isReadOnly && itemField.isUpdateable) {
return true
}
}
})
this.$refs[fieldFocus.columnName][0].focusField()
resolve()
})
},
/**
* @param {object} field
*/
cellClass(field) {
let classReturn = ''
if (field.isReadOnly) {
classReturn += ' cell-no-edit '
}
if (field.componentPath === 'FieldNumber') {
classReturn += ' cell-align-right '
}
// return 'cell-edit'
return classReturn
},
/**
* Select or unselect rows
* USE ONLY MOUNTED
*/
toggleSelection(rows) {
if (rows) {
rows.forEach(row => {
this.$refs.multipleTable.toggleRowSelection(row)
})
} else {
this.$refs.multipleTable.clearSelection()
}
},
confirmEdit(row) {
const fieldsEmpty = this.$store.getters.getFieldsListEmptyMandatory({
containerUuid: this.containerUuid,
row
})
if (row.isNew) {
row.isEdit = true
this.$message({
message: this.$t('notifications.mandatoryFieldMissing') + fieldsEmpty,
type: 'info'
})
return
}
if (row.isEdit && fieldsEmpty) {
row.isEdit = false
this.$message({
message: this.$t('notifications.mandatoryFieldMissing') + fieldsEmpty,
type: 'info'
})
return
}
row.isEdit = false
},
handleRowClick(row, column, event) {
this.currentTable = this.recordsData.findIndex(item => item.UUID === row.UUID)
if (this.isShowedPanelRecord && this.isParent) {
if (this.uuidCurrentRecordSelected !== row.UUID) {
this.uuidCurrentRecordSelected = row.UUID
// disabled rollback when change route
this.$store.dispatch('setDataLog', {})
}
const tableName = this.panelMetadata.tableName
this.$router.push({
name: this.$route.name,
query: {
...this.$route.query,
action: row.UUID
},
params: {
...this.$router.params,
tableName,
recordId: row[`${tableName}_ID`]
}
}, () => {})
this.$store.commit('setCurrentRecord', row)
} else {
if (!row.isEdit) {
row.isEdit = true
/*
const inSelection = this.getDataSelection.some(item => {
return JSON.stringify(item) === JSON.stringify(row)
})
if (inSelection) {
row.isEdit = true
}
*/
}
}
},
handleRowDblClick(row, column, event) {
if (!this.isShowedPanelRecord) {
this.confirmEdit(row)
}
},
handleSelection(rowsSelection, rowSelected) {
this.$store.dispatch('setSelection', {
containerUuid: this.containerUuid,
selection: rowsSelection
})
},
handleSelectionAll(rowsSelection) {
this.$store.dispatch('setSelection', {
containerUuid: this.containerUuid,
selection: rowsSelection
})
},
filterResult() {
const data = this.recordsData.filter(rowItem => {
if (this.searchTable.trim().length) {
let find = false
Object.keys(rowItem).forEach(key => {
if (String(rowItem[key]).toLowerCase().includes(String(this.searchTable).toLowerCase())) {
find = true
return find
}
})
return find
}
return true
})
return data
},
/**
* Verify is displayed field in column table
*/
isDisplayed(field) {
const isDisplayed = field.isDisplayed &&
field.isDisplayedFromLogic &&
field.isShowedTableFromUser &&
!field.isKey
// Verify for displayed and is active
return field.isActive && isDisplayed
},
/**
* Get the tab object with all its attributes as well as the fields it contains
*/
getPanel() {
// get panel from server only window and tab children
if (this.isPanelWindow && !this.isParent && !this.panelMetadata) {
this.$store.dispatch('getPanelAndFields', {
containerUuid: this.containerUuid,
parentUuid: this.parentUuid,
panelType: this.panelType
}).catch(error => {
console.warn(`Fields List Load Error ${error.code}: ${error.message}.`)
})
}
},
/**
* @param {array} columns
* @param {array} data
*/
getSummaries({ columns, data }) {
const sums = []
if (!this.isShowedTotals) {
return
}
const fieldsList = this.fieldsList
columns.forEach((columnItem, index) => {
if (index === 0) {
sums[index] = 'Σ'
return
}
const field = fieldsList.find(fieldItem => fieldItem.columnName === columnItem.property)
const { displayType } = field
if (!FIELDS_QUANTITY.includes(displayType)) {
sums[index] = ''
return
}
const values = this.getDataSelection.map(item => Number(item[columnItem.property]))
if (values.every(value => isNaN(value))) {
sums[index] = 0
} else {
const total = values.reduce((prev, curr) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + curr
}
return prev
}, 0)
sums[index] = this.formatNumber({
displayType,
number: total
})
}
})
return sums
},
formatNumber({ displayType, number }) {
let fixed = 0
// Amount, Costs+Prices, Number
if (FIELDS_DECIMALS.includes(displayType)) {
fixed = 2
}
return new Intl.NumberFormat().format(number.toFixed(fixed))
},
handleChangePage(newPage) {
this.$store.dispatch('setPageNumber', {
parentUuid: this.parentUuid,
containerUuid: this.containerUuid,
pageNumber: newPage,
panelType: this.panelType
})
},
click() {
this.showTableSearch = !this.showTableSearch
if (this.showTableSearch) {
this.$refs.headerSearchInput && this.$refs.headerSearchInput.focus()
}
},
getFieldDefinition(fieldDefinition, row) {
let styleSheet = ''
if (fieldDefinition && (!this.isEmptyValue(fieldDefinition.id) || fieldDefinition.conditions.length)) {
fieldDefinition.conditions.forEach(condition => {
const columns = evaluator.parseDepends(condition.condition)
let conditionLogic = condition.condition
columns.forEach(column => {
conditionLogic = conditionLogic.replace(/@/g, '')
conditionLogic = conditionLogic.replace(column, row[column])
conditionLogic = evaluator.evaluateLogic({
logic: conditionLogic
})
})
if (conditionLogic && condition.isActive) {
styleSheet = condition.styleSheet
}
})
}
return styleSheet
}
}
}

View File

@ -0,0 +1,132 @@
// used in cell type number
td.cell-align-right, .cell-align-right {
text-align: right !important;
}
// options to record with contextual menu
.contextual-menu {
margin: 0;
background: #fff;
z-index: 3000;
position: absolute;
list-style-type: none;
padding: 5px 0;
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
li {
margin: 0;
padding: 7px 16px;
cursor: pointer;
&:hover {
background: #eee;
}
}
}
.el-submenu__title {
border-bottom: 0px !important;
color: #303133;
}
// .el-menu.el-menu--horizontal {
// border-bottom: solid 0px transparent !important;
// }
// Quick options menu (more options in line and center)
ul.menu-table > .el-submenu {
height: 39px !important;
line-height: 39px !important;
padding: 0px;
border: 0px;
}
ul.menu-table > .el-submenu > .el-submenu__title {
line-height: 39px !important;
height: 39px !important;
padding: 0px;
border: 0px;
}
.panel-expand {
float: right;
padding-right: 40px;
display: flex;
line-height: 39px !important;
}
// .el-collapse {
// border-top: 1px solid #e6ebf5;
// border-bottom: 1px solid #e6ebf5;
// overflow: hidden;
// width: 100%;
// }
// if advanced query is open, show in vertical
.collapse_item_wrap {
min-height: 180px;
max-height: 180px;
will-change: height;
background-color: #fff;
overflow: auto;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border-bottom: 1px solid #e6ebf5;
}
.el-table .warning-row {
background: rgba(161, 250, 223, 0.945);
}
.el-table .success-row {
background: #f0f9eb;
}
.el-table > .cell, .el-table .cell {
-webkit-box-sizing: border-box;
box-sizing: border-box;
overflow: hidden;
max-height: 41px;
text-overflow: ellipsis;
white-space: nowrap;
word-break: break-all;
line-height: 23px;
padding-left: 10px;
padding-right: 10px;
}
.el-table .cell {
max-height: 50px;
}
// .tr.current-row > td {
// background-color: initial !important;
// /* background-color: #e8f4ff; */
// }
// .hover-row > tr {
// background-color: initial !important;
// }
// .hover-row > td {
// background-color: initial !important;
// }
// .header-table-records {
// height: 45px !important;
// padding: 0 !important;
// }
// .icon-mobile {
// padding-right: 5%;
// }
.el-table th, .el-table td {
padding: 12px 0;
min-width: 0;
height: 64px;
max-height: 407px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
text-overflow: ellipsis;
vertical-align: middle;
position: relative;
text-align: left;
}

View File

@ -0,0 +1,104 @@
// style in cursor if cell is no edit
.cell-no-edit {
cursor: not-allowed !important;
}
.cell-edit {
cursor: pointer !important;
}
.field-optional {
margin: 3px 10px;
float: right;
}
// Local search input
.local-search-container {
font-size: 0 !important;
float: right;
color: #5a5e66;
height: 39px !important;
line-height: 39px !important;
.search-icon {
cursor: pointer;
font-size: 18px;
vertical-align: middle;
}
.header-search-input {
transition: width 0.2s;
width: 0 !important;
overflow: hidden;
background: transparent;
border-radius: 0;
display: inline-block;
vertical-align: middle;
::v-deep .el-input__inner {
border-radius: 0;
border: 0;
padding-left: 0;
padding-right: 0;
box-shadow: none !important;
border-bottom: 1px solid #d9d9d9;
vertical-align: middle;
}
}
&.show {
.header-search-input {
width: 190px !important;
margin-left: 10px;
}
}
&.mobile {
.header-search-input {
width: 120px !important;
margin-left: 5px;
}
}
}
.table-root {
padding-right: 0px;
.table-footer {
bottom: 0px;
float: right;
text-align: right;
padding: 10px;
}
}
// Quick options menu (new record, fixed columns...)
.menu-table {
width: 75px;
float: right;
height: 39px !important;
}
.menu-table-mobile {
height: 39px !important;
width: 35px;
float: right;
}
.el-table__header-wrapper {
/* totals or summary row */
.el-table__footer-wrapper {
overflow: auto;
/* background: black; */
}
}
// with mouse change color to current row
.el-table-row {
.hover-row {
background-color: black;
}
.current-row {
.hover-row {
background-color: initial !important;
}
}
}

View File

@ -1,6 +1,6 @@
<template>
<el-select
v-model="getterFieldListShowed"
v-model="fieldsListShowed"
:filterable="!isMobile"
:placeholder="$t('components.filterableItems')"
multiple
@ -10,7 +10,7 @@
class="select"
>
<el-option
v-for="(item, key) in getterFieldListAvailable"
v-for="(item, key) in fieldsListAvailable"
:key="key"
:label="item.name"
:value="item.columnName"
@ -31,20 +31,20 @@ export default {
isMobile() {
return this.$store.state.app.device === 'mobile'
},
getterFieldList() {
fieldsList() {
return this.$store.getters.getFieldsListFromPanel(this.containerUuid)
},
// available fields
getterFieldListAvailable() {
return this.getterFieldList.filter(fieldItem => {
fieldsListAvailable() {
return this.fieldsList.filter(fieldItem => {
const isDisplayed = fieldItem.isDisplayed || fieldItem.isDisplayedFromLogic
return fieldItem.isActive && isDisplayed && !fieldItem.isKey
})
},
getterFieldListShowed: {
get: function() {
fieldsListShowed: {
get() {
// columns showed
return this.getterFieldList.filter(itemField => {
return this.fieldsList.filter(itemField => {
if (itemField.isShowedTableFromUser && (itemField.isDisplayed || itemField.isDisplayedFromLogic) && !itemField.isKey) {
return true
}
@ -52,7 +52,7 @@ export default {
return itemField.columnName
})
},
set: function(selecteds) {
set(selecteds) {
// set columns to show/hidden in vuex store
this.addField(selecteds)
}

View File

@ -37,7 +37,7 @@ export default {
isMobile() {
return this.$store.state.app.device === 'mobile'
},
getterFieldList() {
fieldsList() {
return this.$store.getters.getFieldsListFromPanel(this.containerUuid)
}
},
@ -46,18 +46,18 @@ export default {
},
methods: {
getPanel() {
var fieldList = this.getterFieldList
if (fieldList && fieldList.length) {
this.generatePanel(fieldList)
const fieldsList = this.fieldsList
if (!this.isEmptyValue(fieldsList)) {
this.generatePanel(fieldsList)
}
},
generatePanel(fieldList) {
this.columnListAvailable = fieldList.filter(fieldItem => {
generatePanel(fieldsList) {
this.columnListAvailable = fieldsList.filter(fieldItem => {
return this.isDisplayed(fieldItem)
})
},
isDisplayed(field) {
var isDisplayed = field.isActive && field.isDisplayed && field.isDisplayedFromLogic && !field.isKey
const isDisplayed = field.isActive && field.isDisplayed && field.isDisplayedFromLogic && !field.isKey
if (field.isFixedTableColumn && field.isDisplayed) {
this.columnsFixed.push(field.columnName)
}

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@
</el-menu-item>
<el-menu-item
v-if="isPanelWindow"
:disabled="Boolean(getDataSelection.length < 1 || (isReadOnlyParent && !isParent))"
:disabled="isEmptyValue(getDataSelection) || (isReadOnlyParent && !isParent)"
@click="deleteSelection()"
>
{{ $t('table.dataTable.deleteSelection') }}
@ -26,9 +26,9 @@
<el-menu-item
v-for="(process, key) in processMenu"
:key="key"
:disabled="process.type === 'application' ? false : Boolean(getDataSelection.length < 1)"
:disabled="process.type === 'application' ? false : isEmptyValue(getDataSelection)"
:index="'process' + key"
@click="process.type === 'application' ? sortTab(process) : showModalTable(process)"
@click="showModalTable(process)"
>
{{ process.name }}
</el-menu-item>
@ -39,7 +39,7 @@
{{ $t('table.dataTable.exportZip') }}
</el-menu-item>
<el-submenu
:disabled="Boolean(getDataSelection.length < 1)"
:disabled="isEmptyValue(getDataSelection)"
index="xlsx"
@click.native="exporRecordTable(defaultFromatExport)"
>
@ -72,7 +72,7 @@
</el-menu-item>
<el-menu-item
v-if="!isPanelWindow"
:disabled="Boolean(getDataSelection.length < 1)"
:disabled="isEmptyValue(getDataSelection)"
index="zoom-record"
@click="zoomRecord()"
>

View File

@ -1,42 +1,22 @@
import { supportedTypes, exportFileFromJson, exportFileZip } from '@/utils/ADempiere/exportUtil.js'
import { recursiveTreeSearch } from '@/utils/ADempiere/valueUtils.js'
import { FIELDS_QUANTITY } from '@/utils/ADempiere/references'
import TableMixin from '@/components/ADempiere/DataTable/mixin/tableMixin.js'
export default {
name: 'MixinMenuTable',
mixins: [
TableMixin
],
props: {
parentUuid: {
type: String,
default: undefined
},
containerUuid: {
type: String,
required: true
},
panelType: {
type: String,
default: 'window'
},
currentRow: {
type: Object,
default: () => {}
},
isParent: {
type: Boolean,
default: false
},
processMenu: {
type: Array,
default: () => []
},
isPanelWindow: {
type: Boolean,
default: false
},
isMobile: {
type: Boolean,
default: false
},
panelMetadata: {
type: Object,
default: () => {}
@ -60,73 +40,23 @@ export default {
}
return 'menu-table'
},
getterDataRecordsAndSelection() {
return this.$store.getters.getDataRecordAndSelection(this.containerUuid)
},
getterNewRecords() {
if (this.isPanelWindow && !this.isParent) {
const newRecordTable = this.getterDataRecordsAndSelection.record.filter(recordItem => {
return recordItem.isNew
})
return newRecordTable.length
}
return 0
},
getDataSelection() {
return this.getterDataRecordsAndSelection.selection
},
getDataAllRecord() {
return this.getterDataRecordsAndSelection.record
},
fieldsList() {
if (this.panelMetadata && this.panelMetadata.fieldList) {
return this.panelMetadata.fieldList
if (this.panelMetadata && this.panelMetadata.fieldsList) {
return this.panelMetadata.fieldsList
}
return []
},
isReadOnlyParent() {
if (this.isPanelWindow) {
if (!this.$store.getters.getContainerIsActive(this.parentUuid)) {
return true
}
if (this.$store.getters.getContainerProcessing(this.parentUuid)) {
return true
}
if (this.$store.getters.getContainerProcessed(this.parentUuid)) {
return true
}
}
return false
},
isDisabledAddNew() {
if (this.isParent) {
return true
}
if (this.$route.query.action === 'create-new') {
return true
}
if (!this.panelMetadata.isInsertRecord) {
return true
}
if (this.isReadOnlyParent) {
return true
}
if (this.getterNewRecords) {
return true
}
return false
},
isFieldsQuantity() {
const fieldsQuantity = this.getterFieldList.filter(fieldItem => {
const fieldsQuantity = this.getterFieldsList.filter(fieldItem => {
return FIELDS_QUANTITY.includes(fieldItem.displayType)
}).length
return !fieldsQuantity
},
getterFieldList() {
getterFieldsList() {
return this.$store.getters.getFieldsListFromPanel(this.containerUuid)
},
getterFieldListHeader() {
const header = this.getterFieldList.filter(fieldItem => {
getterFieldsListHeader() {
const header = this.getterFieldsList.filter(fieldItem => {
const isDisplayed = fieldItem.isDisplayed || fieldItem.isDisplayedFromLogic
if (fieldItem.isActive && isDisplayed && !fieldItem.isKey) {
return fieldItem.name
@ -136,8 +66,8 @@ export default {
return fieldItem.name
})
},
getterFieldListValue() {
const value = this.getterFieldList.filter(fieldItem => {
getterFieldsListValue() {
const value = this.getterFieldsList.filter(fieldItem => {
const isDisplayed = fieldItem.isDisplayed || fieldItem.isDisplayedFromLogic
if (fieldItem.isActive && isDisplayed && !fieldItem.isKey) {
return fieldItem
@ -163,16 +93,12 @@ export default {
parentRecordUuid: this.$route.query.action
})
},
closeMenu() {
// TODO: Validate to dispatch one action
this.$store.dispatch('showMenuTable', {
isShowedTable: false
})
this.$store.dispatch('showMenuTabChildren', {
isShowedTabChildren: false
})
},
showModalTable(process) {
if (process.type === 'application') {
this.sortTab(process)
return
}
const processData = this.$store.getters.getProcess(process.uuid)
if (!this.currentRow) {
this.$store.dispatch('setProcessSelect', {
@ -231,30 +157,18 @@ export default {
containerUuid: this.containerUuid
})
},
deleteSelection() {
this.$store.dispatch('deleteSelectionDataList', {
parentUuid: this.parentUuid,
containerUuid: this.containerUuid
}).then(() => {
this.$store.dispatch('setRecordSelection', {
parentUuid: this.parentUuid,
containerUuid: this.containerUuid,
panelType: this.panelType
})
})
},
addNewRow() {
if (this.getterNewRecords <= 0) {
if (this.newRecordsQuantity <= 0) {
this.$store.dispatch('addNewRow', {
parentUuid: this.parentUuid,
containerUuid: this.containerUuid,
fieldList: this.fieldsList,
fieldsList: this.fieldsList,
isEdit: true,
isSendServer: false
})
return
}
const fieldsEmpty = this.$store.getters.getFieldListEmptyMandatory({
const fieldsEmpty = this.$store.getters.getFieldsListEmptyMandatory({
containerUuid: this.containerUuid
})
this.$message({
@ -273,11 +187,14 @@ export default {
* @param {string} formatToExport
*/
exporRecordTable(formatToExport) {
const header = this.getterFieldListHeader
const filterVal = this.getterFieldListValue
let list = this.getDataSelection
const header = this.getterFieldsListHeader
const filterVal = this.getterFieldsListValue
let list = []
if (this.menuType === 'tableContextMenu') {
list = [this.currentRow]
} else {
list = this.getDataSelection
}
const data = this.formatJson(filterVal, list)
@ -290,11 +207,11 @@ export default {
this.closeMenu()
},
exporZipRecordTable() {
const header = this.getterFieldListHeader
const filterVal = this.getterFieldListValue
const header = this.getterFieldsListHeader
const filterVal = this.getterFieldsListValue
let list = this.getDataSelection
if (this.getDataSelection.length <= 0) {
list = this.getDataAllRecord
list = this.recordsData
}
const data = this.formatJson(filterVal, list)
exportFileZip({
@ -313,7 +230,7 @@ export default {
},
zoomRecord() {
const browserMetadata = this.$store.getters.getBrowser(this.$route.meta.uuid)
const { elementName } = browserMetadata.fieldList.find(field => field.columnName === browserMetadata.keyColumn)
const { elementName } = browserMetadata.fieldsList.find(field => field.columnName === browserMetadata.keyColumn)
const records = []
this.getDataSelection.forEach(recordItem => {
let record = recordItem[browserMetadata.keyColumn]
@ -337,9 +254,7 @@ export default {
action: 'advancedQuery',
[elementName]: records
}
}).catch(error => {
console.info(`Table Menu Mixin: ${error.name}, ${error.message}`)
})
}, () => {})
}
}
}

View File

@ -4,7 +4,7 @@
>
<el-submenu
index="xlsx"
@click.native="exportRecord(defaultFromatExport)"
@click.native="exporRecordTable(defaultFromatExport)"
>
<template slot="title">
{{ $t('data.exportRecord') }}
@ -13,7 +13,7 @@
v-for="(format, keyFormat) in supportedTypes"
:key="keyFormat"
:index="keyFormat"
@click.native="exportRecord(keyFormat)"
@click.native="exporRecordTable(keyFormat)"
>
{{ format }}
</el-menu-item>

View File

@ -0,0 +1,40 @@
import Sortable from 'sortablejs'
export default {
name: 'MixinTableSort',
data() {
return {
sortable: null
}
},
methods: {
async getList() {
this.oldgetDataDetail = this.recordsData.map(v => v.id)
this.newgetDataDetail = this.oldgetDataDetail.slice()
this.$nextTick(() => {
this.setSort()
})
},
setSort() {
if (!this.isMobile) {
const el = this.$refs.multipleTable.$el.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
this.sortable = Sortable.create(el, {
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
setData: dataTransfer => {
// to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
dataTransfer.setData('Text', '')
},
onEnd: evt => {
const targetRow = this.recordsData.splice(evt.oldIndex, 1)[0]
this.recordsData.splice(evt.newIndex, 0, targetRow)
// for show the changes, you can delete in you code
const tempIndex = this.newgetDataDetail.splice(evt.oldIndex, 1)[0]
this.newgetDataDetail.splice(evt.newIndex, 0, tempIndex)
}
})
}
}
}
}

View File

@ -0,0 +1,109 @@
export default {
name: 'TableMixin',
props: {
parentUuid: {
type: String,
default: undefined
},
containerUuid: {
type: String,
required: true
},
panelType: {
type: String,
default: 'window'
},
// is used in root view with primary records
isParent: {
type: Boolean,
default: false
}
},
computed: {
isMobile() {
return this.$store.state.app.device === 'mobile'
},
isCreateNewRoute() {
return this.$route.query.action === 'create-new'
},
isPanelWindow() {
return Boolean(this.panelType === 'window')
},
isReadOnlyParent() {
if (this.isPanelWindow) {
if (!this.$store.getters.getContainerIsActive(this.parentUuid)) {
return true
}
if (this.$store.getters.getContainerProcessing(this.parentUuid)) {
return true
}
if (this.$store.getters.getContainerProcessed(this.parentUuid)) {
return true
}
}
return false
},
isDisabledAddNew() {
if (this.isParent) {
return true
}
if (this.isCreateNewRoute) {
return true
}
const panelMetadata = this.panelMetadata
if (panelMetadata && !panelMetadata.isInsertRecord) {
return true
}
if (this.isReadOnlyParent) {
return true
}
if (this.getterNewRecords) {
return true
}
return false
},
// records, selection, record count
getterDataRecordsAndSelection() {
return this.$store.getters.getDataRecordAndSelection(this.containerUuid)
},
recordsData() {
return this.getterDataRecordsAndSelection.record
},
getDataSelection() {
return this.getterDataRecordsAndSelection.selection
},
newRecordsQuantity() {
if (this.isPanelWindow && !this.isParent) {
const newRecordTable = this.getterDataRecordsAndSelection.record.filter(recordItem => {
return recordItem.isNew
})
return newRecordTable.length
}
return 0
}
},
methods: {
closeMenu() {
// TODO: Validate to dispatch one action
this.$store.dispatch('showMenuTable', {
isShowedTable: false
})
this.$store.dispatch('showMenuTabChildren', {
isShowedTabChildren: false
})
},
deleteSelection() {
this.$store.dispatch('deleteSelectionDataList', {
parentUuid: this.parentUuid,
containerUuid: this.containerUuid
})
.then(() => {
this.$store.dispatch('setRecordSelection', {
parentUuid: this.parentUuid,
containerUuid: this.containerUuid,
panelType: this.panelType
})
})
}
}
}

View File

@ -140,9 +140,7 @@ export default {
...this.$route.query,
action: this.windowRecordSelected.UUID
}
}).catch(error => {
console.info(`${this.name} Component: ${error.name}, ${error.message}`)
})
}, () => {})
this.closeDialog()
} else if (action !== undefined) {
const fieldNotReady = this.$store.getters.isNotReadyForSubmit(action.uuid)

View File

@ -92,9 +92,7 @@ export default {
...this.$router.params,
childs: item.children
}
}).catch(error => {
console.info(`${this.name} Component: ${error.name}, ${error.message}`)
})
}, () => {})
}
}
}

View File

@ -48,9 +48,6 @@ export default {
isPanelWindow() {
return this.metadata.panelType === 'window'
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
isSelectMultiple() {
return ['IN', 'NOT_IN'].includes(this.metadata.operator) && this.metadata.isAdvancedQuery
},

View File

@ -123,8 +123,8 @@ export default {
*/
formatView() {
let format = ''
if (!this.isEmptyValue(this.metadata.VFormat)) {
format = this.metadata.VFormat
if (!this.isEmptyValue(this.metadata.vFormat)) {
format = this.metadata.vFormat
.replace(/[Y]/gi, 'y')
.replace(/[m]/gi, 'M')
.replace(/[D]/gi, 'd')
@ -153,10 +153,22 @@ export default {
},
value: {
get() {
const { columnName, containerUuid } = this.metadata
// table records values
if (this.metadata.inTable) {
const row = this.$store.getters.getRowData({
containerUuid,
index: this.metadata.tableIndex
})
return row[columnName]
}
// main panel values
let value = this.$store.getters.getValueOfField({
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
columnName: this.metadata.columnName
containerUuid,
columnName
})
if (!this.metadata.isRange) {
return this.parseValue(value)
@ -164,7 +176,7 @@ export default {
const valueTo = this.$store.getters.getValueOfField({
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
containerUuid,
columnName: this.metadata.columnNameTo
})

View File

@ -12,6 +12,7 @@ export default [
...fieldBase,
columnName: 'C_Location_ID',
overwriteDefinition: {
isCustomField: true,
size: 24,
isDisplayed: false,
index: 1
@ -21,6 +22,7 @@ export default [
...fieldBase,
columnName: 'C_Country_ID',
overwriteDefinition: {
isCustomField: true,
isActiveLogics: true, // enable logics
defaultValue: '@#C_Country_ID@',
size: 24,
@ -32,6 +34,7 @@ export default [
...fieldBase,
columnName: 'C_Region_ID',
overwriteDefinition: {
isCustomField: true,
size: 24,
sequenceFields: 'R',
index: 3
@ -41,6 +44,7 @@ export default [
...fieldBase,
columnName: 'C_City_ID',
overwriteDefinition: {
isCustomField: true,
size: 24,
sequenceFields: 'C',
index: 4
@ -50,6 +54,7 @@ export default [
...fieldBase,
columnName: 'Address1',
overwriteDefinition: {
isCustomField: true,
size: 24,
sequenceFields: 'A1',
index: 5
@ -59,6 +64,7 @@ export default [
...fieldBase,
columnName: 'Address2',
overwriteDefinition: {
isCustomField: true,
size: 24,
sequenceFields: 'A2',
index: 6
@ -68,6 +74,7 @@ export default [
...fieldBase,
columnName: 'Address3',
overwriteDefinition: {
isCustomField: true,
size: 24,
sequenceFields: 'A3',
index: 7
@ -77,6 +84,7 @@ export default [
...fieldBase,
columnName: 'Address4',
overwriteDefinition: {
isCustomField: true,
size: 24,
sequenceFields: 'A4',
index: 8
@ -86,6 +94,7 @@ export default [
...fieldBase,
columnName: 'Postal',
overwriteDefinition: {
isCustomField: true,
size: 24,
sequenceFields: 'P',
index: 9

View File

@ -138,8 +138,8 @@ export default {
if (mutation.payload.columnName === 'C_Country_ID') {
const values = []
// Get country definition to sequence fields and displayed value
if (mutation.value !== this.currentCountryDefinition.id) {
this.getCountryDefinition({
if (mutation.value !== this.currentCountryDefinition.countryId) {
this.requestGetCountryDefinition({
id: mutation.payload.value
})
.then(responseCountry => {
@ -209,14 +209,35 @@ export default {
value: this.getDisplayedValue(values)
})
// active update record to server
this.$store.dispatch('notifyFieldChange', {
this.$store.commit('updateValueOfField', {
parentUuid,
containerUuid,
field: this.parentMetadata
columnName: 'Postal',
value: values.Postal
})
this.$store.commit('updateValueOfField', {
parentUuid,
containerUuid,
columnName: 'C_Country_ID',
value: values.C_Country_ID
})
this.$store.commit('updateValueOfField', {
parentUuid,
containerUuid,
columnName: 'C_City_ID',
value: values.C_City_ID
})
// active update record to server
// this.$store.dispatch('notifyFieldChange', {
// containerUuid,
// field: this.parentMetadata
// })
},
sendValuesToServer() {
const fieldsNotReady = this.$store.getters.getFieldListEmptyMandatory({
const fieldsNotReady = this.$store.getters.getFieldsListEmptyMandatory({
containerUuid: this.containerUuid
})
if (!this.isEmptyValue(fieldsNotReady)) {
@ -247,15 +268,13 @@ export default {
})
const updateLocation = (responseLocation) => {
const { values } = responseLocation
// set form values
this.setValues({
values
values: responseLocation.attributes
})
// set field parent values
this.setParentValues(values)
this.setParentValues(responseLocation.attributes)
this.setShowedLocationForm(false)
// set context values to parent continer
@ -270,7 +289,7 @@ export default {
if (this.isEmptyValue(locationId) || locationId === 0) {
requestCreateLocationAddress({
attributes: attributesToServer
attributesList: attributesToServer
})
.then(updateLocation)
.catch(error => {
@ -286,7 +305,7 @@ export default {
}
requestUpdateLocationAddress({
id: locationId,
attributes: attributesToServer
attributesList: attributesToServer
})
.then(updateLocation)
.catch(error => {

View File

@ -1,11 +1,11 @@
import { getCountryDefinition } from '@/api/ADempiere/system-core.js'
import { requestGetCountryDefinition } 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']
return this.$store.getters.getCountry
},
isShowedLocationForm: {
get() {
@ -18,7 +18,7 @@ export default {
},
methods: {
requestGetLocationAddress,
getCountryDefinition,
requestGetCountryDefinition,
toggleShowedLocationForm() {
this.$store.commit('setShowedLocation', !this.isShowedLocationForm)
},

View File

@ -18,11 +18,13 @@
<script>
import fieldMixin from '@/components/ADempiere/Field/mixin/mixinField.js'
import { getLocatorList } from '@/api/ADempiere/field/locator'
import { requestLocatorList } from '@/api/ADempiere/field/locator.js'
export default {
name: 'FieldLocation',
mixins: [fieldMixin],
mixins: [
fieldMixin
],
data() {
return {
options: [],
@ -58,16 +60,18 @@ export default {
if (Array.isArray(value)) {
selected = value[value.length - 1]
}
this.handleFieldChange({ value: selected })
this.handleFieldChange({
value: selected
})
this.value = value
},
searchLocatorByWarehouse(node, resolve) {
getLocatorList({
requestLocatorList({
warehouseId: node.value
})
.then(responseData => {
const locatorList = responseData.recordsList.map(item => {
const { values } = item
const { attributes: values } = item
return {
label: values.Value,
value: values.M_Locator_ID,

View File

@ -82,7 +82,7 @@ export default {
precision() {
// Amount, Costs+Prices, Number
if (this.isDecimal) {
return this.currencyDefinition.stdPrecision
return this.currencyDefinition.standardPrecision
}
return undefined
},
@ -140,13 +140,13 @@ export default {
return formatterInstance.format(value)
},
countryLanguage() {
return this.$store.getters['user/getCountryLanguage']
return this.$store.getters.getCountryLanguage
},
currencyCode() {
return this.currencyDefinition.iSOCode
},
currencyDefinition() {
return this.$store.getters['user/getCurrency']
return this.$store.getters.getCurrency
}
},
methods: {

View File

@ -62,9 +62,6 @@ export default {
isPanelWindow() {
return this.metadata.panelType === 'window'
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
isSelectMultiple() {
return ['IN', 'NOT_IN'].includes(this.metadata.operator) && this.metadata.isAdvancedQuery
},
@ -110,11 +107,23 @@ export default {
},
value: {
get() {
const value = this.$store.getters.getValueOfField({
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
columnName: this.metadata.columnName
})
const { columnName, containerUuid } = this.metadata
let value
// table records values
if (this.metadata.inTable) {
const row = this.$store.getters.getRowData({
containerUuid,
index: this.metadata.tableIndex
})
value = row[columnName]
} else {
value = this.$store.getters.getValueOfField({
parentUuid: this.metadata.parentUuid,
containerUuid,
columnName
})
}
if (this.isEmptyValue(value)) {
/* eslint-disable */
this.displayedValue = undefined
@ -152,6 +161,9 @@ export default {
},
uuidValue: {
get() {
if (this.metadata.inTable) {
return undefined
}
return this.$store.getters.getValueOfField({
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
@ -160,6 +172,9 @@ export default {
})
},
set(value) {
if (this.metadata.inTable) {
return undefined
}
this.$store.commit('updateValueOfField', {
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
@ -171,11 +186,21 @@ export default {
},
displayedValue: {
get() {
const { displayColumnName: columnName, containerUuid } = this.metadata
// table records values
if (this.metadata.inTable) {
const row = this.$store.getters.getRowData({
containerUuid,
index: this.metadata.tableIndex
})
// DisplayColumn_'ColumnName'
return row[columnName]
}
return this.$store.getters.getValueOfField({
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
containerUuid,
// DisplayColumn_'ColumnName'
columnName: this.metadata.displayColumnName
columnName
})
},
set(value) {
@ -240,8 +265,8 @@ export default {
})
} else {
if (!this.isPanelWindow || (this.isPanelWindow &&
(this.isEmptyValue(this.metadata.optionCRUD) ||
this.metadata.optionCRUD === 'create-new'))) {
!this.isEmptyValue(this.$route.query) &&
this.$route.query.action === 'create-new')) {
this.getDataLookupItem()
}
}
@ -259,11 +284,11 @@ export default {
},
changeBlankOption() {
if (Number(this.metadata.defaultValue) === -1) {
this.blankOption.id = -1
this.blankOption.id = this.metadata.defaultValue
}
},
preHandleChange(value) {
const label = this.findLabel(this.value)
const { label } = this.findOption(value)
this.displayedValue = label
this.handleFieldChange({
value,
@ -281,17 +306,6 @@ export default {
uuid: undefined
}
},
/**
* TODO: Verify used
* @deprecated use findOption
*/
findLabel(value) {
const selected = this.optionsList.find(item => item.id === value)
if (selected) {
return selected.label
}
return selected
},
async getDataLookupItem() {
if (this.isEmptyValue(this.metadata.reference.directQuery) ||
(this.metadata.isAdvancedQuery && this.isSelectMultiple)) {
@ -301,6 +315,7 @@ export default {
this.$store.dispatch('getLookupItemFromServer', {
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
columnName: this.metadata.columnName,
tableName: this.metadata.reference.tableName,
directQuery: this.metadata.reference.directQuery,
value: this.value
@ -308,7 +323,9 @@ export default {
.then(responseLookupItem => {
this.displayedValue = responseLookupItem.label
this.uuidValue = responseLookupItem.uuid
this.optionsList = this.getterLookupAll
this.$nextTick(() => {
this.optionsList = this.getterLookupAll
})
})
.finally(() => {
this.isLoading = false
@ -331,8 +348,10 @@ export default {
this.$store.dispatch('getLookupListFromServer', {
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
columnName: this.metadata.columnName,
tableName: this.metadata.reference.tableName,
query: this.metadata.reference.query,
whereClause: this.metadata.validationCode,
// valuesList: this.value
isAddBlankValue: true,
blankValue: this.blankOption.id

View File

@ -24,6 +24,8 @@ import fieldMixin from '@/components/ADempiere/Field/mixin/mixinField.js'
*/
export default {
name: 'FieldSelectMultiple',
mixins: [fieldMixin]
mixins: [
fieldMixin
]
}
</script>

View File

@ -1,164 +0,0 @@
<template>
<div :id="id" :class="classDisable" />
</template>
<script>
// deps for editor
import 'tui-editor/dist/tui-editor.css' // editor ui
import 'tui-editor/dist/tui-editor-contents.css' // editor content
import 'codemirror/lib/codemirror.css' // codemirror
import Editor from 'tui-editor'
import { getLanguage } from '@/lang'
export default {
name: 'ChatTextLong',
props: {
id: {
type: String,
required: false,
default() {
return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0))
}
}
},
data() {
return {
mode: 'markdown', // 'markdown' or 'wysiwyg'
height: '200px',
editor: null
}
},
computed: {
classDisable() {
if (this.isDisabled) {
return 'isdisable'
}
return ''
},
clean() {
return this.$store.getters.getMarkDown
},
language() {
// https://github.com/nhnent/tui.editor/tree/master/src/js/langs
if (this.isEmptyValue(getLanguage())) {
return 'en_US'
}
return getLanguage()
},
editorOptions() {
const options = {
previewStyle: 'vertical',
useCommandShortcut: true,
usageStatistics: false, // send hostname to google analytics
hideModeSwitch: this.isDisabled
}
options.initialEditType = this.mode
options.height = this.height
options.language = this.language
return options
}
},
watch: {
clean(value) {
if (value) {
this.editor.setValue('')
}
},
value(newValue, oldValue) {
if (this.isDisabled) {
// not changed value
this.value = oldValue
this.editor.setValue(oldValue)
} else {
this.editor.setValue(newValue)
}
},
language(langValue) {
this.destroyEditor()
this.initEditor()
},
height(heightValue) {
this.editor.height(heightValue)
},
isDisabled(value) {
this.classDisable
this.destroyEditor()
this.initEditor()
}
},
mounted() {
this.initEditor()
},
destroyed() {
this.destroyEditor()
},
methods: {
initEditor() {
this.editor = new Editor({
el: document.getElementById(this.id),
...this.editorOptions
})
if (!this.isEmptyValue(this.value)) {
this.editor.setValue(this.value)
}
this.setEvents()
},
setEvents() {
if (this.isDisabled) {
this.removeEventSendValues()
this.addReanOnlyChanges()
} else {
this.addEventSendValues()
this.removeReadOnlyChanges()
}
},
addEventSendValues() {
this.editor.on('blur', () => {
this.preHandleChange(this.editor.getValue())
})
},
preHandleChange(value) {
if (this.isEmptyValue(value)) {
this.$store.dispatch('setchatText', value)
.then(responseComment => {
this.$store.dispatch('setMarkDown', false)
this.$store.dispatch('setchatText', '')
})
} else {
this.$store.dispatch('setchatText', value)
}
},
addReanOnlyChanges() {
this.editor.on('change', () => {
this.editor.setValue(this.value)
})
},
removeEventSendValues() {
this.editor.off('blur')
},
removeReadOnlyChanges() {
this.editor.off('change')
},
destroyEditor() {
if (!this.editor) {
return
}
this.removeEventSendValues()
this.removeReadOnlyChanges()
this.editor.remove()
},
setHtml(value) {
this.editor.setHtml(value)
},
getHtml() {
return this.editor.getHtml()
}
}
}
</script>
<style>
.isdisable {
background: #F5F7FA;
}
</style>

View File

@ -3,63 +3,64 @@
this v-show is to indicate that if the field is not shown,
therefore you should not leave the column size spacing of your
<el-col></el-col> container-->
<el-col
v-if="!inTable"
v-show="isDisplayed"
key="is-panel-template"
:xs="sizeFieldResponsive.xs"
:sm="sizeFieldResponsive.sm"
:md="sizeFieldResponsive.md"
:lg="sizeFieldResponsive.lg"
:xl="sizeFieldResponsive.xl"
:class="classField"
>
<el-form-item
:required="isMandatory"
<div v-if="!inTable">
<el-col
v-if="isDisplayed"
key="is-panel-template"
:xs="sizeFieldResponsive.xs"
:sm="sizeFieldResponsive.sm"
:md="sizeFieldResponsive.md"
:lg="sizeFieldResponsive.lg"
:xl="sizeFieldResponsive.xl"
:class="classField"
>
<template slot="label">
<operator-comparison
v-if="field.isComparisonField"
key="is-field-operator-comparison"
:field-attributes="fieldAttributes"
:field-value="field.value"
/>
<context-info
v-else-if="isContextInfo"
key="is-field-context-info"
:field-attributes="fieldAttributes"
:field-value="field.value"
/>
<span v-else key="is-field-name">
{{ isFieldOnly }}
</span>
<el-form-item
:required="isMandatory"
>
<template slot="label">
<operator-comparison
v-if="field.isComparisonField"
key="is-field-operator-comparison"
:field-attributes="fieldAttributes"
:field-value="field.value"
/>
<context-info
v-else-if="isContextInfo"
key="is-field-context-info"
:field-attributes="fieldAttributes"
:field-value="field.value"
/>
<span v-else key="is-field-name">
{{ isFieldOnly }}
</span>
<document-status
v-if="isDocuemntStatus"
:field="fieldAttributes"
/>
<document-status
v-if="isDocuemntStatus"
:field="fieldAttributes"
/>
<translated
v-if="field.isTranslatedField"
:field-attributes="fieldAttributes"
:record-uuid="field.recordUuid"
/>
<translated
v-if="field.isTranslatedField"
:field-attributes="fieldAttributes"
:record-uuid="field.recordUuid"
/>
<calculator
v-if="field.isNumericField && !field.isReadOnlyFromLogic"
:field-attributes="fieldAttributes"
:field-value="field.value"
/>
</template>
<calculator
v-if="field.isNumericField && !field.isReadOnlyFromLogic"
:field-attributes="fieldAttributes"
:field-value="field.value"
/>
</template>
<component
:is="componentRender"
:ref="field.columnName"
:metadata="fieldAttributes"
:value-model="recordDataFields"
/>
</el-form-item>
</el-col>
<component
:is="componentRender"
:ref="field.columnName"
:metadata="fieldAttributes"
:value-model="recordDataFields"
/>
</el-form-item>
</el-col>
</div>
<component
:is="componentRender"
v-else
@ -352,7 +353,7 @@ export default {
return false
}
return Boolean(this.field.contextInfo && this.field.contextInfo.isActive) ||
Boolean(this.field.reference && this.field.reference.windowsList.length)
Boolean(this.field.reference && this.field.reference.zoomWindows.length)
}
},
watch: {

View File

@ -11,19 +11,10 @@ export default {
default: null
}
},
data() {
// value render
let value1 = this.metadata.value
if (this.metadata.inTable) {
value1 = this.valueModel
}
value1 = this.parseValue(value1)
return {
value1
}
},
computed: {
isMobile() {
return this.$store.state.app.device === 'mobile'
},
isDisabled() {
return Boolean(this.metadata.readonly || this.metadata.disabled)
},
@ -36,13 +27,34 @@ export default {
},
value: {
get() {
const { columnName, containerUuid } = this.metadata
// table records values
if (this.metadata.inTable) {
const row = this.$store.getters.getRowData({
containerUuid,
index: this.metadata.tableIndex
})
return row[columnName]
}
// main panel values
return this.$store.getters.getValueOfField({
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
columnName: this.metadata.columnName
containerUuid,
columnName
})
},
set(value) {
if (this.metadata.inTable) {
this.$store.dispatch('notifyCellTableChange', {
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
newValue: value,
field: this.metadata
})
return
}
this.$store.commit('updateValueOfField', {
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
@ -177,6 +189,14 @@ export default {
}
return
}
if (this.metadata.inTable) {
this.$store.dispatch('notifyCellTableChange', {
parentUuid: this.metadata.parentUuid,
containerUuid: this.metadata.containerUuid,
field: this.metadata
})
}
this.$store.dispatch('notifyFieldChange', {
containerUuid: this.metadata.containerUuid,
field: this.metadata

View File

@ -16,7 +16,7 @@
</span>
{{ fieldAttributes.help }}
</div>
<template v-for="(zoomItem, index) in fieldAttributes.reference.windowsList">
<template v-for="(zoomItem, index) in fieldAttributes.reference.zoomWindows">
<el-button
:key="index"
type="text"
@ -81,9 +81,7 @@ export default {
tabParent: 0,
[this.fieldAttributes.columnName]: this.value
}
}).catch(error => {
console.info(`${this.name} Component: ${error.name}, ${error.message}`)
})
}, () => {})
} else {
this.$message({
type: 'error',

View File

@ -55,7 +55,6 @@ export default {
this.$store.dispatch('changeFieldAttribure', {
containerUuid: this.fieldAttributes.containerUuid,
columnName: this.fieldAttributes.columnName,
isAdvancedQuery: true,
attributeName: 'operator',
attributeValue: operatorValue
})

View File

@ -84,7 +84,7 @@ export default {
},
computed: {
languageList() {
return this.$store.getters['user/getLanguagesList'].filter(itemLanguage => {
return this.$store.getters.getLanguagesList.filter(itemLanguage => {
return !itemLanguage.isBaseLanguage
})
},

View File

@ -4,7 +4,7 @@ export default [
elementColumnName: 'ProductValue',
isFromDictionary: true,
overwriteDefinition: {
size: 12,
size: 24,
sequence: 10,
cssClassName: 'price-inquiry',
inputSize: 'large',

View File

@ -7,27 +7,27 @@
<el-container style="height: 100% !important;">
<img
fit="contain"
:src="valuesImage.length > 1 ? valuesImage.value : getDefaultImage()"
:src="backgroundForm"
class="background-price-checking"
>
<el-main>
<div class="inquiry-form">
<el-form
key="form-loaded"
label-position="top"
label-width="10px"
@submit.native.prevent="notSubmitForm"
>
<field
v-for="(field) in fieldsList"
ref="ProductValue"
:key="field.columnName"
:metadata-field="field"
:v-model="field.value"
class="product-value"
/>
</el-form>
</div>
<el-form
key="form-loaded"
class="inquiry-form"
label-position="top"
label-width="10px"
@submit.native.prevent="notSubmitForm"
>
<field
v-for="(field) in fieldsList"
ref="ProductValue"
:key="field.columnName"
:metadata-field="field"
:v-model="field.value"
class="product-value"
/>
</el-form>
<div class="inquiry-product">
<el-row v-if="!isEmptyValue(productPrice)" :gutter="20">
<el-col style="padding-left: 0px; padding-right: 0%;">
@ -70,102 +70,79 @@
element-loading-background="rgba(255, 255, 255, 0.8)"
class="loading-panel"
/>
<!-- </div> -->
</template>
<script>
import formMixin from '@/components/ADempiere/Form/formMixin.js'
import fieldsList from './fieldsList.js'
import { getProductPrice } from '@/api/ADempiere/form/price-checking.js'
import { requestGetProductPrice } from '@/api/ADempiere/form/price-checking.js'
import { formatPercent, formatPrice } from '@/utils/ADempiere/valueFormat.js'
import { getResource } from '@/api/ADempiere/persistence.js'
import { mergeByteArray, buildImageFromArray } from '@/utils/ADempiere/resource.js'
import { buildImageFromArrayBuffer } from '@/utils/ADempiere/resource.js'
import { requestImage } from '@/api/ADempiere/persistence.js'
export default {
name: 'PriceChecking',
mixins: [formMixin],
mixins: [
formMixin
],
data() {
return {
fieldsList,
productPrice: {},
resource: {},
valuesImage: [{
identifier: 'undefined',
value: 'price-checking-background',
isLoaded: true
}],
organizationBackground: '',
currentImageOfProduct: '',
unsubscribe: () => {}
}
},
computed: {
organizationImagePath() {
return this.$store.getters.avatar
},
defaultImage() {
return require('@/image/ADempiere/priceChecking/no-image.jpg')
},
backgroundForm() {
if (this.isEmptyValue(this.currentImageOfProduct)) {
return this.organizationBackground
}
return this.currentImageOfProduct
}
},
created() {
this.unsubscribe = this.subscribeChanges()
},
mounted() {
this.getImage()
},
beforeDestroy() {
this.unsubscribe()
},
methods: {
buildImageFromArray,
getDefaultImage() {
return require('@/image/ADempiere/priceChecking/price-checking-background.png')
},
getImage(resource) {
let isImage
if (resource) {
if (resource.image) {
if (!this.valuesImage.some(item => item.identifier === resource.image)) {
this.valuesImage.push({
identifier: resource.image,
value: '',
isLoaded: false
})
}
if (this.resource[resource.image]) {
this.valuesImage.map(item => {
if (item.identifier === resource.image) {
return {
...item,
value: this.buildImageFromArray(resource, this.resource[resource.image]),
isLoaded: true
}
}
return item
})
} else { // Reload
if (!this.valuesImage.some(item => item.identifier === resource.image)) {
this.valuesImage.push({
identifier: resource.image,
value: '',
isLoaded: false
})
}
let result = new Uint8Array()
const callBack = {}
callBack.onData = (response) => {
result = mergeByteArray(result, response.getData())
}
callBack.onStatus = (status) => {
}
callBack.onEnd = (end) => {
this.resource[resource.image] = result
this.valuesImage.map(item => {
if (item.identifier === resource.image) {
return {
...item,
value: this.buildImageFromArray(resource, this.resource[isImage]),
isLoaded: true
}
}
return item
})
}
getResource({
resourceUuid: '1816f354-a868-4f4f-9a83-b0eb98cf1d05'
},
callBack)
}
async getImage(imageName = '') {
let isSetOrg = false
if (this.isEmptyValue(imageName)) {
if (!this.isEmptyValue(this.organizationBackground)) {
return this.organizationBackground
}
isSetOrg = true
imageName = this.organizationImagePath
}
const imageBuffer = await requestImage({
file: imageName
}).then(responseImage => {
const arrayBufferAsImage = buildImageFromArrayBuffer({
arrayBuffer: responseImage
})
if (isSetOrg) {
this.organizationBackground = arrayBufferAsImage
return arrayBufferAsImage
}
this.currentImageOfProduct = arrayBufferAsImage
return arrayBufferAsImage
})
return imageBuffer
},
focusProductValue() {
this.$refs.ProductValue[0].$children[0].$children[0].$children[1].$children[0].focus()
@ -176,22 +153,23 @@ export default {
return this.$store.subscribe((mutation, state) => {
if (mutation.type === 'addActionKeyPerformed' && mutation.payload.columnName === 'ProductValue') {
// cleans all values except column name 'ProductValue'
getProductPrice({
requestGetProductPrice({
searchValue: mutation.payload.value
})
.then(productPrice => {
const { product, taxRate, priceStd: priceBase } = productPrice
const { product, taxRate, priceStandard: priceBase } = productPrice
const { rate } = taxRate
const { imageURL: image } = product
this.productPrice = {
productName: product.name,
productDescription: product.description,
priceBase,
priceStd: productPrice.priceStd,
priceStandard: productPrice.priceStandard,
priceList: productPrice.priceList,
priceLimit: productPrice.priceLimit,
taxRate: rate,
image: product.imageURL,
image,
taxName: taxRate.name,
taxIndicator: taxRate.taxIndicator,
taxAmt: this.getTaxAmount(priceBase, rate),
@ -202,7 +180,8 @@ export default {
.catch(error => {
this.$message({
type: 'info',
message: error.message
message: error.message,
showClose: true
})
this.productPrice = {}
})
@ -212,7 +191,11 @@ export default {
columnName: 'ProductValue',
value: ''
})
this.getImage(this.productPrice)
this.currentImageOfProduct = ''
if (this.isEmptyValue(this.productPrice.image)) {
this.getImage(this.productPrice.image)
}
})
}
})

View File

@ -0,0 +1,190 @@
<template>
<el-main
v-shortkey="shortsKey"
@shortkey.native="keyAction"
>
<el-form
label-position="top"
size="small"
class="create-bp"
>
<el-row :gutter="24">
<field-definition
v-for="(field) in fieldsList"
:key="field.columnName"
:metadata-field="field"
/>
<el-col :span="24">
<samp style="float: right; padding-right: 10px;">
<el-button
type="primary"
class="custom-button-create-bp"
icon="el-icon-check"
:loading="isLoadingRecord"
@click="createBusinessParter"
/>
<el-button
type="danger"
class="custom-button-create-bp"
icon="el-icon-close"
@click="clearValues"
/>
</samp>
</el-col>
</el-row>
</el-form>
</el-main>
</template>
<script>
import { requestCreateBusinessPartner } from '@/api/ADempiere/system-core.js'
import formMixin from '@/components/ADempiere/Form/formMixin.js'
import fieldsList from './fieldsListCreate.js'
import BParterMixin from './mixinBusinessPartner.js'
export default {
name: 'BusinessPartnerCreate',
mixins: [
formMixin,
BParterMixin
],
props: {
metadata: {
type: Object,
default: () => {
return {
uuid: 'Business-Partner-Create',
containerUuid: 'Business-Partner-Create',
fieldsList
}
}
}
},
data() {
return {
businessPartnerRecord: {},
isLoadingRecord: false,
fieldsList,
isCustomForm: true,
unsubscribe: () => {}
}
},
beforeDestroy() {
this.unsubscribe()
},
methods: {
// TODO: Get locations values.
createBusinessParter() {
let values = this.$store.getters.getValuesView({
containerUuid: this.containerUuid,
format: 'object'
})
if (this.isEmptyValue(values)) {
return
}
values = this.convertValuesToSend(values)
this.isLoadingRecord = true
requestCreateBusinessPartner(values)
.then(responseBPartner => {
// TODO: Add new record into vuex store.
this.setBusinessPartner(responseBPartner)
this.clearValues()
this.$message({
type: 'success',
message: 'Socio de negocio creado exitosamente',
duration: 1500,
showClose: true
})
})
.catch(error => {
this.showsPopovers.isShowCreate = true
this.$message({
type: 'warning',
message: error.message,
duration: 1500,
showClose: true
})
console.warn(`Error create Business Partner. Message: ${error.message}, code ${error.code}.`)
})
.finally(() => {
this.isLoadingRecord = false
})
},
clearValues() {
this.showsPopovers.isShowCreate = false
this.$store.dispatch('setDefaultValues', {
containerUuid: this.containerUuid,
panelType: this.panelType
})
this.clearLocationValues()
},
clearLocationValues() {
this.$store.commit('updateValuesOfContainer', {
containerUuid: this.containerUuid,
attributes: [{
columnName: 'C_Location_ID',
value: undefined
}, {
columnName: 'DisplayColumn_C_Location_ID',
value: undefined
}, {
columnName: 'C_Country_ID',
value: undefined
}, {
columnName: 'C_Country_ID_UUID',
value: undefined
}, {
columnName: 'DisplayColumn_C_Country_ID',
value: undefined
}, {
columnName: 'C_Region_ID',
value: undefined
}, {
columnName: 'C_Region_ID_UUID',
value: undefined
}, {
columnName: 'DisplayColumn_C_Region_ID',
value: undefined
}, {
columnName: 'C_City_ID',
value: undefined
}, {
columnName: 'C_City_ID_UUID',
value: undefined
}, {
columnName: 'DisplayColumn_C_City_ID',
value: undefined
}, {
columnName: 'Address1',
value: undefined
}, {
columnName: 'Address2',
value: undefined
}, {
columnName: 'Address3',
value: undefined
}, {
columnName: 'Address4',
value: undefined
}]
})
}
}
}
</script>
<style scoped lang="scss">
.create-bp {
.el-form-item {
margin-bottom: 0px !important;
}
}
.custom-button-create-bp {
float: right;
margin-right: 10px;
}
</style>

View File

@ -0,0 +1,206 @@
<template>
<el-main
v-shortkey="shortsKey"
@shortkey.native="keyAction"
>
<el-collapse v-model="activeAccordion" accordion>
<el-collapse-item name="query-criteria">
<template slot="title">
Business Partner
<template v-if="!isEmptyValue(parentMetadata.name)">
({{ parentMetadata.name }})
</template>
<!-- <i class="el-icon-circle-plus" /> -->
</template>
<el-form
label-position="top"
size="small"
>
<el-row>
<field-definition
v-for="(field) in fieldsList"
:key="field.columnName"
:metadata-field="field"
/>
</el-row>
</el-form>
</el-collapse-item>
</el-collapse>
<el-table
ref="businesPartnerTable"
:data="businessPartnersList"
highlight-current-row
border
fit
height="350"
@current-change="handleCurrentChange"
>
<el-table-column
label="Key"
prop="value"
width="180"
/>
<el-table-column
label="ID"
prop="id"
width="100"
/>
<el-table-column
prop="name"
label="Name"
/>
<el-table-column
label="Last Name"
prop="lastName"
/>
<el-table-column
label="NAICS"
prop="naics"
width="100"
/>
<el-table-column
label="Tax ID"
prop="taxId"
align="right"
width="100"
/>
</el-table>
<custom-pagination
:total="businessParners.recordCount"
:current-page="1"
:handle-change-page="handleChangePage"
/>
<!-- -->
</el-main>
</template>
<script>
import CustomPagination from '@/components/ADempiere/Pagination'
import formMixin from '@/components/ADempiere/Form/formMixin.js'
import fieldsList from './fieldsListSearch.js'
import BParterMixin from './mixinBusinessPartner.js'
export default {
name: 'BusinessPartnersList',
components: {
CustomPagination
},
mixins: [
formMixin,
BParterMixin
],
props: {
metadata: {
type: Object,
default: () => {
return {
uuid: 'Business-Partner-List',
containerUuid: 'Business-Partner-List'
}
}
},
showsPopovers: {
type: Object,
default: () => {
return {
isShowCreate: false,
isShowList: false
}
}
}
},
data() {
return {
isLoadedRecords: false,
activeAccordion: 'query-criteria',
fieldsList,
unsubscribe: () => {}
}
},
computed: {
businessParners() {
return this.$store.getters.getBusinessPartner
},
businessPartnersList() {
return this.$store.getters.getBusinessPartnersList
},
isReadyFromGetData() {
const { isLoaded, isReload } = this.businessParners
return (!isLoaded || isReload) && this.showsPopovers.isShowList
}
},
watch: {
isReadyFromGetData(isToLoad) {
if (isToLoad) {
this.searchBPartnerList({})
}
}
},
created() {
this.unsubscribe = this.subscribeChanges()
if (this.isReadyFromGetData) {
this.searchBPartnerList({})
}
},
beforeDestroy() {
this.unsubscribe()
},
methods: {
keyAction(event) {
switch (event.srcKey) {
case 'refreshList': {
const values = this.$store.getters.getValuesView({
containerUuid: this.metadata.containerUuid,
format: 'object'
})
this.searchBPartnerList(values)
break
}
case 'refreshListWithoutValues': {
this.searchBPartnerList({})
break
}
case 'closeForm':
this.closeForm()
break
}
},
handleCurrentChange(row) {
this.setBusinessPartner(row)
},
handleChangePage(newPage) {
this.$store.dispatch('setBPartnerPageNumber', newPage)
},
subscribeChanges() {
return this.$store.subscribe((mutation, state) => {
if (mutation.type === 'updateValueOfField' &&
mutation.payload.containerUuid === this.metadata.containerUuid) {
const values = this.$store.getters.getValuesView({
containerUuid: mutation.payload.containerUuid,
format: 'object'
})
this.searchBPartnerList(values)
}
})
},
searchBPartnerList(values, isConvert = true) {
if (isConvert && !this.isEmptyValue(values)) {
values = this.convertValuesToSend(values)
}
return this.$store.dispatch('listBPartnerFromServer', values)
.then(response => {
return response
})
.finally(() => {
this.isLoadedRecords = true
})
}
}
}
</script>

View File

@ -0,0 +1,68 @@
// List of fields to send for create new
export default [
{
columnName: 'Value',
// displayType: CHAR.id,
definition: {
name: 'Search Key',
isCustomField: true,
size: 24
}
},
{
columnName: 'Name',
// displayType: CHAR.id,
definition: {
name: 'Name',
isCustomField: true,
size: 24
}
},
{
columnName: 'Contact',
// displayType: CHAR.id,
definition: {
name: 'Contact Name',
isCustomField: true,
size: 24
}
},
{
columnName: 'EMail',
// displayType: CHAR.id,
definition: {
name: 'E-Mail Address',
isCustomField: true,
size: 24
}
},
{
columnName: 'Postal',
// displayType: CHAR.id,
definition: {
name: 'Postal Code',
isCustomField: true,
size: 24
}
},
{
columnName: 'Phone',
// displayType: CHAR.id,
definition: {
name: 'Phone',
isCustomField: true,
size: 24
}
},
{
columnName: 'C_Location_ID',
tableName: 'C_BPartner_Location',
isFromDictionary: true,
overwriteDefinition: {
size: 24,
handleActionPerformed: false,
isSendParentValues: true,
popoverPlacement: 'top'
}
}
]

View File

@ -0,0 +1,59 @@
// List of fields to send in search
export default [
{
columnName: 'Code',
// displayType: CHAR.id,
definition: {
name: 'Search Value',
isCustomField: true
}
},
{
columnName: 'Value',
// displayType: CHAR.id,
definition: {
name: 'Search Key',
isCustomField: true
}
},
{
columnName: 'Name',
// displayType: CHAR.id,
definition: {
name: 'Name',
isCustomField: true
}
},
{
columnName: 'Contact',
// displayType: CHAR.id,
definition: {
name: 'Contact Name',
isCustomField: true
}
},
{
columnName: 'EMail',
// displayType: CHAR.id,
definition: {
name: 'E-Mail Address',
isCustomField: true
}
},
{
columnName: 'Postal',
// displayType: CHAR.id,
definition: {
name: 'Postal Code',
isCustomField: true
}
},
{
columnName: 'Phone',
// displayType: CHAR.id,
definition: {
name: 'Phone',
isCustomField: true
}
}
]

View File

@ -0,0 +1,386 @@
<template>
<div>
<el-popover
ref="businessPartnerCreate"
v-model="showsPopovers.isShowCreate"
placement="right"
width="400"
trigger="click"
>
<business-partner-create
v-if="showsPopovers.isShowCreate"
:parent-metadata="parentMetadata"
:shows-popovers="showsPopovers"
/>
</el-popover>
<el-popover
ref="businessPartnersList"
v-model="showsPopovers.isShowList"
placement="right"
width="800"
trigger="click"
>
<!-- v-if="showsPopovers.isShowList" -->
<business-partners-list
:parent-metadata="parentMetadata"
:shows-popovers="showsPopovers"
/>
</el-popover>
<el-form-item>
<template slot="label">
Business Partner
<i
v-popover:businessPartnerCreate
class="el-icon-circle-plus"
/>
<i
v-popover:businessPartnersList
class="el-icon-search"
/>
</template>
<el-autocomplete
v-model="displayedValue"
:placeholder="$t('quickAccess.searchWithEnter')"
:fetch-suggestions="localSearch"
clearable
value-key="name"
style="width: 100%;"
popper-class="custom-field-bpartner-info"
@clear="setBusinessPartner(blankBPartner, false)"
@keyup.enter.native="getBPartnerWithEnter"
@select="handleSelect"
@focus="setNewDisplayedValue"
@blur="setOldDisplayedValue"
>
<template
slot="prefix"
>
<i
class="el-icon-user-solid el-input__icon"
/>
</template>
<template slot-scope="props">
<div class="header">
<b>{{ props.item.name }} </b>
</div>
<span class="info">
{{ props.item.value }}
</span>
</template>
</el-autocomplete>
</el-form-item>
</div>
</template>
<script>
/**
* This component is made to be the prototype of the Business Partner search field
* TODO: Before creating you must make a search for all the filled fields.
*/
import { requestGetBusinessPartner } from '@/api/ADempiere/system-core.js'
import BusinessPartnerCreate from './businessPartnerCreate'
import BusinessPartnersList from './businessPartnersList'
import BParterMixin from './mixinBusinessPartner.js'
const { setBusinessPartner } = BParterMixin.methods
const { searchBPartnerList } = BusinessPartnersList.methods
import { trimPercentage } from '@/utils/ADempiere/valueFormat.js'
export default {
name: 'FieldBusinessPartner',
components: {
BusinessPartnerCreate,
BusinessPartnersList
},
props: {
parentMetadata: {
type: Object,
default: () => {}
},
showsPopovers: {
type: Object,
default: () => {
return {
isShowCreate: false,
isShowList: false
}
}
}
},
data() {
return {
controlDisplayed: this.displayedValue,
timeOut: null
}
},
computed: {
value: {
get() {
return this.$store.getters.getValueOfField({
containerUuid: this.parentMetadata.containerUuid,
columnName: 'C_BPartner_ID' // this.parentMetadata.columnName
})
},
set(value) {
this.$store.commit('updateValueOfField', {
containerUuid: this.parentMetadata.containerUuid,
columnName: 'C_BPartner_ID', // this.parentMetadata.columnName,
value
})
}
},
displayedValue: {
get() {
return this.$store.getters.getValueOfField({
containerUuid: this.parentMetadata.containerUuid,
// DisplayColumn_'ColumnName'
columnName: 'DisplayColumn_C_BPartner_ID' // this.parentMetadata.displayColumnName
})
},
set(value) {
this.$store.commit('updateValueOfField', {
containerUuid: this.parentMetadata.containerUuid,
// DisplayColumn_'ColumnName'
columnName: 'DisplayColumn_C_BPartner_ID', // this.parentMetadata.displayColumnName,
value
})
}
},
recordsBusinessPartners() {
return this.$store.getters.getBusinessPartnersList
},
blankBPartner() {
return {
uuid: undefined,
id: undefined,
name: undefined
}
}
},
methods: {
setBusinessPartner,
searchBPartnerList,
setNewDisplayedValue() {
const displayValue = this.displayedValue
if (this.controlDisplayed !== displayValue) {
this.controlDisplayed = displayValue
}
},
setOldDisplayedValue() {
if (this.controlDisplayed !== this.displayedValue) {
this.displayedValue = this.controlDisplayed
}
},
localSearch(stringToMatch, callBack) {
if (this.isEmptyValue(stringToMatch)) {
// not show list
callBack([])
return
}
const recordsList = this.recordsBusinessPartners
let results = recordsList
if (stringToMatch) {
const parsedValue = trimPercentage(stringToMatch.toLowerCase().trim())
results = recordsList.filter(rowBPartner => {
for (const columnBPartner in rowBPartner) {
const valueToCompare = String(rowBPartner[columnBPartner]).toLowerCase()
if (valueToCompare.includes(parsedValue)) {
return true
}
}
return false
})
// Remote search
if (this.isEmptyValue(results) && String(stringToMatch.length > 3)) {
clearTimeout(this.timeOut)
this.timeOut = setTimeout(() => {
this.remoteSearch(stringToMatch)
.then(remoteResponse => {
callBack(remoteResponse)
})
}, 2000)
return
}
}
// call callback function to return suggestions
callBack(results)
},
remoteSearch(searchValue) {
return new Promise(resolve => {
const message = {
message: 'Sin resultados coincidentes con la busqueda',
type: 'info',
showClose: true
}
this.$store.dispatch('listBPartnerFromServer', {
pageNumber: 1,
searchValue
})
.then(() => {
const recordsList = this.recordsBusinessPartners
if (this.isEmptyValue(recordsList)) {
this.$message(message)
}
resolve(recordsList)
})
.catch(error => {
console.warn(error.message)
this.$message(message)
resolve([])
})
})
},
handleSelect(selectedValue) {
let businessPartner = selectedValue
if (this.isEmptyValue(businessPartner)) {
businessPartner = this.blankBPartner
}
this.setBusinessPartner(businessPartner, false)
},
onClose() {
this.showsPopovers.isShowCreate = true
},
// TODO: Improve the handling of the event, if given an option to not search
getBPartnerWithEnter(event) {
const value = String(event.target.value).trim()
// Get one element
// this.getBPartner(value)
const createBP = () => {
this.$store.commit('updateValueOfField', {
containerUuid: 'Business-Partner-Create',
columnName: 'Name',
value
})
this.$store.commit('updateValueOfField', {
containerUuid: 'Business-Partner-Create',
columnName: 'Value',
value
})
this.showsPopovers.isShowList = false
this.showsPopovers.isShowCreate = true
}
this.searchBPartnerList({
searchValue: `%${value}%`
}, false)
.then(responseBPartnerList => {
const records = responseBPartnerList.length
if (records <= 0) {
// open create (without records)
createBP()
this.controlDisplayed = ''
} else if (records === 1) {
// set unique match
this.setBusinessPartner(responseBPartnerList[0], false)
this.controlDisplayed = responseBPartnerList[0].name
} else {
// show list with macth's
const columnName = 'Name' // Value
// if (Number.isNaN(Number(value))) {
// columnName = 'Name'
// }
this.$store.commit('updateValuesOfContainer', {
containerUuid: 'Business-Partner-List',
attributes: [{
columnName,
value: `%${value}%`
}]
})
this.showsPopovers.isShowList = true
this.showsPopovers.isShowCreate = false
}
})
.catch(error => {
// create bpartner with typing values
createBP()
console.warn(error)
})
},
getBPartner(value) {
if (this.isEmptyValue(value)) {
this.$message({
type: 'warning',
message: this.$t('notifications.fieldCannotBeEmpty'),
duration: 1500,
showClose: true
})
return
}
requestGetBusinessPartner({
searchValue: value
})
.then(responseBPartner => {
// set id, uuid and name
this.setBusinessPartner(responseBPartner, false)
})
.catch(error => {
const message = this.$t('businessPartner.notFound') + ' ' + this.$t('data.createNewRecord')
this.$message({
type: 'info',
message,
duration: 1500,
// showClose: true, // TODO: does not activate callback to display create form if closed with click
onClose: this.onClose
})
this.setBusinessPartner({
...this.blankBPartner,
name: value
})
this.$store.commit('updateValueOfField', {
containerUuid: 'Business-Partner-Create',
columnName: 'Name',
value
})
this.$store.commit('updateValueOfField', {
containerUuid: 'Business-Partner-Create',
columnName: 'Value',
value
})
console.info(`Error get Business Partner. Message: ${error.message}, code ${error.code}.`)
})
}
}
}
</script>
<style lang="scss" scope>
.custom-field-bpartner-info {
li {
line-height: normal;
padding: 15px;
.header {
text-overflow: ellipsis;
overflow: hidden;
}
.info {
color: #7e7e7e;
float: left;
font-size: 12px;
}
}
}
</style>

View File

@ -0,0 +1,141 @@
export default {
name: 'MixinBusinessPartner',
props: {
parentMetadata: {
type: Object,
default: () => {}
},
showsPopovers: {
type: Object,
default: () => {
return {
panelType: 'form',
isShowCreate: false,
isShowList: false
}
}
}
},
computed: {
shortsKey() {
return {
closeForm: ['esc'],
refreshListWithoutValues: ['shift', 'r'],
refreshList: ['f5']
}
}
},
methods: {
keyAction(event) {
switch (event.srcKey) {
case 'closeForm':
this.closeForm()
break
}
},
closeForm() {
this.showsPopovers.isShowList = false
this.showsPopovers.isShowCreate = false
},
/**
* ColumnName equals property to set into request's system-core
* @param {object} values
* @returns {object}
*/
convertValuesToSend(values) {
const valuesToSend = {}
Object.keys(values).forEach(key => {
const value = values[key]
if (this.isEmptyValue(value)) {
return
}
switch (key) {
case 'Code':
// Only used with search
valuesToSend['searchValue'] = value
break
case 'Value':
valuesToSend['value'] = value
break
case 'Name':
valuesToSend['name'] = value
break
case 'Contact':
valuesToSend['contactName'] = value
break
case 'EMail':
valuesToSend['eMail'] = value
break
case 'Phone':
valuesToSend['phone'] = value
break
// Location values
case 'C_Country_ID_UUID':
valuesToSend['countryUuid'] = value
break
case 'C_Region_ID_UUID':
valuesToSend['regionUuid'] = value
break
case 'DisplayColumn_C_Region_ID':
valuesToSend['regionName'] = value
break
case 'C_City_ID_UUID':
valuesToSend['cityUuid'] = value
break
case 'DisplayColumn_C_City_ID':
valuesToSend['cityName'] = value
break
case 'Address1':
valuesToSend['address1'] = value
break
case 'Address2':
valuesToSend['address2'] = value
break
case 'Address3':
valuesToSend['address3'] = value
break
case 'Address4':
valuesToSend['address4'] = value
break
case 'Postal':
valuesToSend['postalCode'] = value
break
}
})
valuesToSend['posUuid'] = this.$store.getters.getPointOfSalesUuid
return valuesToSend
},
setBusinessPartner({ id, name, uuid }, isCloseForm = true) {
const { parentUuid, containerUuid } = this.parentMetadata
// set ID value
this.$store.commit('updateValueOfField', {
parentUuid,
containerUuid,
columnName: 'C_BPartner_ID', // this.parentMetadata.columnName,
value: id
})
// set display column (name) value
this.$store.commit('updateValueOfField', {
parentUuid,
containerUuid,
// DisplayColumn_'ColumnName'
columnName: 'DisplayColumn_C_BPartner_ID', // this.parentMetadata.displayColumnName,
value: name
})
// set UUID value
this.$store.commit('updateValueOfField', {
parentUuid,
containerUuid,
columnName: 'C_BPartner_ID_UUID', // this.parentMetadata.columnName + '_UUID',
value: uuid
})
if (isCloseForm) {
this.closeForm()
}
}
}
}

View File

@ -0,0 +1,18 @@
const tableName = 'C_Order'
export default [
// Currency
{
tableName,
columnName: 'C_Currency_ID',
isFromDictionary: true,
overwriteDefinition: {
size: 24,
handleActionKeyPerformed: true,
handleActionPerformed: true,
validationCode: 'C_Currency.C_Currency_ID = 100',
isActiveLogics: true,
isMandatory: true
}
}
]

View File

@ -0,0 +1,100 @@
<template>
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>
<b>
{{ $t('form.pos.collect.convertAmount') }}:
{{ formatPrice(amount * convert, displayCurrency) }}
</b>
</span>
</div>
<div class="text item">
<el-row>
<el-col :span="24">
<el-form
label-position="top"
label-width="10px"
style="float: right; display: flex; line-height: 10px;"
>
<span v-for="(field, index) in fieldsList" :key="index">
<field-definition
:key="field.columnName"
:metadata-field="field"
/>
</span>
</el-form>
</el-col>
</el-row>
</div>
</el-card>
</template>
<script>
import formMixin from '@/components/ADempiere/Form/formMixin'
import { formatPrice } from '@/utils/ADempiere/valueFormat.js'
import fieldsListConvertAmountCollection from './fieldsListConvertAmountCollection.js'
export default {
name: 'ConvertAmount',
mixins: [
formMixin
],
props: {
isAddTypePay: {
type: Array,
default: undefined
},
currency: {
type: Object,
default: undefined
},
amount: {
type: Number,
default: undefined
},
convert: {
type: Number,
default: undefined
},
metadata: {
type: Object,
default: () => {
return {
uuid: 'Collection-Convert-Amount',
containerUuid: 'Collection-Convert-Amount'
}
}
}
},
data() {
return {
fieldsList: fieldsListConvertAmountCollection
}
},
computed: {
displayCurrency() {
return this.$store.getters.getValueOfField({
containerUuid: this.containerUuid,
columnName: 'DisplayColumn_C_Currency_ID'
})
}
},
created() {
this.defaultValueCurrency()
},
methods: {
formatPrice,
defaultValueCurrency() {
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'DisplayColumn_C_Currency_ID',
value: this.currency.iSOCode
})
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'C_Currency_ID',
value: this.currency.id
})
}
}
}
</script>

View File

@ -0,0 +1,136 @@
const tableName = 'C_Payment'
export default [
// Amont
{
tableName,
columnName: 'PayAmt',
isFromDictionary: true,
overwriteDefinition: {
handleContentSelection: true,
handleActionPerformed: true,
size: 24,
isActiveLogics: true,
isMandatory: true
}
},
// TenderType
{
tableName,
columnName: 'TenderType',
isFromDictionary: true,
overwriteDefinition: {
defaultValue: 'X',
handleActionKeyPerformed: true,
handleContentSelection: true,
handleActionPerformed: true,
size: 8,
isActiveLogics: true,
isMandatory: true
}
},
// Bank
{
tableName,
columnName: 'C_Bank_ID',
isFromDictionary: true,
overwriteDefinition: {
handleActionKeyPerformed: true,
handleActionPerformed: true,
handleContentSelection: true,
displayLogic: `@TenderType@=='D'`,
size: 8,
isActiveLogics: true,
isMandatory: true
}
},
// Date
{
tableName,
elementColumnName: 'DateTrx',
isFromDictionary: true,
overwriteDefinition: {
handleActionKeyPerformed: true,
handleActionPerformed: true,
size: 8,
displayLogic: `@TenderType@=='K'`,
isActiveLogics: true,
isMandatory: true
}
},
// Currency
{
tableName: 'C_Order',
columnName: 'C_Currency_ID',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
handleActionKeyPerformed: true,
handleActionPerformed: true,
validationCode: 'C_Currency.C_Currency_ID = 100',
isActiveLogics: true,
isMandatory: true
}
},
// ReferenceNo
{
tableName: 'HR_Attribute',
columnName: 'ReferenceNo',
isFromDictionary: true,
overwriteDefinition: {
handleActionKeyPerformed: true,
handleContentSelection: true,
handleActionPerformed: true,
displayLogic: `@TenderType@<>'X'&@TenderType@<>'C' `,
size: 8,
isActiveLogics: true,
isMandatory: true
}
},
// type credit card
{
tableName,
columnName: 'creditcardtype',
isFromDictionary: true,
overwriteDefinition: {
defaultValue: 'M',
handleActionKeyPerformed: true,
handleContentSelection: true,
handleActionPerformed: true,
size: 8,
displayLogic: `@TenderType@=='C'`,
isActiveLogics: true,
isMandatory: true
}
},
// number credit card
{
tableName,
columnName: 'creditcardnumber',
isFromDictionary: true,
overwriteDefinition: {
handleActionKeyPerformed: true,
handleContentSelection: true,
handleActionPerformed: true,
size: 8,
displayLogic: `@TenderType@=='C'`,
isActiveLogics: true,
isMandatory: true
}
},
// accountno
{
tableName,
columnName: 'accountno',
isFromDictionary: true,
overwriteDefinition: {
handleActionKeyPerformed: true,
handleContentSelection: true,
handleActionPerformed: true,
size: 8,
displayLogic: `@TenderType@=='M'`,
isActiveLogics: true,
isMandatory: true
}
}
]

View File

@ -0,0 +1,714 @@
<template>
<el-container style="background: white; height: 100% !important;">
<el-main style="background: white; padding: 0px; height: 100% !important; overflow: hidden">
<el-container style="background: white; padding: 0px; height: 100% !important;">
<el-header style="height: auto; padding-bottom: 10px; padding-right: 0px; padding-left: 0px">
<el-card class="box-card" style="padding-left: 0px; padding-right: 0px">
<div slot="header" class="clearfix">
<p class="total">
<b>{{ $t('form.pos.collect.orderTotal') }}:</b>
<b style="float: right;">
<el-popover
placement="top-start"
trigger="click"
>
<convert-amount
:convert="multiplyRate"
:amount="order.grandTotal"
:currency="currencyPoint"
/>
<el-button slot="reference" type="text" style="color: #000000;font-weight: 604!important;font-size: 100%;">
{{ formatPrice(order.grandTotal, currencyPoint.iSOCode) }}
</el-button>
</el-popover>
</b>
</p>
<p class="total">
<b> {{ $t('form.pos.collect.pending') }}: </b>
<b style="float: right;">
<el-popover
placement="top-start"
trigger="click"
>
<convert-amount
:convert="multiplyRate"
:amount="pending"
:currency="currencyPoint"
/>
<el-button slot="reference" type="text" style="color: #000000;font-weight: 604!important;font-size: 100%;">
{{ formatPrice(pending, currencyPoint.iSOCode) }}
</el-button>
</el-popover>
</b>
</p>
</div>
<div
v-if="isLoaded"
class="text item"
>
<el-form
label-position="top"
label-width="10px"
style="float: right; display: flex; line-height: 10px;"
>
<el-row>
<el-col v-for="(field, index) in fieldsList" :key="index" :span="8">
<field-definition
:key="field.columnName"
:metadata-field="field"
/>
</el-col>
</el-row>
</el-form>
</div>
</el-card>
<samp style="float: right;padding-right: 10px;">
<el-button type="danger" icon="el-icon-close" @click="cancel" />
<el-button type="primary" :disabled="isValidForPay || validateConvertion" icon="el-icon-plus" @click="addCollectToList(paymentBox)" />
</samp>
</el-header>
<el-main style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;">
<type-collection
v-if="isLoaded"
:is-add-type-pay="paymentBox"
:currency="currencyPoint"
/>
<div
v-else
key="form-loading"
v-loading="!isLoaded"
:element-loading-text="$t('notifications.loading')"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
class="loading-panel"
/>
</el-main>
<el-footer height="auto" style="padding-left: 0px; padding-right: 0px;">
<el-row :gutter="24" style="background-color: rgb(245, 247, 250);">
<el-col :span="24">
<span>
<p class="total">
<b>
{{ $t('form.pos.collect.orderTotal') }}:
</b>
<b style="float: right;">
<el-popover
placement="top-start"
trigger="click"
>
<convert-amount
:convert="multiplyRate"
:amount="order.grandTotal"
:currency="currencyPoint"
/>
<el-button slot="reference" type="text" style="color: #000000;font-weight: 604!important;font-size: 100%;">
{{ formatPrice(order.grandTotal, currencyPoint.iSOCode) }}
</el-button>
</el-popover>
</b>
</p>
<p class="total">
{{ $t('form.pos.collect.pending') }}:
<b style="float: right;">
<el-popover
placement="top-start"
trigger="click"
>
<convert-amount
:convert="multiplyRate"
:amount="pending"
:currency="currencyPoint"
/>
<el-button slot="reference" type="text" style="color: #000000;font-weight: 604!important;font-size: 100%;">
{{ formatPrice(pending, currencyPoint.iSOCode) }}
</el-button>
</el-popover>
</b>
</p>
<p class="total">
{{ $t('form.pos.collect.payment') }}:
<b style="float: right;">
<el-popover
placement="top-start"
trigger="click"
>
<convert-amount
:convert="multiplyRate"
:amount="pay"
:currency="currencyPoint"
/>
<el-button slot="reference" type="text" style="color: #000000;font-weight: 604!important;font-size: 100%;">
{{ formatPrice(pay, currencyPoint.iSOCode) }}
</el-button>
</el-popover>
</b>
</p>
<p class="total">
{{ $t('form.pos.collect.change') }}:
<b style="float: right;">
<el-popover
placement="top-start"
trigger="click"
>
<convert-amount
:convert="multiplyRate"
:amount="change"
:currency="currencyPoint"
/>
<el-button slot="reference" type="text" style="color: #000000;font-weight: 604!important;font-size: 100%;">
{{ formatPrice(change, currencyPoint.iSOCode) }}
</el-button>
</el-popover>
</b>
</p>
</span>
</el-col>
</el-row>
</el-footer>
</el-container>
</el-main>
</el-container>
</template>
<script>
import formMixin from '@/components/ADempiere/Form/formMixin'
import fieldsListCollection from './fieldsListCollection.js'
import typeCollection from '@/components/ADempiere/Form/VPOS/Collection/typeCollection'
import convertAmount from '@/components/ADempiere/Form/VPOS/Collection/convertAmount/index'
import { formatDate, formatPrice } from '@/utils/ADempiere/valueFormat.js'
export default {
name: 'Collection',
components: {
typeCollection,
convertAmount
},
mixins: [
formMixin
],
props: {
isLoadedPanel: {
type: Boolean,
required: false
},
amount: {
type: Object,
default: undefined
},
metadata: {
type: Object,
default: () => {
return {
uuid: 'Collection',
containerUuid: 'Collection'
}
}
}
},
data() {
return {
isCustomForm: true,
currencyConversion: 1,
convertAllPayment: 1,
allPayCurrency: 0,
fieldsList: fieldsListCollection
}
},
computed: {
isPaymentBox() {
return this.$store.getters.getPaymentBox
},
paymentBox() {
const payment = this.isPaymentBox.filter(pay => {
return pay.isVisible
})
if (this.isEmptyValue(payment)) {
return []
}
return payment
},
cashPayment() {
const cash = this.isPaymentBox.filter(pay => {
return pay.tenderType === 'X'
})
return this.sumCash(cash)
},
isValidForPay() {
const containerUuid = this.containerUuid
const typePay = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'TenderType'
})
const amount = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'PayAmt'
})
const allChange = Number.parseFloat(amount + this.change).toFixed(2) - this.cashPayment
if (typePay !== 'X' && !this.isMandatory) {
if (this.cashPayment === this.change && this.pending === 0 || (allChange > 0 && this.pending === 0)) {
return true
}
return false
}
const cash = this.pending === 0 ? true : this.isMandatory
return cash
},
turned() {
const containerUuid = this.containerUuid
const amount = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'PayAmt'
})
const typePay = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'TenderType'
})
const allPay = this.cashPayment + amount
if (typePay !== 'X') {
if (allPay <= this.order.grandTotal) {
return false
}
return true
}
return false
},
isCashAmt() {
const cashAmt = this.paymentBox.map(item => {
if (item.tenderType === 'X') {
return item.payAmt
}
return 0
})
if (!this.isEmptyValue(cashAmt)) {
return cashAmt.reduce((accumulator, currentValue) => accumulator + currentValue)
}
return 0
},
isOtherPayAmt() {
const cashAmt = this.paymentBox.map(item => {
if (item.tenderType !== 'X') {
return item.payAmt
}
return 0
})
if (!this.isEmptyValue(cashAmt)) {
return cashAmt.reduce((accumulator, currentValue) => accumulator + currentValue)
}
return 0
},
pay() {
return this.sumCash(this.isPaymentBox)
},
pending() {
const missing = this.order.grandTotal - this.pay
if (this.pay > 0 && this.pay < this.order.grandTotal) {
return missing
}
const pending = this.order.grandTotal <= this.pay ? 0 : this.order.grandTotal
return pending
},
isMandatory() {
const containerUuid = this.containerUuid
const fieldsEmpty = this.$store.getters.getFieldsListEmptyMandatory({
containerUuid,
fieldsList: this.fieldsList
})
const amount = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'PayAmt'
})
if (this.isEmptyValue(fieldsEmpty) && amount > 0) {
return false
}
return true
},
change() {
const missing = this.pay - this.order.grandTotal
if (this.pay > 0 && this.pay > this.order.grandTotal) {
return missing
}
return 0
},
order() {
return this.$store.getters.getFindOrder
},
currencyPoint() {
const currency = this.$store.getters.getCurrentPOS
if (!this.isEmptyValue(currency)) {
return currency.priceList.currency
}
return {
uuid: '',
iSOCode: '',
curSymbol: '',
id: 0
}
},
currentOrder() {
return this.$store.getters.getFindOrder
},
typeCurrency() {
return this.$store.getters.getValueOfField({
containerUuid: this.containerUuid,
columnName: 'C_Currency_ID'
})
},
currencyUuid() {
return this.$store.getters.getValueOfField({
containerUuid: this.containerUuid,
columnName: 'C_Currency_ID_UUID'
})
},
displayeTypeCurrency() {
return this.$store.getters.getValueOfField({
containerUuid: this.containerUuid,
columnName: 'DisplayColumn_C_Currency_ID'
})
},
multiplyRate() {
return this.$store.getters.getMultiplyRate
},
converCurrency() {
return this.$store.getters.getValueOfField({
containerUuid: 'Collection-Convert-Amount',
columnName: 'C_Currency_ID_UUID'
})
},
divideRate() {
return this.$store.getters.getDivideRate
},
fieldAmount() {
const amount = this.$store.getters.getValueOfField({
containerUuid: this.containerUuid,
columnName: 'PayAmt'
})
return amount * this.divideRate
},
validateConvertion() {
if (this.fieldAmount <= this.pending) {
return false
}
return true
}
// fieldpending() {
// return this.pending / this.divideRate
// }
},
watch: {
// fieldpending(value) {
// this.$store.commit('updateValueOfField', {
// containerUuid: this.containerUuid,
// columnName: 'PayAmt',
// value: value
// })
// },
pending(value) {
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'PayAmt',
value
})
},
currencyUuid(value) {
this.$store.dispatch('conversionDivideRate', {
conversionTypeUuid: this.$store.getters.getCurrentPOS.conversionTypeUuid,
currencyFromUuid: this.currencyPoint.uuid,
currencyToUuid: value,
conversionDate: this.currentOrder.dateOrdered
})
},
convertAllPayment(value) {
if (!this.isEmptyValue(value)) {
this.allPayCurrency = this.pay / value
}
this.allPayCurrency = this.pay
},
converCurrency(value) {
this.$store.dispatch('conversionMultiplyRate', {
conversionTypeUuid: this.$store.getters.getCurrentPOS.conversionTypeUuid,
currencyFromUuid: this.currencyPoint.uuid,
currencyToUuid: value,
conversionDate: this.currentOrder.dateOrdered
})
},
isLoaded(value) {
if (value) {
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'PayAmt',
value: this.pending
})
}
}
},
created() {
this.defaultValueCurrency()
},
methods: {
formatDate,
formatPrice,
sumCash(cash) {
let sum = 0
cash.forEach((pay) => {
sum += pay.payAmt
})
return sum
},
notSubmitForm(event) {
event.preventDefault()
return false
},
displayTenderType(type) {
let label
switch (type) {
case 'A':
label = 'Depósito directo'
break
case 'C':
label = 'Tarjeta de crédito'
break
case 'D':
label = 'Débito directo'
break
case 'K':
label = 'Cheque'
break
case 'M':
label = 'Nota de crédito'
break
case 'P':
label = 'Pago móvil interbancario'
break
case 'T':
label = 'Cuenta'
break
case 'X':
label = 'Efectivo'
break
case 'Z':
label = 'Zelle'
break
}
return label
},
addCollectToList() {
const containerUuid = this.containerUuid
const amount = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'PayAmt'
})
const date = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'DateTrx'
})
const typePay = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'TenderType'
})
const referenceNo = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'ReferenceNo'
})
let currency = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'DisplayColumn_C_Currency_ID'
})
const currencyId = this.$store.getters.getValueOfField({
containerUuid,
columnName: 'C_Currency_ID'
})
if (currency === this.currencyPoint.id) {
currency = this.currencyPoint.iSOCode
}
const displayType = this.displayTenderType(typePay)
this.$store.dispatch('setPaymentBox', {
isVisible: true,
quantityCahs: amount,
payAmt: amount * this.divideRate,
tenderType: typePay,
referenceNo: referenceNo,
dateTrx: date,
currency: {
currency,
id: currencyId
},
displayTenderType: displayType
})
this.addCollect()
},
addCollect() {
this.fieldsList.forEach(element => {
if (element.columnName !== 'PayAmt') {
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: element.columnName,
value: element.defaultValue
})
// set default logics
this.$store.dispatch('changeDependentFieldsList', {
field: element
})
}
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'C_Currency_ID',
value: this.currencyPoint.id
})
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'PayAmt',
value: this.pending
})
})
this.defaultValueCurrency()
this.$store.dispatch('conversionDivideRate', 1)
},
cancel() {
this.fieldsList.forEach(element => {
if (element.columnName !== 'PayAmt' || element.columnName !== 'C_Currency_ID') {
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: element.columnName,
value: element.defaultValue
})
}
this.$store.dispatch('changeDependentFieldsList', {
field: element
})
})
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'C_Currency_ID',
value: this.currencyPoint.id
})
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'PayAmt',
value: this.pending
})
this.defaultValueCurrency()
this.$store.dispatch('conversionDivideRate', 1)
},
getPriceApplyingDiscount(price, discount) {
if (this.isEmptyValue(price)) {
price = 0
}
if (this.isEmptyValue(discount)) {
discount = 0
}
return price - discount * price / 100
},
getDiscountByPriceEntered(unitPrice, priceEntereded) {
if (this.isEmptyValue(unitPrice)) {
unitPrice = 0
}
if (this.isEmptyValue(priceEntereded)) {
priceEntereded = 0
}
const discount = 100 - priceEntereded * 100 / unitPrice
if (this.isEmptyValue(discount) || discount === -Infinity) {
return 0
}
return discount
},
defaultValueCurrency() {
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'DisplayColumn_C_Currency_ID',
value: this.currencyPoint.iSOCode
})
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'C_Currency_ID',
value: this.currencyPoint.id
})
this.$store.commit('updateValueOfField', {
containerUuid: this.containerUuid,
columnName: 'C_Currency_ID_UUID',
value: this.currencyPoint.uuid
})
}
}
}
</script>
<style scoped>
.el-button--text {
border-color: transparent;
color: #1890ff;
background: transparent;
padding-left: 0;
padding-right: 0;
padding-bottom: 0px;
padding-top: 0px;
}
.el-card__header {
padding: 18px 20px;
border-bottom: 1px solid #e6ebf5;
-webkit-box-sizing: border-box;
box-sizing: border-box;
background-color: rgb(245, 247, 250);
}
.delete-buttom {
border: none;
width: 100%;
text-align: left;
}
.el-col-24 {
width: 100%;
padding-right: 0px !important;
padding-left: 0px !important;
}
.el-col-6 {
padding-right: 0px !important;
padding-left: 0px !important;
}
.el-card__body {
padding-top: 0px !important;
padding-right: 0px!important;
padding-bottom: 20px;
padding-left: 10px!important;
height: 100%!important;
}
.el-card__header {
padding: 0px 20px;
border-bottom: 1px solid #e6ebf5;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.bottom {
margin-top: 13px;
line-height: 12px;
}
.button {
padding: 0;
float: right;
}
.el-header {
background: 'white';
color: #333;
line-height: 10px;
}
.el-aside {
color: #333;
}
.el-row {
margin: 0px!important;
}
.el-tag--medium {
height: 34px;
line-height: 32px;
}
.el-col {
border-radius: 4px;
}
</style>

View File

@ -0,0 +1,193 @@
<template>
<el-container style="background: white; height: 100% !important;">
<el-main style="padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px;">
<el-row :gutter="24">
<template v-for="(value, key) in isAddTypePay">
<el-col v-if="value.isVisible" :key="key" :span="12" style="padding-left: 5px; padding-right: 5px;">
<el-card :body-style="{ padding: '0px' }">
<el-row>
<el-col :span="6" style="padding: 10px">
<img src="@/image/ADempiere/pos/no-image.jpg" fit="contain" class="image">
</el-col>
<el-col :span="18">
<el-button
type="text"
icon="el-icon-close"
style="float: right; margin-right: 10px; color: red; padding-top: 10px;"
@click="deleteCollect(key)"
/>
<div style="padding-right: 10px; padding-top: 10%;">
<div class="top clearfix">
<span>{{ value.displayTenderType }}</span>
</div>
<div class="bottom clearfix" style="margin-top: 0px !important!">
<el-button
type="text"
class="button"
style="color: rgb(50, 54, 58); font-size: 13px; text-align: left; float: unset; padding-top: 5px;"
>
{{ value.referenceNo }}
</el-button>
<el-button
v-if="!isEmptyValue(value.dateTrx)"
type="text"
class="button"
style="color: rgb(50, 54, 58); font-size: 13px; text-align: left; float: unset; padding-top: 5px;"
>
{{ formatDate(value.dateTrx) }}
</el-button>
<div slot="header" class="clearfix">
<p class="total" :style="value.currency.id === currency.id ? 'padding-top: 5%;' : ''">
<b style="float: right; padding-bottom: 10px">
{{ formatPrice(value.payAmt, currency.iSOCode) }}
</b>
</p>
<br>
<p v-if="value.currency.id !== currency.id" class="total">
<b style="float: right; padding-bottom: 10px">
{{ formatPrice(value.quantityCahs, value.currency.currency) }}
</b>
</p>
</div>
</div>
</div>
</el-col>
</el-row>
</el-card>
</el-col>
</template>
</el-row>
</el-main>
</el-container>
</template>
<script>
import {
formatDate,
formatPrice
} from '@/utils/ADempiere/valueFormat.js'
export default {
name: 'TypeCollection',
props: {
isAddTypePay: {
type: Array,
default: undefined
},
currency: {
type: Object,
default: undefined
}
},
methods: {
formatDate,
formatPrice,
getImageFromTenderType(typePay) {
// A: Direct Deposit: ACH Automatic Clearing House
// C: Credit Card:
// D: Direct Debit:
// K: Check:
// M: Credit Memo:
// P: Mobile Payment Interbank:
// T: Account:
// X: Cash:
// Z: Zelle:
let image = ''
switch (typePay) {
case 'A':
image = 'DirectDeposit2'
break
case 'M':
image = 'CreditMemo'
break
case 'K':
image = 'check2'
break
case 'X':
image = 'cash'
break
case 'Z':
image = 'zelle'
break
case 'T':
image = 'Account'
break
case 'P':
image = 'paymobile'
break
case 'C':
image = 'CreditCard'
break
case 'D':
image = 'DirectDebit'
break
}
return require('@/image/' + image + '.jpg')
},
deleteCollect(key) {
this.$store.dispatch('deleteCollectBox', key)
}
}
}
</script>
<style scoped>
.el-image {
display: inline-block;
overflow: hidden;
}
.el-card__header {
padding: 18px 20px;
border-bottom: 1px solid #e6ebf5;
-webkit-box-sizing: border-box;
box-sizing: border-box;
background-color: rgb(245, 247, 250);
}
.el-card__body {
padding-top: 0px !important;
padding-right: 0px!important;
padding-bottom: 20px;
padding-left: 10px!important;
height: 100%!important;
}
.bottom {
margin-top: 0px!important;
line-height: 1px;
}
.button {
padding: 0;
float: right;
}
.image {
width: 100%;
display: block;
height: 9vh;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
.el-header {
background: 'white';
color: #333;
line-height: 10px;
}
.el-aside {
color: #333;
}
.el-row {
margin: 0px!important;
}
</style>

View File

@ -0,0 +1,351 @@
<template>
<el-container style="background: white; height: 100% !important;">
<el-header>
<div class="clearfix" style="padding: 0px; margin: 0px">
<el-row>
<el-col :span="20" style="padding-left: 20%; padding-right: 0px">
<p style="text-align: center; padding: 0px; margin: 0px;">
<b>{{ getLayoutHeader.name }}</b>
<br>
{{ getLayoutHeader.description }}
</p>
</el-col>
</el-row>
</div>
<el-button
icon="el-icon-s-home"
size="small"
style="float: right"
circle
@click="handleCommand"
/>
</el-header>
<el-main>
<el-card
v-if="!isEmptyValue(getKeyList)"
>
<el-row style="padding: 2px">
<template v-for="(keyValue, key) in getKeyList">
<el-col :key="key" :span="size">
<el-card
:body-style="{ padding: '0px' }"
:data="getImage(keyValue.resourceReference)"
shadow="always"
>
<span>
<p style="padding-left: 10px; font-size: 12px; color: #0e2ea4">
<b>{{ keyValue.name }}</b>
</p>
<!--
<b style="padding-left: 10px; font-size: 10px; color: #359e43">
Available
</b>
-->
</span>
<div @click="setKeyActionToOrderLine(keyValue)">
<el-image
v-if="ifImage(keyValue)"
:src="srcImage(keyValue)"
class="key-layout"
fit="contain"
/>
<vue-content-loading v-else :width="300" :height="300" />
</div>
<div class="footer-product">
<p class="quantity">
Cantidad: {{ formatQuantity(keyValue.quantity) }}
</p>
<br>
</div>
</el-card>
</el-col>
</template>
</el-row>
</el-card>
<el-card v-else>
<el-image
:src="defaultImage"
class="error-layout"
fit="contain"
/>
<div class="error-product">
<el-link
@click="handleCommand"
>
{{ $t('form.pos.keyLayout.noProducto') }}
<i class="el-icon-s-home" />
</el-link>
</div>
</el-card>
</el-main>
</el-container>
</template>
<script>
import VueContentLoading from '@/components/ADempiere/ContentLoader'
import { requestImage } from '@/api/ADempiere/persistence.js'
import { buildImageFromArrayBuffer } from '@/utils/ADempiere/resource.js'
import { formatQuantity } from '@/utils/ADempiere/valueFormat.js'
export default {
name: 'KeyLayout',
components: {
VueContentLoading
},
data() {
return {
resource: {},
isLoadedKeyLayout: false,
valuesImage: [{
identifier: 'undefined',
value: '',
isLoaded: true
}]
}
},
computed: {
defaultImage() {
return require('@/image/ADempiere/pos/no-image.jpg')
},
currentPoint() {
return this.$store.getters.getCurrentPOS
},
listOrderLine() {
return this.$store.getters.getListOrderLine
},
getKeyLayout() {
return this.$store.getters.getKeyLayout
},
getKeyList() {
return this.getKeyLayout.keysList
},
getLayoutHeader() {
const keyLayout = this.getKeyLayout
if (keyLayout) {
return keyLayout
}
return {
name: undefined,
description: undefined
}
},
// TODO: Verify with panel collection
isShowedPOSKeyLayout() {
return this.$store.getters.getShowPOSKeyLayout
},
isReadyFromGetData() {
const { isLoaded, isReload } = this.getKeyLayout
return (!isLoaded || isReload) && this.isShowedPOSKeyLayout
},
size() {
const size = this.$store.getters.getWidthRight
return 24 / size
}
},
watch: {
isReadyFromGetData(isToLoad) {
if (isToLoad) {
this.loadKeyLayout()
}
}
},
mounted() {
if (this.isReadyFromGetData) {
this.loadKeyLayout()
}
},
methods: {
formatQuantity,
loadKeyLayout(uuid = null) {
const currentPOS = this.currentPoint
if (this.isEmptyValue(currentPOS) || this.isEmptyValue(currentPOS.uuid)) {
this.$message({
type: 'warn',
message: 'Without POS Terminal',
shosClose: true
})
return
}
this.$store.dispatch('getKeyLayoutFromServer', uuid)
.then(() => {
this.isLoadedKeyLayout = true
})
},
getImage(resource) {
if (this.isEmptyValue(resource) || this.isEmptyValue(resource.fileName)) {
return
}
const { fileName, contentType } = resource
if (!this.valuesImage.some(item => item.identifier === fileName)) {
this.valuesImage.push({
identifier: fileName,
value: '',
isLoaded: false
})
}
if (this.resource[fileName]) {
this.valuesImage.forEach(item => {
if (item.identifier === fileName) {
item.value = this.resource[fileName]
item.isLoaded = true
}
})
} else { // Reload
if (!this.valuesImage.some(item => item.identifier === fileName)) {
this.valuesImage.push({
identifier: fileName,
value: '',
isLoaded: false
})
}
requestImage({
file: fileName
}).then(responseImage => {
const arrayBufferAsImage = buildImageFromArrayBuffer({
arrayBuffer: responseImage,
contentType
})
this.resource[fileName] = arrayBufferAsImage
this.valuesImage.forEach(item => {
if (item.identifier === fileName) {
item.value = arrayBufferAsImage
item.isLoaded = true
}
})
})
}
},
setKeyActionToOrderLine(keyValue) {
if (!this.isEmptyValue(keyValue.subKeyLayoutUuid)) {
this.loadKeyLayout(keyValue.subKeyLayoutUuid)
} else {
const products = this.listOrderLine.find(item => item.lineDescription === keyValue.name)
// TODO: Change this dispatch
if (!this.isEmptyValue(products) && keyValue.quantity > 1) {
this.$store.dispatch('notifyActionKeyPerformed', {
value: {
QtyEntered: keyValue.quantity,
value: keyValue.name
}
})
} else {
this.$store.dispatch('notifyActionKeyPerformed', {
columnName: 'ProductValue',
value: {
QtyEntered: keyValue.quantity,
value: keyValue.name
}
})
}
}
},
handleCommand(command) {
const point = this.$store.getters.getPointOfSalesUuid.keyLayoutUuid
const toReturn = this.getKeyList.find(keyLayoutItem => keyLayoutItem.subKeyLayoutUuid === point)
let keyLayoutUuid = this.currentPoint.keyLayoutUuid
if (!this.isEmptyValue(toReturn)) {
keyLayoutUuid = toReturn.subKeyLayoutUuid
}
this.loadKeyLayout(keyLayoutUuid)
},
ifImage(keyValue) {
const { fileName } = keyValue.resourceReference
if (this.isEmptyValue(fileName)) {
return true
}
const image = this.valuesImage.find(item => item.identifier === fileName)
if (this.isEmptyValue(image)) {
return false
}
return image.isLoaded
},
srcImage(keyValue) {
const { fileName } = keyValue.resourceReference
if (this.isEmptyValue(fileName)) {
return this.defaultImage
}
// const image = this.valuesImage.find(item => item.identifier === fileName).value
const image = this.resource[fileName]
if (this.isEmptyValue(image)) {
return this.defaultImage
}
return image
}
}
}
</script>
<style lang="scss" scoped>
.el-card__header {
padding: 0px !important;
border-bottom: 1px solid #e6ebf5;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.key-layout {
width: 100%;
height: 200px;
display: block;
padding-top: 10px;
padding-right: 10px;
padding-bottom: 10px;
padding-left: 10px;
}
.error-layout {
width: 100%;
height: 25%;
display: block;
padding-top: 10px;
padding-right: 10px;
padding-bottom: 10px;
padding-left: 10px;
}
.price {
height: 100%;
font-size: 15px;
width: 50%;
text-align: right;
padding: 0px !important;
margin-top: 5px !important;
margin-bottom: 10px !important;
margin-left: 0px !important;
margin-right: 7px !important;
}
.quantity {
height: 100%;
font-size: 13px;
text-align: left;
width: 50%;
padding: 0px !important;
margin-top: 5px !important;
margin-bottom: 10px !important;
margin-left: 7px !important;
margin-right: 0px !important;
}
.footer-product {
display: flex;
width: 100%;
}
.error-product {
width: 100%;
text-align: center;
}
.el-main {
display: block;
box-sizing: border-box;
padding-top: 5px !important;
padding-bottom: 5px !important;
padding-left: 5px !important;
padding-right: 5px !important;
}
</style>

View File

@ -0,0 +1,456 @@
<template>
<div>
<div style="text-align: center">
<b>{{ $t('form.pos.title') }}</b>
<br>
{{ $t('form.pos.optionsPoinSales.title') }}
</div>
<el-collapse v-model="activeName" accordion>
<el-collapse-item :title="$t('form.pos.optionsPoinSales.salesOrder.title')" name="salesOrder">
<el-row :gutter="12" style="padding-right: 10px;">
<el-col :span="size">
<el-card shadow="hover">
<p
:style="isEmptyValue($route.query.action) ? 'cursor: not-allowed; text-align: center !important; color: gray;' : blockOption"
@click="newOrder"
>
<i class="el-icon-news" />
<br>
{{ $t('form.pos.optionsPoinSales.salesOrder.newOrder') }}
</p>
</el-card>
</el-col>
<el-col :span="size">
<el-card shadow="hover">
<el-popover
v-model="isShowOrdersList"
placement="right"
width="800"
trigger="click"
>
<orders-list
v-if="isShowOrdersList"
:parent-metadata="metadata"
/>
<p
slot="reference"
:style="blockOption"
>
<svg-icon icon-class="list" />
<br>
{{ $t('form.pos.optionsPoinSales.salesOrder.ordersHistory') }}
</p>
</el-popover>
</el-card>
</el-col>
<el-col :span="size">
<el-card shadow="hover">
<p
:style="blockOption"
@click="generateImmediateInvoice"
>
<i class="el-icon-document-add" />
<br>
{{ $t('form.pos.optionsPoinSales.salesOrder.generateImmediateInvoice') }}
</p>
</el-card>
</el-col>
<el-col :span="size">
<el-card shadow="hover">
<p
:style="blockOption"
@click="completePreparedOrder"
>
<i class="el-icon-success" />
<br>
{{ $t('form.pos.optionsPoinSales.salesOrder.completePreparedOrder') }}
</p>
</el-card>
</el-col>
<el-col :span="size">
<el-card shadow="hover">
<p
:style="blockOption"
@click="reverseSalesTransaction"
>
<i class="el-icon-error" />
<br>
{{ $t('form.pos.optionsPoinSales.salesOrder.cancelSaleTransaction') }}
</p>
</el-card>
</el-col>
<el-col :span="size">
<el-card shadow="hover">
<p
:style="blockOption"
@click="createWithdrawal"
>
<i class="el-icon-document-remove" />
<br>
{{ $t('form.pos.optionsPoinSales.salesOrder.createPos') }}
</p>
</el-card>
</el-col>
<el-col :span="size">
<el-card shadow="hover">
<p
:style="blockOption"
@click="printOrder"
>
<i class="el-icon-printer" />
<br>
{{ $t('form.pos.optionsPoinSales.salesOrder.toPrint') }}
</p>
</el-card>
</el-col>
<el-col :span="size">
<el-card shadow="hover">
<p
:style="blockOption"
@click="createNewCustomerReturnOrder"
>
<i class="el-icon-refresh-left" />
<br>
Crear Nueva Orden de Devolución
</p>
</el-card>
</el-col>
</el-row>
</el-collapse-item>
<el-collapse-item :title="$t('form.pos.optionsPoinSales.cashManagement.title')" name="cashManagement">
<el-row :gutter="12" style="padding-right: 10px;">
<el-col :span="size">
<el-card shadow="hover">
<p
:style="blockOption"
>
<i class="el-icon-sell" />
<br>
{{ $t('form.pos.optionsPoinSales.cashManagement.cashOpening') }}
</p>
</el-card>
</el-col>
<el-col :span="size">
<el-card shadow="hover">
<p
:style="blockOption"
>
<i class="el-icon-money" />
<br>
{{ $t('form.pos.optionsPoinSales.cashManagement.cashwithdrawal') }}
</p>
</el-card>
</el-col>
<el-col :span="size">
<el-card shadow="hover">
<p
:style="blockOption"
@click="cashClosing"
>
<i class="el-icon-sold-out" />
<br>
{{ $t('form.pos.optionsPoinSales.cashManagement.closeBox') }}
</p>
</el-card>
</el-col>
</el-row>
</el-collapse-item>
<el-collapse-item :title="$t('form.pos.optionsPoinSales.generalOptions.title')" name="generalOptions">
<el-row :gutter="12" style="padding-right: 10px;">
<!--
<el-col :span="size">
<el-card shadow="hover">
<el-popover
placement="right"
width="400"
trigger="click"
>
<el-form label-position="top" label-width="10px" @submit.native.prevent="notSubmitForm">
<field
:key="typeDocumentMetadata.columnName"
:metadata-field="typeDocumentMetadata"
:v-model="typeDocumentMetadata.value"
style="padding-left: 0px; padding-right: 0px;"
/>
</el-form>
<p slot="reference" :style="blockOption">
<i class="el-icon-document-copy" />
<br>
Cambiar Tipo de Documento
</p>
</el-popover>
</el-card>
</el-col>
-->
<el-col :span="size">
<el-card shadow="hover">
<el-dropdown trigger="click" style="padding-top: 8px; color: black;" @command="changePos">
<p
:style="blockOption"
>
<i class="el-icon-mobile-phone" />
<br>
{{ $t('form.pos.optionsPoinSales.generalOptions.changePos') }}
</p>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="item in sellingPointsList"
:key="item.uuid"
:command="item"
>
{{ item.name }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover">
<el-popover
v-model="isShowProductsPriceList"
placement="right"
width="800"
trigger="manual"
>
<list-product-price
v-if="isShowProductsPriceList"
:is-selectable="false"
popover-name="isShowPopoverMenu"
/>
<div
slot="reference"
:style="blockOption"
@click="isShowProductsPriceList = !isShowProductsPriceList"
>
<svg-icon icon-class="shopping" />
<br>
{{ $t('form.pos.optionsPoinSales.generalOptions.listProducts') }}
</div>
</el-popover>
</el-card>
</el-col>
</el-row>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script>
import OrdersList from '@/components/ADempiere/Form/VPOS/OrderList/index'
import ListProductPrice from '@/components/ADempiere/Form/VPOS/ProductInfo/productList'
import {
requestPrintOrder,
requestGenerateImmediateInvoice,
requestCompletePreparedOrder,
requestReverseSalesTransaction,
requestCreateWithdrawal,
requestCreateNewCustomerReturnOrder,
requestCashClosing
} from '@/api/ADempiere/form/point-of-sales.js'
export default {
name: 'Options',
components: {
ListProductPrice,
OrdersList
},
props: {
metadata: {
type: Object,
default: () => {}
}
},
data() {
return {
activeName: ''
}
},
computed: {
isShowProductsPriceList: {
get() {
return this.$store.state['pointOfSales/listProductPrice'].productPrice.isShowPopoverMenu
},
set(isShowed) {
if (!this.isEmptyValue(this.$route.query.pos)) {
this.$store.commit('showListProductPrice', {
attribute: 'isShowPopoverMenu',
isShowed
})
}
}
},
isShowOrdersList: {
get() {
return this.$store.getters.getListOrder.isShowPopover
},
set(value) {
if (!this.isEmptyValue(this.$route.query.pos)) {
this.$store.commit('showListOrders', value)
}
}
},
sellingPointsList() {
return this.$store.getters.getSellingPointsList
},
currentPOS() {
return this.$store.getters.getCurrentPOS
},
pointOfSalesId() {
const currentPOS = this.currentPOS
if (!this.isEmptyValue(currentPOS)) {
return currentPOS.id
}
return undefined
},
blockOption() {
if (!this.isEmptyValue(this.$route.query.pos)) {
return 'cursor: pointer; text-align: center !important; color: black'
}
return 'cursor: not-allowed; text-align: center !important; color: gray;'
},
size() {
const size = this.$store.getters.getWidthLeft
return 24 / size
}
},
methods: {
notSubmitForm(event) {
event.preventDefault()
return false
},
changePos(posElement) {
this.$store.dispatch('setCurrentPOS', posElement)
this.newOrder()
},
newOrder() {
this.$store.dispatch('findOrderServer', {})
const pos = this.pointOfSalesId || this.$route.query.pos
this.$router.push({
params: {
...this.$route.params
},
query: {
pos
}
}).catch(error => {
console.info(`VPOS/Options component (New Order): ${error.message}`)
}).finally(() => {
const { templateBusinessPartner } = this.currentPOS
this.$store.commit('updateValuesOfContainer', {
containerUuid: this.metadata.containerUuid,
attributes: [{
columnName: 'UUID',
value: undefined
},
{
columnName: 'ProductValue',
value: undefined
},
{
columnName: 'C_BPartner_ID',
value: templateBusinessPartner.id
},
{
columnName: 'DisplayColumn_C_BPartner_ID',
value: templateBusinessPartner.name
},
{
columnName: ' C_BPartner_ID_UUID',
value: templateBusinessPartner.uuid
}]
})
// TODO: Set order with POS Terminal default values
// this.order = {
// documentType: {},
// documentStatus: {},
// salesRepresentative: this.currentPOS.salesRepresentative
//
this.$store.dispatch('listOrderLine', [])
})
},
printOrder() {
requestPrintOrder({
orderUuid: this.$route.query.action
})
},
generateImmediateInvoice() {
// TODO: Add BPartner
const { uuid: posUuid, id: posId } = this.getCurrentPOS
requestGenerateImmediateInvoice({
posId,
posUuid
})
},
completePreparedOrder() {
requestCompletePreparedOrder({
orderUuid: this.$route.query.action
})
},
reverseSalesTransaction() {
// TODO: Add BPartner
requestReverseSalesTransaction({
orderUuid: this.$route.query.action
})
},
createWithdrawal() {
const { uuid: posUuid, id: posId } = this.getCurrentPOS
// TODO: Add BParner, C_BankAccount_ID (caja), CashTransferBankAccount_ID, PAY_C_BankAccount_ID
requestCreateWithdrawal({
posId,
posUuid
})
},
createNewCustomerReturnOrder() {
requestCreateNewCustomerReturnOrder({
orderUuid: this.$route.query.action
})
},
cashClosing() {
const { uuid: posUuid, id: posId } = this.getCurrentPOS
requestCashClosing({
posId,
posUuid
})
}
}
}
</script>
<style lang="scss" scoped>
.el-button--text {
border-color: transparent;
color: black;
background: transparent;
padding-left: 0;
padding-right: 0;
}
.el-button--text:hover, .el-button--text:focus {
color: #46a6ff !important;
border-color: transparent;
background-color: transparent;
}
.el-col :hover {
background-color: rgba(209, 233, 255, 0.719);
}
.title-of-option {
cursor: pointer;
text-align: center !important;
}
</style>

View File

@ -0,0 +1,51 @@
export default [
// Product Code
{
elementColumnName: 'ProductValue',
columnName: 'ProductValue',
isFromDictionary: true,
overwriteDefinition: {
size: 24,
sequence: 10,
handleActionKeyPerformed: true
}
},
{
elementColumnName: 'QtyEntered',
columnName: 'QtyEntered',
isFromDictionary: true,
overwriteDefinition: {
size: 24,
sequence: 8,
handleActionPerformed: true,
handleContentSelection: true,
handleActionKeyPerformed: true
}
},
{
elementColumnName: 'PriceEntered',
columnName: 'PriceEntered',
isFromDictionary: true,
overwriteDefinition: {
size: 24,
sequence: 9,
isReadOnly: true,
handleActionPerformed: true,
handleContentSelection: true,
handleActionKeyPerformed: true
}
},
{
elementColumnName: 'Discount',
columnName: 'Discount',
isFromDictionary: true,
overwriteDefinition: {
size: 24,
sequence: 10,
isReadOnly: true,
handleActionPerformed: true,
handleContentSelection: true,
handleActionKeyPerformed: true
}
}
]

View File

@ -0,0 +1,570 @@
<template>
<div
v-if="isLoaded"
id="headerContainer"
style="display: -webkit-box; height: 100%"
>
<el-container style="background: white; height: 100%!important;">
<el-header
height="auto"
:style="isShowedPOSKeyLayout ? 'padding-right: 20px; padding-left: 0px;' : 'padding-right: 0px; padding-left: 0px;'"
>
<el-form label-position="top" label-width="10px" @submit.native.prevent="notSubmitForm">
<el-row :gutter="24">
<el-col :span="14" style="padding-left: 0px; padding-right: 0px;">
<template
v-for="(field) in fieldsList"
>
<product-info
v-if="field.columnName === 'ProductValue'"
:key="field.columnName"
:metadata="field"
/>
</template>
</el-col>
<el-col :span="6" style="padding-left: 2px; padding-right: 2px;">
<business-partner
:parent-metadata="{
name: panelMetadata.name,
containerUuid: panelMetadata.containerUuid,
uuid: panelMetadata.uuid,
panelType: panelMetadata.panelType
}"
/>
</el-col>
<el-col :span="2" :style="styleTab">
<el-tag
:type="tagStatus(order.documentStatus.value)"
>
<span v-if="isEmptyValue(order.documentStatus.value)">
Borrador
</span>
{{ order.documentStatus.name }}
</el-tag>
</el-col>
</el-row>
</el-form>
</el-header>
<el-main style="background: white; padding: 0px; height: 100% !important; overflow: hidden">
<el-container style="background: white; padding: 0px; height: 100% !important;">
<el-main style="padding-top: 0px; padding-right: 10px; padding-bottom: 0px; padding-left: 10px;">
<el-table
ref="linesTable"
:data="allOrderLines"
border
style="width: 100%; max-width: 100%; background-color: #FFFFFF; font-size: 14px; overflow: auto; color: #606266;"
highlight-current-row
fit
@current-change="handleCurrentLineChange"
>
<el-table-column
v-for="(valueOrder, item, key) in orderLineDefinition"
:key="key"
:column-key="valueOrder.columnName"
:label="valueOrder.label"
:width="valueOrder.isNumeric ? 'auto' : '380'"
:align="valueOrder.isNumeric ? 'right' : 'left'"
>
<template slot-scope="scope">
<span>
{{ displayValue(scope.row, valueOrder) }}
</span>
</template>
</el-table-column>
<el-table-column
label=""
width="120"
>
<template slot-scope="scope">
<el-dropdown trigger="click" @command="changeLine">
<span class="el-dropdown-link">
{{ $t('form.pos.tableProduct.options') }}
<i class="el-icon-arrow-down el-icon--right" />
</span>
<el-dropdown-menu slot="dropdown" style="padding-bottom: 0px;">
<el-dropdown-item icon="el-icon-info" :command="scope.row">
<el-popover
placement="right"
trigger="click"
:title="$t('form.pos.product.productInformation')"
>
<el-form
label-position="top"
label-width="60px"
style="float: right; display: flex; line-height: 30px;"
>
<el-row :gutter="24">
<el-col :span="4">
<div>
<el-avatar shape="square" :size="100" src="https://#" @error="true">
<el-image>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline" />
</div>
</el-image>
</el-avatar>
</div>
</el-col>
<el-col :span="10">
{{ $t('form.pos.product.code') }}: <b>{{ currentOrderLine.product.value }}</b><br>
{{ $t('form.pos.product.name') }}: <b>{{ currentOrderLine.product.name }}</b><br>
{{ $t('form.pos.product.description') }}: <b>{{ currentOrderLine.product.description }}</b><br>
</el-col>
<el-col :span="10">
<div style="float: right">
{{ $t('form.pos.product.price') }}:
<b>{{ formatPrice(currentOrderLine.product.priceStandard, currencyPoint.iSOCode) }}</b>
<br>
{{ $t('form.pos.product.taxRate') }}:
<b>{{ currentOrderLine.taxIndicator }}</b>
<br>
{{ $t('form.pos.product.quantityAvailable') }}:
<b>{{ formatQuantity(currentOrderLine.quantityOrdered) }}</b>
</div>
</el-col>
</el-row>
</el-form>
<el-button slot="reference" type="text">
{{ $t('form.pos.product.productInformation') }}
</el-button>
</el-popover>
</el-dropdown-item>
<el-dropdown-item icon="el-icon-edit" :command="$t('form.pos.tableProduct.editQuantities')">
<el-popover
placement="right"
trigger="click"
:title="$t('form.pos.tableProduct.editQuantities')"
>
<el-row>
<el-col :span="8">
<el-form label-position="top" label-width="10px" @submit.native.prevent="notSubmitForm">
<template
v-for="(field) in fieldsList"
>
<field-definition
v-if="field.columnName === 'PriceEntered'"
:key="field.columnName"
:metadata-field="field"
/>
</template>
</el-form>
</el-col>
<el-col :span="8">
<el-form label-position="top" label-width="10px" @submit.native.prevent="notSubmitForm">
<template
v-for="(field) in fieldsList"
>
<field-definition
v-if="field.columnName === 'QtyEntered'"
:key="field.columnName"
:metadata-field="field"
/>
</template>
</el-form>
</el-col>
<el-col :span="8">
<el-form label-position="top" label-width="10px" @submit.native.prevent="notSubmitForm">
<template
v-for="(field) in fieldsList"
>
<field-definition
v-if="field.columnName === 'Discount'"
:key="field.columnName"
:metadata-field="field"
/>
</template>
</el-form>
</el-col>
</el-row>
<el-button slot="reference" type="text">
{{ $t('form.pos.tableProduct.editQuantities') }}
</el-button>
</el-popover>
</el-dropdown-item>
<el-button type="danger" icon="el-icon-delete" class="delete-buttom" plain @click="deleteOrderLine(scope.row)">
{{ $t('form.pos.tableProduct.remove') }}
</el-button>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
</el-main>
<el-footer class="footer-table">
<div class="keypad">
<el-button type="primary" icon="el-icon-top" @click="arrowTop" />
<el-button type="primary" icon="el-icon-bottom" @click="arrowBottom" />
<el-button v-show="isValidForDeleteLine(allOrderLines)" type="danger" icon="el-icon-delete" @click="deleteOrderLine(currentOrderLine)" />
<el-button
v-show="isValidForDeleteLine(allOrderLines)"
type="success"
icon="el-icon-bank-card"
@click="openCollectionPanel"
>
{{ $t('form.pos.order.collect') }}
</el-button>
<br>
<p>
<el-dropdown
trigger="click"
style="padding-top: 8px; color: black;"
@command="changePos"
>
<p>
<i class="el-icon-mobile-phone" />
{{ $t('form.pos.order.pointSale') }}: <b style="cursor: pointer"> {{ namePointOfSales }} </b>
</p>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="item in sellingPointsList"
:key="item.uuid"
:command="item"
>
{{ item.name }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</p>
</div>
<span style="float: right;">
<p class="total">{{ $t('form.pos.order.seller') }}:<b style="float: right;">
{{ order.salesRepresentative.name }}
</b></p>
<p class="total"> {{ $t('form.pos.order.subTotal') }}:<b class="order-info">{{ formatPrice(order.totalLines, currencyPoint.iSOCode) }}</b></p>
<p class="total"> {{ $t('form.pos.order.discount') }}:<b class="order-info">{{ formatPrice(0, currencyPoint.iSOCode) }}</b> </p>
<p class="total"> {{ $t('form.pos.order.tax') }}:<b style="float: right;">{{ getOrderTax(currencyPoint.iSOCode) }}</b> </p>
<p class="total"><b>{{ $t('form.pos.order.total') }}:</b><b style="float: right;">{{ formatPrice(order.grandTotal, currencyPoint.iSOCode) }}</b></p>
</span>
<span style="float: right;padding-right: 40px;">
<p class="total">{{ $t('form.pos.order.order') }}: <b class="order-info">{{ order.documentNo }}</b></p>
<p class="total">
{{ $t('form.pos.order.date') }}:
<b class="order-info">
{{ orderDate }}
</b>
</p>
<p class="total">{{ $t('form.pos.order.type') }}:<b class="order-info">{{ order.documentType.name }}</b></p>
<p class="total">
{{ $t('form.pos.order.itemQuantity') }}
<b class="order-info">
{{ getItemQuantity }}
</b>
</p>
<p class="total">
{{ $t('form.pos.order.numberLines') }}
<b class="order-info">
{{ numberOfLines }}
</b></p>
</span>
</el-footer>
</el-container>
</el-main>
</el-container>
<div style="position: relative;padding-top: 30vh; z-index: 100;">
<el-button
:circle="true"
type="primary"
:icon="isShowedPOSKeyLayout ? 'el-icon-arrow-right' : 'el-icon-arrow-left'"
@click="isShowedPOSKeyLayout = !isShowedPOSKeyLayout"
/>
</div>
</div>
<div
v-else
key="form-loading"
v-loading="!isLoaded"
:element-loading-text="$t('notifications.loading')"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(255, 255, 255, 0.8)"
class="view-loading"
/>
</template>
<script>
import formMixin from '@/components/ADempiere/Form/formMixin.js'
import orderLineMixin from './orderLineMixin.js'
import fieldsListOrder from './fieldsListOrder.js'
import posMixin from '@/components/ADempiere/Form/VPOS/posMixin.js'
import BusinessPartner from '@/components/ADempiere/Form/VPOS/BusinessPartner'
import ProductInfo from '@/components/ADempiere/Form/VPOS/ProductInfo'
export default {
name: 'Order',
components: {
BusinessPartner,
ProductInfo
},
mixins: [
formMixin,
orderLineMixin,
posMixin
],
data() {
return {
fieldsList: fieldsListOrder
}
},
computed: {
isShowedPOSKeyLayout: {
get() {
return this.$store.getters.getShowPOSKeyLayout
},
set(val) {
this.$store.commit('setShowPOSKeyLayout', val)
}
},
styleTab() {
const isShowedPOSOptions = this.$store.getters.getIsShowPOSOptions
if (this.isShowedPOSKeyLayout || isShowedPOSOptions) {
return 'adding-left: 0px; padding-right: 0px; padding-top: 3.5%;'
}
return 'padding-left: 30px; padding-right: 0px; padding-top: 2.2%;'
},
namePointOfSales() {
const currentPOS = this.$store.getters.getCurrentPOS
if (currentPOS && !this.isEmptyValue(currentPOS.name)) {
return currentPOS.name
}
return undefined
},
sellingPointsList() {
return this.$store.getters.getSellingPointsList
},
orderDate() {
if (this.isEmptyValue(this.order) || this.isEmptyValue(this.order.dateOrdered)) {
return this.formatDate(new Date())
}
return this.formatDate(this.order.dateOrdered)
},
getItemQuantity() {
if (this.isEmptyValue(this.currentOrder)) {
return 0
}
const result = this.allOrderLines.map(order => {
return order.quantityOrdered
})
if (!this.isEmptyValue(result)) {
return result.reduce((accumulator, currentValue) => {
return accumulator + currentValue
})
}
return 0
},
numberOfLines() {
if (this.isEmptyValue(this.currentOrder)) {
return
}
return this.allOrderLines.length
},
currencyPoint() {
const currency = this.currentPoint
if (!this.isEmptyValue(currency)) {
return currency.priceList.currency
}
return {
uuid: '',
iSOCode: '',
curSymbol: ''
}
}
},
methods: {
changePos(posElement) {
this.$store.dispatch('setCurrentPOS', posElement)
this.newOrder()
},
openCollectionPanel() {
this.$store.commit('setShowPOSCollection', !this.$store.getters.getShowCollectionPos)
this.isShowedPOSKeyLayout = true
this.$store.commit('setShowPOSOptions', false)
},
newOrder() {
this.$store.dispatch('findOrderServer', {})
this.$router.push({
params: {
...this.$route.params
},
query: {
pos: this.currentPoint.id
}
}).catch(() => {
}).finally(() => {
const { templateBusinessPartner } = this.currentPoint
this.$store.commit('updateValuesOfContainer', {
containerUuid: this.metadata.containerUuid,
attributes: [{
columnName: 'UUID',
value: undefined
},
{
columnName: 'ProductValue',
value: undefined
},
{
columnName: 'C_BPartner_ID',
value: templateBusinessPartner.id
},
{
columnName: 'DisplayColumn_C_BPartner_ID',
value: templateBusinessPartner.name
},
{
columnName: ' C_BPartner_ID_UUID',
value: templateBusinessPartner.uuid
}]
})
this.$store.dispatch('listOrderLine', [])
})
}
}
}
</script>
<style scoped>
.delete-buttom {
border: none;
width: 100%;
text-align: left;
}
.el-col-24 {
width: 100%;
padding-right: 0px !important;
padding-left: 0px !important;
}
.el-col-6 {
padding-right: 0px !important;
padding-left: 0px !important;
}
.footer-table {
padding-top: 0px;
padding-right: 9px;
padding-bottom: 0px;
padding-left: 9px;
height: auto !important;
}
.keypad {
float: left;
height: 20%;
padding-top: 25px;
}
.total {
margin-top: 10px;
margin-bottom: 10px
}
.split {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
overflow-y: hidden;
overflow-x: hidden;
height: 100%;
width: 100%;
}
.el-card__body {
padding-top: 0px !important;
padding-right: 0px!important;
padding-bottom: 20px;
padding-left: 10px!important;
height: 100%!important;
}
/* Style of Table */
.el-table {
position: relative;
overflow: hidden;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
width: 100%;
max-width: 100%;
height: 100%;
background-color: #FFFFFF;
font-size: 14px;
color: #606266;
}
.el-card__header {
padding: 0px 20px;
border-bottom: 1px solid #e6ebf5;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.time {
font-size: 13px;
color: #999;
}
.bottom {
margin-top: 13px;
line-height: 12px;
}
.button {
padding: 0;
float: right;
}
.image {
width: 100%;
display: block;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
.el-header {
background: 'white';
color: #333;
line-height: 10px;
}
.el-aside {
color: #333;
}
.el-row {
margin: 0px!important;
}
.el-tag--medium {
height: 34px;
line-height: 32px;
}
.el-col {
border-radius: 4px;
}
.bg-purple-dark {
background: #99a9bf;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
background: #e5e9f2;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
.row-bg {
padding: 10px 0;
background-color: #f9fafc;
}
.order-header {
padding-left: 10px;
font-size: 13px;
}
.order-info {
float: right;
padding-left: 9px;
}
</style>

View File

@ -0,0 +1,212 @@
import {
requestCreateOrderLine,
updateOrderLine,
requestDeleteOrderLine
} from '@/api/ADempiere/form/point-of-sales.js'
import { formatPercent } from '@/utils/ADempiere/valueFormat.js'
export default {
name: 'OrderLineMixin',
data() {
return {
orderLineDefinition: {
lineDescription: {
columnName: 'LineDescription',
label: this.$t('form.pos.tableProduct.product'),
isNumeric: false
},
currentPrice: {
columnName: 'CurrentPrice',
label: this.$t('form.pos.product.price'),
isNumeric: true
},
quantityOrdered: {
columnName: 'QtyOrdered',
label: this.$t('form.pos.tableProduct.quantity'),
isNumeric: true
},
discount: {
columnName: 'Discount',
label: '% ' + this.$t('form.pos.order.discount'),
isNumeric: true
},
grandTotal: {
columnName: 'GrandTotal',
label: 'Total',
isNumeric: true
}
}
}
},
methods: {
formatPercent,
changeLine(command) {
switch (command) {
case 'Eliminar':
// this.deleteOrderLine()
break
//
case this.$t('form.pos.tableProduct.editQuantities'):
this.fillOrderLineQuantities({
currentPrice: this.currentOrderLine.currentPrice,
quantityOrdered: this.currentOrderLine.quantityOrdered,
discount: this.currentOrderLine.discount
})
this.edit = true
break
//
case 'informacion':
break
}
},
fillOrderLine(orderLine) {
this.$store.dispatch('updateOrderLines', orderLine)
},
createOrderLine(orderUuid) {
const productUuid = this.product.uuid
requestCreateOrderLine({
orderUuid,
productUuid
})
.then(orderLine => {
this.fillOrderLine(orderLine)
this.reloadOrder(true, orderUuid)
})
.catch(error => {
console.warn(error.message)
this.$message({
type: 'error',
message: error.message,
showClose: true
})
})
},
listOrderLines({ uuid: orderUuid }) {
if (!this.isEmptyValue(orderUuid)) {
this.$store.dispatch('listOrderLinesFromServer', orderUuid)
this.orderLines = this.listOrderLine
this.handleCurrentLineChange(this.currentOrderLine)
}
},
updateOrderLine(line) {
let {
currentPrice: price,
discount: discountRate,
quantityOrdered: quantity
} = this.currentOrderLine
switch (line.columnName) {
case 'QtyEntered':
quantity = line.value
break
case 'PriceEntered':
price = line.value
break
case 'Discount':
discountRate = line.value
break
}
updateOrderLine({
orderLineUuid: this.currentOrderLine.uuid,
quantity,
price,
discountRate
})
.then(response => {
this.fillOrderLine(response)
this.reloadOrder(true)
})
.catch(error => {
console.error(error.message)
this.$message({
type: 'error',
message: error.message,
showClose: true
})
})
},
deleteOrderLine(lineSelection) {
requestDeleteOrderLine({
orderLineUuid: lineSelection.uuid
})
.then(() => {
this.reloadOrder(true)
})
.catch(error => {
console.error(error.message)
this.$message({
type: 'error',
message: error.message,
showClose: true
})
})
},
/**
* Show the correct display format
* @param {object} row record
* @param {object} orderLine or field definition
*/
displayValue(row, orderLine) {
const { columnName } = orderLine
if (columnName === 'LineDescription') {
return row.lineDescription
}
const currency = this.currencyPoint.iSOCode
if (columnName === 'CurrentPrice') {
return this.formatPrice(row.priceActual, currency)
} else if (columnName === 'QtyOrdered') {
return this.formatQuantity(row.quantityOrdered)
} else if (columnName === 'Discount') {
return this.formatPercent(row.discount)
} else if (columnName === 'GrandTotal') {
return this.formatPrice(row.grandTotal, currency)
}
},
handleCurrentLineChange(rowLine) {
if (!this.isEmptyValue(rowLine)) {
this.currentOrderLine = rowLine
this.currentTable = this.listOrderLine.findIndex(item => item.uuid === rowLine.uuid)
if (this.isEmptyValue(this.currentOrderLine) && !this.isEmptyValue(this.listOrderLine)) {
this.$refs.linesTable.setCurrentRow(this.listOrderLine[this.currentTable])
}
}
},
fillOrderLineQuantities({
currentPrice,
quantityOrdered,
discount
}) {
const containerUuid = this.formUuid
// Editable fields
if (!this.isEmptyValue(quantityOrdered)) {
this.$store.commit('updateValueOfField', {
containerUuid,
columnName: 'QtyEntered',
value: quantityOrdered
})
}
if (!this.isEmptyValue(currentPrice)) {
this.$store.commit('updateValueOfField', {
containerUuid,
columnName: 'PriceEntered',
value: currentPrice
})
}
if (!this.isEmptyValue(discount)) {
this.$store.commit('updateValueOfField', {
containerUuid,
columnName: 'Discount',
value: discount
})
}
},
isValidForDeleteLine(line) {
if (this.isEmptyValue(this.currentOrderLine) && !this.isEmptyValue(this.orderLines)) {
this.currentOrderLine = this.orderLines[0]
}
return !this.isEmptyValue(line)
}
}
}

View File

@ -0,0 +1,105 @@
const tableName = 'C_Order'
export default [
{
tableName,
columnName: 'DocumentNo',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
isMandatory: false
}
},
{
tableName,
columnName: 'C_BPartner_ID',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
isMandatory: false
}
},
{
tableName,
columnName: 'GrandTotal',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
isMandatory: false
}
},
{
tableName,
elementColumnName: 'OpenAmt',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
isMandatory: false
}
},
{
tableName,
elementColumnName: 'IsPaid',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
isMandatory: false
}
},
{
tableName,
columnName: 'Processed',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
isMandatory: false
}
},
{
tableName,
elementColumnName: 'IsAisleSeller',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
isMandatory: false
}
},
{
tableName,
columnName: 'IsInvoiced',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
isMandatory: false
}
},
{
tableName,
columnName: 'DateOrdered',
isFromDictionary: true,
overwriteDefinition: {
columnName: 'DateOrderedFrom',
size: 8,
isMandatory: false
}
},
{
tableName,
columnName: 'DateOrdered',
isFromDictionary: true,
overwriteDefinition: {
columnName: 'DateOrderedTo',
size: 8,
isMandatory: false
}
},
{
tableName,
columnName: 'SalesRep_ID',
isFromDictionary: true,
overwriteDefinition: {
size: 8,
isMandatory: false
}
}
]

View File

@ -0,0 +1,318 @@
<template>
<el-main
v-shortkey="shortsKey"
@shortkey.native="keyAction"
>
<el-collapse v-model="activeAccordion" accordion>
<el-collapse-item name="query-criteria">
<template slot="title">
Ver Histórico de Órdenes
</template>
<el-form
v-if="isLoaded"
label-position="top"
label-width="10px"
@submit.native.prevent="notSubmitForm"
>
<template
v-for="(field) in fieldsList"
>
<field-definition
:key="field.columnName"
:metadata-field="field"
/>
</template>
</el-form>
<div
v-else
key="form-loading"
v-loading="!isLoaded"
:element-loading-text="$t('notifications.loading')"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(255, 255, 255, 0.8)"
class="loading-panel"
/>
</el-collapse-item>
</el-collapse>
<el-table
ref="orderTable"
v-shortkey="shortsKey"
v-loading="!tableOrder.isLoaded"
:data="ordersList"
border
fit
:highlight-current-row="highlightRow"
:height="heightTable"
@shortkey.native="keyAction"
@current-change="handleCurrentChange"
>
<el-table-column
prop="documentNo"
label="Nro. Documento"
width="130"
/>
<el-table-column
label="Estado"
width="100"
>
<template slot-scope="scope">
<el-tag
:type="tagStatus(scope.row.documentStatus.value)"
>
{{ scope.row.documentStatus.name }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="salesRepresentative.name"
label="Agente Comercial"
min-width="170"
/>
<el-table-column
label="Socio de Negocio"
min-width="150"
>
<template slot-scope="scope">
{{ scope.row.businessPartner.name }}
</template>
</el-table-column>
<el-table-column
label="Fecha de Orden"
width="135"
>
<template slot-scope="scope">
{{ formatDate(scope.row.dateOrdered) }}
</template>
</el-table-column>
<el-table-column
label="Total General"
align="right"
width="120"
>
<template slot-scope="scope">
{{ formatQuantity(scope.row.grandTotal) }}
</template>
</el-table-column>
</el-table>
<custom-pagination
:total="tableOrder.recordCount"
:current-page="tableOrder.pageNumber"
:handle-change-page="handleChangePage"
/>
</el-main>
</template>
<script>
import formMixin from '@/components/ADempiere/Form/formMixin.js'
import CustomPagination from '@/components/ADempiere/Pagination'
import fieldsListOrders from './fieldsListOrders.js'
import {
formatDate,
formatQuantity
} from '@/utils/ADempiere/valueFormat.js'
export default {
name: 'OrdersList',
components: {
CustomPagination
},
mixins: [
formMixin
],
props: {
metadata: {
type: Object,
default: () => {
return {
uuid: 'Orders-List',
containerUuid: 'Orders-List'
}
}
}
},
data() {
return {
defaultMaxPagination: 50,
fieldsList: fieldsListOrders,
isCustomForm: true,
activeAccordion: 'query-criteria',
timeOut: null
}
},
computed: {
heightTable() {
if (this.isEmptyValue(this.activeAccordion)) {
return 500
}
return 250
},
highlightRow() {
if (!this.isEmptyValue(this.selectOrder)) {
return true
}
return false
},
tableOrder() {
return this.$store.getters.getListOrder
},
ordersList() {
const order = this.tableOrder
if (order && !this.isEmptyValue(order.ordersList)) {
return order.ordersList
}
return []
},
selectOrder() {
const action = this.$route.query.action
const order = this.ordersList.find(item => item.uuid === action)
if (!this.isEmptyValue(order)) {
return order
}
this.$store.dispatch('listOrderLine', [])
return null
},
isReadyFromGetData() {
const { isLoaded, isReload } = this.tableOrder
return !isLoaded || isReload
},
shortsKey() {
return {
closeOrdersList: ['esc'],
refreshList: ['f5']
}
}
},
watch: {
isReadyFromGetData(isToLoad) {
if (isToLoad) {
this.loadOrdersList()
}
}
},
created() {
this.unsubscribe = this.subscribeChanges()
if (this.isReadyFromGetData) {
this.loadOrdersList()
}
},
beforeDestroy() {
this.unsubscribe()
},
methods: {
formatDate,
formatQuantity,
keyAction(event) {
switch (event.srcKey) {
case 'refreshList':
this.loadOrdersList()
break
case 'closeOrdersList':
this.$store.commit('showListOrders', false)
break
}
},
loadOrdersList() {
let values = this.$store.getters.getValuesView({
containerUuid: this.metadata.containerUuid
})
values = this.convertValuesToSend(values)
this.$store.dispatch('listOrdersFromServer', {
...values
})
},
handleChangePage(newPage) {
this.$store.dispatch('setOrdersListPageNumber', newPage)
},
handleCurrentChange(row) {
// close popover
this.$store.commit('showListOrders', false)
this.$store.dispatch('currentOrder', row)
if (!this.isEmptyValue(row)) {
this.$store.dispatch('deleteAllCollectBox')
this.$router.push({
params: {
...this.$route.params
},
query: {
...this.$route.query,
action: row.uuid
}
}, () => {})
}
},
subscribeChanges() {
return this.$store.subscribe((mutation, state) => {
if (mutation.type === 'updateValueOfField' &&
!mutation.payload.columnName.includes('DisplayColumn') &&
!mutation.payload.columnName.includes('_UUID') &&
mutation.payload.containerUuid === this.metadata.containerUuid) {
clearTimeout(this.timeOut)
this.timeOut = setTimeout(() => {
this.loadOrdersList()
}, 2000)
}
})
},
convertValuesToSend(values) {
const valuesToSend = {}
values.forEach(element => {
const { value, columnName } = element
if (this.isEmptyValue(value)) {
return
}
switch (columnName) {
case 'DocumentNo':
valuesToSend['documentNo'] = value
break
case 'C_BPartner_ID_UUID':
valuesToSend['businessPartnerUuid'] = value
break
case 'GrandTotal':
valuesToSend['grandTotal'] = value
break
case 'OpenAmt':
valuesToSend['openAmount'] = value
break
case 'IsPaid':
valuesToSend['isPaid'] = value
break
case 'Processed':
valuesToSend['isProcessed'] = value
break
case 'IsAisleSeller':
valuesToSend['isAisleSeller'] = value
break
case 'IsInvoiced':
valuesToSend['isInvoiced'] = value
break
case 'DateOrderedFrom':
valuesToSend['dateOrderedFrom'] = value
break
case 'DateOrderedTo':
valuesToSend['dateOrderedTo'] = value
break
case 'SalesRep_ID_UUID':
valuesToSend['salesRepresentativeUuid'] = value
break
}
})
return valuesToSend
}
}
}
</script>

View File

@ -0,0 +1,12 @@
export default [
// Product Code
{
elementColumnName: 'ProductValue',
columnName: 'ProductValue',
isFromDictionary: true,
overwriteDefinition: {
size: 24,
sequence: 10
}
}
]

View File

@ -0,0 +1,254 @@
<template>
<div>
<el-popover
ref="productsList"
v-model="isShowProductsPriceList"
placement="right"
width="800"
trigger="manual"
>
<product-info-list
v-if="isShowProductsPriceList"
/>
</el-popover>
<el-form-item>
<template slot="label">
Código Producto
<!-- Ver Lista de Productos Y Precios -->
<el-button
v-popover:productsList
type="text"
icon="el-icon-search"
style="color: black"
@click="isShowProductsPriceList = !isShowProductsPriceList"
/>
</template>
<el-autocomplete
v-model="value"
v-shortkey="keyShortcuts"
:placeholder="$t('quickAccess.searchWithEnter')"
clearable
style="width: 100%;"
popper-class="custom-field-prodcut-info"
:fetch-suggestions="localSearch"
@shortkey.native="shortcutKeyMethod"
@select="handleSelect"
>
<template slot="prefix">
<svg-icon
icon-class="shopping"
class="el-input__icon"
/>
<!--
<i
class="el-icon-shopping-cart-full el-input__icon"
/>
-->
</template>
<template slot-scope="props">
<div class="header">
<b> {{ props.item.product.value }} - {{ props.item.product.name }} </b>
</div>
<el-row :gutter="20">
<el-col :span="12">
<span class="upc">
<!-- <b>UPC / EAN Barras:</b> <br> -->
{{ props.item.product.upc }} <br>
<span class="description">
{{ props.item.product.description }}
</span>
</span>
</el-col>
<!-- <el-col :span="6">
<span class="upc">
{{ props.item.product.description }}
</span>
</el-col> -->
<!-- <el-col :span="6">
<span class="upc">
{{ props.item.quantityAvailable }}
</span>
</el-col> -->
<el-col :span="12">
<span class="price">
{{ formatPrice(props.item.priceStandard, props.item.currency.iSOCode) }}
<br>
<span class="quantityAvailable">
{{ formatQuantity(props.item.quantityAvailable) }}
</span>
<!-- {{ props.item.currency.curSymbol }} -->
</span>
</el-col>
</el-row>
</template>
</el-autocomplete>
</el-form-item>
</div>
</template>
<script>
/**
* This component is made to be the prototype of the Product Info search field
*/
import ProductInfoList from './productList'
import fieldMixin from '@/components/ADempiere/Field/mixin/mixinField.js'
import {
formatPrice,
formatQuantity
} from '@/utils/ADempiere/valueFormat.js'
export default {
name: 'FieldProductInfo',
components: {
ProductInfoList
},
mixins: [
fieldMixin
],
data() {
return {
timeOut: null
}
},
computed: {
isShowProductsPriceList: {
get() {
return this.$store.state['pointOfSales/listProductPrice'].productPrice.isShowPopoverField
},
set(isShowed) {
if (!this.isEmptyValue(this.$route.query.pos)) {
this.$store.commit('showListProductPrice', {
attribute: 'isShowPopoverField',
isShowed
})
}
}
},
listWithPrice() {
const { productPricesList } = this.$store.getters.getProductPrice
if (!this.isEmptyValue(productPricesList)) {
return productPricesList
}
return []
},
keyShortcuts() {
return {
refreshList: ['f5'],
refreshList2: ['shift', 'f5']
}
}
},
methods: {
formatPrice,
formatQuantity,
shortcutKeyMethod(event) {
switch (event.srcKey) {
case 'refreshList':
case 'refreshList2':
this.$store.dispatch('listProductPriceFromServer', {})
break
}
},
localSearch(stringToMatch, callBack) {
if (this.isEmptyValue(stringToMatch)) {
// not show list
callBack([])
return
}
let results = this.listWithPrice
if (stringToMatch) {
const parsedValue = stringToMatch.toLowerCase().trim()
results = results.filter(rowProduct => {
const productAttributes = rowProduct.product
for (const columnProductPrice in productAttributes) {
const valueToCompare = String(productAttributes[columnProductPrice]).toLowerCase()
if (valueToCompare.includes(parsedValue)) {
return true
}
}
return false
})
// Remote search
if (this.isEmptyValue(results) && String(stringToMatch.length > 3)) {
clearTimeout(this.timeOut)
this.timeOut = setTimeout(() => {
this.$store.dispatch('listProductPriceFromServer', {
pageNumber: 1,
searchValue: stringToMatch
})
.then(() => {
const recordsList = this.listWithPrice
if (this.isEmptyValue(recordsList)) {
this.$message({
message: 'Sin resultados coincidentes con la busqueda',
type: 'info',
showClose: true
})
}
callBack(recordsList)
})
}, 2000)
return
}
}
// call callback function to return suggestions
callBack(results)
},
handleSelect(elementSelected) {
const valueProduct = elementSelected.product.value
this.$store.dispatch('notifyActionKeyPerformed', {
containerUuid: 'POS',
columnName: 'ProductValue',
// TODO: Verify with 'value' or 'searchValue' attribute
value: valueProduct
})
}
}
}
</script>
<style lang="scss" scope>
.custom-field-prodcut-info {
li {
line-height: normal;
padding: 15px;
.header {
text-overflow: ellipsis;
overflow: hidden;
}
.upc {
color: #7e7e7e;
padding-top: 10px;
float: left;
}
.description {
padding-top: 10px;
float: left;
}
.price {
color: #7e7e7e;
padding-top: 10px;
float: right;
padding-right: 10px;
}
.quantityAvailable {
float: right;
padding-top: 10px;
}
}
}
</style>

View File

@ -0,0 +1,208 @@
<template>
<el-main
v-shortkey="shortsKey"
@shortkey.native="keyAction"
>
<el-form
v-shortkey="shortsKey"
label-position="top"
label-width="10px"
@shortkey.native="keyAction"
@submit.native.prevent="notSubmitForm"
>
<field-definition
v-for="(field) in fieldsList"
:key="field.columnName"
:metadata-field="field"
/>
</el-form>
<el-table
ref="listProducto"
v-shortkey="shortsKey"
v-loading="!productPrice.isLoaded"
:data="listWithPrice"
border
fit
height="450"
highlight-current-row
@row-click="findlistProductWithRow"
@shortkey.native="keyAction"
>
<el-table-column
prop="product.value"
label="Codigo"
/>
<el-table-column
prop="product.name"
label="Producto"
/>
<el-table-column
prop="priceListName"
label="Lista de Precio"
/>
<el-table-column
label="Precio"
align="right"
>
<template slot-scope="scope">
{{ formatPrice(scope.row.priceStandard) }}
</template>
</el-table-column>
</el-table>
<custom-pagination
:total="productPrice.recordCount"
:current-page="productPrice.pageNumber"
:handle-change-page="handleChangePage"
/>
</el-main>
</template>
<script>
import formMixin from '@/components/ADempiere/Form/formMixin.js'
import CustomPagination from '@/components/ADempiere/Pagination'
import fieldsListProductPrice from './fieldsList.js'
import { formatPrice } from '@/utils/ADempiere/valueFormat.js'
export default {
name: 'ProductList',
components: {
CustomPagination
},
mixins: [
formMixin
],
props: {
metadata: {
type: Object,
default: () => {
return {
uuid: 'Products-Price-List',
containerUuid: 'Products-Price-List'
}
}
},
isSelectable: {
type: Boolean,
default: true
},
popoverName: {
type: String,
default: 'isShowPopoverField'
}
},
data() {
return {
defaultMaxPagination: 50,
fieldsList: fieldsListProductPrice,
isCustomForm: true,
timeOut: null
}
},
computed: {
isShowProductsPriceList() {
return this.$store.state['pointOfSales/listProductPrice'].productPrice[this.attribute]
},
currentPoint() {
return this.$store.getters.getCurrentPOS
},
productPrice() {
return this.$store.getters.getProductPrice
},
listWithPrice() {
const { productPricesList } = this.productPrice
if (!this.isEmptyValue(productPricesList)) {
return productPricesList
}
return []
},
shortsKey() {
return {
closeProductList: ['esc'],
refreshList: ['f5']
}
},
isReadyFromGetData() {
const { isLoaded, isReload } = this.productPrice
return (!isLoaded || isReload) // && this.isShowProductsPriceList
}
},
watch: {
isReadyFromGetData(isToLoad) {
if (isToLoad) {
this.loadProductsPricesList()
}
}
},
created() {
this.unsubscribe = this.subscribeChanges()
if (this.isReadyFromGetData) {
this.loadProductsPricesList()
}
},
beforeDestroy() {
this.unsubscribe()
},
methods: {
formatPrice,
keyAction(event) {
switch (event.srcKey) {
case 'refreshList':
/**
* TODO: When refreshing you are making 2 list requests, you can be the
* observer that activates the second request
*/
this.loadProductsPricesList()
break
case 'closeProductList':
this.$store.commit('showListProductPrice', {
attribute: this.popoverName,
isShowed: false
})
break
}
},
loadProductsPricesList() {
this.$store.dispatch('listProductPriceFromServer', {})
},
/**
* @param {number} newPage
*/
handleChangePage(newPage) {
this.$store.dispatch('setProductPicePageNumber', newPage)
},
findlistProductWithRow(row) {
if (!this.isSelectable) {
return
}
// TODO: Change this dispatch for set values with local methods, to delete subscripton
this.$store.dispatch('notifyActionKeyPerformed', {
containerUuid: 'POS',
columnName: 'ProductValue',
// TODO: Verify with 'value' or 'searchValue' attribute
value: row.product.name
})
// close popover of list product price
this.$store.commit('showListProductPrice', {
attribute: this.popoverName,
isShowed: false
})
},
subscribeChanges() {
return this.$store.subscribe((mutation, state) => {
if (mutation.type === 'updateValueOfField' &&
!mutation.payload.columnName.includes('DisplayColumn') &&
mutation.payload.containerUuid === this.metadata.containerUuid) {
clearTimeout(this.timeOut)
this.timeOut = setTimeout(() => {
this.$store.commit('setIsReloadProductPrice')
}, 1000)
}
})
}
}
}
</script>

View File

@ -0,0 +1,271 @@
<template>
<el-container style="height: 100% !important;">
<el-main style="padding-right: 0px;padding-bottom: 0px;">
<Split :gutter-size="isShowedPOSOptions ? 10 : 0" @onDrag="onDragOption">
<SplitArea :size="isShowedPOSOptions ? 20 : 1" :min-size="400">
<el-container style="height: 100% !important;">
<el-aside :width="isShowedPOSOptions ? '100%' : '0%'" style="background: white; padding: 0px !important; margin-bottom: 0px">
<options
:metadata="metadata"
/>
</el-aside>
<div style="width: 36px;padding-top: 30vh; z-index: 100;">
<el-button
:circle="true"
type="primary"
:icon="isShowedPOSOptions ? 'el-icon-arrow-left' : 'el-icon-arrow-right'"
:style="isShowedPOSOptions ? 'position: absolute;': 'position: absolute;left: 0.8%;'"
@click="isShowedPOSOptions = !isShowedPOSOptions"
/>
</div>
</el-container>
</SplitArea>
<SplitArea :size="isShowedPOSOptions ? 80 : 99" :min-size="990">
<Split :gutter-size="isShowedPOSKeyLaout ? 10 : 0" @onDrag="onDragKeyLayout">
<SplitArea :size="isShowedPOSKeyLaout ? 69 : 99" :min-size="900" style="overflow: hidden">
<order
:metadata="metadata"
/>
</SplitArea>
<SplitArea
v-show="isShowedPOSKeyLaout"
:size="isShowedPOSKeyLaout ? 31: 1"
:min-size="300"
style="overflow: auto"
>
<key-layout
v-if="!showCollection"
key="layout-component"
/>
<collection
v-else
key="collection-component"
/>
</SplitArea>
</Split>
</SplitArea>
</Split>
</el-main>
</el-container>
</template>
<script>
import Order from '@/components/ADempiere/Form/VPOS/Order'
import KeyLayout from '@/components/ADempiere/Form/VPOS/KeyLayout'
import Options from '@/components/ADempiere/Form/VPOS/Options'
import Collection from '@/components/ADempiere/Form/VPOS/Collection'
export default {
name: 'VPOS',
components: {
Order,
KeyLayout,
Options,
Collection
},
props: {
metadata: {
type: Object,
required: true
}
},
data() {
return {
unsubscribePOSList: () => {}
}
},
computed: {
// options to POS, panel left
isShowedPOSOptions: {
get() {
return this.$store.getters.getIsShowPOSOptions
},
set(val) {
this.$store.commit('setShowPOSOptions', val)
}
},
isShowedPOSKeyLaout() {
return this.$store.getters.getShowPOSKeyLayout
},
showCollection() {
return this.$store.getters.getShowCollectionPos
},
pointOfSalesId() {
const currentPOS = this.$store.getters.getCurrentPOS
if (currentPOS && !this.isEmptyValue(currentPOS.id)) {
return currentPOS.id
}
return undefined
}
},
watch: {
isShowedPOSOptions(value) {
if (value) {
if (this.isShowedPOSKeyLaout) {
this.$store.dispatch('changeWidthRight', 3)
}
} else {
this.$store.dispatch('changeWidthRight', 3)
}
}
},
created() {
// load pont of sales list
if (this.isEmptyValue(this.$store.getters.getSellingPointsList)) {
let posToSet
// set pos id with query path
if (!this.isEmptyValue(this.$route.query) && !this.isEmptyValue(this.$route.query.pos)) {
posToSet = Number(this.$route.query.pos)
}
this.$store.dispatch('listPointOfSalesFromServer', posToSet)
}
this.unsubscribePOSList = this.posListWithOrganization()
if (!this.isEmptyValue(this.$route.query.action)) {
this.$store.dispatch('findOrderServer', this.$route.query.action)
}
},
mounted() {
if (this.isEmptyValue(this.$route.query) || this.isEmptyValue(this.$route.query.pos)) {
this.$router.push({
params: {
...this.$route.params
},
query: {
...this.$route.query,
pos: this.pointOfSalesId
}
}, () => {})
}
},
beforeDestroy() {
this.unsubscribePOSList()
},
methods: {
posListWithOrganization() {
return this.$store.subscribe((mutation, state) => {
if (mutation.type === 'user/SET_ORGANIZATION') {
this.$store.dispatch('listPointOfSalesFromServer')
}
})
},
onDragKeyLayout(size) {
const sizeWidthRight = size[1] / 10
this.$store.dispatch('changeWidthRight', Math.trunc(sizeWidthRight))
},
onDragOption(size) {
const sizeWidthLeft = size[0] / 10
this.$store.dispatch('changeWidthLeft', Math.trunc(sizeWidthLeft))
}
}
}
</script>
<style scoped>
.split {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
overflow-y: hidden;
overflow-x: hidden;
height: 100%;
width: 100%;
}
.el-card__body {
padding-top: 0px !important;
padding-right: 0px!important;
padding-bottom: 20px;
padding-left: 10px!important;
height: 100%!important;
}
/* Style of Table */
.el-table {
position: relative;
overflow: hidden;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-flex: 1;
-ms-flex: 1;
flex: 1;
width: 100%;
max-width: 100%;
height: 100%;
background-color: #FFFFFF;
font-size: 14px;
color: #606266;
}
.el-card__header {
padding: 0px 20px;
border-bottom: 1px solid #e6ebf5;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.time {
font-size: 13px;
color: #999;
}
.bottom {
margin-top: 13px;
line-height: 12px;
}
.button {
padding: 0;
float: right;
}
.image {
width: 100%;
display: block;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
.el-header {
background: 'white';
color: #333;
line-height: 10px;
}
.el-aside {
color: #333;
}
.el-row {
margin: 0px!important;
}
.el-col {
border-radius: 4px;
}
.bg-purple-dark {
background: #99a9bf;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
background: #e5e9f2;
}
.grid-content {
border-radius: 4px;
min-height: 36px;
}
.row-bg {
padding: 10px 0;
background-color: #f9fafc;
}
.order-header {
padding-left: 10px;
font-size: 13px;
}
</style>

View File

@ -0,0 +1,444 @@
import {
findProduct,
requestCreateOrder,
requestGetOrder,
requestUpdateOrder
} from '@/api/ADempiere/form/point-of-sales.js'
import {
formatDate,
formatPrice,
formatQuantity
} from '@/utils/ADempiere/valueFormat.js'
export default {
name: 'POSMixin',
props: {
metadata: {
type: Object,
required: false
}
},
data() {
return {
product: {},
order: {
documentType: {},
documentStatus: {
value: ''
},
totalLines: 0,
grandTotal: 0,
salesRepresentative: {},
businessPartner: {
value: ''
}
},
currentTable: 0,
currentOrderLine: {
product: {
value: 0,
name: '',
description: '',
priceStandard: 0
},
taxIndicator: 0,
quantityOrdered: 0
},
orderLines: [],
products: {
uuid: '',
quantityAvailable: 0
},
edit: false,
displayType: ''
}
},
computed: {
allOrderLines() {
if (this.isEmptyValue(this.listOrderLine)) {
return []
}
return this.listOrderLine
},
listOrderLine() {
return this.$store.getters.getListOrderLine
},
ordersList() {
const order = this.$store.getters.getListOrder
if (order && !this.isEmptyValue(order.ordersList)) {
return order.ordersList
}
return []
},
currentOrder() {
const action = this.$route.query.action
if (!this.isEmptyValue(action)) {
const order = this.ordersList.find(item => item.uuid === action)
if (!this.isEmptyValue(order)) {
return order
}
}
return this.$store.getters.getFindOrder
},
currentPoint() {
return this.$store.getters.getCurrentPOS
},
priceListUuid() {
const currentPOS = this.currentPoint
if (this.isEmptyValue(currentPOS)) {
return undefined
}
return this.currentPoint.priceList.uuid
},
getWarehouse() {
return this.$store.getters['user/getWarehouse']
},
isSetTemplateBP() {
const currentPOS = this.currentPoint
if (!this.isEmptyValue(currentPOS) &&
!this.isEmptyValue(currentPOS.templateBusinessPartner) &&
this.isEmptyValue(this.$route.query.action)) {
return currentPOS.templateBusinessPartner
}
return false
}
},
watch: {
currentOrder(value) {
if (this.isEmptyValue(value)) {
this.orderLines = []
this.order = {
documentType: {},
documentStatus: {},
salesRepresentative: {}
}
this.$store.dispatch('listOrderLine', [])
this.listOrderLines({})
} else {
this.fillOrder(value)
this.listOrderLines(value)
}
},
/**
* Used when loading/reloading the app without the order uuid
* @param {oject|boolean} bPartnerToSet
*/
isSetTemplateBP(bPartnerToSet) {
if (bPartnerToSet) {
this.setBusinessPartner(bPartnerToSet)
}
}
},
created() {
this.getPanel()
},
beforeMount() {
if (!this.isEmptyValue(this.currentPoint)) {
if (!this.isEmptyValue(this.currentOrder)) {
this.fillOrder(this.currentOrder)
this.listOrderLines(this.currentOrder)
}
}
this.unsubscribe = this.subscribeChanges()
},
beforeDestroy() {
this.unsubscribe()
},
methods: {
formatDate,
formatPrice,
formatQuantity,
withoutPOSTerminal() {
if (this.isEmptyValue(this.currentPoint)) {
this.$message({
type: 'warn',
message: 'Without POS Terminal',
showClose: true
})
return true
}
return false
},
arrowTop() {
if (this.currentTable > 0) {
this.currentTable--
this.$refs.linesTable.setCurrentRow(this.listOrderLine[this.currentTable])
this.currentOrderLine = this.listOrderLine[this.currentTable]
}
},
arrowBottom() {
const top = this.listOrderLine.length - 1
if (this.currentTable < top) {
this.currentTable++
this.$refs.linesTable.setCurrentRow(this.listOrderLine[this.currentTable])
this.currentOrderLine = this.listOrderLine[this.currentTable]
}
},
updateOrder(update) {
if (this.withoutPOSTerminal()) {
return
}
if (!this.$route.query || this.isEmptyValue(this.$route.query.action)) {
return
}
const { uuid: posUuid } = this.currentPoint
let customerUuid
if (update.columnName === 'C_BPartner_ID_UUID') {
customerUuid = update.value
if (this.isEmptyValue(customerUuid)) {
customerUuid = this.currentPoint.templateBusinessPartner.uuid
}
}
requestUpdateOrder({
orderUuid: this.$route.query.action,
posUuid,
customerUuid
// documentTypeUuid: value.value,
// description
})
.then(response => {
// this.reloadOrder(true)
})
.catch(error => {
console.error(error.message)
this.$message({
type: 'error',
message: error.message,
showClose: true
})
})
},
setBusinessPartner({ name, id, uuid }) {
// Use update values of container (without subscription)
this.$store.commit('updateValuesOfContainer', {
parentUuid: this.parentUuid,
containerUuid: this.containerUuid,
attributes: [{
columnName: 'C_BPartner_ID',
value: id
},
{
columnName: 'DisplayColumn_C_BPartner_ID',
value: name
},
{
columnName: ' C_BPartner_ID_UUID',
value: uuid
}]
})
},
findProduct(searchValue) {
if (this.withoutPOSTerminal()) {
return
}
const searchProduct = (typeof searchValue === 'object') ? searchValue.value : searchValue
findProduct({
searchValue: searchProduct,
priceListUuid: this.priceListUuid
})
.then(productPrice => {
this.product = productPrice.product
this.createOrder(true)
})
.catch(error => {
console.warn(error.message)
this.$message({
type: 'info',
message: error.message,
showClose: true
})
this.$store.commit('updateValueOfField', {
containerUuid: 'Products-Price-List',
columnName: 'ProductValue',
value: `${searchProduct}`
})
this.$store.commit('showListProductPrice', {
attribute: 'isShowPopoverField',
isShowed: true
})
})
.finally(() => {
this.$store.commit('updateValuesOfContainer', {
containerUuid: this.metadata.containerUuid,
attributes: [{
columnName: 'ProductValue',
value: undefined
}]
})
})
},
createOrder(withLine) {
if (this.withoutPOSTerminal()) {
return
}
const orderUuid = this.$route.query.action
if (this.isEmptyValue(orderUuid)) {
const posUuid = this.currentPoint.uuid
let customerUuid = this.$store.getters.getValueOfField({
containerUuid: this.containerUuid,
columnName: 'C_BPartner_ID_UUID'
})
if (this.isEmptyValue(customerUuid)) {
customerUuid = this.currentPoint.templateBusinessPartner.uuid
}
// user session
const salesRepresentativeUuid = this.$store.getters['user/getUserUuid']
requestCreateOrder({
posUuid,
customerUuid,
salesRepresentativeUuid
})
.then(order => {
this.$store.dispatch('currentOrder', order)
this.fillOrder(order)
this.$router.push({
params: {
...this.$route.params
},
query: {
...this.$route.query,
action: order.uuid
}
}).then(() => {
if (withLine) {
this.createOrderLine(order.uuid)
}
}).catch(() => {})
// update orders list
this.$store.commit('setIsReloadListOrders')
})
.catch(error => {
console.error(error.message)
this.$message({
type: 'error',
message: error.message,
showClose: true
})
})
} else {
this.createOrderLine(orderUuid)
}
},
reloadOrder(requery, orderUuid) {
if (requery) {
if (this.isEmptyValue(orderUuid)) {
orderUuid = this.$route.query.action
if (this.isEmptyValue(orderUuid)) {
orderUuid = this.$store.getters.getOrder.uuid // this.currentOrder.uuid
}
}
if (!this.isEmptyValue(orderUuid)) {
requestGetOrder(orderUuid)
.then(orderResponse => {
this.fillOrder(orderResponse)
this.$store.dispatch('currentOrder', orderResponse)
this.listOrderLines(orderResponse)
})
.catch(error => {
this.$message({
type: 'error',
message: error.message,
showClose: true
})
})
}
} else {
this.fillOrder(this.currentOrder, false)
}
},
fillOrder(order, setToStore = true) {
const orderToPush = {
uuid: order.uuid,
id: order.id,
businessPartner: order.businessPartner, // description, duns, id, lastName, naics, name, taxId, uuid, value
documentNo: order.documentNo,
dateOrdered: order.dateOrdered,
documentStatus: order.documentStatus, // value, name, description
documentType: order.documentType, // name, printName
salesRepresentative: order.salesRepresentative, // id, uuid, name, description,
totalLines: order.totalLines,
grandTotal: order.grandTotal
}
if (setToStore) {
this.$store.dispatch('setOrder', {
...orderToPush
})
}
if (!this.isEmptyValue(order.businessPartner)) {
const { businessPartner } = order
this.setBusinessPartner(businessPartner)
}
this.order = orderToPush
},
getOrderTax(currency) {
if (this.isEmptyValue(this.order)) {
return undefined
}
return this.formatPrice(this.order.grandTotal - this.order.totalLines, currency)
},
subscribeChanges() {
return this.$store.subscribe((mutation, state) => {
// TODO: Add container uuid comparison
if (mutation.type === 'addActionKeyPerformed') {
switch (mutation.payload.columnName) {
case 'ProductValue':
this.findProduct(mutation.payload.value)
break
}
} else if (mutation.type === 'addActionPerformed') {
switch (mutation.payload.columnName) {
case 'QtyEntered':
case 'PriceEntered':
case 'Discount':
if (!this.isEmptyValue(this.currentOrderLine)) {
this.updateOrderLine(mutation.payload)
}
break
//
case 'C_DocType_ID':
this.updateOrder(mutation.payload)
break
}
} else if (mutation.type === 'updateValueOfField') {
// if (this.metadata.containerUuid === mutation.payload.containerUuid &&
// mutation.payload.columnName === 'ProductValue') {
// this.findProduct(mutation.payload.value)
// }
switch (mutation.payload.columnName) {
case 'DisplayColumn_TenderType':
this.displayType = mutation.payload.value
break
case 'C_BPartner_ID_UUID': {
const bPartnerValue = mutation.payload.value
const bPartnerPOS = this.currentPoint.templateBusinessPartner.uuid
// Does not send values to server, when empty values are set or
// if BPartner set equal to BPartner POS template
if (this.isEmptyValue(bPartnerValue) || bPartnerValue === bPartnerPOS) {
break
}
this.updateOrder(mutation.payload)
break
}
}
}
})
}
}
}

View File

@ -55,7 +55,7 @@ export default {
async getPanel() {
const panel = this.getterPanel
if (!this.isEmptyValue(panel)) {
this.fieldsList = panel.fieldList
this.fieldsList = panel.fieldsList
this.isLoaded = true
this.panelMetadata = panel
} else {
@ -65,14 +65,14 @@ export default {
isCustomForm: this.isCustomForm,
uuid: this.containerUuid,
panelType: this.panelType,
fieldList: this.fieldsList
fieldsList: this.fieldsList
})
.then(responsePanel => {
this.fieldsList = responsePanel.fieldList
this.fieldsList = responsePanel.fieldsList
this.$store.dispatch('changeFormAttribute', {
containerUuid: this.containerUuid,
attributeName: 'fieldList',
attributeName: 'fieldsList',
attributeValue: this.fieldsList
})
this.panelMetadata = responsePanel

View File

@ -22,6 +22,9 @@ export default {
case 'PriceChecking':
form = import('@/components/ADempiere/Form/PriceChecking')
break
case 'VPOS':
form = import('@/components/ADempiere/Form/VPOS')
break
default:
form = import('@/views/ADempiere/Unsupported')
break

View File

@ -2,9 +2,9 @@
<el-footer style="height: 30px;">
<div style="float: right;">
<el-pagination
:current-page="currentPage"
small
layout="slot, total, prev, pager, next"
:current-page="currentPage"
:page-size="pageSize"
:total="total"
@current-change="handleChangePage"

View File

@ -10,7 +10,7 @@
@change="addField"
>
<el-option
v-for="(item, key) in getterFieldListOptional"
v-for="(item, key) in fieldsListOptional"
:key="key"
:label="item.name"
:value="item.columnName"
@ -48,7 +48,7 @@ export default {
isMobile() {
return this.$store.state.app.device === 'mobile'
},
getterFieldListOptional() {
fieldsListOptional() {
if (this.panelType === 'table') {
// fields to search without taking into account the mandatory
return this.$store.getters.getFieldsListFromPanel(this.containerUuid, this.isAdvancedQuery)
@ -66,7 +66,7 @@ export default {
return this.$store.getters.getFieldsListNotMandatory({ containerUuid: this.containerUuid })
},
getFieldSelected() {
return this.getterFieldListOptional
return this.fieldsListOptional
.filter(fieldItem => {
return fieldItem.isShowedFromUser
})

Some files were not shown because too many files have changed in this diff Show More