Merge branch 'dev' into 5468-account_privileges
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
f37974c835
|
@ -8,13 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
## [2326.01] - 2023-06-29
|
## [2326.01] - 2023-06-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- (Entradas -> Correo) Al cambiar el tipo de cambio enviará un correo a las personas designadas
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
-
|
-
|
||||||
|
|
||||||
## [2324.01] - 2023-06-08
|
## [2324.01] - 2023-06-15
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- (Tickets -> Abono) Al abonar permite crear el ticket abono con almacén o sin almmacén
|
- (Tickets -> Abono) Al abonar permite crear el ticket abono con almacén o sin almmacén
|
||||||
|
|
|
@ -8,7 +8,7 @@ Salix is also the scientific name of a beautifull tree! :)
|
||||||
|
|
||||||
Required applications.
|
Required applications.
|
||||||
|
|
||||||
* Node.js = 14.x LTS
|
* Node.js >= 16.x LTS
|
||||||
* Docker
|
* Docker
|
||||||
* Git
|
* Git
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ $ npm run test:e2e
|
||||||
|
|
||||||
Open Visual Studio Code, press Ctrl+P and paste the following commands.
|
Open Visual Studio Code, press Ctrl+P and paste the following commands.
|
||||||
|
|
||||||
In Visual Studio Code we use the ESLint extension.
|
In Visual Studio Code we use the ESLint extension.
|
||||||
```
|
```
|
||||||
ext install dbaeumer.vscode-eslint
|
ext install dbaeumer.vscode-eslint
|
||||||
```
|
```
|
||||||
|
|
|
@ -2,8 +2,9 @@ const models = require('vn-loopback/server/server').models;
|
||||||
|
|
||||||
describe('docuware upload()', () => {
|
describe('docuware upload()', () => {
|
||||||
const userId = 9;
|
const userId = 9;
|
||||||
const ticketId = 10;
|
const ticketIds = [10];
|
||||||
const ctx = {
|
const ctx = {
|
||||||
|
args: {ticketIds},
|
||||||
req: {
|
req: {
|
||||||
getLocale: () => {
|
getLocale: () => {
|
||||||
return 'en';
|
return 'en';
|
||||||
|
@ -27,7 +28,7 @@ describe('docuware upload()', () => {
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
try {
|
try {
|
||||||
await models.Docuware.upload(ctx, ticketId, fileCabinetName);
|
await models.Docuware.upload(ctx, ticketIds, fileCabinetName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e.message;
|
error = e.message;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,34 +3,34 @@ const axios = require('axios');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('upload', {
|
Self.remoteMethodCtx('upload', {
|
||||||
description: 'Upload an docuware PDF',
|
description: 'Upload docuware PDFs',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'id',
|
arg: 'ticketIds',
|
||||||
type: 'number',
|
type: ['number'],
|
||||||
description: 'The ticket id',
|
description: 'The ticket ids',
|
||||||
http: {source: 'path'}
|
required: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'fileCabinet',
|
arg: 'fileCabinet',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'The file cabinet'
|
description: 'The file cabinet',
|
||||||
},
|
required: true
|
||||||
{
|
|
||||||
arg: 'dialog',
|
|
||||||
type: 'string',
|
|
||||||
description: 'The dialog'
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
returns: [],
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
http: {
|
http: {
|
||||||
path: `/:id/upload`,
|
path: `/upload`,
|
||||||
verb: 'POST'
|
verb: 'POST'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.upload = async function(ctx, id, fileCabinet) {
|
Self.upload = async function(ctx, ticketIds, fileCabinet) {
|
||||||
|
delete ctx.args.ticketIds;
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const action = 'store';
|
const action = 'store';
|
||||||
|
|
||||||
|
@ -38,104 +38,114 @@ module.exports = Self => {
|
||||||
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
|
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
|
||||||
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
|
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
|
||||||
|
|
||||||
// get delivery note
|
const uploaded = [];
|
||||||
const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, {
|
for (id of ticketIds) {
|
||||||
id,
|
// get delivery note
|
||||||
type: 'deliveryNote'
|
ctx.args.id = id;
|
||||||
});
|
const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, {
|
||||||
|
id,
|
||||||
// get ticket data
|
type: 'deliveryNote'
|
||||||
const ticket = await models.Ticket.findById(id, {
|
|
||||||
include: [{
|
|
||||||
relation: 'client',
|
|
||||||
scope: {
|
|
||||||
fields: ['id', 'socialName', 'fi']
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
|
|
||||||
// upload file
|
|
||||||
const templateJson = {
|
|
||||||
'Fields': [
|
|
||||||
{
|
|
||||||
'FieldName': 'N__ALBAR_N',
|
|
||||||
'ItemElementName': 'string',
|
|
||||||
'Item': id,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'FieldName': 'CIF_PROVEEDOR',
|
|
||||||
'ItemElementName': 'string',
|
|
||||||
'Item': ticket.client().fi,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'FieldName': 'CODIGO_PROVEEDOR',
|
|
||||||
'ItemElementName': 'string',
|
|
||||||
'Item': ticket.client().id,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'FieldName': 'NOMBRE_PROVEEDOR',
|
|
||||||
'ItemElementName': 'string',
|
|
||||||
'Item': ticket.client().socialName,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'FieldName': 'FECHA_FACTURA',
|
|
||||||
'ItemElementName': 'date',
|
|
||||||
'Item': ticket.shipped,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'FieldName': 'TOTAL_FACTURA',
|
|
||||||
'ItemElementName': 'Decimal',
|
|
||||||
'Item': ticket.totalWithVat,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'FieldName': 'ESTADO',
|
|
||||||
'ItemElementName': 'string',
|
|
||||||
'Item': 'Pendiente procesar',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'FieldName': 'FIRMA_',
|
|
||||||
'ItemElementName': 'string',
|
|
||||||
'Item': 'Si',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'FieldName': 'FILTRO_TABLET',
|
|
||||||
'ItemElementName': 'string',
|
|
||||||
'Item': 'Tablet1',
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (process.env.NODE_ENV != 'production')
|
|
||||||
throw new UserError('Action not allowed on the test environment');
|
|
||||||
|
|
||||||
// delete old
|
|
||||||
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false);
|
|
||||||
if (docuwareFile) {
|
|
||||||
const deleteJson = {
|
|
||||||
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
|
|
||||||
};
|
|
||||||
const deleteUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
|
|
||||||
await axios.put(deleteUri, deleteJson, options.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
const uploadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`;
|
|
||||||
const FormData = require('form-data');
|
|
||||||
const data = new FormData();
|
|
||||||
|
|
||||||
data.append('document', JSON.stringify(templateJson), 'schema.json');
|
|
||||||
data.append('file[]', deliveryNote[0], 'file.pdf');
|
|
||||||
const uploadOptions = {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data',
|
|
||||||
'X-File-ModifiedDate': Date.vnNew(),
|
|
||||||
'Cookie': options.headers.headers.Cookie,
|
|
||||||
...data.getHeaders()
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return await axios.post(uploadUri, data, uploadOptions)
|
|
||||||
.catch(() => {
|
|
||||||
throw new UserError('Failed to upload file');
|
|
||||||
});
|
});
|
||||||
|
// get ticket data
|
||||||
|
const ticket = await models.Ticket.findById(id, {
|
||||||
|
include: [{
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name', 'fi']
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// upload file
|
||||||
|
const templateJson = {
|
||||||
|
'Fields': [
|
||||||
|
{
|
||||||
|
'FieldName': 'N__ALBAR_N',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'CIF_PROVEEDOR',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': ticket.client().fi,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'CODIGO_PROVEEDOR',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': ticket.client().id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'NOMBRE_PROVEEDOR',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': ticket.client().name + ' - ' + id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'FECHA_FACTURA',
|
||||||
|
'ItemElementName': 'date',
|
||||||
|
'Item': ticket.shipped,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'TOTAL_FACTURA',
|
||||||
|
'ItemElementName': 'Decimal',
|
||||||
|
'Item': ticket.totalWithVat,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'ESTADO',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': 'Pendiente procesar',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'FIRMA_',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': 'Si',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'FieldName': 'FILTRO_TABLET',
|
||||||
|
'ItemElementName': 'string',
|
||||||
|
'Item': 'Tablet1',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV != 'production')
|
||||||
|
throw new UserError('Action not allowed on the test environment');
|
||||||
|
|
||||||
|
// delete old
|
||||||
|
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, false);
|
||||||
|
if (docuwareFile) {
|
||||||
|
const deleteJson = {
|
||||||
|
'Field': [{'FieldName': 'ESTADO', 'Item': 'Pendiente eliminar', 'ItemElementName': 'String'}]
|
||||||
|
};
|
||||||
|
const deleteUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/Fields`;
|
||||||
|
await axios.put(deleteUri, deleteJson, options.headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents?StoreDialogId=${dialogId}`;
|
||||||
|
const FormData = require('form-data');
|
||||||
|
const data = new FormData();
|
||||||
|
|
||||||
|
data.append('document', JSON.stringify(templateJson), 'schema.json');
|
||||||
|
data.append('file[]', deliveryNote[0], 'file.pdf');
|
||||||
|
const uploadOptions = {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
'X-File-ModifiedDate': Date.vnNew(),
|
||||||
|
'Cookie': options.headers.headers.Cookie,
|
||||||
|
...data.getHeaders()
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post(uploadUri, data, uploadOptions);
|
||||||
|
} catch (err) {
|
||||||
|
const $t = ctx.req.__;
|
||||||
|
const message = $t('Failed to upload delivery note', {id});
|
||||||
|
if (uploaded.length)
|
||||||
|
await models.TicketTracking.setDelivered(ctx, uploaded);
|
||||||
|
throw new UserError(message);
|
||||||
|
}
|
||||||
|
uploaded.push(id);
|
||||||
|
}
|
||||||
|
return models.TicketTracking.setDelivered(ctx, ticketIds);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
const UserError = require('vn-loopback/util/user-error');
|
||||||
|
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethodCtx('renewToken', {
|
||||||
|
description: 'Checks if the token has more than renewPeriod seconds to live and if so, renews it',
|
||||||
|
accepts: [],
|
||||||
|
returns: {
|
||||||
|
type: 'Object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/renewToken`,
|
||||||
|
verb: 'POST'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Self.renewToken = async function(ctx) {
|
||||||
|
const models = Self.app.models;
|
||||||
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
const created = ctx.req.accessToken.created;
|
||||||
|
const tokenId = ctx.req.accessToken.id;
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const differenceMilliseconds = now - created;
|
||||||
|
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
|
||||||
|
|
||||||
|
const accessTokenConfig = await models.AccessTokenConfig.findOne({fields: ['renewPeriod']});
|
||||||
|
|
||||||
|
if (differenceSeconds <= accessTokenConfig.renewPeriod)
|
||||||
|
throw new UserError(`The renew period has not been exceeded`);
|
||||||
|
|
||||||
|
await Self.logout(tokenId);
|
||||||
|
const user = await Self.findById(userId);
|
||||||
|
const accessToken = await user.createAccessToken();
|
||||||
|
|
||||||
|
return {token: accessToken.id, created: accessToken.created};
|
||||||
|
};
|
||||||
|
};
|
|
@ -62,10 +62,9 @@ module.exports = Self => {
|
||||||
scopes: ['change-password'],
|
scopes: ['change-password'],
|
||||||
userId: vnUser.id
|
userId: vnUser.id
|
||||||
});
|
});
|
||||||
throw new UserError('Pass expired', 'passExpired', {
|
const err = new UserError('Pass expired', 'passExpired');
|
||||||
id: vnUser.id,
|
err.details = {token: changePasswordToken};
|
||||||
token: changePasswordToken.id
|
throw err;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -77,6 +76,6 @@ module.exports = Self => {
|
||||||
|
|
||||||
let loginInfo = Object.assign({password}, userInfo);
|
let loginInfo = Object.assign({password}, userInfo);
|
||||||
token = await Self.login(loginInfo, 'user');
|
token = await Self.login(loginInfo, 'user');
|
||||||
return {token: token.id};
|
return {token: token.id, created: token.created};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@ describe('VnUser signIn()', () => {
|
||||||
|
|
||||||
expect(login.token).toBeDefined();
|
expect(login.token).toBeDefined();
|
||||||
|
|
||||||
await models.VnUser.signOut(ctx);
|
await models.VnUser.logout(ctx.req.accessToken.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the token if the user doesnt exist but the client does', async() => {
|
it('should return the token if the user doesnt exist but the client does', async() => {
|
||||||
|
@ -19,7 +19,7 @@ describe('VnUser signIn()', () => {
|
||||||
|
|
||||||
expect(login.token).toBeDefined();
|
expect(login.token).toBeDefined();
|
||||||
|
|
||||||
await models.VnUser.signOut(ctx);
|
await models.VnUser.logout(ctx.req.accessToken.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
const {models} = require('vn-loopback/server/server');
|
|
||||||
|
|
||||||
describe('VnUser signOut()', () => {
|
|
||||||
it('should logout and remove token after valid login', async() => {
|
|
||||||
let loginResponse = await models.VnUser.signOut('buyer', 'nightmare');
|
|
||||||
let accessToken = await models.AccessToken.findById(loginResponse.token);
|
|
||||||
let ctx = {req: {accessToken: accessToken}};
|
|
||||||
|
|
||||||
let logoutResponse = await models.VnUser.signOut(ctx);
|
|
||||||
let tokenAfterLogout = await models.AccessToken.findById(loginResponse.token);
|
|
||||||
|
|
||||||
expect(logoutResponse).toBeTrue();
|
|
||||||
expect(tokenAfterLogout).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw a 401 error when token is invalid', async() => {
|
|
||||||
let error;
|
|
||||||
let ctx = {req: {accessToken: {id: 'invalidToken'}}};
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = await models.VnUser.signOut(ctx);
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
expect(error.statusCode).toBe(401);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error when no token is passed', async() => {
|
|
||||||
let error;
|
|
||||||
let ctx = {req: {accessToken: null}};
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = await models.VnUser.signOut(ctx);
|
|
||||||
} catch (e) {
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -2,6 +2,14 @@
|
||||||
"AccountingType": {
|
"AccountingType": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
"AccessTokenConfig": {
|
||||||
|
"dataSource": "vn",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "salix.accessTokenConfig"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Bank": {
|
"Bank": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"name": "AccessTokenConfig",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "accessTokenConfig"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "number",
|
||||||
|
"id": true,
|
||||||
|
"description": "Identifier"
|
||||||
|
},
|
||||||
|
"renewPeriod": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"renewInterval": {
|
||||||
|
"type": "number",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"acls": [{
|
||||||
|
"accessType": "READ",
|
||||||
|
"principalType": "ROLE",
|
||||||
|
"principalId": "$everyone",
|
||||||
|
"permission": "ALLOW"
|
||||||
|
}]
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ module.exports = function(Self) {
|
||||||
require('../methods/vn-user/recover-password')(Self);
|
require('../methods/vn-user/recover-password')(Self);
|
||||||
require('../methods/vn-user/validate-token')(Self);
|
require('../methods/vn-user/validate-token')(Self);
|
||||||
require('../methods/vn-user/privileges')(Self);
|
require('../methods/vn-user/privileges')(Self);
|
||||||
|
require('../methods/vn-user/renew-token')(Self);
|
||||||
|
|
||||||
Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create');
|
Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create');
|
||||||
|
|
||||||
|
|
|
@ -4,4 +4,4 @@ apps:
|
||||||
instances: 1
|
instances: 1
|
||||||
max_restarts: 3
|
max_restarts: 3
|
||||||
restart_delay: 15000
|
restart_delay: 15000
|
||||||
node_args: --tls-min-v1.0
|
node_args: --tls-min-v1.0 --openssl-legacy-provider
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`entry_updateComission`(vCurrency INT)
|
||||||
|
BEGIN
|
||||||
|
/**
|
||||||
|
* Actualiza la comision de las entradas de hoy a futuro y las recalcula
|
||||||
|
*
|
||||||
|
* @param vCurrency id del tipo de moneda(SAR,EUR,USD,GBP,JPY)
|
||||||
|
*/
|
||||||
|
DECLARE vCurrencyName VARCHAR(25);
|
||||||
|
DECLARE vComission INT;
|
||||||
|
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.recalcEntryCommision
|
||||||
|
SELECT e.id
|
||||||
|
FROM vn.entry e
|
||||||
|
JOIN vn.travel t ON t.id = e.travelFk
|
||||||
|
JOIN vn.warehouse w ON w.id = t.warehouseInFk
|
||||||
|
WHERE t.shipped >= util.VN_CURDATE()
|
||||||
|
AND e.currencyFk = vCurrency;
|
||||||
|
|
||||||
|
SET vComission = currency_getCommission(vCurrency);
|
||||||
|
|
||||||
|
UPDATE vn.entry e
|
||||||
|
JOIN tmp.recalcEntryCommision tmp ON tmp.id = e.id
|
||||||
|
SET e.commission = vComission;
|
||||||
|
|
||||||
|
SELECT `name` INTO vCurrencyName
|
||||||
|
FROM currency
|
||||||
|
WHERE id = vCurrency;
|
||||||
|
|
||||||
|
CALL entry_recalc();
|
||||||
|
SELECT util.notification_send(
|
||||||
|
'entry-update-comission',
|
||||||
|
JSON_OBJECT('currencyName', vCurrencyName, 'referenceCurrent', vComission),
|
||||||
|
account.myUser_getId()
|
||||||
|
);
|
||||||
|
|
||||||
|
DROP TEMPORARY TABLE tmp.recalcEntryCommision;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
|
@ -0,0 +1,71 @@
|
||||||
|
CREATE TABLE `vn`.`packingSiteAdvanced` (
|
||||||
|
`ticketFk` int(11),
|
||||||
|
`workerFk` int(10) unsigned,
|
||||||
|
PRIMARY KEY (`ticketFk`),
|
||||||
|
KEY `packingSiteAdvanced_FK_1` (`workerFk`),
|
||||||
|
CONSTRAINT `packingSiteAdvanced_FK` FOREIGN KEY (`ticketFk`) REFERENCES `ticket` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `packingSiteAdvanced_FK_1` FOREIGN KEY (`workerFk`) REFERENCES `worker` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
|
||||||
|
VALUES
|
||||||
|
('PackingSiteAdvanced', '*', '*', 'ALLOW', 'ROLE', 'production');
|
||||||
|
|
||||||
|
DROP PROCEDURE IF EXISTS `vn`.`packingSite_startCollection`;
|
||||||
|
|
||||||
|
DELIMITER $$
|
||||||
|
$$
|
||||||
|
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`packingSite_startCollection`(vSelf INT, vTicketFk INT)
|
||||||
|
proc: BEGIN
|
||||||
|
/**
|
||||||
|
* @param vSelf packingSite id
|
||||||
|
* @param vTicketFk A ticket id from the collection to start
|
||||||
|
*/
|
||||||
|
DECLARE vExists BOOL;
|
||||||
|
DECLARE vIsAdvanced BOOL;
|
||||||
|
DECLARE vNewCollectionFk INT;
|
||||||
|
DECLARE vOldCollectionFk INT;
|
||||||
|
DECLARE vIsPackingByOther BOOL;
|
||||||
|
|
||||||
|
SELECT id, collectionFk
|
||||||
|
INTO vExists, vOldCollectionFk
|
||||||
|
FROM packingSite
|
||||||
|
WHERE id = vSelf;
|
||||||
|
|
||||||
|
IF NOT vExists THEN
|
||||||
|
CALL util.throw('packingSiteNotExists');
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT COUNT(*) > 0
|
||||||
|
INTO vIsAdvanced
|
||||||
|
FROM packingSiteAdvanced
|
||||||
|
WHERE ticketFk = vTicketFk;
|
||||||
|
|
||||||
|
IF vIsAdvanced THEN
|
||||||
|
LEAVE proc;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT collectionFk INTO vNewCollectionFk
|
||||||
|
FROM ticketCollection WHERE ticketFk = vTicketFk;
|
||||||
|
|
||||||
|
IF vOldCollectionFk IS NOT NULL
|
||||||
|
AND vOldCollectionFk <> vNewCollectionFk THEN
|
||||||
|
SELECT COUNT(*) > 0
|
||||||
|
INTO vIsPackingByOther
|
||||||
|
FROM packingSite
|
||||||
|
WHERE id <> vSelf
|
||||||
|
AND collectionFk = vOldCollectionFk;
|
||||||
|
|
||||||
|
IF NOT vIsPackingByOther AND NOT collection_isPacked(vOldCollectionFk) THEN
|
||||||
|
CALL util.throw('cannotChangeCollection');
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
UPDATE packingSite SET collectionFk = vNewCollectionFk
|
||||||
|
WHERE id = vSelf;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
CREATE TABLE `salix`.`accessTokenConfig` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`renewPeriod` int(10) unsigned DEFAULT NULL,
|
||||||
|
`renewInterval` int(10) unsigned DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
|
||||||
|
|
||||||
|
INSERT IGNORE INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `renewInterval`)
|
||||||
|
VALUES
|
||||||
|
(1, 21600, 300);
|
|
@ -2736,7 +2736,8 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`)
|
||||||
(1, 'print-email', 'notification fixture one'),
|
(1, 'print-email', 'notification fixture one'),
|
||||||
(2, 'invoice-electronic', 'A electronic invoice has been generated'),
|
(2, 'invoice-electronic', 'A electronic invoice has been generated'),
|
||||||
(3, 'not-main-printer-configured', 'A printer distinct than main has been configured'),
|
(3, 'not-main-printer-configured', 'A printer distinct than main has been configured'),
|
||||||
(4, 'supplier-pay-method-update', 'A supplier pay method has been updated');
|
(4, 'supplier-pay-method-update', 'A supplier pay method has been updated'),
|
||||||
|
(5, 'modified-entry', 'An entry has been modified');
|
||||||
|
|
||||||
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
|
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
|
||||||
VALUES
|
VALUES
|
||||||
|
@ -2894,6 +2895,10 @@ INSERT INTO `vn`.`wagonTypeTray` (`id`, `typeFk`, `height`, `colorFk`)
|
||||||
(2, 1, 50, 2),
|
(2, 1, 50, 2),
|
||||||
(3, 1, 0, 3);
|
(3, 1, 0, 3);
|
||||||
|
|
||||||
|
INSERT INTO `salix`.`accessTokenConfig` (`id`, `renewPeriod`, `renewInterval`)
|
||||||
|
VALUES
|
||||||
|
(1, 21600, 300);
|
||||||
|
|
||||||
INSERT INTO `vn`.`travelConfig` (`id`, `warehouseInFk`, `warehouseOutFk`, `agencyFk`, `companyFk`)
|
INSERT INTO `vn`.`travelConfig` (`id`, `warehouseInFk`, `warehouseOutFk`, `agencyFk`, `companyFk`)
|
||||||
VALUES
|
VALUES
|
||||||
(1, 1, 1, 1, 442);
|
(1, 1, 1, 1, 442);
|
||||||
|
|
|
@ -311,9 +311,9 @@ export default {
|
||||||
},
|
},
|
||||||
clientDefaulter: {
|
clientDefaulter: {
|
||||||
anyClient: 'vn-client-defaulter tbody > tr',
|
anyClient: 'vn-client-defaulter tbody > tr',
|
||||||
firstClientName: 'vn-client-defaulter tbody > tr:nth-child(1) > td:nth-child(2) > span',
|
firstClientName: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(2) > span',
|
||||||
firstSalesPersonName: 'vn-client-defaulter tbody > tr:nth-child(1) > td:nth-child(3) > span',
|
firstSalesPersonName: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(3) > span',
|
||||||
firstObservation: 'vn-client-defaulter tbody > tr:nth-child(1) > td:nth-child(8) > vn-textarea[ng-model="defaulter.observation"]',
|
firstObservation: 'vn-client-defaulter tbody > tr:nth-child(2) > td:nth-child(8) > vn-textarea[ng-model="defaulter.observation"]',
|
||||||
allDefaulterCheckbox: 'vn-client-defaulter thead vn-multi-check',
|
allDefaulterCheckbox: 'vn-client-defaulter thead vn-multi-check',
|
||||||
addObservationButton: 'vn-client-defaulter vn-button[icon="icon-notes"]',
|
addObservationButton: 'vn-client-defaulter vn-button[icon="icon-notes"]',
|
||||||
observation: '.vn-dialog.shown vn-textarea[ng-model="$ctrl.defaulter.observation"]',
|
observation: '.vn-dialog.shown vn-textarea[ng-model="$ctrl.defaulter.observation"]',
|
||||||
|
@ -334,15 +334,15 @@ export default {
|
||||||
},
|
},
|
||||||
itemsIndex: {
|
itemsIndex: {
|
||||||
createItemButton: `vn-float-button`,
|
createItemButton: `vn-float-button`,
|
||||||
firstSearchResult: 'vn-item-index tbody tr:nth-child(1)',
|
firstSearchResult: 'vn-item-index tbody tr:nth-child(2)',
|
||||||
searchResult: 'vn-item-index tbody tr:not(.empty-rows)',
|
searchResult: 'vn-item-index tbody tr:not(.empty-rows)',
|
||||||
firstResultPreviewButton: 'vn-item-index tbody > :nth-child(1) .buttons > [icon="preview"]',
|
firstResultPreviewButton: 'vn-item-index tbody > :nth-child(2) .buttons > [icon="preview"]',
|
||||||
searchResultCloneButton: 'vn-item-index .buttons > [icon="icon-clone"]',
|
searchResultCloneButton: 'vn-item-index .buttons > [icon="icon-clone"]',
|
||||||
acceptClonationAlertButton: '.vn-confirm.shown [response="accept"]',
|
acceptClonationAlertButton: '.vn-confirm.shown [response="accept"]',
|
||||||
closeItemSummaryPreview: '.vn-popup.shown',
|
closeItemSummaryPreview: '.vn-popup.shown',
|
||||||
shownColumns: 'vn-item-index vn-button[id="shownColumns"]',
|
shownColumns: 'vn-item-index vn-button[id="shownColumns"]',
|
||||||
shownColumnsList: '.vn-popover.shown .content',
|
shownColumnsList: '.vn-popover.shown .content',
|
||||||
firstItemImage: 'vn-item-index tbody > tr:nth-child(1) > td:nth-child(1) > img',
|
firstItemImage: 'vn-item-index tbody > tr:nth-child(2) > td:nth-child(1) > img',
|
||||||
firstItemImageTd: 'vn-item-index smart-table tr:nth-child(1) td:nth-child(1)',
|
firstItemImageTd: 'vn-item-index smart-table tr:nth-child(1) td:nth-child(1)',
|
||||||
firstItemId: 'vn-item-index tbody > tr:nth-child(1) > td:nth-child(2)',
|
firstItemId: 'vn-item-index tbody > tr:nth-child(1) > td:nth-child(2)',
|
||||||
idCheckbox: '.vn-popover.shown vn-horizontal:nth-child(3) > vn-check[label="Identifier"]',
|
idCheckbox: '.vn-popover.shown vn-horizontal:nth-child(3) > vn-check[label="Identifier"]',
|
||||||
|
@ -523,11 +523,11 @@ export default {
|
||||||
searchResultDate: 'vn-ticket-summary [label=Landed] span',
|
searchResultDate: 'vn-ticket-summary [label=Landed] span',
|
||||||
topbarSearch: 'vn-searchbar',
|
topbarSearch: 'vn-searchbar',
|
||||||
moreMenu: 'vn-ticket-index vn-icon-button[icon=more_vert]',
|
moreMenu: 'vn-ticket-index vn-icon-button[icon=more_vert]',
|
||||||
fourthWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(4)',
|
fourthWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(5)',
|
||||||
fiveWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(5)',
|
fiveWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(6)',
|
||||||
weeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table table tbody tr',
|
weeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table table tbody tr',
|
||||||
firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(1) vn-icon-button[icon="delete"]',
|
firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(2) vn-icon-button[icon="delete"]',
|
||||||
firstWeeklyTicketAgency: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(1) [ng-model="weekly.agencyModeFk"]',
|
firstWeeklyTicketAgency: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(2) [ng-model="weekly.agencyModeFk"]',
|
||||||
acceptDeleteTurn: '.vn-confirm.shown button[response="accept"]'
|
acceptDeleteTurn: '.vn-confirm.shown button[response="accept"]'
|
||||||
},
|
},
|
||||||
createTicketView: {
|
createTicketView: {
|
||||||
|
@ -572,8 +572,8 @@ export default {
|
||||||
submitNotesButton: 'button[type=submit]'
|
submitNotesButton: 'button[type=submit]'
|
||||||
},
|
},
|
||||||
ticketExpedition: {
|
ticketExpedition: {
|
||||||
firstSaleCheckbox: 'vn-ticket-expedition tr:nth-child(1) vn-check[ng-model="expedition.checked"]',
|
firstSaleCheckbox: 'vn-ticket-expedition tr:nth-child(2) vn-check[ng-model="expedition.checked"]',
|
||||||
thirdSaleCheckbox: 'vn-ticket-expedition tr:nth-child(3) vn-check[ng-model="expedition.checked"]',
|
thirdSaleCheckbox: 'vn-ticket-expedition tr:nth-child(4) vn-check[ng-model="expedition.checked"]',
|
||||||
deleteExpeditionButton: 'vn-ticket-expedition slot-actions > vn-button[icon="delete"]',
|
deleteExpeditionButton: 'vn-ticket-expedition slot-actions > vn-button[icon="delete"]',
|
||||||
moveExpeditionButton: 'vn-ticket-expedition slot-actions > vn-button[icon="keyboard_arrow_down"]',
|
moveExpeditionButton: 'vn-ticket-expedition slot-actions > vn-button[icon="keyboard_arrow_down"]',
|
||||||
moreMenuWithoutRoute: 'vn-item[name="withoutRoute"]',
|
moreMenuWithoutRoute: 'vn-item[name="withoutRoute"]',
|
||||||
|
@ -712,7 +712,7 @@ export default {
|
||||||
problems: 'vn-check[label="With problems"]',
|
problems: 'vn-check[label="With problems"]',
|
||||||
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
|
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
|
||||||
moveButton: 'vn-button[vn-tooltip="Future tickets"]',
|
moveButton: 'vn-button[vn-tooltip="Future tickets"]',
|
||||||
firstCheck: 'tbody > tr:nth-child(1) > td > vn-check',
|
firstCheck: 'tbody > tr:nth-child(2) > td > vn-check',
|
||||||
multiCheck: 'vn-multi-check',
|
multiCheck: 'vn-multi-check',
|
||||||
tableId: 'vn-textfield[name="id"]',
|
tableId: 'vn-textfield[name="id"]',
|
||||||
tableFutureId: 'vn-textfield[name="futureId"]',
|
tableFutureId: 'vn-textfield[name="futureId"]',
|
||||||
|
@ -736,7 +736,7 @@ export default {
|
||||||
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
|
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
|
||||||
moveButton: 'vn-button[vn-tooltip="Advance tickets"]',
|
moveButton: 'vn-button[vn-tooltip="Advance tickets"]',
|
||||||
acceptButton: '.vn-confirm.shown button[response="accept"]',
|
acceptButton: '.vn-confirm.shown button[response="accept"]',
|
||||||
firstCheck: 'tbody > tr:nth-child(1) > td > vn-check',
|
firstCheck: 'tbody > tr:nth-child(2) > td > vn-check',
|
||||||
tableId: 'vn-textfield[name="id"]',
|
tableId: 'vn-textfield[name="id"]',
|
||||||
tableFutureId: 'vn-textfield[name="futureId"]',
|
tableFutureId: 'vn-textfield[name="futureId"]',
|
||||||
tableLiters: 'vn-textfield[name="liters"]',
|
tableLiters: 'vn-textfield[name="liters"]',
|
||||||
|
@ -810,7 +810,7 @@ export default {
|
||||||
claimAction: {
|
claimAction: {
|
||||||
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
|
importClaimButton: 'vn-claim-action vn-button[label="Import claim"]',
|
||||||
anyLine: 'vn-claim-action vn-tbody > vn-tr',
|
anyLine: 'vn-claim-action vn-tbody > vn-tr',
|
||||||
firstDeleteLine: 'vn-claim-action tr:nth-child(1) vn-icon-button[icon="delete"]',
|
firstDeleteLine: 'vn-claim-action tr:nth-child(2) vn-icon-button[icon="delete"]',
|
||||||
isPaidWithManaCheckbox: 'vn-claim-action vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
|
isPaidWithManaCheckbox: 'vn-claim-action vn-check[ng-model="$ctrl.claim.isChargedToMana"]'
|
||||||
},
|
},
|
||||||
ordersIndex: {
|
ordersIndex: {
|
||||||
|
@ -1216,7 +1216,7 @@ export default {
|
||||||
addTagButton: 'vn-icon-button[vn-tooltip="Add tag"]',
|
addTagButton: 'vn-icon-button[vn-tooltip="Add tag"]',
|
||||||
itemTagInput: 'vn-autocomplete[ng-model="itemTag.tagFk"]',
|
itemTagInput: 'vn-autocomplete[ng-model="itemTag.tagFk"]',
|
||||||
itemTagValueInput: 'vn-autocomplete[ng-model="itemTag.value"]',
|
itemTagValueInput: 'vn-autocomplete[ng-model="itemTag.value"]',
|
||||||
firstBuy: 'vn-entry-latest-buys tbody > tr:nth-child(1)',
|
firstBuy: 'vn-entry-latest-buys tbody > tr:nth-child(2)',
|
||||||
allBuysCheckBox: 'vn-entry-latest-buys thead vn-check',
|
allBuysCheckBox: 'vn-entry-latest-buys thead vn-check',
|
||||||
secondBuyCheckBox: 'vn-entry-latest-buys tbody tr:nth-child(2) vn-check[ng-model="buy.checked"]',
|
secondBuyCheckBox: 'vn-entry-latest-buys tbody tr:nth-child(2) vn-check[ng-model="buy.checked"]',
|
||||||
editBuysButton: 'vn-entry-latest-buys vn-button[icon="edit"]',
|
editBuysButton: 'vn-entry-latest-buys vn-button[icon="edit"]',
|
||||||
|
|
|
@ -19,15 +19,14 @@ describe('SmartTable SearchBar integration', () => {
|
||||||
await page.waitToClick(selectors.itemsIndex.openAdvancedSearchButton);
|
await page.waitToClick(selectors.itemsIndex.openAdvancedSearchButton);
|
||||||
await page.autocompleteSearch(selectors.itemsIndex.advancedSearchItemType, 'Anthurium');
|
await page.autocompleteSearch(selectors.itemsIndex.advancedSearchItemType, 'Anthurium');
|
||||||
await page.waitToClick(selectors.itemsIndex.advancedSearchButton);
|
await page.waitToClick(selectors.itemsIndex.advancedSearchButton);
|
||||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 3);
|
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 4);
|
||||||
|
|
||||||
await page.reload({
|
await page.reload({
|
||||||
waitUntil: 'networkidle2'
|
waitUntil: 'networkidle2'
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 3);
|
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 4);
|
||||||
|
|
||||||
await page.waitToClick(selectors.itemsIndex.advancedSmartTableButton);
|
|
||||||
await page.write(selectors.itemsIndex.advancedSmartTableGrouping, '1');
|
await page.write(selectors.itemsIndex.advancedSmartTableGrouping, '1');
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 2);
|
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 2);
|
||||||
|
@ -36,7 +35,7 @@ describe('SmartTable SearchBar integration', () => {
|
||||||
waitUntil: 'networkidle2'
|
waitUntil: 'networkidle2'
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 2);
|
await page.waitForNumberOfElements(selectors.itemsIndex.searchResult, 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filter in section without smart-table and search in searchBar go to zone section', async() => {
|
it('should filter in section without smart-table and search in searchBar go to zone section', async() => {
|
||||||
|
|
|
@ -19,7 +19,7 @@ describe('Client defaulter path', () => {
|
||||||
it('should count the amount of clients in the turns section', async() => {
|
it('should count the amount of clients in the turns section', async() => {
|
||||||
const result = await page.countElement(selectors.clientDefaulter.anyClient);
|
const result = await page.countElement(selectors.clientDefaulter.anyClient);
|
||||||
|
|
||||||
expect(result).toEqual(5);
|
expect(result).toEqual(6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check contain expected client', async() => {
|
it('should check contain expected client', async() => {
|
||||||
|
|
|
@ -18,11 +18,11 @@ describe('Item summary path', () => {
|
||||||
await page.doSearch('Ranged weapon');
|
await page.doSearch('Ranged weapon');
|
||||||
const resultsCount = await page.countElement(selectors.itemsIndex.searchResult);
|
const resultsCount = await page.countElement(selectors.itemsIndex.searchResult);
|
||||||
|
|
||||||
await page.waitForTextInElement(selectors.itemsIndex.searchResult, 'Ranged weapon');
|
await page.waitForTextInElement(selectors.itemsIndex.firstSearchResult, 'Ranged weapon');
|
||||||
await page.waitToClick(selectors.itemsIndex.firstResultPreviewButton);
|
await page.waitToClick(selectors.itemsIndex.firstResultPreviewButton);
|
||||||
const isVisible = await page.isVisible(selectors.itemSummary.basicData);
|
const isVisible = await page.isVisible(selectors.itemSummary.basicData);
|
||||||
|
|
||||||
expect(resultsCount).toBe(3);
|
expect(resultsCount).toBe(4);
|
||||||
expect(isVisible).toBeTruthy();
|
expect(isVisible).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ describe('Item summary path', () => {
|
||||||
await page.waitToClick(selectors.itemsIndex.firstResultPreviewButton);
|
await page.waitToClick(selectors.itemsIndex.firstResultPreviewButton);
|
||||||
await page.waitForSelector(selectors.itemSummary.basicData, {visible: true});
|
await page.waitForSelector(selectors.itemSummary.basicData, {visible: true});
|
||||||
|
|
||||||
expect(resultsCount).toBe(2);
|
expect(resultsCount).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should now check the item summary preview shows fields from basic data`, async() => {
|
it(`should now check the item summary preview shows fields from basic data`, async() => {
|
||||||
|
|
|
@ -18,7 +18,7 @@ describe('Item log path', () => {
|
||||||
await page.doSearch('Knowledge artifact');
|
await page.doSearch('Knowledge artifact');
|
||||||
const nResults = await page.countElement(selectors.itemsIndex.searchResult);
|
const nResults = await page.countElement(selectors.itemsIndex.searchResult);
|
||||||
|
|
||||||
expect(nResults).toEqual(0);
|
expect(nResults).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should access to the create item view by clicking the create floating button', async() => {
|
it('should access to the create item view by clicking the create floating button', async() => {
|
||||||
|
|
|
@ -27,6 +27,6 @@ describe('Ticket expeditions and log path', () => {
|
||||||
const result = await page
|
const result = await page
|
||||||
.countElement(selectors.ticketExpedition.expeditionRow);
|
.countElement(selectors.ticketExpedition.expeditionRow);
|
||||||
|
|
||||||
expect(result).toEqual(3);
|
expect(result).toEqual(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -19,7 +19,7 @@ describe('Ticket descriptor path', () => {
|
||||||
it('should count the amount of tickets in the turns section', async() => {
|
it('should count the amount of tickets in the turns section', async() => {
|
||||||
const result = await page.countElement(selectors.ticketsIndex.weeklyTicket);
|
const result = await page.countElement(selectors.ticketsIndex.weeklyTicket);
|
||||||
|
|
||||||
expect(result).toEqual(6);
|
expect(result).toEqual(7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should go back to the ticket index then search and access a ticket summary', async() => {
|
it('should go back to the ticket index then search and access a ticket summary', async() => {
|
||||||
|
@ -89,7 +89,7 @@ describe('Ticket descriptor path', () => {
|
||||||
await page.doSearch('11');
|
await page.doSearch('11');
|
||||||
const nResults = await page.countElement(selectors.ticketsIndex.searchWeeklyResult);
|
const nResults = await page.countElement(selectors.ticketsIndex.searchWeeklyResult);
|
||||||
|
|
||||||
expect(nResults).toEqual(1);
|
expect(nResults).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete the weekly ticket 11', async() => {
|
it('should delete the weekly ticket 11', async() => {
|
||||||
|
@ -104,7 +104,7 @@ describe('Ticket descriptor path', () => {
|
||||||
await page.doSearch();
|
await page.doSearch();
|
||||||
const nResults = await page.countElement(selectors.ticketsIndex.searchWeeklyResult);
|
const nResults = await page.countElement(selectors.ticketsIndex.searchWeeklyResult);
|
||||||
|
|
||||||
expect(nResults).toEqual(6);
|
expect(nResults).toEqual(7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update the agency then remove it afterwards', async() => {
|
it('should update the agency then remove it afterwards', async() => {
|
||||||
|
|
|
@ -29,7 +29,7 @@ describe('Ticket expeditions', () => {
|
||||||
const result = await page
|
const result = await page
|
||||||
.countElement(selectors.ticketExpedition.expeditionRow);
|
.countElement(selectors.ticketExpedition.expeditionRow);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`should move one expedition to new ticket with route`, async() => {
|
it(`should move one expedition to new ticket with route`, async() => {
|
||||||
|
@ -45,6 +45,6 @@ describe('Ticket expeditions', () => {
|
||||||
const result = await page
|
const result = await page
|
||||||
.countElement(selectors.ticketExpedition.expeditionRow);
|
.countElement(selectors.ticketExpedition.expeditionRow);
|
||||||
|
|
||||||
expect(result).toEqual(1);
|
expect(result).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -87,7 +87,7 @@ describe('Ticket Future path', () => {
|
||||||
await page.clearInput(selectors.ticketFuture.futureState);
|
await page.clearInput(selectors.ticketFuture.futureState);
|
||||||
await page.waitToClick(selectors.ticketFuture.submit);
|
await page.waitToClick(selectors.ticketFuture.submit);
|
||||||
|
|
||||||
await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 4);
|
await page.waitForNumberOfElements(selectors.ticketFuture.searchResult, 5);
|
||||||
await page.waitToClick(selectors.ticketFuture.multiCheck);
|
await page.waitToClick(selectors.ticketFuture.multiCheck);
|
||||||
await page.waitToClick(selectors.ticketFuture.firstCheck);
|
await page.waitToClick(selectors.ticketFuture.firstCheck);
|
||||||
await page.waitToClick(selectors.ticketFuture.moveButton);
|
await page.waitToClick(selectors.ticketFuture.moveButton);
|
||||||
|
|
|
@ -40,6 +40,8 @@ export default class SmartTable extends Component {
|
||||||
this._options = options;
|
this._options = options;
|
||||||
if (!options) return;
|
if (!options) return;
|
||||||
|
|
||||||
|
options.defaultSearch = true;
|
||||||
|
|
||||||
if (options.defaultSearch)
|
if (options.defaultSearch)
|
||||||
this.displaySearch();
|
this.displaySearch();
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default class Auth {
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoginOk(json, remember) {
|
onLoginOk(json, remember) {
|
||||||
this.vnToken.set(json.data.token, remember);
|
this.vnToken.set(json.data.token, json.data.created, remember);
|
||||||
|
|
||||||
return this.loadAcls().then(() => {
|
return this.loadAcls().then(() => {
|
||||||
let continueHash = this.$state.params.continue;
|
let continueHash = this.$state.params.continue;
|
||||||
|
|
|
@ -11,3 +11,4 @@ import './report';
|
||||||
import './email';
|
import './email';
|
||||||
import './file';
|
import './file';
|
||||||
import './date';
|
import './date';
|
||||||
|
|
||||||
|
|
|
@ -9,25 +9,33 @@ export default class Token {
|
||||||
constructor() {
|
constructor() {
|
||||||
try {
|
try {
|
||||||
this.token = sessionStorage.getItem('vnToken');
|
this.token = sessionStorage.getItem('vnToken');
|
||||||
if (!this.token)
|
this.created = sessionStorage.getItem('vnTokenCreated');
|
||||||
|
if (!this.token) {
|
||||||
this.token = localStorage.getItem('vnToken');
|
this.token = localStorage.getItem('vnToken');
|
||||||
|
this.created = localStorage.getItem('vnTokenCreated');
|
||||||
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
set(value, remember) {
|
set(token, created, remember) {
|
||||||
this.unset();
|
this.unset();
|
||||||
try {
|
try {
|
||||||
if (remember)
|
if (remember) {
|
||||||
localStorage.setItem('vnToken', value);
|
localStorage.setItem('vnToken', token);
|
||||||
else
|
localStorage.setItem('vnTokenCreated', created);
|
||||||
sessionStorage.setItem('vnToken', value);
|
} else {
|
||||||
|
sessionStorage.setItem('vnToken', token);
|
||||||
|
sessionStorage.setItem('vnTokenCreated', created);
|
||||||
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
this.token = value;
|
this.token = token;
|
||||||
|
this.created = created;
|
||||||
}
|
}
|
||||||
unset() {
|
unset() {
|
||||||
localStorage.removeItem('vnToken');
|
localStorage.removeItem('vnToken');
|
||||||
sessionStorage.removeItem('vnToken');
|
sessionStorage.removeItem('vnToken');
|
||||||
this.token = null;
|
this.token = null;
|
||||||
|
this.created = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
if (!this.$state.params || !this.$state.params.id || !this.$state.params.token)
|
if (!this.$state.params.id)
|
||||||
this.$state.go('login');
|
this.$state.go('login');
|
||||||
|
|
||||||
this.$http.get('UserPasswords/findOne')
|
this.$http.get('UserPasswords/findOne')
|
||||||
|
@ -25,7 +25,7 @@ export default class Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
const id = this.$state.params.id;
|
const userId = this.$state.params.userId;
|
||||||
const newPassword = this.newPassword;
|
const newPassword = this.newPassword;
|
||||||
const oldPassword = this.oldPassword;
|
const oldPassword = this.oldPassword;
|
||||||
|
|
||||||
|
@ -35,12 +35,12 @@ export default class Controller {
|
||||||
throw new UserError(`Passwords don't match`);
|
throw new UserError(`Passwords don't match`);
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: this.$state.params.token
|
Authorization: this.$state.params.id
|
||||||
};
|
};
|
||||||
|
|
||||||
this.$http.post('VnUsers/change-password',
|
this.$http.post('VnUsers/change-password',
|
||||||
{
|
{
|
||||||
id,
|
id: userId,
|
||||||
oldPassword,
|
oldPassword,
|
||||||
newPassword
|
newPassword
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,13 +3,14 @@ import Component from 'core/lib/component';
|
||||||
import './style.scss';
|
import './style.scss';
|
||||||
|
|
||||||
export class Layout extends Component {
|
export class Layout extends Component {
|
||||||
constructor($element, $, vnModules) {
|
constructor($element, $, vnModules, vnToken) {
|
||||||
super($element, $);
|
super($element, $);
|
||||||
this.modules = vnModules.get();
|
this.modules = vnModules.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
$onInit() {
|
$onInit() {
|
||||||
this.getUserData();
|
this.getUserData();
|
||||||
|
this.getAccessTokenConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserData() {
|
getUserData() {
|
||||||
|
@ -30,8 +31,42 @@ export class Layout extends Component {
|
||||||
refresh() {
|
refresh() {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAccessTokenConfig() {
|
||||||
|
this.$http.get('AccessTokenConfigs').then(json => {
|
||||||
|
const firtsResult = json.data[0];
|
||||||
|
if (!firtsResult) return;
|
||||||
|
this.renewPeriod = firtsResult.renewPeriod;
|
||||||
|
this.renewInterval = firtsResult.renewInterval;
|
||||||
|
|
||||||
|
const intervalMilliseconds = firtsResult.renewInterval * 1000;
|
||||||
|
this.inservalId = setInterval(this.checkTokenValidity.bind(this), intervalMilliseconds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkTokenValidity() {
|
||||||
|
const now = new Date();
|
||||||
|
const differenceMilliseconds = now - new Date(this.vnToken.created);
|
||||||
|
const differenceSeconds = Math.floor(differenceMilliseconds / 1000);
|
||||||
|
|
||||||
|
if (differenceSeconds > this.renewPeriod) {
|
||||||
|
this.$http.post('VnUsers/renewToken')
|
||||||
|
.then(json => {
|
||||||
|
if (json.data.token) {
|
||||||
|
let remember = true;
|
||||||
|
if (window.sessionStorage.vnToken) remember = false;
|
||||||
|
|
||||||
|
this.vnToken.set(json.data.token, json.data.created, remember);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$onDestroy() {
|
||||||
|
clearInterval(this.inservalId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Layout.$inject = ['$element', '$scope', 'vnModules'];
|
Layout.$inject = ['$element', '$scope', 'vnModules', 'vnToken'];
|
||||||
|
|
||||||
ngModule.vnComponent('vnLayout', {
|
ngModule.vnComponent('vnLayout', {
|
||||||
template: require('./index.html'),
|
template: require('./index.html'),
|
||||||
|
|
|
@ -37,4 +37,49 @@ describe('Component vnLayout', () => {
|
||||||
expect(url).not.toBeDefined();
|
expect(url).not.toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getAccessTokenConfig()', () => {
|
||||||
|
it(`should set the renewPeriod and renewInterval properties in localStorage`, () => {
|
||||||
|
const response = [{
|
||||||
|
renewPeriod: 100,
|
||||||
|
renewInterval: 5
|
||||||
|
}];
|
||||||
|
|
||||||
|
$httpBackend.expect('GET', `AccessTokenConfigs`).respond(response);
|
||||||
|
controller.getAccessTokenConfig();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.renewPeriod).toBe(100);
|
||||||
|
expect(controller.renewInterval).toBe(5);
|
||||||
|
expect(controller.inservalId).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('checkTokenValidity()', () => {
|
||||||
|
it(`should not call renewToken and not set vnToken in the controller`, () => {
|
||||||
|
controller.renewPeriod = 100;
|
||||||
|
controller.vnToken.created = new Date();
|
||||||
|
|
||||||
|
controller.checkTokenValidity();
|
||||||
|
|
||||||
|
expect(controller.vnToken.token).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should call renewToken and set vnToken properties in the controller`, () => {
|
||||||
|
const response = {
|
||||||
|
token: 999,
|
||||||
|
created: new Date()
|
||||||
|
};
|
||||||
|
controller.renewPeriod = 100;
|
||||||
|
const oneHourBefore = new Date(Date.now() - (60 * 60 * 1000));
|
||||||
|
controller.vnToken.created = oneHourBefore;
|
||||||
|
|
||||||
|
$httpBackend.expect('POST', `VnUsers/renewToken`).respond(response);
|
||||||
|
controller.checkTokenValidity();
|
||||||
|
$httpBackend.flush();
|
||||||
|
|
||||||
|
expect(controller.vnToken.token).toBe(999);
|
||||||
|
expect(controller.vnToken.created).toEqual(response.created);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,10 +27,9 @@ export default class Controller {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.password = '';
|
this.password = '';
|
||||||
this.focusUser();
|
this.focusUser();
|
||||||
if (req?.data?.error?.code == 'passExpired') {
|
const err = req.data?.error;
|
||||||
const [args] = req.data.error.translateArgs;
|
if (err?.code == 'passExpired')
|
||||||
this.$state.go('change-password', args);
|
this.$state.go('change-password', err.details.token);
|
||||||
}
|
|
||||||
|
|
||||||
throw req;
|
throw req;
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,7 +38,7 @@ function config($stateProvider, $urlRouterProvider) {
|
||||||
})
|
})
|
||||||
.state('change-password', {
|
.state('change-password', {
|
||||||
parent: 'outLayout',
|
parent: 'outLayout',
|
||||||
url: '/change-password?id&token',
|
url: '/change-password?id&userId',
|
||||||
description: 'Change password',
|
description: 'Change password',
|
||||||
template: '<vn-change-password></vn-change-password>'
|
template: '<vn-change-password></vn-change-password>'
|
||||||
})
|
})
|
||||||
|
|
|
@ -200,22 +200,20 @@ module.exports = function(Self) {
|
||||||
const connector = this.dataSource.connector;
|
const connector = this.dataSource.connector;
|
||||||
let conn;
|
let conn;
|
||||||
let res;
|
let res;
|
||||||
const opts = Object.assign({}, options);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (userId) {
|
if (userId) {
|
||||||
conn = await new Promise((resolve, reject) => {
|
if (!options.transaction) {
|
||||||
connector.client.getConnection(function(err, conn) {
|
options = Object.assign({}, options);
|
||||||
if (err)
|
conn = await new Promise((resolve, reject) => {
|
||||||
reject(err);
|
connector.client.getConnection(function(err, conn) {
|
||||||
else
|
if (err)
|
||||||
resolve(conn);
|
reject(err);
|
||||||
|
else
|
||||||
|
resolve(conn);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
options.transaction = {
|
||||||
|
|
||||||
const opts = Object.assign({}, options);
|
|
||||||
if (!opts.transaction) {
|
|
||||||
opts.transaction = {
|
|
||||||
connection: conn,
|
connection: conn,
|
||||||
connector
|
connector
|
||||||
};
|
};
|
||||||
|
@ -223,15 +221,14 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
await connector.executeP(
|
await connector.executeP(
|
||||||
'CALL account.myUser_loginWithName((SELECT name FROM account.user WHERE id = ?))',
|
'CALL account.myUser_loginWithName((SELECT name FROM account.user WHERE id = ?))',
|
||||||
[userId], opts
|
[userId], options
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
res = await connector.executeP(query, params, opts);
|
res = await connector.executeP(query, params, options);
|
||||||
|
|
||||||
if (userId) {
|
if (userId)
|
||||||
await connector.executeP('CALL account.myUser_logout()', null, opts);
|
await connector.executeP('CALL account.myUser_logout()', null, options);
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
if (conn) conn.release();
|
if (conn) conn.release();
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,5 +174,6 @@
|
||||||
"A claim with that sale already exists": "A claim with that sale already exists",
|
"A claim with that sale already exists": "A claim with that sale already exists",
|
||||||
"Pass expired": "The password has expired, change it from Salix",
|
"Pass expired": "The password has expired, change it from Salix",
|
||||||
"Can't transfer claimed sales": "Can't transfer claimed sales",
|
"Can't transfer claimed sales": "Can't transfer claimed sales",
|
||||||
"Invalid quantity": "Invalid quantity"
|
"Invalid quantity": "Invalid quantity",
|
||||||
|
"Failed to upload delivery note": "Error to upload delivery note {{id}}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,7 +258,7 @@
|
||||||
"App name does not exist": "El nombre de aplicación no es válido",
|
"App name does not exist": "El nombre de aplicación no es válido",
|
||||||
"Try again": "Vuelve a intentarlo",
|
"Try again": "Vuelve a intentarlo",
|
||||||
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
|
"Aplicación bloqueada por el usuario 9": "Aplicación bloqueada por el usuario 9",
|
||||||
"Failed to upload file": "Error al subir archivo",
|
"Failed to upload delivery note": "Error al subir albarán {{id}}",
|
||||||
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
|
"The DOCUWARE PDF document does not exists": "El documento PDF Docuware no existe",
|
||||||
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
|
"It is not possible to modify tracked sales": "No es posible modificar líneas de pedido que se hayan empezado a preparar",
|
||||||
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
|
"It is not possible to modify sales that their articles are from Floramondo": "No es posible modificar líneas de pedido cuyos artículos sean de Floramondo",
|
||||||
|
@ -293,5 +293,6 @@
|
||||||
"Pass expired": "La contraseña ha caducado, cambiela desde Salix",
|
"Pass expired": "La contraseña ha caducado, cambiela desde Salix",
|
||||||
"Invalid NIF for VIES": "Invalid NIF for VIES",
|
"Invalid NIF for VIES": "Invalid NIF for VIES",
|
||||||
"Ticket does not exist": "Este ticket no existe",
|
"Ticket does not exist": "Este ticket no existe",
|
||||||
"Ticket is already signed": "Este ticket ya ha sido firmado"
|
"Ticket is already signed": "Este ticket ya ha sido firmado",
|
||||||
|
"The renew period has not been exceeded": "El periodo de renovación no ha sido superado"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
name: account
|
||||||
|
columns:
|
||||||
|
id: id
|
|
@ -0,0 +1,3 @@
|
||||||
|
name: cuenta
|
||||||
|
columns:
|
||||||
|
id: id
|
|
@ -0,0 +1,5 @@
|
||||||
|
name: mail alias
|
||||||
|
columns:
|
||||||
|
id: id
|
||||||
|
mailAlias: alias
|
||||||
|
account: account
|
|
@ -0,0 +1,5 @@
|
||||||
|
name: alias de correo
|
||||||
|
columns:
|
||||||
|
id: id
|
||||||
|
mailAlias: alias
|
||||||
|
account: cuenta
|
|
@ -5,8 +5,7 @@ import UserError from 'core/lib/user-error';
|
||||||
export default class Controller extends Section {
|
export default class Controller extends Section {
|
||||||
onSynchronizeAll() {
|
onSynchronizeAll() {
|
||||||
this.vnApp.showSuccess(this.$t('Synchronizing in the background'));
|
this.vnApp.showSuccess(this.$t('Synchronizing in the background'));
|
||||||
this.$http.patch(`Accounts/syncAll`)
|
this.$http.patch(`Accounts/syncAll`);
|
||||||
.then(() => this.vnApp.showSuccess(this.$t('Users synchronized!')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onUserSync() {
|
onUserSync() {
|
||||||
|
|
|
@ -2,7 +2,7 @@ const UserError = require('vn-loopback/util/user-error');
|
||||||
const base64url = require('base64url');
|
const base64url = require('base64url');
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('confirm', {
|
Self.remoteMethod('confirm', {
|
||||||
description: 'Confirms electronic payment transaction',
|
description: 'Confirms electronic payment transaction',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [
|
accepts: [
|
||||||
|
@ -30,7 +30,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self.confirm = async(ctx, signatureVersion, merchantParameters, signature) => {
|
Self.confirm = async(signatureVersion, merchantParameters, signature) => {
|
||||||
const $ = Self.app.models;
|
const $ = Self.app.models;
|
||||||
let transaction;
|
let transaction;
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ module.exports = Self => {
|
||||||
params['Ds_Currency'],
|
params['Ds_Currency'],
|
||||||
params['Ds_Response'],
|
params['Ds_Response'],
|
||||||
params['Ds_ErrorCode']
|
params['Ds_ErrorCode']
|
||||||
], {userId: ctx.req.accessToken.userId});
|
]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let buy = await models.Buy.findOne({where: {entryFk: args.id}}, myOptions);
|
let buy = await models.Buy.findOne({where: {entryFk: args.id, itemFk: args.item}}, myOptions);
|
||||||
if (buy)
|
if (buy)
|
||||||
await buy.updateAttribute('printedStickers', args.printedStickers, myOptions);
|
await buy.updateAttribute('printedStickers', args.printedStickers, myOptions);
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -118,7 +118,8 @@
|
||||||
label="Company"
|
label="Company"
|
||||||
show-field="code"
|
show-field="code"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
ng-model="$ctrl.companyFk">
|
ng-model="$ctrl.companyFk"
|
||||||
|
on-change="$ctrl.getInvoiceDate(value)">
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
url="Printers"
|
url="Printers"
|
||||||
|
|
|
@ -7,23 +7,29 @@ class Controller extends Section {
|
||||||
$onInit() {
|
$onInit() {
|
||||||
const date = Date.vnNew();
|
const date = Date.vnNew();
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
maxShipped: new Date(date.getFullYear(), date.getMonth(), 0),
|
maxShipped: new Date(date.getFullYear(), date.getMonth(), 0),
|
||||||
clientsToInvoice: 'all',
|
clientsToInvoice: 'all',
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$http.get('UserConfigs/getUserConfig')
|
this.$http.get('UserConfigs/getUserConfig')
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.companyFk = res.data.companyFk;
|
this.companyFk = res.data.companyFk;
|
||||||
const params = {
|
this.getInvoiceDate(this.companyFk);
|
||||||
companyFk: this.companyFk
|
});
|
||||||
};
|
}
|
||||||
return this.$http.get('InvoiceOuts/getInvoiceDate', {params});
|
|
||||||
})
|
getInvoiceDate(companyFk) {
|
||||||
.then(res => {
|
const params = { companyFk: companyFk };
|
||||||
this.minInvoicingDate = res.data.issued ? new Date(res.data.issued) : null;
|
this.fetchInvoiceDate(params);
|
||||||
this.invoiceDate = this.minInvoicingDate;
|
}
|
||||||
});
|
|
||||||
}
|
fetchInvoiceDate(params) {
|
||||||
|
this.$http.get('InvoiceOuts/getInvoiceDate', { params })
|
||||||
|
.then(res => {
|
||||||
|
this.minInvoicingDate = res.data.issued ? new Date(res.data.issued) : null;
|
||||||
|
this.invoiceDate = this.minInvoicingDate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
stopInvoicing() {
|
stopInvoicing() {
|
||||||
this.status = 'stopping';
|
this.status = 'stopping';
|
||||||
|
@ -42,7 +48,7 @@ class Controller extends Section {
|
||||||
throw new UserError('Invoice date and the max date should be filled');
|
throw new UserError('Invoice date and the max date should be filled');
|
||||||
if (this.invoiceDate < this.maxShipped)
|
if (this.invoiceDate < this.maxShipped)
|
||||||
throw new UserError('Invoice date can\'t be less than max date');
|
throw new UserError('Invoice date can\'t be less than max date');
|
||||||
if (this.invoiceDate.getTime() < this.minInvoicingDate.getTime())
|
if (this.minInvoicingDate && this.invoiceDate.getTime() < this.minInvoicingDate.getTime())
|
||||||
throw new UserError('Exists an invoice with a previous date');
|
throw new UserError('Exists an invoice with a previous date');
|
||||||
if (!this.companyFk)
|
if (!this.companyFk)
|
||||||
throw new UserError('Choose a valid company');
|
throw new UserError('Choose a valid company');
|
||||||
|
|
|
@ -19,7 +19,8 @@ export default class Controller extends Section {
|
||||||
this.smartTableOptions = {
|
this.smartTableOptions = {
|
||||||
activeButtons: {
|
activeButtons: {
|
||||||
search: true,
|
search: true,
|
||||||
}, columns: [
|
},
|
||||||
|
columns: [
|
||||||
{
|
{
|
||||||
field: 'isActive',
|
field: 'isActive',
|
||||||
searchable: false
|
searchable: false
|
||||||
|
|
|
@ -13,7 +13,6 @@ export default class Controller extends Section {
|
||||||
activeButtons: {
|
activeButtons: {
|
||||||
search: true
|
search: true
|
||||||
},
|
},
|
||||||
defaultSearch: true,
|
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
field: 'warehouseFk',
|
field: 'warehouseFk',
|
||||||
|
|
|
@ -50,13 +50,13 @@
|
||||||
<th field="salesPersonFk">
|
<th field="salesPersonFk">
|
||||||
<span translate>Salesperson</span>
|
<span translate>Salesperson</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="shippedDate" shrink-date>
|
<th field="shippedDate" shrink-date filter-enabled="false">
|
||||||
<span translate>Date</span>
|
<span translate>Date</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="theoreticalHour">
|
<th field="theoreticalHour" filter-enabled="false">
|
||||||
<span translate>Theoretical</span>
|
<span translate>Theoretical</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="practicalHour">
|
<th field="practicalHour" filter-enabled="false">
|
||||||
<span translate>Practical</span>
|
<span translate>Practical</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="preparationHour" filter-enabled="false">
|
<th field="preparationHour" filter-enabled="false">
|
||||||
|
|
|
@ -21,7 +21,6 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.confirm = async(ctx, orderFk) => {
|
Self.confirm = async(ctx, orderFk) => {
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
|
|
||||||
const query = `CALL hedera.order_confirmWithUser(?, ?)`;
|
const query = `CALL hedera.order_confirmWithUser(?, ?)`;
|
||||||
const response = await Self.rawSql(query, [orderFk, userId], {userId});
|
const response = await Self.rawSql(query, [orderFk, userId], {userId});
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,26 @@
|
||||||
<vn-auto-search
|
<vn-auto-search
|
||||||
model="model">
|
model="model">
|
||||||
</vn-auto-search>
|
</vn-auto-search>
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="AgencyModes"
|
||||||
|
data="agencyModes">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="Vehicles"
|
||||||
|
data="vehicles">
|
||||||
|
</vn-crud-model>
|
||||||
|
<vn-crud-model
|
||||||
|
auto-load="true"
|
||||||
|
url="Workers/activeWithInheritedRole"
|
||||||
|
data="activeWithInheritedRole">
|
||||||
|
</vn-crud-model>
|
||||||
<div class="vn-w-xl">
|
<div class="vn-w-xl">
|
||||||
<vn-card>
|
<vn-card>
|
||||||
<smart-table
|
<smart-table
|
||||||
model="model"
|
model="model"
|
||||||
options="$ctrl.smartTableOptions"
|
options="$ctrl.smartTableOptions"
|
||||||
expr-builder="$ctrl.exprBuilder(param, value)">
|
expr-builder="$ctrl.exprBuilder(param, value)">
|
||||||
<slot-actions>
|
<slot-actions>
|
||||||
<section class="header">
|
<section class="header">
|
||||||
|
@ -35,7 +50,7 @@
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th shrink>
|
<th shrink>
|
||||||
<vn-multi-check
|
<vn-multi-check
|
||||||
model="model">
|
model="model">
|
||||||
</vn-multi-check>
|
</vn-multi-check>
|
||||||
</th>
|
</th>
|
||||||
|
@ -52,19 +67,19 @@
|
||||||
<span translate>Vehicle</span>
|
<span translate>Vehicle</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="created" shrink-date>
|
<th field="created" shrink-date>
|
||||||
<span translate>Date</span>
|
<span translate>Date</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="m3" number>
|
<th field="m3" number>
|
||||||
<span translate>m³</span>
|
<span translate>m³</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="description">
|
<th field="description">
|
||||||
<span translate>Description</span>
|
<span translate>Description</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="started">
|
<th field="started">
|
||||||
<span translate>Hour started</span>
|
<span translate>Hour started</span>
|
||||||
</th>
|
</th>
|
||||||
<th field="finished">
|
<th field="finished">
|
||||||
<span translate>Hour finished</span>
|
<span translate>Hour finished</span>
|
||||||
</th>
|
</th>
|
||||||
<th shrink></th>
|
<th shrink></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -74,7 +89,7 @@
|
||||||
class="clickable vn-tr search-result"
|
class="clickable vn-tr search-result"
|
||||||
ng-attr-id="{{::route.id}}" vn-droppable="$ctrl.onDrop($event)">
|
ng-attr-id="{{::route.id}}" vn-droppable="$ctrl.onDrop($event)">
|
||||||
<td shrink>
|
<td shrink>
|
||||||
<vn-check
|
<vn-check
|
||||||
ng-model="route.checked"
|
ng-model="route.checked"
|
||||||
vn-click-stop>
|
vn-click-stop>
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
@ -83,7 +98,7 @@
|
||||||
<td>
|
<td>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
ng-model="route.workerFk"
|
ng-model="route.workerFk"
|
||||||
url="Workers/activeWithInheritedRole"
|
data="activeWithInheritedRole"
|
||||||
show-field="nickname"
|
show-field="nickname"
|
||||||
search-function="{firstName: $search}"
|
search-function="{firstName: $search}"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
|
@ -98,25 +113,25 @@
|
||||||
<td expand>
|
<td expand>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
ng-model="route.agencyModeFk"
|
ng-model="route.agencyModeFk"
|
||||||
url="AgencyModes"
|
data="agencyModes"
|
||||||
show-field="name"
|
show-field="name"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
on-change="$ctrl.updateAttributes(route)"
|
on-change="$ctrl.updateAttributes(route)"
|
||||||
vn-click-stop>
|
vn-click-stop>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</td>
|
</td>
|
||||||
<td expand>
|
<td expand>
|
||||||
<vn-autocomplete
|
<vn-autocomplete
|
||||||
ng-model="route.vehicleFk"
|
ng-model="route.vehicleFk"
|
||||||
url="Vehicles"
|
data="vehicles"
|
||||||
show-field="numberPlate"
|
show-field="numberPlate"
|
||||||
value-field="id"
|
value-field="id"
|
||||||
on-change="$ctrl.updateAttributes(route)"
|
on-change="$ctrl.updateAttributes(route)"
|
||||||
vn-click-stop>
|
vn-click-stop>
|
||||||
</vn-autocomplete>
|
</vn-autocomplete>
|
||||||
</td >
|
</td >
|
||||||
<td>
|
<td>
|
||||||
<vn-date-picker
|
<vn-date-picker
|
||||||
ng-model="route.created"
|
ng-model="route.created"
|
||||||
on-change="$ctrl.updateAttributes(route)">
|
on-change="$ctrl.updateAttributes(route)">
|
||||||
</vn-horizontal>
|
</vn-horizontal>
|
||||||
|
@ -156,7 +171,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</slot-table>
|
</slot-table>
|
||||||
</smart-table>
|
</smart-table>
|
||||||
</vn-card>
|
</vn-card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -171,7 +186,7 @@
|
||||||
route="$ctrl.routeSelected">
|
route="$ctrl.routeSelected">
|
||||||
</vn-route-ticket-popup>
|
</vn-route-ticket-popup>
|
||||||
|
|
||||||
<vn-worker-descriptor-popover
|
<vn-worker-descriptor-popover
|
||||||
vn-id="workerDescriptor">
|
vn-id="workerDescriptor">
|
||||||
</vn-worker-descriptor-popover>
|
</vn-worker-descriptor-popover>
|
||||||
<vn-ticket-descriptor-popover
|
<vn-ticket-descriptor-popover
|
||||||
|
@ -194,7 +209,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Clonation dialog -->
|
<!-- Clonation dialog -->
|
||||||
<vn-dialog class="edit"
|
<vn-dialog class="edit"
|
||||||
vn-id="clonationDialog"
|
vn-id="clonationDialog"
|
||||||
on-accept="$ctrl.cloneSelectedRoutes()"
|
on-accept="$ctrl.cloneSelectedRoutes()"
|
||||||
message="Select the starting date">
|
message="Select the starting date">
|
||||||
|
@ -210,4 +225,4 @@
|
||||||
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
<input type="button" response="cancel" translate-attr="{value: 'Cancel'}"/>
|
||||||
<button response="accept" translate>Clone</button>
|
<button response="accept" translate>Clone</button>
|
||||||
</tpl-buttons>
|
</tpl-buttons>
|
||||||
</vn-dialog>
|
</vn-dialog>
|
||||||
|
|
|
@ -5,3 +5,4 @@ columns:
|
||||||
beneficiary: beneficiary
|
beneficiary: beneficiary
|
||||||
supplierFk: supplier
|
supplierFk: supplier
|
||||||
bankEntityFk: bank entity
|
bankEntityFk: bank entity
|
||||||
|
accountingFk: ledger account
|
||||||
|
|
|
@ -5,3 +5,4 @@ columns:
|
||||||
beneficiary: beneficiario
|
beneficiary: beneficiario
|
||||||
supplierFk: proveedor
|
supplierFk: proveedor
|
||||||
bankEntityFk: entidad bancaria
|
bankEntityFk: entidad bancaria
|
||||||
|
accountingFk: cuenta contable
|
||||||
|
|
|
@ -26,14 +26,14 @@ module.exports = Self => {
|
||||||
Self.getItemsPackaging = async(id, entry) => {
|
Self.getItemsPackaging = async(id, entry) => {
|
||||||
return Self.rawSql(`
|
return Self.rawSql(`
|
||||||
WITH entryTmp AS (
|
WITH entryTmp AS (
|
||||||
SELECT i.id, SUM(b.quantity) quantity
|
SELECT i.id, SUM(b.quantity) quantity, SUM(b.printedStickers) printedStickers
|
||||||
FROM vn.entry e
|
FROM vn.entry e
|
||||||
JOIN vn.buy b ON b.entryFk = e.id
|
JOIN vn.buy b ON b.entryFk = e.id
|
||||||
JOIN vn.supplier s ON s.id = e.supplierFk
|
JOIN vn.supplier s ON s.id = e.supplierFk
|
||||||
JOIN vn.item i ON i.id = b.itemFk
|
JOIN vn.item i ON i.id = b.itemFk
|
||||||
WHERE e.id = ? AND e.supplierFk = ?
|
WHERE e.id = ? AND e.supplierFk = ?
|
||||||
GROUP BY i.id
|
GROUP BY i.id
|
||||||
) SELECT i.id, i.name, et.quantity, SUM(b.quantity) quantityTotal
|
) SELECT i.id, i.name, et.quantity, SUM(b.quantity) quantityTotal, et.printedStickers
|
||||||
FROM vn.buy b
|
FROM vn.buy b
|
||||||
JOIN vn.item i ON i.id = b.itemFk
|
JOIN vn.item i ON i.id = b.itemFk
|
||||||
JOIN vn.entry e ON e.id = b.entryFk
|
JOIN vn.entry e ON e.id = b.entryFk
|
||||||
|
|
|
@ -2,14 +2,17 @@ name: request
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
description: description
|
description: description
|
||||||
created: created
|
buyerCode: buyer
|
||||||
quantity: quantity
|
quantity: quantity
|
||||||
price: price
|
price: price
|
||||||
isOk: ok
|
|
||||||
response: response
|
|
||||||
saleFk: sale
|
|
||||||
ticketFk: ticket
|
|
||||||
attenderFk: attender
|
|
||||||
requesterFk: requester
|
|
||||||
itemFk: item
|
itemFk: item
|
||||||
|
clientFk: client
|
||||||
|
response: response
|
||||||
|
total: total
|
||||||
|
buyed: buyed
|
||||||
|
saleFk: sale
|
||||||
|
created: created
|
||||||
|
isOk: ok
|
||||||
|
requesterFk: requester
|
||||||
|
attenderFk: attender
|
||||||
|
ticketFk: ticket
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
name: peticion
|
name: petición
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
description: descripción
|
description: descripción
|
||||||
created: creado
|
buyerCode: comprador
|
||||||
quantity: cantidad
|
quantity: cantidad
|
||||||
price: precio
|
price: precio
|
||||||
isOk: ok
|
|
||||||
response: respuesta
|
|
||||||
saleFk: línea
|
|
||||||
ticketFk: ticket
|
|
||||||
attenderFk: asistente
|
|
||||||
requesterFk: solicitante
|
|
||||||
itemFk: artículo
|
itemFk: artículo
|
||||||
|
clientFk: cliente
|
||||||
|
response: respuesta
|
||||||
|
total: total
|
||||||
|
buyed: comprado
|
||||||
|
saleFk: línea
|
||||||
|
created: creado
|
||||||
|
isOk: ok
|
||||||
|
requesterFk: solicitante
|
||||||
|
attenderFk: asistente
|
||||||
|
ticketFk: ticket
|
||||||
|
|
|
@ -100,5 +100,8 @@
|
||||||
},
|
},
|
||||||
"TicketConfig": {
|
"TicketConfig": {
|
||||||
"dataSource": "vn"
|
"dataSource": "vn"
|
||||||
|
},
|
||||||
|
"PackingSiteAdvanced": {
|
||||||
|
"dataSource": "vn"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "PackingSiteAdvanced",
|
||||||
|
"base": "VnModel",
|
||||||
|
"options": {
|
||||||
|
"mysql": {
|
||||||
|
"table": "packingSiteAdvanced"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"properties": {
|
||||||
|
"ticketFk": {
|
||||||
|
"id": true,
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"workerFk": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -326,14 +326,8 @@ class Controller extends Section {
|
||||||
if (!force)
|
if (!force)
|
||||||
return this.$.pdfToTablet.show();
|
return this.$.pdfToTablet.show();
|
||||||
|
|
||||||
return this.$http.post(`Docuwares/${this.id}/upload`, {fileCabinet: 'deliveryNote'})
|
return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds: [this.id]})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.$.balanceCreate.amountPaid = this.ticket.totalWithVat;
|
|
||||||
this.$.balanceCreate.clientFk = this.ticket.clientFk;
|
|
||||||
this.$.balanceCreate.description = 'Albaran: ';
|
|
||||||
this.$.balanceCreate.description += this.ticket.id;
|
|
||||||
|
|
||||||
this.$.balanceCreate.show();
|
|
||||||
this.vnApp.showSuccess(this.$t('PDF sent!'));
|
this.vnApp.showSuccess(this.$t('PDF sent!'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,17 +304,15 @@ describe('Ticket Component vnTicketDescriptorMenu', () => {
|
||||||
expect(controller.$.pdfToTablet.show).toHaveBeenCalled();
|
expect(controller.$.pdfToTablet.show).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should make a query and show balance create', () => {
|
it('should make a query', () => {
|
||||||
controller.$.balanceCreate = {show: () => {}};
|
controller.$.balanceCreate = {show: () => {}};
|
||||||
jest.spyOn(controller.$.balanceCreate, 'show');
|
|
||||||
jest.spyOn(controller.vnApp, 'showSuccess');
|
jest.spyOn(controller.vnApp, 'showSuccess');
|
||||||
|
|
||||||
$httpBackend.whenPOST(`Docuwares/${ticket.id}/upload`).respond(true);
|
$httpBackend.whenPOST(`Docuwares/upload`).respond(true);
|
||||||
controller.uploadDocuware(true);
|
controller.uploadDocuware(true);
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
expect(controller.vnApp.showSuccess).toHaveBeenCalled();
|
||||||
expect(controller.$.balanceCreate.show).toHaveBeenCalled();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
<vn-thead>
|
<vn-thead>
|
||||||
<vn-tr>
|
<vn-tr>
|
||||||
<vn-th shrink>
|
<vn-th shrink>
|
||||||
<vn-multi-check
|
<vn-multi-check
|
||||||
model="model">
|
model="model">
|
||||||
</vn-multi-check>
|
</vn-multi-check>
|
||||||
</vn-th>
|
</vn-th>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
class="clickable vn-tr search-result"
|
class="clickable vn-tr search-result"
|
||||||
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
|
ui-sref="ticket.card.summary({id: {{::ticket.id}}})">
|
||||||
<vn-td>
|
<vn-td>
|
||||||
<vn-check
|
<vn-check
|
||||||
ng-model="ticket.checked"
|
ng-model="ticket.checked"
|
||||||
vn-click-stop>
|
vn-click-stop>
|
||||||
</vn-check>
|
</vn-check>
|
||||||
|
@ -109,7 +109,7 @@
|
||||||
class="link">
|
class="link">
|
||||||
{{::ticket.refFk}}
|
{{::ticket.refFk}}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
ng-show="!ticket.refFk"
|
ng-show="!ticket.refFk"
|
||||||
class="chip {{$ctrl.stateColor(ticket)}}">
|
class="chip {{$ctrl.stateColor(ticket)}}">
|
||||||
{{ticket.state}}
|
{{ticket.state}}
|
||||||
|
@ -132,8 +132,8 @@
|
||||||
<vn-td actions>
|
<vn-td actions>
|
||||||
<vn-icon-button
|
<vn-icon-button
|
||||||
vn-anchor="::{
|
vn-anchor="::{
|
||||||
state: 'ticket.card.sale',
|
state: 'ticket.card.sale',
|
||||||
params: {id: ticket.id},
|
params: {id: ticket.id},
|
||||||
target: '_blank'
|
target: '_blank'
|
||||||
}"
|
}"
|
||||||
vn-tooltip="Go to lines"
|
vn-tooltip="Go to lines"
|
||||||
|
@ -152,21 +152,21 @@
|
||||||
</vn-data-viewer>
|
</vn-data-viewer>
|
||||||
<div fixed-bottom-right>
|
<div fixed-bottom-right>
|
||||||
<vn-vertical style="align-items: center;">
|
<vn-vertical style="align-items: center;">
|
||||||
<vn-button class="round sm vn-mb-sm"
|
<vn-button class="round vn-mb-sm"
|
||||||
icon="print"
|
icon="install_mobile"
|
||||||
ng-show="$ctrl.totalChecked > 0"
|
ng-show="$ctrl.totalChecked > 0"
|
||||||
ng-click="$ctrl.setDelivered()"
|
ng-click="$ctrl.sendDocuware()"
|
||||||
vn-tooltip="Set as delivered and open delivery note(s)"
|
vn-tooltip="Set as delivered and open delivery note(s)"
|
||||||
tooltip-position="left">
|
tooltip-position="left">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
<vn-button class="round sm vn-mb-sm"
|
<vn-button class="round vn-mb-sm"
|
||||||
icon="icon-recovery"
|
icon="icon-recovery"
|
||||||
ng-show="$ctrl.totalChecked > 0"
|
ng-show="$ctrl.totalChecked > 0"
|
||||||
ng-click="$ctrl.openBalanceDialog()"
|
ng-click="$ctrl.openBalanceDialog()"
|
||||||
vn-tooltip="Payment on account..."
|
vn-tooltip="Payment on account..."
|
||||||
tooltip-position="left">
|
tooltip-position="left">
|
||||||
</vn-button>
|
</vn-button>
|
||||||
<vn-button class="round sm vn-mb-sm"
|
<vn-button class="round vn-mb-sm"
|
||||||
icon="icon-invoice"
|
icon="icon-invoice"
|
||||||
ng-click="makeInvoiceConfirmation.show()"
|
ng-click="makeInvoiceConfirmation.show()"
|
||||||
ng-show="$ctrl.totalChecked > 0"
|
ng-show="$ctrl.totalChecked > 0"
|
||||||
|
@ -193,16 +193,16 @@
|
||||||
<vn-client-descriptor-popover
|
<vn-client-descriptor-popover
|
||||||
vn-id="clientDescriptor">
|
vn-id="clientDescriptor">
|
||||||
</vn-client-descriptor-popover>
|
</vn-client-descriptor-popover>
|
||||||
<vn-worker-descriptor-popover
|
<vn-worker-descriptor-popover
|
||||||
vn-id="workerDescriptor">
|
vn-id="workerDescriptor">
|
||||||
</vn-worker-descriptor-popover>
|
</vn-worker-descriptor-popover>
|
||||||
<vn-zone-descriptor-popover
|
<vn-zone-descriptor-popover
|
||||||
vn-id="zoneDescriptor">
|
vn-id="zoneDescriptor">
|
||||||
</vn-zone-descriptor-popover>
|
</vn-zone-descriptor-popover>
|
||||||
<vn-client-balance-create
|
<vn-client-balance-create
|
||||||
vn-id="balanceCreateDialog">
|
vn-id="balanceCreateDialog">
|
||||||
</vn-client-balance-create>
|
</vn-client-balance-create>
|
||||||
<vn-invoice-out-descriptor-popover
|
<vn-invoice-out-descriptor-popover
|
||||||
vn-id="invoiceOutDescriptor">
|
vn-id="invoiceOutDescriptor">
|
||||||
</vn-invoice-out-descriptor-popover>
|
</vn-invoice-out-descriptor-popover>
|
||||||
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
|
<vn-contextmenu vn-id="contextmenu" targets="['vn-data-viewer']" model="model"
|
||||||
|
@ -213,22 +213,22 @@
|
||||||
ng-click="contextmenu.filterBySelection()">
|
ng-click="contextmenu.filterBySelection()">
|
||||||
Filter by selection
|
Filter by selection
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item translate
|
<vn-item translate
|
||||||
ng-if="contextmenu.isFilterAllowed()"
|
ng-if="contextmenu.isFilterAllowed()"
|
||||||
ng-click="contextmenu.excludeSelection()">
|
ng-click="contextmenu.excludeSelection()">
|
||||||
Exclude selection
|
Exclude selection
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item translate
|
<vn-item translate
|
||||||
ng-if="contextmenu.isFilterAllowed()"
|
ng-if="contextmenu.isFilterAllowed()"
|
||||||
ng-click="contextmenu.removeFilter()">
|
ng-click="contextmenu.removeFilter()">
|
||||||
Remove filter
|
Remove filter
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item translate
|
<vn-item translate
|
||||||
ng-click="contextmenu.removeAllFilters()">
|
ng-click="contextmenu.removeAllFilters()">
|
||||||
Remove all filters
|
Remove all filters
|
||||||
</vn-item>
|
</vn-item>
|
||||||
<vn-item translate
|
<vn-item translate
|
||||||
ng-if="contextmenu.isActionAllowed()"
|
ng-if="contextmenu.isActionAllowed()"
|
||||||
ng-click="contextmenu.copyValue()">
|
ng-click="contextmenu.copyValue()">
|
||||||
Copy value
|
Copy value
|
||||||
</vn-item>
|
</vn-item>
|
||||||
|
@ -241,4 +241,4 @@
|
||||||
on-accept="$ctrl.makeInvoice()"
|
on-accept="$ctrl.makeInvoice()"
|
||||||
question="{{$ctrl.confirmationMessage}}"
|
question="{{$ctrl.confirmationMessage}}"
|
||||||
message="Invoice selected tickets">
|
message="Invoice selected tickets">
|
||||||
</vn-confirm>
|
</vn-confirm>
|
||||||
|
|
|
@ -9,28 +9,23 @@ export default class Controller extends Section {
|
||||||
this.vnReport = vnReport;
|
this.vnReport = vnReport;
|
||||||
}
|
}
|
||||||
|
|
||||||
setDelivered() {
|
sendDocuware() {
|
||||||
const checkedTickets = this.checked;
|
const checkedTickets = this.checked;
|
||||||
let ids = [];
|
let ticketIds = [];
|
||||||
|
|
||||||
for (let ticket of checkedTickets)
|
for (let ticket of checkedTickets)
|
||||||
ids.push(ticket.id);
|
ticketIds.push(ticket.id);
|
||||||
|
|
||||||
this.$http.post('TicketTrackings/setDelivered', ids).then(res => {
|
return this.$http.post(`Docuwares/upload`, {fileCabinet: 'deliveryNote', ticketIds})
|
||||||
let state = res.data;
|
.then(res => {
|
||||||
for (let ticket of checkedTickets) {
|
let state = res.data;
|
||||||
ticket.stateFk = state.id;
|
for (let ticket of checkedTickets) {
|
||||||
ticket.state = state.name;
|
ticket.stateFk = state.id;
|
||||||
ticket.alertLevel = state.alertLevel;
|
ticket.state = state.name;
|
||||||
ticket.alertLevelCode = state.code;
|
ticket.alertLevel = state.alertLevel;
|
||||||
}
|
ticket.alertLevelCode = state.code;
|
||||||
this.openDeliveryNotes(ids);
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
openDeliveryNotes(ids) {
|
|
||||||
for (let id of ids)
|
|
||||||
this.vnReport.show(`Tickets/${id}/delivery-note-pdf`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openBalanceDialog() {
|
openBalanceDialog() {
|
||||||
|
|
|
@ -12,12 +12,14 @@ describe('Component vnTicketIndex', () => {
|
||||||
id: 2,
|
id: 2,
|
||||||
clientFk: 1,
|
clientFk: 1,
|
||||||
checked: true,
|
checked: true,
|
||||||
totalWithVat: 20.5
|
totalWithVat: 20.5,
|
||||||
|
stateFk: 1
|
||||||
}, {
|
}, {
|
||||||
id: 3,
|
id: 3,
|
||||||
clientFk: 1,
|
clientFk: 1,
|
||||||
checked: true,
|
checked: true,
|
||||||
totalWithVat: 30
|
totalWithVat: 30,
|
||||||
|
stateFk: 1
|
||||||
}];
|
}];
|
||||||
|
|
||||||
beforeEach(ngModule('ticket'));
|
beforeEach(ngModule('ticket'));
|
||||||
|
@ -86,18 +88,16 @@ describe('Component vnTicketIndex', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setDelivered()/openDeliveryNotes()', () => {
|
describe('sendDocuware()', () => {
|
||||||
it('should perform a post to setDelivered and open tabs with the delivery notes', () => {
|
it('should perform a post to sendDocuware and change tickets state', () => {
|
||||||
controller.$.model = {data: tickets, refresh: () => {}};
|
controller.$.model = {data: tickets, refresh: () => {}};
|
||||||
|
const newState = {id: 2};
|
||||||
|
|
||||||
$window.open = jest.fn();
|
$httpBackend.expect('POST', 'Docuwares/upload').respond({id: newState.id});
|
||||||
|
controller.sendDocuware();
|
||||||
$httpBackend.expect('POST', 'TicketTrackings/setDelivered').respond('ok');
|
|
||||||
controller.setDelivered();
|
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
|
|
||||||
expect($window.open).toHaveBeenCalledWith(`api/Tickets/${tickets[1].id}/delivery-note-pdf`);
|
expect(controller.$.model.data[1].stateFk).toEqual(newState.id);
|
||||||
expect($window.open).toHaveBeenCalledWith(`api/Tickets/${tickets[2].id}/delivery-note-pdf`);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@
|
||||||
<vn-th ng-click="$ctrl.sortBy('price')" field="price" number>Price</vn-th>
|
<vn-th ng-click="$ctrl.sortBy('price')" field="price" number>Price</vn-th>
|
||||||
<vn-th ng-click="$ctrl.sortBy('discount')" field="discount" number>Disc</vn-th>
|
<vn-th ng-click="$ctrl.sortBy('discount')" field="discount" number>Disc</vn-th>
|
||||||
<vn-th ng-click="$ctrl.sortBy('amount')" field="amount" number>Amount</vn-th>
|
<vn-th ng-click="$ctrl.sortBy('amount')" field="amount" number>Amount</vn-th>
|
||||||
<vn-th ng-click="$ctrl.sortBy('itemPackingTypeFk')" field="itemPackingTypeFk" shrink>Packaging</vn-th>
|
<vn-th ng-click="$ctrl.sortBy('item.itemPackingTypeFk')" field="itemPackingTypeFk" shrink>Packaging</vn-th>
|
||||||
<vn-th shrink></vn-th>
|
<vn-th shrink></vn-th>
|
||||||
</vn-tr>
|
</vn-tr>
|
||||||
</vn-thead>
|
</vn-thead>
|
||||||
|
@ -202,7 +202,7 @@
|
||||||
</span>
|
</span>
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td number>
|
<vn-td number>
|
||||||
{{$ctrl.getSaleTotal(sale) | currency: 'EUR':2}}
|
{{sale.amount | currency: 'EUR':2}}
|
||||||
</vn-td>
|
</vn-td>
|
||||||
<vn-td shrink>
|
<vn-td shrink>
|
||||||
{{::sale.item.itemPackingTypeFk | dashIfEmpty}}
|
{{::sale.item.itemPackingTypeFk | dashIfEmpty}}
|
||||||
|
|
|
@ -34,6 +34,11 @@ class Controller extends Section {
|
||||||
}
|
}
|
||||||
|
|
||||||
get sales() {
|
get sales() {
|
||||||
|
if (this._sales) {
|
||||||
|
for (let sale of this._sales)
|
||||||
|
sale.amount = this.getSaleTotal(sale);
|
||||||
|
}
|
||||||
|
|
||||||
return this._sales;
|
return this._sales;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +54,7 @@ class Controller extends Section {
|
||||||
|
|
||||||
return ticketState && ticketState.state.code;
|
return ticketState && ticketState.state.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfig() {
|
getConfig() {
|
||||||
let filter = {
|
let filter = {
|
||||||
fields: ['daysForWarningClaim'],
|
fields: ['daysForWarningClaim'],
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx, ('getLeaves', {
|
Self.remoteMethodCtx('getLeaves', {
|
||||||
description: 'Returns the nodes for a department',
|
description: 'Returns the nodes for a department',
|
||||||
accepts: [{
|
accepts: [{
|
||||||
arg: 'parentId',
|
arg: 'parentId',
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
name: zone event
|
name: event
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
zoneFk: zone
|
zoneFk: zone
|
||||||
type: type
|
type: type
|
||||||
dated: dated
|
dated: dated
|
||||||
started: started
|
started: starts
|
||||||
ended: ended
|
ended: ends
|
||||||
weekDays: week days
|
weekDays: week days
|
||||||
hour: hour
|
hour: hour
|
||||||
travelingDays: traveling days
|
travelingDays: traveling days
|
||||||
price: price
|
price: price
|
||||||
bonus: bonus
|
bonus: bonus
|
||||||
m3Max: max m3
|
m3Max: max. m3
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
name: evento zona
|
name: evento
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
zoneFk: zona
|
zoneFk: zona
|
||||||
type: tipo
|
type: tipo
|
||||||
dated: fecha
|
dated: fecha
|
||||||
started: comenzado
|
started: empieza
|
||||||
ended: terminado
|
ended: termina
|
||||||
weekDays: días semana
|
weekDays: días semana
|
||||||
hour: hora
|
hour: hora
|
||||||
travelingDays: días de viaje
|
travelingDays: días de viaje
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: zone exclusion
|
name: exclusion
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
dated: dated
|
dated: date
|
||||||
zoneFk: zone
|
zoneFk: zone
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: zone exclusion
|
name: exclusión
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
dated: fecha
|
dated: fecha
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
name: zone included
|
name: inclusion
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
dated: dated
|
dated: dated
|
||||||
zoneFk: zone
|
zoneFk: zone
|
||||||
|
isIncluded: incluida
|
||||||
|
geoFk: localización
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
name: zona incluida
|
name: inclusión
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
dated: fecha
|
dated: fecha
|
||||||
zoneFk: zona
|
zoneFk: zona
|
||||||
|
isIncluded: incluida
|
||||||
|
geoFk: localización
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: zone warehouse
|
name: warehouse
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
warehouseFk: warehouse
|
warehouseFk: warehouse
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
name: almacén zona
|
name: almacén
|
||||||
columns:
|
columns:
|
||||||
id: id
|
id: id
|
||||||
warehouseFk: almacén
|
warehouseFk: almacén
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "salix-back",
|
"name": "salix-back",
|
||||||
"version": "23.24.01",
|
"version": "23.26.01",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
const Stylesheet = require(`vn-print/core/stylesheet`);
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const vnPrintPath = path.resolve('print');
|
||||||
|
|
||||||
|
module.exports = new Stylesheet([
|
||||||
|
`${vnPrintPath}/common/css/spacing.css`,
|
||||||
|
`${vnPrintPath}/common/css/misc.css`,
|
||||||
|
`${vnPrintPath}/common/css/layout.css`,
|
||||||
|
`${vnPrintPath}/common/css/email.css`,
|
||||||
|
`${__dirname}/style.css`])
|
||||||
|
.mergeStyles();
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
.external-link {
|
||||||
|
border: 2px dashed #8dba25;
|
||||||
|
border-radius: 3px;
|
||||||
|
text-align: center
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<email-body v-bind="$props">
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="grid-block vn-pa-ml">
|
||||||
|
<p>
|
||||||
|
{{$t('dear')}}
|
||||||
|
</p>
|
||||||
|
<p v-html="$t('body',[currencyName,referenceCurrent])"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</email-body>
|
|
@ -0,0 +1,19 @@
|
||||||
|
const Component = require(`vn-print/core/component`);
|
||||||
|
const emailBody = new Component('email-body');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'entry-update-comission',
|
||||||
|
components: {
|
||||||
|
'email-body': emailBody.build(),
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
currencyName: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
referenceCurrent: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
subject: Actualización tipo de cambio en entradas
|
||||||
|
title: Actualización tipo de cambio en entradas
|
||||||
|
dear: Hola,
|
||||||
|
body: 'El tipo de cambio para las ENTRADAS/COMPRAS en <strong>{0}</strong> se ha actualizado a partir de hoy en: <strong>{1}</strong>'
|
|
@ -0,0 +1,11 @@
|
||||||
|
const Stylesheet = require(`vn-print/core/stylesheet`);
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const vnPrintPath = path.resolve('print');
|
||||||
|
|
||||||
|
module.exports = new Stylesheet([
|
||||||
|
`${vnPrintPath}/common/css/spacing.css`,
|
||||||
|
`${vnPrintPath}/common/css/misc.css`,
|
||||||
|
`${vnPrintPath}/common/css/layout.css`,
|
||||||
|
`${vnPrintPath}/common/css/email.css`])
|
||||||
|
.mergeStyles();
|
|
@ -0,0 +1,3 @@
|
||||||
|
subject: Modified entry
|
||||||
|
title: Modified entry
|
||||||
|
description: From Warehouse the following packaging entry has been created/modified
|
|
@ -0,0 +1,3 @@
|
||||||
|
subject: Entrada modificada
|
||||||
|
title: Entrada modificada
|
||||||
|
description: Desde Almacén se ha creado/modificado la siguiente entrada de embalajes
|
|
@ -0,0 +1,11 @@
|
||||||
|
<email-body v-bind="$props">
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="grid-block vn-pa-ml">
|
||||||
|
<h1>{{ $t('title') }}</h1>
|
||||||
|
<p>
|
||||||
|
{{ $t('description') }}:
|
||||||
|
<a :href="url">{{ url }}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</email-body>
|
|
@ -0,0 +1,16 @@
|
||||||
|
const Component = require(`vn-print/core/component`);
|
||||||
|
const emailBody = new Component('email-body');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'modified-entry',
|
||||||
|
components: {
|
||||||
|
'email-body': emailBody.build(),
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -242,7 +242,7 @@
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="columns vn-mt-xl" v-if="invoice.payMethodCode == 'wireTransfer' && invoice.iban">
|
<div class="columns vn-mt-lg" v-if="(invoice.payMethodCode == 'wireTransfer' && invoice.iban) || ticketObservations">
|
||||||
<div class="size50 pull-left no-page-break">
|
<div class="size50 pull-left no-page-break">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="header">{{$t('observations')}}</div>
|
<div class="header">{{$t('observations')}}</div>
|
||||||
|
@ -267,9 +267,7 @@
|
||||||
v-bind:left-text="$t('invoiceRef', [invoice.ref])"
|
v-bind:left-text="$t('invoiceRef', [invoice.ref])"
|
||||||
v-bind:center-text="client.socialName"
|
v-bind:center-text="client.socialName"
|
||||||
v-bind:recipient-id="client.id"
|
v-bind:recipient-id="client.id"
|
||||||
v-bind="$props"
|
v-bind="$props">
|
||||||
|
|
||||||
>
|
|
||||||
</report-footer>
|
</report-footer>
|
||||||
</template>
|
</template>
|
||||||
</report-body>
|
</report-body>
|
||||||
|
|
Loading…
Reference in New Issue