diff --git a/.vscode/settings.json b/.vscode/settings.json
index 05d23f3bb..9ed1c8fc2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -10,5 +10,8 @@
"eslint.format.enable": true,
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
- }
+ },
+ "cSpell.words": [
+ "salix"
+ ]
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d677b80e3..30afaa69b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,21 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [2342.01] - 2023-10-19
+## [2348.01] - 2023-11-30
### Added
### Changed
### Fixed
-## [2340.01] - 2023-10-05
+## [2346.01] - 2023-11-16
+
+### Added
+### Changed
+### Fixed
+
+
+## [2342.01] - 2023-11-02
### Added
- (Usuarios -> Foto) Se muestra la foto del trabajador
-
-### Changed
### Fixed
- (Usuarios -> Historial) Abre el descriptor del usuario correctamente
+
+## [2340.01] - 2023-10-05
+
## [2338.01] - 2023-09-21
### Added
@@ -29,17 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- (Trabajadores -> Calendario) Icono de check arreglado cuando pulsas un tipo de dia
-### Fixed
-
## [2336.01] - 2023-09-07
-### Added
-
-### Changed
-
-### Fixed
-
-
## [2334.01] - 2023-08-24
### Added
diff --git a/back/methods/chat/sendCheckingPresence.js b/back/methods/chat/sendCheckingPresence.js
index 274ec3a5b..85b66e94b 100644
--- a/back/methods/chat/sendCheckingPresence.js
+++ b/back/methods/chat/sendCheckingPresence.js
@@ -26,15 +26,14 @@ module.exports = Self => {
Self.sendCheckingPresence = async(ctx, recipientId, message) => {
if (!recipientId) return false;
-
const models = Self.app.models;
+
const userId = ctx.req.accessToken.userId;
const sender = await models.VnUser.findById(userId, {fields: ['id']});
const recipient = await models.VnUser.findById(recipientId, null);
// Prevent sending messages to yourself
if (recipientId == userId) return false;
-
if (!recipient)
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
diff --git a/back/methods/collection/getTickets.js b/back/methods/collection/getTickets.js
index 445fc070d..f04822697 100644
--- a/back/methods/collection/getTickets.js
+++ b/back/methods/collection/getTickets.js
@@ -26,7 +26,7 @@ module.exports = Self => {
Self.getTickets = async(ctx, id, print, options) => {
const userId = ctx.req.accessToken.userId;
- const origin = ctx.req.headers.origin;
+ const url = await Self.app.models.Url.getUrl();
const $t = ctx.req.__;
const myOptions = {};
@@ -36,7 +36,6 @@ module.exports = Self => {
myOptions.userId = userId;
const promises = [];
-
const [tickets] = await Self.rawSql(`CALL vn.collection_getTickets(?)`, [id], myOptions);
const sales = await Self.rawSql(`
SELECT s.ticketFk,
@@ -86,24 +85,19 @@ module.exports = Self => {
if (tickets && tickets.length) {
for (const ticket of tickets) {
const ticketId = ticket.ticketFk;
-
- // SEND ROCKET
if (ticket.observaciones != '') {
for (observation of ticket.observaciones.split(' ')) {
if (['#', '@'].includes(observation.charAt(0))) {
promises.push(Self.app.models.Chat.send(ctx, observation,
$t('The ticket is in preparation', {
ticketId: ticketId,
- ticketUrl: `${origin}/#!/ticket/${ticketId}/summary`,
+ ticketUrl: `${url}ticket/${ticketId}/summary`,
salesPersonId: ticket.salesPersonFk
})));
}
}
}
-
- // SET COLLECTION
if (sales && sales.length) {
- // GET BARCODES
const barcodes = await Self.rawSql(`
SELECT s.id saleFk, b.code, c.id
FROM vn.sale s
@@ -114,13 +108,10 @@ module.exports = Self => {
WHERE s.ticketFk = ?
AND tr.landed >= util.VN_CURDATE() - INTERVAL 1 YEAR`,
[ticketId], myOptions);
-
- // BINDINGS
ticket.sales = [];
for (const sale of sales) {
if (sale.ticketFk === ticketId) {
sale.Barcodes = [];
-
if (barcodes && barcodes.length) {
for (const barcode of barcodes) {
if (barcode.saleFk === sale.saleFk) {
@@ -131,7 +122,6 @@ module.exports = Self => {
}
}
}
-
ticket.sales.push(sale);
}
}
@@ -140,7 +130,6 @@ module.exports = Self => {
}
}
await Promise.all(promises);
-
return collection;
};
};
diff --git a/back/methods/collection/spec/setSaleQuantity.spec.js b/back/methods/collection/spec/setSaleQuantity.spec.js
index fdc1bce1a..b563f5b19 100644
--- a/back/methods/collection/spec/setSaleQuantity.spec.js
+++ b/back/methods/collection/spec/setSaleQuantity.spec.js
@@ -18,6 +18,14 @@ describe('setSaleQuantity()', () => {
it('should change quantity sale', async() => {
const tx = await models.Ticket.beginTransaction({});
+ spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
+ if (sqlStatement.includes('catalog_calcFromItem')) {
+ sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
+ SELECT 100 as available;`;
+ params = null;
+ }
+ return models.Ticket.rawSql(sqlStatement, params, options);
+ });
try {
const options = {transaction: tx};
diff --git a/back/methods/notification/getList.js b/back/methods/notification/getList.js
new file mode 100644
index 000000000..3881f0f63
--- /dev/null
+++ b/back/methods/notification/getList.js
@@ -0,0 +1,54 @@
+module.exports = Self => {
+ Self.remoteMethod('getList', {
+ description: 'Get list of the available and active notification subscriptions',
+ accessType: 'READ',
+ accepts: [
+ {
+ arg: 'id',
+ type: 'number',
+ description: 'User to modify',
+ http: {source: 'path'}
+ }
+ ],
+ returns: {
+ type: 'object',
+ root: true
+ },
+ http: {
+ path: `/:id/getList`,
+ verb: 'GET'
+ }
+ });
+
+ Self.getList = async(id, options) => {
+ const activeNotificationsMap = new Map();
+
+ const myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const availableNotificationsMap = await Self.getAvailable(id, myOptions);
+ const activeNotifications = await Self.app.models.NotificationSubscription.find({
+ fields: ['id', 'notificationFk'],
+ include: {relation: 'notification'},
+ where: {userFk: id}
+ }, myOptions);
+
+ for (active of activeNotifications) {
+ activeNotificationsMap.set(active.notificationFk, {
+ id: active.id,
+ notificationFk: active.notificationFk,
+ name: active.notification().name,
+ description: active.notification().description,
+ active: true
+ });
+ availableNotificationsMap.delete(active.notificationFk);
+ }
+
+ return {
+ active: [...activeNotificationsMap.entries()],
+ available: [...availableNotificationsMap.entries()]
+ };
+ };
+};
diff --git a/back/methods/notification/specs/getList.spec.js b/back/methods/notification/specs/getList.spec.js
new file mode 100644
index 000000000..52ac497a5
--- /dev/null
+++ b/back/methods/notification/specs/getList.spec.js
@@ -0,0 +1,13 @@
+const models = require('vn-loopback/server/server').models;
+
+describe('NotificationSubscription getList()', () => {
+ it('should return a list of available and active notifications of a user', async() => {
+ const userId = 9;
+ const {active, available} = await models.NotificationSubscription.getList(userId);
+ const notifications = await models.Notification.find({});
+ const totalAvailable = notifications.length - active.length;
+
+ expect(active.length).toEqual(2);
+ expect(available.length).toEqual(totalAvailable);
+ });
+});
diff --git a/back/methods/url/getByUser.js b/back/methods/url/getByUser.js
new file mode 100644
index 000000000..dd4805182
--- /dev/null
+++ b/back/methods/url/getByUser.js
@@ -0,0 +1,40 @@
+module.exports = function(Self) {
+ Self.remoteMethod('getByUser', {
+ description: 'returns the starred modules for the current user',
+ accessType: 'READ',
+ accepts: [{
+ arg: 'userId',
+ type: 'number',
+ description: 'The user id',
+ required: true,
+ http: {source: 'path'}
+ }],
+ returns: {
+ type: 'object',
+ root: true
+ },
+ http: {
+ path: `/:userId/get-by-user`,
+ verb: 'GET'
+ }
+ });
+
+ Self.getByUser = async userId => {
+ const models = Self.app.models;
+ const appNames = ['hedera'];
+ const filter = {
+ fields: ['appName', 'url'],
+ where: {
+ appName: {inq: appNames},
+ environment: process.env.NODE_ENV ?? 'development',
+ }
+ };
+
+ const isWorker = await models.Account.findById(userId, {fields: ['id']});
+ if (!isWorker)
+ return models.Url.find(filter);
+
+ appNames.push('salix');
+ return models.Url.find(filter);
+ };
+};
diff --git a/back/methods/url/getUrl.js b/back/methods/url/getUrl.js
new file mode 100644
index 000000000..f30719b9f
--- /dev/null
+++ b/back/methods/url/getUrl.js
@@ -0,0 +1,30 @@
+module.exports = Self => {
+ Self.remoteMethod('getUrl', {
+ description: 'Returns the colling app name',
+ accessType: 'READ',
+ accepts: [
+ {
+ arg: 'app',
+ type: 'string',
+ required: false
+ }
+ ],
+ returns: {
+ type: 'object',
+ root: true
+ },
+ http: {
+ path: `/getUrl`,
+ verb: 'get'
+ }
+ });
+ Self.getUrl = async(appName = 'salix') => {
+ const {url} = await Self.app.models.Url.findOne({
+ where: {
+ appName,
+ enviroment: process.env.NODE_ENV || 'development'
+ }
+ });
+ return url;
+ };
+};
diff --git a/back/methods/url/specs/getByUser.spec.js b/back/methods/url/specs/getByUser.spec.js
new file mode 100644
index 000000000..f6af6ec00
--- /dev/null
+++ b/back/methods/url/specs/getByUser.spec.js
@@ -0,0 +1,19 @@
+const {models} = require('vn-loopback/server/server');
+
+describe('getByUser()', () => {
+ const worker = 1;
+ const notWorker = 2;
+ it(`should return only hedera url if not is worker`, async() => {
+ const urls = await models.Url.getByUser(notWorker);
+
+ expect(urls.length).toEqual(1);
+ expect(urls[0].appName).toEqual('hedera');
+ });
+
+ it(`should return more than hedera url`, async() => {
+ const urls = await models.Url.getByUser(worker);
+
+ expect(urls.length).toBeGreaterThan(1);
+ expect(urls.find(url => url.appName == 'salix').appName).toEqual('salix');
+ });
+});
diff --git a/back/methods/vn-user/update-user.js b/back/methods/vn-user/update-user.js
new file mode 100644
index 000000000..ddaae8548
--- /dev/null
+++ b/back/methods/vn-user/update-user.js
@@ -0,0 +1,39 @@
+module.exports = Self => {
+ Self.remoteMethodCtx('updateUser', {
+ description: 'Update user data',
+ accepts: [
+ {
+ arg: 'id',
+ type: 'integer',
+ description: 'The user id',
+ required: true,
+ http: {source: 'path'}
+ }, {
+ arg: 'name',
+ type: 'string',
+ description: 'The user name',
+ }, {
+ arg: 'nickname',
+ type: 'string',
+ description: 'The user nickname',
+ }, {
+ arg: 'email',
+ type: 'string',
+ description: 'The user email'
+ }, {
+ arg: 'lang',
+ type: 'string',
+ description: 'The user lang'
+ }
+ ],
+ http: {
+ path: `/:id/update-user`,
+ verb: 'PATCH'
+ }
+ });
+
+ Self.updateUser = async(ctx, id, name, nickname, email, lang) => {
+ await Self.userSecurity(ctx, id);
+ await Self.upsertWithWhere({id}, {name, nickname, email, lang});
+ };
+};
diff --git a/back/models/chat.js b/back/models/chat.js
index a18edbd3f..882db747e 100644
--- a/back/models/chat.js
+++ b/back/models/chat.js
@@ -7,17 +7,14 @@ module.exports = Self => {
Self.observe('before save', async function(ctx) {
if (!ctx.isNewInstance) return;
-
let {message} = ctx.instance;
if (!message) return;
const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g);
if (!parts) return;
-
const replacedParts = parts.map(part => {
return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, '');
});
-
for (const [index, part] of parts.entries())
message = message.replace(part, replacedParts[index]);
diff --git a/back/models/notificationSubscription.js b/back/models/notificationSubscription.js
index f1b2811fa..8efb83e7d 100644
--- a/back/models/notificationSubscription.js
+++ b/back/models/notificationSubscription.js
@@ -1,62 +1,74 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
+ require('../methods/notification/getList')(Self);
+
Self.observe('before save', async function(ctx) {
+ await checkModifyPermission(ctx);
+ });
+
+ Self.observe('before delete', async function(ctx) {
+ await checkModifyPermission(ctx);
+ });
+
+ async function checkModifyPermission(ctx) {
const models = Self.app.models;
+ const instance = ctx.instance;
const userId = ctx.options.accessToken.userId;
- const user = await ctx.instance.userFk;
- const modifiedUser = await getUserToModify(null, user, models);
- if (userId != modifiedUser.id && userId != modifiedUser.bossFk)
- throw new UserError('You dont have permission to modify this user');
- });
+ let notificationFk;
+ let workerId;
- Self.remoteMethod('deleteNotification', {
- description: 'Deletes a notification subscription',
- accepts: [
- {
- arg: 'ctx',
- type: 'object',
- http: {source: 'context'}
- },
- {
- arg: 'notificationId',
- type: 'number',
- required: true
- },
- ],
- returns: {
- type: 'object',
- root: true
- },
- http: {
- verb: 'POST',
- path: '/deleteNotification'
+ if (instance) {
+ notificationFk = instance.notificationFk;
+ workerId = instance.userFk;
+ } else {
+ const notificationSubscription = await models.NotificationSubscription.findById(ctx.where.id);
+ notificationFk = notificationSubscription.notificationFk;
+ workerId = notificationSubscription.userFk;
}
- });
- Self.deleteNotification = async function(ctx, notificationId) {
- const models = Self.app.models;
- const user = ctx.req.accessToken.userId;
- const modifiedUser = await getUserToModify(notificationId, null, models);
+ const worker = await models.Worker.findById(workerId, {fields: ['id', 'bossFk']});
+ const available = await Self.getAvailable(workerId);
+ const hasAcl = available.has(notificationFk);
- if (user != modifiedUser.id && user != modifiedUser.bossFk)
- throw new UserError('You dont have permission to modify this user');
-
- await models.NotificationSubscription.destroyById(notificationId);
- };
-
- async function getUserToModify(notificationId, userFk, models) {
- let userToModify = userFk;
- if (notificationId) {
- const subscription = await models.NotificationSubscription.findById(notificationId);
- userToModify = subscription.userFk;
- }
- return await models.Worker.findOne({
- fields: ['id', 'bossFk'],
- where: {
- id: userToModify
- }
- });
+ if (!hasAcl || (userId != worker.id && userId != worker.bossFk))
+ throw new UserError('The notification subscription of this worker cant be modified');
}
+
+ Self.getAvailable = async function(userId, options) {
+ const availableNotificationsMap = new Map();
+ const models = Self.app.models;
+
+ const myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ const roles = await models.RoleMapping.find({
+ fields: ['roleId'],
+ where: {principalId: userId}
+ }, myOptions);
+
+ const availableNotifications = await models.NotificationAcl.find({
+ fields: ['notificationFk', 'roleFk'],
+ include: {relation: 'notification'},
+ where: {
+ roleFk: {
+ inq: roles.map(role => role.roleId),
+ },
+ }
+ }, myOptions);
+
+ for (available of availableNotifications) {
+ availableNotificationsMap.set(available.notificationFk, {
+ id: null,
+ notificationFk: available.notificationFk,
+ name: available.notification().name,
+ description: available.notification().description,
+ active: false
+ });
+ }
+ return availableNotificationsMap;
+ };
};
diff --git a/back/models/specs/notificationSubscription.spec.js b/back/models/specs/notificationSubscription.spec.js
index c7f37abed..c2adcbc59 100644
--- a/back/models/specs/notificationSubscription.spec.js
+++ b/back/models/specs/notificationSubscription.spec.js
@@ -1,74 +1,126 @@
const models = require('vn-loopback/server/server').models;
describe('loopback model NotificationSubscription', () => {
- it('Should fail to delete a notification if the user is not editing itself or a subordinate', async() => {
+ it('should fail to add a notification subscription if the worker doesnt have ACLs', async() => {
const tx = await models.NotificationSubscription.beginTransaction({});
+ let error;
try {
- const options = {transaction: tx};
- const user = 9;
+ const options = {transaction: tx, accessToken: {userId: 9}};
+ await models.NotificationSubscription.create({notificationFk: 1, userFk: 62}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toEqual('The notification subscription of this worker cant be modified');
+ });
+
+ it('should fail to add a notification subscription if the user isnt editing itself or subordinate', async() => {
+ const tx = await models.NotificationSubscription.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 1}};
+ await models.NotificationSubscription.create({notificationFk: 1, userFk: 9}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error.message).toEqual('The notification subscription of this worker cant be modified');
+ });
+
+ it('should fail to delete a notification subscription if the user isnt editing itself or subordinate', async() => {
+ const tx = await models.NotificationSubscription.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 9}};
const notificationSubscriptionId = 2;
- const ctx = {req: {accessToken: {userId: user}}};
- const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
+ await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
- let error;
-
- try {
- await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
- } catch (e) {
- error = e;
- }
-
- expect(error.message).toContain('You dont have permission to modify this user');
await tx.rollback();
} catch (e) {
await tx.rollback();
- throw e;
+ error = e;
}
+
+ expect(error.message).toEqual('The notification subscription of this worker cant be modified');
});
- it('Should delete a notification if the user is editing itself', async() => {
+ it('should add a notification subscription if the user is editing itself', async() => {
const tx = await models.NotificationSubscription.beginTransaction({});
+ let error;
try {
- const options = {transaction: tx};
- const user = 9;
+ const options = {transaction: tx, accessToken: {userId: 9}};
+ await models.NotificationSubscription.create({notificationFk: 2, userFk: 9}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+
+ it('should delete a notification subscription if the user is editing itself', async() => {
+ const tx = await models.NotificationSubscription.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 9}};
+ const notificationSubscriptionId = 6;
+ await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+
+ it('should add a notification subscription if the user is editing a subordinate', async() => {
+ const tx = await models.NotificationSubscription.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 9}};
+ await models.NotificationSubscription.create({notificationFk: 1, userFk: 5}, options);
+
+ await tx.rollback();
+ } catch (e) {
+ await tx.rollback();
+ error = e;
+ }
+
+ expect(error).toBeUndefined();
+ });
+
+ it('should delete a notification subscription if the user is editing a subordinate', async() => {
+ const tx = await models.NotificationSubscription.beginTransaction({});
+ let error;
+
+ try {
+ const options = {transaction: tx, accessToken: {userId: 19}};
const notificationSubscriptionId = 4;
- const ctx = {req: {accessToken: {userId: user}}};
- const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
+ await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options);
- await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
-
- const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
-
- expect(deletedNotification).toBeNull();
await tx.rollback();
} catch (e) {
await tx.rollback();
- throw e;
+ error = e;
}
- });
- it('Should delete a notification if the user is editing a subordinate', async() => {
- const tx = await models.NotificationSubscription.beginTransaction({});
-
- try {
- const options = {transaction: tx};
- const user = 9;
- const notificationSubscriptionId = 5;
- const ctx = {req: {accessToken: {userId: user}}};
- const notification = await models.NotificationSubscription.findById(notificationSubscriptionId);
-
- await models.NotificationSubscription.deleteNotification(ctx, notification.id, options);
-
- const deletedNotification = await models.NotificationSubscription.findById(notificationSubscriptionId);
-
- expect(deletedNotification).toBeNull();
- await tx.rollback();
- } catch (e) {
- await tx.rollback();
- throw e;
- }
+ expect(error).toBeUndefined();
});
});
diff --git a/back/models/specs/vn-user.spec.js b/back/models/specs/vn-user.spec.js
index 3700b919a..8689a7854 100644
--- a/back/models/specs/vn-user.spec.js
+++ b/back/models/specs/vn-user.spec.js
@@ -1,4 +1,5 @@
const models = require('vn-loopback/server/server').models;
+const ForbiddenError = require('vn-loopback/util/forbiddenError');
describe('loopback model VnUser', () => {
it('should return true if the user has the given role', async() => {
@@ -12,4 +13,42 @@ describe('loopback model VnUser', () => {
expect(result).toBeFalsy();
});
+
+ describe('userSecurity', () => {
+ const itManagementId = 115;
+ const hrId = 37;
+ const employeeId = 1;
+
+ it('should check if you are the same user', async() => {
+ const ctx = {options: {accessToken: {userId: employeeId}}};
+ await models.VnUser.userSecurity(ctx, employeeId);
+ });
+
+ it('should check for higher privileges', async() => {
+ const ctx = {options: {accessToken: {userId: itManagementId}}};
+ await models.VnUser.userSecurity(ctx, employeeId);
+ });
+
+ it('should check if you have medium privileges and the user email is not verified', async() => {
+ const ctx = {options: {accessToken: {userId: hrId}}};
+ await models.VnUser.userSecurity(ctx, employeeId);
+ });
+
+ it('should throw an error if you have medium privileges and the users email is verified', async() => {
+ const tx = await models.VnUser.beginTransaction({});
+ const ctx = {options: {accessToken: {userId: hrId}}};
+ try {
+ const options = {transaction: tx};
+ const userToUpdate = await models.VnUser.findById(1, null, options);
+ userToUpdate.updateAttribute('emailVerified', 1, options);
+
+ await models.VnUser.userSecurity(ctx, employeeId, options);
+ await tx.rollback();
+ } catch (error) {
+ await tx.rollback();
+
+ expect(error).toEqual(new ForbiddenError());
+ }
+ });
+ });
});
diff --git a/back/models/url.js b/back/models/url.js
new file mode 100644
index 000000000..b603e9eb5
--- /dev/null
+++ b/back/models/url.js
@@ -0,0 +1,4 @@
+module.exports = Self => {
+ require('../methods/url/getByUser')(Self);
+ require('../methods/url/getUrl')(Self);
+};
diff --git a/back/models/vn-user.js b/back/models/vn-user.js
index cf210b61b..de5bf7b63 100644
--- a/back/models/vn-user.js
+++ b/back/models/vn-user.js
@@ -1,6 +1,7 @@
const vnModel = require('vn-loopback/common/models/vn-model');
-const LoopBackContext = require('loopback-context');
const {Email} = require('vn-print');
+const ForbiddenError = require('vn-loopback/util/forbiddenError');
+const LoopBackContext = require('loopback-context');
module.exports = function(Self) {
vnModel(Self);
@@ -12,6 +13,7 @@ module.exports = function(Self) {
require('../methods/vn-user/privileges')(Self);
require('../methods/vn-user/validate-auth')(Self);
require('../methods/vn-user/renew-token')(Self);
+ require('../methods/vn-user/update-user')(Self);
Self.definition.settings.acls = Self.definition.settings.acls.filter(acl => acl.property !== 'create');
@@ -90,11 +92,7 @@ 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 url = await Self.app.models.Url.getUrl();
const defaultHash = '/reset-password?access_token=$token$';
const recoverHashes = {
@@ -110,7 +108,7 @@ module.exports = function(Self) {
const params = {
recipient: info.email,
lang: user.lang,
- url: origin + '/#!' + recoverHash
+ url: url.slice(0, -1) + recoverHash
};
const options = Object.assign({}, info.options);
@@ -178,45 +176,75 @@ module.exports = function(Self) {
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
.filter(acl => acl.property != 'changePassword');
- // FIXME: https://redmine.verdnatura.es/issues/5761
- // Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
- // if (!ctx.args || !ctx.args.data.email) return;
+ Self.userSecurity = async(ctx, userId, options) => {
+ const models = Self.app.models;
+ const accessToken = ctx?.options?.accessToken || LoopBackContext.getCurrentContext().active.accessToken;
+ const ctxToken = {req: {accessToken}};
- // 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(':');
+ if (userId === accessToken.userId) return;
- // class Mailer {
- // async send(verifyOptions, cb) {
- // const params = {
- // url: verifyOptions.verifyHref,
- // recipient: verifyOptions.to,
- // lang: ctx.req.getLocale()
- // };
+ const myOptions = {};
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
- // const email = new Email('email-verify', params);
- // email.send();
+ const hasHigherPrivileges = await models.ACL.checkAccessAcl(ctxToken, 'VnUser', 'higherPrivileges', myOptions);
+ if (hasHigherPrivileges) return;
- // cb(null, verifyOptions.to);
- // }
- // }
+ const hasMediumPrivileges = await models.ACL.checkAccessAcl(ctxToken, 'VnUser', 'mediumPrivileges', myOptions);
+ const user = await models.VnUser.findById(userId, {fields: ['id', 'emailVerified']}, myOptions);
+ if (!user.emailVerified && hasMediumPrivileges) return;
- // 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
- // };
+ throw new ForbiddenError();
+ };
- // await instance.verify(options);
- // });
+ Self.observe('after save', async ctx => {
+ const instance = ctx?.instance;
+ const newEmail = instance?.email;
+ const oldEmail = ctx?.hookState?.oldInstance?.email;
+ if (!ctx.isNewInstance && (!newEmail || !oldEmail || newEmail == oldEmail)) return;
+
+ 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 env = process.env.NODE_ENV;
+ const liliumUrl = await Self.app.models.Url.findOne({
+ where: {and: [
+ {appName: 'lilium'},
+ {environment: env}
+ ]}
+ });
+
+ class Mailer {
+ async send(verifyOptions, cb) {
+ const params = {
+ url: verifyOptions.verifyHref,
+ recipient: verifyOptions.to
+ };
+
+ const email = new Email('email-verify', params);
+ email.send();
+
+ cb(null, verifyOptions.to);
+ }
+ }
+
+ const options = {
+ type: 'email',
+ to: newEmail,
+ from: {},
+ redirect: `${liliumUrl.url}verifyEmail?userId=${instance.id}`,
+ template: false,
+ mailer: new Mailer,
+ host: url[1].split('/')[2],
+ port: url[2],
+ protocol: url[0],
+ user: Self
+ };
+
+ await instance.verify(options, ctx.options);
+ });
};
diff --git a/back/models/vn-user.json b/back/models/vn-user.json
index 9e3f8df89..0f6daff5a 100644
--- a/back/models/vn-user.json
+++ b/back/models/vn-user.json
@@ -13,19 +13,12 @@
"type": "number",
"id": true
},
- "name": {
+ "name": {
"type": "string",
"required": true
},
"username": {
- "type": "string",
- "mysql": {
- "columnName": "name"
- }
- },
- "password": {
- "type": "string",
- "required": true
+ "type": "string"
},
"roleFk": {
"type": "number",
@@ -45,6 +38,9 @@
"email": {
"type": "string"
},
+ "emailVerified": {
+ "type": "boolean"
+ },
"created": {
"type": "date"
},
@@ -144,7 +140,8 @@
"image",
"hasGrant",
"realm",
- "email"
+ "email",
+ "emailVerified"
]
}
}
diff --git a/db/changes/231201/00-ACL.sql b/db/.archive/231201/00-ACL.sql
similarity index 100%
rename from db/changes/231201/00-ACL.sql
rename to db/.archive/231201/00-ACL.sql
diff --git a/db/changes/231201/00-chatRefactor.sql b/db/.archive/231201/00-chatRefactor.sql
similarity index 100%
rename from db/changes/231201/00-chatRefactor.sql
rename to db/.archive/231201/00-chatRefactor.sql
diff --git a/db/changes/231201/00-invoiceInSerial.sql b/db/.archive/231201/00-invoiceInSerial.sql
similarity index 100%
rename from db/changes/231201/00-invoiceInSerial.sql
rename to db/.archive/231201/00-invoiceInSerial.sql
diff --git a/db/changes/231201/00-itemType_isFragile.sql b/db/.archive/231201/00-itemType_isFragile.sql
similarity index 100%
rename from db/changes/231201/00-itemType_isFragile.sql
rename to db/.archive/231201/00-itemType_isFragile.sql
diff --git a/db/changes/231201/00-mailACL.sql b/db/.archive/231201/00-mailACL.sql
similarity index 100%
rename from db/changes/231201/00-mailACL.sql
rename to db/.archive/231201/00-mailACL.sql
diff --git a/db/changes/231201/00-operator.sql b/db/.archive/231201/00-operator.sql
similarity index 100%
rename from db/changes/231201/00-operator.sql
rename to db/.archive/231201/00-operator.sql
diff --git a/db/changes/231201/00-supplierAccount_deleteTriggers.sql b/db/.archive/231201/00-supplierAccount_deleteTriggers.sql
similarity index 100%
rename from db/changes/231201/00-supplierAccount_deleteTriggers.sql
rename to db/.archive/231201/00-supplierAccount_deleteTriggers.sql
diff --git a/db/changes/231201/00-ticket_getWarnings.sql b/db/.archive/231201/00-ticket_getWarnings.sql
similarity index 100%
rename from db/changes/231201/00-ticket_getWarnings.sql
rename to db/.archive/231201/00-ticket_getWarnings.sql
diff --git a/db/changes/231201/00-wagon.sql b/db/.archive/231201/00-wagon.sql
similarity index 100%
rename from db/changes/231201/00-wagon.sql
rename to db/.archive/231201/00-wagon.sql
diff --git a/db/changes/231202/00-delivery.sql b/db/.archive/231202/00-delivery.sql
similarity index 100%
rename from db/changes/231202/00-delivery.sql
rename to db/.archive/231202/00-delivery.sql
diff --git a/db/changes/231203/00-delivery.sql b/db/.archive/231203/00-delivery.sql
similarity index 100%
rename from db/changes/231203/00-delivery.sql
rename to db/.archive/231203/00-delivery.sql
diff --git a/db/changes/231204/00-rollbackDelivery.sql b/db/.archive/231204/00-rollbackDelivery.sql
similarity index 100%
rename from db/changes/231204/00-rollbackDelivery.sql
rename to db/.archive/231204/00-rollbackDelivery.sql
diff --git a/db/changes/231205/00-printQueueArgs.sql b/db/.archive/231205/00-printQueueArgs.sql
similarity index 100%
rename from db/changes/231205/00-printQueueArgs.sql
rename to db/.archive/231205/00-printQueueArgs.sql
diff --git a/db/changes/231401/00-claimBeginningAfterInsert.sql b/db/.archive/231401/00-claimBeginningAfterInsert.sql
similarity index 100%
rename from db/changes/231401/00-claimBeginningAfterInsert.sql
rename to db/.archive/231401/00-claimBeginningAfterInsert.sql
diff --git a/db/changes/231401/00-clientBeforeUpdate.sql b/db/.archive/231401/00-clientBeforeUpdate.sql
similarity index 100%
rename from db/changes/231401/00-clientBeforeUpdate.sql
rename to db/.archive/231401/00-clientBeforeUpdate.sql
diff --git a/db/changes/231401/00-hotfixDelivery.sql b/db/.archive/231401/00-hotfixDelivery.sql
similarity index 100%
rename from db/changes/231401/00-hotfixDelivery.sql
rename to db/.archive/231401/00-hotfixDelivery.sql
diff --git a/db/changes/231401/00-invoiceOutAfterInsert.sql b/db/.archive/231401/00-invoiceOutAfterInsert.sql
similarity index 100%
rename from db/changes/231401/00-invoiceOutAfterInsert.sql
rename to db/.archive/231401/00-invoiceOutAfterInsert.sql
diff --git a/db/changes/231401/00-negativeBases.sql b/db/.archive/231401/00-negativeBases.sql
similarity index 100%
rename from db/changes/231401/00-negativeBases.sql
rename to db/.archive/231401/00-negativeBases.sql
diff --git a/db/changes/231401/00-workerNotes.sql b/db/.archive/231401/00-workerNotes.sql
similarity index 100%
rename from db/changes/231401/00-workerNotes.sql
rename to db/.archive/231401/00-workerNotes.sql
diff --git a/db/changes/231402/00-negativeBases.sql b/db/.archive/231402/00-negativeBases.sql
similarity index 100%
rename from db/changes/231402/00-negativeBases.sql
rename to db/.archive/231402/00-negativeBases.sql
diff --git a/db/changes/231801/00-aclClientInforma.sql b/db/.archive/231801/00-aclClientInforma.sql
similarity index 100%
rename from db/changes/231801/00-aclClientInforma.sql
rename to db/.archive/231801/00-aclClientInforma.sql
diff --git a/db/changes/231801/00-acl_receiptEmail.sql b/db/.archive/231801/00-acl_receiptEmail.sql
similarity index 100%
rename from db/changes/231801/00-acl_receiptEmail.sql
rename to db/.archive/231801/00-acl_receiptEmail.sql
diff --git a/db/changes/231801/00-clientInforma.sql b/db/.archive/231801/00-clientInforma.sql
similarity index 100%
rename from db/changes/231801/00-clientInforma.sql
rename to db/.archive/231801/00-clientInforma.sql
diff --git a/db/changes/231801/00-client_setRatingAcl.sql b/db/.archive/231801/00-client_setRatingAcl.sql
similarity index 100%
rename from db/changes/231801/00-client_setRatingAcl.sql
rename to db/.archive/231801/00-client_setRatingAcl.sql
diff --git a/db/changes/231801/00-deleteProcs_refund.sql b/db/.archive/231801/00-deleteProcs_refund.sql
similarity index 100%
rename from db/changes/231801/00-deleteProcs_refund.sql
rename to db/.archive/231801/00-deleteProcs_refund.sql
diff --git a/db/changes/231801/00-deviceProduction.sql b/db/.archive/231801/00-deviceProduction.sql
similarity index 100%
rename from db/changes/231801/00-deviceProduction.sql
rename to db/.archive/231801/00-deviceProduction.sql
diff --git a/db/changes/231801/00-kkearEntryNotes.sql b/db/.archive/231801/00-kkearEntryNotes.sql
similarity index 100%
rename from db/changes/231801/00-kkearEntryNotes.sql
rename to db/.archive/231801/00-kkearEntryNotes.sql
diff --git a/db/changes/231801/00-newCompanyI18n.sql b/db/.archive/231801/00-newCompanyI18n.sql
similarity index 100%
rename from db/changes/231801/00-newCompanyI18n.sql
rename to db/.archive/231801/00-newCompanyI18n.sql
diff --git a/db/changes/231801/00-newTableWeb.sql b/db/.archive/231801/00-newTableWeb.sql
similarity index 100%
rename from db/changes/231801/00-newTableWeb.sql
rename to db/.archive/231801/00-newTableWeb.sql
diff --git a/db/changes/231801/00-observationEmailACL.sql b/db/.archive/231801/00-observationEmailACL.sql
similarity index 100%
rename from db/changes/231801/00-observationEmailACL.sql
rename to db/.archive/231801/00-observationEmailACL.sql
diff --git a/db/changes/231801/00-optimiceZoneEstimatedDelivery.sql b/db/.archive/231801/00-optimiceZoneEstimatedDelivery.sql
similarity index 100%
rename from db/changes/231801/00-optimiceZoneEstimatedDelivery.sql
rename to db/.archive/231801/00-optimiceZoneEstimatedDelivery.sql
diff --git a/db/changes/231801/00-saleTracking.sql b/db/.archive/231801/00-saleTracking.sql
similarity index 100%
rename from db/changes/231801/00-saleTracking.sql
rename to db/.archive/231801/00-saleTracking.sql
diff --git a/db/changes/231801/00-ticketConfig.sql b/db/.archive/231801/00-ticketConfig.sql
similarity index 100%
rename from db/changes/231801/00-ticketConfig.sql
rename to db/.archive/231801/00-ticketConfig.sql
diff --git a/db/changes/231801/00-updateIsVies.sql b/db/.archive/231801/00-updateIsVies.sql
similarity index 100%
rename from db/changes/231801/00-updateIsVies.sql
rename to db/.archive/231801/00-updateIsVies.sql
diff --git a/db/changes/231801/00-updateisViesClient.sql b/db/.archive/231801/00-updateisViesClient.sql
similarity index 100%
rename from db/changes/231801/00-updateisViesClient.sql
rename to db/.archive/231801/00-updateisViesClient.sql
diff --git a/db/changes/231801/00-userAcl.sql b/db/.archive/231801/00-userAcl.sql
similarity index 100%
rename from db/changes/231801/00-userAcl.sql
rename to db/.archive/231801/00-userAcl.sql
diff --git a/db/changes/231801/00-userRoleLog.sql b/db/.archive/231801/00-userRoleLog.sql
similarity index 100%
rename from db/changes/231801/00-userRoleLog.sql
rename to db/.archive/231801/00-userRoleLog.sql
diff --git a/db/changes/231801/01-viewCompany10L.sql b/db/.archive/231801/01-viewCompany10L.sql
similarity index 100%
rename from db/changes/231801/01-viewCompany10L.sql
rename to db/.archive/231801/01-viewCompany10L.sql
diff --git a/db/changes/232001/00-clientWorkerName.sql b/db/.archive/232001/00-clientWorkerName.sql
similarity index 100%
rename from db/changes/232001/00-clientWorkerName.sql
rename to db/.archive/232001/00-clientWorkerName.sql
diff --git a/db/changes/232001/00-createWorker.sql b/db/.archive/232001/00-createWorker.sql
similarity index 100%
rename from db/changes/232001/00-createWorker.sql
rename to db/.archive/232001/00-createWorker.sql
diff --git a/db/changes/232001/00-invoiceOut_new.sql b/db/.archive/232001/00-invoiceOut_new.sql
similarity index 100%
rename from db/changes/232001/00-invoiceOut_new.sql
rename to db/.archive/232001/00-invoiceOut_new.sql
diff --git a/db/changes/232001/00-wagon.sql b/db/.archive/232001/00-wagon.sql
similarity index 100%
rename from db/changes/232001/00-wagon.sql
rename to db/.archive/232001/00-wagon.sql
diff --git a/db/changes/232201/00-defaulterView.sql b/db/.archive/232201/00-defaulterView.sql
similarity index 100%
rename from db/changes/232201/00-defaulterView.sql
rename to db/.archive/232201/00-defaulterView.sql
diff --git a/db/changes/232201/00-procedurecanAdvance.sql b/db/.archive/232201/00-procedurecanAdvance.sql
similarity index 100%
rename from db/changes/232201/00-procedurecanAdvance.sql
rename to db/.archive/232201/00-procedurecanAdvance.sql
diff --git a/db/changes/232201/00-procedurecanbePostponed.sql b/db/.archive/232201/00-procedurecanbePostponed.sql
similarity index 100%
rename from db/changes/232201/00-procedurecanbePostponed.sql
rename to db/.archive/232201/00-procedurecanbePostponed.sql
diff --git a/db/changes/232201/00-workerConfigPayMethod.sql b/db/.archive/232201/00-workerConfigPayMethod.sql
similarity index 100%
rename from db/changes/232201/00-workerConfigPayMethod.sql
rename to db/.archive/232201/00-workerConfigPayMethod.sql
diff --git a/db/changes/232202/00-procedurecanAdvance.sql b/db/.archive/232202/00-procedurecanAdvance.sql
similarity index 100%
rename from db/changes/232202/00-procedurecanAdvance.sql
rename to db/.archive/232202/00-procedurecanAdvance.sql
diff --git a/db/changes/232202/00-procedurecanbePostponed.sql b/db/.archive/232202/00-procedurecanbePostponed.sql
similarity index 100%
rename from db/changes/232202/00-procedurecanbePostponed.sql
rename to db/.archive/232202/00-procedurecanbePostponed.sql
diff --git a/db/changes/232401/.gitkeep b/db/.archive/232401/.gitkeep
similarity index 100%
rename from db/changes/232401/.gitkeep
rename to db/.archive/232401/.gitkeep
diff --git a/db/changes/232401/00-buyConfig_travelConfig.sql b/db/.archive/232401/00-buyConfig_travelConfig.sql
similarity index 100%
rename from db/changes/232401/00-buyConfig_travelConfig.sql
rename to db/.archive/232401/00-buyConfig_travelConfig.sql
diff --git a/db/changes/232401/00-printer.sql b/db/.archive/232401/00-printer.sql
similarity index 100%
rename from db/changes/232401/00-printer.sql
rename to db/.archive/232401/00-printer.sql
diff --git a/db/changes/232401/00-ticket_warehouse.sql b/db/.archive/232401/00-ticket_warehouse.sql
similarity index 100%
rename from db/changes/232401/00-ticket_warehouse.sql
rename to db/.archive/232401/00-ticket_warehouse.sql
diff --git a/db/changes/232401/00-userPassExpired.sql b/db/.archive/232401/00-userPassExpired.sql
similarity index 100%
rename from db/changes/232401/00-userPassExpired.sql
rename to db/.archive/232401/00-userPassExpired.sql
diff --git a/db/changes/232402/00-hotFix_travelConfig.sql b/db/.archive/232402/00-hotFix_travelConfig.sql
similarity index 100%
rename from db/changes/232402/00-hotFix_travelConfig.sql
rename to db/.archive/232402/00-hotFix_travelConfig.sql
diff --git a/db/changes/232601/00-aclAccount.sql b/db/.archive/232601/00-aclAccount.sql
similarity index 100%
rename from db/changes/232601/00-aclAccount.sql
rename to db/.archive/232601/00-aclAccount.sql
diff --git a/db/changes/232601/00-aclInvoiceTickets.sql b/db/.archive/232601/00-aclInvoiceTickets.sql
similarity index 100%
rename from db/changes/232601/00-aclInvoiceTickets.sql
rename to db/.archive/232601/00-aclInvoiceTickets.sql
diff --git a/db/changes/232601/00-aclMailAliasAccount.sql b/db/.archive/232601/00-aclMailAliasAccount.sql
similarity index 100%
rename from db/changes/232601/00-aclMailAliasAccount.sql
rename to db/.archive/232601/00-aclMailAliasAccount.sql
diff --git a/db/changes/232601/00-aclMailForward.sql b/db/.archive/232601/00-aclMailForward.sql
similarity index 100%
rename from db/changes/232601/00-aclMailForward.sql
rename to db/.archive/232601/00-aclMailForward.sql
diff --git a/db/changes/232601/00-aclRole.sql b/db/.archive/232601/00-aclRole.sql
similarity index 100%
rename from db/changes/232601/00-aclRole.sql
rename to db/.archive/232601/00-aclRole.sql
diff --git a/db/changes/232601/00-aclVnUser.sql b/db/.archive/232601/00-aclVnUser.sql
similarity index 100%
rename from db/changes/232601/00-aclVnUser.sql
rename to db/.archive/232601/00-aclVnUser.sql
diff --git a/db/changes/232601/00-aclVnUser_renewToken.sql b/db/.archive/232601/00-aclVnUser_renewToken.sql
similarity index 100%
rename from db/changes/232601/00-aclVnUser_renewToken.sql
rename to db/.archive/232601/00-aclVnUser_renewToken.sql
diff --git a/db/changes/232601/00-entry_updateComission.sql b/db/.archive/232601/00-entry_updateComission.sql
similarity index 100%
rename from db/changes/232601/00-entry_updateComission.sql
rename to db/.archive/232601/00-entry_updateComission.sql
diff --git a/db/changes/232601/00-packingSiteAdvanced.sql b/db/.archive/232601/00-packingSiteAdvanced.sql
similarity index 100%
rename from db/changes/232601/00-packingSiteAdvanced.sql
rename to db/.archive/232601/00-packingSiteAdvanced.sql
diff --git a/db/changes/232601/00-salix.sql b/db/.archive/232601/00-salix.sql
similarity index 100%
rename from db/changes/232601/00-salix.sql
rename to db/.archive/232601/00-salix.sql
diff --git a/db/changes/232601/00-useSpecificsAcls.sql b/db/.archive/232601/00-useSpecificsAcls.sql
similarity index 100%
rename from db/changes/232601/00-useSpecificsAcls.sql
rename to db/.archive/232601/00-useSpecificsAcls.sql
diff --git a/db/changes/232601/01-invoiceOutPdf.sql b/db/.archive/232601/01-invoiceOutPdf.sql
similarity index 100%
rename from db/changes/232601/01-invoiceOutPdf.sql
rename to db/.archive/232601/01-invoiceOutPdf.sql
diff --git a/db/changes/232602/01-aclAddAlias.sql b/db/.archive/232602/01-aclAddAlias.sql
similarity index 100%
rename from db/changes/232602/01-aclAddAlias.sql
rename to db/.archive/232602/01-aclAddAlias.sql
diff --git a/db/changes/232801/00-authCode.sql b/db/.archive/232801/00-authCode.sql
similarity index 100%
rename from db/changes/232801/00-authCode.sql
rename to db/.archive/232801/00-authCode.sql
diff --git a/db/changes/232801/00-client_create.sql b/db/.archive/232801/00-client_create.sql
similarity index 100%
rename from db/changes/232801/00-client_create.sql
rename to db/.archive/232801/00-client_create.sql
diff --git a/db/changes/232801/00-client_create2.sql b/db/.archive/232801/00-client_create2.sql
similarity index 100%
rename from db/changes/232801/00-client_create2.sql
rename to db/.archive/232801/00-client_create2.sql
diff --git a/db/changes/232801/00-department.sql b/db/.archive/232801/00-department.sql
similarity index 100%
rename from db/changes/232801/00-department.sql
rename to db/.archive/232801/00-department.sql
diff --git a/db/changes/232801/00-fix_editCredit.sql b/db/.archive/232801/00-fix_editCredit.sql
similarity index 100%
rename from db/changes/232801/00-fix_editCredit.sql
rename to db/.archive/232801/00-fix_editCredit.sql
diff --git a/db/changes/232801/00-user.sql b/db/.archive/232801/00-user.sql
similarity index 100%
rename from db/changes/232801/00-user.sql
rename to db/.archive/232801/00-user.sql
diff --git a/db/changes/232802/01-aclWorkerDisable.sql b/db/.archive/232802/01-aclWorkerDisable.sql
similarity index 100%
rename from db/changes/232802/01-aclWorkerDisable.sql
rename to db/.archive/232802/01-aclWorkerDisable.sql
diff --git a/db/changes/234003/00-ticket_canAdvance_zone.sql b/db/changes/234003/00-ticket_canAdvance_zone.sql
new file mode 100644
index 000000000..ee07ce978
--- /dev/null
+++ b/db/changes/234003/00-ticket_canAdvance_zone.sql
@@ -0,0 +1,133 @@
+DELIMITER $$
+$$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_canAdvance`(vDateFuture DATE, vDateToAdvance DATE, vWarehouseFk INT)
+BEGIN
+/**
+ * Devuelve los tickets y la cantidad de lineas de venta que se pueden adelantar.
+ *
+ * @param vDateFuture Fecha de los tickets que se quieren adelantar.
+ * @param vDateToAdvance Fecha a cuando se quiere adelantar.
+ * @param vWarehouseFk Almacén
+ */
+ DECLARE vDateInventory DATE;
+
+ SELECT inventoried INTO vDateInventory FROM config;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.stock;
+ CREATE TEMPORARY TABLE tmp.stock
+ (itemFk INT PRIMARY KEY,
+ amount INT)
+ ENGINE = MEMORY;
+
+ INSERT INTO tmp.stock(itemFk, amount)
+ SELECT itemFk, SUM(quantity) amount FROM
+ (
+ SELECT itemFk, quantity
+ FROM itemTicketOut
+ WHERE shipped >= vDateInventory
+ AND shipped < vDateFuture
+ AND warehouseFk = vWarehouseFk
+ UNION ALL
+ SELECT itemFk, quantity
+ FROM itemEntryIn
+ WHERE landed >= vDateInventory
+ AND landed < vDateFuture
+ AND isVirtualStock = FALSE
+ AND warehouseInFk = vWarehouseFk
+ UNION ALL
+ SELECT itemFk, quantity
+ FROM itemEntryOut
+ WHERE shipped >= vDateInventory
+ AND shipped < vDateFuture
+ AND warehouseOutFk = vWarehouseFk
+ ) t
+ GROUP BY itemFk HAVING amount != 0;
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.filter
+ (INDEX (id))
+ SELECT
+ origin.ticketFk futureId,
+ dest.ticketFk id,
+ dest.state,
+ origin.futureState,
+ origin.futureIpt,
+ dest.ipt,
+ origin.workerFk,
+ origin.futureLiters,
+ origin.futureLines,
+ dest.shipped,
+ origin.shipped futureShipped,
+ dest.totalWithVat,
+ origin.totalWithVat futureTotalWithVat,
+ dest.agency,
+ origin.futureAgency,
+ dest.lines,
+ dest.liters,
+ origin.futureLines - origin.hasStock AS notMovableLines,
+ (origin.futureLines = origin.hasStock) AS isFullMovable,
+ origin.futureZoneFk,
+ origin.futureZoneName,
+ origin.classColor futureClassColor,
+ dest.classColor
+ FROM (
+ SELECT
+ s.ticketFk,
+ c.salesPersonFk workerFk,
+ t.shipped,
+ t.totalWithVat,
+ st.name futureState,
+ t.addressFk,
+ am.name futureAgency,
+ count(s.id) futureLines,
+ GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) futureIpt,
+ CAST(SUM(litros) AS DECIMAL(10,0)) futureLiters,
+ SUM((s.quantity <= IFNULL(st.amount,0))) hasStock,
+ z.id futureZoneFk,
+ z.name futureZoneName,
+ st.classColor
+ FROM ticket t
+ JOIN client c ON c.id = t.clientFk
+ JOIN sale s ON s.ticketFk = t.id
+ JOIN saleVolume sv ON sv.saleFk = s.id
+ JOIN item i ON i.id = s.itemFk
+ JOIN ticketState ts ON ts.ticketFk = t.id
+ JOIN state st ON st.id = ts.stateFk
+ JOIN agencyMode am ON t.agencyModeFk = am.id
+ JOIN zone z ON t.zoneFk = z.id
+ LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
+ LEFT JOIN tmp.stock st ON st.itemFk = i.id
+ WHERE t.shipped BETWEEN vDateFuture AND util.dayend(vDateFuture)
+ AND t.warehouseFk = vWarehouseFk
+ GROUP BY t.id
+ ) origin
+ JOIN (
+ SELECT
+ t.id ticketFk,
+ t.addressFk,
+ st.name state,
+ GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt,
+ t.shipped,
+ t.totalWithVat,
+ am.name agency,
+ CAST(SUM(litros) AS DECIMAL(10,0)) liters,
+ CAST(COUNT(*) AS DECIMAL(10,0)) `lines`,
+ st.classColor
+ FROM ticket t
+ JOIN sale s ON s.ticketFk = t.id
+ JOIN saleVolume sv ON sv.saleFk = s.id
+ JOIN item i ON i.id = s.itemFk
+ JOIN ticketState ts ON ts.ticketFk = t.id
+ JOIN state st ON st.id = ts.stateFk
+ JOIN agencyMode am ON t.agencyModeFk = am.id
+ LEFT JOIN itemPackingType ipt ON ipt.code = i.itemPackingTypeFk
+ WHERE t.shipped BETWEEN vDateToAdvance AND util.dayend(vDateToAdvance)
+ AND t.warehouseFk = vWarehouseFk
+ AND st.order <= 5
+ GROUP BY t.id
+ ) dest ON dest.addressFk = origin.addressFk
+ WHERE origin.hasStock != 0;
+
+ DROP TEMPORARY TABLE tmp.stock;
+END$$
+DELIMITER ;
+
diff --git a/db/changes/234004/.gitkeep b/db/changes/234004/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/db/changes/234004/00-workerTimeControl.sql b/db/changes/234004/00-workerTimeControl.sql
new file mode 100644
index 000000000..8f6263533
--- /dev/null
+++ b/db/changes/234004/00-workerTimeControl.sql
@@ -0,0 +1,41 @@
+UPDATE `vn`.`workerTimeControlConfig`
+ SET `timeToBreakTime` = 18000;
+
+ALTER TABLE `vn`.`workerTimeControlConfig`
+ DROP COLUMN IF EXISTS `maxTimeToBreak`;
+ALTER TABLE `vn`.`workerTimeControlConfig`
+ ADD COLUMN maxTimeToBreak INT DEFAULT 3600 NULL;
+
+ALTER TABLE `vn`.`workerTimeControlConfig`
+ DROP COLUMN IF EXISTS `maxWorkShortCycle`;
+
+ALTER TABLE `vn`.`workerTimeControlConfig`
+ ADD COLUMN `maxWorkShortCycle` INT(10) UNSIGNED DEFAULT 561600
+ COMMENT 'Máximo tiempo que un trabajador puede estar trabajando con el que adquirirá el derecho a un descanso semanal corto';
+
+ALTER TABLE `vn`.`workerTimeControlConfig`
+ DROP COLUMN IF EXISTS `maxWorkLongCycle`;
+
+ALTER TABLE `vn`.`workerTimeControlConfig`
+ ADD COLUMN `maxWorkLongCycle` INT(10) UNSIGNED DEFAULT 950400
+ COMMENT 'Máximo tiempo que un trabajador puede estar trabajando con el que adquirirá el derecho a un descanso semanal largo';
+
+CREATE TABLE IF NOT EXISTS `vn`.`workerTimeControlError` (
+ `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
+ `code` char(35) NOT NULL,
+ `description` varchar(255) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `code` (`code`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
+
+INSERT IGNORE INTO `vn`.`workerTimeControlError` (`code`, `description`)
+ VALUES
+ ('IS_NOT_ALLOWED_FUTURE', 'No se permite fichar a futuro'),
+ ('INACTIVE_BUSINESS', 'No hay un contrato en vigor'),
+ ('IS_NOT_ALLOWED_WORK', 'No está permitido trabajar'),
+ ('ODD_WORKERTIMECONTROL', 'Fichadas impares'),
+ ('DAY_MAX_TIME', 'Superado el tiempo máximo entre entrada y salida'),
+ ('BREAK_DAY', 'Descanso diario'),
+ ('BREAK_WEEK', 'Descanso semanal'),
+ ('WRONG_DIRECTION', 'Dirección incorrecta'),
+ ('UNDEFINED_ERROR', 'Error sin definir');
\ No newline at end of file
diff --git a/db/changes/234004/01-timeControl_calculate.sql b/db/changes/234004/01-timeControl_calculate.sql
new file mode 100644
index 000000000..93d88c047
--- /dev/null
+++ b/db/changes/234004/01-timeControl_calculate.sql
@@ -0,0 +1,194 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`timeControl_calculate`(
+ vDatedFrom DATETIME,
+ vDatedTo DATETIME)
+BEGIN
+/*
+ * Agrupa por trabajador y día, el tiempo de trabajo y descanso retribuido(si tiene).
+ * Los registros horarios incorrectos (tmp.timeControlError) no se considerarán.
+ * Si un trabajador ha trabajado más de un cierto umbral de tiempo (vTimeToBreakTime)
+ * y no ha tenido descansos que superen un parámetro determinado(vMaxTimeToBreak),
+ * se le añadirá un tiempo de descanso (vBreakTime) a sus horas trabajadas.
+ * El tiempo de descanso solo se añade si el trabajador realmente disfrutó del descanso.
+ * Si disfrutó de menos tiempo de descanso, solo se añade el tiempo que disfrutó.
+ *
+ * @param vDatedFrom
+ * @param vDatedTo
+ *
+ * @return tmp.timeControlCalculate
+ * (workerFk, dated, timeWorkSeconds, timeWorkSexagesimal, timeWorkDecimal, timed)
+ */
+ DECLARE vHourSeconds INTEGER;
+ DECLARE vDatedFromYesterday DATETIME;
+ DECLARE vDatedToTomorrow DATETIME;
+ DECLARE vTimeToBreakTime INT;
+ DECLARE vBreakTime INT;
+ DECLARE vMaxTimeToBreak INT;
+
+ SELECT DATE_SUB(vDatedFrom, INTERVAL 1 DAY), DATE_ADD(vDatedTo, INTERVAL 1 DAY)
+ INTO vDatedFromYesterday, vDatedToTomorrow;
+
+ SELECT timeToBreakTime, breakTime, maxTimeToBreak, TIME_TO_SEC('01:00:00')
+ INTO vTimeToBreakTime, vBreakTime, vMaxTimeToBreak, vHourSeconds
+ FROM workerTimeControlConfig
+ LIMIT 1;
+
+ CALL timeControl_getError(vDatedFromYesterday, vDatedToTomorrow);
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.workerTimeControl
+ (INDEX(userFk, timed), INDEX(timed), INDEX(direction))
+ ENGINE = MEMORY
+ SELECT wtc.userFk,
+ wtc.timed,
+ DATE(wtc.timed) dated,
+ wtc.direction,
+ TRUE isReal
+ FROM workerTimeControl wtc
+ JOIN tmp.`user` u ON u.userFk = wtc.userFk
+ LEFT JOIN (
+ SELECT wtc.userFk, MIN(wtc.timed) firstIn
+ FROM workerTimeControl wtc
+ JOIN tmp.`user` u ON u.userFk = wtc.userFk
+ LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
+ WHERE wtc.timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
+ AND wtc.direction = 'in'
+ AND tce.id IS NULL
+ GROUP BY userFk
+ ) fi ON wtc.userFk = fi.userFk
+ LEFT JOIN (
+ SELECT wtc.userFk, MAX(wtc.timed) lastOut
+ FROM workerTimeControl wtc
+ JOIN tmp.`user` u ON u.userFk = wtc.userFk
+ LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
+ WHERE wtc.timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
+ AND wtc.direction = 'out'
+ AND tce.id IS NULL
+ GROUP BY userFk
+ ) lo ON wtc.userFk = lo.userFk
+ LEFT JOIN tmp.timeControlError tce ON tce.id = wtc.id
+ WHERE wtc.timed BETWEEN fi.firstIn AND lo.lastOut
+ AND tce.id IS NULL
+ ORDER BY wtc.userFk, wtc.timed;
+
+ CREATE OR REPLACE TEMPORARY TABLE tmp.wtcToinsert
+ (INDEX(timed))
+ ENGINE = MEMORY
+ WITH wtc AS(
+ SELECT timed,
+ userFk,
+ dated,
+ direction,
+ LEAD(dated) OVER
+ (PARTITION BY userFk, dated ORDER BY timed) nextDay,
+ LEAD(userFk) OVER
+ (PARTITION BY userFk ORDER BY timed) nextUserFk,
+ ROW_NUMBER() OVER (ORDER BY userFk, timed) MOD 2 isOdd
+ FROM tmp.workerTimeControl
+ WHERE timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
+ ORDER BY userFk, timed
+ ), wtcToinsert AS(
+ SELECT userFk,
+ dated,
+ IF(userFk = nextUserFk
+ AND nextDay IS NULL
+ AND isOdd
+ AND direction <> 'out', TRUE, FALSE) outNextDay,
+ IF(userFk = nextUserFk
+ AND nextDay IS NULL
+ AND NOT isOdd
+ AND direction <> 'out', TRUE, FALSE) outNextDayWhitBreak
+ FROM wtc
+ HAVING outNextDay OR outNextDayWhitBreak
+ )SELECT userFk, util.dayEnd(dated) timed, 'out' direction
+ FROM wtcToinsert
+ WHERE outNextDay
+ UNION ALL
+ SELECT userFk, dated + INTERVAL 1 DAY, 'in'
+ FROM wtcToinsert
+ WHERE outNextDay
+ UNION ALL
+ SELECT userFk, util.dayEnd(dated) - INTERVAL 1 SECOND, 'middle'
+ FROM wtcToinsert
+ WHERE outNextDayWhitBreak
+ UNION ALL
+ SELECT userFk, util.dayEnd(dated), 'out'
+ FROM wtcToinsert
+ WHERE outNextDayWhitBreak
+ UNION ALL
+ SELECT userFk, dated + INTERVAL 1 DAY, 'in'
+ FROM wtcToinsert
+ WHERE outNextDayWhitBreak
+ UNION ALL
+ SELECT userFk, dated + INTERVAL 1 DAY + INTERVAL 1 SECOND, 'middle'
+ FROM wtcToinsert
+ WHERE outNextDayWhitBreak;
+
+ INSERT INTO tmp.workerTimeControl (userFk, timed, dated, direction, isReal)
+ SELECT userFk, timed, DATE(timed), direction, FALSE
+ FROM tmp.wtcToinsert;
+
+ SET @accumulatedForBreakTime = 0;
+ SET @oldrealDay = NULL;
+ CREATE OR REPLACE TEMPORARY TABLE tmp.timeControlCalculate
+ WITH workerTimed AS (
+ SELECT
+ userFk,
+ dated,
+ timed,
+ (direction ='in' AND isReal) breakPoint,
+ SUM(CASE WHEN (direction ='in' AND isReal) THEN TRUE ELSE FALSE END)
+ OVER (ORDER BY userFk, timed) AS realDay,
+ TIMESTAMPDIFF(SECOND, LAG(timed)
+ OVER (PARTITION BY userFk, dated ORDER BY timed), timed) gapTime,
+ ROW_NUMBER()
+ OVER (PARTITION BY userFk, dated ORDER BY timed) MOD 2 isOdd
+ FROM tmp.workerTimeControl
+ WHERE timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
+ ), accumulated AS (
+ SELECT SUM(IF(isOdd, 0, gapTime))
+ OVER (PARTITION BY userFk,dated ORDER BY userFk,timed) accumulatedWorkTime,
+ SUM(IF(NOT isOdd OR breakPoint, 0, IFNULL(gapTime, 0)))
+ OVER (PARTITION BY realDay ORDER BY realDay,timed) accumulatedBreakTime,
+ IF(realDay <> @oldrealDay OR (isOdd AND gapTime >= vMaxTimeToBreak),
+ @accumulatedForBreakTime := 0,
+ @accumulatedForBreakTime := @accumulatedForBreakTime +
+ IF(isOdd, 0, gapTime )) accumulatedForBreakTime,
+ @oldrealDay := realDay,
+ userFk,
+ dated,
+ realDay
+ FROM workerTimed
+ ), totalWorked AS (
+ SELECT userFk,
+ dated,
+ MAX(accumulatedWorkTime) +
+ IF(MAX(accumulatedForBreakTime) >= vTimeToBreakTime,
+ LEAST(vBreakTime, MAX(accumulatedBreakTime)),
+ 0) timeWorkSeconds
+ FROM accumulated
+ GROUP BY userFk, dated
+ )SELECT tw.userFk,
+ tw.dated,
+ timeWorkSeconds,
+ SEC_TO_TIME(timeWorkSeconds) timeWorkSexagesimal,
+ timeWorkSeconds / vHourSeconds timeWorkDecimal,
+ sub.tableTimed
+ FROM totalWorked tw
+ JOIN (
+ SELECT userFk,
+ dated,
+ GROUP_CONCAT(DATE_FORMAT(timed, "%H:%i") ORDER BY timed ASC
+ SEPARATOR ' - ')tableTimed
+ FROM tmp.workerTimeControl
+ WHERE timed BETWEEN vDatedFromYesterday AND vDatedToTomorrow
+ AND isReal
+ GROUP BY userFk, dated
+ )sub ON sub.dated = tw.dated
+ AND sub.userFk = tw.userFk
+ WHERE tw.dated BETWEEN vDatedFrom AND vDatedTo;
+
+ DROP TEMPORARY TABLE tmp.timeControlError;
+ DROP TEMPORARY TABLE tmp.wtcToinsert;
+ DROP TEMPORARY TABLE tmp.workerTimeControl;
+END$$
+DELIMITER ;
\ No newline at end of file
diff --git a/db/changes/234004/02-workerTimeControl_clockIn.sql b/db/changes/234004/02-workerTimeControl_clockIn.sql
new file mode 100644
index 000000000..69091e51c
--- /dev/null
+++ b/db/changes/234004/02-workerTimeControl_clockIn.sql
@@ -0,0 +1,286 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`workerTimeControl_clockIn`(
+ vWorkerFk INT,
+ vTimed DATETIME,
+ vDirection VARCHAR(10)
+)
+BEGIN
+/**
+ * Verifica si el empleado puede fichar
+ * @param vWorkerFk Identificador del trabajador
+ * @param vTimed valor de la fichada, IF vTimed IS NULL vTimed = NOW
+ * @param vDirection solo se pueden pasa los valores del campo
+ * workerTimeControl.direction ENUM('in', 'out', 'middle')
+ * @return Si todo es correcto, retorna el número de id la tabla workerTimeControl.
+ * Si hay algún problema, devuelve el mesaje que se debe mostrar al usuario
+ * Solo retorna el primer problema, en caso de no ocurrir ningún error se añadirá
+ * fichada a la tabla vn.workerTimeControl
+ */
+
+ DECLARE vLastIn DATETIME;
+ DECLARE vLastOut DATETIME;
+ DECLARE vNextIn DATETIME;
+ DECLARE vNextOut DATETIME;
+ DECLARE vNextDirection ENUM('in', 'out');
+ DECLARE vLastDirection ENUM('in', 'out');
+ DECLARE vDayMaxTime INTEGER;
+ DECLARE vDayBreak INT;
+ DECLARE vShortWeekBreak INT;
+ DECLARE vLongWeekBreak INT;
+ DECLARE vWeekScope INT;
+ DECLARE vMailTo VARCHAR(50) DEFAULT NULL;
+ DECLARE vUserName VARCHAR(50) DEFAULT NULL;
+ DECLARE vIsError BOOLEAN DEFAULT FALSE;
+ DECLARE vErrorMessage VARCHAR(255) DEFAULT NULL;
+ DECLARE vErrorCode VARCHAR(50);
+ DECLARE vDated DATE;
+ DECLARE vIsAllowedToWork VARCHAR(50);
+ DECLARE vIsManual BOOLEAN DEFAULT TRUE;
+ DECLARE vMaxWorkShortCycle INT;
+ DECLARE vMaxWorkLongCycle INT;
+
+ DECLARE EXIT HANDLER FOR SQLSTATE '45000'
+ BEGIN
+
+ SELECT CONCAT(u.name, '@verdnatura.es'),
+ CONCAT(w.firstName, ' ', w.lastName)
+ INTO vMailTo, vUserName
+ FROM account.user u
+ JOIN worker w ON w.bossFk = u.id
+ WHERE w.id = vWorkerFk;
+
+ SELECT `description` INTO vErrorMessage
+ FROM workerTimeControlError
+ WHERE `code` = vErrorCode;
+
+ IF vErrorMessage IS NULL THEN
+ SET vErrorMessage = 'Error sin definir';
+ END IF;
+
+ SELECT vErrorMessage `error`;
+ SELECT CONCAT(vUserName,
+ ' no ha podido fichar por el siguiente problema: ',
+ vErrorMessage)
+ INTO vErrorMessage;
+
+ CALL mail_insert( vMailTo, vMailTo, 'Error al fichar', vErrorMessage);
+ END;
+
+ IF (vTimed IS NULL) THEN
+ SET vTimed = util.VN_NOW();
+ SET vIsManual = FALSE;
+ END IF;
+
+ SET vDated = DATE(vTimed);
+
+ SELECT IF(pc.name = 'Conductor +3500kg',
+ wc.dayBreakDriver,
+ wc.dayBreak),
+ wc.shortWeekBreak,
+ wc.longWeekBreak,
+ wc.weekScope,
+ wc.dayMaxTime,
+ wc.maxWorkShortCycle,
+ wc.maxWorkLongCycle
+ INTO vDayBreak,
+ vShortWeekBreak,
+ vLongWeekBreak,
+ vWeekScope,
+ vDayMaxTime,
+ vMaxWorkShortCycle,
+ vMaxWorkLongCycle
+ FROM business b
+ JOIN professionalCategory pc
+ ON pc.id = b.workerBusinessProfessionalCategoryFk
+ JOIN workerTimeControlConfig wc
+ WHERE b.workerFk = vWorkerFk
+ AND vDated BETWEEN b.started AND IFNULL(b.ended, vDated);
+
+ -- CONTRATO EN VIGOR
+ IF vDayBreak IS NULL THEN
+ SET vErrorCode = 'INACTIVE_BUSINESS';
+ CALL util.throw(vErrorCode);
+ END IF;
+
+ -- FICHADAS A FUTURO
+ IF vTimed > util.VN_NOW() + INTERVAL 1 MINUTE THEN
+ SET vErrorCode = 'IS_NOT_ALLOWED_FUTURE';
+ CALL util.throw(vErrorCode);
+ END IF;
+
+ -- VERIFICAR SI ESTÁ PERMITIDO TRABAJAR
+ CALL timeBusiness_calculateByWorker(vWorkerFk, vDated, vDated);
+ SELECT isAllowedToWork INTO vIsAllowedToWork
+ FROM tmp.timeBusinessCalculate;
+ DROP TEMPORARY TABLE tmp.timeBusinessCalculate;
+
+ IF NOT vIsAllowedToWork THEN
+ SET vErrorCode = 'IS_NOT_ALLOWED_WORK';
+ CALL util.throw(vErrorCode);
+ END IF;
+
+ -- DIRECCION CORRECTA
+ CALL workerTimeControl_direction(vWorkerFk, vTimed);
+ IF (SELECT
+ IF(IF(option1 IN ('inMiddle', 'outMiddle'),
+ 'middle',
+ option1) <> vDirection
+ AND IF(option2 IN ('inMiddle', 'outMiddle'),
+ 'middle',
+ IFNULL(option2, '')) <> vDirection,
+ TRUE ,
+ FALSE)
+ FROM tmp.workerTimeControlDirection
+ ) THEN
+ SET vIsError = TRUE;
+ END IF;
+
+ DROP TEMPORARY TABLE tmp.workerTimeControlDirection;
+ IF vIsError THEN
+ SET vErrorCode = 'WRONG_DIRECTION';
+ CALL util.throw(vErrorCode);
+ END IF;
+
+ -- FICHADAS IMPARES
+ SELECT timed INTO vLastIn
+ FROM workerTimeControl
+ WHERE userFk = vWorkerFk
+ AND direction = 'in'
+ AND timed < vTimed
+ ORDER BY timed DESC
+ LIMIT 1;
+
+ IF (SELECT IF(vDirection = 'in',
+ MOD(COUNT(*), 2) ,
+ IF (vDirection = 'out', NOT MOD(COUNT(*), 2), FALSE))
+ FROM workerTimeControl
+ WHERE userFk = vWorkerFk
+ AND timed BETWEEN vLastIn AND vTimed
+ ) THEN
+ SET vErrorCode = 'ODD_WORKERTIMECONTROL';
+ CALL util.throw(vErrorCode);
+ END IF;
+
+ -- DESCANSO DIARIO
+ SELECT timed INTO vLastOut
+ FROM workerTimeControl
+ WHERE userFk = vWorkerFk
+ AND direction = 'out'
+ AND timed < vTimed
+ ORDER BY timed DESC
+ LIMIT 1;
+
+ SELECT timed INTO vNextIn
+ FROM workerTimeControl
+ WHERE userFk = vWorkerFk
+ AND direction = 'in'
+ AND timed > vTimed
+ ORDER BY timed ASC
+ LIMIT 1;
+
+ CASE vDirection
+ WHEN 'in' THEN
+ IF UNIX_TIMESTAMP(vTimed) - UNIX_TIMESTAMP(vLastOut) <= vDayBreak THEN
+ SET vIsError = TRUE;
+ END IF;
+ WHEN 'out' THEN
+ IF UNIX_TIMESTAMP(vNextIn) - UNIX_TIMESTAMP(vTimed) <= vDayBreak THEN
+ SET vIsError = TRUE;
+ END IF;
+ ELSE BEGIN END;
+ END CASE;
+
+ IF vIsError THEN
+ SET vErrorCode = 'BREAK_DAY';
+ CALL util.throw(vErrorCode);
+ END IF;
+
+
+
+ IF (vDirection IN('in', 'out')) THEN
+ -- VERIFICA MAXIMO TIEMPO DESDE ENTRADA HASTA LA SALIDA
+
+ SELECT timed INTO vNextOut
+ FROM workerTimeControl
+ WHERE userFk = vWorkerFk
+ AND direction = 'out'
+ AND timed > vTimed
+ ORDER BY timed ASC
+ LIMIT 1;
+
+ SELECT direction INTO vNextDirection
+ FROM workerTimeControl
+ WHERE userFk = vWorkerFk
+ AND direction IN('in','out')
+ AND timed > vTimed
+ ORDER BY timed ASC
+ LIMIT 1;
+
+ SELECT direction INTO vLastDirection
+ FROM workerTimeControl
+ WHERE userFk = vWorkerFk
+ AND direction IN('in', 'out')
+ AND timed < vTimed
+ ORDER BY timed ASC
+ LIMIT 1;
+
+ IF (vDirection ='in'
+ AND vNextDirection = 'out'
+ AND UNIX_TIMESTAMP(vNextOut) - UNIX_TIMESTAMP(vTimed) > vDayMaxTime) OR
+ (vDirection ='out'
+ AND vLastDirection = 'in'
+ AND UNIX_TIMESTAMP(vTimed) -UNIX_TIMESTAMP(vLastIn) > vDayMaxTime) THEN
+ SET vErrorCode = 'DAY_MAX_TIME';
+ CALL util.throw(vErrorCode);
+ END IF;
+
+ -- VERIFICA DESCANSO SEMANAL
+
+ WITH wtc AS(
+ (SELECT timed
+ FROM vn.workerTimeControl
+ WHERE userFk = vWorkerFk
+ AND direction IN ('in', 'out')
+ AND timed BETWEEN vTimed - INTERVAL (vWeekScope * 2) SECOND
+ AND vTimed + INTERVAL (vWeekScope * 2) SECOND )
+ UNION
+ (SELECT vTimed)
+ ), wtcGap AS(
+ SELECT timed,
+ TIMESTAMPDIFF(SECOND, LAG(timed) OVER (ORDER BY timed), timed) gap
+ FROM wtc
+ ORDER BY timed
+ ), wtcBreak AS(
+ SELECT timed,
+ IF(IFNULL(gap, 0) > vShortWeekBreak, TRUE, FALSE) hasShortBreak,
+ IF(IFNULL(gap, 0) > vLongWeekBreak, TRUE, FALSE) hasLongBreak
+ FROM wtcGap
+ ORDER BY timed
+ ), wtcBreakCounter AS(
+ SELECT timed,
+ SUM(hasShortBreak) OVER (ORDER BY timed) breakCounter ,
+ LEAD(hasLongBreak) OVER (ORDER BY timed) nextHasLongBreak
+ FROM wtcBreak
+ )SELECT TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed)) > vMaxWorkLongCycle OR
+ (TIMESTAMPDIFF(SECOND, MIN(timed), MAX(timed))> vMaxWorkShortCycle
+ AND NOT SUM(IFNULL(nextHasLongBreak, 1)))
+ hasError INTO vIsError
+ FROM wtcBreakCounter
+ GROUP BY breakCounter
+ HAVING hasError
+ LIMIT 1;
+
+ IF vIsError THEN
+ SET vErrorCode = 'BREAK_WEEK';
+ CALL util.throw(vErrorCode);
+ END IF;
+ END IF;
+
+ -- SE PERMITE FICHAR
+ INSERT INTO workerTimeControl(userFk, timed, direction, `manual`)
+ VALUES(vWorkerFk, vTimed, vDirection, vIsManual);
+
+ SELECT LAST_INSERT_ID() id;
+
+END$$
+DELIMITER ;
\ No newline at end of file
diff --git a/db/changes/234201/00-ACL_workerDepartment.sql b/db/changes/234201/00-ACL_workerDepartment.sql
new file mode 100644
index 000000000..ceb8d5d75
--- /dev/null
+++ b/db/changes/234201/00-ACL_workerDepartment.sql
@@ -0,0 +1,3 @@
+INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
+ VALUES
+ ('WorkerDepartment', '*', '*', 'ALLOW', 'ROLE', 'employee');
diff --git a/db/changes/234201/00-account_acl.sql b/db/changes/234201/00-account_acl.sql
new file mode 100644
index 000000000..8dfe1d1ec
--- /dev/null
+++ b/db/changes/234201/00-account_acl.sql
@@ -0,0 +1,7 @@
+INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
+ VALUES
+ ('VnUser', 'higherPrivileges', '*', 'ALLOW', 'ROLE', 'itManagement'),
+ ('VnUser', 'mediumPrivileges', '*', 'ALLOW', 'ROLE', 'hr'),
+ ('VnUser', 'updateUser', '*', 'ALLOW', 'ROLE', 'employee');
+
+ALTER TABLE `account`.`user` ADD `username` varchar(30) AS (name) VIRTUAL;
diff --git a/db/changes/234201/00-aclClient.sql b/db/changes/234201/00-aclClient.sql
new file mode 100644
index 000000000..3df9522cf
--- /dev/null
+++ b/db/changes/234201/00-aclClient.sql
@@ -0,0 +1,3 @@
+
+INSERT INTO `salix`.`ACL` ( model, property, accessType, permission, principalType, principalId)
+ VALUES('TicketCollection', '*', 'WRITE', 'ALLOW', 'ROLE', 'production');
diff --git a/db/changes/234201/00-aclSetPassword.sql b/db/changes/234201/00-aclSetPassword.sql
new file mode 100644
index 000000000..44b3e9de0
--- /dev/null
+++ b/db/changes/234201/00-aclSetPassword.sql
@@ -0,0 +1,4 @@
+INSERT INTO `salix`.`ACL` (model,property,accessType,permission,principalType,principalId)
+ VALUES ('Worker','setPassword','*','ALLOW','ROLE','employee');
+
+
diff --git a/db/changes/234201/00-aclUrlHedera.sql b/db/changes/234201/00-aclUrlHedera.sql
new file mode 100644
index 000000000..79d9fb4c8
--- /dev/null
+++ b/db/changes/234201/00-aclUrlHedera.sql
@@ -0,0 +1,7 @@
+INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
+ VALUES
+ ('hedera', 'test', 'https://test-shop.verdnatura.es/'),
+ ('hedera', 'production', 'https://shop.verdnatura.es/');
+
+INSERT INTO `salix`.`ACL` ( model, property, accessType, permission, principalType, principalId)
+ VALUES('Url', 'getByUser', 'READ', 'ALLOW', 'ROLE', '$everyone');
diff --git a/db/changes/234201/00-dropWorkerCreate.sql b/db/changes/234201/00-dropWorkerCreate.sql
new file mode 100644
index 000000000..b903909b6
--- /dev/null
+++ b/db/changes/234201/00-dropWorkerCreate.sql
@@ -0,0 +1 @@
+DROP PROCEDURE IF EXISTS `vn`.`workerCreate`;
diff --git a/db/changes/234201/00-packagingFk.sql b/db/changes/234201/00-packagingFk.sql
new file mode 100644
index 000000000..9a775ada9
--- /dev/null
+++ b/db/changes/234201/00-packagingFk.sql
@@ -0,0 +1,5 @@
+ALTER TABLE `vn`.`buy` CHANGE `packageFk` `packagingFk` varchar(10)
+CHARACTER SET utf8mb3 COLLATE utf8mb3_unicode_ci DEFAULT '--' NULL;
+
+ALTER TABLE `vn`.`buy`
+ADD COLUMN `packageFk` varchar(10) AS (`packagingFk`) VIRTUAL;
\ No newline at end of file
diff --git a/db/changes/234201/00-packagingFkviews.sql b/db/changes/234201/00-packagingFkviews.sql
new file mode 100644
index 000000000..49d41c26c
--- /dev/null
+++ b/db/changes/234201/00-packagingFkviews.sql
@@ -0,0 +1,150 @@
+CREATE SCHEMA IF NOT EXISTS `vn2008`;
+
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `vn`.`awbVolume`
+AS SELECT `d`.`awbFk` AS `awbFk`,
+ `b`.`stickers` * `i`.`density` * IF(
+ `p`.`volume` > 0,
+ `p`.`volume`,
+ `p`.`width` * `p`.`depth` * IF(`p`.`height` = 0, `i`.`size` + 10, `p`.`height`)
+ ) / (`vc`.`aerealVolumetricDensity` * 1000) AS `volume`,
+ `b`.`id` AS `buyFk`
+FROM (
+ (
+ (
+ (
+ (
+ (
+ (
+ (
+ `vn`.`buy` `b`
+ JOIN `vn`.`item` `i` ON(`b`.`itemFk` = `i`.`id`)
+ )
+ JOIN `vn`.`itemType` `it` ON(`i`.`typeFk` = `it`.`id`)
+ )
+ JOIN `vn`.`packaging` `p` ON(`p`.`id` = `b`.`packagingFk`)
+ )
+ JOIN `vn`.`entry` `e` ON(`b`.`entryFk` = `e`.`id`)
+ )
+ JOIN `vn`.`travel` `t` ON(`t`.`id` = `e`.`travelFk`)
+ )
+ JOIN `vn`.`duaEntry` `de` ON(`de`.`entryFk` = `e`.`id`)
+ )
+ JOIN `vn`.`dua` `d` ON(`d`.`id` = `de`.`duaFk`)
+ )
+ JOIN `vn`.`volumeConfig` `vc`
+ )
+WHERE `t`.`shipped` > makedate(year(`util`.`VN_CURDATE`()) - 1, 1);
+
+
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `vn2008`.`Compres`
+AS SELECT `c`.`id` AS `Id_Compra`,
+ `c`.`entryFk` AS `Id_Entrada`,
+ `c`.`itemFk` AS `Id_Article`,
+ `c`.`buyingValue` AS `Costefijo`,
+ `c`.`quantity` AS `Cantidad`,
+ `c`.`packagingFk` AS `Id_Cubo`,
+ `c`.`stickers` AS `Etiquetas`,
+ `c`.`freightValue` AS `Portefijo`,
+ `c`.`packageValue` AS `Embalajefijo`,
+ `c`.`comissionValue` AS `Comisionfija`,
+ `c`.`packing` AS `Packing`,
+ `c`.`grouping` AS `grouping`,
+ `c`.`groupingMode` AS `caja`,
+ `c`.`location` AS `Nicho`,
+ `c`.`price1` AS `Tarifa1`,
+ `c`.`price2` AS `Tarifa2`,
+ `c`.`price3` AS `Tarifa3`,
+ `c`.`minPrice` AS `PVP`,
+ `c`.`printedStickers` AS `Vida`,
+ `c`.`isChecked` AS `punteo`,
+ `c`.`ektFk` AS `buy_edi_id`,
+ `c`.`created` AS `odbc_date`,
+ `c`.`isIgnored` AS `Novincular`,
+ `c`.`isPickedOff` AS `isPickedOff`,
+ `c`.`workerFk` AS `Id_Trabajador`,
+ `c`.`weight` AS `weight`,
+ `c`.`dispatched` AS `dispatched`,
+ `c`.`containerFk` AS `container_id`,
+ `c`.`itemOriginalFk` AS `itemOriginalFk`
+FROM `vn`.`buy` `c`;
+
+CREATE OR REPLACE DEFINER=`root`@`localhost`
+ SQL SECURITY DEFINER
+ VIEW `vn2008`.`buySource`
+AS SELECT `b`.`entryFk` AS `Id_Entrada`,
+ `b`.`isPickedOff` AS `isPickedOff`,
+ NULL AS `tarifa0`,
+ `e`.`kop` AS `kop`,
+ `b`.`id` AS `Id_Compra`,
+ `i`.`typeFk` AS `tipo_id`,
+ `b`.`itemFk` AS `Id_Article`,
+ `i`.`size` AS `Medida`,
+ `i`.`stems` AS `Tallos`,
+ `b`.`stickers` AS `Etiquetas`,
+ `b`.`packagingFk` AS `Id_Cubo`,
+ `b`.`buyingValue` AS `Costefijo`,
+ `b`.`packing` AS `Packing`,
+ `b`.`grouping` AS `Grouping`,
+ `b`.`quantity` AS `Cantidad`,
+ `b`.`price2` AS `Tarifa2`,
+ `b`.`price3` AS `Tarifa3`,
+ `b`.`isChecked` AS `Punteo`,
+ `b`.`groupingMode` AS `Caja`,
+ `i`.`isToPrint` AS `Imprimir`,
+ `i`.`name` AS `Article`,
+ `vn`.`ink`.`picture` AS `Tinta`,
+ `i`.`originFk` AS `id_origen`,
+ `i`.`minPrice` AS `PVP`,
+ NULL AS `Id_Accion`,
+ `s`.`company_name` AS `pro`,
+ `i`.`hasMinPrice` AS `Min`,
+ `b`.`isIgnored` AS `Novincular`,
+ `b`.`freightValue` AS `Portefijo`,
+ round(`b`.`buyingValue` * `b`.`quantity`, 2) AS `Importe`,
+ `b`.`printedStickers` AS `Vida`,
+ `i`.`comment` AS `reference`,
+ `b`.`workerFk` AS `Id_Trabajador`,
+ `e`.`s1` AS `S1`,
+ `e`.`s2` AS `S2`,
+ `e`.`s3` AS `S3`,
+ `e`.`s4` AS `S4`,
+ `e`.`s5` AS `S5`,
+ `e`.`s6` AS `S6`,
+ 0 AS `price_fixed`,
+ `i`.`producerFk` AS `producer_id`,
+ `i`.`subName` AS `tag1`,
+ `i`.`value5` AS `tag2`,
+ `i`.`value6` AS `tag3`,
+ `i`.`value7` AS `tag4`,
+ `i`.`value8` AS `tag5`,
+ `i`.`value9` AS `tag6`,
+ `s`.`company_name` AS `company_name`,
+ `b`.`weight` AS `weightPacking`,
+ `i`.`packingOut` AS `packingOut`,
+ `b`.`itemOriginalFk` AS `itemOriginalFk`,
+ `io`.`longName` AS `itemOriginalName`,
+ `it`.`gramsMax` AS `gramsMax`
+FROM (
+ (
+ (
+ (
+ (
+ (
+ `vn`.`item` `i`
+ JOIN `vn`.`itemType` `it` ON(`it`.`id` = `i`.`typeFk`)
+ )
+ LEFT JOIN `vn`.`ink` ON(`vn`.`ink`.`id` = `i`.`inkFk`)
+ )
+ LEFT JOIN `vn`.`buy` `b` ON(`b`.`itemFk` = `i`.`id`)
+ )
+ LEFT JOIN `vn`.`item` `io` ON(`io`.`id` = `b`.`itemOriginalFk`)
+ )
+ LEFT JOIN `edi`.`ekt` `e` ON(`e`.`id` = `b`.`ektFk`)
+ )
+ LEFT JOIN `edi`.`supplier` `s` ON(`e`.`pro` = `s`.`supplier_id`)
+ );
+
diff --git a/db/changes/234201/00-zoneIncluded.sql b/db/changes/234201/00-zoneIncluded.sql
new file mode 100644
index 000000000..12d4058cf
--- /dev/null
+++ b/db/changes/234201/00-zoneIncluded.sql
@@ -0,0 +1,26 @@
+ALTER TABLE `vn`.`zoneIncluded`
+ ADD COLUMN `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
+ DROP PRIMARY KEY,
+ DROP FOREIGN KEY `zoneFk2`,
+ DROP FOREIGN KEY `zoneGeoFk2`,
+ DROP KEY `geoFk_idx`,
+ ADD PRIMARY KEY (`id`),
+ ADD CONSTRAINT `zoneIncluded_FK_1` FOREIGN KEY (zoneFk) REFERENCES `vn`.`zone`(id) ON DELETE CASCADE ON UPDATE CASCADE,
+ ADD CONSTRAINT `zoneIncluded_FK_2` FOREIGN KEY (geoFk) REFERENCES `vn`.`zoneGeo`(id) ON DELETE CASCADE ON UPDATE CASCADE,
+ ADD CONSTRAINT `unique_zone_geo` UNIQUE (`zoneFk`, `geoFk`);
+
+DROP TRIGGER IF EXISTS `vn`.`zoneIncluded_afterDelete`;
+USE `vn`;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`zoneIncluded_afterDelete`
+ AFTER DELETE ON `zoneIncluded`
+ FOR EACH ROW
+BEGIN
+ INSERT INTO zoneLog
+ SET `action` = 'delete',
+ `changedModel` = 'zoneIncluded',
+ `changedModelId` = OLD.zoneFk,
+ `userFk` = account.myUser_getId();
+END$$
+DELIMITER ;
diff --git a/db/changes/234201/02-packagingFktrigger.sql b/db/changes/234201/02-packagingFktrigger.sql
new file mode 100644
index 000000000..4edcd3f3e
--- /dev/null
+++ b/db/changes/234201/02-packagingFktrigger.sql
@@ -0,0 +1,57 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`buy_afterUpdate`
+ AFTER UPDATE ON `buy`
+ FOR EACH ROW
+trig: BEGIN
+ DECLARE vLanded DATE;
+ DECLARE vBuyerFk INT;
+ DECLARE vIsBuyerToBeEmailed BOOL;
+ DECLARE vItemName VARCHAR(50);
+
+ IF @isModeInventory OR @isTriggerDisabled THEN
+ LEAVE trig;
+ END IF;
+
+ IF !(NEW.id <=> OLD.id)
+ OR !(NEW.entryFk <=> OLD.entryFk)
+ OR !(NEW.itemFk <=> OLD.itemFk)
+ OR !(NEW.quantity <=> OLD.quantity)
+ OR !(NEW.created <=> OLD.created) THEN
+ CALL stock.log_add('buy', NEW.id, OLD.id);
+ END IF;
+
+ CALL buy_afterUpsert(NEW.id);
+
+ SELECT w.isBuyerToBeEmailed, t.landed
+ INTO vIsBuyerToBeEmailed, vLanded
+ FROM entry e
+ JOIN travel t ON t.id = e.travelFk
+ JOIN warehouse w ON w.id = t.warehouseInFk
+ WHERE e.id = NEW.entryFk;
+
+ SELECT it.workerFk, i.longName
+ INTO vBuyerFk, vItemName
+ FROM itemCategory k
+ JOIN itemType it ON it.categoryFk = k.id
+ JOIN item i ON i.typeFk = it.id
+ WHERE i.id = OLD.itemFk;
+
+ IF vIsBuyerToBeEmailed AND
+ vBuyerFk != account.myUser_getId() AND
+ vLanded = util.VN_CURDATE() THEN
+ IF !(NEW.itemFk <=> OLD.itemFk) OR
+ !(NEW.quantity <=> OLD.quantity) OR
+ !(NEW.packing <=> OLD.packing) OR
+ !(NEW.grouping <=> OLD.grouping) OR
+ !(NEW.packagingFk <=> OLD.packagingFk) OR
+ !(NEW.weight <=> OLD.weight) THEN
+ CALL vn.mail_insert(
+ CONCAT(account.user_getNameFromId(vBuyerFk),'@verdnatura.es'),
+ CONCAT(account.myUser_getName(),'@verdnatura.es'),
+ CONCAT('E ', NEW.entryFk ,' Se ha modificado item ', NEW.itemFk, ' ', vItemName),
+ 'Este email se ha generado automáticamente'
+ );
+ END IF;
+ END IF;
+END$$
+DELIMITER ;
\ No newline at end of file
diff --git a/db/changes/234201/03-packagingFkProc.sql b/db/changes/234201/03-packagingFkProc.sql
new file mode 100644
index 000000000..4876c270e
--- /dev/null
+++ b/db/changes/234201/03-packagingFkProc.sql
@@ -0,0 +1,1381 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`travelVolume`(vTravelFk INT)
+BEGIN
+
+ SELECT w1.name AS ORI,
+ w2.name AS DES,
+ tr.shipped shipment,
+ tr.landed landing,
+ a.name Agencia,
+ s.name Proveedor,
+ e.id Id_Entrada,
+ e.invoiceNumber Referencia,
+ CAST(ROUND(SUM(GREATEST(b.stickers ,b.quantity /b.packing ) *
+ vn.item_getVolume(b.itemFk ,b.packagingFk)) / vc.trolleyM3 / 1000000 ,1) AS DECIMAL(10,2)) AS CC,
+ CAST(ROUND(SUM(GREATEST(b.stickers ,b.quantity /b.packing ) *
+ vn.item_getVolume(b.itemFk ,b.packagingFk)) / vc.palletM3 / 1000000,1) AS DECIMAL(10,2)) AS espais
+ FROM vn.buy b
+ JOIN vn.entry e ON e.id = b.entryFk
+ JOIN vn.supplier s ON s.id = e.supplierFk
+ JOIN vn.travel tr ON tr.id = e.travelFk
+ JOIN vn.agencyMode a ON a.id = tr.agencyModeFk
+ JOIN vn.warehouse w1 ON w1.id = tr.warehouseInFk
+ JOIN vn.warehouse w2 ON w2.id = tr.warehouseOutFk
+ JOIN vn.volumeConfig vc
+ JOIN vn.item i ON i.id = b.itemFk
+ JOIN vn.itemType it ON it.id = i.typeFk
+ WHERE tr.id = vTravelFk;
+
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`travelVolume_get`(vFromDated DATE, vToDated DATE, vWarehouseFk INT)
+BEGIN
+ SELECT tr.landed Fecha,
+ a.name Agencia,
+ count(DISTINCT e.id) numEntradas,
+ FLOOR(sum(item_getVolume(b.itemFk, b.packagingFk) * b.stickers / 1000000 )) AS m3
+ FROM vn.travel tr
+ JOIN vn.agencyMode a ON a.id = tr.agencyModeFk
+ JOIN vn.entry e ON e.travelFk = tr.id
+ JOIN vn.buy b ON b.entryFk = e.id
+ WHERE tr.landed BETWEEN vFromDated AND vToDated
+ AND e.isRaid = FALSE
+ AND tr.warehouseInFk = vWarehouseFk
+ GROUP BY tr.landed , a.name ;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`travel_getEntriesMissingPackage`(vSelf INT)
+BEGIN
+ DECLARE vpackageOrPackingNull INT;
+ DECLARE vTravelFk INT;
+
+ SELECT travelfk INTO vTravelFk
+ FROM entry
+ WHERE id = vSelf;
+
+ SELECT e.id entryFk
+ FROM travel t
+ JOIN entry e ON e.travelFk = t.id
+ JOIN buy b ON b.entryFk = e.id
+ WHERE t.id = vTravelFk
+ AND (b.packing IS NULL OR b.packagingFk IS NULL);
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticketBoxesView`(IN vTicketFk INT)
+BEGIN
+
+ SELECT s.id,
+ s.itemFk,
+ s.concept,
+ floor(s.quantity / b.packing) as Cajas,
+ b.packing,
+ s.isPicked,
+ i.size
+ FROM ticket t
+ JOIN sale s ON s.ticketFk = t.id
+ JOIN item i ON i.id = s.itemFk
+ JOIN cache.last_buy lb on lb.warehouse_id = t.warehouseFk AND lb.item_id = s.itemFk
+ JOIN buy b on b.id = lb.buy_id
+ JOIN packaging p on p.id = b.packagingFk
+ WHERE s.quantity >= b.packing
+ AND t.id = vTicketFk
+ AND p.isBox
+ GROUP BY s.itemFk;
+
+
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`stockBuyedByWorker`(
+ vDated DATE,
+ vWorker INT
+)
+BEGIN
+/**
+ * Inserta el volumen de compra de un comprador
+ * en stockBuyed de acuerdo con la fecha.
+ *
+ * @param vDated Fecha de compra
+ * @param vWorker Id de trabajador
+ */
+ CREATE OR REPLACE TEMPORARY TABLE tStockBuyed
+ (INDEX (userFk))
+ ENGINE = MEMORY
+ SELECT requested, reserved, userFk
+ FROM stockBuyed
+ WHERE dated = vDated
+ AND userFk = vWorker;
+
+ DELETE FROM stockBuyed
+ WHERE dated = vDated
+ AND userFk = vWorker;
+
+ CALL stockTraslation(vDated);
+
+ INSERT INTO stockBuyed(userFk, buyed, `dated`, reserved, requested, description)
+ SELECT it.workerFk,
+ SUM((ti.quantity / b.packing) * buy_getVolume(b.id)) / vc.palletM3 / 1000000,
+ vDated,
+ sb.reserved,
+ sb.requested,
+ u.name
+ FROM itemType it
+ JOIN item i ON i.typeFk = it.id
+ LEFT JOIN tmp.item ti ON ti.itemFk = i.id
+ JOIN itemCategory ic ON ic.id = it.categoryFk
+ JOIN warehouse wh ON wh.code = 'VNH'
+ JOIN tmp.buyUltimate bu ON bu.itemFk = i.id
+ AND bu.warehouseFk = wh.id
+ JOIN buy b ON b.id = bu.buyFk
+ JOIN volumeConfig vc
+ JOIN account.`user` u ON u.id = it.workerFk
+ LEFT JOIN tStockBuyed sb ON sb.userFk = it.workerFk
+ WHERE ic.display
+ AND it.workerFk = vWorker;
+
+ SELECT b.entryFk Id_Entrada,
+ i.id Id_Article,
+ i.name Article,
+ ti.quantity Cantidad,
+ (ac.conversionCoefficient * (ti.quantity / b.packing) * buy_getVolume(b.id))
+ / (vc.trolleyM3 * 1000000) buyed,
+ b.packagingFk id_cubo,
+ b.packing
+ FROM tmp.item ti
+ JOIN item i ON i.id = ti.itemFk
+ JOIN itemType it ON i.typeFk = it.id
+ JOIN itemCategory ic ON ic.id = it.categoryFk
+ JOIN worker w ON w.id = it.workerFk
+ JOIN auctionConfig ac
+ JOIN tmp.buyUltimate bu ON bu.itemFk = i.id
+ AND bu.warehouseFk = ac.warehouseFk
+ JOIN buy b ON b.id = bu.buyFk
+ JOIN volumeConfig vc
+ WHERE ic.display
+ AND w.id = vWorker;
+
+ DROP TEMPORARY TABLE tmp.buyUltimate,
+ tmp.item,
+ tStockBuyed;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemShelvingMakeFromDate`(IN `vShelvingFk` VARCHAR(8), IN `vBarcode` VARCHAR(22), IN `vQuantity` INT, IN `vPackagingFk` VARCHAR(10), IN `vGrouping` INT, IN `vPacking` INT, IN `vWarehouseFk` INT, `vCreated` VARCHAR(22))
+BEGIN
+
+ DECLARE vItemFk INT;
+
+ SELECT vn.barcodeToItem(vBarcode) INTO vItemFk;
+
+ SELECT itemFk INTO vItemFk
+ FROM vn.buy b
+ WHERE b.id = vItemFk;
+
+ IF (SELECT COUNT(*) FROM vn.shelving WHERE code = vShelvingFk COLLATE utf8_unicode_ci) = 0 THEN
+
+ INSERT IGNORE INTO vn.parking(`code`) VALUES(vShelvingFk);
+ INSERT INTO vn.shelving(`code`, parkingFk)
+ SELECT vShelvingFk, id
+ FROM vn.parking
+ WHERE `code` = vShelvingFk COLLATE utf8_unicode_ci;
+
+ END IF;
+
+ IF (SELECT COUNT(*) FROM vn.itemShelving
+ WHERE shelvingFk COLLATE utf8_unicode_ci = vShelvingFk
+ AND itemFk = vItemFk
+ AND packing = vPacking) = 1 THEN
+
+ UPDATE vn.itemShelving
+ SET visible = visible+vQuantity,
+ created = vCreated
+ WHERE shelvingFk COLLATE utf8_unicode_ci = vShelvingFk
+ AND itemFk = vItemFk
+ AND packing = vPacking;
+
+ ELSE
+ CALL cache.last_buy_refresh(FALSE);
+ INSERT INTO itemShelving( itemFk,
+ shelvingFk,
+ visible,
+ created,
+ `grouping`,
+ packing,
+ packagingFk)
+ SELECT vItemFk,
+ vShelvingFk,
+ vQuantity,
+ vCreated,
+ IF(vGrouping = 0, IFNULL(b.packing, vPacking), vGrouping) `grouping`,
+ IF(vPacking = 0, b.packing, vPacking) packing,
+ IF(vPackagingFk = '', b.packagingFk, vPackagingFk) packaging
+ FROM vn.item i
+ LEFT JOIN cache.last_buy lb ON i.id = lb.item_id AND lb.warehouse_id = vWarehouseFk
+ LEFT JOIN vn.buy b ON b.id = lb.buy_id
+ WHERE i.id = vItemFk;
+ END IF;
+
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemShelving_add`(IN vShelvingFk VARCHAR(8), IN vBarcode VARCHAR(22), IN vQuantity INT, IN vPackagingFk VARCHAR(10), IN vGrouping INT, IN vPacking INT, IN vWarehouseFk INT)
+BEGIN
+
+
+/**
+ * Añade registro o lo actualiza si ya existe.
+ *
+ * @param vShelvingFk matrícula del carro
+ * @param vBarcode el id del registro
+ * @param vQuantity indica la cantidad del producto
+ * @param vPackagingFk el packaging del producto en itemShelving, NULL para coger el de la ultima compra
+ * @param vGrouping el grouping del producto en itemShelving, NULL para coger el de la ultima compra
+ * @param vPacking el packing del producto, NULL para coger el de la ultima compra
+ * @param vWarehouseFk indica el sector
+ *
+ **/
+
+ DECLARE vItemFk INT;
+
+ SELECT barcodeToItem(vBarcode) INTO vItemFk;
+
+ IF (SELECT COUNT(*) FROM shelving WHERE code = vShelvingFk COLLATE utf8_unicode_ci) = 0 THEN
+
+ INSERT IGNORE INTO parking(code) VALUES(vShelvingFk);
+ INSERT INTO shelving(code, parkingFk)
+ SELECT vShelvingFk, id
+ FROM parking
+ WHERE `code` = vShelvingFk COLLATE utf8_unicode_ci;
+
+ END IF;
+
+ IF (SELECT COUNT(*) FROM itemShelving
+ WHERE shelvingFk COLLATE utf8_unicode_ci = vShelvingFk
+ AND itemFk = vItemFk
+ AND packing = vPacking) = 1 THEN
+
+ UPDATE itemShelving
+ SET visible = visible+vQuantity
+ WHERE shelvingFk COLLATE utf8_unicode_ci = vShelvingFk AND itemFk = vItemFk AND packing = vPacking;
+
+ ELSE
+ CALL cache.last_buy_refresh(FALSE);
+ INSERT INTO itemShelving( itemFk,
+ shelvingFk,
+ visible,
+ grouping,
+ packing,
+ packagingFk)
+
+ SELECT vItemFk,
+ vShelvingFk,
+ vQuantity,
+ IFNULL(vGrouping, b.grouping),
+ IFNULL(vPacking, b.packing),
+ IFNULL(vPackagingFk, b.packagingFk)
+ FROM item i
+ LEFT JOIN cache.last_buy lb ON i.id = lb.item_id AND lb.warehouse_id = vWarehouseFk
+ LEFT JOIN buy b ON b.id = lb.buy_id
+ WHERE i.id = vItemFk;
+ END IF;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`itemFreight_Show`(vItemFk INT, vWarehouseFk INT)
+BEGIN
+
+ SELECT cm3 Volumen_Entrada,
+ cm3delivery Volumen_Salida,
+ p.volume Volumen_del_embalaje,
+ p.width Ancho_del_embalaje,
+ p.`depth` Largo_del_embalaje,
+ b.packagingFk ,
+ IFNULL(p.height, i.`size`) + 10 Altura,
+ b.packing Packing_Entrada,
+ i.packingOut Packing_Salida,
+ i.id itemFk,
+ b.id buyFk,
+ b.entryFk,
+ w.name warehouseFk
+ FROM vn.itemCost ic
+ JOIN vn.item i ON i.id = ic.itemFk
+ LEFT JOIN cache.last_buy lb ON lb.item_id = ic.itemFk AND lb.warehouse_id = ic.warehouseFk
+ LEFT JOIN vn.buy b ON b.id = lb.buy_id
+ LEFT JOIN vn.packaging p ON p.id = b.packagingFk
+ LEFT JOIN vn.warehouse w ON w.id = ic.warehouseFk
+ WHERE ic.itemFk = vItemFk
+ AND ic.warehouseFk = vWarehouseFk;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`inventoryMake`(vDate DATE, vWh INT)
+proc: BEGIN
+/**
+* Recalcula los inventarios de todos los almacenes, si vWh = 0
+*
+* @param vDate Fecha de los nuevos inventarios
+* @param vWh almacen al cual hacer el inventario
+*/
+
+ DECLARE vDone BOOL;
+ DECLARE vEntryFk INT;
+ DECLARE vTravelFk INT;
+ DECLARE vDateLastInventory DATE;
+ DECLARE vDateYesterday DATETIME DEFAULT vDate - INTERVAL 1 SECOND;
+ DECLARE vWarehouseOutFkInventory INT;
+ DECLARE vInventorySupplierFk INT;
+ DECLARE vAgencyModeFkInventory INT;
+
+ DECLARE cWarehouses CURSOR FOR
+ SELECT id
+ FROM warehouse
+ WHERE isInventory
+ AND vWh IN (0,id);
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
+
+ OPEN cWarehouses;
+ SET @isModeInventory := TRUE;
+ l: LOOP
+
+ SET vDone = FALSE;
+ FETCH cWarehouses INTO vWh;
+
+ IF vDone THEN
+ LEAVE l;
+ END IF;
+
+ SELECT w.id INTO vWarehouseOutFkInventory
+ FROM warehouse w
+ WHERE w.code = 'inv';
+
+ SELECT inventorySupplierFk INTO vInventorySupplierFk
+ FROM entryConfig;
+
+ SELECT am.id INTO vAgencyModeFkInventory
+ FROM agencyMode am
+ where code = 'inv';
+
+ SELECT MAX(landed) INTO vDateLastInventory
+ FROM travel tr
+ JOIN entry e ON e.travelFk = tr.id
+ JOIN buy b ON b.entryFk = e.id
+ WHERE warehouseOutFk = vWarehouseOutFkInventory
+ AND landed < vDate
+ AND e.supplierFk = vInventorySupplierFk
+ AND warehouseInFk = vWh
+ AND NOT isRaid;
+
+ IF vDateLastInventory IS NULL THEN
+ SELECT inventoried INTO vDateLastInventory FROM config;
+ END IF;
+
+ -- Generamos travel, si no existe.
+ SET vTravelFK = 0;
+
+ SELECT id INTO vTravelFk
+ FROM travel
+ WHERE warehouseOutFk = vWarehouseOutFkInventory
+ AND warehouseInFk = vWh
+ AND landed = vDate
+ AND agencyModeFk = vAgencyModeFkInventory
+ AND ref = 'inventario'
+ LIMIT 1;
+
+ IF NOT vTravelFK THEN
+
+ INSERT INTO travel SET
+ warehouseOutFk = vWarehouseOutFkInventory,
+ warehouseInFk = vWh,
+ shipped = vDate,
+ landed = vDate,
+ agencyModeFk = vAgencyModeFkInventory,
+ ref = 'inventario',
+ isDelivered = TRUE,
+ isReceived = TRUE;
+
+ SELECT LAST_INSERT_ID() INTO vTravelFk;
+
+ END IF;
+
+ -- Generamos entrada si no existe, o la vaciamos.
+ SET vEntryFk = 0;
+
+ SELECT id INTO vEntryFk
+ FROM entry
+ WHERE supplierFk = vInventorySupplierFk
+ AND travelFk = vTravelFk;
+
+ IF NOT vEntryFk THEN
+
+ INSERT INTO entry SET
+ supplierFk = vInventorySupplierFk,
+ isConfirmed = TRUE,
+ isOrdered = TRUE,
+ travelFk = vTravelFk;
+
+ SELECT LAST_INSERT_ID() INTO vEntryFk;
+
+ ELSE
+
+ DELETE FROM buy WHERE entryFk = vEntryFk;
+
+ END IF;
+
+ -- Preparamos tabla auxilar
+ CREATE OR REPLACE TEMPORARY TABLE tmp.inventory (
+ itemFk INT(11) NOT NULL PRIMARY KEY,
+ quantity int(11) DEFAULT '0',
+ buyingValue decimal(10,3) DEFAULT '0.000',
+ freightValue decimal(10,3) DEFAULT '0.000',
+ packing int(11) DEFAULT '0',
+ `grouping` smallint(5) unsigned NOT NULL DEFAULT '1',
+ groupingMode tinyint(4) NOT NULL DEFAULT 0 ,
+ comissionValue decimal(10,3) DEFAULT '0.000',
+ packageValue decimal(10,3) DEFAULT '0.000',
+ packageFk varchar(10) COLLATE utf8_unicode_ci DEFAULT '--',
+ price1 decimal(10,2) DEFAULT '0.00',
+ price2 decimal(10,2) DEFAULT '0.00',
+ price3 decimal(10,2) DEFAULT '0.00',
+ minPrice decimal(10,2) DEFAULT '0.00',
+ producer varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
+ INDEX (itemFK)) ENGINE = MEMORY;
+
+ -- Compras
+ INSERT INTO tmp.inventory(itemFk,quantity)
+ SELECT b.itemFk, SUM(b.quantity)
+ FROM buy b
+ JOIN entry e ON e.id = b.entryFk
+ JOIN travel tr ON tr.id = e.travelFk
+ WHERE tr.warehouseInFk = vWh
+ AND tr.landed BETWEEN vDateLastInventory
+ AND vDateYesterday
+ AND NOT isRaid
+ GROUP BY b.itemFk;
+ SELECT vDateLastInventory , vDateYesterday;
+
+ -- Traslados
+ INSERT INTO tmp.inventory(itemFk, quantity)
+ SELECT itemFk, quantityOut
+ FROM (
+ SELECT b.itemFk,- SUM(b.quantity) quantityOut
+ FROM buy b
+ JOIN entry e ON e.id = b.entryFk
+ JOIN travel tr ON tr.id = e.travelFk
+ WHERE tr.warehouseOutFk = vWh
+ AND tr.shipped BETWEEN vDateLastInventory
+ AND vDateYesterday
+ AND NOT isRaid
+ GROUP BY b.itemFk
+ ) sub
+ ON DUPLICATE KEY UPDATE quantity = IFNULL(quantity, 0) + sub.quantityOut;
+
+ -- Ventas
+ INSERT INTO tmp.inventory(itemFk,quantity)
+ SELECT itemFk, saleOut
+ FROM (
+ SELECT s.itemFk, - SUM(s.quantity) saleOut
+ FROM sale s
+ JOIN ticket t ON t.id = s.ticketFk
+ WHERE t.warehouseFk = vWh
+ AND t.shipped BETWEEN vDateLastInventory AND vDateYesterday
+ GROUP BY s.itemFk
+ ) sub
+ ON DUPLICATE KEY UPDATE quantity = IFNULL(quantity,0) + sub.saleOut;
+
+ -- Actualiza valores de la ultima compra
+ UPDATE tmp.inventory inv
+ JOIN cache.last_buy lb ON lb.item_id = inv.itemFk AND lb.warehouse_id = vWh
+ JOIN buy b ON b.id = lb.buy_id
+ JOIN item i ON i.id = b.itemFk
+ LEFT JOIN producer p ON p.id = i.producerFk
+ SET inv.buyingValue = b.buyingValue,
+ inv.freightValue = b.freightValue,
+ inv.packing = b.packing,
+ inv.`grouping`= b.`grouping`,
+ inv.groupingMode = b.groupingMode,
+ inv.comissionValue = b.comissionValue,
+ inv.packageValue = b.packageValue,
+ inv.packageFk = b.packagingFk,
+ inv.price1 = b.price1,
+ inv.price2 = b.price2,
+ inv.price3 = b.price3,
+ inv.minPrice = b.minPrice,
+ inv.producer = p.name;
+
+ INSERT INTO buy( itemFk,
+ quantity,
+ buyingValue,
+ freightValue,
+ packing,
+ `grouping`,
+ groupingMode,
+ comissionValue,
+ packageValue,
+ packagingFk,
+ price1,
+ price2,
+ price3,
+ minPrice,
+ entryFk)
+ SELECT itemFk,
+ GREATEST(quantity, 0),
+ buyingValue,
+ freightValue,
+ packing,
+ `grouping`,
+ groupingMode,
+ comissionValue,
+ packageValue,
+ packagingFk,
+ price1,
+ price2,
+ price3,
+ minPrice,
+ vEntryFk
+ FROM tmp.inventory;
+
+ SELECT vWh, COUNT(*), util.VN_NOW() FROM tmp.inventory;
+
+ -- Actualizamos el campo lastUsed de item
+ UPDATE item i
+ JOIN tmp.inventory i2 ON i2.itemFk = i.id
+ SET i.lastUsed = NOW()
+ WHERE i2.quantity;
+
+ -- DROP TEMPORARY TABLE tmp.inventory;
+
+ END LOOP;
+
+ CLOSE cWarehouses;
+
+ UPDATE config SET inventoried = vDate;
+ SET @isModeInventory := FALSE;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.entryToDelete;
+ CREATE TEMPORARY TABLE tmp.entryToDelete
+ (INDEX(entryId) USING BTREE) ENGINE = MEMORY
+ SELECT e.id as entryId,
+ t.id as travelId
+ FROM travel t
+ JOIN `entry` e ON e.travelFk = t.id
+ WHERE e.supplierFk = vInventorySupplierFk
+ AND t.shipped <= util.VN_CURDATE() - INTERVAL 12 DAY
+ AND (DAY(t.shipped) <> 1 OR shipped < util.VN_CURDATE() - INTERVAL 12 DAY);
+
+ DELETE e
+ FROM `entry` e
+ JOIN tmp.entryToDelete tmp ON tmp.entryId = e.id;
+
+ DELETE IGNORE t
+ FROM travel t
+ JOIN tmp.entryToDelete tmp ON tmp.travelId = t.id;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`inventory_repair`()
+BEGIN
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.lastEntry;
+ CREATE TEMPORARY TABLE tmp.lastEntry
+ (PRIMARY KEY (buyFk))
+ SELECT
+ i.id AS itemFk,
+ w.id AS warehouseFk,
+ w.name AS warehouse,
+ tr.landed,
+ b.id AS buyFk,
+ b.entryFk,
+ b.isIgnored,
+ b.price2,
+ b.price3,
+ b.stickers,
+ b.packing,
+ b.grouping,
+ b.groupingMode,
+ b.weight,
+ i.stems,
+ b.quantity,
+ b.buyingValue,
+ b.packagingFk ,
+ s.id AS supplierFk,
+ s.name AS supplier
+ FROM itemType it
+ RIGHT JOIN (entry e
+ LEFT JOIN supplier s ON s.id = e.supplierFk
+ RIGHT JOIN buy b ON b.entryFk = e.id
+ LEFT JOIN item i ON i.id = b.itemFk
+ LEFT JOIN ink ON ink.id = i.inkFk
+ LEFT JOIN travel tr ON tr.id = e.travelFk
+ LEFT JOIN warehouse w ON w.id = tr.warehouseInFk
+ LEFT JOIN origin o ON o.id = i.originFk
+ ) ON it.id = i.typeFk
+ LEFT JOIN edi.ekt ek ON b.ektFk = ek.id
+ WHERE (b.packagingFk = "--" OR b.price2 = 0 OR b.packing = 0 OR b.buyingValue = 0) AND tr.landed > util.firstDayOfMonth(TIMESTAMPADD(MONTH,-1,util.VN_CURDATE())) AND s.name = 'INVENTARIO';
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.lastEntryOk;
+ CREATE TEMPORARY TABLE tmp.lastEntryOk
+ (PRIMARY KEY (buyFk))
+ SELECT
+ i.id AS itemFk,
+ w.id AS warehouseFk,
+ w.name AS warehouse,
+ tr.landed,
+ b.id AS buyFk,
+ b.entryFk,
+ b.isIgnored,
+ b.price2,
+ b.price3,
+ b.stickers,
+ b.packing,
+ b.grouping,
+ b.groupingMode,
+ b.weight,
+ i.stems,
+ b.quantity,
+ b.buyingValue,
+ b.packagingFk,
+ s.id AS supplierFk,
+ s.name AS supplier
+ FROM itemType it
+ RIGHT JOIN (entry e
+ LEFT JOIN supplier s ON s.id = e.supplierFk
+ RIGHT JOIN buy b ON b.entryFk = e.id
+ LEFT JOIN item i ON i.id = b.itemFk
+ LEFT JOIN ink ON ink.id = i.inkFk
+ LEFT JOIN travel tr ON tr.id = e.travelFk
+ LEFT JOIN warehouse w ON w.id = tr.warehouseInFk
+ LEFT JOIN origin o ON o.id = i.originFk
+ ) ON it.id = i.typeFk
+ LEFT JOIN edi.ekt ek ON b.ektFk = ek.id
+ WHERE b.packagingFk != "--" AND b.price2 != 0 AND b.packing != 0 AND b.buyingValue > 0 AND tr.landed > util.firstDayOfMonth(TIMESTAMPADD(MONTH,-2,util.VN_CURDATE()))
+ ORDER BY tr.landed DESC;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.lastEntryOkGroup;
+ CREATE TEMPORARY TABLE tmp.lastEntryOkGroup
+ (INDEX (warehouseFk,itemFk))
+ SELECT *
+ FROM tmp.lastEntryOk tmp
+ GROUP BY tmp.itemFk,tmp.warehouseFk;
+
+ UPDATE buy b
+ JOIN tmp.lastEntry lt ON lt.buyFk = b.id
+ JOIN tmp.lastEntryOkGroup eo ON eo.itemFk = lt.itemFk AND eo.warehouseFk = lt.warehouseFk
+ SET b.packagingFk = eo.packagingFk WHERE b.packagingFk = "--";
+
+ UPDATE buy b
+ JOIN tmp.lastEntry lt ON lt.buyFk = b.id
+ JOIN tmp.lastEntryOkGroup eo ON eo.itemFk = lt.itemFk AND eo.warehouseFk = lt.warehouseFk
+ SET b.price2 = eo.price2 WHERE b.price2 = 0 ;
+
+ UPDATE buy b
+ JOIN tmp.lastEntry lt ON lt.buyFk = b.id
+ JOIN tmp.lastEntryOkGroup eo ON eo.itemFk = lt.itemFk AND eo.warehouseFk = lt.warehouseFk
+ SET b.packing = eo.packing WHERE b.packing = 0;
+
+ UPDATE buy b
+ JOIN tmp.lastEntry lt ON lt.buyFk = b.id
+ JOIN tmp.lastEntryOkGroup eo ON eo.itemFk = lt.itemFk AND eo.warehouseFk = lt.warehouseFk
+ SET b.buyingValue = eo.buyingValue WHERE b.buyingValue = 0;
+
+ DROP TEMPORARY TABLE tmp.lastEntry;
+ DROP TEMPORARY TABLE tmp.lastEntryOk;
+ DROP TEMPORARY TABLE tmp.lastEntryOkGroup;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`buy_afterUpsert`(vSelf INT)
+BEGIN
+/**
+ * Triggered actions when a buy is updated or inserted.
+ *
+ * @param vSelf The buy reference
+ */
+ DECLARE vEntryFk INT;
+ DECLARE vItemFk INT;
+ DECLARE vPackingOut DECIMAL(10,2);
+ DECLARE vWarehouse INT;
+ DECLARE vStandardFlowerBox INT;
+ DECLARE vWarehouseOut INT;
+ DECLARE vIsMerchandise BOOL;
+ DECLARE vIsFeedStock BOOL;
+ DECLARE vWeight DECIMAL(10,2);
+ DECLARE vPacking INT;
+
+ SELECT b.entryFk,
+ b.itemFk,
+ i.packingOut,
+ ic.merchandise,
+ vc.standardFlowerBox,
+ b.weight,
+ b.packing
+ INTO
+ vEntryFk,
+ vItemFk,
+ vPackingOut,
+ vIsMerchandise,
+ vStandardFlowerBox,
+ vWeight,
+ vPacking
+ FROM buy b
+ LEFT JOIN item i ON i.id = b.itemFk
+ LEFT JOIN itemType it ON it.id = i.typeFk
+ LEFT JOIN itemCategory ic ON ic.id = it.categoryFk
+ LEFT JOIN packaging p ON p.id = b.packagingFk AND NOT p.isBox
+ JOIN volumeConfig vc ON TRUE
+ WHERE b.id = vSelf;
+
+ SELECT t.warehouseInFk, t.warehouseOutFk
+ INTO vWarehouse, vWarehouseOut
+ FROM entry e
+ JOIN travel t ON t.id = e.travelFk
+ WHERE e.id = vEntryFk;
+
+ IF vIsMerchandise THEN
+
+ REPLACE itemCost SET
+ itemFk = vItemFk,
+ warehouseFk = vWarehouse,
+ cm3 = buy_getUnitVolume(vSelf),
+ cm3Delivery = IFNULL((vStandardFlowerBox * 1000) / vPackingOut, buy_getUnitVolume(vSelf));
+
+ IF vWeight AND vPacking THEN
+ UPDATE itemCost SET
+ grams = vWeight * 1000 / vPacking
+ WHERE itemFk = vItemFk
+ AND warehouseFk = vWarehouse;
+ END IF;
+ END IF;
+
+ SELECT isFeedStock INTO vIsFeedStock
+ FROM warehouse WHERE id = vWarehouseOut;
+
+ IF vIsFeedStock THEN
+ INSERT IGNORE INTO producer(`name`)
+ SELECT es.company_name
+ FROM buy b
+ JOIN edi.ekt be ON be.id = b.ektFk
+ JOIN edi.supplier es ON es.supplier_id = be.pro
+ WHERE b.id = vSelf;
+
+ END IF;
+
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` FUNCTION `vn`.`buy_getUnitVolume`(vSelf INT)
+ RETURNS int(11)
+ DETERMINISTIC
+BEGIN
+/**
+ * Calculates the unit volume occupied by a buy.
+ *
+ * @param vSelf The buy id
+ * @return The unit volume in cubic centimeters
+ */
+ DECLARE vItem INT;
+ DECLARE vPackaging VARCHAR(10);
+ DECLARE vPacking INT;
+
+ SELECT itemFk, packagingFk, packing
+ INTO vItem, vPackaging, vPacking
+ FROM buy
+ WHERE id = vSelf;
+
+ RETURN IFNULL(ROUND(item_getVolume(vItem, vPackaging) / vPacking), 0);
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`buy_recalcPrices`()
+BEGIN
+/**
+ * Recalcula los precios para las compras insertadas en tmp.buyRecalc
+ *
+ * @param tmp.buyRecalc (id)
+ */
+ DECLARE vLanded DATE;
+ DECLARE vWarehouseFk INT;
+ DECLARE vHasNotPrice BOOL;
+ DECLARE vBuyingValue DECIMAL(10,3);
+ DECLARE vPackagingFk VARCHAR(10);
+ DECLARE vIsWarehouseFloramondo BOOL;
+
+ SELECT t.landed, t.warehouseInFk, (w.`name` = 'Floramondo')
+ INTO vLanded, vWarehouseFk, vIsWarehouseFloramondo
+ FROM tmp.buyRecalc br
+ JOIN buy b ON b.id = br.id
+ JOIN entry e ON e.id = b.entryFk
+ JOIN travel t ON t.id = e.travelFk
+ JOIN warehouse w ON w.id = t.warehouseInFk
+ LIMIT 1;
+
+ CALL rate_getPrices(vLanded, vWarehouseFk);
+
+ UPDATE buy b
+ JOIN tmp.buyRecalc br ON br.id = b.id AND (@buyId := b.id)
+ LEFT JOIN packaging p ON p.id = b.packagingFk
+ JOIN item i ON i.id = b.itemFk
+ JOIN entry e ON e.id = b.entryFk
+ JOIN itemType it ON it.id = i.typeFk
+ JOIN travel tr ON tr.id = e.travelFk
+ JOIN agencyMode am ON am.id = tr.agencyModeFk
+ JOIN tmp.rate r
+ JOIN volumeConfig vc
+ SET b.freightValue = @PF:= IFNULL(((am.m3 * @m3:= item_getVolume(b.itemFk, b.packagingFk) / 1000000)
+ / b.packing) * IF(am.hasWeightVolumetric, GREATEST(b.weight / @m3 / vc.aerealVolumetricDensity, 1), 1), 0),
+ b.comissionValue = @CF:= ROUND(IFNULL(e.commission * b.buyingValue / 100, 0), 3),
+ b.packageValue = @EF:= IF(vIsWarehouseFloramondo, 0, IFNULL(ROUND(IF(p.isPackageReturnable, p.returnCost / b.packing , p.`value` / b.packing), 3),0)),
+ b.price3 = @t3:= IF(r.rate3 = 0, b.buyingValue,ROUND((b.buyingValue + @CF + @EF + @PF) / ((100 - r.rate3 - it.promo ) /100) ,2)), -- He añadido que el coste sea igual a tarifa3 si t3 = 0
+ b.price2 = @t2:= round(@t3 * (1 + ((r.rate2 - r.rate3)/100)),2),
+ b.price2 = @t2:= IF(@t2 <= @t3,@t3 , @t2);
+
+ SELECT (b.buyingValue = b.price2), b.buyingValue, b.packagingFk
+ INTO vHasNotPrice, vBuyingValue, vPackagingFk
+ FROM vn.buy b
+ WHERE b.id = @buyId AND b.buyingValue <> 0.01;
+
+ DROP TEMPORARY TABLE tmp.rate;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `edi`.`ekt_load`(IN `vSelf` INT)
+proc:BEGIN
+
+ DECLARE vRef INT;
+ DECLARE vBuy INT;
+ DECLARE vItem INT;
+ DECLARE vQty INT;
+ DECLARE vPackage INT;
+ DECLARE vPutOrderFk INT;
+ DECLARE vIsLot BOOLEAN;
+ DECLARE vForceToPacking INT DEFAULT 2;
+ DECLARE vEntryFk INT;
+ DECLARE vHasToChangePackagingFk BOOLEAN;
+ DECLARE vIsFloramondoDirect BOOLEAN;
+ DECLARE vTicketFk INT;
+ DECLARE vHasItemGroup BOOL;
+ DECLARE vDescription VARCHAR(255);
+ DECLARE vSaleFk INT;
+
+ -- Carga los datos necesarios del EKT
+
+ SELECT e.ref, qty, package, putOrderFk MOD 1000000, i2.id , NOT ISNULL(eea.addressFk), NOT ISNULL(igto.group_code),
+ CONCAT(e.`ref`, ' ', e.item, ' ', e.sub, ' EktFk:', e.id)
+ INTO vRef, vQty, vPackage, vPutOrderFk, vItem, vIsFloramondoDirect, vHasItemGroup, vDescription
+ FROM edi.ekt e
+ LEFT JOIN edi.item i ON e.ref = i.id
+ LEFT JOIN edi.putOrder po ON po.id = e.putOrderFk
+ LEFT JOIN vn.item i2 ON i2.supplyResponseFk = po.supplyResponseID
+ LEFT JOIN vn.ektEntryAssign eea ON eea.sub = e.sub
+ LEFT JOIN edi.item_groupToOffer igto ON igto.group_code = i.group_id
+ WHERE e.id = vSelf
+ LIMIT 1;
+
+ IF NOT vHasItemGroup THEN
+
+ CALL vn.mail_insert('logistica@verdnatura.es', 'nocontestar@verdnatura.es', 'Nuevo grupo en Floramondo', vDescription);
+
+ CALL vn.mail_insert('pako@verdnatura.es', 'nocontestar@verdnatura.es', CONCAT('Nuevo grupo en Floramondo: ', vDescription), vDescription);
+
+ LEAVE proc;
+
+ END IF;
+
+ -- Asigna la entrada
+ SELECT vn.ekt_getEntry(vSelf) INTO vEntryFk;
+
+ -- Inserta el cubo si no existe
+
+ IF vPackage = 800 THEN
+
+ SET vHasToChangePackagingFk = TRUE;
+
+ IF vItem THEN
+
+ SELECT vn.item_getPackage(vItem) INTO vPackage ;
+
+ ELSE
+
+ SET vPackage = 8000 + vQty;
+
+ INSERT IGNORE INTO vn.packaging(id, width, `depth`)
+ SELECT vPackage, vc.ccLength / vQty, vc.ccWidth
+ FROM vn.volumeConfig vc;
+
+ END IF;
+
+ ELSE
+
+ INSERT IGNORE INTO vn2008.Cubos (Id_Cubo, X, Y, Z)
+ SELECT bucket_id, ROUND(x_size/10), ROUND(y_size/10), ROUND(z_size/10)
+ FROM bucket WHERE bucket_id = vPackage;
+
+ IF ROW_COUNT() > 0
+ THEN
+ INSERT INTO vn2008.mail SET
+ `subject` = 'Cubo añadido',
+ `text` = CONCAT('Se ha añadido el cubo: ', vPackage),
+ `to` = 'ekt@verdnatura.es';
+ END IF;
+ END IF;
+
+ -- Si es una compra de Logiflora obtiene el articulo
+ IF vPutOrderFk THEN
+
+ SELECT i.id INTO vItem
+ FROM edi.putOrder po
+ JOIN vn.item i ON i.supplyResponseFk = po.supplyResponseID
+ WHERE po.id = vPutOrderFk
+ LIMIT 1;
+
+ END IF;
+
+ INSERT IGNORE INTO item_track SET
+ item_id = vRef;
+
+ IF IFNULL(vItem,0) = 0 THEN
+
+ -- Intenta obtener el artículo en base a los atributos holandeses
+
+ SELECT b.id, IFNULL(b.itemOriginalFk ,b.itemFk) INTO vBuy, vItem
+ FROM edi.ekt e
+ JOIN edi.item_track t ON t.item_id = e.ref
+ LEFT JOIN edi.ekt l ON l.ref = e.ref
+ LEFT JOIN vn.buy b ON b.ektFk = l.id
+ LEFT JOIN vn.item i ON i.id = b.itemFk
+ JOIN vn2008.config cfg
+ WHERE e.id = vSelf
+ AND l.id != vSelf
+ AND b.itemFk != cfg.generic_item
+ AND IF(t.s1, l.s1 = e.s1, TRUE)
+ AND IF(t.s2, l.s2 = e.s2, TRUE)
+ AND IF(t.s3, l.s3 = e.s3, TRUE)
+ AND IF(t.s4, l.s4 = e.s4, TRUE)
+ AND IF(t.s5, l.s5 = e.s5, TRUE)
+ AND IF(t.s6, l.s6 = e.s6, TRUE)
+ AND IF(t.pac, l.pac = e.pac, TRUE)
+ AND IF(t.cat, l.cat = e.cat, TRUE)
+ AND IF(t.ori, l.ori = e.ori, TRUE)
+ AND IF(t.pro, l.pro = e.pro, TRUE)
+ AND IF(t.package, l.package = e.package, TRUE)
+ AND IF(t.item, l.item = e.item, TRUE)
+ AND i.isFloramondo = vIsFloramondoDirect
+ ORDER BY l.now DESC, b.id ASC
+ LIMIT 1;
+
+ END IF;
+
+ -- Si no encuentra el articulo lo crea en el caso de las compras directas en Floramondo
+ IF ISNULL(vItem) AND vIsFloramondoDirect THEN
+
+ CALL edi.item_getNewByEkt(vSelf, vItem);
+
+ END IF;
+
+ INSERT INTO vn.buy
+ (
+ entryFk
+ ,ektFk
+ ,buyingValue
+ ,itemFk
+ ,stickers
+ ,packing
+ ,`grouping`
+ ,quantity
+ ,groupingMode
+ ,packagingFk
+ ,weight
+ )
+ SELECT
+ vEntryFk
+ ,vSelf
+ ,(@t := IF(i.stems, i.stems, 1)) * e.pri / IFNULL(i.stemMultiplier, 1) buyingValue
+ ,IFNULL(vItem, cfg.generic_item) itemFk
+ ,e.qty stickers
+ ,@pac := IFNULL(i.stemMultiplier, 1) * e.pac / @t packing
+ ,IFNULL(b.`grouping`, e.pac)
+ ,@pac * e.qty
+ ,vForceToPacking
+ ,IF(vHasToChangePackagingFk OR ISNULL(b.packagingFk), vPackage, b.packagingFk)
+ ,(IFNULL(i.weightByPiece,0) * @pac)/1000
+ FROM edi.ekt e
+ LEFT JOIN vn.buy b ON b.id = vBuy
+ LEFT JOIN vn.item i ON i.id = b.itemFk
+ LEFT JOIN vn.supplier s ON e.pro = s.id
+ JOIN vn2008.config cfg
+ WHERE e.id = vSelf
+ LIMIT 1;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.buyRecalc;
+
+ CREATE TEMPORARY TABLE tmp.buyRecalc
+ SELECT buy.id
+ FROM vn.buy
+ WHERE ektFk = vSelf;
+
+ CALL vn.buy_recalcPrices();
+
+ -- Si es una compra de Logiflora hay que informar la tabla vn.saleBuy
+ IF vPutOrderFk THEN
+
+ REPLACE vn.saleBuy(saleFk, buyFk, workerFk)
+ SELECT po.saleFk, b.id, account.myUser_getId()
+ FROM edi.putOrder po
+ JOIN vn.buy b ON b.ektFk = vSelf
+ WHERE po.id = vPutOrderFk;
+
+ END IF;
+ -- Si es una compra directa en Floramondo hay que añadirlo al ticket
+
+ IF vIsFloramondoDirect THEN
+
+ SELECT t.id INTO vTicketFk
+ FROM vn.ticket t
+ JOIN vn.ektEntryAssign eea
+ ON eea.addressFk = t.addressFk
+ AND t.warehouseFk = eea.warehouseInFk
+ JOIN edi.ekt e
+ ON e.sub = eea.sub
+ AND e.id = vSelf
+ WHERE e.fec = t.shipped
+ LIMIT 1;
+
+ IF ISNULL(vTicketFk) THEN
+
+ INSERT INTO vn.ticket (
+ clientFk,
+ shipped,
+ addressFk,
+ agencyModeFk,
+ nickname,
+ warehouseFk,
+ companyFk,
+ landed,
+ zoneFk,
+ zonePrice,
+ zoneBonus
+ )
+ SELECT
+ a.clientFk,
+ e.fec,
+ a.id,
+ a.agencyModeFk,
+ a.nickname,
+ eea.warehouseInFk,
+ c.id,
+ e.fec,
+ z.id,
+ z.price,
+ z.bonus
+ FROM edi.ekt e
+ JOIN vn.ektEntryAssign eea ON eea.sub = e.sub
+ JOIN vn.address a ON a.id = eea.addressFk
+ JOIN vn.company c ON c.code = 'VNL'
+ JOIN vn.`zone` z ON z.code = 'FLORAMONDO'
+ WHERE e.id = vSelf
+ LIMIT 1;
+
+ SET vTicketFk = LAST_INSERT_ID();
+
+ INSERT INTO vn.ticketLog
+ SET originFk = vTicketFk,
+ userFk = account.myUser_getId(),
+ `action` = 'insert',
+ description = CONCAT('EktLoad ha creado el ticket:', ' ', vTicketFk);
+
+ END IF;
+
+ INSERT INTO vn.sale (itemFk, ticketFk, concept, quantity, price)
+ SELECT vItem, vTicketFk, e.item, e.qty * e.pac, e.pri * ( 1 + fhc.floramondoMargin )
+ FROM edi.ekt e
+ JOIN edi.floraHollandConfig fhc
+ WHERE e.id = vSelf;
+
+ SELECT LAST_INSERT_ID() INTO vSaleFk;
+
+ REPLACE vn.saleBuy(saleFk, buyFk, workerFk)
+ SELECT vSaleFk, b.id, account.myUser_getId()
+ FROM vn.buy b
+ WHERE b.ektFk = vSelf;
+
+ INSERT INTO vn.saleComponent(saleFk, componentFk, value)
+ SELECT vSaleFk, c.id, e.pri
+ FROM edi.ekt e
+ JOIN vn.component c ON c.code = 'purchaseValue'
+ WHERE e.id = vSelf;
+
+ INSERT INTO vn.saleComponent(saleFk, componentFk, value)
+ SELECT vSaleFk, c.id, e.pri * fhc.floramondoMargin
+ FROM edi.ekt e
+ JOIN edi.floraHollandConfig fhc
+ JOIN vn.component c ON c.code = 'margin'
+ WHERE e.id = vSelf;
+ END IF;
+ DROP TEMPORARY TABLE tmp.buyRecalc;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `hedera`.`item_getVisible`(
+ vWarehouse TINYINT,
+ vDate DATE,
+ vType INT,
+ vPrefix VARCHAR(255))
+BEGIN
+
+/**
+ * Gets visible items of the specified type at specified date.
+ *
+ * @param vWarehouse The warehouse id
+ * @param vDate The visible date
+ * @param vType The type id
+ * @param vPrefix The article prefix to filter or %NULL for all
+ * @return tmp.itemVisible Visible items
+ */
+ DECLARE vPrefixLen SMALLINT;
+ DECLARE vFilter VARCHAR(255) DEFAULT NULL;
+ DECLARE vDateInv DATE DEFAULT vn2008.date_inv();
+ DECLARE EXIT HANDLER FOR 1114
+ BEGIN
+ GET DIAGNOSTICS CONDITION 1
+ @message = MESSAGE_TEXT;
+ CALL vn.mail_insert(
+ 'cau@verdnatura.es',
+ NULL,
+ CONCAT('hedera.item_getVisible error: ', @message),
+ CONCAT(
+ 'warehouse: ', IFNULL(vWarehouse, ''),
+ ', Fecha:', IFNULL(vDate, ''),
+ ', tipo: ', IFNULL(vType,''),
+ ', prefijo: ', IFNULL(vPrefix,'')));
+ RESIGNAL;
+ END;
+ SET vPrefixLen = IFNULL(LENGTH(vPrefix), 0) + 1;
+
+ IF vPrefixLen > 1 THEN
+ SET vFilter = CONCAT(vPrefix, '%');
+ END IF;
+
+ DROP TEMPORARY TABLE IF EXISTS `filter`;
+ CREATE TEMPORARY TABLE `filter`
+ (INDEX (itemFk))
+ ENGINE = MEMORY
+ SELECT id itemFk FROM vn.item
+ WHERE typeFk = vType
+ AND (vFilter IS NULL OR `name` LIKE vFilter);
+
+ DROP TEMPORARY TABLE IF EXISTS currentStock;
+ CREATE TEMPORARY TABLE currentStock
+ (INDEX (itemFk))
+ ENGINE = MEMORY
+ SELECT itemFk, SUM(quantity) quantity
+ FROM (
+ SELECT b.itemFk, b.quantity
+ FROM vn.buy b
+ JOIN vn.entry e ON e.id = b.entryFk
+ JOIN vn.travel t ON t.id = e.travelFk
+ WHERE t.landed BETWEEN vDateInv AND vDate
+ AND t.warehouseInFk = vWarehouse
+ AND NOT e.isRaid
+ UNION ALL
+ SELECT b.itemFk, -b.quantity
+ FROM vn.buy b
+ JOIN vn.entry e ON e.id = b.entryFk
+ JOIN vn.travel t ON t.id = e.travelFk
+ WHERE t.shipped BETWEEN vDateInv AND util.VN_CURDATE()
+ AND t.warehouseOutFk = vWarehouse
+ AND NOT e.isRaid
+ AND t.isDelivered
+ UNION ALL
+ SELECT m.itemFk, -m.quantity
+ FROM vn.sale m
+ JOIN vn.ticket t ON t.id = m.ticketFk
+ JOIN vn.ticketState s ON s.ticket = t.id
+ WHERE t.shipped BETWEEN vDateInv AND util.VN_CURDATE()
+ AND t.warehouseFk = vWarehouse
+ AND s.alertLevel = 3
+ ) t
+ GROUP BY itemFk
+ HAVING quantity > 0;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp;
+ CREATE TEMPORARY TABLE tmp
+ (INDEX (itemFk))
+ ENGINE = MEMORY
+ SELECT *
+ FROM (
+ SELECT b.itemFk, b.packagingFk, b.packing
+ FROM vn.buy b
+ JOIN vn.entry e ON e.id = b.entryFk
+ JOIN vn.travel t ON t.id = e.travelFk
+ WHERE t.landed BETWEEN vDateInv AND vDate
+ AND NOT b.isIgnored
+ AND b.price2 >= 0
+ AND b.packagingFk IS NOT NULL
+ ORDER BY t.warehouseInFk = vWarehouse DESC, t.landed DESC
+ LIMIT 10000000000000000000
+ ) t GROUP BY itemFk;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.itemVisible;
+ CREATE TEMPORARY TABLE tmp.itemVisible
+ ENGINE = MEMORY
+ SELECT i.id Id_Article,
+ SUBSTRING(i.`name`, vPrefixLen) Article,
+ t.packing, p.id Id_Cubo,
+ IF(p.depth > 0, p.depth, 0) depth, p.width, p.height,
+ CEIL(s.quantity / t.packing) etiquetas
+ FROM vn.item i
+ JOIN `filter` f ON f.itemFk = i.id
+ JOIN currentStock s ON s.itemFk = i.id
+ LEFT JOIN tmp t ON t.itemFk = i.id
+ LEFT JOIN vn.packaging p ON p.id = t.packagingFk
+ WHERE CEIL(s.quantity / t.packing) > 0
+ -- FIXME: Column Cubos.box not included in view vn.packaging
+ /* AND p.box */;
+
+ DROP TEMPORARY TABLE
+ `filter`,
+ currentStock,
+ tmp;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`buy_getVolume`()
+BEGIN
+/**
+ * Cálculo de volumen en líneas de compra
+ * @table tmp.buy(buyFk)
+ */
+ SELECT t.name Temp,
+ CAST(ROUND(SUM(GREATEST(b.stickers ,b.quantity /b.packing ) *
+ item_getVolume(b.itemFk, b.packagingFk)) / vc.trolleyM3 / 1000000 ,1) AS DECIMAL(10,2)) carros ,
+ CAST(ROUND(SUM(GREATEST(b.stickers ,b.quantity /b.packing ) *
+ item_getVolume(b.itemFk, b.packagingFk)) / vc.palletM3 / 1000000,1) AS DECIMAL(10,2)) espais
+ FROM buy b
+ JOIN tmp.buy tb ON tb.buyFk = b.id
+ JOIN volumeConfig vc
+ JOIN item i ON i.id = b.itemFk
+ JOIN itemType it ON it.id = i.typeFk
+ LEFT JOIN temperature t ON t.code = it.temperatureFk
+ GROUP BY Temp;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`entry_checkPackaging`(vEntryFk INT)
+BEGIN
+/**
+ * Comprueba que los campos package y packaging no sean nulos
+ *
+ * @param vEntryFk Id de entrada
+ */
+ DECLARE vpackageOrPackingNull INT;
+
+ SELECT count(*) INTO vpackageOrPackingNull
+ FROM buy b
+ WHERE b.entryFk = vEntryFk
+ AND (b.packing IS NULL OR b.packagingFk IS NULL);
+
+ IF vpackageOrPackingNull THEN
+ CALL util.throw("packageOrPackingNull");
+ END IF;
+END$$
+DELIMITER ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`fustControl`(vFromDated DATE, vToDated DATE)
+BEGIN
+
+ DECLARE vSijsnerClientFk INT DEFAULT 19752;
+
+ DECLARE vDateStart DATETIME;
+ DECLARE vDateEnd DATETIME;
+
+ SET vDateStart = vFromDated;
+ SET vDateEnd = util.Dayend(vToDated);
+
+ SELECT p.id FustCode,
+ CAST(sent.stucks AS DECIMAL(10,0)) FH,
+ CAST(tp.stucks AS DECIMAL(10,0)) Tickets,
+ CAST(-sj.stucks AS DECIMAL(10,0)) Sijsner,
+ CAST(IFNULL(sent.stucks,0) - IFNULL(tp.stucks,0) + IFNULL(sj.stucks,0) AS DECIMAL(10,0)) saldo
+ FROM vn.packaging p
+ LEFT JOIN (
+ SELECT FustCode, sum(fustQuantity) stucks
+ FROM (
+ SELECT IFNULL(pe.equivalentFk ,b.packagingFk) FustCode, s.quantity / b.packing AS fustQuantity
+ FROM vn.sale s
+ JOIN vn.ticket t ON t.id = s.ticketFk
+ JOIN vn.warehouse w ON w.id = t.warehouseFk
+ JOIN vn.warehouseAlias wa ON wa.id = w.aliasFk
+ JOIN cache.last_buy lb ON lb.item_id = s.itemFk AND lb.warehouse_id = t.warehouseFk
+ JOIN vn.buy b ON b.id = lb.buy_id
+ JOIN vn.packaging p ON p.id = b.packagingFk
+ LEFT JOIN vn.packageEquivalent pe ON pe.packagingFk = p.id
+ JOIN vn.address a ON a.id = t.addressFk
+ JOIN vn.province p2 ON p2.id = a.provinceFk
+ JOIN vn.country c ON c.id = p2.countryFk
+ WHERE t.shipped BETWEEN vDateStart AND vDateEnd
+ AND wa.name = 'VNH'
+ AND p.isPackageReturnable
+ AND c.country = 'FRANCIA') sub
+ GROUP BY FustCode) sent ON sent.FustCode = p.id
+ LEFT JOIN (
+ SELECT FustCode, sum(quantity) stucks
+ FROM (
+ SELECT IFNULL(pe.equivalentFk ,tp.packagingFk) FustCode, tp.quantity
+ FROM vn.ticketPackaging tp
+ JOIN vn.ticket t ON t.id = tp.ticketFk
+ JOIN vn.warehouse w ON w.id = t.warehouseFk
+ JOIN vn.warehouseAlias wa ON wa.id = w.aliasFk
+ JOIN vn.packaging p ON p.id = tp.packagingFk
+ LEFT JOIN vn.packageEquivalent pe ON pe.packagingFk = p.id
+ JOIN vn.address a ON a.id = t.addressFk
+ JOIN vn.province p2 ON p2.id = a.provinceFk
+ JOIN vn.country c ON c.id = p2.countryFk
+ WHERE t.shipped BETWEEN vDateStart AND vDateEnd
+ AND wa.name = 'VNH'
+ AND p.isPackageReturnable
+ AND c.country = 'FRANCIA'
+ AND t.clientFk != vSijsnerClientFk
+ AND tp.quantity > 0) sub
+ GROUP BY FustCode) tp ON tp.FustCode = p.id
+ LEFT JOIN (
+ SELECT FustCode, sum(quantity) stucks
+ FROM (
+ SELECT IFNULL(pe.equivalentFk ,tp.packagingFk) FustCode, tp.quantity
+ FROM vn.ticketPackaging tp
+ JOIN vn.ticket t ON t.id = tp.ticketFk
+ JOIN vn.warehouse w ON w.id = t.warehouseFk
+ JOIN vn.warehouseAlias wa ON wa.id = w.aliasFk
+ JOIN vn.packaging p ON p.id = tp.packagingFk
+ LEFT JOIN vn.packageEquivalent pe ON pe.packagingFk = p.id
+ WHERE t.shipped BETWEEN TIMESTAMPADD(DAY, 1, vDateStart ) AND TIMESTAMPADD(DAY, 1, vDateEnd )
+ AND wa.name = 'VNH'
+ AND p.isPackageReturnable
+ AND t.clientFk = vSijsnerClientFk) sub
+ GROUP BY FustCode) sj ON sj.FustCode = p.id
+ WHERE sent.stucks
+ OR tp.stucks
+ OR sj.stucks;
+
+END$$
+DELIMITER ;
+
diff --git a/db/changes/234601/.gitkeep b/db/changes/234601/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/db/changes/234601/00-ACLticketTrackingState.sql b/db/changes/234601/00-ACLticketTrackingState.sql
new file mode 100644
index 000000000..ca6dce0c9
--- /dev/null
+++ b/db/changes/234601/00-ACLticketTrackingState.sql
@@ -0,0 +1,4 @@
+UPDATE `salix`.`ACL`
+ SET `property` = 'state',
+ `model` = 'Ticket'
+ WHERE `property` = 'changeState';
diff --git a/db/changes/234601/00-addressShortage.sql b/db/changes/234601/00-addressShortage.sql
new file mode 100644
index 000000000..57c07d480
--- /dev/null
+++ b/db/changes/234601/00-addressShortage.sql
@@ -0,0 +1,98 @@
+
+-- Place your SQL code here
+
+ALTER TABLE `vn`.`productionConfig` ADD shortageAddressFk int(11) COMMENT 'Consignatario por defecto para añadir un item de alta';
+ALTER TABLE `vn`.`productionConfig` ADD CONSTRAINT productionConfig_FK FOREIGN KEY (shortageAddressFk) REFERENCES vn.address(id) ON DELETE RESTRICT ON UPDATE CASCADE;
+
+ALTER TABLE `vn`.`sale` MODIFY COLUMN originalQuantity double(9,1) DEFAULT NULL NULL COMMENT 'Se utiliza para notificar a través de rocket los cambios de quantity';
+
+INSERT INTO `salix`.`ACL` ( model, property, accessType, permission, principalType, principalId) VALUES( 'AddressShortage', '*', 'READ', 'ALLOW', 'ROLE', 'production');
+
+-- vn.addressShortage definition
+
+CREATE TABLE `vn`.`addressShortage` (
+ `addressFk` int(11) NOT NULL,
+ PRIMARY KEY (`addressFk`),
+ CONSTRAINT `addressShortage_FK` FOREIGN KEY (`addressFk`) REFERENCES `address` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci;
+
+
+DELIMITER $$
+
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`item_setVisibleDiscard`(
+ vItemFk INT,
+ vWarehouseFk INT,
+ vQuantity INT,
+ vAddressFk INT)
+BEGIN
+/**
+ * Procedimiento para dar dar de baja/alta un item, si vAddressFk es NULL se entiende que se da de alta y se toma el addressFk de la configuración
+ *
+ * @param vItemFk Identificador del ítem
+ * @param vWarehouseFk id del warehouse
+ * @param vQuantity a dar de alta/baja
+ * @param vAddressFk id address
+ */
+ DECLARE vTicketFk INT;
+ DECLARE vClientFk INT;
+ DECLARE vDefaultCompanyFk INT;
+ DECLARE vCalc INT;
+ DECLARE vAddressShortage INT;
+
+ SELECT barcodeToItem(vItemFk) INTO vItemFk;
+
+ SELECT DEFAULT(companyFk) INTO vDefaultCompanyFk
+ FROM vn.ticket LIMIT 1;
+
+ IF vAddressFk IS NULL THEN
+ SELECT pc.shortageAddressFk INTO vAddressShortage
+ FROM productionConfig pc ;
+ ELSE
+ SET vAddressShortage = vAddressFk;
+ END IF;
+
+ SELECT a.clientFk INTO vClientFk
+ FROM address a
+ WHERE a.id = vAddressFk;
+
+ SELECT t.id INTO vTicketFk
+ FROM ticket t
+ JOIN address a ON a.id = t.addressFk
+ JOIN ticketState ts ON ts.ticketFk = t.id
+ WHERE t.warehouseFk = vWarehouseFk
+ AND a.id = vAddressShortage
+ AND DATE(t.shipped) = util.VN_CURDATE()
+ AND ts.code = 'DELIVERED'
+ LIMIT 1;
+
+ CALL cache.visible_refresh(vCalc, TRUE, vWarehouseFk);
+
+ IF vTicketFk IS NULL THEN
+ CALL ticket_add(
+ vClientFk,
+ util.VN_CURDATE(),
+ vWarehouseFk,
+ vDefaultCompanyFk,
+ vAddressFk,
+ NULL,
+ NULL,
+ util.VN_CURDATE(),
+ account.myUser_getId(),
+ FALSE,
+ vTicketFk);
+ END IF;
+
+ INSERT INTO sale(ticketFk, itemFk, concept, quantity)
+ SELECT vTicketFk,
+ vItemFk,
+ CONCAT(longName,' ', worker_getCode(), ' ', LEFT(CAST(util.VN_NOW() AS TIME),5)),
+ vQuantity
+ FROM item
+ WHERE id = vItemFk;
+
+ UPDATE cache.visible
+ SET visible = visible - vQuantity
+ WHERE calc_id = vCalc
+ AND item_id = vItemFk;
+END$$
+DELIMITER ;
diff --git a/db/changes/233601/00-createClaimReader.sql b/db/changes/234601/00-claimViewerAcl.sql
similarity index 86%
rename from db/changes/233601/00-createClaimReader.sql
rename to db/changes/234601/00-claimViewerAcl.sql
index e913c0ed9..17d8d4ce0 100644
--- a/db/changes/233601/00-createClaimReader.sql
+++ b/db/changes/234601/00-claimViewerAcl.sql
@@ -21,11 +21,11 @@ DELETE FROM `salix`.`ACL`
'getSummary'
);
-INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
+INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
VALUES ('Claim','filter','READ','ALLOW','ROLE','claimViewer');
-INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
+INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
VALUES ('Claim','find','READ','ALLOW','ROLE','claimViewer');
-INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
+INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
VALUES ('Claim','findById','READ','ALLOW','ROLE','claimViewer');
-INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalid`)
- VALUES ('Claim','getSummary','READ','ALLOW','ROLE','claimViewer');
\ No newline at end of file
+INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
+ VALUES ('Claim','getSummary','READ','ALLOW','ROLE','claimViewer');
diff --git a/db/changes/234601/00-clientAfterUpdate.sql b/db/changes/234601/00-clientAfterUpdate.sql
new file mode 100644
index 000000000..c6483813f
--- /dev/null
+++ b/db/changes/234601/00-clientAfterUpdate.sql
@@ -0,0 +1,95 @@
+ALTER TABLE `vn`.`client` MODIFY COLUMN `credit` decimal(10,2) unsigned DEFAULT 0.00 NOT NULL;
+
+DELETE FROM `salix`.`ACL` WHERE `model` = 'Client' AND `property` = 'create';
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`client_beforeUpdate`
+ BEFORE UPDATE ON `client`
+ FOR EACH ROW
+BEGIN
+ DECLARE vText VARCHAR(255) DEFAULT NULL;
+ DECLARE vPayMethodFk INT;
+
+ SET NEW.editorFk = account.myUser_getId();
+
+ IF NOT(NEW.credit <=> OLD.credit) THEN
+ INSERT INTO clientCredit
+ SET clientFk = NEW.id,
+ amount = NEW.credit,
+ workerFk = NEW.editorFk;
+ END IF;
+ -- 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.id = 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 ;
+
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`client_AfterInsert`
+ AFTER INSERT ON `client`
+ FOR EACH ROW
+BEGIN
+ IF NEW.credit IS NOT NULL AND NEW.credit THEN
+ INSERT INTO clientCredit
+ SET clientFk = NEW.id,
+ workerFk = NEW.editorFk,
+ amount = NEW.credit;
+ END IF;
+END$$
+DELIMITER ;
+
diff --git a/db/changes/234601/00-expeditionStateBeforeInsert.sql b/db/changes/234601/00-expeditionStateBeforeInsert.sql
new file mode 100644
index 000000000..565b3ded3
--- /dev/null
+++ b/db/changes/234601/00-expeditionStateBeforeInsert.sql
@@ -0,0 +1,13 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` TRIGGER `vn`.`expeditionState_BeforeInsert`
+ BEFORE INSERT ON `expeditionState`
+ FOR EACH ROW
+BEGIN
+
+ SET NEW.userFk = IFNULL(NEW.userFk, account.myUser_getId());
+
+END$$
+DELIMITER ;
+
+
+
diff --git a/db/changes/234601/00-ticketSetStateRefactor.sql b/db/changes/234601/00-ticketSetStateRefactor.sql
new file mode 100644
index 000000000..9078ffb0e
--- /dev/null
+++ b/db/changes/234601/00-ticketSetStateRefactor.sql
@@ -0,0 +1,55 @@
+DELIMITER $$
+CREATE OR REPLACE DEFINER=`root`@`localhost` PROCEDURE `vn`.`ticket_setState`(
+ vSelf INT,
+ vStateCode VARCHAR(255) COLLATE utf8_general_ci
+)
+BEGIN
+/**
+ * Modifica el estado de un ticket si se cumplen las condiciones necesarias.
+ *
+ * @param vSelf el id del ticket
+ * @param vStateCode estado a modificar del ticket
+ */
+ DECLARE vticketAlertLevel INT;
+ DECLARE vTicketStateCode VARCHAR(255);
+ DECLARE vCanChangeState BOOL;
+ DECLARE vPackedAlertLevel INT;
+ DECLARE vZoneFk INT;
+
+ SELECT s.alertLevel, s.`code`, t.zoneFk
+ INTO vticketAlertLevel, vTicketStateCode, vZoneFk
+ FROM state s
+ JOIN ticketTracking tt ON tt.stateFk = s.id
+ JOIN ticket t ON t.id = tt.ticketFk
+ WHERE tt.ticketFk = vSelf
+ ORDER BY tt.created DESC
+ LIMIT 1;
+
+ SELECT id INTO vPackedAlertLevel FROM alertLevel WHERE code = 'PACKED';
+
+ IF vStateCode = 'OK' AND vZoneFk IS NULL THEN
+ CALL util.throw('ASSIGN_ZONE_FIRST');
+ END IF;
+
+ SET vCanChangeState = (
+ vStateCode <> 'ON_CHECKING' OR
+ vticketAlertLevel < vPackedAlertLevel
+ )AND NOT (
+ vTicketStateCode IN ('CHECKED', 'CHECKING')
+ AND vStateCode IN ('PREPARED', 'ON_PREPARATION')
+ );
+
+ IF vCanChangeState THEN
+ INSERT INTO ticketTracking (stateFk, ticketFk, workerFk)
+ SELECT id, vSelf, account.myUser_getId()
+ FROM state
+ WHERE `code` = vStateCode COLLATE utf8_unicode_ci;
+
+ IF vStateCode = 'PACKED' THEN
+ CALL ticket_doCmr(vSelf);
+ END IF;
+ ELSE
+ CALL util.throw('INCORRECT_TICKET_STATE');
+ END IF;
+END$$
+DELIMITER ;
diff --git a/db/changes/234601/00-transferInvoice.sql b/db/changes/234601/00-transferInvoice.sql
new file mode 100644
index 000000000..7a9890ae4
--- /dev/null
+++ b/db/changes/234601/00-transferInvoice.sql
@@ -0,0 +1,6 @@
+INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId)
+ VALUES
+ ('CplusRectificationType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'),
+ ('CplusInvoiceType477', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'),
+ ('InvoiceCorrectionType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'),
+ ('InvoiceOut', 'transferInvoice', 'WRITE', 'ALLOW', 'ROLE', 'administrative');
diff --git a/db/changes/234801/.gitkeep b/db/changes/234801/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/db/changes/234801/00-notificationSubscription.sql b/db/changes/234801/00-notificationSubscription.sql
new file mode 100644
index 000000000..ef081437d
--- /dev/null
+++ b/db/changes/234801/00-notificationSubscription.sql
@@ -0,0 +1,3 @@
+INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
+ VALUES
+ ('NotificationSubscription', 'getList', 'READ', 'ALLOW', 'ROLE', 'employee');
diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index b6c89bd40..499336243 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -470,22 +470,22 @@ CREATE TEMPORARY TABLE tmp.address
WHERE `defaultAddressFk` IS NULL;
DROP TEMPORARY TABLE tmp.address;
-INSERT INTO `vn`.`clientCredit`(`id`, `clientFk`, `workerFk`, `amount`, `created`)
+INSERT INTO `vn`.`clientCredit`(`clientFk`, `workerFk`, `amount`, `created`)
VALUES
- (1 , 1101, 5, 300, DATE_ADD(util.VN_CURDATE(), INTERVAL -11 MONTH)),
- (2 , 1101, 5, 900, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 MONTH)),
- (3 , 1101, 5, 800, DATE_ADD(util.VN_CURDATE(), INTERVAL -9 MONTH)),
- (4 , 1101, 5, 700, DATE_ADD(util.VN_CURDATE(), INTERVAL -8 MONTH)),
- (5 , 1101, 5, 600, DATE_ADD(util.VN_CURDATE(), INTERVAL -7 MONTH)),
- (6 , 1101, 5, 500, DATE_ADD(util.VN_CURDATE(), INTERVAL -6 MONTH)),
- (7 , 1101, 5, 400, DATE_ADD(util.VN_CURDATE(), INTERVAL -5 MONTH)),
- (8 , 1101, 9, 300, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)),
- (9 , 1101, 9, 200, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)),
- (10, 1101, 9, 100, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
- (11, 1101, 9, 50 , DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
- (12, 1102, 9, 800, util.VN_CURDATE()),
- (14, 1104, 9, 90 , util.VN_CURDATE()),
- (15, 1105, 9, 90 , util.VN_CURDATE());
+ (1101, 5, 300, DATE_ADD(util.VN_CURDATE(), INTERVAL -11 MONTH)),
+ (1101, 5, 900, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 MONTH)),
+ (1101, 5, 800, DATE_ADD(util.VN_CURDATE(), INTERVAL -9 MONTH)),
+ (1101, 5, 700, DATE_ADD(util.VN_CURDATE(), INTERVAL -8 MONTH)),
+ (1101, 5, 600, DATE_ADD(util.VN_CURDATE(), INTERVAL -7 MONTH)),
+ (1101, 5, 500, DATE_ADD(util.VN_CURDATE(), INTERVAL -6 MONTH)),
+ (1101, 5, 400, DATE_ADD(util.VN_CURDATE(), INTERVAL -5 MONTH)),
+ (1101, 9, 300, DATE_ADD(util.VN_CURDATE(), INTERVAL -4 MONTH)),
+ (1101, 9, 200, DATE_ADD(util.VN_CURDATE(), INTERVAL -3 MONTH)),
+ (1101, 9, 100, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
+ (1101, 9, 50 , DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
+ (1102, 9, 800, util.VN_CURDATE()),
+ (1104, 9, 90 , util.VN_CURDATE()),
+ (1105, 9, 90 , util.VN_CURDATE());
INSERT INTO `vn`.`clientCreditLimit`(`id`, `maxAmount`, `roleFk`)
VALUES
@@ -604,7 +604,7 @@ INSERT INTO `vn`.`invoiceOutSerial` (`code`, `description`, `isTaxed`, `taxAreaF
INSERT INTO `vn`.`invoiceOut`(`id`, `serial`, `amount`, `issued`,`clientFk`, `created`, `companyFk`, `dued`, `booked`, `bankFk`, `hasPdf`)
VALUES
- (1, 'T', 1014.24, util.VN_CURDATE(), 1101, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
+ (1, 'T', 1026.24, util.VN_CURDATE(), 1101, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
(2, 'T', 121.36, util.VN_CURDATE(), 1102, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
(3, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
(4, 'T', 8.88, util.VN_CURDATE(), 1103, util.VN_CURDATE(), 442, util.VN_CURDATE(), util.VN_CURDATE(), 1, 0),
@@ -1454,7 +1454,7 @@ INSERT INTO `bs`.`waste`(`buyer`, `year`, `week`, `family`, `itemFk`, `itemTypeF
('HankPym', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Miscellaneous Accessories', 6, 1, '186', '0', '0.0'),
('HankPym', YEAR(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK)), WEEK(DATE_ADD(util.VN_CURDATE(), INTERVAL -1 WEEK), 1), 'Adhesives', 7, 1, '277', '0', '0.0');
-INSERT INTO `vn`.`buy`(`id`,`entryFk`,`itemFk`,`buyingValue`,`quantity`,`packageFk`,`stickers`,`freightValue`,`packageValue`,`comissionValue`,`packing`,`grouping`,`groupingMode`,`location`,`price1`,`price2`,`price3`, `printedStickers`,`isChecked`,`isIgnored`,`weight`, `created`)
+INSERT INTO `vn`.`buy`(`id`,`entryFk`,`itemFk`,`buyingValue`,`quantity`,`packagingFk`,`stickers`,`freightValue`,`packageValue`,`comissionValue`,`packing`,`grouping`,`groupingMode`,`location`,`price1`,`price2`,`price3`, `printedStickers`,`isChecked`,`isIgnored`,`weight`, `created`)
VALUES
(1, 1, 1, 50, 5000, 4, 1, 1.500, 1.500, 0.000, 1, 1, 1, NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -2 MONTH)),
(2, 2, 1, 50, 100, 4, 1, 1.500, 1.500, 0.000, 1, 1, 1, NULL, 0.00, 99.6, 99.4, 0, 1, 0, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL -1 MONTH)),
@@ -2003,6 +2003,10 @@ UPDATE `vn`.`business` b
SET b.`departmentFk` = 43
WHERE b.id IN(18, 19);
+UPDATE `vn`.`business` b
+ SET b.`started` = b.`started` - INTERVAL 100 DAY
+ WHERE b.id = 1107;
+
INSERT INTO `vn`.`workCenterHoliday` (`workCenterFk`, `days`, `year`)
VALUES
('1', '27.5', YEAR(util.VN_CURDATE())),
@@ -2051,22 +2055,22 @@ INSERT INTO `vn`.`absenceType` (`id`, `name`, `rgb`, `code`, `holidayEntitlement
INSERT INTO `vn`.`calendar` (`businessFk`, `dayOffTypeFk`, `dated`)
VALUES
- (1, 6, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 10 DAY))),
- (1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 10 DAY))),
- (1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -11 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 11 DAY))),
- (1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -12 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 12 DAY))),
- (1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -20 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 20 DAY))),
- (1106, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -13 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 8 DAY))),
- (1106, 1, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -14 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 9 DAY))),
- (1106, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 7 DAY))),
- (1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -10 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 10 DAY))),
- (1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -11 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 11 DAY))),
- (1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -12 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 12 DAY))),
- (1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, DATE_ADD(util.VN_CURDATE(), INTERVAL -20 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 20 DAY))),
- (1107, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -13 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 8 DAY))),
- (1107, 1, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -14 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 9 DAY))),
- (1107, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, DATE_ADD(util.VN_CURDATE(), INTERVAL -15 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL 7 DAY))),
- (1107, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL - 16 DAY));
+ (1, 6, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 10 DAY, util.VN_CURDATE() + INTERVAL 10 DAY)),
+ (1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 10 DAY, util.VN_CURDATE() + INTERVAL 10 DAY)),
+ (1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 11 DAY, util.VN_CURDATE() + INTERVAL 11 DAY)),
+ (1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 12 DAY, util.VN_CURDATE() + INTERVAL 12 DAY)),
+ (1106, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 20 DAY, util.VN_CURDATE() + INTERVAL 20 DAY)),
+ (1106, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 13 DAY, util.VN_CURDATE() + INTERVAL 8 DAY)),
+ (1106, 1, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 14 DAY, util.VN_CURDATE() + INTERVAL 9 DAY)),
+ (1106, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 15 DAY, util.VN_CURDATE() + INTERVAL 7 DAY)),
+ (1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 10 DAY, util.VN_CURDATE() + INTERVAL 10 DAY)),
+ (1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 11 DAY, util.VN_CURDATE() + INTERVAL 11 DAY)),
+ (1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 12 DAY, util.VN_CURDATE() + INTERVAL 12 DAY)),
+ (1107, 1, IF(MONTH(util.VN_CURDATE()) = 12 AND DAY(util.VN_CURDATE()) > 10, util.VN_CURDATE() - INTERVAL 20 DAY, util.VN_CURDATE() + INTERVAL 20 DAY)),
+ (1107, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 13 DAY, util.VN_CURDATE() + INTERVAL 8 DAY)),
+ (1107, 1, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 14 DAY, util.VN_CURDATE() + INTERVAL 9 DAY)),
+ (1107, 2, IF(MONTH(util.VN_CURDATE()) >= 1 AND DAY(util.VN_CURDATE()) > 20, util.VN_CURDATE() - INTERVAL 15 DAY, util.VN_CURDATE() + INTERVAL 7 DAY)),
+ (1107, 2, util.VN_CURDATE() - INTERVAL 16 DAY);
INSERT INTO `vn`.`smsConfig` (`id`, `uri`, `title`, `apiKey`)
VALUES
@@ -2754,9 +2758,9 @@ INSERT INTO `vn`.`sectorCollectionSaleGroup` (`sectorCollectionFk`, `saleGroupFk
VALUES
(1, 1);
-INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`, `shortWeekBreak`, `longWeekBreak`, `weekScope`, `mailPass`, `mailHost`, `mailSuccessFolder`, `mailErrorFolder`, `mailUser`, `minHoursToBreak`, `breakHours`, `hoursCompleteWeek`, `startNightlyHours`, `endNightlyHours`, `maxTimePerDay`, `breakTime`, `timeToBreakTime`, `dayMaxTime`, `shortWeekDays`, `longWeekDays`, `teleworkingStart`, `teleworkingStartBreakTime`)
+INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`, `shortWeekBreak`, `longWeekBreak`, `weekScope`, `mailPass`, `mailHost`, `mailSuccessFolder`, `mailErrorFolder`, `mailUser`, `minHoursToBreak`, `breakHours`, `hoursCompleteWeek`, `startNightlyHours`, `endNightlyHours`, `maxTimePerDay`, `breakTime`, `timeToBreakTime`, `dayMaxTime`, `shortWeekDays`, `longWeekDays`, `teleworkingStart`, `teleworkingStartBreakTime`, `maxTimeToBreak`, `maxWorkShortCycle`, `maxWorkLongCycle`)
VALUES
- (1, 43200, 32400, 129600, 259200, 604800, '', '', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.33, 0.33, 40, '22:00:00', '06:00:00', 57600, 1200, 18000, 57600, 6, 13, 28800, 32400);
+ (1, 43200, 32400, 129600, 259200, 1080000, '', 'imap.verdnatura.es', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.00, 0.33, 40, '22:00:00', '06:00:00', 72000, 1200, 18000, 72000, 6, 13, 28800, 32400, 3600, 561600, 950400);
INSERT INTO `vn`.`host` (`id`, `code`, `description`, `warehouseFk`, `bankFk`)
VALUES
@@ -2784,6 +2788,11 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`)
INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`)
VALUES
(1, 9),
+ (1, 1),
+ (2, 1),
+ (3, 9),
+ (4, 1),
+ (5, 9),
(6, 9);
INSERT INTO `util`.`notificationQueue` (`id`, `notificationFk`, `params`, `authorFk`, `status`, `created`)
@@ -2796,6 +2805,8 @@ INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`)
VALUES
(1, 1109),
(1, 1110),
+ (2, 1110),
+ (4, 1110),
(2, 1109),
(1, 9),
(1, 3),
@@ -2867,6 +2878,7 @@ INSERT INTO `vn`.`profileType` (`id`, `name`)
INSERT INTO `salix`.`url` (`appName`, `environment`, `url`)
VALUES
('lilium', 'development', 'http://localhost:9000/#/'),
+ ('hedera', 'development', 'http://localhost:9090/'),
('salix', 'development', 'http://localhost:5000/#!/');
INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`)
@@ -2976,3 +2988,9 @@ INSERT INTO vn.XDiario (id, ASIEN, FECHA, SUBCTA, CONTRA, CONCEPTO, EURODEBE, EU
INSERT INTO `vn`.`mistakeType` (`id`, `description`)
VALUES
(1, 'Incorrect quantity');
+
+INSERT INTO `vn`.`invoiceCorrectionType` (`id`, `description`)
+ VALUES
+ (1, 'Error in VAT calculation'),
+ (2, 'Error in sales details'),
+ (3, 'Error in customer data');
diff --git a/db/dump/structure.sql b/db/dump/structure.sql
index 08df0541c..b242821fc 100644
--- a/db/dump/structure.sql
+++ b/db/dump/structure.sql
@@ -30434,6 +30434,7 @@ CREATE TABLE `item` (
`editorFk` int(10) unsigned DEFAULT NULL,
`recycledPlastic` int(11) DEFAULT NULL,
`nonRecycledPlastic` int(11) DEFAULT NULL,
+ `minQuantity` int(10) unsigned DEFAULT NULL COMMENT 'Cantidad mínima para una línea de venta',
PRIMARY KEY (`id`),
UNIQUE KEY `item_supplyResponseFk_idx` (`supplyResponseFk`),
KEY `Color` (`inkFk`),
diff --git a/db/tests/vn/ticketCalculateClon.spec.js b/db/tests/vn/ticketCalculateClon.spec.js
index 9116d805f..665d52ed0 100644
--- a/db/tests/vn/ticketCalculateClon.spec.js
+++ b/db/tests/vn/ticketCalculateClon.spec.js
@@ -53,7 +53,8 @@ describe('ticket ticketCalculateClon()', () => {
expect(result[orderIndex][0].ticketFk).toBeGreaterThan(newestTicketIdInFixtures);
});
- it('should add the ticket to the order containing the original ticket and generate landed value if it was null', async() => {
+ it('should add the ticket to the order containing the original ' +
+ 'ticket and generate landed value if it was null', async() => {
let stmts = [];
let stmt;
diff --git a/db/tests/vn/timeControl_calculateByUser.spec.js b/db/tests/vn/timeControl_calculateByUser.spec.js
deleted file mode 100644
index 0b385d2c9..000000000
--- a/db/tests/vn/timeControl_calculateByUser.spec.js
+++ /dev/null
@@ -1,91 +0,0 @@
-const app = require('vn-loopback/server/server');
-const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
-
-describe('timeControl_calculateByUser()', () => {
- it(`should return today's worked hours`, async() => {
- let start = Date.vnNew();
- start.setHours(0, 0, 0, 0);
- start.setDate(start.getDate() - 1);
-
- let end = Date.vnNew();
- end.setHours(0, 0, 0, 0);
- end.setDate(end.getDate() + 1);
-
- let stmts = [];
- let stmt;
-
- let params = {
- workerID: 1106,
- start: start,
- end: end
- };
-
- stmt = new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
- params.workerID,
- params.start,
- params.end
- ]);
- stmts.push(stmt);
-
- let tableIndex = stmts.push('SELECT * FROM tmp.timeControlCalculate') - 1;
-
- let sql = ParameterizedSQL.join(stmts, ';');
- let result = await app.models.Ticket.rawStmt(sql);
-
- let [timeControlCalculateTable] = result[tableIndex];
-
- expect(timeControlCalculateTable.timeWorkSeconds).toEqual(28200);
- });
- // #2261
- xit(`should return the worked hours between last sunday and monday`, async() => {
- let lastSunday = Date.vnNew();
- let daysSinceSunday = lastSunday.getDay();
- if (daysSinceSunday === 0) // this means today is sunday but you need the previous sunday :)
- daysSinceSunday = 7;
- lastSunday.setHours(23, 0, 0, 0);
- lastSunday.setDate(lastSunday.getDate() - daysSinceSunday);
-
- let monday = Date.vnNew();
- let daysSinceMonday = daysSinceSunday - 1; // aiming for monday (today could be monday)
- monday.setHours(7, 0, 0, 0);
- monday.setDate(monday.getDate() - daysSinceMonday);
-
- let stmts = [];
- let stmt;
-
- stmts.push('START TRANSACTION');
-
- const workerID = 1108;
-
- stmt = new ParameterizedSQL(`
- INSERT INTO vn.workerTimeControl(userFk, timed, manual, direction)
- VALUES
- (?, ?, 1, 'in'),
- (?, ?, 1, 'out')
- `, [
- workerID,
- lastSunday,
- workerID,
- monday
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.timeControl_calculateByUser(?, ?, ?)', [
- workerID,
- lastSunday,
- monday
- ]);
- stmts.push(stmt);
-
- let tableIndex = stmts.push('SELECT * FROM tmp.timeControlCalculate') - 1;
-
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
- let result = await app.models.Ticket.rawStmt(sql);
-
- let [timeControlCalculateTable] = result[tableIndex];
-
- expect(timeControlCalculateTable.timeWorkSeconds).toEqual(30000);
- });
-});
diff --git a/db/tests/vn/workerTimeControlCheck.spec.js b/db/tests/vn/workerTimeControlCheck.spec.js
deleted file mode 100644
index 0ca1429d4..000000000
--- a/db/tests/vn/workerTimeControlCheck.spec.js
+++ /dev/null
@@ -1,580 +0,0 @@
-const app = require('vn-loopback/server/server');
-const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
-
-// #2261 xdescribe dbtest workerTimeControl_check()
-xdescribe('worker workerTimeControl_check()', () => {
- it(`should throw an error if the worker can't sign on that tablet`, async() => {
- let stmts = [];
- let stmt;
- const workerId = 1110;
- const tabletId = 2;
- let err;
- stmts.push('START TRANSACTION');
- try {
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
-
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- err = e;
- }
-
- expect(err.sqlMessage).toEqual('No perteneces a este departamento.');
- });
-
- it('should check that the worker can sign on that tablet', async() => {
- let stmts = [];
- let stmt;
- const workerId = 1110;
- const tabletId = 1;
- let err;
- stmts.push('START TRANSACTION');
- try {
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
-
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- err = e;
- }
-
- expect(err).not.toBeDefined();
- });
-
- it('should throw an error if the worker with a special category has not finished the 9h break', async() => {
- const workerId = 1110;
- const tabletId = 1;
- let stmts = [];
- let stmt;
- let sql;
- let error;
-
- stmts.push('START TRANSACTION');
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-17,NOW()),0,"in"),
- (?,TIMESTAMPADD(SECOND,-32399,NOW()),0,"out")`, [
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
- sql = ParameterizedSQL.join(stmts, ';');
-
- try {
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- await app.models.Worker.rawSql('ROLLBACK');
- error = e;
- }
-
- expect(error.sqlMessage).toEqual('Descansos 9 h');
- });
-
- it('should check f the worker with a special category has finished the 9h break', async() => {
- const workerId = 1110;
- const tabletId = 1;
- let stmts = [];
- let stmt;
- let err;
- stmts.push('START TRANSACTION');
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-17,NOW()),0,"in"),
- (?,TIMESTAMPADD(SECOND,-32401,NOW()),0,"out")`, [
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
-
- try {
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- await app.models.Worker.rawSql('ROLLBACK');
- err = e;
- }
-
- expect(err).not.toBeDefined();
- });
-
- it('should throw an error if the worker has not finished the 12h break', async() => {
- const workerId = 1109;
- const tabletId = 1;
- let stmts = [];
- let stmt;
- let sql;
- let error;
-
- stmts.push('START TRANSACTION');
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-20,NOW()),0,"in"),
- (?,TIMESTAMPADD(SECOND,-43199,NOW()),0,"out")`, [
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
- sql = ParameterizedSQL.join(stmts, ';');
-
- try {
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- await app.models.Worker.rawSql('ROLLBACK');
- error = e;
- }
-
- expect(error.sqlMessage).toEqual('Descansos 12 h');
- });
-
- it('should throw an error if the worker has finished the 12h break', async() => {
- const workerId = 1109;
- const tabletId = 1;
- let stmts = [];
- let stmt;
- let err;
- stmts.push('START TRANSACTION');
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-20,NOW()),0,"in"),
- (?,TIMESTAMPADD(SECOND,-43201,NOW()),0,"out")`, [
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
-
- try {
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- await app.models.Worker.rawSql('ROLLBACK');
- err = e;
- }
-
- expect(err).not.toBeDefined();
- });
-
- it('should throw an error if the worker has odd entry records', async() => {
- const workerId = 1109;
- const tabletId = 1;
- let stmts = [];
- let stmt;
- let err;
- stmts.push('START TRANSACTION');
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in")`, [
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
-
- try {
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- await app.models.Worker.rawSql('ROLLBACK');
- err = e;
- }
-
- expect(err.sqlMessage).toEqual('Dias con fichadas impares');
- });
-
- it('should throw an error if the worker try to sign on a holiday day', async() => {
- const workerId = 1109;
- const tabletId = 1;
- let stmts = [];
- let stmt;
- let err;
-
- stmts.push('START TRANSACTION');
-
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-20,NOW()),0,"out")`, [
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
-
- try {
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- await app.models.Worker.rawSql('ROLLBACK');
- err = e;
- }
-
- expect(err.sqlMessage).toEqual('Holidays');
- });
-
- it('should throw an error if the worker try to sign with your contract ended', async() => {
- const workerId = 1109;
- const tabletId = 1;
- let stmts = [];
- let stmt;
- let err;
-
- stmts.push('START TRANSACTION');
-
- stmt = new ParameterizedSQL(`UPDATE vn.business SET ended = DATE_ADD(CURDATE(), INTERVAL -1 DAY) WHERE id = ?`, [
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-20,NOW()),0,"out")`, [
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
-
- let sql = ParameterizedSQL.join(stmts, ';');
-
- try {
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- await app.models.Worker.rawSql('ROLLBACK');
- err = e;
- }
-
- expect(err.sqlMessage).toEqual('No hay un contrato en vigor');
- });
-
- it('should throw an error if the worker has not finished the 36h weekly break', async() => {
- const workerId = 1109;
- const tabletId = 1;
- let stmts = [];
- let stmt;
-
- stmts.push('SET @warn := NULL');
-
- stmts.push('START TRANSACTION');
-
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-16,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-48,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-40,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-72,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-64,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-96,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-88,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-120,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-112,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-144,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-136,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-168,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-160,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-192,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-184,NOW()),0,"out")`, [
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
-
- let warningMessageIndex = stmts.push('SELECT @warn AS warning') - 1;
- stmts.push('ROLLBACK');
- let sql = ParameterizedSQL.join(stmts, ';');
- let result = await app.models.Worker.rawStmt(sql);
-
- expect(result[warningMessageIndex][0].warning).toEqual('Descansos 36 h');
- });
-
- it('should check if the worker has finished the 36h weekly break', async() => {
- const workerId = 1109;
- const tabletId = 1;
- let stmts = [];
- let stmt;
-
- stmts.push('SET @warn := NULL');
-
- stmts.push('START TRANSACTION');
-
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-16,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-48,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-40,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-72,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-64,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-96,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-88,NOW()),0,"out")`, [
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
-
- let warningMessageIndex = stmts.push('SELECT @warn AS warning') - 1;
-
- let sql = ParameterizedSQL.join(stmts, ';');
- let result = await app.models.Worker.rawStmt(sql);
-
- expect(result[warningMessageIndex][0].warning).toBe(null);
- });
-
- it('should throw an error if the worker has not finished the 72h biweekly break', async() => {
- const workerId = 1109;
- const tabletId = 1;
- let stmts = [];
- let stmt;
- let err;
- stmts.push('START TRANSACTION');
-
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-16,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-48,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-40,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-72,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-64,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-96,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-88,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-120,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-112,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-144,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-136,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-168,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-160,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-192,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-184,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-216,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-208,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-240,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-232,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-264,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-256,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-289,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-280,NOW()),0,"out")`, [
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
-
- stmts.push('SELECT @warn AS warning') - 1;
-
- let sql = ParameterizedSQL.join(stmts, ';');
-
- try {
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- await app.models.Worker.rawSql('ROLLBACK');
- err = e;
- }
-
- expect(err.sqlMessage).toEqual('Descansos 72 h');
- });
-
- it('should check if the worker has finished the 72h biweekly break', async() => {
- const workerId = 1109;
- const tabletId = 1;
- let stmts = [];
- let stmt;
- let err;
- stmts.push('START TRANSACTION');
-
- stmt = new ParameterizedSQL(`INSERT INTO vn.workerTimeControl(userFk,timed,manual,direction)
- VALUES
- (?,TIMESTAMPADD(HOUR,-24,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-16,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-48,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-40,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-72,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-64,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-96,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-88,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-120,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-112,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-144,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-136,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-168,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-160,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-192,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-184,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-216,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-208,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-240,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-232,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-264,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-256,NOW()),0,"out"),
- (?,TIMESTAMPADD(HOUR,-288,NOW()),0,"in"),
- (?,TIMESTAMPADD(HOUR,-280,NOW()),0,"out")`, [
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId,
- workerId
- ]);
- stmts.push(stmt);
-
- stmt = new ParameterizedSQL('CALL vn.workerTimeControl_check(?, ?, NULL)', [
- workerId,
- tabletId
- ]);
- stmts.push(stmt);
- stmts.push('ROLLBACK');
-
- stmts.push('SELECT @warn AS warning') - 1;
-
- let sql = ParameterizedSQL.join(stmts, ';');
-
- try {
- await app.models.Worker.rawStmt(sql);
- } catch (e) {
- await app.models.Worker.rawSql('ROLLBACK');
- err = e;
- }
-
- expect(err).not.toBeDefined();
- });
-});
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index 6f78d7c4e..cec0545a0 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -409,11 +409,11 @@ export default {
inactiveIcon: 'vn-item-descriptor vn-icon[icon="icon-unavailable"]'
},
itemRequest: {
- firstRequestItemID: 'vn-item-request vn-tbody > vn-tr:nth-child(1) > vn-td-editable:nth-child(7)',
- firstRequestQuantity: 'vn-item-request vn-tbody > vn-tr:nth-child(1) > vn-td-editable:nth-child(8)',
- firstRequestConcept: 'vn-item-request vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(9)',
- firstRequestStatus: 'vn-item-request vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(10)',
- secondRequestStatus: 'vn-item-request vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(10)',
+ firstRequestItemID: 'vn-item-request vn-tbody > vn-tr:nth-child(1) > vn-td-editable:nth-child(8)',
+ firstRequestQuantity: 'vn-item-request vn-tbody > vn-tr:nth-child(1) > vn-td-editable:nth-child(9)',
+ firstRequestConcept: 'vn-item-request vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(10)',
+ firstRequestStatus: 'vn-item-request vn-tbody > vn-tr:nth-child(1) > vn-td:nth-child(11)',
+ secondRequestStatus: 'vn-item-request vn-tbody > vn-tr:nth-child(2) > vn-td:nth-child(11)',
secondRequestDecline: 'vn-item-request vn-tr:nth-child(2) vn-icon-button[icon="thumb_down"]',
declineReason: 'vn-textarea[ng-model="$ctrl.denyObservation"]'
},
@@ -1192,7 +1192,7 @@ export default {
secondBuyPacking: 'vn-entry-buy-index tbody:nth-child(3) > tr:nth-child(1) vn-input-number[ng-model="buy.packing"]',
secondBuyWeight: 'vn-entry-buy-index tbody:nth-child(3) > tr:nth-child(1) vn-input-number[ng-model="buy.weight"]',
secondBuyStickers: 'vn-entry-buy-index tbody:nth-child(3) > tr:nth-child(1) vn-input-number[ng-model="buy.stickers"]',
- secondBuyPackage: 'vn-entry-buy-index tbody:nth-child(3) > tr:nth-child(1) vn-autocomplete[ng-model="buy.packageFk"]',
+ secondBuyPackage: 'vn-entry-buy-index tbody:nth-child(3) > tr:nth-child(1) vn-autocomplete[ng-model="buy.packagingFk"]',
secondBuyQuantity: 'vn-entry-buy-index tbody:nth-child(3) > tr:nth-child(1) vn-input-number[ng-model="buy.quantity"]',
secondBuyItem: 'vn-entry-buy-index tbody:nth-child(3) > tr:nth-child(1) vn-autocomplete[ng-model="buy.itemFk"]',
importButton: 'vn-entry-buy-index vn-icon[icon="publish"]',
diff --git a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
index 6264073f6..5e82306cc 100644
--- a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
+++ b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
@@ -134,14 +134,6 @@ describe('Ticket Edit sale path', () => {
await page.accessToSection('ticket.card.sale');
});
- it('should try to add a higher quantity value and then receive an error', async() => {
- await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
- await page.type(selectors.ticketSales.firstSaleQuantity, '11\u000d');
- const message = await page.waitForSnackbar();
-
- expect(message.text).toContain('The new quantity should be smaller than the old one');
- });
-
it('should remove 1 from the first sale quantity', async() => {
await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
await page.waitForSelector(selectors.ticketSales.firstSaleQuantity);
@@ -220,7 +212,7 @@ describe('Ticket Edit sale path', () => {
it('should log in as salesAssistant and navigate to ticket sales', async() => {
await page.loginAndModule('salesAssistant', 'ticket');
- await page.accessToSearchResult('17');
+ await page.accessToSearchResult('15');
await page.accessToSection('ticket.card.sale');
});
@@ -324,7 +316,7 @@ describe('Ticket Edit sale path', () => {
});
it('should confirm the transfered quantity is the correct one', async() => {
- const result = await page.waitToGetProperty(selectors.ticketSales.secondSaleQuantityCell, 'innerText');
+ const result = await page.waitToGetProperty(selectors.ticketSales.firstSaleQuantityCell, 'innerText');
expect(result).toContain('20');
});
@@ -378,7 +370,7 @@ describe('Ticket Edit sale path', () => {
await page.waitToClick(selectors.ticketSales.moveToNewTicketButton);
const message = await page.waitForSnackbar();
- expect(message.text).toContain(`You can't create a ticket for a inactive client`);
+ expect(message.text).toContain(`You can't create a ticket for an inactive client`);
await page.closePopup();
});
diff --git a/e2e/paths/13-supplier/03_fiscal_data.spec.js b/e2e/paths/13-supplier/03_fiscal_data.spec.js
index 891b769c9..96977e708 100644
--- a/e2e/paths/13-supplier/03_fiscal_data.spec.js
+++ b/e2e/paths/13-supplier/03_fiscal_data.spec.js
@@ -1,20 +1,5 @@
import getBrowser from '../../helpers/puppeteer';
-const $ = {
- saveButton: 'vn-supplier-fiscal-data button[type="submit"]',
-};
-const $inputs = {
- province: 'vn-supplier-fiscal-data [name="province"]',
- country: 'vn-supplier-fiscal-data [name="country"]',
- postcode: 'vn-supplier-fiscal-data [name="postcode"]',
- city: 'vn-supplier-fiscal-data [name="city"]',
- socialName: 'vn-supplier-fiscal-data [name="socialName"]',
- taxNumber: 'vn-supplier-fiscal-data [name="taxNumber"]',
- account: 'vn-supplier-fiscal-data [name="account"]',
- sageWithholding: 'vn-supplier-fiscal-data [ng-model="$ctrl.supplier.sageWithholdingFk"]',
- sageTaxType: 'vn-supplier-fiscal-data [ng-model="$ctrl.supplier.sageTaxTypeFk"]'
-};
-
describe('Supplier fiscal data path', () => {
let browser;
let page;
@@ -30,7 +15,7 @@ describe('Supplier fiscal data path', () => {
await browser.close();
});
- it('should attempt to edit the fiscal data and check data is saved', async() => {
+ it('should attempt to edit the fiscal data and check data iss saved', async() => {
await page.accessToSection('supplier.card.fiscalData');
const form = 'vn-supplier-fiscal-data form';
@@ -40,16 +25,16 @@ describe('Supplier fiscal data path', () => {
postcode: null,
city: 'Valencia',
socialName: 'Farmer King SL',
- taxNumber: 'Wrong tax number',
+ taxNumber: '12345678Z',
account: '0123456789',
sageWithholding: 'retencion estimacion objetiva',
sageTaxType: 'operaciones no sujetas'
};
- const errorMessage = await page.sendForm(form, values);
- const message = await page.sendForm(form, {
- taxNumber: '12345678Z'
+ const errorMessage = await page.sendForm(form, {
+ taxNumber: 'Wrong tax number'
});
+ const message = await page.sendForm(form, values);
await page.reloadSection('supplier.card.fiscalData');
const formValues = await page.fetchForm(form, Object.keys(values));
diff --git a/front/Dockerfile b/front/Dockerfile
index 098ee161e..d0ee26904 100644
--- a/front/Dockerfile
+++ b/front/Dockerfile
@@ -1,4 +1,4 @@
-FROM debian:stretch-slim
+FROM debian:bookworm-slim
EXPOSE 80
ENV TZ Europe/Madrid
diff --git a/front/core/components/index.js b/front/core/components/index.js
index eb40d5e1f..cfa46f0cb 100644
--- a/front/core/components/index.js
+++ b/front/core/components/index.js
@@ -39,6 +39,7 @@ import './range';
import './input-time';
import './input-file';
import './label';
+import './link-phone';
import './list';
import './popover';
import './popup';
diff --git a/front/core/components/link-phone/index.html b/front/core/components/link-phone/index.html
new file mode 100644
index 000000000..2789ab75c
--- /dev/null
+++ b/front/core/components/link-phone/index.html
@@ -0,0 +1,14 @@
+
+ {{$ctrl.phoneNumber}}
+
+