Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 4858-chat_realTime

This commit is contained in:
Alex Moreno 2023-02-06 10:05:17 +01:00
commit 7ad9e2cb5a
500 changed files with 6356 additions and 6523 deletions

View File

@ -5,13 +5,56 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2302.01] - 2023-01-12
## [2306.01] - 2023-02-23
### Added
- [General](Inicio) Permite recuperar la contraseña
-
### Changed
- (General -> Inicio) Ahora permite recuperar la contraseña tanto con el correo de recuperación como el usuario
### Fixed
- (Monitor de tickets) Cuando ordenas por columna, ya no se queda deshabilitado el botón de 'Actualizar'
- (Zone -> Días de entrega) Al hacer click en un día, muestra correctamente las zonas
## [2304.01] - 2023-02-09
### Added
- (Rutas) Al descargar varias facturas se comprime en un zip
- (Trabajadores -> Nuevo trabajador) Nueva sección
- (Tickets -> Adelantar tickets) Añadidos campos "líneas" y "litros" al ticket origen
- (Tickets -> Adelantar tickets) Nuevo icono muestra cuando las agencias de los tickets origen/destino son distintas
### Changed
- (Entradas -> Compras) Cambiados los campos "Precio Grouping/Packing" por "PVP" y "Precio" por "Coste"
- (Artículos -> Últimas entradas) Cambiados los campos "P.P.U." y "P.P.P." por "PVP"
- (Rutas -> Sumario/Tickets) Actualizados campos de los tickets
- (Proveedores -> Crear/Editar) Permite añadir Proveedores con la misma razón social pero con países distintos
- (Tickets -> Adelantar tickets) Cambiados selectores de estado por checks "Pendiente origen/destino"
- (Tickets -> Adelantar tickets) Cambiado stock de destino a origen.
### Fixed
- (Artículos -> Etiquetas) Permite intercambiar la relevancia entre dos etiquetas.
- (Cliente -> Datos Fiscales) No se permite seleccionar 'Notificar vía e-mail' a los clientes sin e-mail
- (Tickets -> Datos básicos) Permite guardar la hora de envío
- (Tickets -> Añadir pago) Eliminado "null" en las referencias
- (Tickets -> Adelantar tickets) Permite ordenar por importe
- (Tickets -> Adelantar tickets) El filtrado por encajado muestra también los tickets sin tipo de encajado
## [2302.01] - 2023-01-26
### Added
- (General -> Inicio) Permite recuperar la contraseña
- (Tickets -> Opciones) Subir albarán a Docuware
- (Tickets -> Opciones) Enviar correo con PDF de Docuware
- (Artículos -> Datos Básicos) Añadido campo Unidades/Caja
### Changed
- (Reclamaciones -> Descriptor) Cambiado el campo Agencia por Zona
- (Tickets -> Líneas preparadas) Actualizada sección para que sea más visual
### Fixed
- (General) Al utilizar el traductor de Google se descuadraban los iconos
### Removed
- [Tickets](Control clientes) Eliminada sección
- (Tickets -> Control clientes) Eliminada sección

View File

