Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 5436-mensajeClienteSinCorreo

This commit is contained in:
Carlos Satorres 2023-04-20 10:40:22 +02:00
commit c103eeab7f
449 changed files with 11919 additions and 5175 deletions

View File

@ -5,5 +5,10 @@
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": true "source.fixAll.eslint": true
}, },
"search.useIgnoreFiles": false "search.useIgnoreFiles": false,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
"eslint.format.enable": true,
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
}
} }

View File

@ -5,17 +5,38 @@ 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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2312.01] - 2023-04-06 ## [2316.01] - 2023-05-04
### Added ### Added
- -
### Changed ### Changed
- - (Artículo -> Precio fijado) Modificado el buscador superior por uno lateral
### Fixed ### Fixed
- -
## [2314.01] - 2023-04-20
### Added
- (Clientes -> Morosos) Ahora se puede filtrar por las columnas "Desde" y "Fecha Ú. O.". También se envia un email al comercial cuando se añade una nota.
- (Monitor tickets) Muestra un icono al lado de la zona, si el ticket es frágil y se envía por agencia
- (Facturas recibidas -> Bases negativas) Nueva sección
### Fixed
- (Clientes -> Morosos) Ahora se mantienen los elementos seleccionados al hacer sroll.
## [2312.01] - 2023-04-06
### Added
- (Monitor tickets) Muestra un icono al lado de la zona, si el ticket es frágil y se envía por agencia
### Changed
- (Monitor tickets) Cuando se filtra por 'Pendiente' ya no muestra los estados de 'Previa'
- (Envíos -> Extra comunitarios) Se agrupan las entradas del mismo travel. Añadidos campos Referencia y Importe.
- (Envíos -> Índice) Cambiado el buscador superior por uno lateral
## [2310.01] - 2023-03-23 ## [2310.01] - 2023-03-23
### Added ### Added

View File

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

View File

@ -1,8 +1,8 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('campaign upcoming()', () => { describe('campaign upcoming()', () => {
it('should return the upcoming campaign but from the last year', async() => { it('should return the upcoming campaign but from the last year', async() => {
const response = await app.models.Campaign.upcoming(); const response = await models.Campaign.upcoming();
const campaignDated = response.dated; const campaignDated = response.dated;
const now = Date.vnNew(); const now = Date.vnNew();

View File

@ -26,20 +26,27 @@ module.exports = Self => {
Self.send = async(ctx, to, message) => { Self.send = async(ctx, to, message) => {
const models = Self.app.models; const models = Self.app.models;
const accessToken = ctx.req.accessToken; const accessToken = ctx.req.accessToken;
const sender = await models.Account.findById(accessToken.userId); const sender = await models.VnUser.findById(accessToken.userId);
const recipient = to.replace('@', ''); const recipient = to.replace('@', '');
if (sender.name != recipient) { if (sender.name != recipient) {
await models.Chat.create({ const chat = await models.Chat.create({
senderFk: sender.id, senderFk: sender.id,
recipient: to, recipient: to,
dated: Date.vnNew(), dated: Date.vnNew(),
checkUserStatus: 0, checkUserStatus: 0,
message: message, message: message,
status: 0, status: 'sending',
attempts: 0 attempts: 0
}); });
try {
await Self.sendMessage(chat.senderFk, chat.recipient, chat.message);
await Self.updateChat(chat, 'sent');
} catch (error) {
await Self.updateChat(chat, 'error', error);
}
return true; return true;
} }

View File

@ -24,18 +24,13 @@ module.exports = Self => {
} }
}); });
Self.sendCheckingPresence = async(ctx, recipientId, message, options) => { Self.sendCheckingPresence = async(ctx, recipientId, message) => {
if (!recipientId) return false; if (!recipientId) return false;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const models = Self.app.models; const models = Self.app.models;
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const sender = await models.Account.findById(userId); const sender = await models.VnUser.findById(userId, {fields: ['id']});
const recipient = await models.Account.findById(recipientId, null, myOptions); const recipient = await models.VnUser.findById(recipientId, null);
// Prevent sending messages to yourself // Prevent sending messages to yourself
if (recipientId == userId) return false; if (recipientId == userId) return false;
@ -46,16 +41,23 @@ module.exports = Self => {
if (process.env.NODE_ENV == 'test') if (process.env.NODE_ENV == 'test')
message = `[Test:Environment to user ${userId}] ` + message; message = `[Test:Environment to user ${userId}] ` + message;
await models.Chat.create({ const chat = await models.Chat.create({
senderFk: sender.id, senderFk: sender.id,
recipient: `@${recipient.name}`, recipient: `@${recipient.name}`,
dated: Date.vnNew(), dated: Date.vnNew(),
checkUserStatus: 1, checkUserStatus: 1,
message: message, message: message,
status: 0, status: 'sending',
attempts: 0 attempts: 0
}); });
try {
await Self.sendCheckingUserStatus(chat);
await Self.updateChat(chat, 'sent');
} catch (error) {
await Self.updateChat(chat, 'error', error);
}
return true; return true;
}; };
}; };

View File

@ -3,7 +3,6 @@ module.exports = Self => {
Self.remoteMethodCtx('sendQueued', { Self.remoteMethodCtx('sendQueued', {
description: 'Send a RocketChat message', description: 'Send a RocketChat message',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [],
returns: { returns: {
type: 'object', type: 'object',
root: true root: true
@ -16,14 +15,17 @@ module.exports = Self => {
Self.sendQueued = async() => { Self.sendQueued = async() => {
const models = Self.app.models; const models = Self.app.models;
const maxAttempts = 3;
const sentStatus = 1;
const errorStatus = 2;
const chats = await models.Chat.find({ const chats = await models.Chat.find({
where: { where: {
status: {neq: sentStatus}, status: {
attempts: {lt: maxAttempts} nin: [
'sent',
'sending'
]
},
attempts: {lt: 3}
} }
}); });
@ -31,16 +33,16 @@ module.exports = Self => {
if (chat.checkUserStatus) { if (chat.checkUserStatus) {
try { try {
await Self.sendCheckingUserStatus(chat); await Self.sendCheckingUserStatus(chat);
await updateChat(chat, sentStatus); await Self.updateChat(chat, 'sent');
} catch (error) { } catch (error) {
await updateChat(chat, errorStatus, error); await Self.updateChat(chat, 'error', error);
} }
} else { } else {
try { try {
await Self.sendMessage(chat.senderFk, chat.recipient, chat.message); await Self.sendMessage(chat.senderFk, chat.recipient, chat.message);
await updateChat(chat, sentStatus); await Self.updateChat(chat, 'sent');
} catch (error) { } catch (error) {
await updateChat(chat, errorStatus, error); await Self.updateChat(chat, 'error', error);
} }
} }
} }
@ -56,7 +58,7 @@ module.exports = Self => {
const models = Self.app.models; const models = Self.app.models;
const recipientName = chat.recipient.slice(1); const recipientName = chat.recipient.slice(1);
const recipient = await models.Account.findOne({ const recipient = await models.VnUser.findOne({
where: { where: {
name: recipientName name: recipientName
} }
@ -102,7 +104,7 @@ module.exports = Self => {
} }
const models = Self.app.models; const models = Self.app.models;
const sender = await models.Account.findById(senderFk); const sender = await models.VnUser.findById(senderFk);
const login = await Self.getServiceAuth(); const login = await Self.getServiceAuth();
const avatar = `${login.host}/avatar/${sender.name}`; const avatar = `${login.host}/avatar/${sender.name}`;
@ -128,15 +130,17 @@ module.exports = Self => {
* @param {object} chat - The chat * @param {object} chat - The chat
* @param {string} status - The new status * @param {string} status - The new status
* @param {string} error - The error * @param {string} error - The error
* @param {object} options - Query options
* @return {Promise} - The request promise * @return {Promise} - The request promise
*/ */
async function updateChat(chat, status, error) {
Self.updateChat = async(chat, status, error) => {
return chat.updateAttributes({ return chat.updateAttributes({
status: status, status: status,
attempts: ++chat.attempts, attempts: ++chat.attempts,
error: error error: error
}); });
} };
/** /**
* Returns the current user status on Rocketchat * Returns the current user status on Rocketchat

View File

@ -1,12 +1,12 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('Chat notifyIssue()', () => { describe('Chat notifyIssue()', () => {
const ctx = {req: {accessToken: {userId: 1}}}; const ctx = {req: {accessToken: {userId: 1}}};
ctx.req.__ = value => { ctx.req.__ = value => {
return value; return value;
}; };
const chatModel = app.models.Chat; const chatModel = models.Chat;
const osTicketModel = app.models.OsTicket; const osTicketModel = models.OsTicket;
const departmentId = 31; const departmentId = 31;
it(`should not call to the send() method and neither return a response`, async() => { it(`should not call to the send() method and neither return a response`, async() => {
@ -29,7 +29,7 @@ describe('Chat notifyIssue()', () => {
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const expectedMessage = `@all ➔ There's a new urgent ticket:\r\n[ID: 00001 - Issue title @batman](https://cau.verdnatura.es/scp/tickets.php?id=1)`; const expectedMessage = `@all ➔ There's a new urgent ticket:\r\n[ID: 00001 - Issue title @batman](https://cau.verdnatura.es/scp/tickets.php?id=1)`;
const department = await app.models.Department.findById(departmentId); const department = await models.Department.findById(departmentId);
let orgChatName = department.chatName; let orgChatName = department.chatName;
await department.updateAttribute('chatName', 'IT'); await department.updateAttribute('chatName', 'IT');

View File

@ -1,16 +1,16 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('Chat send()', () => { describe('Chat send()', () => {
it('should return true as response', async() => { it('should return true as response', async() => {
let ctx = {req: {accessToken: {userId: 1}}}; let ctx = {req: {accessToken: {userId: 1}}};
let response = await app.models.Chat.send(ctx, '@salesPerson', 'I changed something'); let response = await models.Chat.send(ctx, '@salesPerson', 'I changed something');
expect(response).toEqual(true); expect(response).toEqual(true);
}); });
it('should return false as response', async() => { it('should return false as response', async() => {
let ctx = {req: {accessToken: {userId: 18}}}; let ctx = {req: {accessToken: {userId: 18}}};
let response = await app.models.Chat.send(ctx, '@salesPerson', 'I changed something'); let response = await models.Chat.send(ctx, '@salesPerson', 'I changed something');
expect(response).toEqual(false); expect(response).toEqual(false);
}); });

View File

@ -10,7 +10,7 @@ describe('Chat sendCheckingPresence()', () => {
const chat = { const chat = {
checkUserStatus: 1, checkUserStatus: 1,
status: 0, status: 'pending',
attempts: 0 attempts: 0
}; };
@ -27,7 +27,7 @@ describe('Chat sendCheckingPresence()', () => {
const chat = { const chat = {
checkUserStatus: 0, checkUserStatus: 0,
status: 0, status: 'pending',
attempts: 0 attempts: 0
}; };

View File

@ -1,8 +1,8 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('getSectors()', () => { describe('getSectors()', () => {
it('return list of sectors', async() => { it('return list of sectors', async() => {
let response = await app.models.Collection.getSectors(); let response = await models.Collection.getSectors();
expect(response.length).toBeGreaterThan(0); expect(response.length).toBeGreaterThan(0);
expect(response[0].id).toEqual(1); expect(response[0].id).toEqual(1);

View File

@ -1,10 +1,10 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('newCollection()', () => { describe('newCollection()', () => {
it('should return a new collection', async() => { it('should return a new collection', async() => {
pending('#3400 analizar que hacer con rutas de back collection'); pending('#3400 analizar que hacer con rutas de back collection');
let ctx = {req: {accessToken: {userId: 1106}}}; let ctx = {req: {accessToken: {userId: 1106}}};
let response = await app.models.Collection.newCollection(ctx, 1, 1, 1); let response = await models.Collection.newCollection(ctx, 1, 1, 1);
expect(response.length).toBeGreaterThan(0); expect(response.length).toBeGreaterThan(0);
expect(response[0].ticketFk).toEqual(2); expect(response[0].ticketFk).toEqual(2);

View File

@ -1,6 +1,21 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context');
describe('setSaleQuantity()', () => { describe('setSaleQuantity()', () => {
beforeAll(async() => {
const activeCtx = {
accessToken: {userId: 9},
http: {
req: {
headers: {origin: 'http://localhost'}
}
}
};
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({
active: activeCtx
});
});
it('should change quantity sale', async() => { it('should change quantity sale', async() => {
const tx = await models.Ticket.beginTransaction({}); const tx = await models.Ticket.beginTransaction({});

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('dms downloadFile()', () => { describe('dms downloadFile()', () => {
let dmsId = 1; let dmsId = 1;
@ -6,7 +6,7 @@ describe('dms downloadFile()', () => {
it('should return a response for an employee with text content-type', async() => { it('should return a response for an employee with text content-type', async() => {
let workerId = 1107; let workerId = 1107;
let ctx = {req: {accessToken: {userId: workerId}}}; let ctx = {req: {accessToken: {userId: workerId}}};
const result = await app.models.Dms.downloadFile(ctx, dmsId); const result = await models.Dms.downloadFile(ctx, dmsId);
expect(result[1]).toEqual('text/plain'); expect(result[1]).toEqual('text/plain');
}); });
@ -16,7 +16,7 @@ describe('dms downloadFile()', () => {
let ctx = {req: {accessToken: {userId: clientId}}}; let ctx = {req: {accessToken: {userId: clientId}}};
let error; let error;
await app.models.Dms.downloadFile(ctx, dmsId).catch(e => { await models.Dms.downloadFile(ctx, dmsId).catch(e => {
error = e; error = e;
}).finally(() => { }).finally(() => {
expect(error.message).toEqual(`You don't have enough privileges`); expect(error.message).toEqual(`You don't have enough privileges`);

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('dms removeFile()', () => { describe('dms removeFile()', () => {
let dmsId = 1; let dmsId = 1;
@ -8,7 +8,7 @@ describe('dms removeFile()', () => {
let ctx = {req: {accessToken: {userId: clientId}}}; let ctx = {req: {accessToken: {userId: clientId}}};
let error; let error;
await app.models.Dms.removeFile(ctx, dmsId).catch(e => { await models.Dms.removeFile(ctx, dmsId).catch(e => {
error = e; error = e;
}).finally(() => { }).finally(() => {
expect(error.message).toEqual(`You don't have enough privileges`); expect(error.message).toEqual(`You don't have enough privileges`);

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('dms updateFile()', () => { describe('dms updateFile()', () => {
it(`should return an error for a user without enough privileges`, async() => { it(`should return an error for a user without enough privileges`, async() => {
@ -11,7 +11,7 @@ describe('dms updateFile()', () => {
let ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: dmsTypeId}}; let ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: dmsTypeId}};
let error; let error;
await app.models.Dms.updateFile(ctx, dmsId, warehouseId, companyId, dmsTypeId).catch(e => { await models.Dms.updateFile(ctx, dmsId, warehouseId, companyId, dmsTypeId).catch(e => {
error = e; error = e;
}).finally(() => { }).finally(() => {
expect(error.message).toEqual(`You don't have enough privileges`); expect(error.message).toEqual(`You don't have enough privileges`);

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('dms uploadFile()', () => { describe('dms uploadFile()', () => {
it(`should return an error for a user without enough privileges`, async() => { it(`should return an error for a user without enough privileges`, async() => {
@ -7,7 +7,7 @@ describe('dms uploadFile()', () => {
let ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: ticketDmsTypeId}}; let ctx = {req: {accessToken: {userId: clientId}}, args: {dmsTypeId: ticketDmsTypeId}};
let error; let error;
await app.models.Dms.uploadFile(ctx).catch(e => { await models.Dms.uploadFile(ctx).catch(e => {
error = e; error = e;
}).finally(() => { }).finally(() => {
expect(error.message).toEqual(`You don't have enough privileges`); expect(error.message).toEqual(`You don't have enough privileges`);

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('image download()', () => { describe('image download()', () => {
const collection = 'user'; const collection = 'user';
@ -8,7 +8,7 @@ describe('image download()', () => {
it('should return the image content-type of the user', async() => { it('should return the image content-type of the user', async() => {
const userId = 9; const userId = 9;
const image = await app.models.Image.download(ctx, collection, size, userId); const image = await models.Image.download(ctx, collection, size, userId);
const contentType = image[1]; const contentType = image[1];
expect(contentType).toEqual('image/png'); expect(contentType).toEqual('image/png');
@ -16,7 +16,7 @@ describe('image download()', () => {
it(`should return false if the user doesn't have image`, async() => { it(`should return false if the user doesn't have image`, async() => {
const userId = 1110; const userId = 1110;
const image = await app.models.Image.download(ctx, collection, size, userId); const image = await models.Image.download(ctx, collection, size, userId);
expect(image).toBeFalse(); expect(image).toBeFalse();
}); });

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('image upload()', () => { describe('image upload()', () => {
describe('as buyer', () => { describe('as buyer', () => {
@ -16,7 +16,7 @@ describe('image upload()', () => {
let error; let error;
try { try {
await app.models.Image.upload(ctx); await models.Image.upload(ctx);
} catch (err) { } catch (err) {
error = err; error = err;
} }
@ -25,7 +25,7 @@ describe('image upload()', () => {
}); });
it('should call to the TempContainer upload method for the collection "catalog"', async() => { it('should call to the TempContainer upload method for the collection "catalog"', async() => {
const containerModel = app.models.TempContainer; const containerModel = models.TempContainer;
spyOn(containerModel, 'upload'); spyOn(containerModel, 'upload');
const ctx = {req: {accessToken: {userId: buyerId}}, const ctx = {req: {accessToken: {userId: buyerId}},
@ -36,7 +36,7 @@ describe('image upload()', () => {
}; };
try { try {
await app.models.Image.upload(ctx); await models.Image.upload(ctx);
} catch (err) { } } catch (err) { }
expect(containerModel.upload).toHaveBeenCalled(); expect(containerModel.upload).toHaveBeenCalled();
@ -49,7 +49,7 @@ describe('image upload()', () => {
const itemId = 4; const itemId = 4;
it('should be able to call to the TempContainer upload method for the collection "user"', async() => { it('should be able to call to the TempContainer upload method for the collection "user"', async() => {
const containerModel = app.models.TempContainer; const containerModel = models.TempContainer;
spyOn(containerModel, 'upload'); spyOn(containerModel, 'upload');
const ctx = {req: {accessToken: {userId: marketingId}}, const ctx = {req: {accessToken: {userId: marketingId}},
@ -60,14 +60,14 @@ describe('image upload()', () => {
}; };
try { try {
await app.models.Image.upload(ctx); await models.Image.upload(ctx);
} catch (err) { } } catch (err) { }
expect(containerModel.upload).toHaveBeenCalled(); expect(containerModel.upload).toHaveBeenCalled();
}); });
it('should be able to call to the TempContainer upload method for the collection "catalog"', async() => { it('should be able to call to the TempContainer upload method for the collection "catalog"', async() => {
const containerModel = app.models.TempContainer; const containerModel = models.TempContainer;
spyOn(containerModel, 'upload'); spyOn(containerModel, 'upload');
const ctx = {req: {accessToken: {userId: marketingId}}, const ctx = {req: {accessToken: {userId: marketingId}},
@ -78,7 +78,7 @@ describe('image upload()', () => {
}; };
try { try {
await app.models.Image.upload(ctx); await models.Image.upload(ctx);
} catch (err) { } } catch (err) { }
expect(containerModel.upload).toHaveBeenCalled(); expect(containerModel.upload).toHaveBeenCalled();
@ -91,7 +91,7 @@ describe('image upload()', () => {
const itemId = 4; const itemId = 4;
it('should upload a file for the collection "user" and call to the TempContainer upload method', async() => { it('should upload a file for the collection "user" and call to the TempContainer upload method', async() => {
const containerModel = app.models.TempContainer; const containerModel = models.TempContainer;
spyOn(containerModel, 'upload'); spyOn(containerModel, 'upload');
const ctx = {req: {accessToken: {userId: hhrrId}}, const ctx = {req: {accessToken: {userId: hhrrId}},
@ -102,7 +102,7 @@ describe('image upload()', () => {
}; };
try { try {
await app.models.Image.upload(ctx); await models.Image.upload(ctx);
} catch (err) { } } catch (err) { }
expect(containerModel.upload).toHaveBeenCalled(); expect(containerModel.upload).toHaveBeenCalled();
@ -118,7 +118,7 @@ describe('image upload()', () => {
let error; let error;
try { try {
await app.models.Image.upload(ctx); await models.Image.upload(ctx);
} catch (err) { } catch (err) {
error = err; error = err;
} }

View File

@ -1,7 +1,6 @@
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
const fs = require('fs-extra'); const fs = require('fs/promises');
const path = require('path'); const path = require('path');
const uuid = require('uuid');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('upload', { Self.remoteMethodCtx('upload', {
@ -36,7 +35,7 @@ module.exports = Self => {
const fileOptions = {}; const fileOptions = {};
const args = ctx.args; const args = ctx.args;
let srcFile; let tempFilePath;
try { try {
const hasWriteRole = await models.ImageCollection.hasWriteRole(ctx, args.collection); const hasWriteRole = await models.ImageCollection.hasWriteRole(ctx, args.collection);
if (!hasWriteRole) if (!hasWriteRole)
@ -53,15 +52,20 @@ module.exports = Self => {
}); });
const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name); const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name);
srcFile = path.join(file.client.root, file.container, file.name); tempFilePath = path.join(file.client.root, file.container, file.name);
const fileName = `${uuid.v4()}.png`; const fileName = `${args.id}.png`;
await models.Image.registerImage(args.collection, srcFile, fileName, args.id);
} catch (e) {
if (fs.existsSync(srcFile))
await fs.unlink(srcFile);
throw e; await models.Image.resize({
collectionName: args.collection,
srcFile: tempFilePath,
fileName: fileName,
entityId: args.id
});
} finally {
try {
await fs.unlink(tempFilePath);
} catch (error) { }
} }
}; };
}; };

View File

@ -69,15 +69,15 @@ module.exports = Self => {
const result = response.headers.get('set-cookie'); const result = response.headers.get('set-cookie');
const [firtHeader] = result.split(' '); const [firtHeader] = result.split(' ');
const firtCookie = firtHeader.substring(0, firtHeader.length - 1); const cookie = firtHeader.substring(0, firtHeader.length - 1);
const body = await response.text(); const body = await response.text();
const dom = new jsdom.JSDOM(body); const dom = new jsdom.JSDOM(body);
const token = dom.window.document.querySelector('[name="__CSRFToken__"]').value; const token = dom.window.document.querySelector('[name="__CSRFToken__"]').value;
await login(token, firtCookie); await login(token, cookie);
} }
async function login(token, firtCookie) { async function login(token, cookie) {
const data = { const data = {
__CSRFToken__: token, __CSRFToken__: token,
do: 'scplogin', do: 'scplogin',
@ -90,21 +90,18 @@ module.exports = Self => {
body: new URLSearchParams(data), body: new URLSearchParams(data),
headers: { headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': firtCookie 'Cookie': cookie
} }
}; };
const response = await fetch(ostUri, params); await fetch(ostUri, params);
const result = response.headers.get('set-cookie');
const [firtHeader] = result.split(' ');
const secondCookie = firtHeader.substring(0, firtHeader.length - 1);
await close(token, secondCookie); await close(token, cookie);
} }
async function close(token, secondCookie) { async function close(token, cookie) {
for (const ticketId of ticketsId) { for (const ticketId of ticketsId) {
try { try {
const lock = await getLockCode(token, secondCookie, ticketId); const lock = await getLockCode(token, cookie, ticketId);
if (!lock.code) { if (!lock.code) {
let error = `Can't get lock code`; let error = `Can't get lock code`;
if (lock.msg) error += `: ${lock.msg}`; if (lock.msg) error += `: ${lock.msg}`;
@ -127,7 +124,7 @@ module.exports = Self => {
method: 'POST', method: 'POST',
body: form, body: form,
headers: { headers: {
'Cookie': secondCookie 'Cookie': cookie
} }
}; };
await fetch(ostUri, params); await fetch(ostUri, params);
@ -139,13 +136,13 @@ module.exports = Self => {
} }
} }
async function getLockCode(token, secondCookie, ticketId) { async function getLockCode(token, cookie, ticketId) {
const ostUri = `${config.host}/ajax.php/lock/ticket/${ticketId}`; const ostUri = `${config.host}/ajax.php/lock/ticket/${ticketId}`;
const params = { const params = {
method: 'POST', method: 'POST',
headers: { headers: {
'X-CSRFToken': token, 'X-CSRFToken': token,
'Cookie': secondCookie 'Cookie': cookie
} }
}; };
const response = await fetch(ostUri, params); const response = await fetch(ostUri, params);

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('getStarredModules()', () => { describe('getStarredModules()', () => {
@ -19,13 +19,13 @@ describe('getStarredModules()', () => {
}); });
it(`should return the starred modules for a given user`, async() => { it(`should return the starred modules for a given user`, async() => {
const newStarred = await app.models.StarredModule.create({workerFk: 9, moduleFk: 'customer', position: 1}); const newStarred = await models.StarredModule.create({workerFk: 9, moduleFk: 'customer', position: 1});
const starredModules = await app.models.StarredModule.getStarredModules(ctx); const starredModules = await models.StarredModule.getStarredModules(ctx);
expect(starredModules.length).toEqual(1); expect(starredModules.length).toEqual(1);
expect(starredModules[0].moduleFk).toEqual('customer'); expect(starredModules[0].moduleFk).toEqual('customer');
// restores // restores
await app.models.StarredModule.destroyById(newStarred.id); await models.StarredModule.destroyById(newStarred.id);
}); });
}); });

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('setPosition()', () => { describe('setPosition()', () => {
@ -21,7 +21,7 @@ describe('setPosition()', () => {
}); });
it('should increase the orders module position by replacing it with clients and vice versa', async() => { it('should increase the orders module position by replacing it with clients and vice versa', async() => {
const tx = await app.models.StarredModule.beginTransaction({}); const tx = await models.StarredModule.beginTransaction({});
const filter = { const filter = {
where: { where: {
@ -32,24 +32,24 @@ describe('setPosition()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options); await models.StarredModule.toggleStarredModule(ctx, 'order', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options); await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
let orders = await app.models.StarredModule.findOne(filter, options); let orders = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'customer'; filter.where.moduleFk = 'customer';
let clients = await app.models.StarredModule.findOne(filter, options); let clients = await models.StarredModule.findOne(filter, options);
expect(orders.position).toEqual(1); expect(orders.position).toEqual(1);
expect(clients.position).toEqual(2); expect(clients.position).toEqual(2);
await app.models.StarredModule.setPosition(ctx, 'customer', 'left', options); await models.StarredModule.setPosition(ctx, 'customer', 'left', options);
filter.where.moduleFk = 'customer'; filter.where.moduleFk = 'customer';
clients = await app.models.StarredModule.findOne(filter, options); clients = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'order'; filter.where.moduleFk = 'order';
orders = await app.models.StarredModule.findOne(filter, options); orders = await models.StarredModule.findOne(filter, options);
expect(clients.position).toEqual(1); expect(clients.position).toEqual(1);
expect(orders.position).toEqual(2); expect(orders.position).toEqual(2);
@ -62,7 +62,7 @@ describe('setPosition()', () => {
}); });
it('should decrease the orders module position by replacing it with clients and vice versa', async() => { it('should decrease the orders module position by replacing it with clients and vice versa', async() => {
const tx = await app.models.StarredModule.beginTransaction({}); const tx = await models.StarredModule.beginTransaction({});
const filter = { const filter = {
where: { where: {
@ -73,24 +73,24 @@ describe('setPosition()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options); await models.StarredModule.toggleStarredModule(ctx, 'order', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options); await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
let orders = await app.models.StarredModule.findOne(filter, options); let orders = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'customer'; filter.where.moduleFk = 'customer';
let clients = await app.models.StarredModule.findOne(filter, options); let clients = await models.StarredModule.findOne(filter, options);
expect(orders.position).toEqual(1); expect(orders.position).toEqual(1);
expect(clients.position).toEqual(2); expect(clients.position).toEqual(2);
await app.models.StarredModule.setPosition(ctx, 'order', 'right', options); await models.StarredModule.setPosition(ctx, 'order', 'right', options);
filter.where.moduleFk = 'order'; filter.where.moduleFk = 'order';
orders = await app.models.StarredModule.findOne(filter, options); orders = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'customer'; filter.where.moduleFk = 'customer';
clients = await app.models.StarredModule.findOne(filter, options); clients = await models.StarredModule.findOne(filter, options);
expect(orders.position).toEqual(2); expect(orders.position).toEqual(2);
expect(clients.position).toEqual(1); expect(clients.position).toEqual(1);
@ -103,7 +103,7 @@ describe('setPosition()', () => {
}); });
it('should switch two modules after adding and deleting several modules', async() => { it('should switch two modules after adding and deleting several modules', async() => {
const tx = await app.models.StarredModule.beginTransaction({}); const tx = await models.StarredModule.beginTransaction({});
const filter = { const filter = {
where: { where: {
@ -115,29 +115,29 @@ describe('setPosition()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options); await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options); await models.StarredModule.toggleStarredModule(ctx, 'order', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options); await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options); await models.StarredModule.toggleStarredModule(ctx, 'order', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'item', options); await models.StarredModule.toggleStarredModule(ctx, 'item', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'claim', options); await models.StarredModule.toggleStarredModule(ctx, 'claim', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options); await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options); await models.StarredModule.toggleStarredModule(ctx, 'order', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'zone', options); await models.StarredModule.toggleStarredModule(ctx, 'zone', options);
const items = await app.models.StarredModule.findOne(filter, options); const items = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'claim'; filter.where.moduleFk = 'claim';
const claims = await app.models.StarredModule.findOne(filter, options); const claims = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'customer'; filter.where.moduleFk = 'customer';
let clients = await app.models.StarredModule.findOne(filter, options); let clients = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'order'; filter.where.moduleFk = 'order';
let orders = await app.models.StarredModule.findOne(filter, options); let orders = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'zone'; filter.where.moduleFk = 'zone';
const zones = await app.models.StarredModule.findOne(filter, options); const zones = await models.StarredModule.findOne(filter, options);
expect(items.position).toEqual(1); expect(items.position).toEqual(1);
expect(claims.position).toEqual(2); expect(claims.position).toEqual(2);
@ -145,13 +145,13 @@ describe('setPosition()', () => {
expect(orders.position).toEqual(4); expect(orders.position).toEqual(4);
expect(zones.position).toEqual(5); expect(zones.position).toEqual(5);
await app.models.StarredModule.setPosition(ctx, 'customer', 'right', options); await models.StarredModule.setPosition(ctx, 'customer', 'right', options);
filter.where.moduleFk = 'order'; filter.where.moduleFk = 'order';
orders = await app.models.StarredModule.findOne(filter, options); orders = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'customer'; filter.where.moduleFk = 'customer';
clients = await app.models.StarredModule.findOne(filter, options); clients = await models.StarredModule.findOne(filter, options);
expect(orders.position).toEqual(3); expect(orders.position).toEqual(3);
expect(clients.position).toEqual(4); expect(clients.position).toEqual(4);
@ -164,7 +164,7 @@ describe('setPosition()', () => {
}); });
it('should switch two modules after adding and deleting a module between them', async() => { it('should switch two modules after adding and deleting a module between them', async() => {
const tx = await app.models.StarredModule.beginTransaction({}); const tx = await models.StarredModule.beginTransaction({});
const filter = { const filter = {
where: { where: {
@ -176,25 +176,25 @@ describe('setPosition()', () => {
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await app.models.StarredModule.toggleStarredModule(ctx, 'item', options); await models.StarredModule.toggleStarredModule(ctx, 'item', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'customer', options); await models.StarredModule.toggleStarredModule(ctx, 'customer', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'claim', options); await models.StarredModule.toggleStarredModule(ctx, 'claim', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'order', options); await models.StarredModule.toggleStarredModule(ctx, 'order', options);
await app.models.StarredModule.toggleStarredModule(ctx, 'zone', options); await models.StarredModule.toggleStarredModule(ctx, 'zone', options);
const items = await app.models.StarredModule.findOne(filter, options); const items = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'customer'; filter.where.moduleFk = 'customer';
let clients = await app.models.StarredModule.findOne(filter, options); let clients = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'claim'; filter.where.moduleFk = 'claim';
const claims = await app.models.StarredModule.findOne(filter, options); const claims = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'order'; filter.where.moduleFk = 'order';
let orders = await app.models.StarredModule.findOne(filter, options); let orders = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'zone'; filter.where.moduleFk = 'zone';
const zones = await app.models.StarredModule.findOne(filter, options); const zones = await models.StarredModule.findOne(filter, options);
expect(items.position).toEqual(1); expect(items.position).toEqual(1);
expect(clients.position).toEqual(2); expect(clients.position).toEqual(2);
@ -202,14 +202,14 @@ describe('setPosition()', () => {
expect(orders.position).toEqual(4); expect(orders.position).toEqual(4);
expect(zones.position).toEqual(5); expect(zones.position).toEqual(5);
await app.models.StarredModule.toggleStarredModule(ctx, 'claim', options); await models.StarredModule.toggleStarredModule(ctx, 'claim', options);
await app.models.StarredModule.setPosition(ctx, 'customer', 'right', options); await models.StarredModule.setPosition(ctx, 'customer', 'right', options);
filter.where.moduleFk = 'customer'; filter.where.moduleFk = 'customer';
clients = await app.models.StarredModule.findOne(filter, options); clients = await models.StarredModule.findOne(filter, options);
filter.where.moduleFk = 'order'; filter.where.moduleFk = 'order';
orders = await app.models.StarredModule.findOne(filter, options); orders = await models.StarredModule.findOne(filter, options);
expect(orders.position).toEqual(2); expect(orders.position).toEqual(2);
expect(clients.position).toEqual(4); expect(clients.position).toEqual(4);

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('toggleStarredModule()', () => { describe('toggleStarredModule()', () => {
@ -21,16 +21,16 @@ describe('toggleStarredModule()', () => {
}); });
it('should create a new starred module and then remove it by calling the method again with same args', async() => { it('should create a new starred module and then remove it by calling the method again with same args', async() => {
const starredModule = await app.models.StarredModule.toggleStarredModule(ctx, 'order'); const starredModule = await models.StarredModule.toggleStarredModule(ctx, 'order');
let starredModules = await app.models.StarredModule.getStarredModules(ctx); let starredModules = await models.StarredModule.getStarredModules(ctx);
expect(starredModules.length).toEqual(1); expect(starredModules.length).toEqual(1);
expect(starredModule.moduleFk).toEqual('order'); expect(starredModule.moduleFk).toEqual('order');
expect(starredModule.workerFk).toEqual(activeCtx.accessToken.userId); expect(starredModule.workerFk).toEqual(activeCtx.accessToken.userId);
expect(starredModule.position).toEqual(starredModules.length); expect(starredModule.position).toEqual(starredModules.length);
await app.models.StarredModule.toggleStarredModule(ctx, 'order'); await models.StarredModule.toggleStarredModule(ctx, 'order');
starredModules = await app.models.StarredModule.getStarredModules(ctx); starredModules = await models.StarredModule.getStarredModules(ctx);
expect(starredModules.length).toEqual(0); expect(starredModules.length).toEqual(0);
}); });

View File

@ -22,7 +22,7 @@ module.exports = Self => {
let userId = ctx.req.accessToken.userId; let userId = ctx.req.accessToken.userId;
let models = Self.app.models; let models = Self.app.models;
let user = await models.Account.findById(userId, { let user = await Self.findById(userId, {
fields: ['id', 'name', 'nickname', 'email', 'lang'], fields: ['id', 'name', 'nickname', 'email', 'lang'],
include: { include: {
relation: 'userConfig', relation: 'userConfig',

View File

@ -1,9 +1,14 @@
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethodCtx('privileges', { Self.remoteMethod('privileges', {
description: 'Change role and hasGrant if user has privileges', description: 'Change role and hasGrant if user has privileges',
accepts: [ accepts: [
{
arg: 'ctx',
type: 'Object',
http: {source: 'context'}
},
{ {
arg: 'id', arg: 'id',
type: 'number', type: 'number',
@ -39,9 +44,9 @@ module.exports = Self => {
if (typeof options == 'object') if (typeof options == 'object')
Object.assign(myOptions, options); Object.assign(myOptions, options);
const user = await models.Account.findById(userId, {fields: ['hasGrant']}, myOptions); const user = await Self.findById(userId, {fields: ['hasGrant']}, myOptions);
const userToUpdate = await models.Account.findById(id, { const userToUpdate = await Self.findById(id, {
fields: ['id', 'name', 'hasGrant', 'roleFk', 'password'], fields: ['id', 'name', 'hasGrant', 'roleFk', 'password'],
include: { include: {
relation: 'role', relation: 'role',
@ -54,7 +59,7 @@ module.exports = Self => {
if (!user.hasGrant) if (!user.hasGrant)
throw new UserError(`You don't have grant privilege`); throw new UserError(`You don't have grant privilege`);
const hasRoleFromUser = await models.Account.hasRole(userId, userToUpdate.role().name, myOptions); const hasRoleFromUser = await Self.hasRole(userId, userToUpdate.role().name, myOptions);
if (!hasRoleFromUser) if (!hasRoleFromUser)
throw new UserError(`You don't own the role and you can't assign it to another user`); throw new UserError(`You don't own the role and you can't assign it to another user`);
@ -64,7 +69,7 @@ module.exports = Self => {
if (roleFk) { if (roleFk) {
const role = await models.Role.findById(roleFk, {fields: ['name']}, myOptions); const role = await models.Role.findById(roleFk, {fields: ['name']}, myOptions);
const hasRole = await models.Account.hasRole(userId, role.name, myOptions); const hasRole = await Self.hasRole(userId, role.name, myOptions);
if (!hasRole) if (!hasRole)
throw new UserError(`You don't own the role and you can't assign it to another user`); throw new UserError(`You don't own the role and you can't assign it to another user`);
@ -73,6 +78,6 @@ module.exports = Self => {
} }
await userToUpdate.save(userToUpdate); await userToUpdate.save(userToUpdate);
await models.UserAccount.sync(userToUpdate.name); await models.Account.sync(userToUpdate.name);
}; };
}; };

View File

@ -20,7 +20,7 @@ module.exports = Self => {
const usesEmail = user.indexOf('@') !== -1; const usesEmail = user.indexOf('@') !== -1;
if (!usesEmail) { if (!usesEmail) {
const account = await models.Account.findOne({ const account = await models.VnUser.findOne({
fields: ['email'], fields: ['email'],
where: {name: user} where: {name: user}
}); });
@ -28,7 +28,7 @@ module.exports = Self => {
} }
try { try {
await models.user.resetPassword({email: user, emailTemplate: 'recover-password'}); await Self.resetPassword({email: user, emailTemplate: 'recover-password'});
} catch (err) { } catch (err) {
if (err.code === 'EMAIL_NOT_FOUND') if (err.code === 'EMAIL_NOT_FOUND')
return; return;

View File

@ -1,14 +1,14 @@
const md5 = require('md5');
const UserError = require('vn-loopback/util/user-error'); const UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('login', { Self.remoteMethod('signIn', {
description: 'Login a user with username/email and password', description: 'Login a user with username/email and password',
accepts: [ accepts: [
{ {
arg: 'user', arg: 'user',
type: 'String', type: 'String',
description: 'The user name or email', description: 'The user name or email',
http: {source: 'form'},
required: true required: true
}, { }, {
arg: 'password', arg: 'password',
@ -21,20 +21,20 @@ module.exports = Self => {
root: true root: true
}, },
http: { http: {
path: `/login`, path: `/signIn`,
verb: 'POST' verb: 'POST'
} }
}); });
Self.login = async function(user, password) { Self.signIn = async function(user, password) {
let $ = Self.app.models; let models = Self.app.models;
let token; let token;
let usesEmail = user.indexOf('@') !== -1; let usesEmail = user.indexOf('@') !== -1;
let userInfo = usesEmail let userInfo = usesEmail
? {email: user} ? {email: user}
: {username: user}; : {username: user};
let instance = await $.User.findOne({ let instance = await Self.findOne({
fields: ['username', 'password'], fields: ['username', 'password'],
where: userInfo where: userInfo
}); });
@ -42,29 +42,27 @@ module.exports = Self => {
let where = usesEmail let where = usesEmail
? {email: user} ? {email: user}
: {name: user}; : {name: user};
let account = await Self.findOne({ let vnUser = await Self.findOne({
fields: ['active', 'password'], fields: ['active'],
where where
}); });
let validCredentials = instance && ( let validCredentials = instance
await instance.hasPassword(password) || && await instance.hasPassword(password);
account.password == md5(password || '')
);
if (validCredentials) { if (validCredentials) {
if (!account.active) if (!vnUser.active)
throw new UserError('User disabled'); throw new UserError('User disabled');
try { try {
await $.UserAccount.sync(instance.username, password); await models.Account.sync(instance.username, password);
} catch (err) { } catch (err) {
console.warn(err); console.warn(err);
} }
} }
let loginInfo = Object.assign({password}, userInfo); let loginInfo = Object.assign({password}, userInfo);
token = await $.User.login(loginInfo, 'user'); token = await Self.login(loginInfo, 'user');
return {token: token.id}; return {token: token.id};
}; };
}; };

View File

@ -1,6 +1,6 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
describe('account privileges()', () => { describe('VnUser privileges()', () => {
const employeeId = 1; const employeeId = 1;
const developerId = 9; const developerId = 9;
const sysadminId = 66; const sysadminId = 66;
@ -10,13 +10,13 @@ describe('account privileges()', () => {
it('should throw an error when user not has privileges', async() => { it('should throw an error when user not has privileges', async() => {
const ctx = {req: {accessToken: {userId: developerId}}}; const ctx = {req: {accessToken: {userId: developerId}}};
const tx = await models.Account.beginTransaction({}); const tx = await models.VnUser.beginTransaction({});
let error; let error;
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await models.Account.privileges(ctx, employeeId, null, true, options); await models.VnUser.privileges(ctx, employeeId, null, true, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -29,13 +29,13 @@ describe('account privileges()', () => {
it('should throw an error when user has privileges but not has the role', async() => { it('should throw an error when user has privileges but not has the role', async() => {
const ctx = {req: {accessToken: {userId: sysadminId}}}; const ctx = {req: {accessToken: {userId: sysadminId}}};
const tx = await models.Account.beginTransaction({}); const tx = await models.VnUser.beginTransaction({});
let error; let error;
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await models.Account.privileges(ctx, employeeId, rootId, null, options); await models.VnUser.privileges(ctx, employeeId, rootId, null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -48,13 +48,13 @@ describe('account privileges()', () => {
it('should throw an error when user has privileges but not has the role from user', async() => { it('should throw an error when user has privileges but not has the role from user', async() => {
const ctx = {req: {accessToken: {userId: sysadminId}}}; const ctx = {req: {accessToken: {userId: sysadminId}}};
const tx = await models.Account.beginTransaction({}); const tx = await models.VnUser.beginTransaction({});
let error; let error;
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await models.Account.privileges(ctx, itBossId, developerId, null, options); await models.VnUser.privileges(ctx, itBossId, developerId, null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -67,7 +67,7 @@ describe('account privileges()', () => {
it('should change role', async() => { it('should change role', async() => {
const ctx = {req: {accessToken: {userId: sysadminId}}}; const ctx = {req: {accessToken: {userId: sysadminId}}};
const tx = await models.Account.beginTransaction({}); const tx = await models.VnUser.beginTransaction({});
const options = {transaction: tx}; const options = {transaction: tx};
const agency = await models.Role.findOne({ const agency = await models.Role.findOne({
@ -79,8 +79,8 @@ describe('account privileges()', () => {
let error; let error;
let result; let result;
try { try {
await models.Account.privileges(ctx, clarkKent, agency.id, null, options); await models.VnUser.privileges(ctx, clarkKent, agency.id, null, options);
result = await models.Account.findById(clarkKent, null, options); result = await models.VnUser.findById(clarkKent, null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {
@ -94,14 +94,14 @@ describe('account privileges()', () => {
it('should change hasGrant', async() => { it('should change hasGrant', async() => {
const ctx = {req: {accessToken: {userId: sysadminId}}}; const ctx = {req: {accessToken: {userId: sysadminId}}};
const tx = await models.Account.beginTransaction({}); const tx = await models.VnUser.beginTransaction({});
let error; let error;
let result; let result;
try { try {
const options = {transaction: tx}; const options = {transaction: tx};
await models.Account.privileges(ctx, clarkKent, null, true, options); await models.VnUser.privileges(ctx, clarkKent, null, true, options);
result = await models.Account.findById(clarkKent, null, options); result = await models.VnUser.findById(clarkKent, null, options);
await tx.rollback(); await tx.rollback();
} catch (e) { } catch (e) {

View File

@ -1,25 +1,25 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('account login()', () => { describe('VnUser signIn()', () => {
describe('when credentials are correct', () => { describe('when credentials are correct', () => {
it('should return the token', async() => { it('should return the token', async() => {
let login = await app.models.Account.login('salesAssistant', 'nightmare'); let login = await models.VnUser.signIn('salesAssistant', 'nightmare');
let accessToken = await app.models.AccessToken.findById(login.token); let accessToken = await models.AccessToken.findById(login.token);
let ctx = {req: {accessToken: accessToken}}; let ctx = {req: {accessToken: accessToken}};
expect(login.token).toBeDefined(); expect(login.token).toBeDefined();
await app.models.Account.logout(ctx); await models.VnUser.signOut(ctx);
}); });
it('should return the token if the user doesnt exist but the client does', async() => { it('should return the token if the user doesnt exist but the client does', async() => {
let login = await app.models.Account.login('PetterParker', 'nightmare'); let login = await models.VnUser.signIn('PetterParker', 'nightmare');
let accessToken = await app.models.AccessToken.findById(login.token); let accessToken = await models.AccessToken.findById(login.token);
let ctx = {req: {accessToken: accessToken}}; let ctx = {req: {accessToken: accessToken}};
expect(login.token).toBeDefined(); expect(login.token).toBeDefined();
await app.models.Account.logout(ctx); await models.VnUser.signOut(ctx);
}); });
}); });
@ -28,7 +28,7 @@ describe('account login()', () => {
let error; let error;
try { try {
await app.models.Account.login('IDontExist', 'TotallyWrongPassword'); await models.VnUser.signIn('IDontExist', 'TotallyWrongPassword');
} catch (e) { } catch (e) {
error = e; error = e;
} }

View File

@ -1,13 +1,13 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('account logout()', () => { describe('VnUser signOut()', () => {
it('should logout and remove token after valid login', async() => { it('should logout and remove token after valid login', async() => {
let loginResponse = await app.models.Account.login('buyer', 'nightmare'); let loginResponse = await models.VnUser.signOut('buyer', 'nightmare');
let accessToken = await app.models.AccessToken.findById(loginResponse.token); let accessToken = await models.AccessToken.findById(loginResponse.token);
let ctx = {req: {accessToken: accessToken}}; let ctx = {req: {accessToken: accessToken}};
let logoutResponse = await app.models.Account.logout(ctx); let logoutResponse = await models.VnUser.signOut(ctx);
let tokenAfterLogout = await app.models.AccessToken.findById(loginResponse.token); let tokenAfterLogout = await models.AccessToken.findById(loginResponse.token);
expect(logoutResponse).toBeTrue(); expect(logoutResponse).toBeTrue();
expect(tokenAfterLogout).toBeNull(); expect(tokenAfterLogout).toBeNull();
@ -18,7 +18,7 @@ describe('account logout()', () => {
let ctx = {req: {accessToken: {id: 'invalidToken'}}}; let ctx = {req: {accessToken: {id: 'invalidToken'}}};
try { try {
response = await app.models.Account.logout(ctx); response = await models.VnUser.signOut(ctx);
} catch (e) { } catch (e) {
error = e; error = e;
} }
@ -32,7 +32,7 @@ describe('account logout()', () => {
let ctx = {req: {accessToken: null}}; let ctx = {req: {accessToken: null}};
try { try {
response = await app.models.Account.logout(ctx); response = await models.VnUser.signOut(ctx);
} catch (e) { } catch (e) {
error = e; error = e;
} }

View File

@ -1,7 +1,4 @@
{ {
"Account": {
"dataSource": "vn"
},
"AccountingType": { "AccountingType": {
"dataSource": "vn" "dataSource": "vn"
}, },
@ -125,10 +122,10 @@
"UserConfigView": { "UserConfigView": {
"dataSource": "vn" "dataSource": "vn"
}, },
"UserLog": { "Warehouse": {
"dataSource": "vn" "dataSource": "vn"
}, },
"Warehouse": { "VnUser": {
"dataSource": "vn" "dataSource": "vn"
}, },
"OsTicket": { "OsTicket": {

View File

@ -1,139 +0,0 @@
/* eslint max-len: ["error", { "code": 150 }]*/
const md5 = require('md5');
const LoopBackContext = require('loopback-context');
const {Email} = require('vn-print');
module.exports = Self => {
require('../methods/account/login')(Self);
require('../methods/account/logout')(Self);
require('../methods/account/acl')(Self);
require('../methods/account/change-password')(Self);
require('../methods/account/set-password')(Self);
require('../methods/account/recover-password')(Self);
require('../methods/account/validate-token')(Self);
require('../methods/account/privileges')(Self);
// Validations
Self.validatesFormatOf('email', {
message: 'Invalid email',
allowNull: true,
allowBlank: true,
with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/
});
Self.validatesUniquenessOf('name', {
message: `A client with that Web User name already exists`
});
Self.observe('before save', async function(ctx) {
if (ctx.currentInstance && ctx.currentInstance.id && ctx.data && ctx.data.password)
ctx.data.password = md5(ctx.data.password);
});
Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
if (!ctx.args || !ctx.args.data.email) return;
const models = Self.app.models;
const loopBackContext = LoopBackContext.getCurrentContext();
const httpCtx = {req: loopBackContext.active};
const httpRequest = httpCtx.req.http.req;
const headers = httpRequest.headers;
const origin = headers.origin;
const url = origin.split(':');
const userId = ctx.instance.id;
const user = await models.user.findById(userId);
class Mailer {
async send(verifyOptions, cb) {
const params = {
url: verifyOptions.verifyHref,
recipient: verifyOptions.to,
lang: ctx.req.getLocale()
};
const email = new Email('email-verify', params);
email.send();
cb(null, verifyOptions.to);
}
}
const options = {
type: 'email',
to: instance.email,
from: {},
redirect: `${origin}/#!/account/${instance.id}/basic-data?emailConfirmed`,
template: false,
mailer: new Mailer,
host: url[1].split('/')[2],
port: url[2],
protocol: url[0],
user: Self
};
await user.verify(options);
});
Self.remoteMethod('getCurrentUserData', {
description: 'Gets the current user data',
accepts: [
{
arg: 'ctx',
type: 'object',
http: {source: 'context'}
}
],
returns: {
type: 'object',
root: true
},
http: {
verb: 'GET',
path: '/getCurrentUserData'
}
});
Self.getCurrentUserData = async function(ctx) {
let userId = ctx.req.accessToken.userId;
return await Self.findById(userId, {
fields: ['id', 'name', 'nickname']
});
};
/**
* Checks if user has a role.
*
* @param {Integer} userId The user id
* @param {String} name The role name
* @param {object} options Options
* @return {Boolean} %true if user has the role, %false otherwise
*/
Self.hasRole = async function(userId, name, options) {
let roles = await Self.getRoles(userId, options);
return roles.some(role => role == name);
};
/**
* Get all user roles.
*
* @param {Integer} userId The user id
* @param {object} options Options
* @return {object} User role list
*/
Self.getRoles = async(userId, options) => {
let result = await Self.rawSql(
`SELECT r.name
FROM account.user u
JOIN account.roleRole rr ON rr.role = u.role
JOIN account.role r ON r.id = rr.inheritsFrom
WHERE u.id = ?`, [userId], options);
let roles = [];
for (const role of result)
roles.push(role.name);
return roles;
};
};

View File

@ -54,8 +54,8 @@ module.exports = Self => {
const writeRole = dmsType.writeRole() && dmsType.writeRole().name; const writeRole = dmsType.writeRole() && dmsType.writeRole().name;
const requiredRole = readRole || writeRole; const requiredRole = readRole || writeRole;
const hasRequiredRole = await models.Account.hasRole(myUserId, requiredRole, options); const hasRequiredRole = await models.VnUser.hasRole(myUserId, requiredRole, options);
const isRoot = await models.Account.hasRole(myUserId, 'root', options); const isRoot = await models.VnUser.hasRole(myUserId, 'root', options);
if (isRoot || hasRequiredRole) if (isRoot || hasRequiredRole)
return true; return true;

View File

@ -20,7 +20,7 @@
"relations": { "relations": {
"user": { "user": {
"type": "belongsTo", "type": "belongsTo",
"model": "Account", "model": "VnUser",
"foreignKey": "userFk" "foreignKey": "userFk"
} }
}, },

View File

@ -53,8 +53,8 @@ module.exports = Self => {
const writeRole = collection.writeRole() && collection.writeRole().name; const writeRole = collection.writeRole() && collection.writeRole().name;
const requiredRole = readRole || writeRole; const requiredRole = readRole || writeRole;
const hasRequiredRole = await models.Account.hasRole(myUserId, requiredRole, options); const hasRequiredRole = await models.VnUser.hasRole(myUserId, requiredRole, options);
const isRoot = await models.Account.hasRole(myUserId, 'root', options); const isRoot = await models.VnUser.hasRole(myUserId, 'root', options);
if (isRoot || hasRequiredRole) if (isRoot || hasRequiredRole)
return true; return true;

View File

@ -1,161 +1,110 @@
const fs = require('fs-extra'); const fs = require('fs-extra');
const sharp = require('sharp');
const path = require('path'); const path = require('path');
const readChunk = require('read-chunk'); const gm = require('gm');
const imageType = require('image-type');
const bmp = require('bmp-js');
module.exports = Self => { module.exports = Self => {
require('../methods/image/download')(Self); require('../methods/image/download')(Self);
require('../methods/image/upload')(Self); require('../methods/image/upload')(Self);
// Function extracted from jimp package (utils) Self.resize = async function({collectionName, srcFile, fileName, entityId}) {
function scan(image, x, y, w, h, f) {
// round input
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
for (let _y = y; _y < y + h; _y++) {
for (let _x = x; _x < x + w; _x++) {
const idx = (image.bitmap.width * _y + _x) << 2;
f.call(image, _x, _y, idx);
}
}
return image;
}
// Function extracted from jimp package (type-bmp)
function fromAGBR(bitmap) {
return scan({bitmap}, 0, 0, bitmap.width, bitmap.height, function(
x,
y,
index
) {
const alpha = this.bitmap.data[index + 0];
const blue = this.bitmap.data[index + 1];
const green = this.bitmap.data[index + 2];
const red = this.bitmap.data[index + 3];
this.bitmap.data[index + 0] = red;
this.bitmap.data[index + 1] = green;
this.bitmap.data[index + 2] = blue;
this.bitmap.data[index + 3] = bitmap.is_with_alpha ? alpha : 0xff;
}).bitmap;
}
Self.registerImage = async(collectionName, srcFilePath, fileName, entityId) => {
const models = Self.app.models; const models = Self.app.models;
const tx = await Self.beginTransaction({});
const myOptions = {transaction: tx};
try { const collection = await models.ImageCollection.findOne(
const collection = await models.ImageCollection.findOne({ {
fields: [ fields: [
'id', 'id',
'name',
'maxWidth', 'maxWidth',
'maxHeight', 'maxHeight',
'model', 'model',
'property' 'property',
], ],
where: {name: collectionName}, where: {name: collectionName},
include: { include: {
relation: 'sizes', relation: 'sizes',
scope: { scope: {
fields: ['width', 'height', 'crop'] fields: ['width', 'height', 'crop'],
} },
} },
}, myOptions); }
);
const data = { // Insert image row
await models.Image.upsertWithWhere(
{
name: fileName, name: fileName,
collectionFk: collectionName collectionFk: collectionName
}; },
const newImage = await Self.upsertWithWhere(data, { {
name: fileName, name: fileName,
collectionFk: collectionName, collectionFk: collectionName,
updated: Date.vnNow() updated: Date.vnNow() / 1000,
}, myOptions);
// Resizes and saves the image
const container = await models.ImageContainer.container(collectionName);
const rootPath = container.client.root;
const collectionDir = path.join(rootPath, collectionName);
const dstDir = path.join(collectionDir, 'full');
const dstFile = path.join(dstDir, fileName);
const buffer = readChunk.sync(srcFilePath, 0, 12);
const type = imageType(buffer);
let sharpOptions;
let imgSrc = srcFilePath;
if (type.mime == 'image/bmp') {
const bmpBuffer = fs.readFileSync(srcFilePath);
const bmpData = fromAGBR(bmp.decode(bmpBuffer));
imgSrc = bmpData.data;
sharpOptions = {
raw: {
width: bmpData.width,
height: bmpData.height,
channels: 4
},
failOn: 'none'
};
} }
);
const resizeOpts = { // Update entity image file name
withoutEnlargement: true, const model = models[collection.model];
fit: 'inside' if (!model) throw new Error('No matching model found');
};
await fs.mkdir(dstDir, {recursive: true}); const entity = await model.findById(entityId);
await sharp(imgSrc, sharpOptions) if (entity) {
.resize(collection.maxWidth, collection.maxHeight, resizeOpts) await entity.updateAttribute(
.png() collection.property,
.toFile(dstFile); fileName
);
}
const sizes = collection.sizes(); // Resize
for (let size of sizes) { const container = await models.ImageContainer.container(
const dstDir = path.join(collectionDir, `${size.width}x${size.height}`); collectionName
const dstFile = path.join(dstDir, fileName); );
const resizeOpts = { const rootPath = container.client.root;
withoutEnlargement: true, const collectionDir = path.join(rootPath, collectionName);
fit: size.crop ? 'cover' : 'inside'
};
await fs.mkdir(dstDir, {recursive: true}); // To max size
await sharp(imgSrc, sharpOptions) const {maxWidth, maxHeight} = collection;
.resize(size.width, size.height, resizeOpts) const fullSizePath = path.join(collectionDir, 'full');
.png() const toFullSizePath = `${fullSizePath}/${fileName}`;
.toFile(dstFile);
}
const model = models[collection.model]; await fs.mkdir(fullSizePath, {recursive: true});
await new Promise((resolve, reject) => {
gm(srcFile)
.resize(maxWidth, maxHeight, '>')
.setFormat('png')
.quality(100)
.write(toFullSizePath, function(err) {
if (err) reject(err);
if (!err) resolve();
});
});
if (!model) // To collection sizes
throw new Error('Matching model not found'); for (const size of collection.sizes()) {
const {width, height} = size;
const item = await model.findById(entityId, null, myOptions); const sizePath = path.join(collectionDir, `${width}x${height}`);
if (item) { const toSizePath = `${sizePath}/${fileName}`;
await item.updateAttribute(
collection.property,
fileName,
myOptions
);
}
if (fs.existsSync(srcFilePath)) await fs.mkdir(sizePath, {recursive: true});
await fs.unlink(srcFilePath); await new Promise((resolve, reject) => {
const gmInstance = gm(srcFile);
await tx.commit(); if (size.crop) {
gmInstance
.resize(width, height, '^')
.gravity('Center')
.crop(width, height);
}
return newImage; if (!size.crop) gmInstance.resize(width, height, '>');
} catch (e) {
await tx.rollback(); gmInstance
throw e; .setFormat('png')
.quality(100)
.write(toSizePath, function(err) {
if (err) reject(err);
if (!err) resolve();
});
});
} }
}; };
}; };

View File

@ -31,7 +31,7 @@
}, },
"author": { "author": {
"type": "belongsTo", "type": "belongsTo",
"model": "Account", "model": "VnUser",
"foreignKey": "authorFk" "foreignKey": "authorFk"
} }
} }

View File

@ -29,7 +29,7 @@
}, },
"user": { "user": {
"type": "belongsTo", "type": "belongsTo",
"model": "Account", "model": "VnUser",
"foreignKey": "userFk" "foreignKey": "userFk"
} }
} }

View File

@ -1,8 +1,8 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('loopback model Company', () => { describe('loopback model Company', () => {
it('should check that the company FTH doesnt exists', async() => { it('should check that the company FTH doesnt exists', async() => {
let result = await app.models.Company.findOne({where: {code: 'FTH'}}); let result = await models.Company.findOne({where: {code: 'FTH'}});
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });

View File

@ -1,6 +1,6 @@
const app = require('vn-loopback/server/server'); const {models} = require('vn-loopback/server/server');
describe('Dms', () => { describe('Dms', () => {
const Dms = app.models.Dms; const Dms = models.Dms;
describe('getFile()', () => { describe('getFile()', () => {
it('should return a response with text content-type', async() => { it('should return a response with text content-type', async() => {
@ -23,7 +23,7 @@ describe('Dms', () => {
it('should return an error for a record does not exists', async() => { it('should return an error for a record does not exists', async() => {
let error = {}; let error = {};
try { try {
await app.models.Dms.getFile('NotExistentId'); await models.Dms.getFile('NotExistentId');
} catch (e) { } catch (e) {
error = e; error = e;
} }

View File

@ -1,7 +1,7 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('account recoverPassword()', () => { describe('VnUser recoverPassword()', () => {
const userId = 1107; const userId = 1107;
const activeCtx = { const activeCtx = {
@ -21,9 +21,9 @@ describe('account recoverPassword()', () => {
it('should send email with token', async() => { it('should send email with token', async() => {
const userId = 1107; const userId = 1107;
const user = await models.Account.findById(userId); const user = await models.VnUser.findById(userId);
await models.Account.recoverPassword(user.email); await models.VnUser.recoverPassword(user.email);
const result = await models.AccessToken.findOne({where: {userId: userId}}); const result = await models.AccessToken.findOne({where: {userId: userId}});

View File

@ -1,14 +1,14 @@
const models = require('vn-loopback/server/server').models; const models = require('vn-loopback/server/server').models;
describe('loopback model Account', () => { describe('loopback model VnUser', () => {
it('should return true if the user has the given role', async() => { it('should return true if the user has the given role', async() => {
let result = await models.Account.hasRole(1, 'employee'); let result = await models.VnUser.hasRole(1, 'employee');
expect(result).toBeTruthy(); expect(result).toBeTruthy();
}); });
it('should return false if the user doesnt have the given role', async() => { it('should return false if the user doesnt have the given role', async() => {
let result = await models.Account.hasRole(1, 'administrator'); let result = await models.VnUser.hasRole(1, 'administrator');
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });

View File

@ -26,7 +26,7 @@
"relations": { "relations": {
"user": { "user": {
"type": "belongsTo", "type": "belongsTo",
"model": "Account", "model": "VnUser",
"foreignKey": "userFk" "foreignKey": "userFk"
} }
} }

View File

@ -39,9 +39,9 @@
"model": "Company", "model": "Company",
"foreignKey": "companyFk" "foreignKey": "companyFk"
}, },
"account": { "VnUser": {
"type": "belongsTo", "type": "belongsTo",
"model": "Account", "model": "VnUser",
"foreignKey": "userFk" "foreignKey": "userFk"
} }
} }

View File

@ -1,27 +0,0 @@
const LoopBackContext = require('loopback-context');
const {Email} = require('vn-print');
module.exports = function(Self) {
Self.on('resetPasswordRequest', async function(info) {
const loopBackContext = LoopBackContext.getCurrentContext();
const httpCtx = {req: loopBackContext.active};
const httpRequest = httpCtx.req.http.req;
const headers = httpRequest.headers;
const origin = headers.origin;
const user = await Self.app.models.Account.findById(info.user.id);
const params = {
recipient: info.email,
lang: user.lang,
url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}`
};
const options = Object.assign({}, info.options);
for (const param in options)
params[param] = options[param];
const email = new Email(options.emailTemplate, params);
return email.send();
});
};

View File

@ -1,19 +0,0 @@
{
"name": "user",
"base": "User",
"options": {
"mysql": {
"table": "salix.User"
}
},
"properties": {
"id": {
"id": true,
"type": "number",
"forceId": false
},
"username":{
"type": "string"
}
}
}

110
back/models/vn-user.js Normal file
View File

@ -0,0 +1,110 @@
const vnModel = require('vn-loopback/common/models/vn-model');
const LoopBackContext = require('loopback-context');
const {Email} = require('vn-print');
module.exports = function(Self) {
vnModel(Self);
require('../methods/vn-user/signIn')(Self);
require('../methods/vn-user/acl')(Self);
require('../methods/vn-user/recover-password')(Self);
require('../methods/vn-user/validate-token')(Self);
require('../methods/vn-user/privileges')(Self);
// Validations
Self.validatesFormatOf('email', {
message: 'Invalid email',
allowNull: true,
allowBlank: true,
with: /^[\w|.|-]+@[\w|-]+(\.[\w|-]+)*(,[\w|.|-]+@[\w|-]+(\.[\w|-]+)*)*$/
});
Self.validatesUniquenessOf('name', {
message: `A client with that Web User name already exists`
});
Self.remoteMethod('getCurrentUserData', {
description: 'Gets the current user data',
accepts: [
{
arg: 'ctx',
type: 'Object',
http: {source: 'context'}
}
],
returns: {
type: 'Object',
root: true
},
http: {
verb: 'GET',
path: '/getCurrentUserData'
}
});
Self.getCurrentUserData = async function(ctx) {
let userId = ctx.req.accessToken.userId;
return await Self.findById(userId, {
fields: ['id', 'name', 'nickname']
});
};
/**
* Checks if user has a role.
*
* @param {Integer} userId The user id
* @param {String} name The role name
* @param {Object} options Options
* @return {Boolean} %true if user has the role, %false otherwise
*/
Self.hasRole = async function(userId, name, options) {
const roles = await Self.getRoles(userId, options);
return roles.some(role => role == name);
};
/**
* Get all user roles.
*
* @param {Integer} userId The user id
* @param {Object} options Options
* @return {Object} User role list
*/
Self.getRoles = async(userId, options) => {
const result = await Self.rawSql(
`SELECT r.name
FROM account.user u
JOIN account.roleRole rr ON rr.role = u.role
JOIN account.role r ON r.id = rr.inheritsFrom
WHERE u.id = ?`, [userId], options);
const roles = [];
for (const role of result)
roles.push(role.name);
return roles;
};
Self.on('resetPasswordRequest', async function(info) {
const loopBackContext = LoopBackContext.getCurrentContext();
const httpCtx = {req: loopBackContext.active};
const httpRequest = httpCtx.req.http.req;
const headers = httpRequest.headers;
const origin = headers.origin;
const user = await Self.app.models.VnUser.findById(info.user.id);
const params = {
recipient: info.email,
lang: user.lang,
url: `${origin}/#!/reset-password?access_token=${info.accessToken.id}`
};
const options = Object.assign({}, info.options);
for (const param in options)
params[param] = options[param];
const email = new Email(options.emailTemplate, params);
return email.send();
});
};

View File

@ -1,11 +1,13 @@
{ {
"name": "Account", "name": "VnUser",
"base": "VnModel", "base": "User",
"validateUpsert": true,
"options": { "options": {
"mysql": { "mysql": {
"table": "account.user" "table": "account.user"
} }
}, },
"resetPasswordTokenTTL": "604800",
"properties": { "properties": {
"id": { "id": {
"type": "number", "type": "number",
@ -15,6 +17,19 @@
"type": "string", "type": "string",
"required": true "required": true
}, },
"username": {
"type": "string",
"mysql": {
"columnName": "name"
}
},
"password": {
"type": "string",
"required": true,
"mysql": {
"columnName": "bcryptPassword"
}
},
"roleFk": { "roleFk": {
"type": "number", "type": "number",
"mysql": { "mysql": {
@ -27,10 +42,6 @@
"lang": { "lang": {
"type": "string" "type": "string"
}, },
"password": {
"type": "string",
"required": true
},
"bcryptPassword": { "bcryptPassword": {
"type": "string" "type": "string"
}, },
@ -40,9 +51,6 @@
"email": { "email": {
"type": "string" "type": "string"
}, },
"emailVerified": {
"type": "boolean"
},
"created": { "created": {
"type": "date" "type": "date"
}, },
@ -86,7 +94,7 @@
}, },
"acls": [ "acls": [
{ {
"property": "login", "property": "signIn",
"accessType": "EXECUTE", "accessType": "EXECUTE",
"principalType": "ROLE", "principalType": "ROLE",
"principalId": "$everyone", "principalId": "$everyone",
@ -100,13 +108,6 @@
"permission": "ALLOW" "permission": "ALLOW"
}, },
{ {
"property": "logout",
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$authenticated",
"permission": "ALLOW"
},
{
"property": "validateToken", "property": "validateToken",
"accessType": "EXECUTE", "accessType": "EXECUTE",
"principalType": "ROLE", "principalType": "ROLE",

View File

@ -0,0 +1,18 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES ('VnUser', '*', '*', 'ALLOW', 'ROLE', 'employee');
INSERT INTO `salix`.`ACL` (id, model, property, accessType, permission, principalType, principalId)
VALUES ('VnUser', 'acl', 'READ', 'ALLOW', 'ROLE', 'account');
INSERT INTO `salix`.`ACL` (id, model, property, accessType, permission, principalType, principalId)
VALUES ('VnUser', 'getCurrentUserData', 'READ', 'ALLOW', 'ROLE', 'account');
INSERT INTO `salix`.`ACL` (id, model, property, accessType, permission, principalType, principalId)
VALUES ('VnUser', 'changePassword', '*', 'ALLOW', 'ROLE', 'account');
UPDATE `hedera`.`imageCollection` t
SET t.model = 'VnUser'
WHERE t.id = 6;

View File

@ -0,0 +1 @@
ALTER TABLE `vn`.`ticketConfig` ADD daysForWarningClaim INT DEFAULT 2 NOT NULL COMMENT 'dias restantes hasta que salte el aviso de reclamación fuerade plazo';

View File

@ -0,0 +1,16 @@
ALTER TABLE `vn`.`chat` ADD statusNew enum('pending','sent','error','sending') DEFAULT 'pending' NOT NULL;
UPDATE `vn`.`chat`
SET statusNew = 'pending'
WHERE status = 0;
UPDATE `vn`.`chat`
SET statusNew = 'sent'
WHERE status = 1;
UPDATE `vn`.`chat`
SET statusNew = 'error'
WHERE status = 2;
ALTER TABLE `vn`.`chat` CHANGE status status__ tinyint(1) DEFAULT NULL NULL;
ALTER TABLE `vn`.`chat` CHANGE statusNew status enum('pending','sent','error','sending') CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT 'pending' NOT NULL;

View File

@ -0,0 +1,4 @@
ALTER TABLE `vn`.`invoiceInConfig` ADD daysAgo INT UNSIGNED DEFAULT 45 COMMENT 'Días en el pasado para mostrar facturas en invoiceIn series en salix';
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('InvoiceIn', 'getSerial', 'READ', 'ALLOW', 'ROLE', 'administrative');

View File

@ -0,0 +1,14 @@
ALTER TABLE `vn`.`itemType` ADD isFragile tinyint(1) NULL;
ALTER TABLE `vn`.`itemType` MODIFY COLUMN isFragile tinyint(1) DEFAULT 0 NOT NULL;
UPDATE `vn`.`itemType`
SET isFragile = 1
WHERE code IN ('ZKA', 'ZKE');
UPDATE `vn`.`itemType`
SET isFragile = 1
WHERE id IN (SELECT it.id
FROM `vn`.`itemCategory` ic
JOIN `vn`.`itemType` it ON it.categoryFk = ic.id
WHERE ic.code = 'plant');

View File

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

View File

@ -0,0 +1,3 @@
DROP TRIGGER `vn`.`supplierAccount_afterInsert`;
DROP TRIGGER `vn`.`supplierAccount_afterUpdate`;
DROP TRIGGER `vn`.`supplierAccount_afterDelete`;

View File

@ -0,0 +1,47 @@
DROP PROCEDURE IF EXISTS `vn`.`ticket_getWarnings`;
DELIMITER $$
$$
CREATE PROCEDURE `vn`.`ticket_getWarnings`()
BEGIN
/**
* Calcula las adventencias para un conjunto de tickets.
* Agrupados por ticket
*
* @table tmp.sale_getWarnings(ticketFk) Identificadores de los tickets a calcular
* @return tmp.ticket_warnings
*/
DROP TEMPORARY TABLE IF EXISTS tmp.sale_warnings;
CREATE TEMPORARY TABLE tmp.sale_warnings (
ticketFk INT(11),
saleFk INT(11),
isFragile INTEGER(1) DEFAULT 0,
PRIMARY KEY (ticketFk, saleFk)
) ENGINE = MEMORY;
-- Frágil
INSERT INTO tmp.sale_warnings(ticketFk, saleFk, isFragile)
SELECT tt.ticketFk, s.id, TRUE
FROM tmp.sale_getWarnings tt
LEFT JOIN sale s ON s.ticketFk = tt.ticketFk
LEFT JOIN item i ON i.id = s.itemFk
LEFT JOIN itemType it ON it.id = i.typeFk
LEFT JOIN agencyMode am ON am.id = tt.agencyModeFk
LEFT JOIN deliveryMethod dm ON dm.id = am.deliveryMethodFk
WHERE dm.code IN ('AGENCY')
AND it.isFragile;
DROP TEMPORARY TABLE IF EXISTS tmp.ticket_warnings;
CREATE TEMPORARY TABLE tmp.ticket_warnings
(PRIMARY KEY (ticketFk))
ENGINE = MEMORY
SELECT
sw.ticketFk,
MAX(sw.isFragile) AS isFragile
FROM tmp.sale_warnings sw
GROUP BY sw.ticketFk;
DROP TEMPORARY TABLE
tmp.sale_warnings;
END$$
DELIMITER ;

View File

@ -0,0 +1,72 @@
CREATE TABLE `vn`.`wagonType` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL UNIQUE,
`divisible` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
CREATE TABLE `vn`.`wagonTypeColor` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL UNIQUE,
`rgb` varchar(30) NOT NULL UNIQUE,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
CREATE TABLE `vn`.`wagonTypeTray` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`typeFk` int(11) unsigned,
`height` int(11) unsigned NOT NULL,
`colorFk` int(11) unsigned,
PRIMARY KEY (`id`),
UNIQUE KEY (`typeFk`,`height`),
CONSTRAINT `wagonTypeTray_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE,
CONSTRAINT `wagonTypeTray_color` FOREIGN KEY (`colorFk`) REFERENCES `wagonTypeColor` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
CREATE TABLE `vn`.`wagonConfig` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`width` int(11) unsigned DEFAULT 1350,
`height` int(11) unsigned DEFAULT 1900,
`maxWagonHeight` int(11) unsigned DEFAULT 200,
`minHeightBetweenTrays` int(11) unsigned DEFAULT 50,
`maxTrays` int(11) unsigned DEFAULT 6,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3;
CREATE TABLE `vn`.`collectionWagon` (
`collectionFk` int(11) NOT NULL,
`wagonFk` int(11) NOT NULL,
`position` int(11) unsigned,
PRIMARY KEY (`collectionFk`,`position`),
UNIQUE KEY `collectionWagon_unique` (`collectionFk`,`wagonFk`),
CONSTRAINT `collectionWagon_collection` FOREIGN KEY (`collectionFk`) REFERENCES `collection` (`id`) ON UPDATE CASCADE,
CONSTRAINT `collectionWagon_wagon` FOREIGN KEY (`wagonFk`) REFERENCES `wagon` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
CREATE TABLE `vn`.`collectionWagonTicket` (
`ticketFk` int(11) NOT NULL,
`wagonFk` int(11) NOT NULL,
`trayFk` int(11) unsigned NOT NULL,
`side` SET('L', 'R') NULL,
PRIMARY KEY (`ticketFk`),
CONSTRAINT `collectionWagonTicket_ticket` FOREIGN KEY (`ticketFk`) REFERENCES `ticket` (`id`) ON UPDATE CASCADE,
CONSTRAINT `collectionWagonTicket_wagon` FOREIGN KEY (`wagonFk`) REFERENCES `wagon` (`id`) ON UPDATE CASCADE,
CONSTRAINT `collectionWagonTicket_tray` FOREIGN KEY (`trayFk`) REFERENCES `wagonTypeTray` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
ALTER TABLE `vn`.`wagon` ADD `typeFk` int(11) unsigned NOT NULL;
ALTER TABLE `vn`.`wagon` ADD `label` int(11) unsigned NOT NULL;
ALTER TABLE `vn`.`wagon` ADD CONSTRAINT `wagon_type` FOREIGN KEY (`typeFk`) REFERENCES `wagonType` (`id`) ON UPDATE CASCADE;
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('WagonType', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonTypeColor', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonTypeTray', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonConfig', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('CollectionWagon', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('CollectionWagonTicket', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('Wagon', '*', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonType', 'createWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonType', 'deleteWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi'),
('WagonType', 'editWagonType', '*', 'ALLOW', 'ROLE', 'productionAssi');

View File

@ -0,0 +1,74 @@
DROP TABLE `vn`.`dmsRecover`;
ALTER TABLE `vn`.`delivery` DROP FOREIGN KEY delivery_FK;
ALTER TABLE `vn`.`delivery` DROP COLUMN addressFk;
ALTER TABLE `vn`.`delivery` ADD ticketFk INT NOT NULL;
ALTER TABLE `vn`.`delivery` ADD CONSTRAINT delivery_ticketFk_FK FOREIGN KEY (`ticketFk`) REFERENCES `vn`.`ticket`(`id`);
DELETE FROM `salix`.`ACL` WHERE `property` = 'saveSign';
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
VALUES
('Ticket','saveSign','WRITE','ALLOW','employee');
DROP PROCEDURE IF EXISTS vn.route_getTickets;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`route_getTickets`(vRouteFk INT)
BEGIN
/**
* Pasado un RouteFk devuelve la información
* de sus tickets.
*
* @param vRouteFk
*
* @select Información de los tickets
*/
SELECT
t.id Id,
t.clientFk Client,
a.id Address,
t.packages Packages,
a.street AddressName,
a.postalCode PostalCode,
a.city City,
sub2.itemPackingTypeFk PackingType,
c.phone ClientPhone,
c.mobile ClientMobile,
a.phone AddressPhone,
a.mobile AddressMobile,
d.longitude Longitude,
d.latitude Latitude,
wm.mediaValue SalePersonPhone,
tob.Note Note,
t.isSigned Signed
FROM ticket t
JOIN client c ON t.clientFk = c.id
JOIN address a ON t.addressFk = a.id
LEFT JOIN delivery d ON t.id = d.ticketFk
LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk
LEFT JOIN
(SELECT tob.description Note, t.id
FROM ticketObservation tob
JOIN ticket t ON tob.ticketFk = t.id
JOIN observationType ot ON ot.id = tob.observationTypeFk
WHERE t.routeFk = vRouteFk
AND ot.code = 'delivery'
)tob ON tob.id = t.id
LEFT JOIN
(SELECT sub.ticketFk,
CONCAT('(', GROUP_CONCAT(DISTINCT sub.itemPackingTypeFk ORDER BY sub.items DESC SEPARATOR ','), ') ') itemPackingTypeFk
FROM (SELECT s.ticketFk , i.itemPackingTypeFk, COUNT(*) items
FROM ticket t
JOIN sale s ON s.ticketFk = t.id
JOIN item i ON i.id = s.itemFk
WHERE t.routeFk = vRouteFk
GROUP BY t.id,i.itemPackingTypeFk)sub
GROUP BY sub.ticketFk
) sub2 ON sub2.ticketFk = t.id
WHERE t.routeFk = vRouteFk
GROUP BY t.id
ORDER BY t.priority;
END$$
DELIMITER ;

View File

@ -0,0 +1,67 @@
DELETE FROM `salix`.`ACL` WHERE `property` = 'saveSign';
INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalId`)
VALUES
('Ticket','saveSign','WRITE','ALLOW','employee');
DROP PROCEDURE IF EXISTS vn.route_getTickets;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`route_getTickets`(vRouteFk INT)
BEGIN
/**
* Pasado un RouteFk devuelve la información
* de sus tickets.
*
* @param vRouteFk
*
* @select Información de los tickets
*/
SELECT
t.id Id,
t.clientFk Client,
a.id Address,
t.packages Packages,
a.street AddressName,
a.postalCode PostalCode,
a.city City,
sub2.itemPackingTypeFk PackingType,
c.phone ClientPhone,
c.mobile ClientMobile,
a.phone AddressPhone,
a.mobile AddressMobile,
d.longitude Longitude,
d.latitude Latitude,
wm.mediaValue SalePersonPhone,
tob.Note Note,
t.isSigned Signed
FROM ticket t
JOIN client c ON t.clientFk = c.id
JOIN address a ON t.addressFk = a.id
LEFT JOIN delivery d ON t.id = d.ticketFk
LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk
LEFT JOIN
(SELECT tob.description Note, t.id
FROM ticketObservation tob
JOIN ticket t ON tob.ticketFk = t.id
JOIN observationType ot ON ot.id = tob.observationTypeFk
WHERE t.routeFk = vRouteFk
AND ot.code = 'delivery'
)tob ON tob.id = t.id
LEFT JOIN
(SELECT sub.ticketFk,
CONCAT('(', GROUP_CONCAT(DISTINCT sub.itemPackingTypeFk ORDER BY sub.items DESC SEPARATOR ','), ') ') itemPackingTypeFk
FROM (SELECT s.ticketFk , i.itemPackingTypeFk, COUNT(*) items
FROM ticket t
JOIN sale s ON s.ticketFk = t.id
JOIN item i ON i.id = s.itemFk
WHERE t.routeFk = vRouteFk
GROUP BY t.id,i.itemPackingTypeFk)sub
GROUP BY sub.ticketFk
) sub2 ON sub2.ticketFk = t.id
WHERE t.routeFk = vRouteFk
GROUP BY t.id
ORDER BY t.priority;
END$$
DELIMITER ;

View File

@ -0,0 +1,83 @@
CREATE TABLE `vn`.`dmsRecover` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ticketFk` int(11) DEFAULT NULL,
`sign` text DEFAULT NULL,
`created` timestamp NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
KEY `ticketFk_idx` (`ticketFk`),
CONSTRAINT `ticketFk` FOREIGN KEY (`ticketFk`) REFERENCES `ticket` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=31917 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
ALTER TABLE `vn`.`delivery` ADD addressFk INT;
DROP PROCEDURE IF EXISTS `vn`.`route_getTickets`;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`route_getTickets`(vRouteFk INT)
BEGIN
/**
* Pasado un RouteFk devuelve la información
* de sus tickets.
*
* @param vRouteFk
* @select Información de los tickets
*/
SELECT *
FROM (
SELECT t.id Id,
t.clientFk Client,
a.id Address,
a.nickname ClientName,
t.packages Packages,
a.street AddressName,
a.postalCode PostalCode,
a.city City,
sub2.itemPackingTypeFk PackingType,
c.phone ClientPhone,
c.mobile ClientMobile,
a.phone AddressPhone,
a.mobile AddressMobile,
d.longitude Longitude,
d.latitude Latitude,
wm.mediaValue SalePersonPhone,
tob.description Note,
t.isSigned Signed,
t.priority
FROM ticket t
JOIN client c ON t.clientFk = c.id
JOIN address a ON t.addressFk = a.id
LEFT JOIN delivery d ON d.addressFk = a.id
LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk
LEFT JOIN(
SELECT tob.description, t.id
FROM ticketObservation tob
JOIN ticket t ON tob.ticketFk = t.id
JOIN observationType ot ON ot.id = tob.observationTypeFk
WHERE t.routeFk = vRouteFk
AND ot.code = 'delivery'
)tob ON tob.id = t.id
LEFT JOIN(
SELECT sub.ticketFk,
CONCAT('(',
GROUP_CONCAT(DISTINCT sub.itemPackingTypeFk
ORDER BY sub.items DESC SEPARATOR ','),
') ') itemPackingTypeFk
FROM (
SELECT s.ticketFk, i.itemPackingTypeFk, COUNT(*) items
FROM ticket t
JOIN sale s ON s.ticketFk = t.id
JOIN item i ON i.id = s.itemFk
WHERE t.routeFk = vRouteFk
GROUP BY t.id, i.itemPackingTypeFk
)sub
GROUP BY sub.ticketFk
)sub2 ON sub2.ticketFk = t.id
WHERE t.routeFk = vRouteFk
ORDER BY d.id DESC
LIMIT 10000000000000000000
)sub3
GROUP BY sub3.id
ORDER BY sub3.priority;
END$$
DELIMITER ;

View File

@ -0,0 +1 @@
DROP TRIGGER IF EXISTS `vn`.`claimBeginning_afterInsert`;

View File

@ -0,0 +1,72 @@
DROP TRIGGER IF EXISTS `vn`.`client_beforeUpdate`;
USE `vn`;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` TRIGGER `vn`.`client_beforeUpdate`
BEFORE UPDATE ON `client`
FOR EACH ROW
BEGIN
DECLARE vText VARCHAR(255) DEFAULT NULL;
DECLARE vPayMethodFk INT;
-- Comprueba que el formato de los teléfonos es válido
IF !(NEW.phone <=> OLD.phone) AND (NEW.phone <> '') THEN
CALL pbx.phone_isValid(NEW.phone);
END IF;
IF !(NEW.mobile <=> OLD.mobile) AND (NEW.mobile <> '')THEN
CALL pbx.phone_isValid(NEW.mobile);
END IF;
SELECT id INTO vPayMethodFk
FROM vn.payMethod
WHERE code = 'bankDraft';
IF NEW.payMethodFk = vPayMethodFk AND NEW.dueDay = 0 THEN
SET NEW.dueDay = 5;
END IF;
-- Avisar al comercial si ha llegado la documentación sepa/core
IF NEW.hasSepaVnl AND !OLD.hasSepaVnl THEN
SET vText = 'Sepa de VNL';
END IF;
IF NEW.hasCoreVnl AND !OLD.hasCoreVnl THEN
SET vText = 'Core de VNL';
END IF;
IF vText IS NOT NULL
THEN
INSERT INTO mail(receiver, replyTo, `subject`, body)
SELECT
CONCAT(IF(ac.id,u.name, 'jgallego'), '@verdnatura.es'),
'administracion@verdnatura.es',
CONCAT('Cliente ', NEW.id),
CONCAT('Recibida la documentación: ', vText)
FROM worker w
LEFT JOIN account.user u ON w.userFk = u.id AND u.active
LEFT JOIN account.account ac ON ac.id = u.id
WHERE w.id = NEW.salesPersonFk;
END IF;
IF NEW.salespersonFk IS NULL AND OLD.salespersonFk IS NOT NULL THEN
IF (SELECT COUNT(clientFk)
FROM clientProtected
WHERE clientFk = NEW.id
) > 0 THEN
CALL util.throw("HAS_CLIENT_PROTECTED");
END IF;
END IF;
IF !(NEW.salesPersonFk <=> OLD.salesPersonFk) THEN
SET NEW.lastSalesPersonFk = IFNULL(NEW.salesPersonFk, OLD.salesPersonFk);
END IF;
IF !(NEW.businessTypeFk <=> OLD.businessTypeFk) AND (NEW.businessTypeFk = 'individual' OR OLD.businessTypeFk = 'individual') THEN
SET NEW.isTaxDataChecked = 0;
END IF;
END$$
DELIMITER ;

View File

@ -0,0 +1,70 @@
DROP TABLE IF EXISTS `vn`.`dmsRecover`;
ALTER TABLE `vn`.`delivery` DROP COLUMN addressFk;
ALTER TABLE `vn`.`delivery` DROP CONSTRAINT delivery_ticketFk_FK;
ALTER TABLE `vn`.`delivery` DROP COLUMN ticketFk;
ALTER TABLE `vn`.`delivery` ADD ticketFk INT DEFAULT NULL;
ALTER TABLE `vn`.`delivery` ADD CONSTRAINT delivery_ticketFk_FK FOREIGN KEY (`ticketFk`) REFERENCES `vn`.`ticket`(`id`);
DROP PROCEDURE IF EXISTS vn.route_getTickets;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` PROCEDURE `vn`.`route_getTickets`(vRouteFk INT)
BEGIN
/**
* Pasado un RouteFk devuelve la información
* de sus tickets.
*
* @param vRouteFk
*
* @select Información de los tickets
*/
SELECT
t.id Id,
t.clientFk Client,
a.id Address,
t.packages Packages,
a.street AddressName,
a.postalCode PostalCode,
a.city City,
sub2.itemPackingTypeFk PackingType,
c.phone ClientPhone,
c.mobile ClientMobile,
a.phone AddressPhone,
a.mobile AddressMobile,
d.longitude Longitude,
d.latitude Latitude,
wm.mediaValue SalePersonPhone,
tob.Note Note,
t.isSigned Signed
FROM ticket t
JOIN client c ON t.clientFk = c.id
JOIN address a ON t.addressFk = a.id
LEFT JOIN delivery d ON t.id = d.ticketFk
LEFT JOIN workerMedia wm ON wm.workerFk = c.salesPersonFk
LEFT JOIN
(SELECT tob.description Note, t.id
FROM ticketObservation tob
JOIN ticket t ON tob.ticketFk = t.id
JOIN observationType ot ON ot.id = tob.observationTypeFk
WHERE t.routeFk = vRouteFk
AND ot.code = 'delivery'
)tob ON tob.id = t.id
LEFT JOIN
(SELECT sub.ticketFk,
CONCAT('(', GROUP_CONCAT(DISTINCT sub.itemPackingTypeFk ORDER BY sub.items DESC SEPARATOR ','), ') ') itemPackingTypeFk
FROM (SELECT s.ticketFk , i.itemPackingTypeFk, COUNT(*) items
FROM ticket t
JOIN sale s ON s.ticketFk = t.id
JOIN item i ON i.id = s.itemFk
WHERE t.routeFk = vRouteFk
GROUP BY t.id,i.itemPackingTypeFk)sub
GROUP BY sub.ticketFk
) sub2 ON sub2.ticketFk = t.id
WHERE t.routeFk = vRouteFk
GROUP BY t.id
ORDER BY t.priority;
END$$
DELIMITER ;

View File

@ -0,0 +1,13 @@
DROP TRIGGER IF EXISTS `vn`.`invoiceOut_afterInsert`;
USE vn;
DELIMITER $$
$$
CREATE DEFINER=`root`@`localhost` TRIGGER `vn`.`invoiceOut_afterInsert`
AFTER INSERT ON `invoiceOut`
FOR EACH ROW
BEGIN
CALL clientRisk_update(NEW.clientFk, NEW.companyFk, NEW.amount);
END$$
DELIMITER ;

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('InvoiceIn', 'negativeBases', 'READ', 'ALLOW', 'ROLE', 'administrative'),
('InvoiceIn', 'negativeBasesCsv', 'READ', 'ALLOW', 'ROLE', 'administrative');

View File

@ -0,0 +1,14 @@
CREATE TABLE `vn`.`workerObservation` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`workerFk` int(10) unsigned DEFAULT NULL,
`userFk` int(10) unsigned DEFAULT NULL,
`text` text COLLATE utf8mb3_unicode_ci NOT NULL,
`created` timestamp NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`),
CONSTRAINT `workerFk_workerObservation_FK` FOREIGN KEY (`workerFk`) REFERENCES `vn`.`worker` (`id`) ON UPDATE CASCADE,
CONSTRAINT `userFk_workerObservation_FK` FOREIGN KEY (`userFk`) REFERENCES `account`.`user`(`id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='Todas las observaciones referentes a un trabajador';
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('WorkerObservation', '*', '*', 'ALLOW', 'ROLE', 'hr');

View File

@ -0,0 +1,21 @@
create or replace definer = root@localhost view User as
select `account`.`user`.`id` AS `id`,
`account`.`user`.`realm` AS `realm`,
`account`.`user`.`name` AS `name`,
`account`.`user`.`nickname` AS `nickname`,
`account`.`user`.`bcryptPassword` AS `password`,
`account`.`user`.`role` AS `role`,
`account`.`user`.`active` AS `active`,
`account`.`user`.`email` AS `email`,
`account`.`user`.`emailVerified` AS `emailVerified`,
`account`.`user`.`verificationToken` AS `verificationToken`,
`account`.`user`.`lang` AS `lang`,
`account`.`user`.`lastPassChange` AS `lastPassChange`,
`account`.`user`.`created` AS `created`,
`account`.`user`.`updated` AS `updated`,
`account`.`user`.`image` AS `image`,
`account`.`user`.`recoverPass` AS `recoverPass`,
`account`.`user`.`sync` AS `sync`,
`account`.`user`.`hasGrant` AS `hasGrant`
from `account`.`user`;

View File

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

View File

@ -0,0 +1,21 @@
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('VnUser', '*', '*', 'ALLOW', 'ROLE', 'employee'),
('VnUser','acl','READ','ALLOW','ROLE','account'),
('VnUser','getCurrentUserData','READ','ALLOW','ROLE','account'),
('VnUser','changePassword', 'WRITE', 'ALLOW', 'ROLE', 'account'),
('Account','exists','READ','ALLOW','ROLE','account');
INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
VALUES
('Account','exists','READ','ALLOW','ROLE','account');
DELETE FROM `salix`.`ACL` WHERE (model, property) = ('Account', 'acl');
DELETE FROM `salix`.`ACL` WHERE (model, property) = ('Account', 'getCurrentUserData');
DELETE FROM `salix`.`ACL` WHERE (model, property) = ('Account', 'changePassword');
DELETE FROM `salix`.`ACL` WHERE model = 'UserAccount';
UPDATE `hedera`.`imageCollection` t
SET t.model = 'VnUser'
WHERE t.id = 6;

View File

@ -0,0 +1,4 @@
INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
VALUES
('UserLog', '*', 'READ', 'ALLOW', 'ROLE', 'employee'),
('RoleLog', '*', 'READ', 'ALLOW', 'ROLE', 'employee');

View File

@ -98,20 +98,20 @@ INSERT INTO `hedera`.`tpvConfig`(`id`, `currency`, `terminal`, `transactionType`
VALUES VALUES
(1, 978, 1, 0, 2000, 9, 0); (1, 978, 1, 0, 2000, 9, 0);
INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `password`,`role`,`active`,`email`,`lang`, `image`) INSERT INTO `account`.`user`(`id`,`name`,`nickname`, `bcryptPassword`, `password`,`role`,`active`,`email`,`lang`, `image`)
VALUES VALUES
(1101, 'BruceWayne', 'Bruce Wayne', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'), (1101, 'BruceWayne', 'Bruce Wayne', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'BruceWayne@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
(1102, 'PetterParker', 'Petter Parker', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), (1102, 'PetterParker', 'Petter Parker', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'PetterParker@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(1103, 'ClarkKent', 'Clark Kent', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr', 'e7723f0b24ff05b32ed09d95196f2f29'), (1103, 'ClarkKent', 'Clark Kent', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'ClarkKent@mydomain.com', 'fr', 'e7723f0b24ff05b32ed09d95196f2f29'),
(1104, 'TonyStark', 'Tony Stark', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'), (1104, 'TonyStark', 'Tony Stark', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'TonyStark@mydomain.com', 'es', 'e7723f0b24ff05b32ed09d95196f2f29'),
(1105, 'MaxEisenhardt', 'Max Eisenhardt', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt', 'e7723f0b24ff05b32ed09d95196f2f29'), (1105, 'MaxEisenhardt', 'Max Eisenhardt', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 1, 'MaxEisenhardt@mydomain.com', 'pt', 'e7723f0b24ff05b32ed09d95196f2f29'),
(1106, 'DavidCharlesHaller', 'David Charles Haller', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), (1106, 'DavidCharlesHaller', 'David Charles Haller', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'DavidCharlesHaller@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(1107, 'HankPym', 'Hank Pym', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), (1107, 'HankPym', 'Hank Pym', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'HankPym@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(1108, 'CharlesXavier', 'Charles Xavier', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), (1108, 'CharlesXavier', 'Charles Xavier', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'CharlesXavier@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(1109, 'BruceBanner', 'Bruce Banner', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'), (1109, 'BruceBanner', 'Bruce Banner', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'BruceBanner@mydomain.com', 'en', 'e7723f0b24ff05b32ed09d95196f2f29'),
(1110, 'JessicaJones', 'Jessica Jones', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en', NULL), (1110, 'JessicaJones', 'Jessica Jones', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 1, 1, 'JessicaJones@mydomain.com', 'en', NULL),
(1111, 'Missing', 'Missing', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL), (1111, 'Missing', 'Missing', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL),
(1112, 'Trash', 'Trash', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL); (1112, 'Trash', 'Trash', '$2b$10$UzQHth.9UUQ1T5aiQJ21lOU0oVlbxoqH4PFM9V8T90KNSAcg0eEL2', 'ac754a330530832ba1bf7687f577da91', 2, 0, NULL, 'en', NULL);
INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`) INSERT INTO `account`.`mailAlias`(`id`, `alias`, `description`, `isPublic`)
VALUES VALUES
@ -299,7 +299,8 @@ INSERT INTO `vn`.`payMethod`(`id`,`code`, `name`, `graceDays`, `outstandingDebt
INSERT INTO `vn`.`payDem`(`id`, `payDem`) INSERT INTO `vn`.`payDem`(`id`, `payDem`)
VALUES VALUES
(1, 10), (1, 10),
(2, 20); (2, 20),
(7, 0);
INSERT INTO `vn`.`autonomy`(`id`, `name`, `countryFk`) INSERT INTO `vn`.`autonomy`(`id`, `name`, `countryFk`)
VALUES VALUES
@ -500,7 +501,8 @@ INSERT INTO `vn`.`observationType`(`id`,`description`, `code`)
(3, 'Delivery', 'delivery'), (3, 'Delivery', 'delivery'),
(4, 'SalesPerson', 'salesPerson'), (4, 'SalesPerson', 'salesPerson'),
(5, 'Administrative', 'administrative'), (5, 'Administrative', 'administrative'),
(6, 'Weight', 'weight'); (6, 'Weight', 'weight'),
(7, 'InvoiceOut', 'invoiceOut');
INSERT INTO `vn`.`addressObservation`(`id`,`addressFk`,`observationTypeFk`,`description`) INSERT INTO `vn`.`addressObservation`(`id`,`addressFk`,`observationTypeFk`,`description`)
VALUES VALUES
@ -737,7 +739,9 @@ INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `des
(9, 23, 5, 'care with the dog'), (9, 23, 5, 'care with the dog'),
(10, 23, 4, 'Reclama ticket: 8'), (10, 23, 4, 'Reclama ticket: 8'),
(11, 24, 4, 'Reclama ticket: 7'), (11, 24, 4, 'Reclama ticket: 7'),
(12, 11, 3, 'Delivery after 10am'); (12, 11, 3, 'Delivery after 10am'),
(13, 1, 7, 'observation of ticket one'),
(14, 2, 7, 'observation of ticket two');
-- FIX for state hours on local, inter_afterInsert -- FIX for state hours on local, inter_afterInsert
-- UPDATE vncontrol.inter SET odbc_date = DATE_ADD(util.VN_CURDATE(), INTERVAL -10 SECOND); -- UPDATE vncontrol.inter SET odbc_date = DATE_ADD(util.VN_CURDATE(), INTERVAL -10 SECOND);
@ -837,14 +841,14 @@ INSERT INTO `vn`.`temperature`(`code`, `name`, `description`)
('warm', 'Warm', 'Warm'), ('warm', 'Warm', 'Warm'),
('cool', 'Cool', 'Cool'); ('cool', 'Cool', 'Cool');
INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `life`, `workerFk`, `isPackaging`, `temperatureFk`) INSERT INTO `vn`.`itemType`(`id`, `code`, `name`, `categoryFk`, `life`, `workerFk`, `isPackaging`, `temperatureFk`, `isFragile`)
VALUES VALUES
(1, 'CRI', 'Crisantemo', 2, 31, 35, 0, 'cool'), (1, 'CRI', 'Crisantemo', 2, 31, 35, 0, 'cool', 0),
(2, 'ITG', 'Anthurium', 1, 31, 35, 0, 'cool'), (2, 'ITG', 'Anthurium', 1, 31, 35, 0, 'cool', 1),
(3, 'WPN', 'Paniculata', 2, 31, 35, 0, 'cool'), (3, 'WPN', 'Paniculata', 2, 31, 35, 0, 'cool', 0),
(4, 'PRT', 'Delivery ports', 3, NULL, 35, 1, 'warm'), (4, 'PRT', 'Delivery ports', 3, NULL, 35, 1, 'warm', 0),
(5, 'CON', 'Container', 3, NULL, 35, 1, 'warm'), (5, 'CON', 'Container', 3, NULL, 35, 1, 'warm', 0),
(6, 'ALS', 'Alstroemeria', 1, 31, 16, 0, 'warm'); (6, 'ALS', 'Alstroemeria', 1, 31, 16, 0, 'warm', 1);
INSERT INTO `vn`.`ink`(`id`, `name`, `picture`, `showOrder`, `hex`) INSERT INTO `vn`.`ink`(`id`, `name`, `picture`, `showOrder`, `hex`)
VALUES VALUES
@ -1770,12 +1774,12 @@ INSERT INTO `vn`.`claimState`(`id`, `code`, `description`, `roleFk`, `priority`,
( 6, 'mana', 'Mana', 72, 4, 0), ( 6, 'mana', 'Mana', 72, 4, 0),
( 7, 'lack', 'Faltas', 72, 2, 0); ( 7, 'lack', 'Faltas', 72, 2, 0);
INSERT INTO `vn`.`claim`(`id`, `ticketCreated`, `claimStateFk`, `clientFk`, `workerFk`, `responsibility`, `isChargedToMana`, `created`, `packages`, `rma`) INSERT INTO `vn`.`claim`(`id`, `ticketCreated`, `claimStateFk`, `clientFk`, `workerFk`, `responsibility`, `isChargedToMana`, `created`, `packages`, `rma`, `ticketFk`)
VALUES VALUES
(1, util.VN_CURDATE(), 1, 1101, 18, 3, 0, util.VN_CURDATE(), 0, '02676A049183'), (1, util.VN_CURDATE(), 1, 1101, 18, 3, 0, util.VN_CURDATE(), 0, '02676A049183', 11),
(2, util.VN_CURDATE(), 2, 1101, 18, 3, 0, util.VN_CURDATE(), 1, NULL), (2, util.VN_CURDATE(), 2, 1101, 18, 3, 0, util.VN_CURDATE(), 1, NULL, 16),
(3, util.VN_CURDATE(), 3, 1101, 18, 1, 1, util.VN_CURDATE(), 5, NULL), (3, util.VN_CURDATE(), 3, 1101, 18, 1, 1, util.VN_CURDATE(), 5, NULL, 7),
(4, util.VN_CURDATE(), 3, 1104, 18, 5, 0, util.VN_CURDATE(), 10, NULL); (4, util.VN_CURDATE(), 3, 1104, 18, 5, 0, util.VN_CURDATE(), 10, NULL, 8);
INSERT INTO `vn`.`claimObservation` (`claimFk`, `workerFk`, `text`, `created`) INSERT INTO `vn`.`claimObservation` (`claimFk`, `workerFk`, `text`, `created`)
VALUES VALUES
@ -2486,9 +2490,9 @@ REPLACE INTO `vn`.`invoiceIn`(`id`, `serialNumber`,`serial`, `supplierFk`, `issu
(9, 1009, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1242, 1, 442, 1), (9, 1009, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1242, 1, 442, 1),
(10, 1010, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1243, 1, 442, 1); (10, 1010, 'R', 2, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH), 1243, 1, 442, 1);
INSERT INTO `vn`.`invoiceInConfig` (`id`, `retentionRate`, `retentionName`, `sageWithholdingFk`) INSERT INTO `vn`.`invoiceInConfig` (`id`, `retentionRate`, `retentionName`, `sageWithholdingFk`, `daysAgo`)
VALUES VALUES
(1, -2, '2% retention', 2); (1, -2, '2% retention', 2, 45);
INSERT INTO `vn`.`invoiceInDueDay`(`invoiceInFk`, `dueDated`, `bankFk`, `amount`) INSERT INTO `vn`.`invoiceInDueDay`(`invoiceInFk`, `dueDated`, `bankFk`, `amount`)
VALUES VALUES
@ -2630,8 +2634,8 @@ INSERT INTO `vn`.`supplierAgencyTerm` (`agencyFk`, `supplierFk`, `minimumPackage
INSERT INTO `vn`.`chat` (`senderFk`, `recipient`, `dated`, `checkUserStatus`, `message`, `status`, `attempts`) INSERT INTO `vn`.`chat` (`senderFk`, `recipient`, `dated`, `checkUserStatus`, `message`, `status`, `attempts`)
VALUES VALUES
(1101, '@PetterParker', util.VN_CURDATE(), 1, 'First test message', 0, 0), (1101, '@PetterParker', util.VN_CURDATE(), 1, 'First test message', 0, 'sent'),
(1101, '@PetterParker', util.VN_CURDATE(), 0, 'Second test message', 0, 0); (1101, '@PetterParker', util.VN_CURDATE(), 0, 'Second test message', 0, 'pending');
INSERT INTO `vn`.`mobileAppVersionControl` (`appName`, `version`, `isVersionCritical`) INSERT INTO `vn`.`mobileAppVersionControl` (`appName`, `version`, `isVersionCritical`)
@ -2836,4 +2840,27 @@ INSERT INTO `vn`.`workerTimeControlMail` (`id`, `workerFk`, `year`, `week`, `sta
(3, 9, 2000, 51, 'CONFIRMED', util.VN_NOW(), 1, NULL), (3, 9, 2000, 51, 'CONFIRMED', util.VN_NOW(), 1, NULL),
(4, 9, 2001, 1, 'SENDED', util.VN_NOW(), 1, NULL); (4, 9, 2001, 1, 'SENDED', util.VN_NOW(), 1, NULL);
INSERT INTO `vn`.`wagonConfig` (`id`, `width`, `height`, `maxWagonHeight`, `minHeightBetweenTrays`, `maxTrays`)
VALUES
(1, 1350, 1900, 200, 50, 6);
INSERT INTO `vn`.`wagonTypeColor` (`id`, `name`, `rgb`)
VALUES
(1, 'white', '#ffffff'),
(2, 'red', '#ff0000'),
(3, 'green', '#00ff00'),
(4, 'blue', '#0000ff');
INSERT INTO `vn`.`wagonType` (`id`, `name`, `divisible`)
VALUES
(1, 'Wagon Type #1', 1);
INSERT INTO `vn`.`wagonTypeTray` (`id`, `typeFk`, `height`, `colorFk`)
VALUES
(1, 1, 100, 1),
(2, 1, 50, 2),
(3, 1, 0, 3);

View File

@ -414,7 +414,7 @@ export default {
saveFieldsButton: '.vn-popover.shown vn-button[label="Save"] > button' saveFieldsButton: '.vn-popover.shown vn-button[label="Save"] > button'
}, },
itemFixedPrice: { itemFixedPrice: {
add: 'vn-fixed-price vn-icon-button[icon="add_circle"]', add: 'vn-fixed-price vn-icon-button[vn-tooltip="Add fixed price"]',
firstItemID: 'vn-fixed-price tr:nth-child(2) vn-autocomplete[ng-model="price.itemFk"]', firstItemID: 'vn-fixed-price tr:nth-child(2) vn-autocomplete[ng-model="price.itemFk"]',
fourthFixedPrice: 'vn-fixed-price tr:nth-child(5)', fourthFixedPrice: 'vn-fixed-price tr:nth-child(5)',
fourthItemID: 'vn-fixed-price tr:nth-child(5) vn-autocomplete[ng-model="price.itemFk"]', fourthItemID: 'vn-fixed-price tr:nth-child(5) vn-autocomplete[ng-model="price.itemFk"]',
@ -426,7 +426,19 @@ export default {
fourthStarted: 'vn-fixed-price tr:nth-child(5) vn-date-picker[ng-model="price.started"]', fourthStarted: 'vn-fixed-price tr:nth-child(5) vn-date-picker[ng-model="price.started"]',
fourthEnded: 'vn-fixed-price tr:nth-child(5) vn-date-picker[ng-model="price.ended"]', fourthEnded: 'vn-fixed-price tr:nth-child(5) vn-date-picker[ng-model="price.ended"]',
fourthDeleteIcon: 'vn-fixed-price tr:nth-child(5) > td:nth-child(9) > vn-icon-button[icon="delete"]', fourthDeleteIcon: 'vn-fixed-price tr:nth-child(5) > td:nth-child(9) > vn-icon-button[icon="delete"]',
orderColumnId: 'vn-fixed-price th[field="itemFk"]' orderColumnId: 'vn-fixed-price th[field="itemFk"]',
removeWarehouseFilter: 'vn-searchbar > form > vn-textfield > div.container > div.prepend > prepend > div > span:nth-child(1) > vn-icon > i',
generalSearchFilter: 'vn-fixed-price-search-panel vn-textfield[ng-model="$ctrl.filter.search"]',
reignFilter: 'vn-fixed-price-search-panel vn-horizontal.item-category vn-one',
typeFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="$ctrl.filter.typeFk"]',
buyerFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="$ctrl.filter.buyerFk"]',
warehouseFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="$ctrl.filter.warehouseFk"]',
mineFilter: 'vn-fixed-price-search-panel vn-check[ng-model="$ctrl.filter.mine"]',
hasMinPriceFilter: 'vn-fixed-price-search-panel vn-check[ng-model="$ctrl.filter.hasMinPrice"]',
addTag: 'vn-fixed-price-search-panel vn-icon-button[icon="add_circle"]',
tagFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="itemTag.tagFk"]',
tagValueFilter: 'vn-fixed-price-search-panel vn-autocomplete[ng-model="itemTag.value"]',
chip: 'vn-fixed-price-search-panel vn-chip > vn-icon'
}, },
itemCreateView: { itemCreateView: {
temporalName: 'vn-item-create vn-textfield[ng-model="$ctrl.item.provisionalName"]', temporalName: 'vn-item-create vn-textfield[ng-model="$ctrl.item.provisionalName"]',
@ -524,7 +536,6 @@ export default {
}, },
itemLog: { itemLog: {
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr', 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(4) td.after',
}, },
ticketSummary: { ticketSummary: {
header: 'vn-ticket-summary > vn-card > h5', header: 'vn-ticket-summary > vn-card > h5',
@ -988,6 +999,12 @@ export default {
locker: 'vn-worker-basic-data vn-input-number[ng-model="$ctrl.worker.locker"]', locker: 'vn-worker-basic-data vn-input-number[ng-model="$ctrl.worker.locker"]',
saveButton: 'vn-worker-basic-data button[type=submit]' saveButton: 'vn-worker-basic-data button[type=submit]'
}, },
workerNotes: {
addNoteFloatButton: 'vn-worker-note vn-icon[icon="add"]',
note: 'vn-note-worker-create vn-textarea[ng-model="$ctrl.note.text"]',
saveButton: 'vn-note-worker-create button[type=submit]',
firstNoteText: 'vn-worker-note .text'
},
workerPbx: { workerPbx: {
extension: 'vn-worker-pbx vn-textfield[ng-model="$ctrl.worker.sip.extension"]', extension: 'vn-worker-pbx vn-textfield[ng-model="$ctrl.worker.sip.extension"]',
saveButton: 'vn-worker-pbx button[type=submit]' saveButton: 'vn-worker-pbx button[type=submit]'
@ -1040,7 +1057,6 @@ export default {
boss: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bossFk"]', boss: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.bossFk"]',
role: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.roleFk"]', role: 'vn-worker-create vn-autocomplete[ng-model="$ctrl.worker.roleFk"]',
iban: 'vn-worker-create vn-textfield[ng-model="$ctrl.worker.iban"]', 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"]', createButton: 'vn-worker-create vn-submit[label="Create"]',
}, },
workerPda: { workerPda: {
@ -1129,6 +1145,15 @@ export default {
saveButton: 'vn-invoice-in-tax vn-submit', saveButton: 'vn-invoice-in-tax vn-submit',
}, },
invoiceInIndex: {
topbarSearchParams: 'vn-searchbar div.search-params > span',
},
invoiceInSerial: {
daysAgo: 'vn-invoice-in-serial-search-panel vn-input-number[ng-model="$ctrl.filter.daysAgo"]',
serial: 'vn-invoice-in-serial-search-panel vn-textfield[ng-model="$ctrl.filter.serial"]',
chip: 'vn-chip > vn-icon',
goToIndex: 'vn-invoice-in-serial vn-icon-button[icon="icon-invoice-in"]',
},
travelIndex: { travelIndex: {
anySearchResult: 'vn-travel-index vn-tbody > a', anySearchResult: 'vn-travel-index vn-tbody > a',
firstSearchResult: 'vn-travel-index vn-tbody > a:nth-child(1)', firstSearchResult: 'vn-travel-index vn-tbody > a:nth-child(1)',
@ -1140,7 +1165,16 @@ export default {
landingDate: 'vn-travel-create vn-date-picker[ng-model="$ctrl.travel.landed"]', landingDate: 'vn-travel-create vn-date-picker[ng-model="$ctrl.travel.landed"]',
warehouseOut: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseOutFk"]', warehouseOut: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseOutFk"]',
warehouseIn: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseInFk"]', warehouseIn: 'vn-travel-create vn-autocomplete[ng-model="$ctrl.travel.warehouseInFk"]',
save: 'vn-travel-create vn-submit > button' save: 'vn-travel-create vn-submit > button',
generalSearchFilter: 'vn-travel-search-panel vn-textfield[ng-model="$ctrl.search"]',
agencyFilter: 'vn-travel-search-panel vn-autocomplete[ng-model="$ctrl.filter.agencyModeFk"]',
warehouseOutFilter: 'vn-travel-search-panel vn-autocomplete[ng-model="$ctrl.filter.warehouseOutFk"]',
warehouseInFilter: 'vn-travel-search-panel vn-autocomplete[ng-model="$ctrl.filter.warehouseInFk"]',
scopeDaysFilter: 'vn-travel-search-panel vn-input-number[ng-model="$ctrl.filter.scopeDays"]',
continentFilter: 'vn-travel-search-panel vn-autocomplete[ng-model="$ctrl.filter.continent"]',
totalEntriesFilter: 'vn-travel-search-panel vn-input-number[ng-model="$ctrl.totalEntries"]',
chip: 'vn-travel-search-panel vn-chip > vn-icon',
}, },
travelExtraCommunity: { travelExtraCommunity: {
anySearchResult: 'vn-travel-extra-community > vn-card div > tbody > tr[ng-attr-id="{{::travel.id}}"]', anySearchResult: 'vn-travel-extra-community > vn-card div > tbody > tr[ng-attr-id="{{::travel.id}}"]',

View File

@ -79,21 +79,15 @@ describe('SmartTable SearchBar integration', () => {
it('should order by first id', async() => { it('should order by first id', async() => {
await page.loginAndModule('developer', 'item'); await page.loginAndModule('developer', 'item');
await page.accessToSection('item.fixedPrice'); await page.accessToSection('item.fixedPrice');
await page.doSearch(); await page.keyboard.press('Enter');
const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value'); const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value');
expect(result).toEqual('1'); expect(result).toEqual('1');
}); });
it('should order by last id', async() => { it('should order by last id, reload page and have same order', async() => {
await page.waitToClick(selectors.itemFixedPrice.orderColumnId); await page.waitToClick(selectors.itemFixedPrice.orderColumnId);
const result = await page.waitToGetProperty(selectors.itemFixedPrice.firstItemID, 'value');
expect(result).toEqual('13');
});
it('should reload page and have same order', async() => {
await page.reload({ await page.reload({
waitUntil: 'networkidle2' waitUntil: 'networkidle2'
}); });

View File

@ -28,22 +28,4 @@ describe('Client log path', () => {
it('should navigate to the log section', async() => { it('should navigate to the log section', async() => {
await page.accessToSection('client.card.log'); await page.accessToSection('client.card.log');
}); });
it('should check the previous value of the last logged change', async() => {
let lastModificationPreviousValue = await page
.waitToGetProperty(selectors.clientLog.lastModificationPreviousValue, 'innerText');
expect(lastModificationPreviousValue).toContain('DavidCharlesHaller');
});
it('should check the current value of the last logged change', async() => {
let lastModificationPreviousValue = await page
.waitToGetProperty(selectors.clientLog.lastModificationPreviousValue, 'innerText');
let lastModificationCurrentValue = await page.
waitToGetProperty(selectors.clientLog.lastModificationCurrentValue, 'innerText');
expect(lastModificationPreviousValue).toEqual('DavidCharlesHaller');
expect(lastModificationCurrentValue).toEqual('this is a test');
});
}); });

View File

@ -50,7 +50,7 @@ describe('Client defaulter path', () => {
expect(message.text).toContain(`The message can't be empty`); expect(message.text).toContain(`The message can't be empty`);
}); });
it('shoul checked all defaulters', async() => { it('should checked all defaulters', async() => {
await page.loginAndModule('insurance', 'client'); await page.loginAndModule('insurance', 'client');
await page.accessToSection('client.defaulter'); await page.accessToSection('client.defaulter');

View File

@ -27,7 +27,7 @@ describe('Worker time control path', () => {
date.setMonth(date.getMonth() + 1); date.setMonth(date.getMonth() + 1);
let month = date.toLocaleString('default', {month: 'long'}); let month = date.toLocaleString('default', {month: 'long'});
await page.click(selectors.workerTimeControl.nextMonthButton); await page.waitToClick(selectors.workerTimeControl.nextMonthButton);
let result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText'); let result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
expect(result).toContain(month); expect(result).toContain(month);
@ -36,7 +36,7 @@ describe('Worker time control path', () => {
date.setDate(1); date.setDate(1);
month = date.toLocaleString('default', {month: 'long'}); month = date.toLocaleString('default', {month: 'long'});
await page.click(selectors.workerTimeControl.previousMonthButton); await page.waitToClick(selectors.workerTimeControl.previousMonthButton);
result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText'); result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');
expect(result).toContain(month); expect(result).toContain(month);
@ -49,7 +49,7 @@ describe('Worker time control path', () => {
await page.loginAndModule('salesBoss', 'worker'); await page.loginAndModule('salesBoss', 'worker');
await page.goto(`http://localhost:5000/#!/worker/${hankPymId}/time-control?timestamp=${timestamp}`); await page.goto(`http://localhost:5000/#!/worker/${hankPymId}/time-control?timestamp=${timestamp}`);
await page.click(selectors.workerTimeControl.secondWeekDay); await page.waitToClick(selectors.workerTimeControl.secondWeekDay);
result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText'); result = await page.getProperty(selectors.workerTimeControl.monthName, 'innerText');

View File

@ -26,7 +26,6 @@ describe('Worker create path', () => {
await page.write(selectors.workerCreate.street, 'S/ Doomstadt'); await page.write(selectors.workerCreate.street, 'S/ Doomstadt');
await page.write(selectors.workerCreate.email, 'doctorDoom@marvel.com'); await page.write(selectors.workerCreate.email, 'doctorDoom@marvel.com');
await page.write(selectors.workerCreate.iban, 'ES9121000418450200051332'); await page.write(selectors.workerCreate.iban, 'ES9121000418450200051332');
await page.autocompleteSearch(selectors.workerCreate.switft, 'BBKKESMMMMM');
// should check for autocompleted worker code and worker user name // should check for autocompleted worker code and worker user name
const workerCode = await page const workerCode = await page

View File

@ -0,0 +1,42 @@
import selectors from '../../helpers/selectors';
import getBrowser from '../../helpers/puppeteer';
describe('Worker Add notes path', () => {
let browser;
let page;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('hr', 'worker');
await page.accessToSearchResult('Bruce Banner');
await page.accessToSection('worker.card.note.index');
});
afterAll(async() => {
await browser.close();
});
it(`should reach the notes index`, async() => {
await page.waitForState('worker.card.note.index');
});
it(`should click on the add note button`, async() => {
await page.waitToClick(selectors.workerNotes.addNoteFloatButton);
await page.waitForState('worker.card.note.create');
});
it(`should create a note`, async() => {
await page.waitForSelector(selectors.workerNotes.note);
await page.type(`${selectors.workerNotes.note} textarea`, 'Meeting with Black Widow 21st 9am');
await page.waitToClick(selectors.workerNotes.saveButton);
const message = await page.waitForSnackbar();
expect(message.text).toContain('Data saved!');
});
it('should confirm the note was created', async() => {
const result = await page.waitToGetProperty(selectors.workerNotes.firstNoteText, 'innerText');
expect(result).toEqual('Meeting with Black Widow 21st 9am');
});
});

View File

@ -42,23 +42,4 @@ describe('Item log path', () => {
await page.waitForSelector(selectors.itemsIndex.createItemButton); await page.waitForSelector(selectors.itemsIndex.createItemButton);
await page.waitForState('item.index'); await page.waitForState('item.index');
}); });
it(`should search for the created item and navigate to it's log section`, async() => {
await page.accessToSearchResult('Knowledge artifact');
await page.accessToSection('item.card.log');
});
it(`should confirm the log is showing 5 entries`, async() => {
await page.waitForSelector(selectors.itemLog.anyLineCreated);
const anyLineCreatedCount = await page.countElement(selectors.itemLog.anyLineCreated);
expect(anyLineCreatedCount).toEqual(5);
});
it(`should confirm the log is showing the intrastat for the created item`, async() => {
const fifthLineCreatedProperty = await page
.waitToGetProperty(selectors.itemLog.fifthLineCreatedProperty, 'innerText');
expect(fifthLineCreatedProperty).toEqual('05080000');
});
}); });

View File

@ -4,19 +4,69 @@ import getBrowser from '../../helpers/puppeteer';
describe('Item fixed prices path', () => { describe('Item fixed prices path', () => {
let browser; let browser;
let page; let page;
let httpRequest;
beforeAll(async() => { beforeAll(async() => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('buyer', 'item'); await page.loginAndModule('buyer', 'item');
await page.accessToSection('item.fixedPrice'); await page.accessToSection('item.fixedPrice');
page.on('request', req => {
if (req.url().includes(`FixedPrices/filter`))
httpRequest = req.url();
});
}); });
afterAll(async() => { afterAll(async() => {
await browser.close(); await browser.close();
}); });
it('should click on the add new foxed price button', async() => { it('should filter using all the fields', async() => {
await page.doSearch(); await page.write(selectors.itemFixedPrice.generalSearchFilter, 'item');
await page.keyboard.press('Enter');
expect(httpRequest).toContain('search=item');
await page.click(selectors.itemFixedPrice.chip);
await page.click(selectors.itemFixedPrice.reignFilter);
expect(httpRequest).toContain('categoryFk');
await page.autocompleteSearch(selectors.itemFixedPrice.typeFilter, 'Alstroemeria');
expect(httpRequest).toContain('typeFk');
await page.click(selectors.itemFixedPrice.chip);
await page.autocompleteSearch(selectors.itemFixedPrice.buyerFilter, 'buyerNick');
expect(httpRequest).toContain('buyerFk');
await page.click(selectors.itemFixedPrice.chip);
await page.autocompleteSearch(selectors.itemFixedPrice.warehouseFilter, 'Algemesi');
expect(httpRequest).toContain('warehouseFk');
await page.click(selectors.itemFixedPrice.chip);
await page.click(selectors.itemFixedPrice.mineFilter);
expect(httpRequest).toContain('mine=true');
await page.click(selectors.itemFixedPrice.chip);
await page.click(selectors.itemFixedPrice.hasMinPriceFilter);
expect(httpRequest).toContain('hasMinPrice=true');
await page.click(selectors.itemFixedPrice.chip);
await page.click(selectors.itemFixedPrice.addTag);
await page.autocompleteSearch(selectors.itemFixedPrice.tagFilter, 'Color');
await page.autocompleteSearch(selectors.itemFixedPrice.tagValueFilter, 'Brown');
expect(httpRequest).toContain('tags');
await page.click(selectors.itemFixedPrice.chip);
});
it('should click on the add new fixed price button', async() => {
await page.waitToClick(selectors.itemFixedPrice.add); await page.waitToClick(selectors.itemFixedPrice.add);
await page.waitForSelector(selectors.itemFixedPrice.fourthFixedPrice); await page.waitForSelector(selectors.itemFixedPrice.fourthFixedPrice);
}); });
@ -35,9 +85,7 @@ describe('Item fixed prices path', () => {
}); });
it('should reload the section and check the created price has the expected ID', async() => { it('should reload the section and check the created price has the expected ID', async() => {
await page.accessToSection('item.index'); await page.goto(`http://localhost:5000/#!/item/fixed-price`);
await page.accessToSection('item.fixedPrice');
await page.doSearch();
const result = await page.waitToGetProperty(selectors.itemFixedPrice.fourthItemID, 'value'); const result = await page.waitToGetProperty(selectors.itemFixedPrice.fourthItemID, 'value');

View File

@ -197,6 +197,7 @@ describe('Ticket Edit sale path', () => {
}); });
it('should check in the history that logs has been added', async() => { it('should check in the history that logs has been added', async() => {
pending('https://redmine.verdnatura.es/issues/5455');
await page.reload({waitUntil: ['networkidle0', 'domcontentloaded']}); await page.reload({waitUntil: ['networkidle0', 'domcontentloaded']});
await page.waitToClick(selectors.ticketSales.firstSaleHistoryButton); await page.waitToClick(selectors.ticketSales.firstSaleHistoryButton);
await page.waitForSelector(selectors.ticketSales.firstSaleHistory); await page.waitForSelector(selectors.ticketSales.firstSaleHistory);
@ -248,6 +249,7 @@ describe('Ticket Edit sale path', () => {
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox); await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu); await page.waitToClick(selectors.ticketSales.moreMenu);
await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim); await page.waitToClick(selectors.ticketSales.moreMenuCreateClaim);
await page.waitToClick(selectors.globalItems.acceptButton);
await page.waitForState('claim.card.basicData'); await page.waitForState('claim.card.basicData');
}); });

View File

@ -9,7 +9,7 @@ describe('Ticket Create notes path', () => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('employee', 'ticket'); await page.loginAndModule('employee', 'ticket');
await page.accessToSearchResult('1'); await page.accessToSearchResult('5');
await page.accessToSection('ticket.card.observation'); await page.accessToSection('ticket.card.observation');
}); });

View File

@ -29,20 +29,4 @@ describe('Ticket expeditions and log path', () => {
expect(result).toEqual(3); expect(result).toEqual(3);
}); });
it(`should confirm the expedition deleted is shown now in the ticket log`, async() => {
await page.accessToSection('ticket.card.log');
const user = await page
.waitToGetProperty(selectors.ticketLog.user, 'innerText');
const action = await page
.waitToGetProperty(selectors.ticketLog.action, 'innerText');
const id = await page
.waitToGetProperty(selectors.ticketLog.id, 'innerText');
expect(user).toContain('production');
expect(action).toContain('Deletes');
expect(id).toEqual('2');
});
}); });

View File

@ -31,30 +31,4 @@ describe('Ticket log path', () => {
expect(message.text).toContain('Data saved!'); expect(message.text).toContain('Data saved!');
}); });
it('should navigate to the log section', async() => {
await page.accessToSection('ticket.card.log');
});
it('should set the viewport width to 1920 to see the table full width', async() => {
await page.setViewport({
width: 1920,
height: 0,
});
const result = await page.waitToGetProperty(selectors.ticketLog.firstTD, 'innerText');
expect(result.length).not.toBeGreaterThan('20');
});
it('should set the viewport width to 800 to see the table shrink and move data to the 1st column', async() => {
await page.setViewport({
width: 800,
height: 0,
});
const result = await page.waitToGetProperty(selectors.ticketLog.firstTD, 'innerText');
expect(result.length).toBeGreaterThan('15');
});
}); });

View File

@ -54,7 +54,7 @@ describe('Ticket Future path', () => {
it('should search with the origin IPT', async() => { it('should search with the origin IPT', async() => {
await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton); await page.waitToClick(selectors.ticketFuture.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketFuture.ipt, 'Horizontal'); await page.autocompleteSearch(selectors.ticketFuture.ipt, 'H');
await page.waitToClick(selectors.ticketFuture.submit); await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('ipt=H'); expect(httpRequest).toContain('ipt=H');
@ -65,7 +65,7 @@ describe('Ticket Future path', () => {
await page.clearInput(selectors.ticketFuture.ipt); await page.clearInput(selectors.ticketFuture.ipt);
await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'Horizontal'); await page.autocompleteSearch(selectors.ticketFuture.futureIpt, 'H');
await page.waitToClick(selectors.ticketFuture.submit); await page.waitToClick(selectors.ticketFuture.submit);
expect(httpRequest).toContain('futureIpt=H'); expect(httpRequest).toContain('futureIpt=H');
@ -108,7 +108,7 @@ describe('Ticket Future path', () => {
it('should search in smart-table with an IPT Destination', async() => { it('should search in smart-table with an IPT Destination', async() => {
await page.waitToClick(selectors.ticketFuture.tableButtonSearch); await page.waitToClick(selectors.ticketFuture.tableButtonSearch);
await page.autocompleteSearch(selectors.ticketFuture.tableFutureIpt, 'Horizontal'); await page.autocompleteSearch(selectors.ticketFuture.tableFutureIpt, 'H');
expect(httpRequest).toContain('futureIpt'); expect(httpRequest).toContain('futureIpt');
await page.waitToClick(selectors.ticketFuture.tableButtonSearch); await page.waitToClick(selectors.ticketFuture.tableButtonSearch);

View File

@ -54,7 +54,7 @@ describe('Ticket Advance path', () => {
it('should search with the origin IPT', async() => { it('should search with the origin IPT', async() => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'Horizontal'); await page.autocompleteSearch(selectors.ticketAdvance.futureIpt, 'H');
await page.waitToClick(selectors.ticketAdvance.submit); await page.waitToClick(selectors.ticketAdvance.submit);
expect(httpRequest).toContain('futureIpt=H'); expect(httpRequest).toContain('futureIpt=H');
@ -66,7 +66,7 @@ describe('Ticket Advance path', () => {
it('should search with the destination IPT', async() => { it('should search with the destination IPT', async() => {
await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton); await page.waitToClick(selectors.ticketAdvance.openAdvancedSearchButton);
await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'Horizontal'); await page.autocompleteSearch(selectors.ticketAdvance.ipt, 'H');
await page.waitToClick(selectors.ticketAdvance.submit); await page.waitToClick(selectors.ticketAdvance.submit);
expect(httpRequest).toContain('ipt=H'); expect(httpRequest).toContain('ipt=H');
@ -78,7 +78,7 @@ describe('Ticket Advance path', () => {
it('should search in smart-table with an IPT Origin', async() => { it('should search in smart-table with an IPT Origin', async() => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.autocompleteSearch(selectors.ticketAdvance.tableFutureIpt, 'Vertical'); await page.autocompleteSearch(selectors.ticketAdvance.tableFutureIpt, 'V');
expect(httpRequest).toContain('futureIpt'); expect(httpRequest).toContain('futureIpt');
@ -89,7 +89,7 @@ describe('Ticket Advance path', () => {
it('should search in smart-table with an IPT Destination', async() => { it('should search in smart-table with an IPT Destination', async() => {
await page.waitToClick(selectors.ticketAdvance.tableButtonSearch); await page.waitToClick(selectors.ticketAdvance.tableButtonSearch);
await page.autocompleteSearch(selectors.ticketAdvance.tableIpt, 'Vertical'); await page.autocompleteSearch(selectors.ticketAdvance.tableIpt, 'V');
expect(httpRequest).toContain('ipt'); expect(httpRequest).toContain('ipt');

View File

@ -0,0 +1,29 @@
import getBrowser from '../../helpers/puppeteer';
describe('InvoiceIn negative bases path', () => {
let browser;
let page;
const httpRequests = [];
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
page.on('request', req => {
if (req.url().includes(`InvoiceIns/negativeBases`))
httpRequests.push(req.url());
});
await page.loginAndModule('administrative', 'invoiceIn');
await page.accessToSection('invoiceIn.negative-bases');
});
afterAll(async() => {
await browser.close();
});
it('should show negative bases in a date range', async() => {
const request = httpRequests.find(req =>
req.includes(`from`) && req.includes(`to`));
expect(request).toBeDefined();
});
});

View File

@ -0,0 +1,48 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('InvoiceIn serial path', () => {
let browser;
let page;
let httpRequest;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('administrative', 'invoiceIn');
await page.accessToSection('invoiceIn.serial');
page.on('request', req => {
if (req.url().includes(`InvoiceIns/getSerial`))
httpRequest = req.url();
});
});
afterAll(async() => {
await browser.close();
});
it('should check that passes the correct params to back', async() => {
await page.overwrite(selectors.invoiceInSerial.daysAgo, '30');
await page.keyboard.press('Enter');
expect(httpRequest).toContain('daysAgo=30');
await page.overwrite(selectors.invoiceInSerial.serial, 'R');
await page.keyboard.press('Enter');
expect(httpRequest).toContain('serial=R');
await page.click(selectors.invoiceInSerial.chip);
});
it('should go to index and check if the search-panel has the correct params', async() => {
await page.click(selectors.invoiceInSerial.goToIndex);
const params = await page.$$(selectors.invoiceInIndex.topbarSearchParams);
const serial = await params[0].getProperty('title');
const isBooked = await params[1].getProperty('title');
const from = await params[2].getProperty('title');
expect(await serial.jsonValue()).toContain('serial');
expect(await isBooked.jsonValue()).toContain('not isBooked');
expect(await from.jsonValue()).toContain('from');
});
});

View File

@ -9,7 +9,8 @@ describe('Travel basic data path', () => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('buyer', 'travel'); await page.loginAndModule('buyer', 'travel');
await page.accessToSearchResult('3'); await page.write(selectors.travelIndex.generalSearchFilter, '3');
await page.keyboard.press('Enter');
await page.accessToSection('travel.card.basicData'); await page.accessToSection('travel.card.basicData');
}); });
@ -89,11 +90,13 @@ describe('Travel basic data path', () => {
}); });
it('should navigate to the travel logs', async() => { it('should navigate to the travel logs', async() => {
pending('https://redmine.verdnatura.es/issues/5455');
await page.accessToSection('travel.card.log'); await page.accessToSection('travel.card.log');
await page.waitForState('travel.card.log'); await page.waitForState('travel.card.log');
}); });
it('should check the 1st log contains details from the changes made', async() => { it('should check the 1st log contains details from the changes made', async() => {
pending('https://redmine.verdnatura.es/issues/5455');
const result = await page.waitToGetProperty(selectors.travelLog.firstLogFirstTD, 'innerText'); const result = await page.waitToGetProperty(selectors.travelLog.firstLogFirstTD, 'innerText');
expect(result).toContain('new reference!'); expect(result).toContain('new reference!');

View File

@ -9,7 +9,8 @@ describe('Travel descriptor path', () => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('buyer', 'travel'); await page.loginAndModule('buyer', 'travel');
await page.accessToSearchResult('1'); await page.write(selectors.travelIndex.generalSearchFilter, '1');
await page.keyboard.press('Enter');
await page.waitForState('travel.card.summary'); await page.waitForState('travel.card.summary');
}); });
@ -81,7 +82,8 @@ describe('Travel descriptor path', () => {
await page.waitToClick('.cancel'); await page.waitToClick('.cancel');
await page.waitToClick(selectors.globalItems.homeButton); await page.waitToClick(selectors.globalItems.homeButton);
await page.selectModule('travel'); await page.selectModule('travel');
await page.accessToSearchResult('3'); await page.write(selectors.travelIndex.generalSearchFilter, '3');
await page.keyboard.press('Enter');
await page.waitForState('travel.card.summary'); await page.waitForState('travel.card.summary');
const state = await page.getState(); const state = await page.getState();

View File

@ -10,7 +10,8 @@ describe('Travel thermograph path', () => {
browser = await getBrowser(); browser = await getBrowser();
page = browser.page; page = browser.page;
await page.loginAndModule('buyer', 'travel'); await page.loginAndModule('buyer', 'travel');
await page.accessToSearchResult('3'); await page.write(selectors.travelIndex.generalSearchFilter, '3');
await page.keyboard.press('Enter');
await page.accessToSection('travel.card.thermograph.index'); await page.accessToSection('travel.card.thermograph.index');
}); });

View File

@ -0,0 +1,62 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
describe('Travel search panel path', () => {
let browser;
let page;
let httpRequest;
beforeAll(async() => {
browser = await getBrowser();
page = browser.page;
await page.loginAndModule('buyer', 'travel');
page.on('request', req => {
if (req.url().includes(`Travels/filter`))
httpRequest = req.url();
});
});
afterAll(async() => {
await browser.close();
});
it('should filter using all the fields', async() => {
await page.click(selectors.travelIndex.chip);
await page.write(selectors.travelIndex.generalSearchFilter, 'travel');
await page.keyboard.press('Enter');
expect(httpRequest).toContain('search=travel');
await page.click(selectors.travelIndex.chip);
await page.autocompleteSearch(selectors.travelIndex.agencyFilter, 'Entanglement');
expect(httpRequest).toContain('agencyModeFk');
await page.click(selectors.travelIndex.chip);
await page.autocompleteSearch(selectors.travelIndex.warehouseOutFilter, 'Warehouse One');
expect(httpRequest).toContain('warehouseOutFk');
await page.click(selectors.travelIndex.chip);
await page.autocompleteSearch(selectors.travelIndex.warehouseInFilter, 'Warehouse Two');
expect(httpRequest).toContain('warehouseInFk');
await page.click(selectors.travelIndex.chip);
await page.overwrite(selectors.travelIndex.scopeDaysFilter, '15');
await page.keyboard.press('Enter');
expect(httpRequest).toContain('scopeDays=15');
await page.click(selectors.travelIndex.chip);
await page.autocompleteSearch(selectors.travelIndex.continentFilter, 'Asia');
expect(httpRequest).toContain('continent');
await page.click(selectors.travelIndex.chip);
await page.write(selectors.travelIndex.totalEntriesFilter, '1');
await page.keyboard.press('Enter');
expect(httpRequest).toContain('totalEntries=1');
});
});

View File

@ -29,14 +29,4 @@ describe('Zone descriptor path', () => {
expect(count).toEqual(0); expect(count).toEqual(0);
}); });
it('should check the ticket whom lost the zone and see evidence on the logs', async() => {
await page.waitToClick(selectors.globalItems.homeButton);
await page.selectModule('ticket');
await page.accessToSearchResult('20');
await page.accessToSection('ticket.card.log');
const lastChanges = await page.waitToGetProperty(selectors.ticketLog.changes, 'innerText');
expect(lastChanges).toContain('1');
});
}); });

View File

@ -64,14 +64,4 @@ describe('Supplier basic data path', () => {
expect(result).toEqual('Some notes'); expect(result).toEqual('Some notes');
}); });
it('should navigate to the log section', async() => {
await page.accessToSection('supplier.card.log');
});
it('should check the changes have been recorded', async() => {
const result = await page.waitToGetProperty('vn-tr table tr:nth-child(3) td.after', 'innerText');
expect(result).toEqual('Some notes');
});
}); });

View File

@ -23,7 +23,7 @@ describe('Account ACL path', () => {
it('should create new acl', async() => { it('should create new acl', async() => {
await page.autocompleteSearch(selectors.accountAcl.role, 'sysadmin'); await page.autocompleteSearch(selectors.accountAcl.role, 'sysadmin');
await page.autocompleteSearch(selectors.accountAcl.model, 'UserAccount'); await page.autocompleteSearch(selectors.accountAcl.model, 'Account');
await page.autocompleteSearch(selectors.accountAcl.accessType, '*'); await page.autocompleteSearch(selectors.accountAcl.accessType, '*');
await page.autocompleteSearch(selectors.accountAcl.permission, 'ALLOW'); await page.autocompleteSearch(selectors.accountAcl.permission, 'ALLOW');
await page.waitToClick(selectors.accountAcl.save); await page.waitToClick(selectors.accountAcl.save);

View File

@ -114,12 +114,14 @@ export default class Calendar extends FormInput {
let day = date.getDate(); let day = date.getDate();
let wday = date.getDay(); let wday = date.getDay();
let month = date.getMonth(); let month = date.getMonth();
let year = date.getFullYear();
const currentDay = Date.vnNew().getDate(); const currentDay = Date.vnNew().getDate();
const currentMonth = Date.vnNew().getMonth(); const currentMonth = Date.vnNew().getMonth();
const currentYear = Date.vnNew().getFullYear();
let classes = { let classes = {
today: day === currentDay && month === currentMonth, today: day === currentDay && month === currentMonth && year === currentYear,
weekend: wday === 6 || wday === 0, weekend: wday === 6 || wday === 0,
previous: month < this.month, previous: month < this.month,
current: month == this.month, current: month == this.month,

View File

@ -7,7 +7,7 @@ describe('Directive acl', () => {
beforeEach(ngModule('vnCore')); beforeEach(ngModule('vnCore'));
beforeEach(inject(($httpBackend, aclService) => { beforeEach(inject(($httpBackend, aclService) => {
$httpBackend.whenGET('Accounts/acl') $httpBackend.whenGET('VnUsers/acl')
.respond({ .respond({
user: {id: 1, name: 'myUser'}, user: {id: 1, name: 'myUser'},
roles: [ roles: [

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