@ -3,9 +3,9 @@ module.exports = Self => {
description: 'Send email to the user',
accepts: [
{
arg: 'email',
arg: 'user',
type: 'string',
description: 'The email of user',
description: 'The user name or email',
required: true
}
],
@ -15,11 +15,20 @@ module.exports = Self => {
}
});
Self.recoverPassword = async function(email) {
Self.recoverPassword = async function(user) {
const models = Self.app.models;
const usesEmail = user.indexOf('@') !== -1;
if (!usesEmail) {
const account = await models.Account.findOne({
fields: ['email'],
where: {name: user}
});
user = account.email;
}
try {
await models.user.resetPassword({email, emailTemplate: 'recover-password'});
await models.user.resetPassword({email: user, emailTemplate: 'recover-password'});
} catch (err) {
if (err.code === 'EMAIL_NOT_FOUND')
return;

View File

@ -22,7 +22,7 @@ module.exports = Self => {
Self.latest = async filter => {
const conn = Self.dataSource.connector;
const minDate = new Date();
const minDate = Date.vnNew();
minDate.setFullYear(minDate.getFullYear() - 1);
const where = {dated: {gte: minDate}};

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
describe('campaign latest()', () => {
it('should return the campaigns from the last year', async() => {
const now = new Date();
const now = Date.vnNew();
const result = await app.models.Campaign.latest();
const randomIndex = Math.floor(Math.random() * result.length);
const campaignDated = result[randomIndex].dated;
@ -12,7 +12,7 @@ describe('campaign latest()', () => {
});
it('should return the campaigns from the current year', async() => {
const now = new Date();
const now = Date.vnNew();
const currentYear = now.getFullYear();
const result = await app.models.Campaign.latest({
where: {dated: {like: `%${currentYear}%`}}

View File

@ -4,7 +4,7 @@ describe('campaign upcoming()', () => {
it('should return the upcoming campaign but from the last year', async() => {
const response = await app.models.Campaign.upcoming();
const campaignDated = response.dated;
const now = new Date();
const now = Date.vnNew();
expect(campaignDated).toEqual(jasmine.any(Date));
expect(campaignDated).toBeLessThanOrEqual(now);

View File

@ -14,7 +14,7 @@ module.exports = Self => {
});
Self.upcoming = async() => {
const minDate = new Date();
const minDate = Date.vnNew();
minDate.setFullYear(minDate.getFullYear() - 1);
return Self.findOne({

View File

@ -21,7 +21,7 @@ module.exports = Self => {
if (!this.login) return;
if (Date.now() > this.login.expires)
if (Date.vnNow() > this.login.expires)
this.login = await requestToken();
return this.login;
@ -48,7 +48,7 @@ module.exports = Self => {
userId: requestData.userId,
token: requestData.authToken
},
expires: Date.now() + (1000 * 60 * tokenLifespan)
expires: Date.vnNow() + (1000 * 60 * tokenLifespan)
};
}
}

View File

@ -33,7 +33,7 @@ module.exports = Self => {
await models.Chat.create({
senderFk: sender.id,
recipient: to,
dated: new Date(),
dated: Date.vnNew(),
checkUserStatus: 0,
message: message,
status: 'pending',

View File

@ -52,7 +52,7 @@ module.exports = Self => {
const chat = await models.Chat.create({
senderFk: sender.id,
recipient: `@${recipient.name}`,
dated: new Date(),
dated: Date.vnNew(),
checkUserStatus: 1,
message: message,
status: 'sending',

View File

@ -1,7 +1,7 @@
const models = require('vn-loopback/server/server').models;
describe('Chat sendCheckingPresence()', () => {
const today = new Date();
const today = Date.vnNew();
today.setHours(6, 0);
const chatModel = models.Chat;

View File

@ -0,0 +1,49 @@
const {Report} = require('vn-print');
module.exports = Self => {
Self.remoteMethodCtx('previousLabel', {
description: 'Returns the previa label pdf',
accessType: 'READ',
accepts: [
{
arg: 'id',
type: 'number',
required: true,
description: 'The item id',
http: {source: 'path'}
}],
returns: [
{
arg: 'body',
type: 'file',
root: true
}, {
arg: 'Content-Type',
type: 'String',
http: {target: 'header'}
}, {
arg: 'Content-Disposition',
type: 'String',
http: {target: 'header'}
}
],
http: {
path: '/:id/previousLabel',
verb: 'GET'
}
});
Self.previousLabel = async(ctx, id) => {
const args = Object.assign({}, ctx.args);
const params = {lang: ctx.req.getLocale()};
delete args.ctx;
for (const param in args)
params[param] = args[param];
const report = new Report('previa-label', params);
const stream = await report.toPdfStream();
return [stream, 'application/pdf', `filename="previa-${id}.pdf"`];
};
};

View File

@ -24,7 +24,7 @@ module.exports = Self => {
}
});
Self.setSaleQuantity = async(saleId, quantity) => {
Self.setSaleQuantity = async(saleId, quantity, options) => {
const models = Self.app.models;
const myOptions = {};
let tx;

View File

@ -32,7 +32,7 @@ module.exports = Self => {
where: {code: 'trash'}
}, myOptions);
const date = new Date();
const date = Date.vnNew();
date.setMonth(date.getMonth() - 4);
const dmsToDelete = await models.Dms.find({

View File

@ -163,7 +163,7 @@ module.exports = Self => {
fields: ['alertLevel']
});
signedTime ? signedTime != undefined : signedTime = new Date();
signedTime ? signedTime != undefined : signedTime = Date.vnNew();
if (alertLevel >= 2) {
let dir;

View File

@ -1,4 +1,4 @@
const got = require('got');
const axios = require('axios');
module.exports = Self => {
Self.remoteMethodCtx('checkFile', {
@ -8,7 +8,7 @@ module.exports = Self => {
{
arg: 'id',
type: 'number',
description: 'The id',
description: 'The id',
http: {source: 'path'}
},
{
@ -18,14 +18,14 @@ module.exports = Self => {
description: 'The fileCabinet name'
},
{
arg: 'dialog',
type: 'string',
arg: 'signed',
type: 'boolean',
required: true,
description: 'The dialog name'
description: 'If pdf is necessary to be signed'
}
],
returns: {
type: 'boolean',
type: 'object',
root: true
},
http: {
@ -34,58 +34,51 @@ module.exports = Self => {
}
});
Self.checkFile = async function(ctx, id, fileCabinet, dialog) {
const myUserId = ctx.req.accessToken.userId;
if (!myUserId)
return false;
Self.checkFile = async function(ctx, id, fileCabinet, signed) {
const models = Self.app.models;
const docuwareConfig = await models.DocuwareConfig.findOne();
const action = 'find';
const docuwareInfo = await models.Docuware.findOne({
where: {
code: fileCabinet,
dialogName: dialog
action: action
}
});
const docuwareUrl = docuwareConfig.url;
const cookie = docuwareConfig.token;
const fileCabinetName = docuwareInfo.fileCabinetName;
const find = docuwareInfo.find;
const options = {
'headers': {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Cookie': cookie
}
};
const searchFilter = {
condition: [
{
DBName: find,
DBName: docuwareInfo.findById,
Value: [id]
}
],
sortOrder: [
{
Field: 'FILENAME',
Direction: 'Desc'
}
]
};
try {
// get fileCabinetId
const fileCabinetResponse = await got.get(`${docuwareUrl}/FileCabinets`, options);
const fileCabinetJson = JSON.parse(fileCabinetResponse.body).FileCabinet;
const fileCabinetId = fileCabinetJson.find(dialogs => dialogs.Name === fileCabinetName).Id;
const options = await Self.getOptions();
// get dialog
const dialogResponse = await got.get(`${docuwareUrl}/FileCabinets/${fileCabinetId}/dialogs`, options);
const dialogJson = JSON.parse(dialogResponse.body).Dialog;
const dialogId = dialogJson.find(dialogs => dialogs.DisplayName === 'find').Id;
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
// get docuwareID
Object.assign(options, {'body': JSON.stringify(searchFilter)});
const response = await got.post(
`${docuwareUrl}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`, options);
JSON.parse(response.body).Items[0].Id;
const response = await axios.post(
`${options.url}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`,
searchFilter,
options.headers
);
const [documents] = response.data.Items;
if (!documents) return false;
return true;
const state = documents.Fields.find(field => field.FieldName == 'ESTADO');
if (signed && state.Item != 'Firmado') return false;
return {id: documents.Id};
} catch (error) {
return false;
}

View File

@ -0,0 +1,78 @@
const axios = require('axios');
module.exports = Self => {
/**
* Returns the dialog id
*
* @param {string} code - The fileCabinet name
* @param {string} action - The fileCabinet name
* @param {string} fileCabinetId - Optional The fileCabinet name
* @return {number} - The fileCabinet id
*/
Self.getDialog = async(code, action, fileCabinetId) => {
const docuwareInfo = await Self.app.models.Docuware.findOne({
where: {
code: code,
action: action
}
});
if (!fileCabinetId) fileCabinetId = await Self.getFileCabinet(code);
const options = await Self.getOptions();
if (!process.env.NODE_ENV)
return Math.round();
const response = await axios.get(`${options.url}/FileCabinets/${fileCabinetId}/dialogs`, options.headers);
const dialogs = response.data.Dialog;
const dialogId = dialogs.find(dialogs => dialogs.DisplayName === docuwareInfo.dialogName).Id;
return dialogId;
};
/**
* Returns the fileCabinetId
*
* @param {string} code - The fileCabinet code
* @return {number} - The fileCabinet id
*/
Self.getFileCabinet = async code => {
const options = await Self.getOptions();
const docuwareInfo = await Self.app.models.Docuware.findOne({
where: {
code: code
}
});
if (!process.env.NODE_ENV)
return Math.round();
const fileCabinetResponse = await axios.get(`${options.url}/FileCabinets`, options.headers);
const fileCabinets = fileCabinetResponse.data.FileCabinet;
const fileCabinetId = fileCabinets.find(fileCabinet => fileCabinet.Name === docuwareInfo.fileCabinetName).Id;
return fileCabinetId;
};
/**
* Returns basic headers
*
* @param {string} cookie - The docuware cookie
* @return {object} - The headers
*/
Self.getOptions = async() => {
const docuwareConfig = await Self.app.models.DocuwareConfig.findOne();
const headers = {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Cookie': docuwareConfig.cookie
}
};
return {
url: docuwareConfig.url,
headers
};
};
};

View File

@ -0,0 +1,72 @@
const {Email} = require('vn-print');
module.exports = Self => {
Self.remoteMethodCtx('deliveryNoteEmail', {
description: 'Sends the delivery note email with an docuware attached PDF',
accessType: 'WRITE',
accepts: [
{
arg: 'id',
type: 'string',
required: true,
description: 'The ticket id',
http: {source: 'path'}
},
{
arg: 'recipient',
type: 'string',
description: 'The recipient email',
required: true,
},
{
arg: 'recipientId',
type: 'number',
description: 'The client id',
required: false
}
],
returns: [
{
arg: 'body',
type: 'file',
root: true
}, {
arg: 'Content-Type',
type: 'String',
http: {target: 'header'}
}, {
arg: 'Content-Disposition',
type: 'String',
http: {target: 'header'}
}
],
http: {
path: '/:id/delivery-note-email',
verb: 'POST'
}
});
Self.deliveryNoteEmail = async(ctx, id) => {
const args = Object.assign({}, ctx.args);
const params = {
recipient: args.recipient,
lang: ctx.req.getLocale()
};
delete args.ctx;
for (const param in args)
params[param] = args[param];
const email = new Email('delivery-note', params);
const docuwareFile = await Self.app.models.Docuware.download(ctx, id, 'deliveryNote');
return email.send({
overrideAttachments: true,
attachments: [{
filename: `${id}.pdf`,
content: docuwareFile[0]
}]
});
};
};

View File

@ -1,5 +1,5 @@
/* eslint max-len: ["error", { "code": 180 }]*/
const got = require('got');
const axios = require('axios');
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
@ -10,19 +10,13 @@ module.exports = Self => {
{
arg: 'id',
type: 'number',
description: 'The id',
description: 'The ticket id',
http: {source: 'path'}
},
{
arg: 'fileCabinet',
type: 'string',
description: 'The id',
http: {source: 'path'}
},
{
arg: 'dialog',
type: 'string',
description: 'The id',
description: 'The file cabinet',
http: {source: 'path'}
}
],
@ -42,79 +36,26 @@ module.exports = Self => {
}
],
http: {
path: `/:id/download/:fileCabinet/:dialog`,
path: `/:id/download/:fileCabinet`,
verb: 'GET'
}
});
Self.download = async function(ctx, id, fileCabinet, dialog) {
const myUserId = ctx.req.accessToken.userId;
if (!myUserId)
throw new UserError(`You don't have enough privileges`);
Self.download = async function(ctx, id, fileCabinet) {
const models = Self.app.models;
const docuwareConfig = await models.DocuwareConfig.findOne();
const docuwareInfo = await models.Docuware.findOne({
where: {
code: fileCabinet,
dialogName: dialog
}
});
const docuwareFile = await models.Docuware.checkFile(ctx, id, fileCabinet, true);
if (!docuwareFile) throw new UserError('The DOCUWARE PDF document does not exists');
const docuwareUrl = docuwareConfig.url;
const cookie = docuwareConfig.token;
const fileCabinetName = docuwareInfo.fileCabinetName;
const find = docuwareInfo.find;
const options = {
'headers': {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Cookie': cookie
}
};
const searchFilter = {
condition: [
{
DBName: find,
Value: [id]
}
]
};
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
const options = await Self.getOptions();
options.headers.responseType = 'stream';
try {
// get fileCabinetId
const fileCabinetResponse = await got.get(`${docuwareUrl}/FileCabinets`, options);
const fileCabinetJson = JSON.parse(fileCabinetResponse.body).FileCabinet;
const fileCabinetId = fileCabinetJson.find(dialogs => dialogs.Name === fileCabinetName).Id;
const fileName = `filename="${id}.pdf"`;
const contentType = 'application/pdf';
const downloadUri = `${options.url}/FileCabinets/${fileCabinetId}/Documents/${docuwareFile.id}/FileDownload?targetFileType=Auto&keepAnnotations=false`;
// get dialog
const dialogResponse = await got.get(`${docuwareUrl}/FileCabinets/${fileCabinetId}/dialogs`, options);
const dialogJson = JSON.parse(dialogResponse.body).Dialog;
const dialogId = dialogJson.find(dialogs => dialogs.DisplayName === 'find').Id;
const stream = await axios.get(downloadUri, options.headers);
// get docuwareID
Object.assign(options, {'body': JSON.stringify(searchFilter)});
const response = await got.post(`${docuwareUrl}/FileCabinets/${fileCabinetId}/Query/DialogExpression?dialogId=${dialogId}`, options);
const docuwareId = JSON.parse(response.body).Items[0].Id;
// download & save file
const fileName = `filename="${id}.pdf"`;
const contentType = 'application/pdf';
const downloadUri = `${docuwareUrl}/FileCabinets/${fileCabinetId}/Documents/${docuwareId}/FileDownload?targetFileType=Auto&keepAnnotations=false`;
const downloadOptions = {
'headers': {
'Cookie': cookie
}
};
const stream = got.stream(downloadUri, downloadOptions);
return [stream, contentType, fileName];
} catch (error) {
if (error.code === 'ENOENT')
throw new UserError('The DOCUWARE PDF document does not exists');
throw error;
}
return [stream.data, contentType, fileName];
};
};

View File

@ -1,5 +1,5 @@
const models = require('vn-loopback/server/server').models;
const got = require('got');
const axios = require('axios');
describe('docuware download()', () => {
const ticketId = 1;
@ -12,53 +12,71 @@ describe('docuware download()', () => {
}
};
const fileCabinetName = 'deliveryClient';
const dialogDisplayName = 'find';
const dialogName = 'findTicket';
const docuwareModel = models.Docuware;
const fileCabinetName = 'deliveryNote';
const gotGetResponse = {
body: JSON.stringify(
{
FileCabinet: [
{Id: 12, Name: fileCabinetName}
],
Dialog: [
{Id: 34, DisplayName: dialogDisplayName}
]
})
};
it('should return exist file in docuware', async() => {
const gotPostResponse = {
body: JSON.stringify(
{
Items: [
{Id: 56}
],
})
};
spyOn(got, 'get').and.returnValue(new Promise(resolve => resolve(gotGetResponse)));
spyOn(got, 'post').and.returnValue(new Promise(resolve => resolve(gotPostResponse)));
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, dialogName);
expect(result).toEqual(true);
beforeAll(() => {
spyOn(docuwareModel, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(docuwareModel, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
});
it('should return not exist file in docuware', async() => {
const gotPostResponse = {
body: JSON.stringify(
{
Items: [],
})
it('should return false if there are no documents', async() => {
const response = {
data: {
Items: []
}
};
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(response)));
spyOn(got, 'get').and.returnValue(new Promise(resolve => resolve(gotGetResponse)));
spyOn(got, 'post').and.returnValue(new Promise(resolve => resolve(gotPostResponse)));
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, dialogName);
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, true);
expect(result).toEqual(false);
});
it('should return false if the document is unsigned', async() => {
const response = {
data: {
Items: [
{
Id: 1,
Fields: [
{
FieldName: 'ESTADO',
Item: 'Unsigned'
}
]
}
]
}
};
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(response)));
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, true);
expect(result).toEqual(false);
});
it('should return the document data', async() => {
const docuwareId = 1;
const response = {
data: {
Items: [
{
Id: docuwareId,
Fields: [
{
FieldName: 'ESTADO',
Item: 'Firmado'
}
]
}
]
}
};
spyOn(axios, 'post').and.returnValue(new Promise(resolve => resolve(response)));
const result = await models.Docuware.checkFile(ctx, ticketId, fileCabinetName, true);
expect(result.id).toEqual(docuwareId);
});
});

View File

@ -1,5 +1,5 @@
const models = require('vn-loopback/server/server').models;
const got = require('got');
const axios = require('axios');
const stream = require('stream');
describe('docuware download()', () => {
@ -13,36 +13,33 @@ describe('docuware download()', () => {
}
};
it('should return the downloaded file name', async() => {
const fileCabinetName = 'deliveryClient';
const dialogDisplayName = 'find';
const dialogName = 'findTicket';
const gotGetResponse = {
body: JSON.stringify(
{
FileCabinet: [
{Id: 12, Name: fileCabinetName}
],
Dialog: [
{Id: 34, DisplayName: dialogDisplayName}
]
})
};
const docuwareModel = models.Docuware;
const fileCabinetName = 'deliveryNote';
const gotPostResponse = {
body: JSON.stringify(
{
Items: [
{Id: 56}
],
})
};
beforeAll(() => {
spyOn(docuwareModel, 'getFileCabinet').and.returnValue((new Promise(resolve => resolve(Math.random()))));
spyOn(docuwareModel, 'getDialog').and.returnValue((new Promise(resolve => resolve(Math.random()))));
});
spyOn(got, 'get').and.returnValue(new Promise(resolve => resolve(gotGetResponse)));
spyOn(got, 'post').and.returnValue(new Promise(resolve => resolve(gotPostResponse)));
spyOn(got, 'stream').and.returnValue(new stream.PassThrough({objectMode: true}));
it('should return error if file not exist', async() => {
spyOn(docuwareModel, 'checkFile').and.returnValue(false);
spyOn(axios, 'get').and.returnValue(new stream.PassThrough({objectMode: true}));
const result = await models.Docuware.download(ctx, ticketId, fileCabinetName, dialogName);
let error;
try {
await models.Docuware.download(ctx, ticketId, fileCabinetName);
} catch (e) {
error = e.message;
}
expect(error).toEqual('The DOCUWARE PDF document does not exists');
});
it('should return the downloaded file if exist file ', async() => {
spyOn(docuwareModel, 'checkFile').and.returnValue({});
spyOn(axios, 'get').and.returnValue(new stream.PassThrough({objectMode: true}));
const result = await models.Docuware.download(ctx, ticketId, fileCabinetName);
expect(result[1]).toEqual('application/pdf');
expect(result[2]).toEqual(`filename="${ticketId}.pdf"`);

View File

@ -0,0 +1,37 @@
const models = require('vn-loopback/server/server').models;
describe('docuware upload()', () => {
const userId = 9;
const ticketId = 10;
const ctx = {
req: {
getLocale: () => {
return 'en';
},
accessToken: {userId: userId},
headers: {origin: 'http://localhost:5000'},
}
};
const docuwareModel = models.Docuware;
const ticketModel = models.Ticket;
const fileCabinetName = 'deliveryNote';
beforeAll(() => {
spyOn(docuwareModel, 'getFileCabinet').and.returnValue(new Promise(resolve => resolve(Math.random())));
spyOn(docuwareModel, 'getDialog').and.returnValue(new Promise(resolve => resolve(Math.random())));
});
it('should try upload file', async() => {
spyOn(ticketModel, 'deliveryNotePdf').and.returnValue(new Promise(resolve => resolve({})));
let error;
try {
await models.Docuware.upload(ctx, ticketId, fileCabinetName);
} catch (e) {
error = e.message;
}
expect(error).toEqual('Action not allowed on the test environment');
});
});

View File

@ -0,0 +1,141 @@
const UserError = require('vn-loopback/util/user-error');
const axios = require('axios');
module.exports = Self => {
Self.remoteMethodCtx('upload', {
description: 'Upload an docuware PDF',
accessType: 'WRITE',
accepts: [
{
arg: 'id',
type: 'number',
description: 'The ticket id',
http: {source: 'path'}
},
{
arg: 'fileCabinet',
type: 'string',
description: 'The file cabinet'
},
{
arg: 'dialog',
type: 'string',
description: 'The dialog'
}
],
returns: [],
http: {
path: `/:id/upload`,
verb: 'POST'
}
});
Self.upload = async function(ctx, id, fileCabinet) {
const models = Self.app.models;
const action = 'store';
const options = await Self.getOptions();
const fileCabinetId = await Self.getFileCabinet(fileCabinet);
const dialogId = await Self.getDialog(fileCabinet, action, fileCabinetId);
// get delivery note
const deliveryNote = await models.Ticket.deliveryNotePdf(ctx, {
id,
type: 'deliveryNote'
});
// get ticket data
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');
});
};
};

View File

@ -230,7 +230,7 @@ module.exports = Self => {
UPDATE edi.tableConfig
SET updated = ?
WHERE fileName = ?
`, [new Date(), baseName], options);
`, [Date.vnNew(), baseName], options);
}
console.log(`Updated table ${toTable}\n`);

View File

@ -32,7 +32,7 @@ module.exports = Self => {
if (!config.cleanDays) return;
const cleanDate = new Date();
const cleanDate = Date.vnNew();
cleanDate.setDate(cleanDate.getDate() - config.cleanDays);
await models.NotificationQueue.destroyAll({

View File

@ -10,7 +10,7 @@ describe('Notification Clean()', () => {
const notification = await models.Notification.findOne({}, options);
const notificationConfig = await models.NotificationConfig.findOne({});
const cleanDate = new Date();
const cleanDate = Date.vnNew();
cleanDate.setDate(cleanDate.getDate() - (notificationConfig.cleanDays + 1));
let before;

View File

@ -25,36 +25,43 @@ module.exports = Self => {
return false;
const con = mysql.createConnection({
host: `${config.hostDb}`,
user: `${config.userDb}`,
password: `${config.passwordDb}`,
port: `${config.portDb}`
host: config.hostDb,
user: config.userDb,
password: config.passwordDb,
port: config.portDb
});
const sql = `SELECT ot.ticket_id, ot.number
FROM osticket.ost_ticket ot
JOIN osticket.ost_ticket_status ots ON ots.id = ot.status_id
JOIN osticket.ost_ticket_status ots ON ots.id = ot.status_id
JOIN osticket.ost_thread ot2 ON ot2.object_id = ot.ticket_id AND ot2.object_type = 'T'
JOIN (
SELECT ote.thread_id, MAX(ote.created) created, MAX(ote.updated) updated
FROM osticket.ost_thread_entry ote
WHERE ote.staff_id != 0 AND ote.type = 'R'
GROUP BY ote.thread_id
) sub ON sub.thread_id = ot2.id
WHERE ot.isanswered = 1
AND ots.state = '${config.oldStatus}'
AND IF(sub.updated > sub.created, sub.updated, sub.created) < DATE_SUB(CURDATE(), INTERVAL ${config.day} DAY)`;
SELECT sub2.thread_id, sub2.type, sub2.updated, sub2.created
FROM (
SELECT ote.thread_id, ote.created, ote.updated, ote.type
FROM osticket.ost_thread_entry ote
WHERE ote.staff_id
ORDER BY ote.id DESC
LIMIT 10000000000000000000) sub2
GROUP BY sub2.thread_id
) sub ON sub.thread_id = ot2.id
WHERE ot.isanswered
AND ots.id IN (?)
AND sub.type = 'R'
AND IF(sub.updated > sub.created, sub.updated, sub.created) < DATE_SUB(CURDATE(), INTERVAL ? DAY);`;
const ticketsId = [];
const statusIdToClose = config.oldStatus.split(',');
let ticketsId = [];
con.connect(err => {
if (err) throw err;
con.query(sql, (err, results) => {
if (err) throw err;
for (const result of results)
ticketsId.push(result.ticket_id);
});
con.query(sql, [statusIdToClose, config.day],
(err, results) => {
if (err) throw err;
for (const result of results)
ticketsId.push(result.ticket_id);
});
});
await getRequestToken();
async function getRequestToken() {
@ -94,6 +101,44 @@ module.exports = Self => {
await close(token, secondCookie);
}
async function close(token, secondCookie) {
for (const ticketId of ticketsId) {
try {
const lock = await getLockCode(token, secondCookie, ticketId);
if (!lock.code) {
let error = `Can't get lock code`;
if (lock.msg) error += `: ${lock.msg}`;
throw new Error(error);
}
let form = new FormData();
form.append('__CSRFToken__', token);
form.append('id', ticketId);
form.append('a', config.responseType);
form.append('lockCode', lock.code);
form.append('from_email_id', config.fromEmailId);
form.append('reply-to', config.replyTo);
form.append('cannedResp', 0);
form.append('response', config.comment);
form.append('signature', 'none');
form.append('reply_status_id', config.newStatusId);
const ostUri = `${config.host}/tickets.php?id=${ticketId}`;
const params = {
method: 'POST',
body: form,
headers: {
'Cookie': secondCookie
}
};
await fetch(ostUri, params);
} catch (e) {
const err = new Error(`${ticketId} Ticket close failed: ${e.message}`);
err.stack += e.stack;
console.error(err);
}
}
}
async function getLockCode(token, secondCookie, ticketId) {
const ostUri = `${config.host}/ajax.php/lock/ticket/${ticketId}`;
const params = {
@ -107,34 +152,7 @@ module.exports = Self => {
const body = await response.text();
const json = JSON.parse(body);
return json.code;
}
async function close(token, secondCookie) {
for (const ticketId of ticketsId) {
const lockCode = await getLockCode(token, secondCookie, ticketId);
let form = new FormData();
form.append('__CSRFToken__', token);
form.append('id', ticketId);
form.append('a', config.responseType);
form.append('lockCode', lockCode);
form.append('from_email_id', config.fromEmailId);
form.append('reply-to', config.replyTo);
form.append('cannedResp', 0);
form.append('response', config.comment);
form.append('signature', 'none');
form.append('reply_status_id', config.newStatusId);
const ostUri = `${config.host}/tickets.php?id=${ticketId}`;
const params = {
method: 'POST',
body: form,
headers: {
'Cookie': secondCookie
}
};
return fetch(ostUri, params);
}
return json;
}
};
};

View File

@ -3,4 +3,5 @@ module.exports = Self => {
require('../methods/collection/newCollection')(Self);
require('../methods/collection/getSectors')(Self);
require('../methods/collection/setSaleQuantity')(Self);
require('../methods/collection/previousLabel')(Self);
};

View File

@ -16,7 +16,7 @@
"url": {
"type": "string"
},
"token": {
"cookie": {
"type": "string"
}
},
@ -29,4 +29,4 @@
"permission": "ALLOW"
}
]
}
}

View File

@ -1,4 +1,7 @@
module.exports = Self => {
require('../methods/docuware/download')(Self);
require('../methods/docuware/upload')(Self);
require('../methods/docuware/checkFile')(Self);
require('../methods/docuware/deliveryNoteEmail')(Self);
require('../methods/docuware/core')(Self);
};

View File

@ -19,20 +19,14 @@
"fileCabinetName": {
"type": "string"
},
"action": {
"type": "string"
},
"dialogName": {
"type": "string"
},
"find": {
"findById": {
"type": "string"
}
},
"acls": [
{
"property": "*",
"accessType": "*",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
}
]
}
}
}

View File

@ -77,7 +77,7 @@ module.exports = Self => {
const newImage = await Self.upsertWithWhere(data, {
name: fileName,
collectionFk: collectionName,
updated: (new Date).getTime()
updated: Date.vnNow()
}, myOptions);
// Resizes and saves the image

View File

@ -6,6 +6,16 @@
"table": "util.notificationAcl"
}
},
"properties":{
"notificationFk": {
"id": true,
"type": "number"
},
"roleFk":{
"id": true,
"type": "number"
}
},
"relations": {
"notification": {
"type": "belongsTo",

View File

@ -0,0 +1,62 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.observe('before save', async function(ctx) {
const models = Self.app.models;
const userId = ctx.options.accessToken.userId;
const user = await ctx.instance.userFk;
const modifiedUser = await getUserToModify(null, user, models);
if (userId != modifiedUser.id && userId != modifiedUser.bossFk)
throw new UserError('You dont have permission to modify this user');
});
Self.remoteMethod('deleteNotification', {
description: 'Deletes a notification subscription',
accepts: [
{
arg: 'ctx',
type: 'object',
http: {source: 'context'}
},
{
arg: 'notificationId',
type: 'number',
required: true
},
],
returns: {
type: 'object',
root: true
},
http: {
verb: 'POST',
path: '/deleteNotification'
}
});
Self.deleteNotification = async function(ctx, notificationId) {
const models = Self.app.models;
const user = ctx.req.accessToken.userId;
const modifiedUser = await getUserToModify(notificationId, null, models);
if (user != modifiedUser.id && user != modifiedUser.bossFk)
throw new UserError('You dont have permission to modify this user');
await models.NotificationSubscription.destroyById(notificationId);
};
async function getUserToModify(notificationId, userFk, models) {
let userToModify = userFk;
if (notificationId) {
const subscription = await models.NotificationSubscription.findById(notificationId);
userToModify = subscription.userFk;
}
return await models.Worker.findOne({
fields: ['id', 'bossFk'],
where: {
id: userToModify
}
});
}
};

View File

@ -7,15 +7,18 @@
}
},
"properties": {
"notificationFk": {
"id": {
"type": "number",
"id": true,
"description": "Identifier"
"description": "Primary key"
},
"notificationFk": {
"type": "number",
"description": "Foreign key to Notification"
},
"userFk": {
"type": "number",
"id": true,
"description": "Identifier"
"description": "Foreign key to Account"
}
},
"relations": {

View File

@ -0,0 +1,74 @@
const models = require('vn-loopback/server/server').models;
describe('loopback model NotificationSubscription', () => {
it('Should fail to delete a notification if the user is not editing itself or a subordinate', async() => {
const tx = await models.NotificationSubscription.beginTransaction({});
try {
const options = {transaction: tx};
const user = 9;
const notificationSubscriptionId = 2;
const ctx = {req: {accessToken: {userId: user}}};
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
let error;
try {
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
} catch (e) {
error = e;
}
expect(error.message).toContain('You dont have permission to modify this user');
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('Should delete a notification if the user is editing itself', async() => {
const tx = await models.NotificationSubscription.beginTransaction({});
try {
const options = {transaction: tx};
const user = 9;
const notificationSubscriptionId = 4;
const ctx = {req: {accessToken: {userId: user}}};
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
expect(deletedNotification).toBeNull();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('Should delete a notification if the user is editing a subordinate', async() => {
const tx = await models.NotificationSubscription.beginTransaction({});
try {
const options = {transaction: tx};
const user = 9;
const notificationSubscriptionId = 5;
const ctx = {req: {accessToken: {userId: user}}};
const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
expect(deletedNotification).toBeNull();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
});

View File

@ -1 +0,0 @@
insert into `util`.`notification` (`id`, `name`,`description`) values (2, 'invoiceElectronic', 'A electronic invoice has been generated');

View File

@ -0,0 +1,3 @@
UPDATE `vn`.`collection`
SET sectorFk=1
WHERE id=1;

View File

@ -0,0 +1,225 @@
DROP PROCEDURE IF EXISTS `vn`.`invoiceOut_new`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`invoiceOut_new`(
vSerial VARCHAR(255),
vInvoiceDate DATETIME,
vTaxArea VARCHAR(25),
OUT vNewInvoiceId INT)
BEGIN
/**
* Creación de facturas emitidas.
* requiere previamente tabla ticketToInvoice(id).
*
* @param vSerial serie a la cual se hace la factura
* @param vInvoiceDate fecha de la factura
* @param vTaxArea tipo de iva en relacion a la empresa y al cliente
* @param vNewInvoiceId id de la factura que se acaba de generar
* @return vNewInvoiceId
*/
DECLARE vSpainCountryCode INT DEFAULT 1;
DECLARE vIsAnySaleToInvoice BOOL;
DECLARE vIsAnyServiceToInvoice BOOL;
DECLARE vNewRef VARCHAR(255);
DECLARE vWorker INT DEFAULT account.myUser_getId();
DECLARE vCompany INT;
DECLARE vSupplier INT;
DECLARE vClient INT;
DECLARE vCplusStandardInvoiceTypeFk INT DEFAULT 1;
DECLARE vCplusCorrectingInvoiceTypeFk INT DEFAULT 6;
DECLARE vCplusSimplifiedInvoiceTypeFk INT DEFAULT 2;
DECLARE vCorrectingSerial VARCHAR(1) DEFAULT 'R';
DECLARE vSimplifiedSerial VARCHAR(1) DEFAULT 'S';
DECLARE vNewInvoiceInId INT;
DECLARE vIsInterCompany BOOL;
SET vInvoiceDate = IFNULL(vInvoiceDate,CURDATE());
SELECT t.clientFk, t.companyFk
INTO vClient, vCompany
FROM ticketToInvoice tt
JOIN ticket t ON t.id = tt.id
LIMIT 1;
-- Eliminem de ticketToInvoice els tickets que no han de ser facturats
DELETE ti.*
FROM ticketToInvoice ti
JOIN ticket t ON t.id = ti.id
JOIN sale s ON s.ticketFk = t.id
JOIN item i ON i.id = s.itemFk
JOIN supplier su ON su.id = t.companyFk
JOIN client c ON c.id = t.clientFk
LEFT JOIN itemTaxCountry itc ON itc.itemFk = i.id AND itc.countryFk = su.countryFk
WHERE YEAR(t.shipped) < 2001
OR c.isTaxDataChecked = FALSE
OR t.isDeleted
OR c.hasToInvoice = FALSE
OR itc.id IS NULL;
SELECT SUM(s.quantity * s.price * (100 - s.discount)/100), ts.id
INTO vIsAnySaleToInvoice, vIsAnyServiceToInvoice
FROM ticketToInvoice t
LEFT JOIN sale s ON s.ticketFk = t.id
LEFT JOIN ticketService ts ON ts.ticketFk = t.id;
IF (vIsAnySaleToInvoice OR vIsAnyServiceToInvoice)
AND (vCorrectingSerial = vSerial OR NOT hasAnyNegativeBase())
THEN
-- el trigger añade el siguiente Id_Factura correspondiente a la vSerial
INSERT INTO invoiceOut
(
ref,
serial,
issued,
clientFk,
dued,
companyFk,
cplusInvoiceType477Fk
)
SELECT
1,
vSerial,
vInvoiceDate,
vClient,
getDueDate(vInvoiceDate, dueDay),
vCompany,
IF(vSerial = vCorrectingSerial,
vCplusCorrectingInvoiceTypeFk,
IF(vSerial = vSimplifiedSerial,
vCplusSimplifiedInvoiceTypeFk,
vCplusStandardInvoiceTypeFk))
FROM client
WHERE id = vClient;
SET vNewInvoiceId = LAST_INSERT_ID();
SELECT `ref`
INTO vNewRef
FROM invoiceOut
WHERE id = vNewInvoiceId;
UPDATE ticket t
JOIN ticketToInvoice ti ON ti.id = t.id
SET t.refFk = vNewRef;
DROP TEMPORARY TABLE IF EXISTS tmp.updateInter;
CREATE TEMPORARY TABLE tmp.updateInter ENGINE = MEMORY
SELECT s.id,ti.id ticket_id,vWorker Id_Trabajador
FROM ticketToInvoice ti
LEFT JOIN ticketState ts ON ti.id = ts.ticket
JOIN state s
WHERE IFNULL(ts.alertLevel,0) < 3 and s.`code` = getAlert3State(ti.id);
INSERT INTO vncontrol.inter(state_id,Id_Ticket,Id_Trabajador)
SELECT * FROM tmp.updateInter;
INSERT INTO ticketLog (action, userFk, originFk, description)
SELECT 'UPDATE', account.myUser_getId(), ti.id, CONCAT('Crea factura ', vNewRef)
FROM ticketToInvoice ti;
CALL invoiceExpenceMake(vNewInvoiceId);
CALL invoiceTaxMake(vNewInvoiceId,vTaxArea);
UPDATE invoiceOut io
JOIN (
SELECT SUM(amount) AS total
FROM invoiceOutExpence
WHERE invoiceOutFk = vNewInvoiceId
) base
JOIN (
SELECT SUM(vat) AS total
FROM invoiceOutTax
WHERE invoiceOutFk = vNewInvoiceId
) vat
SET io.amount = base.total + vat.total
WHERE io.id = vNewInvoiceId;
DROP TEMPORARY TABLE tmp.updateInter;
SELECT ios.isCEE INTO vIsInterCompany
FROM vn.ticket t
JOIN vn.invoiceOut io ON io.`ref` = t.refFk
JOIN vn.invoiceOutSerial ios ON ios.code = io.serial
WHERE t.refFk = vNewRef
LIMIT 1;
IF (vIsInterCompany) THEN
SELECT vCompany INTO vSupplier;
SELECT id INTO vCompany FROM company WHERE clientFk = vClient;
INSERT INTO invoiceIn(supplierFk, supplierRef, issued, companyFk)
SELECT vSupplier, vNewRef, vInvoiceDate, vCompany;
SET vNewInvoiceInId = LAST_INSERT_ID();
DROP TEMPORARY TABLE IF EXISTS tmp.ticket;
CREATE TEMPORARY TABLE tmp.ticket
(KEY (ticketFk))
ENGINE = MEMORY
SELECT id ticketFk
FROM ticketToInvoice;
CALL `ticket_getTax`('NATIONAL');
SET @vTaxableBaseServices := 0.00;
SET @vTaxCodeGeneral := NULL;
INSERT INTO vn.invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
SELECT vNewInvoiceInId, @vTaxableBaseServices, sub.expenceFk, sub.taxTypeSageFk , sub.transactionTypeSageFk
FROM (
SELECT @vTaxableBaseServices := SUM(tst.taxableBase) taxableBase, i.expenceFk, i.taxTypeSageFk , i.transactionTypeSageFk, @vTaxCodeGeneral := i.taxClassCodeFk
FROM tmp.ticketServiceTax tst
JOIN vn.invoiceOutTaxConfig i ON i.taxClassCodeFk = tst.code
WHERE i.isService
HAVING taxableBase
) sub;
INSERT INTO vn.invoiceInTax(invoiceInFk, taxableBase, expenceFk, taxTypeSageFk, transactionTypeSageFk)
SELECT vNewInvoiceInId, SUM(tt.taxableBase) - IF(tt.code = @vTaxCodeGeneral, @vTaxableBaseServices, 0) taxableBase, i.expenceFk, i.taxTypeSageFk , i.transactionTypeSageFk
FROM tmp.ticketTax tt
JOIN vn.invoiceOutTaxConfig i ON i.taxClassCodeFk = tt.code
WHERE !i.isService
GROUP BY tt.pgcFk
HAVING taxableBase
ORDER BY tt.priority;
CALL invoiceInDueDay_calculate(vNewInvoiceInId);
INSERT INTO invoiceInIntrastat (
invoiceInFk,
intrastatFk,
amount,
stems,
countryFk,
net)
SELECT
vNewInvoiceInId invoiceInFk,
i.intrastatFk,
CAST(SUM((s.quantity * s.price * (100 - s.discount) / 100 )) AS DECIMAL(10,2)) subtotal,
CAST(SUM(IFNULL(i.stems, 1) * s.quantity) AS DECIMAL(10,2)) stems,
su.countryFk,
CAST(SUM(IFNULL(i.stems, 1)
* s.quantity
* IF(ic.grams, ic.grams, i.weightByPiece) / 1000) AS DECIMAL(10,2)) netKg
FROM sale s
JOIN ticket t ON s.ticketFk = t.id
JOIN supplier su ON su.id = t.companyFk
JOIN item i ON i.id = s.itemFk
JOIN vn.itemCost ic ON ic.itemFk = i.id AND ic.warehouseFk = t.warehouseFk
JOIN intrastat ir ON ir.id = i.intrastatFk
WHERE t.refFk = vNewRef;
DROP TEMPORARY TABLE tmp.ticket;
DROP TEMPORARY TABLE tmp.ticketAmount;
DROP TEMPORARY TABLE tmp.ticketTax;
DROP TEMPORARY TABLE tmp.ticketServiceTax;
END IF;
END IF;
DROP TEMPORARY TABLE `ticketToInvoice`;
END$$
DELIMITER ;

View File

@ -43,7 +43,7 @@ SET t.code = 'claim'
WHERE t.code LIKE 'Claims' ESCAPE '#';
UPDATE salix.module t
SET t.code = 'user'
SET t.code = 'account'
WHERE t.code LIKE 'Users' ESCAPE '#';
UPDATE salix.module t

View File

@ -0,0 +1,28 @@
ALTER TABLE `vn`.`mdbApp` DROP PRIMARY KEY;
ALTER TABLE `vn`.`mdbApp` ADD CONSTRAINT mdbApp_PK PRIMARY KEY (app,baselineBranchFk);
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('com','master');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('enc','master');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('ent','master');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('eti','master');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('lab','master');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('tpv','master');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('com','dev');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('enc','dev');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('ent','dev');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('eti','dev');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('lab','dev');
INSERT INTO `vn`.`mdbApp` (app,baselineBranchFk)
VALUES ('tpv','dev');

View File

@ -0,0 +1,5 @@
UPDATE `vn`.`osTicketConfig`
SET oldStatus='1,6'
WHERE id=0;

View File

@ -0,0 +1,2 @@
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
VALUES ('ItemShelvingSale','*','*','ALLOW','employee');

View File

@ -0,0 +1 @@
ALTER TABLE `vn`.`supplier` ADD UNIQUE (name, countryFk);

View File

@ -0,0 +1,4 @@
SET FOREIGN_KEY_CHECKS = 0;
ALTER TABLE `vn`.`report` MODIFY COLUMN id tinyint(3) unsigned NOT NULL AUTO_INCREMENT;
ALTER TABLE `vn`.`printer` MODIFY COLUMN id tinyint(3) unsigned NOT NULL AUTO_INCREMENT;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -0,0 +1,3 @@
ALTER TABLE `vn`.`itemPackingType` ADD isActive BOOLEAN NOT NULL;
UPDATE `vn`.`itemPackingType` SET isActive = 0 WHERE code IN ('P', 'F');
UPDATE `vn`.`itemPackingType` SET isActive = 1 WHERE code IN ('V', 'H');

View File

@ -0,0 +1,23 @@
CREATE OR REPLACE TABLE `vn`.`docuware` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code` varchar(50) COLLATE utf8mb3_unicode_ci NOT NULL,
`fileCabinetName` varchar(50) COLLATE utf8mb3_unicode_ci NOT NULL,
`action` varchar(255) COLLATE utf8mb3_unicode_ci NOT NULL,
`dialogName` varchar(100) COLLATE utf8mb3_unicode_ci NOT NULL,
`findById` varchar(50) COLLATE utf8mb3_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
INSERT INTO `vn`.`docuware` (`code`, `fileCabinetName`, `action`, `dialogName`, `findById`)
VALUES
('deliveryNote', 'Albaranes cliente', 'find', 'find', 'N__ALBAR_N'),
('deliveryNote', 'Albaranes cliente', 'store', 'Archivar', 'N__ALBAR_N');
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
VALUES
('Docuware','checkFile','READ','ALLOW','employee'),
('Docuware','download','READ','ALLOW','salesPerson'),
('Docuware','upload','WRITE','ALLOW','productionAssi'),
('Docuware','deliveryNoteEmail','WRITE','ALLOW','salesPerson');
ALTER TABLE `vn`.`docuwareConfig` CHANGE token cookie varchar(1000) CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT NULL NULL;

View File

@ -0,0 +1,23 @@
DROP FUNCTION IF EXISTS `vn`.`priceFixed_getRate2`;
DELIMITER $$
$$
CREATE FUNCTION `vn`.`priceFixed_getRate2`(vFixedPriceFk INT, vRate3 DOUBLE)
RETURNS DOUBLE
BEGIN
DECLARE vWarehouse INT;
DECLARE vRate2 DOUBLE;
SELECT round(vRate3 * (1 + ((r.rate2 - r.rate3)/100)), 2) INTO vRate2
FROM vn.rate r
JOIN vn.priceFixed p ON p.id = vFixedPriceFk
WHERE r.dated <= p.started
AND r.warehouseFk = p.warehouseFk
ORDER BY r.dated DESC
LIMIT 1;
RETURN vRate2;
END$$
DELIMITER ;

View File

@ -0,0 +1,73 @@
DROP TRIGGER IF EXISTS vn.XDiario_beforeUpdate;
USE vn;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` TRIGGER `vn`.`XDiario_beforeUpdate`
BEFORE UPDATE ON `XDiario`
FOR EACH ROW
BEGIN
IF NOT NEW.SUBCTA <=> OLD.SUBCTA THEN
IF NEW.SUBCTA <=> '' THEN
SET NEW.SUBCTA = NULL;
END IF;
IF NEW.SUBCTA IS NOT NULL AND NOT LENGTH(NEW.SUBCTA) <=> 10 THEN
CALL util.throw('INVALID_STRING_LENGTH');
END IF;
END IF;
IF NOT NEW.CONTRA <=> OLD.CONTRA THEN
IF NEW.CONTRA <=> '' THEN
SET NEW.CONTRA = NULL;
END IF;
IF NEW.CONTRA IS NOT NULL AND NOT LENGTH(NEW.CONTRA) <=> 10 THEN
CALL util.throw('INVALID_STRING_LENGTH');
END IF;
END IF;
IF NOT NEW.FECHA <=> OLD.FECHA THEN
CALL XDiario_checkDate(NEW.FECHA);
END IF;
IF NOT NEW.FECHA_EX <=> OLD.FECHA_EX THEN
CALL XDiario_checkDate(NEW.FECHA_EX);
END IF;
IF NOT NEW.FECHA_OP <=> OLD.FECHA_OP THEN
CALL XDiario_checkDate(NEW.FECHA_OP);
END IF;
IF NOT NEW.FECHA_RT <=> OLD.FECHA_RT THEN
CALL XDiario_checkDate(NEW.FECHA_RT);
END IF;
IF NOT NEW.FECREGCON <=> OLD.FECREGCON THEN
CALL XDiario_checkDate(NEW.FECREGCON);
END IF;
END$$
DELIMITER ;
DROP TRIGGER IF EXISTS vn.XDiario_beforeInsert;
USE vn;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` TRIGGER `vn`.`XDiario_beforeInsert`
BEFORE INSERT ON `XDiario`
FOR EACH ROW
BEGIN
IF NEW.SUBCTA <=> '' THEN
SET NEW.SUBCTA = NULL;
END IF;
IF NEW.SUBCTA IS NOT NULL AND NOT LENGTH(NEW.SUBCTA) <=> 10 THEN
CALL util.throw('INVALID_STRING_LENGTH');
END IF;
IF NEW.CONTRA <=> '' THEN
SET NEW.CONTRA = NULL;
END IF;
IF NEW.CONTRA IS NOT NULL AND NOT LENGTH(NEW.CONTRA) <=> 10 THEN
CALL util.throw('INVALID_STRING_LENGTH');
END IF;
CALL XDiario_checkDate(NEW.FECHA);
CALL XDiario_checkDate(NEW.FECHA_EX);
CALL XDiario_checkDate(NEW.FECHA_OP);
CALL XDiario_checkDate(NEW.FECHA_RT);
CALL XDiario_checkDate(NEW.FECREGCON);
END$$
DELIMITER ;

View File

@ -0,0 +1,9 @@
ALTER TABLE `vn`.`itemConfig` ADD defaultTag INT DEFAULT 56 NOT NULL;
ALTER TABLE `vn`.`itemConfig` ADD CONSTRAINT itemConfig_FK FOREIGN KEY (defaultTag) REFERENCES vn.tag(id);
ALTER TABLE `vn`.`itemConfig` ADD validPriorities varchar(50) DEFAULT '[1,2,3]' NOT NULL;
ALTER TABLE `vn`.`itemConfig` ADD defaultPriority INT DEFAULT 2 NOT NULL;
ALTER TABLE `vn`.`item` MODIFY COLUMN relevancy tinyint(1) DEFAULT 0 NOT NULL COMMENT 'La web ordena de forma descendiente por este campo para mostrar los artículos';
INSERT INTO `salix`.`ACL`
(model, property, accessType, permission, principalType, principalId)
VALUES('ItemConfig', '*', 'READ', 'ALLOW', 'ROLE', 'buyer');

View File

@ -0,0 +1,6 @@
ALTER TABLE `vn`.`workerTimeControlConfig` ADD teleworkingStart INT NULL COMMENT 'Hora comienzo jornada de los teletrabajdores expresada en segundos';
ALTER TABLE `vn`.`workerTimeControlConfig` ADD teleworkingStartBreakTime INT NULL COMMENT 'Hora comienzo descanso de los teletrabjadores expresada en segundos';
UPDATE `vn`.`workerTimeControlConfig`
SET `teleworkingStart`=28800, `teleworkingStartBreakTime`=32400
WHERE `id`=1;

View File

@ -0,0 +1,3 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Tag', 'onSubmit', 'WRITE', 'ALLOW', 'ROLE', 'employee');

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (model,property,accessType,principalId)
VALUES
('NotificationSubscription','*','*','employee'),
('NotificationAcl','*','READ','employee');

View File

@ -0,0 +1,24 @@
UPDATE `salix`.`ACL`
SET accessType='READ'
WHERE model='Worker'
AND property='*'
AND accessType='*'
AND permission='ALLOW'
AND principalType='ROLE'
AND principalId='employee';
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('Worker', 'updateAttributes', 'WRITE', 'ALLOW', 'ROLE', 'hr'),
('Worker', 'createAbsence', '*', 'ALLOW', 'ROLE', 'employee'),
('Worker', 'updateAbsence', 'WRITE', 'ALLOW', 'ROLE', 'employee'),
('Worker', 'deleteAbsence', '*', 'ALLOW', 'ROLE', 'employee'),
('Worker', 'new', 'WRITE', 'ALLOW', 'ROLE', 'hr'),
('Role', '*', 'READ', 'ALLOW', 'ROLE', 'hr');
ALTER TABLE `vn`.`workerConfig` ADD roleFk int(10) unsigned NOT NULL COMMENT 'Rol por defecto al dar de alta un trabajador nuevo';
UPDATE `vn`.`workerConfig`
SET roleFk = 1
WHERE id = 1;

View File

@ -0,0 +1,110 @@
DROP PROCEDURE IF EXISTS vn.ticket_canAdvance;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canAdvance`(vDateFuture DATE, vDateToAdvance DATE, vWarehouseFk INT)
BEGIN
/**
* Devuelve los tickets y la cantidad de lineas de venta que se pueden adelantar.
*
* @param vDateFuture Fecha de los tickets que se quieren adelantar.
* @param vDateToAdvance Fecha a cuando se quiere adelantar.
* @param vWarehouseFk Almacén
*/
DECLARE vDateInventory DATE;
SELECT inventoried INTO vDateInventory FROM vn.config;
DROP TEMPORARY TABLE IF EXISTS tmp.stock;
CREATE TEMPORARY TABLE tmp.stock
(itemFk INT PRIMARY KEY,
amount INT)
ENGINE = MEMORY;
INSERT INTO tmp.stock(itemFk, amount)
SELECT itemFk, SUM(quantity) amount FROM
(
SELECT itemFk, quantity
FROM vn.itemTicketOut
WHERE shipped >= vDateInventory
AND shipped < vDateFuture
AND warehouseFk = vWarehouseFk
UNION ALL
SELECT itemFk, quantity
FROM vn.itemEntryIn
WHERE landed >= vDateInventory
AND landed < vDateFuture
AND isVirtualStock = FALSE
AND warehouseInFk = vWarehouseFk
UNION ALL
SELECT itemFk, quantity
FROM vn.itemEntryOut
WHERE shipped >= vDateInventory
AND shipped < vDateFuture
AND warehouseOutFk = vWarehouseFk
) t
GROUP BY itemFk HAVING amount != 0;
DROP TEMPORARY TABLE IF EXISTS tmp.filter;
CREATE TEMPORARY TABLE tmp.filter
(INDEX (id))
SELECT s.ticketFk futureId,
t2.ticketFk id,
count(DISTINCT s.id) saleCount,
t2.state,
t2.isNotValidated,
st.name futureState,
st.isNotValidated futureIsNotValidated,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt,
t2.ipt,
t.workerFk,
CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters,
CAST(COUNT(*) AS DECIMAL(10,0)) `futureLines`,
t2.shipped,
t.shipped futureShipped,
t2.totalWithVat,
t.totalWithVat futureTotalWithVat,
t2.agency,
am.name futureAgency,
t2.lines,
t2.liters,
SUM((s.quantity <= IFNULL(st.amount,0))) hasStock
FROM vn.ticket t
JOIN vn.ticketState ts ON ts.ticketFk = t.id
JOIN vn.state st ON st.id = ts.stateFk
JOIN vn.saleVolume sv ON t.id = sv.ticketFk
JOIN (SELECT
t2.id ticketFk,
t2.addressFk,
st.isNotValidated,
st.name state,
GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt,
t2.shipped,
t2.totalWithVat,
am.name agency,
CAST(SUM(litros) AS DECIMAL(10,0)) liters,
CAST(COUNT(*) AS DECIMAL(10,0)) `lines`
FROM vn.ticket t2
JOIN vn.saleVolume sv ON t2.id = sv.ticketFk
JOIN vn.sale s ON s.ticketFk = t2.id
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.ticketState ts ON ts.ticketFk = t2.id
JOIN vn.state st ON st.id = ts.stateFk
JOIN vn.agencyMode am ON t2.agencyModeFk = am.id
LEFT JOIN vn.itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
WHERE t2.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance)
AND t2.warehouseFk = vWarehouseFk
GROUP BY t2.id) t2 ON t2.addressFk = t.addressFk
JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.item i ON i.id = s.itemFk
JOIN vn.agencyMode am ON t.agencyModeFk = am.id
LEFT JOIN vn.itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
LEFT JOIN tmp.stock st ON st.itemFk = s.itemFk
WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
AND t.warehouseFk = vWarehouseFk
GROUP BY t.id;
DROP TEMPORARY TABLE tmp.stock;
END$$
DELIMITER ;

View File

@ -0,0 +1,4 @@
ALTER TABLE
`util`.`notificationSubscription`
ADD
CONSTRAINT `notificationSubscription_UN` UNIQUE KEY (`notificationFk`, `userFk`);

View File

@ -0,0 +1,6 @@
UPDATE `vn`.`client`
SET isToBeMailed = FALSE
WHERE
mailAddress is NULL
AND email is NULL
AND isToBeMailed = TRUE;

View File

@ -0,0 +1,7 @@
ALTER TABLE `util`.`notificationSubscription`
ADD `id` int(11) auto_increment NULL,
DROP PRIMARY KEY,
ADD CONSTRAINT PRIMARY KEY (`id`);
ALTER TABLE `util`.`notificationSubscription`
ADD KEY `notificationSubscription_ibfk_1` (`notificationFk`);

View File

@ -2,7 +2,33 @@ CREATE SCHEMA IF NOT EXISTS `vn2008`;
CREATE SCHEMA IF NOT EXISTS `tmp`;
UPDATE `util`.`config`
SET `environment`= 'test';
SET `environment`= 'development';
-- FOR MOCK vn.time
DROP PROCEDURE IF EXISTS `vn`.`mockVnTime`;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`mockVnTime`()
BEGIN
DECLARE vDate DATE;
SET vDate = '2000-01-01';
WHILE ( YEAR(vDate) <= 2002 ) DO
INSERT IGNORE INTO vn.`time` (dated, period, `month`, `year`, `day`, week, yearMonth, salesYear)
VALUES (vDate, CONCAT(YEAR(vDate), (WEEK(vDate)+1)), MONTH(vDate), YEAR(vDate), DAY(vDate), WEEK(vDate)+1, CONCAT(YEAR(vDate), MONTH(vDate)), YEAR(vDate));
SET vDate = DATE_ADD(vDate, INTERVAL 1 DAY);
END WHILE;
END$$
DELIMITER ;
CALL `vn`.`mockVnTime`();
DROP PROCEDURE IF EXISTS `vn`.`mockVnTime`;
-- END MOCK vn.time
ALTER TABLE `vn`.`itemTaxCountry` AUTO_INCREMENT = 1;
ALTER TABLE `vn`.`address` AUTO_INCREMENT = 1;
@ -934,10 +960,10 @@ INSERT INTO `vn`.`expedition`(`id`, `agencyModeFk`, `ticketFk`, `freightItemFk`,
(7, 2, 4, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH), 1, 18, NULL, 94, NULL,NULL),
(8, 3, 5, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH), 1, 18, NULL, 94, 1, NULL),
(9, 3, 6, 71, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1, 18, NULL, 94, 2, NULL),
(10, 7, 7, 71, NOW(), 1, 18, NULL, 94, 3, NULL),
(11, 7, 8, 71, NOW(), 1, 18, NULL, 94, 3, NULL),
(12, 7, 9, 71, NOW(), 1, 18, NULL, 94, 3, NULL),
(13, 1, 10,71, NOW(), 1, 18, NULL, 94, 3, NULL);
(10, 7, 7, 71, util.VN_NOW(), 1, 18, NULL, 94, 3, NULL),
(11, 7, 8, 71, util.VN_NOW(), 1, 18, NULL, 94, 3, NULL),
(12, 7, 9, 71, util.VN_NOW(), 1, 18, NULL, 94, 3, NULL),
(13, 1, 10,71, util.VN_NOW(), 1, 18, NULL, 94, 3, NULL);
INSERT INTO `vn`.`expeditionState`(`id`, `created`, `expeditionFk`, `typeFk`, `userFk`)
@ -1143,10 +1169,10 @@ INSERT INTO `vn`.`itemShelving` (`itemFk`, `shelvingFk`, `visible`, `grouping`,
INSERT INTO `vn`.`itemShelvingSale` (`itemShelvingFk`, `saleFk`, `quantity`, `created`, `userFk`)
VALUES
('1', '1', '1', '', '1106'),
('2', '2', '5', '', '1106'),
('1', '7', '1', '', '1106'),
('2', '8', '5', '', '1106');
('1', '1', '1', util.VN_CURDATE(), '1106'),
('2', '2', '5', util.VN_CURDATE(), '1106'),
('1', '7', '1', util.VN_CURDATE(), '1106'),
('2', '8', '5', util.VN_CURDATE(), '1106');
INSERT INTO `vncontrol`.`accion`(`accion_id`, `accion`)
VALUES
@ -1215,7 +1241,7 @@ INSERT INTO `vn`.`tag`(`id`, `code`, `name`, `isFree`, `isQuantitatif`, `sourceT
(7, NULL, 'Ancho de la base', 1, 1, NULL, 'mm',NULL, NULL),
(23, 'stems', 'Tallos', 1, 1, NULL, NULL, NULL, 'stems'),
(27, NULL, 'Longitud(cm)', 1, 1, NULL, 'cm', NULL, NULL),
(36, NULL, 'Proveedor', 1, 0, NULL, NULL, NULL, NULL),
(36, 'producer', 'Proveedor', 1, 0, NULL, NULL, NULL, 'producer'),
(56, NULL, 'Genero', 1, 0, NULL, NULL, NULL, NULL),
(58, NULL, 'Variedad', 1, 0, NULL, NULL, NULL, NULL),
(67, 'category', 'Categoria', 1, 0, NULL, NULL, NULL, NULL),
@ -1779,7 +1805,7 @@ INSERT INTO `vn`.`claimDestination`(`id`, `description`, `addressFk`)
INSERT INTO `vn`.`claimDevelopment`(`id`, `claimFk`, `claimResponsibleFk`, `workerFk`, `claimReasonFk`, `claimResultFk`, `claimRedeliveryFk`, `claimDestinationFk`)
VALUES
(1, 1, 1, 21, 1, 1, 2, 5),
(2, 1, 1, 21, 7, 2, 2, 5),
(2, 1, 2, 21, 7, 2, 2, 5),
(3, 2, 7, 21, 9, 3, 2, 5),
(4, 3, 7, 21, 15, 8, 2, 5),
(5, 4, 7, 21, 7, 8, 2, 5);
@ -1910,7 +1936,7 @@ DROP TEMPORARY TABLE IF EXISTS tmp.worker;
CREATE TEMPORARY TABLE tmp.worker
(PRIMARY KEY (id))
ENGINE = MEMORY
SELECT w.id, w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-12-25'), CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL +1 YEAR)), '-12-25'), CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
SELECT w.id, w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR)), '-12-25') as started, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL +1 YEAR)), '-12-25') as ended, CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
FROM `vn`.`worker` `w`;
INSERT INTO `vn`.`business`(`id`, `workerFk`, `companyCodeFk`, `started`, `ended`, `workerBusiness`, `reasonEndFk`, `notes`, `departmentFk`, `workerBusinessProfessionalCategoryFk`, `calendarTypeFk`, `isHourlyLabor`, `workerBusinessAgreementFk`, `workcenterFk`)
@ -1920,7 +1946,7 @@ DROP TEMPORARY TABLE IF EXISTS tmp.worker;
CREATE TEMPORARY TABLE tmp.worker
(PRIMARY KEY (id))
ENGINE = MEMORY
SELECT '1111' as 'id', w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -2 YEAR)), '-12-25'), CONCAT(YEAR(DATE_ADD(CURDATE(), INTERVAL -1 YEAR)), '-12-24'), CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
SELECT '1111' as 'id', w.id as `workerFk`, 'VNL', CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -2 YEAR)), '-12-25') as started, CONCAT(YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 YEAR)) as ended, '-12-24'), CONCAT('E-46-', RPAD(CONCAT(w.id, 9), 8, w.id)), NULL as `notes`, NULL as `departmentFk`, 23, 1 as `workerBusinessProfessionalCategoryFk`, 1 as `calendarTypeFk`, 1 as `isHourlyLabor`, 1 as `workerBusinessAgreementFk`, 1 as `workcenterFk`
FROM `vn`.`worker` `w`
WHERE `w`.`id` = 1109;
@ -1954,10 +1980,6 @@ INSERT INTO `vn`.`workerBusinessType` (`id`, `name`, `isFullTime`, `isPermanent`
(100, 'INDEFINIDO A TIEMPO COMPLETO', 1, 1, 1),
(109, 'CONVERSION DE TEMPORAL EN INDEFINIDO T.COMPLETO', 1, 1, 1);
INSERT INTO `vn`.`businessCategory` (`id`, `description`, `rate`)
VALUES
(1, 'basic employee', 1);
UPDATE `vn`.`business` b
SET `rate` = 7,
`workerBusinessCategoryFk` = 1,
@ -2580,13 +2602,9 @@ INSERT INTO `bs`.`sale` (`saleFk`, `amount`, `dated`, `typeFk`, `clientFk`)
(4, 33.8, util.VN_CURDATE(), 1, 1101),
(30, 34.4, util.VN_CURDATE(), 1, 1108);
INSERT INTO `vn`.`docuware` (`code`, `fileCabinetName`, `dialogName` , `find`)
VALUES
('deliveryClient', 'deliveryClient', 'findTicket', 'word');
INSERT INTO `vn`.`docuwareConfig` (`url`)
VALUES
('https://verdnatura.docuware.cloud/docuware/platform');
('http://docuware.url/');
INSERT INTO `vn`.`calendarHolidaysName` (`id`, `name`)
VALUES
@ -2633,7 +2651,7 @@ INSERT INTO `vn`.`machineWorker` (`workerFk`, `machineFk`, `inTimed`, `outTimed`
INSERT INTO `vn`.`zoneExclusion` (`id`, `zoneFk`, `dated`, `created`, `userFk`)
VALUES
(1, 1, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=7, 7, 14) - DAYOFWEEK(util.VN_CURDATE())) DAY), util.VN_CURDATE(), 100),
(1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL (IF(DAYOFWEEK(util.VN_CURDATE())<=7, 7, 14) - DAYOFWEEK(util.VN_CURDATE())) DAY), util.VN_CURDATE(), 100),
(2, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL (IF(DAYOFWEEK(util.VN_CURDATE())<=8, 8, 15) - DAYOFWEEK(util.VN_CURDATE())) DAY), util.VN_CURDATE(), 100);
INSERT INTO `vn`.`zoneExclusionGeo` (`zoneExclusionFk`, `geoFk`)
@ -2671,9 +2689,9 @@ INSERT INTO `vn`.`sectorCollectionSaleGroup` (`sectorCollectionFk`, `saleGroupFk
VALUES
(1, 1);
INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`, `shortWeekBreak`, `longWeekBreak`, `weekScope`, `mailPass`, `mailHost`, `mailSuccessFolder`, `mailErrorFolder`, `mailUser`, `minHoursToBreak`, `breakHours`, `hoursCompleteWeek`, `startNightlyHours`, `endNightlyHours`, `maxTimePerDay`, `breakTime`, `timeToBreakTime`, `dayMaxTime`, `shortWeekDays`, `longWeekDays`)
INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`, `shortWeekBreak`, `longWeekBreak`, `weekScope`, `mailPass`, `mailHost`, `mailSuccessFolder`, `mailErrorFolder`, `mailUser`, `minHoursToBreak`, `breakHours`, `hoursCompleteWeek`, `startNightlyHours`, `endNightlyHours`, `maxTimePerDay`, `breakTime`, `timeToBreakTime`, `dayMaxTime`, `shortWeekDays`, `longWeekDays`, `teleworkingStart`, `teleworkingStartBreakTime`)
VALUES
(1, 43200, 32400, 129600, 259200, 604800, '', '', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.33, 0.33, 40, '22:00:00', '06:00:00', 57600, 1200, 18000, 57600, 6, 13);
(1, 43200, 32400, 129600, 259200, 604800, '', '', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.33, 0.33, 40, '22:00:00', '06:00:00', 57600, 1200, 18000, 57600, 6, 13, 28800, 32400);
INSERT INTO `vn`.`host` (`id`, `code`, `description`, `warehouseFk`, `bankFk`)
VALUES
@ -2692,6 +2710,7 @@ INSERT INTO `util`.`notificationConfig`
INSERT INTO `util`.`notification` (`id`, `name`, `description`)
VALUES
(1, 'print-email', 'notification fixture one'),
(2, 'invoice-electronic', 'A electronic invoice has been generated'),
(4, 'supplier-pay-method-update', 'A supplier pay method has been updated');
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
@ -2708,7 +2727,10 @@ INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`)
VALUES
(1, 1109),
(1, 1110),
(3, 1109);
(3, 1109),
(1,9),
(1,3);
INSERT INTO `vn`.`routeConfig` (`id`, `defaultWorkCenterFk`)
VALUES
@ -2722,6 +2744,10 @@ INSERT INTO `vn`.`collection` (`id`, `created`, `workerFk`, `stateFk`, `itemPack
VALUES
(3, util.VN_NOW(), 1107, 5, NULL, 0, 0, 1, NULL, NULL);
INSERT INTO `vn`.`itemConfig` (`id`, `isItemTagTriggerDisabled`, `monthToDeactivate`, `wasteRecipients`, `validPriorities`, `defaultPriority`, `defaultTag`)
VALUES
(0, 0, 24, '', '[1,2,3]', 2, 56);
INSERT INTO `vn`.`ticketCollection` (`ticketFk`, `collectionFk`, `created`, `level`, `wagon`, `smartTagFk`, `usedShelves`, `itemCount`, `liters`)
VALUES
(9, 3, util.VN_NOW(), NULL, 0, NULL, NULL, NULL, NULL);
@ -2744,16 +2770,20 @@ INSERT INTO `vn`.`ticketLog` (`originFk`, userFk, `action`, changedModel, oldIns
INSERT INTO `vn`.`osTicketConfig` (`id`, `host`, `user`, `password`, `oldStatus`, `newStatusId`, `day`, `comment`, `hostDb`, `userDb`, `passwordDb`, `portDb`, `responseType`, `fromEmailId`, `replyTo`)
VALUES
(0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', 'open', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
(0, 'http://localhost:56596/scp', 'ostadmin', 'Admin1', '1,6', 3, 60, 'Este CAU se ha cerrado automáticamente. Si el problema persiste responda a este mensaje.', 'localhost', 'osticket', 'osticket', 40003, 'reply', 1, 'all');
INSERT INTO `vn`.`mdbApp` (`app`, `baselineBranchFk`, `userFk`, `locked`)
VALUES
('foo', 'master', NULL, NULL),
('bar', 'test', 9, util.VN_NOW());
INSERT INTO `vn`.`ticketLog` (`id`, `originFk`, `userFk`, `action`, `changedModel`, `oldInstance`, `newInstance`, `changedModelId`)
VALUES
(1, 1, 9, 'insert', 'Ticket', '{}', '{"clientFk":1, "nickname": "Bat cave"}', 1);
INSERT INTO `vn`.`profileType` (`id`, `name`)
VALUES
(1, 'working');
INSERT INTO `vn`.`newWorkerConfig` (`id`, `street`, `provinceFk`, `companyFk`, `profileTypeFk`, `roleFk`)
VALUES
(1, 'S/ ', 1, 442, 1, 1);
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
VALUES
@ -2764,3 +2794,7 @@ INSERT INTO `vn`.`payDemDetail` (`id`, `detail`)
VALUES
(1, 1);
INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`)
VALUES
(1, NULL, 1);

View File

@ -3,18 +3,17 @@ USE `util`;
DELIMITER ;;
DROP FUNCTION IF EXISTS `util`.`mockedDate`;
CREATE FUNCTION `util`.`mockedDate`()
CREATE FUNCTION `util`.`mockedDate`()
RETURNS DATETIME
DETERMINISTIC
BEGIN
RETURN NOW();
-- '2022-01-19 08:00:00'
RETURN CONVERT_TZ('2001-01-01 11:00:00', 'utc', 'Europe/Madrid');
END ;;
DELIMITER ;
DELIMITER ;;
DROP FUNCTION IF EXISTS `util`.`VN_CURDATE`;
CREATE FUNCTION `util`.`VN_CURDATE`()
CREATE FUNCTION `util`.`VN_CURDATE`()
RETURNS DATE
DETERMINISTIC
BEGIN
@ -24,7 +23,7 @@ DELIMITER ;
DELIMITER ;;
DROP FUNCTION IF EXISTS `util`.`VN_CURTIME`;
CREATE FUNCTION `util`.`VN_CURTIME`()
CREATE FUNCTION `util`.`VN_CURTIME`()
RETURNS TIME
DETERMINISTIC
BEGIN
@ -34,10 +33,10 @@ DELIMITER ;
DELIMITER ;;
DROP FUNCTION IF EXISTS `util`.`VN_NOW`;
CREATE FUNCTION `util`.`VN_NOW`()
CREATE FUNCTION `util`.`VN_NOW`()
RETURNS DATETIME
DETERMINISTIC
BEGIN
RETURN mockedDate();
RETURN mockedDate();
END ;;
DELIMITER ;
DELIMITER ;

View File

@ -80202,3 +80202,4 @@ USE `vncontrol`;
-- Dump completed on 2022-11-21 7:57:28

View File

@ -59,6 +59,7 @@ TABLES=(
componentType
continent
department
docuware
itemPackingType
pgc
sample

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('buyUltimate()', () => {
const today = new Date();
const today = Date.vnNew();
it(`should create buyUltimate temporal table and update it's values`, async() => {
let stmts = [];
let stmt;

View File

@ -5,7 +5,7 @@ describe('buyUltimateFromInterval()', () => {
let today;
let future;
beforeAll(() => {
let now = new Date();
let now = Date.vnNew();
now.setHours(0, 0, 0, 0);
today = now;

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('ticket ticketCalculateClon()', () => {
const today = new Date();
const today = Date.vnNew();
it('should add the ticket to the order containing the original ticket', async() => {
let stmts = [];
let stmt;

View File

@ -2,7 +2,7 @@ const app = require('vn-loopback/server/server');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('ticket ticketCreateWithUser()', () => {
const today = new Date();
const today = Date.vnNew();
it('should confirm the procedure creates the expected ticket', async() => {
let stmts = [];
let stmt;

View File

@ -3,9 +3,9 @@ const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('timeBusiness_calculateByUser()', () => {
it('should return the expected hours for today', async() => {
let start = new Date();
let start = Date.vnNew();
start.setHours(0, 0, 0, 0);
let end = new Date();
let end = Date.vnNew();
end.setHours(0, 0, 0, 0);
let stmts = [];

View File

@ -3,11 +3,11 @@ const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
describe('timeControl_calculateByUser()', () => {
it(`should return today's worked hours`, async() => {
let start = new Date();
let start = Date.vnNew();
start.setHours(0, 0, 0, 0);
start.setDate(start.getDate() - 1);
let end = new Date();
let end = Date.vnNew();
end.setHours(0, 0, 0, 0);
end.setDate(end.getDate() + 1);
@ -17,7 +17,7 @@ describe('timeControl_calculateByUser()', () => {
stmts.push('START TRANSACTION');
stmts.push(`
DROP TEMPORARY TABLE IF EXISTS
DROP TEMPORARY TABLE IF EXISTS
tmp.timeControlCalculate,
tmp.timeBusinessCalculate
`);
@ -48,14 +48,14 @@ describe('timeControl_calculateByUser()', () => {
});
it(`should return the worked hours between last sunday and monday`, async() => {
let lastSunday = new Date();
let lastSunday = Date.vnNew();
let daysSinceSunday = lastSunday.getDay();
if (daysSinceSunday === 0) // this means today is sunday but you need the previous sunday :)
daysSinceSunday = 7;
lastSunday.setHours(23, 0, 0, 0);
lastSunday.setDate(lastSunday.getDate() - daysSinceSunday);
let monday = new Date();
let monday = Date.vnNew();
let daysSinceMonday = daysSinceSunday - 1; // aiming for monday (today could be monday)
monday.setHours(7, 0, 0, 0);
monday.setDate(monday.getDate() - daysSinceMonday);
@ -66,7 +66,7 @@ describe('timeControl_calculateByUser()', () => {
stmts.push('START TRANSACTION');
stmts.push(`
DROP TEMPORARY TABLE IF EXISTS
DROP TEMPORARY TABLE IF EXISTS
tmp.timeControlCalculate,
tmp.timeBusinessCalculate
`);

View File

@ -6,7 +6,7 @@ describe('zone zone_getLanded()', () => {
let stmts = [];
let stmt;
stmts.push('START TRANSACTION');
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
let params = {
@ -40,7 +40,7 @@ describe('zone zone_getLanded()', () => {
it(`should return data for a shipped tomorrow`, async() => {
let stmts = [];
let stmt;
const date = new Date();
const date = Date.vnNew();
date.setHours(0, 0, 0, 0);
stmts.push('START TRANSACTION');

View File

@ -436,7 +436,7 @@ let actions = {
},
pickDate: async function(selector, date) {
date = date || new Date();
date = date || Date.vnNew();
const timeZoneOffset = date.getTimezoneOffset() * 60000;
const localDate = (new Date(date.getTime() - timeZoneOffset))

View File

@ -31,7 +31,7 @@ export default {
},
recoverPassword: {
recoverPasswordButton: 'vn-login a[ui-sref="recover-password"]',
email: 'vn-recover-password vn-textfield[ng-model="$ctrl.email"]',
email: 'vn-recover-password vn-textfield[ng-model="$ctrl.user"]',
sendEmailButton: 'vn-recover-password vn-submit',
},
accountIndex: {
@ -58,6 +58,7 @@ export default {
deleteAccount: '.vn-menu [name="deleteUser"]',
setPassword: '.vn-menu [name="setPassword"]',
activateAccount: '.vn-menu [name="enableAccount"]',
disableAccount: '.vn-menu [name="disableAccount"]',
activateUser: '.vn-menu [name="activateUser"]',
deactivateUser: '.vn-menu [name="deactivateUser"]',
newPassword: 'vn-textfield[ng-model="$ctrl.newPassword"]',
@ -417,8 +418,8 @@ export default {
fourthFixedPrice: 'vn-fixed-price tr:nth-child(5)',
fourthItemID: 'vn-fixed-price tr:nth-child(5) vn-autocomplete[ng-model="price.itemFk"]',
fourthWarehouse: 'vn-fixed-price tr:nth-child(5) vn-autocomplete[ng-model="price.warehouseFk"]',
fourthPPU: 'vn-fixed-price tr:nth-child(5) > td:nth-child(4)',
fourthPPP: 'vn-fixed-price tr:nth-child(5) > td:nth-child(5)',
fourthGroupingPrice: 'vn-fixed-price tr:nth-child(5) > td:nth-child(4)',
fourthPackingPrice: 'vn-fixed-price tr:nth-child(5) > td:nth-child(5)',
fourthHasMinPrice: 'vn-fixed-price tr:nth-child(5) > td:nth-child(6) > vn-check[ng-model="price.hasMinPrice"]',
fourthMinPrice: 'vn-fixed-price tr:nth-child(5) > td:nth-child(6) > vn-input-number[ng-model="price.minPrice"]',
fourthStarted: 'vn-fixed-price tr:nth-child(5) vn-date-picker[ng-model="price.started"]',
@ -428,6 +429,7 @@ export default {
},
itemCreateView: {
temporalName: 'vn-item-create vn-textfield[ng-model="$ctrl.item.provisionalName"]',
priority: 'vn-autocomplete[ng-model="$ctrl.item.priority"]',
type: 'vn-autocomplete[ng-model="$ctrl.item.typeFk"]',
intrastat: 'vn-autocomplete[ng-model="$ctrl.item.intrastatFk"]',
origin: 'vn-autocomplete[ng-model="$ctrl.item.originFk"]',
@ -463,6 +465,7 @@ export default {
generic: 'vn-autocomplete[ng-model="$ctrl.item.genericFk"]',
isFragile: 'vn-check[ng-model="$ctrl.item.isFragile"]',
longName: 'vn-textfield[ng-model="$ctrl.item.longName"]',
packingOut: 'vn-input-number[ng-model="$ctrl.item.packingOut"]',
isActiveCheckbox: 'vn-check[label="Active"]',
priceInKgCheckbox: 'vn-check[label="Price in kg"]',
newIntrastatButton: 'vn-item-basic-data vn-icon-button[vn-tooltip="New intrastat"] > button',
@ -520,7 +523,7 @@ export default {
},
itemLog: {
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr',
fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(3) td.after',
fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(2) td.after',
},
ticketSummary: {
header: 'vn-ticket-summary > vn-card > h5',
@ -559,15 +562,15 @@ export default {
payoutBank: '.vn-dialog vn-autocomplete[ng-model="$ctrl.bankFk"]',
payoutDescription: 'vn-textfield[ng-model="$ctrl.receipt.description"]',
submitPayout: '.vn-dialog button[response="accept"]',
searchWeeklyResult: 'vn-ticket-weekly-index vn-table vn-tbody > vn-tr',
searchWeeklyResult: 'vn-ticket-weekly-index vn-card smart-table slot-table table tbody tr',
searchResultDate: 'vn-ticket-summary [label=Landed] span',
topbarSearch: 'vn-searchbar',
moreMenu: 'vn-ticket-index vn-icon-button[icon=more_vert]',
fourthWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tbody vn-tr:nth-child(4)',
fiveWeeklyTicket: 'vn-ticket-weekly-index vn-table vn-tbody vn-tr:nth-child(5)',
weeklyTicket: 'vn-ticket-weekly-index vn-table > div > vn-tbody > vn-tr',
firstWeeklyTicketDeleteIcon: 'vn-ticket-weekly-index vn-tr:nth-child(1) vn-icon-button[icon="delete"]',
firstWeeklyTicketAgency: 'vn-ticket-weekly-index vn-tr:nth-child(1) [ng-model="weekly.agencyModeFk"]',
fourthWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(4)',
fiveWeeklyTicket: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(5)',
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"]',
firstWeeklyTicketAgency: 'vn-ticket-weekly-index vn-card smart-table slot-table tr:nth-child(1) [ng-model="weekly.agencyModeFk"]',
acceptDeleteTurn: '.vn-confirm.shown button[response="accept"]'
},
createTicketView: {
@ -678,7 +681,10 @@ export default {
moveToTicketButton: '.vn-popover.shown vn-icon[icon="arrow_forward_ios"]',
moveToNewTicketButton: '.vn-popover.shown vn-button[label="New ticket"]',
stateMenuButton: 'vn-ticket-sale vn-tool-bar > vn-button-menu[label="State"]',
moreMenuState: 'body > div > div > div.content > div.filter.ng-scope > vn-textfield'
moreMenuState: 'body > div > div > div.content > div.filter.ng-scope > vn-textfield',
firstSaleHistoryButton: 'vn-ticket-sale vn-tr:nth-child(1) vn-icon-button[icon="history"]',
firstSaleHistory: 'form vn-table div > vn-tbody > vn-tr',
closeHistory: 'div.window vn-button[icon="clear"]'
},
ticketTracking: {
createStateButton: 'vn-float-button'
@ -772,8 +778,8 @@ export default {
ipt: 'vn-autocomplete[label="Destination IPT"]',
tableIpt: 'vn-autocomplete[name="ipt"]',
tableFutureIpt: 'vn-autocomplete[name="futureIpt"]',
futureState: 'vn-autocomplete[label="Origin Grouped State"]',
state: 'vn-autocomplete[label="Destination Grouped State"]',
futureState: 'vn-check[label="Pending Origin"]',
state: 'vn-check[label="Pending Destination"]',
warehouseFk: 'vn-autocomplete[label="Warehouse"]',
tableButtonSearch: 'vn-button[vn-tooltip="Search"]',
moveButton: 'vn-button[vn-tooltip="Advance tickets"]',
@ -938,9 +944,9 @@ export default {
routeSummary: {
header: 'vn-route-summary > vn-card > h5',
cost: 'vn-route-summary vn-label-value[label="Cost"]',
firstTicketID: 'vn-route-summary vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(2) > span',
firstTicketID: 'vn-route-summary vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(10) > span',
firstTicketDescriptor: '.vn-popover.shown vn-ticket-descriptor',
firstAlias: 'vn-route-summary vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(3) > span',
firstAlias: 'vn-route-summary vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(5) > span',
firstClientDescriptor: '.vn-popover.shown vn-client-descriptor',
goToRouteSummaryButton: 'vn-route-summary > vn-card > h5 > a',
@ -964,6 +970,7 @@ export default {
confirmButton: '.vn-confirm.shown button[response="accept"]'
},
workerSummary: {
summaryIcon: 'vn-worker-descriptor a[title="Go to module summary"]',
header: 'vn-worker-summary h5',
id: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(3) > section > span',
email: 'vn-worker-summary vn-one:nth-child(1) > vn-label-value:nth-child(4) > section > span',
@ -1016,6 +1023,25 @@ export default {
furlough: 'vn-worker-calendar > vn-side-menu [name="absenceTypes"] > vn-chip:nth-child(4)',
halfFurlough: 'vn-worker-calendar > vn-side-menu [name="absenceTypes"] > vn-chip:nth-child(5)',
},
workerCreate: {
newWorkerButton: 'vn-worker-index a[ui-sref="worker.create"]',
firstname: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.firstName"]',
lastname: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.lastNames"]',
birth: 'vn-worker-create vn-date-picker[ng-model="$ctrl.worker.birth"]',
fi: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.fi"]',
code: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.code"]',
phone: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.phone"]',
city: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.city"]',
postcode: 'vn-worker-create vn-datalist[ng-model="$ctrl.worker.postcode"]',
street: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.street"]',
user: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.name"]',
email: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.email"]',
boss: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bossFk"]',
role: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.roleFk"]',
iban: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.iban"]',
switft: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bankEntityFk"]',
createButton: 'vn-worker-create vn-submit[label="Create"]',
},
invoiceOutIndex: {
topbarSearch: 'vn-searchbar',
searchResult: 'vn-invoice-out-index vn-card > vn-table > div > vn-tbody > a.vn-tr',
@ -1234,6 +1260,21 @@ export default {
importBuysButton: 'vn-entry-buy-import button[type="submit"]'
},
entryLatestBuys: {
table: 'tbody > tr:not(.empty-rows)',
chip: 'vn-chip > vn-icon',
generalSearchInput: 'vn-textfield[ng-model="$ctrl.filter.search"]',
firstReignIcon: 'vn-horizontal.item-category vn-one',
typeInput: 'vn-autocomplete[ng-model="$ctrl.filter.typeFk"]',
salesPersonInput: 'vn-autocomplete[ng-model="$ctrl.filter.salesPersonFk"]',
supplierInput: 'vn-autocomplete[ng-model="$ctrl.filter.supplierFk"]',
fromInput: 'vn-date-picker[ng-model="$ctrl.filter.from"]',
toInput: 'vn-date-picker[ng-model="$ctrl.filter.to"]',
activeCheck: 'vn-check[ng-model="$ctrl.filter.active"]',
floramondoCheck: 'vn-check[ng-model="$ctrl.filter.floramondo"]',
visibleCheck: 'vn-check[ng-model="$ctrl.filter.visible"]',
addTagButton: 'vn-icon-button[vn-tooltip="Add tag"]',
itemTagInput: 'vn-autocomplete[ng-model="itemTag.tagFk"]',
itemTagValueInput: 'vn-autocomplete[ng-model="itemTag.value"]',
firstBuy: 'vn-entry-latest-buys tbody > tr:nth-child(1)',
allBuysCheckBox: 'vn-entry-latest-buys thead vn-check',
secondBuyCheckBox: 'vn-entry-latest-buys tbody tr:nth-child(2) vn-check[ng-model="buy.checked"]',

View File

@ -1,6 +1,7 @@
require('@babel/register')({presets: ['@babel/env']});
require('core-js/stable');
require('regenerator-runtime/runtime');
require('vn-loopback/server/boot/date')();
const axios = require('axios');
const Docker = require('../../db/docker.js');

View File

@ -15,7 +15,7 @@ describe('SmartTable SearchBar integration', () => {
await browser.close();
});
describe('as filters', () => {
describe('as filters in smart-table section', () => {
it('should search by type in searchBar', async() => {
await page.waitToClick(selectors.itemsIndex.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.itemsIndex.advancedSearchItemType, 'Anthurium');
@ -47,6 +47,34 @@ describe('SmartTable SearchBar integration', () => {
});
});
describe('as filters in section without smart-table', () => {
it('go to zone section', async() => {
await page.loginAndModule('salesPerson', 'zone');
await page.waitToClick(selectors.globalItems.searchButton);
});
it('should search in searchBar first time', async() => {
await page.doSearch('A');
const count = await page.countElement(selectors.zoneIndex.searchResult);
expect(count).toEqual(7);
});
it('should search in searchBar second time', async() => {
await page.doSearch('A');
const count = await page.countElement(selectors.zoneIndex.searchResult);
expect(count).toEqual(7);
});
it('should search in searchBar third time', async() => {
await page.doSearch('A');
const count = await page.countElement(selectors.zoneIndex.searchResult);
expect(count).toEqual(7);
});
});
describe('as orders', () => {
it('should order by first id', async() => {
await page.loginAndModule('developer', 'item');

View File

@ -26,7 +26,7 @@ describe('RecoverPassword path', async() => {
expect(message.text).toContain('Notification sent!');
});
it('should send email', async() => {
it('should send email using email', async() => {
await page.waitForState('login');
await page.waitToClick(selectors.recoverPassword.recoverPasswordButton);
@ -37,4 +37,16 @@ describe('RecoverPassword path', async() => {
expect(message.text).toContain('Notification sent!');
});
it('should send email using username', async() => {
await page.waitForState('login');
await page.waitToClick(selectors.recoverPassword.recoverPasswordButton);
await page.write(selectors.recoverPassword.email, 'BruceWayne');
await page.waitToClick(selectors.recoverPassword.sendEmailButton);
const message = await page.waitForSnackbar();
await page.waitForState('login');
expect(message.text).toContain('Notification sent!');
});
});

View File

@ -4,7 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
describe('Client credit insurance path', () => {
let browser;
let page;
let previousMonth = new Date();
let previousMonth = Date.vnNew();
previousMonth.setMonth(previousMonth.getMonth() - 1);
beforeAll(async() => {

View File

@ -2,68 +2,32 @@ import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Worker summary path', () => {
const workerId = 3;
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('employee', 'worker');
const httpDataResponse = page.waitForResponse(response => {
return response.status() === 200 && response.url().includes(`Workers/${workerId}`);
});
await page.accessToSearchResult('agencyNick');
await httpDataResponse;
});
afterAll(async() => {
await browser.close();
});
it('should reach the employee summary section', async() => {
await page.waitForState('worker.card.summary');
});
it('should check the summary contains the name and userName on the header', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.header, 'innerText');
expect(result).toEqual('agency agency');
});
it('should check the summary contains the basic data id', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.id, 'innerText');
expect(result).toEqual('3');
});
it('should check the summary contains the basic data email', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.email, 'innerText');
expect(result).toEqual('agency@verdnatura.es');
});
it('should check the summary contains the basic data department', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.department, 'innerText');
expect(result).toEqual('CAMARA');
});
it('should check the summary contains the user data id', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.userId, 'innerText');
expect(result).toEqual('3');
});
it('should check the summary contains the user data name', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.userName, 'innerText');
expect(result).toEqual('agency');
});
it('should check the summary contains the user data role', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.role, 'innerText');
expect(result).toEqual('agency');
});
it('should check the summary contains the user data extension', async() => {
const result = await page.waitToGetProperty(selectors.workerSummary.extension, 'innerText');
expect(result).toEqual('1101');
it('should reach the employee summary section and check all properties', async() => {
expect(await page.getProperty(selectors.workerSummary.header, 'innerText')).toEqual('agency agency');
expect(await page.getProperty(selectors.workerSummary.id, 'innerText')).toEqual('3');
expect(await page.getProperty(selectors.workerSummary.email, 'innerText')).toEqual('agency@verdnatura.es');
expect(await page.getProperty(selectors.workerSummary.department, 'innerText')).toEqual('CAMARA');
expect(await page.getProperty(selectors.workerSummary.userId, 'innerText')).toEqual('3');
expect(await page.getProperty(selectors.workerSummary.userName, 'innerText')).toEqual('agency');
expect(await page.getProperty(selectors.workerSummary.role, 'innerText')).toEqual('agency');
expect(await page.getProperty(selectors.workerSummary.extension, 'innerText')).toEqual('1101');
});
});

View File

@ -2,13 +2,18 @@ import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Worker basic data path', () => {
const workerId = 1106;
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('hr', 'worker');
const httpDataResponse = page.waitForResponse(response => {
return response.status() === 200 && response.url().includes(`Workers/${workerId}`);
});
await page.accessToSearchResult('David Charles Haller');
await httpDataResponse;
await page.accessToSection('worker.card.basicData');
});
@ -16,35 +21,20 @@ describe('Worker basic data path', () => {
await browser.close();
});
it('should edit the form', async() => {
await page.clearInput(selectors.workerBasicData.name);
await page.write(selectors.workerBasicData.name, 'David C.');
await page.clearInput(selectors.workerBasicData.surname);
await page.write(selectors.workerBasicData.surname, 'H.');
await page.clearInput(selectors.workerBasicData.phone);
await page.write(selectors.workerBasicData.phone, '444332211');
await page.waitToClick(selectors.workerBasicData.saveButton);
it('should edit the form and then reload the section and check the data was edited', async() => {
await page.overwrite(selectors.workerBasicData.name, 'David C.');
await page.overwrite(selectors.workerBasicData.surname, 'H.');
await page.overwrite(selectors.workerBasicData.phone, '444332211');
await page.click(selectors.workerBasicData.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should reload the section then check the name was edited', async() => {
await page.reloadSection('worker.card.basicData');
const result = await page.waitToGetProperty(selectors.workerBasicData.name, 'value');
expect(result).toEqual('David C.');
});
it('should the surname was edited', async() => {
const result = await page.waitToGetProperty(selectors.workerBasicData.surname, 'value');
expect(result).toEqual('H.');
});
it('should the phone was edited', async() => {
const result = await page.waitToGetProperty(selectors.workerBasicData.phone, 'value');
expect(result).toEqual('444332211');
expect(await page.waitToGetProperty(selectors.workerBasicData.name, 'value')).toEqual('David C.');
expect(await page.waitToGetProperty(selectors.workerBasicData.surname, 'value')).toEqual('H.');
expect(await page.waitToGetProperty(selectors.workerBasicData.phone, 'value')).toEqual('444332211');
});
});

View File

@ -16,19 +16,16 @@ describe('Worker pbx path', () => {
await browser.close();
});
it('should receive an error when the extension exceeds 4 characters', async() => {
it('should receive an error when the extension exceeds 4 characters and then sucessfully save the changes', async() => {
await page.write(selectors.workerPbx.extension, '55555');
await page.waitToClick(selectors.workerPbx.saveButton);
const message = await page.waitForSnackbar();
await page.click(selectors.workerPbx.saveButton);
let message = await page.waitForSnackbar();
expect(message.text).toContain('Extension format is invalid');
});
it('should sucessfully save the changes', async() => {
await page.clearInput(selectors.workerPbx.extension);
await page.write(selectors.workerPbx.extension, '4444');
await page.waitToClick(selectors.workerPbx.saveButton);
const message = await page.waitForSnackbar();
await page.overwrite(selectors.workerPbx.extension, '4444');
await page.click(selectors.workerPbx.saveButton);
message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved! User must access web');
});

View File

@ -21,38 +21,37 @@ describe('Worker time control path', () => {
const fourPm = '16:00';
const hankPymId = 1107;
it('should go to the next month', async() => {
const date = new Date();
it('should go to the next month, go to current month and go 1 month in the past', async() => {
let date = Date.vnNew();
date.setDate(1);
date.setMonth(date.getMonth() + 1);
const month = date.toLocaleString('default', {month: 'long'});
let month = date.toLocaleString('default', {month: 'long'});
await page.waitToClick(selectors.workerTimeControl.nextMonthButton);
const result = await page.waitToGetProperty(selectors.workerTimeControl.monthName, 'innerText');
await page.click(selectors.workerTimeControl.nextMonthButton);
let result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
expect(result).toContain(month);
});
it('should go to current month', async() => {
const date = new Date();
const month = date.toLocaleString('default', {month: 'long'});
date = Date.vnNew();
date.setDate(1);
month = date.toLocaleString('default', {month: 'long'});
await page.waitToClick(selectors.workerTimeControl.previousMonthButton);
const result = await page.waitToGetProperty(selectors.workerTimeControl.monthName, 'innerText');
await page.click(selectors.workerTimeControl.previousMonthButton);
result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
expect(result).toContain(month);
});
it('should go 1 month in the past', async() => {
const date = new Date();
date = Date.vnNew();
date.setDate(1);
date.setMonth(date.getMonth() - 1);
const timestamp = Math.round(date.getTime() / 1000);
const month = date.toLocaleString('default', {month: 'long'});
month = date.toLocaleString('default', {month: 'long'});
await page.loginAndModule('salesBoss', 'worker');
await page.goto(`http://localhost:5000/#!/worker/${hankPymId}/time-control?timestamp=${timestamp}`);
await page.waitToClick(selectors.workerTimeControl.secondWeekDay);
await page.click(selectors.workerTimeControl.secondWeekDay);
const result = await page.waitToGetProperty(selectors.workerTimeControl.monthName, 'innerText');
result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
expect(result).toContain(month);
});
@ -115,7 +114,9 @@ describe('Worker time control path', () => {
});
it('should change week of month', async() => {
await page.waitToClick(selectors.workerTimeControl.thrirdWeekDay);
await page.waitForTextInElement(selectors.workerTimeControl.mondayWorkedHours, '00:00 h.');
await page.click(selectors.workerTimeControl.thrirdWeekDay);
const result = await page.getProperty(selectors.workerTimeControl.mondayWorkedHours, 'innerText');
expect(result).toEqual('00:00 h.');
});
});

View File

@ -1,16 +1,25 @@
/* eslint-disable max-len */
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Worker calendar path', () => {
let reasonableTimeBetweenClicks = 400;
const reasonableTimeBetweenClicks = 300;
const date = Date.vnNew();
const lastYear = (date.getFullYear() - 1).toString();
let browser;
let page;
async function accessAs(user) {
await page.loginAndModule(user, 'worker');
await page.accessToSearchResult('Charles Xavier');
await page.accessToSection('worker.card.calendar');
}
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('hr', 'worker');
await page.accessToSearchResult('Charles Xavier');
await page.accessToSection('worker.card.calendar');
accessAs('hr');
});
afterAll(async() => {
@ -21,48 +30,40 @@ describe('Worker calendar path', () => {
it('should set two days as holidays on the calendar and check the total holidays increased by 1.5', async() => {
await page.waitToClick(selectors.workerCalendar.holidays);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.penultimateMondayOfJanuary);
await page.click(selectors.workerCalendar.penultimateMondayOfJanuary);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.absence);
await page.click(selectors.workerCalendar.absence);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.lastMondayOfMarch);
await page.click(selectors.workerCalendar.lastMondayOfMarch);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.halfHoliday);
await page.click(selectors.workerCalendar.halfHoliday);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.fistMondayOfMay);
await page.click(selectors.workerCalendar.fistMondayOfMay);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.furlough);
await page.click(selectors.workerCalendar.furlough);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondTuesdayOfMay);
await page.click(selectors.workerCalendar.secondTuesdayOfMay);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondWednesdayOfMay);
await page.click(selectors.workerCalendar.secondWednesdayOfMay);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondThursdayOfMay);
await page.click(selectors.workerCalendar.secondThursdayOfMay);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.halfFurlough);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondFridayOfJun);
await page.click(selectors.workerCalendar.halfFurlough);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.click(selectors.workerCalendar.secondFridayOfJun);
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
expect(result).toContain(' 1.5 ');
expect(await page.getProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText')).toContain(' 1.5 ');
});
});
describe(`as salesBoss`, () => {
it(`should log in and get to Charles Xavier's calendar`, async() => {
await page.loginAndModule('salesBoss', 'worker');
await page.accessToSearchResult('Charles Xavier');
await page.accessToSection('worker.card.calendar');
});
it(`should log in, get to Charles Xavier's calendar, undo what was done here, and check the total holidays used are back to what it was`, async() => {
accessAs('salesBoss');
it('should undo what was done here', async() => {
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.holidays);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.penultimateMondayOfJanuary);
@ -90,45 +91,24 @@ describe('Worker calendar path', () => {
await page.waitToClick(selectors.workerCalendar.halfFurlough);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.secondFridayOfJun);
});
it('should check the total holidays used are back to what it was', async() => {
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
expect(result).toContain(' 0 ');
expect(await page.getProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText')).toContain(' 0 ');
});
});
describe(`as Charles Xavier`, () => {
it(`should log in and get to his calendar`, async() => {
await page.loginAndModule('CharlesXavier', 'worker');
await page.accessToSearchResult('Charles Xavier');
await page.accessToSection('worker.card.calendar');
});
it('should make a futile attempt to add holidays', async() => {
await page.waitForTimeout(reasonableTimeBetweenClicks);
it('should log in and get to his calendar, make a futile attempt to add holidays, check the total holidays used are now the initial ones and use the year selector to go to the previous year', async() => {
accessAs('CharlesXavier');
await page.waitToClick(selectors.workerCalendar.holidays);
await page.waitForTimeout(reasonableTimeBetweenClicks);
await page.waitToClick(selectors.workerCalendar.penultimateMondayOfJanuary);
});
it('should check the total holidays used are now the initial ones', async() => {
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
await page.click(selectors.workerCalendar.penultimateMondayOfJanuary);
expect(result).toContain(' 0 ');
});
it('should use the year selector to go to the previous year', async() => {
const date = new Date();
const lastYear = (date.getFullYear() - 1).toString();
expect(await page.getProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText')).toContain(' 0 ');
await page.autocompleteSearch(selectors.workerCalendar.year, lastYear);
await page.waitForTimeout(reasonableTimeBetweenClicks);
const result = await page.waitToGetProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText');
expect(result).toContain(' 0 ');
expect(await page.getProperty(selectors.workerCalendar.totalHolidaysUsed, 'innerText')).toContain(' 0 ');
});
});
});

View File

@ -0,0 +1,74 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Worker create path', () => {
let browser;
let page;
let newWorker;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('hr', 'worker');
await page.waitToClick(selectors.workerCreate.newWorkerButton);
await page.waitForState('worker.create');
});
afterAll(async() => {
await browser.close();
});
it('should insert default data', async() => {
await page.write(selectors.workerCreate.firstname, 'Victor');
await page.write(selectors.workerCreate.lastname, 'Von Doom');
await page.write(selectors.workerCreate.fi, '78457139E');
await page.write(selectors.workerCreate.phone, '12356789');
await page.write(selectors.workerCreate.postcode, '46680');
await page.write(selectors.workerCreate.street, 'S/ Doomstadt');
await page.write(selectors.workerCreate.email, 'doctorDoom@marvel.com');
await page.write(selectors.workerCreate.iban, 'ES9121000418450200051332');
await page.autocompleteSearch(selectors.workerCreate.switft, 'BBKKESMMMMM');
// should check for autocompleted worker code and worker user name
const workerCode = await page
.waitToGetProperty(selectors.workerCreate.code, 'value');
newWorker = await page
.waitToGetProperty(selectors.workerCreate.user, 'value');
expect(workerCode).toEqual('VVD');
expect(newWorker).toContain('victorvd');
// should fail if necessary data is void
await page.waitToClick(selectors.workerCreate.createButton);
let message = await page.waitForSnackbar();
expect(message.text).toContain('is a required argument');
// should create a new worker and go to worker basic data'
await page.pickDate(selectors.workerCreate.birth, new Date(1962, 8, 5));
await page.autocompleteSearch(selectors.workerCreate.boss, 'deliveryBoss');
await page.waitToClick(selectors.workerCreate.createButton);
message = await page.waitForSnackbar();
await page.waitForState('worker.card.basicData');
expect(message.text).toContain('Data saved!');
// 'rollback'
await page.loginAndModule('sysadmin', 'account');
await page.accessToSearchResult(newWorker);
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.deactivateUser);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
message = await page.waitForSnackbar();
expect(message.text).toContain('User deactivated!');
await page.waitToClick(selectors.accountDescriptor.menuButton);
await page.waitToClick(selectors.accountDescriptor.disableAccount);
await page.waitToClick(selectors.accountDescriptor.acceptButton);
message = await page.waitForSnackbar();
expect(message.text).toContain('Account disabled!');
});
});

View File

@ -35,6 +35,7 @@ describe('Item Edit basic data path', () => {
await page.waitToClick(selectors.itemBasicData.isActiveCheckbox);
await page.waitToClick(selectors.itemBasicData.priceInKgCheckbox);
await page.waitToClick(selectors.itemBasicData.isFragile);
await page.write(selectors.itemBasicData.packingOut, '5');
await page.waitToClick(selectors.itemBasicData.submitBasicDataButton);
const message = await page.waitForSnackbar();
@ -128,4 +129,11 @@ describe('Item Edit basic data path', () => {
expect(result).toBe('checked');
});
it(`should confirm the item packingOut was edited`, async() => {
const result = await page
.waitToGetProperty(selectors.itemBasicData.packingOut, 'value');
expect(result).toEqual('5');
});
});

View File

@ -36,11 +36,20 @@ describe('Item Create', () => {
await page.waitForState('item.create');
});
it('should create the Infinity Gauntlet item', async() => {
it('should throw an error when insert an invalid priority', async() => {
await page.write(selectors.itemCreateView.temporalName, 'Infinity Gauntlet');
await page.autocompleteSearch(selectors.itemCreateView.type, 'Crisantemo');
await page.autocompleteSearch(selectors.itemCreateView.intrastat, 'Coral y materiales similares');
await page.autocompleteSearch(selectors.itemCreateView.origin, 'Holand');
await page.clearInput(selectors.itemCreateView.priority);
await page.waitToClick(selectors.itemCreateView.createButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Valid priorities');
});
it('should create the Infinity Gauntlet item', async() => {
await page.autocompleteSearch(selectors.itemCreateView.priority, '2');
await page.waitToClick(selectors.itemCreateView.createButton);
const message = await page.waitForSnackbar();

View File

@ -22,10 +22,10 @@ describe('Item fixed prices path', () => {
});
it('should fill the fixed price data', async() => {
const now = new Date();
const now = Date.vnNew();
await page.autocompleteSearch(selectors.itemFixedPrice.fourthWarehouse, 'Warehouse one');
await page.write(selectors.itemFixedPrice.fourthPPU, '1');
await page.write(selectors.itemFixedPrice.fourthPPP, '1');
await page.writeOnEditableTD(selectors.itemFixedPrice.fourthGroupingPrice, '1');
await page.writeOnEditableTD(selectors.itemFixedPrice.fourthPackingPrice, '1');
await page.write(selectors.itemFixedPrice.fourthMinPrice, '1');
await page.pickDate(selectors.itemFixedPrice.fourthStarted, now);
await page.pickDate(selectors.itemFixedPrice.fourthEnded, now);

View File

@ -196,6 +196,15 @@ describe('Ticket Edit sale path', () => {
expect(result).toContain('22.50');
});
it('should check in the history that logs has been added', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleHistoryButton);
await page.waitForSelector(selectors.ticketSales.firstSaleHistory);
const result = await page.countElement(selectors.ticketSales.firstSaleHistory);
expect(result).toBeGreaterThan(0);
await page.waitToClick(selectors.ticketSales.closeHistory);
});
it('should recalculate price of sales', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
await page.waitToClick(selectors.ticketSales.secondSaleCheckbox);

View File

@ -93,7 +93,7 @@ describe('Ticket Edit basic data path', () => {
it(`should split ticket without negatives`, async() => {
const newAgency = 'Gotham247';
const newDate = new Date();
const newDate = Date.vnNew();
newDate.setDate(newDate.getDate() - 1);
await page.accessToSearchResult('14');
@ -127,7 +127,7 @@ describe('Ticket Edit basic data path', () => {
});
it(`should old ticket have old date and agency`, async() => {
const oldDate = new Date();
const oldDate = Date.vnNew();
const oldAgency = 'Super-Man delivery';
await page.accessToSearchResult('14');

View File

@ -4,7 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
describe('Ticket create path', () => {
let browser;
let page;
let nextMonth = new Date();
let nextMonth = Date.vnNew();
nextMonth.setMonth(nextMonth.getMonth() + 1);
beforeAll(async() => {

View File

@ -55,7 +55,7 @@ describe('Ticket Future path', () => {
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'Horizontal');
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 0);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should search with the destination IPT', async() => {
@ -68,7 +68,7 @@ describe('Ticket Future path', () => {
await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'Horizontal');
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 0);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should search with the origin grouped state', async() => {
@ -152,50 +152,6 @@ describe('Ticket Future path', () => {
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should search in smart-table with especified Lines', async() => {
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.write(selectors.ticketFuture.tableLines, '0');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.ticketFuture.table, 1);
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.write(selectors.ticketFuture.tableLines, '1');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.ticketFuture.table, 5);
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should search in smart-table with especified Liters', async() => {
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.write(selectors.ticketFuture.tableLiters, '0');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.ticketFuture.table, 1);
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.write(selectors.ticketFuture.tableLiters, '28');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.ticketFuture.table, 5);
await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketFuture.submit);
await page.waitForNumberOfElements(selectors.ticketFuture.table, 4);
});
it('should check the three last tickets and move to the future', async() => {
await page.waitToClick(selectors.ticketFuture.multiCheck);
await page.waitToClick(selectors.ticketFuture.firstCheck);

View File

@ -50,7 +50,7 @@ describe('Ticket Advance path', () => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'Horizontal');
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.ipt);
@ -62,7 +62,7 @@ describe('Ticket Advance path', () => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'Horizontal');
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.futureIpt);
@ -70,26 +70,36 @@ describe('Ticket Advance path', () => {
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search with the origin grouped state', async() => {
it('should search with the origin pending state', async() => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.futureState, 'Free');
await page.waitToClick(selectors.ticketAdvance.futureState);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.futureState);
await page.waitToClick(selectors.ticketAdvance.futureState);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.futureState);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search with the destination grouped state', async() => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.state, 'Free');
await page.waitToClick(selectors.ticketAdvance.state);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 0);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.clearInput(selectors.ticketAdvance.state);
await page.waitToClick(selectors.ticketAdvance.state);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.state);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
@ -116,42 +126,7 @@ describe('Ticket Advance path', () => {
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search in smart-table with stock', async() => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.write(selectors.ticketAdvance.tableStock, '5');
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 2);
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search in smart-table with especified Lines', async() => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.write(selectors.ticketAdvance.tableLines, '0');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should search in smart-table with especified Liters', async() => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.write(selectors.ticketAdvance.tableLiters, '0');
await page.keyboard.press('Enter');
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.waitToClick(selectors.ticketAdvance.submit);
await page.waitForNumberOfElements(selectors.ticketAdvance.table, 1);
});
it('should check the three last tickets and move to the future', async() => {
it('should check the one ticket and move to the present', async() => {
await page.waitToClick(selectors.ticketAdvance.multiCheck);
await page.waitToClick(selectors.ticketAdvance.moveButton);
await page.waitToClick(selectors.ticketAdvance.acceptButton);

View File

@ -18,7 +18,7 @@ describe('Route basic Data path', () => {
});
it('should edit the route basic data', async() => {
const nextMonth = new Date();
const nextMonth = Date.vnNew();
nextMonth.setMonth(nextMonth.getMonth() + 1);
await page.autocompleteSearch(selectors.routeBasicData.worker, 'adminBossNick');

View File

@ -19,7 +19,7 @@ describe('InvoiceIn basic data path', () => {
});
it(`should edit the invoiceIn basic data`, async() => {
const now = new Date();
const now = Date.vnNew();
await page.pickDate(selectors.invoiceInBasicData.issued, now);
await page.pickDate(selectors.invoiceInBasicData.operated, now);
await page.autocompleteSearch(selectors.invoiceInBasicData.supplier, 'Verdnatura');

View File

@ -100,7 +100,7 @@ describe('InvoiceOut descriptor path', () => {
});
it(`should check the invoiceOut booked in the summary data`, async() => {
let today = new Date();
let today = Date.vnNew();
let day = today.getDate();
if (day < 10) day = `0${day}`;

View File

@ -4,7 +4,7 @@ import getBrowser from '../../helpers/puppeteer';
describe('Travel create path', () => {
let browser;
let page;
const date = new Date();
const date = Date.vnNew();
const day = 15;
date.setDate(day);

View File

@ -22,7 +22,7 @@ describe('Travel basic data path', () => {
});
it('should set a wrong delivery date then receive an error on submit', async() => {
const lastMonth = new Date();
const lastMonth = Date.vnNew();
lastMonth.setMonth(lastMonth.getMonth() - 1);
await page.pickDate(selectors.travelBasicData.deliveryDate, lastMonth);

View File

@ -123,7 +123,7 @@ describe('Travel descriptor path', () => {
});
it('should update the landed date to a future date to enable cloneWithEntries', async() => {
const nextMonth = new Date();
const nextMonth = Date.vnNew();
nextMonth.setMonth(nextMonth.getMonth() + 1);
await page.pickDate(selectors.travelBasicData.deliveryDate, nextMonth);
await page.waitToClick(selectors.travelBasicData.save);

View File

@ -4,10 +4,15 @@ import getBrowser from '../../helpers/puppeteer';
describe('Entry lastest buys path', () => {
let browser;
let page;
const httpRequests = [];
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
page.on('request', req => {
if (req.url().includes(`Buys/latestBuysFilter`))
httpRequests.push(req.url());
});
await page.loginAndModule('buyer', 'entry');
});
@ -20,6 +25,87 @@ describe('Entry lastest buys path', () => {
await page.waitForSelector(selectors.entryLatestBuys.editBuysButton, {visible: false});
});
it('should filter by name', async() => {
await page.write(selectors.entryLatestBuys.generalSearchInput, 'Melee');
await page.keyboard.press('Enter');
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('search=Melee')))).toBeDefined();
});
it('should filter by reign and type', async() => {
await page.click(selectors.entryLatestBuys.firstReignIcon);
await page.autocompleteSearch(selectors.entryLatestBuys.typeInput, 'Alstroemeria');
await page.click(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('categoryFk')))).toBeDefined();
expect(httpRequests.find(req => req.includes(('typeFk')))).toBeDefined();
});
it('should filter by from date', async() => {
await page.pickDate(selectors.entryLatestBuys.fromInput, new Date());
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('from')))).toBeDefined();
});
it('should filter by to date', async() => {
await page.pickDate(selectors.entryLatestBuys.toInput, new Date());
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('to')))).toBeDefined();
});
it('should filter by sales person', async() => {
await page.autocompleteSearch(selectors.entryLatestBuys.salesPersonInput, 'buyerNick');
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('salesPersonFk')))).toBeDefined();
});
it('should filter by supplier', async() => {
await page.autocompleteSearch(selectors.entryLatestBuys.supplierInput, 'Farmer King');
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('supplierFk')))).toBeDefined();
});
it('should filter by active', async() => {
await page.waitToClick(selectors.entryLatestBuys.activeCheck);
await page.waitToClick(selectors.entryLatestBuys.activeCheck);
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('active=true')))).toBeDefined();
expect(httpRequests.find(req => req.includes(('active=false')))).toBeDefined();
});
it('should filter by visible', async() => {
await page.waitToClick(selectors.entryLatestBuys.visibleCheck);
await page.waitToClick(selectors.entryLatestBuys.visibleCheck);
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('visible=true')))).toBeDefined();
expect(httpRequests.find(req => req.includes(('visible=false')))).toBeDefined();
});
it('should filter by floramondo', async() => {
await page.waitToClick(selectors.entryLatestBuys.floramondoCheck);
await page.waitToClick(selectors.entryLatestBuys.floramondoCheck);
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('floramondo=true')))).toBeDefined();
expect(httpRequests.find(req => req.includes(('floramondo=false')))).toBeDefined();
});
it('should filter by tag Color', async() => {
await page.waitToClick(selectors.entryLatestBuys.addTagButton);
await page.autocompleteSearch(selectors.entryLatestBuys.itemTagInput, 'Color');
await page.autocompleteSearch(selectors.entryLatestBuys.itemTagValueInput, 'Brown');
await page.waitToClick(selectors.entryLatestBuys.chip);
expect(httpRequests.find(req => req.includes(('tags')))).toBeDefined();
});
it('should select all lines but one and then check the edit buys button appears', async() => {
await page.waitToClick(selectors.entryLatestBuys.allBuysCheckBox);
await page.waitToClick(selectors.entryLatestBuys.secondBuyCheckBox);

View File

@ -87,6 +87,7 @@ ngModule.vnComponent('vnButtonMenu', {
selectFields: '<?',
initialData: '<?',
showFilter: '<?',
fields: '<?',
field: '=?',
url: '@?',
data: '<?',

View File

@ -15,7 +15,7 @@ export default class Calendar extends FormInput {
constructor($element, $scope, vnWeekDays, moment) {
super($element, $scope);
this.weekDays = vnWeekDays.locales;
this.defaultDate = new Date();
this.defaultDate = Date.vnNew();
this.displayControls = true;
this.moment = moment;
}
@ -115,8 +115,8 @@ export default class Calendar extends FormInput {
let wday = date.getDay();
let month = date.getMonth();
const currentDay = new Date().getDate();
const currentMonth = new Date().getMonth();
const currentDay = Date.vnNew().getDate();
const currentMonth = Date.vnNew().getMonth();
let classes = {
today: day === currentDay && month === currentMonth,

View File

@ -2,7 +2,7 @@ describe('Component vnCalendar', () => {
let controller;
let $element;
let date = new Date();
let date = Date.vnNew();
date.setHours(0, 0, 0, 0);
date.setDate(1);
@ -48,7 +48,7 @@ describe('Component vnCalendar', () => {
it(`should return the selected element, then emit a 'selection' event`, () => {
jest.spyOn(controller, 'emit');
const day = new Date();
const day = Date.vnNew();
day.setHours(0, 0, 0, 0);
const clickEvent = new Event('click');

View File

@ -147,28 +147,17 @@ export default class CrudModel extends ModelProxy {
this.moreRows = null;
}
/**
* Saves current changes on the server.
*
* @return {Promise} The save request promise
*/
save() {
if (!this.isChanged)
return this.$q.resolve();
getChanges() {
if (!this.isChanged) return null;
let deletes = [];
let updates = [];
let creates = [];
let orgDeletes = [];
let orgUpdates = [];
let orgCreates = [];
const deletes = [];
const updates = [];
const creates = [];
let pk = this.primaryKey;
const pk = this.primaryKey;
for (let row of this.removed) {
for (let row of this.removed)
deletes.push(row.$orgRow[pk]);
orgDeletes.push(row);
}
for (let row of this.data) {
if (row.$isNew) {
@ -178,7 +167,6 @@ export default class CrudModel extends ModelProxy {
data[prop] = row[prop];
}
creates.push(row);
orgCreates.push(row);
} else if (row.$oldData) {
let data = {};
for (let prop in row.$oldData)
@ -187,28 +175,38 @@ export default class CrudModel extends ModelProxy {
data,
where: {[pk]: row.$orgRow[pk]}
});
orgUpdates.push(row);
}
}
let changes = {deletes, updates, creates};
const changes = {deletes, updates, creates};
for (let prop in changes) {
if (changes[prop].length === 0)
changes[prop] = undefined;
}
if (!changes)
return this.$q.resolve();
return changes;
}
/**
* Saves current changes on the server.
*
* @return {Promise} The save request promise
*/
save() {
const pk = this.primaryKey;
const changes = this.getChanges();
if (!changes) return this.$q.resolve();
const creates = changes.creates || [];
let url = this.saveUrl ? this.saveUrl : `${this._url}/crud`;
return this.$http.post(url, changes)
.then(res => {
const created = res.data;
// Apply new data to created instances
for (let i = 0; i < orgCreates.length; i++) {
const row = orgCreates[i];
for (let i = 0; i < creates.length; i++) {
const row = creates[i];
row[pk] = created[i][pk];
for (let prop in row) {

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