From c2de5c5ff4fa4c07f9c6721b540832d566778b52 Mon Sep 17 00:00:00 2001 From: alexandre Date: Tue, 2 May 2023 07:28:59 +0200 Subject: [PATCH 01/81] refs #4797 added redirection section --- db/dump/fixtures.sql | 7 +++++- loopback/locale/es.json | 23 ++++++++++--------- modules/client/back/models/client.json | 2 +- modules/worker/front/index.js | 1 + modules/worker/front/notifications/index.html | 2 ++ modules/worker/front/notifications/index.js | 21 +++++++++++++++++ modules/worker/front/routes.json | 9 ++++++++ 7 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 modules/worker/front/notifications/index.html create mode 100644 modules/worker/front/notifications/index.js diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 15ccece355..6554adc33c 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2723,7 +2723,10 @@ INSERT INTO `util`.`notification` (`id`, `name`, `description`) INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`) VALUES - (1, 9); + (1, 9), + (1, 1), + (2, 1), + (4, 1); INSERT INTO `util`.`notificationQueue` (`id`, `notificationFk`, `params`, `authorFk`, `status`, `created`) VALUES @@ -2735,6 +2738,8 @@ INSERT INTO `util`.`notificationSubscription` (`notificationFk`, `userFk`) VALUES (1, 1109), (1, 1110), + (2, 1110), + (4, 1110), (2, 1109), (1,9), (1,3); diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 3ef3c4a227..628f7ace68 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -279,15 +279,16 @@ "Comment added to client": "Observación añadida al cliente {{clientFk}}", "Cannot create a new claimBeginning from a different ticket": "No se puede crear una línea de reclamación de un ticket diferente al origen", "company": "Compañía", - "country": "País", - "clientId": "Id cliente", - "clientSocialName": "Cliente", - "amount": "Importe", - "taxableBase": "Base", - "ticketFk": "Id ticket", - "isActive": "Activo", - "hasToInvoice": "Facturar", - "isTaxDataChecked": "Datos comprobados", - "comercialId": "Id comercial", - "comercialName": "Comercial" + "country": "País", + "clientId": "Id cliente", + "clientSocialName": "Cliente", + "amount": "Importe", + "taxableBase": "Base", + "ticketFk": "Id ticket", + "isActive": "Activo", + "hasToInvoice": "Facturar", + "isTaxDataChecked": "Datos comprobados", + "comercialId": "Id comercial", + "comercialName": "Comercial", + "You dont have permission to modify this user": "No tienes permiso para modificar este usuario" } diff --git a/modules/client/back/models/client.json b/modules/client/back/models/client.json index 5f56a1ed2e..73b6cccb45 100644 --- a/modules/client/back/models/client.json +++ b/modules/client/back/models/client.json @@ -158,7 +158,7 @@ }, "user": { "type": "belongsTo", - "model": "Account", + "model": "VnUser", "foreignKey": "id" }, "payMethod": { diff --git a/modules/worker/front/index.js b/modules/worker/front/index.js index 8fad2c0df7..5c03dc8de7 100644 --- a/modules/worker/front/index.js +++ b/modules/worker/front/index.js @@ -20,4 +20,5 @@ import './dms/create'; import './dms/edit'; import './note/index'; import './note/create'; +import './notifications'; diff --git a/modules/worker/front/notifications/index.html b/modules/worker/front/notifications/index.html new file mode 100644 index 0000000000..7fb3b870e0 --- /dev/null +++ b/modules/worker/front/notifications/index.html @@ -0,0 +1,2 @@ + + diff --git a/modules/worker/front/notifications/index.js b/modules/worker/front/notifications/index.js new file mode 100644 index 0000000000..622892979a --- /dev/null +++ b/modules/worker/front/notifications/index.js @@ -0,0 +1,21 @@ +import ngModule from '../module'; +import Section from 'salix/components/section'; + +class Controller extends Section { + constructor($element, $) { + super($element, $); + } + + async $onInit() { + const url = await this.vnApp.getUrl(`worker/${this.$params.id}/notifications`); + window.open(url).focus(); + } +} + +ngModule.vnComponent('vnWorkerNotifications', { + template: require('./index.html'), + controller: Controller, + bindings: { + ticket: '<' + } +}); diff --git a/modules/worker/front/routes.json b/modules/worker/front/routes.json index 25a0ffbcf2..bce0172a20 100644 --- a/modules/worker/front/routes.json +++ b/modules/worker/front/routes.json @@ -15,6 +15,7 @@ {"state": "worker.card.timeControl", "icon": "access_time"}, {"state": "worker.card.calendar", "icon": "icon-calendar"}, {"state": "worker.card.pda", "icon": "phone_android"}, + {"state": "worker.card.notifications", "icon": "notifications"}, {"state": "worker.card.pbx", "icon": "icon-pbx"}, {"state": "worker.card.dms.index", "icon": "cloud_upload"}, { @@ -109,6 +110,14 @@ "params": { "worker": "$ctrl.worker" } + }, { + "url": "/notifications", + "state": "worker.card.notifications", + "component": "vn-worker-notifications", + "description": "Notifications", + "params": { + "worker": "$ctrl.worker" + } }, { "url": "/time-control?timestamp", "state": "worker.card.timeControl", From 70c46efe275e0e518c884d8e76eedf4174f02ec9 Mon Sep 17 00:00:00 2001 From: alexandre Date: Tue, 2 May 2023 09:51:04 +0200 Subject: [PATCH 02/81] refs #4797 fix back test --- .../specs/notificationSubscription.spec.js | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/back/models/specs/notificationSubscription.spec.js b/back/models/specs/notificationSubscription.spec.js index c7f37abedd..c5ef698fd9 100644 --- a/back/models/specs/notificationSubscription.spec.js +++ b/back/models/specs/notificationSubscription.spec.js @@ -1,8 +1,9 @@ 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 delete a notification if the user is not editing itself or a subordinate', async() => { const tx = await models.NotificationSubscription.beginTransaction({}); + let error; try { const options = {transaction: tx}; @@ -11,29 +12,23 @@ describe('loopback model NotificationSubscription', () => { const ctx = {req: {accessToken: {userId: user}}}; const notification = await models.NotificationSubscription.findById(notificationSubscriptionId); - let error; - - try { - await models.NotificationSubscription.deleteNotification(ctx, notification.id, options); - } catch (e) { - error = e; - } + await models.NotificationSubscription.deleteNotification(ctx, notification.id, options); expect(error.message).toContain('You dont have permission to modify this user'); await tx.rollback(); } catch (e) { + error = e; await tx.rollback(); - throw e; } }); - it('Should delete a notification if the user is editing itself', async() => { + it('should delete a notification if the user is editing itself', async() => { const tx = await models.NotificationSubscription.beginTransaction({}); try { const options = {transaction: tx}; const user = 9; - const notificationSubscriptionId = 4; + const notificationSubscriptionId = 6; const ctx = {req: {accessToken: {userId: user}}}; const notification = await models.NotificationSubscription.findById(notificationSubscriptionId); @@ -49,13 +44,13 @@ describe('loopback model NotificationSubscription', () => { } }); - it('Should delete a notification if the user is editing a subordinate', async() => { + 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 user = 19; + const notificationSubscriptionId = 4; const ctx = {req: {accessToken: {userId: user}}}; const notification = await models.NotificationSubscription.findById(notificationSubscriptionId); From 4c0ec6faf53a4118a46e97ddc1b59d7d71651341 Mon Sep 17 00:00:00 2001 From: alexandre Date: Tue, 2 May 2023 14:26:47 +0200 Subject: [PATCH 03/81] changedModel sms --- e2e/paths/13-supplier/03_fiscal_data.spec.js | 2 +- modules/client/back/methods/client/sendSms.js | 2 +- modules/ticket/back/methods/ticket/sendSms.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/paths/13-supplier/03_fiscal_data.spec.js b/e2e/paths/13-supplier/03_fiscal_data.spec.js index 31b3fafd2f..170a8d05ac 100644 --- a/e2e/paths/13-supplier/03_fiscal_data.spec.js +++ b/e2e/paths/13-supplier/03_fiscal_data.spec.js @@ -15,7 +15,7 @@ const $inputs = { sageTaxType: 'vn-supplier-fiscal-data [ng-model="$ctrl.supplier.sageTaxTypeFk"]' }; -fdescribe('Supplier fiscal data path', () => { +describe('Supplier fiscal data path', () => { let browser; let page; diff --git a/modules/client/back/methods/client/sendSms.js b/modules/client/back/methods/client/sendSms.js index 9d6a12416e..456d1d6217 100644 --- a/modules/client/back/methods/client/sendSms.js +++ b/modules/client/back/methods/client/sendSms.js @@ -44,7 +44,7 @@ module.exports = Self => { originFk: id, userFk: userId, action: 'insert', - changedModel: 'sms', + changedModel: 'Sms', newInstance: { destinationFk: id, destination: destination, diff --git a/modules/ticket/back/methods/ticket/sendSms.js b/modules/ticket/back/methods/ticket/sendSms.js index 2336ae8595..3c361b7833 100644 --- a/modules/ticket/back/methods/ticket/sendSms.js +++ b/modules/ticket/back/methods/ticket/sendSms.js @@ -58,7 +58,7 @@ module.exports = Self => { originFk: id, userFk: userId, action: 'insert', - changedModel: 'sms', + changedModel: 'Sms', newInstance: { destinationFk: id, destination: destination, From e7d16dc8baf1e26c8a02cd7f80082b49967a1f9d Mon Sep 17 00:00:00 2001 From: alexandre Date: Tue, 9 May 2023 11:34:32 +0200 Subject: [PATCH 04/81] refs #4797 fix test, add getList --- back/methods/notification/getList.js | 80 ++++++++++++ .../notification/specs/getList.spec.js | 38 ++++++ back/models/notificationSubscription.js | 76 ++++------- .../specs/notificationSubscription.spec.js | 123 +++++++++++++----- .../232001/00-notificationSubscription.sql | 3 + loopback/locale/es.json | 3 +- 6 files changed, 240 insertions(+), 83 deletions(-) create mode 100644 back/methods/notification/getList.js create mode 100644 back/methods/notification/specs/getList.spec.js create mode 100644 db/changes/232001/00-notificationSubscription.sql diff --git a/back/methods/notification/getList.js b/back/methods/notification/getList.js new file mode 100644 index 0000000000..9f33671843 --- /dev/null +++ b/back/methods/notification/getList.js @@ -0,0 +1,80 @@ +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 notifications = []; + const models = Self.app.models; + + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + const activeNotifications = await models.NotificationSubscription.find({ + include: {relation: 'notification'}, + where: {userFk: id} + }, myOptions); + + const roles = await models.RoleMapping.find({ + fields: ['roleId'], + where: {principalId: id} + }, myOptions); + + const availableNotifications = await models.NotificationAcl.find({ + include: {relation: 'notification'}, + where: { + roleFk: { + inq: roles.map(role => { + return role.roleId; + }), + }, + } + }, myOptions); + + activeNotifications.forEach(subscription => { + notifications.push({ + id: subscription.id, + notificationFk: subscription.notificationFk, + name: subscription.notification().name, + description: subscription.notification().description, + active: true + }); + }); + + availableNotifications.forEach(acl => { + const activeNotif = notifications.find( + notif => notif.notificationFk === acl.notificationFk + ); + if (!activeNotif) { + notifications.push({ + id: null, + notificationFk: acl.notificationFk, + name: acl.notification().name, + description: acl.notification().description, + active: false, + }); + } + }); + + return notifications; + }; +}; diff --git a/back/methods/notification/specs/getList.spec.js b/back/methods/notification/specs/getList.spec.js new file mode 100644 index 0000000000..b220f2215a --- /dev/null +++ b/back/methods/notification/specs/getList.spec.js @@ -0,0 +1,38 @@ +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 = 1109; + const activeNotifications = await models.NotificationSubscription.find({ + where: {userFk: userId} + }); + const roles = await models.RoleMapping.find({ + fields: ['roleId'], + where: {principalId: userId} + }); + const availableNotifications = await models.NotificationAcl.find({ + where: { + roleFk: { + inq: roles.map(role => { + return role.roleId; + }), + }, + } + }); + + const result = await models.NotificationSubscription.getList(userId); + + expect(result.filter(notification => notification.active == true).length) + .toEqual(activeNotifications.length); + + expect(result.filter(notification => notification.active == false).length) + .toEqual(availableNotifications.length - activeNotifications.length); + }); + + it('should return empty from a non existent user', async() => { + const userId = 123456789; + const result = await models.NotificationSubscription.getList(userId); + + expect(result.length).toEqual(0); + }); +}); diff --git a/back/models/notificationSubscription.js b/back/models/notificationSubscription.js index f1b2811fa7..a4d4a8bacd 100644 --- a/back/models/notificationSubscription.js +++ b/back/models/notificationSubscription.js @@ -1,62 +1,40 @@ 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); - - 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 - } + const worker = await models.Worker.findById(workerId, {fields: ['id', 'bossFk']}); + const notificationsAvailables = await models.NotificationSubscription.getList(workerId); + const hasAcl = notificationsAvailables.some(function(available) { + return available.notificationFk === notificationFk; }); + + if (!hasAcl || (userId != worker.id && userId != worker.bossFk)) + throw new UserError('The notification subscription of this worker cant be modified'); } }; diff --git a/back/models/specs/notificationSubscription.spec.js b/back/models/specs/notificationSubscription.spec.js index c5ef698fd9..c2adcbc59d 100644 --- a/back/models/specs/notificationSubscription.spec.js +++ b/back/models/specs/notificationSubscription.spec.js @@ -1,69 +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 notificationSubscriptionId = 2; - const ctx = {req: {accessToken: {userId: user}}}; - const notification = await models.NotificationSubscription.findById(notificationSubscriptionId); + const options = {transaction: tx, accessToken: {userId: 9}}; + await models.NotificationSubscription.create({notificationFk: 1, userFk: 62}, options); - await models.NotificationSubscription.deleteNotification(ctx, notification.id, options); - - expect(error.message).toContain('You dont have permission to modify this user'); await tx.rollback(); } catch (e) { + await tx.rollback(); error = e; - await tx.rollback(); } + + 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 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}; - const user = 9; + 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; + await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, 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 add 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}}; + 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; - 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; } + + expect(error).toBeUndefined(); }); - it('should delete a notification if the user is editing a subordinate', async() => { + 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}; - const user = 19; - const notificationSubscriptionId = 4; - const ctx = {req: {accessToken: {userId: user}}}; - const notification = await models.NotificationSubscription.findById(notificationSubscriptionId); + const options = {transaction: tx, accessToken: {userId: 9}}; + await models.NotificationSubscription.create({notificationFk: 1, userFk: 5}, 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; } + + 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; + await models.NotificationSubscription.destroyAll({id: notificationSubscriptionId}, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error).toBeUndefined(); }); }); diff --git a/db/changes/232001/00-notificationSubscription.sql b/db/changes/232001/00-notificationSubscription.sql new file mode 100644 index 0000000000..ef081437dd --- /dev/null +++ b/db/changes/232001/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/loopback/locale/es.json b/loopback/locale/es.json index 527fa1108a..a9659329f5 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -290,5 +290,6 @@ "isTaxDataChecked": "Datos comprobados", "comercialId": "Id comercial", "comercialName": "Comercial", - "Invalid NIF for VIES": "Invalid NIF for VIES" + "Invalid NIF for VIES": "Invalid NIF for VIES", + "The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada" } From 5171e63188bbd3dbc5f1b3ab1bf5fa354d9572cd Mon Sep 17 00:00:00 2001 From: alexandre Date: Tue, 9 May 2023 14:20:20 +0200 Subject: [PATCH 05/81] refs #4797 minor fixes --- back/methods/notification/getList.js | 8 ++++---- back/methods/notification/specs/getList.spec.js | 7 ------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/back/methods/notification/getList.js b/back/methods/notification/getList.js index 9f33671843..f8a1631268 100644 --- a/back/methods/notification/getList.js +++ b/back/methods/notification/getList.js @@ -50,7 +50,7 @@ module.exports = Self => { } }, myOptions); - activeNotifications.forEach(subscription => { + for (subscription of activeNotifications) { notifications.push({ id: subscription.id, notificationFk: subscription.notificationFk, @@ -58,9 +58,9 @@ module.exports = Self => { description: subscription.notification().description, active: true }); - }); + } - availableNotifications.forEach(acl => { + for (acl of availableNotifications) { const activeNotif = notifications.find( notif => notif.notificationFk === acl.notificationFk ); @@ -73,7 +73,7 @@ module.exports = Self => { active: false, }); } - }); + } return notifications; }; diff --git a/back/methods/notification/specs/getList.spec.js b/back/methods/notification/specs/getList.spec.js index b220f2215a..7c986eba2d 100644 --- a/back/methods/notification/specs/getList.spec.js +++ b/back/methods/notification/specs/getList.spec.js @@ -28,11 +28,4 @@ describe('NotificationSubscription getList()', () => { expect(result.filter(notification => notification.active == false).length) .toEqual(availableNotifications.length - activeNotifications.length); }); - - it('should return empty from a non existent user', async() => { - const userId = 123456789; - const result = await models.NotificationSubscription.getList(userId); - - expect(result.length).toEqual(0); - }); }); From f9e7963e74c1c2701e89c8ba9e47663f97381b64 Mon Sep 17 00:00:00 2001 From: alexandre Date: Wed, 10 May 2023 14:10:00 +0200 Subject: [PATCH 06/81] refs #4797 minor fixes --- back/methods/notification/getList.js | 48 +++++++++---------------- back/models/notificationSubscription.js | 4 +-- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/back/methods/notification/getList.js b/back/methods/notification/getList.js index f8a1631268..cb0f1a4f97 100644 --- a/back/methods/notification/getList.js +++ b/back/methods/notification/getList.js @@ -21,7 +21,7 @@ module.exports = Self => { }); Self.getList = async(id, options) => { - const notifications = []; + const notifications = new Map(); const models = Self.app.models; const myOptions = {}; @@ -29,11 +29,6 @@ module.exports = Self => { if (typeof options == 'object') Object.assign(myOptions, options); - const activeNotifications = await models.NotificationSubscription.find({ - include: {relation: 'notification'}, - where: {userFk: id} - }, myOptions); - const roles = await models.RoleMapping.find({ fields: ['roleId'], where: {principalId: id} @@ -43,38 +38,29 @@ module.exports = Self => { include: {relation: 'notification'}, where: { roleFk: { - inq: roles.map(role => { - return role.roleId; - }), + inq: roles.map(role => role.roleId), }, } }, myOptions); - for (subscription of activeNotifications) { - notifications.push({ - id: subscription.id, - notificationFk: subscription.notificationFk, - name: subscription.notification().name, - description: subscription.notification().description, - active: true + for (acl of availableNotifications) { + notifications.set(acl.notificationFk, { + id: null, + notificationFk: acl.notificationFk, + name: acl.notification().name, + description: acl.notification().description, + active: false, }); } - for (acl of availableNotifications) { - const activeNotif = notifications.find( - notif => notif.notificationFk === acl.notificationFk - ); - if (!activeNotif) { - notifications.push({ - id: null, - notificationFk: acl.notificationFk, - name: acl.notification().name, - description: acl.notification().description, - active: false, - }); - } - } + const activeNotifications = await models.NotificationSubscription.find({ + include: {relation: 'notification'}, + where: {userFk: id} + }, myOptions); - return notifications; + for (subscription of activeNotifications) + notifications.get(subscription.notificationFk).active = true; + + return [...notifications.values()]; }; }; diff --git a/back/models/notificationSubscription.js b/back/models/notificationSubscription.js index a4d4a8bacd..f750a0528a 100644 --- a/back/models/notificationSubscription.js +++ b/back/models/notificationSubscription.js @@ -30,9 +30,7 @@ module.exports = Self => { const worker = await models.Worker.findById(workerId, {fields: ['id', 'bossFk']}); const notificationsAvailables = await models.NotificationSubscription.getList(workerId); - const hasAcl = notificationsAvailables.some(function(available) { - return available.notificationFk === notificationFk; - }); + const hasAcl = notificationsAvailables.some(available => available.notificationFk === notificationFk); if (!hasAcl || (userId != worker.id && userId != worker.bossFk)) throw new UserError('The notification subscription of this worker cant be modified'); From 9cb756544e69ec3561d153604145067d5e01d752 Mon Sep 17 00:00:00 2001 From: alexandre Date: Wed, 10 May 2023 14:13:38 +0200 Subject: [PATCH 07/81] refs #4797 added id to notification in activeNotifications --- back/methods/notification/getList.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/back/methods/notification/getList.js b/back/methods/notification/getList.js index cb0f1a4f97..fe5aa23a50 100644 --- a/back/methods/notification/getList.js +++ b/back/methods/notification/getList.js @@ -58,8 +58,13 @@ module.exports = Self => { where: {userFk: id} }, myOptions); - for (subscription of activeNotifications) - notifications.get(subscription.notificationFk).active = true; + for (subscription of activeNotifications) { + const notification = notifications.get(subscription.notificationFk); + if (notification) { + notification.active = true; + notification.id = subscription.id; + } + } return [...notifications.values()]; }; From 33ef9bdf61ea9f42a3ef5053ab81a5fe5a95be61 Mon Sep 17 00:00:00 2001 From: alexandre Date: Thu, 11 May 2023 07:18:58 +0200 Subject: [PATCH 08/81] refs #4797 refactor --- back/methods/notification/getList.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/back/methods/notification/getList.js b/back/methods/notification/getList.js index fe5aa23a50..35a0498c00 100644 --- a/back/methods/notification/getList.js +++ b/back/methods/notification/getList.js @@ -43,6 +43,11 @@ module.exports = Self => { } }, myOptions); + const activeNotifications = await models.NotificationSubscription.find({ + include: {relation: 'notification'}, + where: {userFk: id} + }, myOptions); + for (acl of availableNotifications) { notifications.set(acl.notificationFk, { id: null, @@ -53,17 +58,14 @@ module.exports = Self => { }); } - const activeNotifications = await models.NotificationSubscription.find({ - include: {relation: 'notification'}, - where: {userFk: id} - }, myOptions); - for (subscription of activeNotifications) { - const notification = notifications.get(subscription.notificationFk); - if (notification) { - notification.active = true; - notification.id = subscription.id; - } + notifications.set(subscription.notificationFk, { + id: subscription.id, + notificationFk: subscription.notificationFk, + name: subscription.notification().name, + description: subscription.notification().description, + active: true, + }); } return [...notifications.values()]; From 2c254cbc089ab55d2d0c2f3a91edc5704dd7487b Mon Sep 17 00:00:00 2001 From: alexandre Date: Mon, 15 May 2023 14:34:32 +0200 Subject: [PATCH 09/81] refs #4797 added fields --- back/methods/notification/getList.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/back/methods/notification/getList.js b/back/methods/notification/getList.js index 35a0498c00..780b8eb746 100644 --- a/back/methods/notification/getList.js +++ b/back/methods/notification/getList.js @@ -35,6 +35,7 @@ module.exports = Self => { }, myOptions); const availableNotifications = await models.NotificationAcl.find({ + fields: ['notificationFk', 'roleFk'], include: {relation: 'notification'}, where: { roleFk: { @@ -44,6 +45,7 @@ module.exports = Self => { }, myOptions); const activeNotifications = await models.NotificationSubscription.find({ + fields: ['id', 'notificationFk'], include: {relation: 'notification'}, where: {userFk: id} }, myOptions); From 8db7a5b16a812c4b74adfb77782bcbb8e42b5c7d Mon Sep 17 00:00:00 2001 From: alexandre Date: Thu, 18 May 2023 14:53:09 +0200 Subject: [PATCH 10/81] refs #4797 moved sql --- db/changes/232201/.gitkeep | 0 db/changes/{232001 => 232201}/00-notificationSubscription.sql | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 db/changes/232201/.gitkeep rename db/changes/{232001 => 232201}/00-notificationSubscription.sql (100%) diff --git a/db/changes/232201/.gitkeep b/db/changes/232201/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/db/changes/232001/00-notificationSubscription.sql b/db/changes/232201/00-notificationSubscription.sql similarity index 100% rename from db/changes/232001/00-notificationSubscription.sql rename to db/changes/232201/00-notificationSubscription.sql From 4d3d38ebe301bdd62a7cc252ab7bbbb8cba7a3ce Mon Sep 17 00:00:00 2001 From: alexm Date: Wed, 18 Oct 2023 15:22:04 +0200 Subject: [PATCH 11/81] refs #6014 feat: executeRoutine --- .../methods/application/executeRoutine.js | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 loopback/common/methods/application/executeRoutine.js diff --git a/loopback/common/methods/application/executeRoutine.js b/loopback/common/methods/application/executeRoutine.js new file mode 100644 index 0000000000..a5557bc617 --- /dev/null +++ b/loopback/common/methods/application/executeRoutine.js @@ -0,0 +1,61 @@ +module.exports = Self => { + Self.remoteMethodCtx('executeRoutine', { + description: 'Return the routes by worker', + accessType: '*', + accepts: [ + { + arg: 'routine', + type: 'string', + description: 'The routine sql', + required: true, + http: {source: 'path'} + }, + { + arg: 'params', + type: ['any'], + description: 'The array of params', + required: true, + } + ], + returns: { + type: 'any', + root: true + }, + http: { + path: `/:routine/execute-routine`, + verb: 'POST' + } + }); + + Self.executeRoutine = async(ctx, routine, params, options) => { + const userId = ctx.req.accessToken.userId; + + const myOptions = {}; + if (typeof options == 'object') + Object.assign(myOptions, options); + + const user = await Self.app.models.VnUser.findById(userId, { + fields: ['id', 'roleFk'], + include: { + relation: 'role', + scope: { + fields: ['id', 'name'] + } + } + }); + + const inherits = await Self.app.models.RoleRole.find({ + where: { + + } + }); + console.log(user.role.name); + + const checkACL = await models.ACL.checkAccessAcl(ctx, 'Application', routine, '*'); + if (!checkACL) throw error; + + const requestParams = [routine]; + requestParams.concat(params); + return Self.app.models.Route.rawSql(`CALL ?(...)`, requestParams, myOptions); + }; +}; From 483526c9702fe03ddd2fcc18586cb6b62cc36102 Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 23 Oct 2023 15:03:05 +0200 Subject: [PATCH 12/81] refs #6015 feat(executeRoutine): check db restrictions. test: add executeRoutine tests --- db/changes/234201/00-ACL_executeRoutine.sql | 3 + .../methods/application/executeRoutine.js | 67 +++++++-- .../application/spec/executeRoutine.spec.js | 138 ++++++++++++++++++ loopback/common/models/application.js | 1 + loopback/common/models/procs-priv.json | 44 ++++++ loopback/server/model-config.json | 10 +- 6 files changed, 248 insertions(+), 15 deletions(-) create mode 100644 db/changes/234201/00-ACL_executeRoutine.sql create mode 100644 loopback/common/methods/application/spec/executeRoutine.spec.js create mode 100644 loopback/common/models/procs-priv.json diff --git a/db/changes/234201/00-ACL_executeRoutine.sql b/db/changes/234201/00-ACL_executeRoutine.sql new file mode 100644 index 0000000000..dd112171a3 --- /dev/null +++ b/db/changes/234201/00-ACL_executeRoutine.sql @@ -0,0 +1,3 @@ +INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId) + VALUES + ('Application', 'executeRoutine', '*', 'ALLOW', 'ROLE', 'employee'); diff --git a/loopback/common/methods/application/executeRoutine.js b/loopback/common/methods/application/executeRoutine.js index a5557bc617..eed34b3441 100644 --- a/loopback/common/methods/application/executeRoutine.js +++ b/loopback/common/methods/application/executeRoutine.js @@ -1,3 +1,5 @@ +const UserError = require('vn-loopback/util/user-error'); + module.exports = Self => { Self.remoteMethodCtx('executeRoutine', { description: 'Return the routes by worker', @@ -6,15 +8,24 @@ module.exports = Self => { { arg: 'routine', type: 'string', - description: 'The routine sql', + description: 'The routine name', required: true, http: {source: 'path'} }, { arg: 'params', type: ['any'], - description: 'The array of params', - required: true, + description: 'The params array', + }, + { + arg: 'schema', + type: 'string', + description: 'The routine schema', + }, + { + arg: 'type', + type: 'string', + description: 'The routine type', } ], returns: { @@ -27,14 +38,23 @@ module.exports = Self => { } }); - Self.executeRoutine = async(ctx, routine, params, options) => { + Self.executeRoutine = async(ctx, routine, params, schema, type, options) => { const userId = ctx.req.accessToken.userId; + const models = Self.app.models; + const isFunction = type == 'function'; + params = params ?? []; + schema = schema ?? 'vn'; + type = type ?? 'procedure'; + let caller = 'CALL'; - const myOptions = {}; + if (isFunction) + caller = 'SELECT'; + + const myOptions = {userId: ctx.req.accessToken.userId}; if (typeof options == 'object') Object.assign(myOptions, options); - const user = await Self.app.models.VnUser.findById(userId, { + const user = await models.VnUser.findById(userId, { fields: ['id', 'roleFk'], include: { relation: 'role', @@ -44,18 +64,37 @@ module.exports = Self => { } }); - const inherits = await Self.app.models.RoleRole.find({ + const inherits = await models.RoleRole.find({ + include: { + relation: 'inherits', + scope: { + fields: ['id', 'name'] + } + }, where: { - + role: user.role().id } }); - console.log(user.role.name); - const checkACL = await models.ACL.checkAccessAcl(ctx, 'Application', routine, '*'); - if (!checkACL) throw error; + const roles = inherits.map(inherit => inherit.inherits().name); - const requestParams = [routine]; - requestParams.concat(params); - return Self.app.models.Route.rawSql(`CALL ?(...)`, requestParams, myOptions); + const canExecute = await models.ProcsPriv.findOne({ + where: { + schema, + type: type.toUpperCase(), + name: routine, + host: process.env.NODE_ENV ? '' : '%', + role: {inq: roles} + } + }); + + if (!canExecute) throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED'); + + let argString = params.map(() => '?').join(','); + + const query = `${caller} ${schema}.${routine}(${argString})`; + + const [response] = await models.ProcsPriv.rawSql(query, params, myOptions); + return isFunction ? Object.values(response)[0] : response; }; }; diff --git a/loopback/common/methods/application/spec/executeRoutine.spec.js b/loopback/common/methods/application/spec/executeRoutine.spec.js new file mode 100644 index 0000000000..150c5d416d --- /dev/null +++ b/loopback/common/methods/application/spec/executeRoutine.spec.js @@ -0,0 +1,138 @@ +const models = require('vn-loopback/server/server').models; + +describe('Application executeRoutine()', () => { + const userWithoutPrivileges = 1; + const userWithPrivileges = 9; + const userWithInheritedPrivileges = 120; + let tx; + + function getCtx(userId) { + return { + req: { + accessToken: {userId}, + headers: {origin: 'http://localhost'} + } + }; + } + + beforeEach(async() => { + tx = await models.Application.beginTransaction({}); + const options = {transaction: tx}; + + await models.Application.rawSql(` + CREATE OR REPLACE PROCEDURE vn.myProcedure(vMyParam INT) + BEGIN + SELECT vMyParam myParam, t.* + FROM ticket t + LIMIT 2; + END + `, null, options); + + await models.Application.rawSql(` + CREATE OR REPLACE FUNCTION bs.myFunction(vMyParam INT) RETURNS int(11) + BEGIN + RETURN vMyParam; + END + `, null, options); + + await models.Application.rawSql(` + GRANT EXECUTE ON PROCEDURE vn.myProcedure TO developer; + GRANT EXECUTE ON FUNCTION bs.myFunction TO developer; + `, null, options); + }); + + it('should throw error when execute procedure and not have privileges', async() => { + const ctx = getCtx(userWithoutPrivileges); + + let error; + try { + const options = {transaction: tx}; + + await models.Application.executeRoutine( + ctx, + 'myProcedure', + [1], + null, + null, + options + ); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toEqual(`You don't have enough privileges`); + }); + + it('should execute procedure and get data', async() => { + const ctx = getCtx(userWithPrivileges); + try { + const options = {transaction: tx}; + + const response = await models.Application.executeRoutine( + ctx, + 'myProcedure', + [1], + null, + null, + options + ); + + expect(response.length).toEqual(2); + expect(response[0].myParam).toEqual(1); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should execute function and get data', async() => { + const ctx = getCtx(userWithPrivileges); + try { + const options = {transaction: tx}; + + const response = await models.Application.executeRoutine( + ctx, + 'myFunction', + [1], + 'bs', + 'function', + options + ); + + expect(response).toEqual(1); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should execute function and get data with user with inherited privileges', async() => { + const ctx = getCtx(userWithInheritedPrivileges); + try { + const options = {transaction: tx}; + + const response = await models.Application.executeRoutine( + ctx, + 'myFunction', + [1], + 'bs', + 'function', + options + ); + + expect(response).toEqual(1); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); diff --git a/loopback/common/models/application.js b/loopback/common/models/application.js index 5e767fdc11..b9e639b1be 100644 --- a/loopback/common/models/application.js +++ b/loopback/common/models/application.js @@ -2,4 +2,5 @@ module.exports = function(Self) { require('../methods/application/status')(Self); require('../methods/application/post')(Self); + require('../methods/application/executeRoutine')(Self); }; diff --git a/loopback/common/models/procs-priv.json b/loopback/common/models/procs-priv.json new file mode 100644 index 0000000000..25221d586c --- /dev/null +++ b/loopback/common/models/procs-priv.json @@ -0,0 +1,44 @@ +{ + "name": "ProcsPriv", + "base": "VnModel", + "options": { + "mysql": { + "table": "mysql.procs_priv" + } + }, + "properties": { + "name": { + "id": 1, + "type": "string", + "mysql": { + "columnName": "Routine_name" + } + }, + "schema": { + "id": 3, + "type": "string", + "mysql": { + "columnName": "Db" + } + }, + "role": { + "type": "string", + "mysql": { + "columnName": "user" + } + }, + "type": { + "id": 2, + "type": "string", + "mysql": { + "columnName": "Routine_type" + } + }, + "host": { + "type": "string", + "mysql": { + "columnName": "Host" + } + } + } +} diff --git a/loopback/server/model-config.json b/loopback/server/model-config.json index 52b539f60e..33ef3797d6 100644 --- a/loopback/server/model-config.json +++ b/loopback/server/model-config.json @@ -49,5 +49,13 @@ }, "Container": { "dataSource": "vn" + }, + "ProcsPriv": { + "dataSource": "vn", + "options": { + "mysql": { + "table": "mysql.procs_priv" + } + } } -} \ No newline at end of file +} From 26bdbabff5c7b4f6720214715b14d19ac7cc0888 Mon Sep 17 00:00:00 2001 From: alexm Date: Wed, 25 Oct 2023 15:48:19 +0200 Subject: [PATCH 13/81] refs #6335 feat(ticketAdvanced): show all tickets --- .../234201/00-ticket_canAdvance_leftJoin.sql | 132 ++++++++++++++++++ loopback/server/boot/date.js | 4 +- modules/ticket/front/advance/index.html | 10 +- 3 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 db/changes/234201/00-ticket_canAdvance_leftJoin.sql diff --git a/db/changes/234201/00-ticket_canAdvance_leftJoin.sql b/db/changes/234201/00-ticket_canAdvance_leftJoin.sql new file mode 100644 index 0000000000..b26c3a102c --- /dev/null +++ b/db/changes/234201/00-ticket_canAdvance_leftJoin.sql @@ -0,0 +1,132 @@ +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 + LEFT 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/loopback/server/boot/date.js b/loopback/server/boot/date.js index 8107455625..88505cf62c 100644 --- a/loopback/server/boot/date.js +++ b/loopback/server/boot/date.js @@ -1,8 +1,8 @@ module.exports = () => { Date.vnUTC = () => { const env = process.env.NODE_ENV; - if (!env || env === 'development') - return new Date(Date.UTC(2001, 0, 1, 11)); + // if (!env || env === 'development') + // return new Date(Date.UTC(2001, 0, 1, 11)); return new Date(); }; diff --git a/modules/ticket/front/advance/index.html b/modules/ticket/front/advance/index.html index a6cf8facea..af5b8f7044 100644 --- a/modules/ticket/front/advance/index.html +++ b/modules/ticket/front/advance/index.html @@ -105,7 +105,7 @@ @@ -159,7 +159,13 @@ {{::ticket.futureLiters | dashIfEmpty}} {{::ticket.futureZoneName | dashIfEmpty}} - {{::ticket.notMovableLines | dashIfEmpty}} + + + {{::ticket.notMovableLines | dashIfEmpty}} + + {{::ticket.futureLines | dashIfEmpty}} Date: Fri, 27 Oct 2023 15:06:20 +0200 Subject: [PATCH 14/81] refs #6335 feat(ticketAdvance): use componentUpdate --- .../00-ticket_canAdvance_leftJoin.sql | 30 +++++-- .../back/methods/ticket/componentUpdate.js | 76 ++++++++++------ .../ticket/specs/componentUpdate.spec.js | 2 +- modules/ticket/front/advance/index.html | 67 +++++++++----- modules/ticket/front/advance/index.js | 87 +++++++++++++++---- .../ticket/front/basic-data/step-two/index.js | 4 +- 6 files changed, 198 insertions(+), 68 deletions(-) rename db/changes/{234201 => 234601}/00-ticket_canAdvance_leftJoin.sql (85%) diff --git a/db/changes/234201/00-ticket_canAdvance_leftJoin.sql b/db/changes/234601/00-ticket_canAdvance_leftJoin.sql similarity index 85% rename from db/changes/234201/00-ticket_canAdvance_leftJoin.sql rename to db/changes/234601/00-ticket_canAdvance_leftJoin.sql index b26c3a102c..31472afd8e 100644 --- a/db/changes/234201/00-ticket_canAdvance_leftJoin.sql +++ b/db/changes/234601/00-ticket_canAdvance_leftJoin.sql @@ -68,7 +68,15 @@ BEGIN origin.futureZoneFk, origin.futureZoneName, origin.classColor futureClassColor, - dest.classColor + dest.classColor, + IFNULL(dest.clientFk, origin.clientFk) clientFk, + IFNULL(dest.nickname, origin.nickname) nickname, + IFNULL(dest.addressFk, origin.addressFk) addressFk, + IFNULL(dest.zoneFk, origin.futureZoneFk) zoneFk, + IFNULL(dest.warehouseFk, origin.warehouseFk) warehouseFk, + IFNULL(dest.companyFk, origin.companyFk) companyFk, + IFNULL(dest.agencyModeFk, origin.agencyModeFk) agencyModeFk, + dest.landed FROM ( SELECT s.ticketFk, @@ -76,7 +84,6 @@ BEGIN 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, @@ -84,7 +91,13 @@ BEGIN SUM((s.quantity <= IFNULL(st.amount,0))) hasStock, z.id futureZoneFk, z.name futureZoneName, - st.classColor + st.classColor, + t.clientFk, + t.nickname, + t.addressFk, + t.warehouseFk, + t.companyFk, + t.agencyModeFk FROM ticket t JOIN client c ON c.id = t.clientFk JOIN sale s ON s.ticketFk = t.id @@ -103,7 +116,6 @@ BEGIN LEFT JOIN ( SELECT t.id ticketFk, - t.addressFk, st.name state, GROUP_CONCAT(DISTINCT ipt.code ORDER BY ipt.code) ipt, t.shipped, @@ -111,7 +123,15 @@ BEGIN am.name agency, CAST(SUM(litros) AS DECIMAL(10,0)) liters, CAST(COUNT(*) AS DECIMAL(10,0)) `lines`, - st.classColor + st.classColor, + t.clientFk, + t.nickname, + t.addressFk, + t.zoneFk, + t.warehouseFk, + t.companyFk, + t.landed, + t.agencyModeFk FROM ticket t JOIN sale s ON s.ticketFk = t.id JOIN saleVolume sv ON sv.saleFk = s.id diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js index f7c36f1088..483afdaeee 100644 --- a/modules/ticket/back/methods/ticket/componentUpdate.js +++ b/modules/ticket/back/methods/ticket/componentUpdate.js @@ -75,8 +75,7 @@ module.exports = Self => { { arg: 'option', type: 'number', - description: 'Action id', - required: true + description: 'Action id' }, { arg: 'isWithoutNegatives', @@ -88,7 +87,18 @@ module.exports = Self => { arg: 'withWarningAccept', type: 'boolean', description: 'Has pressed in confirm message', - }], + }, + { + arg: 'newTicket', + type: 'number', + description: 'Ticket id you want to move the lines to, if applicable', + }, + { + arg: 'keepPrice', + type: 'boolean', + description: 'If prices should be maintained', + } + ], returns: { type: ['object'], root: true @@ -101,6 +111,7 @@ module.exports = Self => { Self.componentUpdate = async(ctx, options) => { const args = ctx.args; + const originalTicketId = args.id; const myOptions = {userId: ctx.req.accessToken.userId}; let tx; @@ -141,7 +152,13 @@ module.exports = Self => { const salesNewTicketLength = salesNewTicket.length; if (salesNewTicketLength && sales.length != salesNewTicketLength) { - const newTicket = await models.Ticket.transferSales(ctx, args.id, null, salesNewTicket, myOptions); + const newTicket = await models.Ticket.transferSales( + ctx, + args.id, + args.newTicket, + salesNewTicket, + myOptions + ); args.id = newTicket.id; } } @@ -186,24 +203,32 @@ module.exports = Self => { delete updatedTicket.ctx; delete updatedTicket.option; - // Force to unroute ticket - const hasToBeUnrouted = true; - const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - const res = await Self.rawSql(query, [ - args.id, - args.clientFk, - args.nickname, - args.agencyModeFk, - args.addressFk, - args.zoneFk, - args.warehouseFk, - args.companyFk, - args.shipped, - args.landed, - args.isDeleted, - hasToBeUnrouted, - args.option - ], myOptions); + const ticketChages = { + clientFk: args.clientFk, + nickname: args.nickname, + agencyModeFk: args.agencyModeFk, + addressFk: args.addressFk, + zoneFk: args.zoneFk, + warehouseFk: args.warehouseFk, + companyFk: args.companyFk, + shipped: args.shipped, + landed: args.landed, + isDeleted: args.isDeleted + }; + + let response; + if (args.keepPrice) { + ticketChages.routeFk = null; + response = await originalTicket.updateAttributes(ticketChages, myOptions); + } else { + const hasToBeUnrouted = true; + const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; + response = await Self.rawSql( + query, + [args.id].concat(Object.values(ticketChages), [hasToBeUnrouted, args.option]), + myOptions + ); + } if (originalTicket.addressFk != updatedTicket.addressFk && args.id) { await models.TicketObservation.destroyAll({ @@ -256,10 +281,11 @@ module.exports = Self => { } } - res.id = args.id; - if (tx) await tx.commit(); + response.id = args.id; + console.log(originalTicketId, args.newTicket ? ` TRANSFER TO → ` : ` CREATE NEW TICKET → `, args.id); + if (tx) await tx.rollback(); - return res; + return response; } catch (e) { if (tx) await tx.rollback(); throw e; diff --git a/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js b/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js index ef6422be2b..b51e1b47b8 100644 --- a/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js +++ b/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js @@ -1,7 +1,7 @@ const models = require('vn-loopback/server/server').models; const LoopBackContext = require('loopback-context'); -describe('ticket componentUpdate()', () => { +fdescribe('ticket componentUpdate()', () => { const userID = 1101; const ticketID = 11; const today = Date.vnNew(); diff --git a/modules/ticket/front/advance/index.html b/modules/ticket/front/advance/index.html index af5b8f7044..25664782ec 100644 --- a/modules/ticket/front/advance/index.html +++ b/modules/ticket/front/advance/index.html @@ -21,10 +21,17 @@ expr-builder="$ctrl.exprBuilder(param, value)" > - + vn-tooltip="Advance tickets with negatives"> + + @@ -32,8 +39,14 @@ - Destination - Origin + + Destination + {{model.userParams.dateToAdvance| date: 'dd/MM/yyyy'}} + + + Origin + {{model.userParams.dateFuture | date: 'dd/MM/yyyy'}} + @@ -48,9 +61,6 @@ ID - - Date - IPT @@ -69,9 +79,6 @@ ID - - Date - IPT @@ -117,11 +124,6 @@ {{::ticket.id | dashIfEmpty}} - - - {{::ticket.shipped | date: 'dd/MM/yyyy'}} - - {{::ticket.ipt | dashIfEmpty}} - - - {{::ticket.futureShipped | date: 'dd/MM/yyyy'}} - - {{::ticket.futureIpt | dashIfEmpty}} + + + + + + + + + + + + + + + + + + diff --git a/modules/ticket/front/advance/index.js b/modules/ticket/front/advance/index.js index 389bcdf14a..f98d0a6a9c 100644 --- a/modules/ticket/front/advance/index.js +++ b/modules/ticket/front/advance/index.js @@ -75,20 +75,6 @@ export default class Controller extends Section { }); } - compareDate(date) { - let today = Date.vnNew(); - today.setHours(0, 0, 0, 0); - let timeTicket = new Date(date); - timeTicket.setHours(0, 0, 0, 0); - - let comparation = today - timeTicket; - - if (comparation == 0) - return 'warning'; - if (comparation < 0) - return 'success'; - } - get checked() { const tickets = this.$.model.data || []; const checkedLines = []; @@ -136,7 +122,8 @@ export default class Controller extends Section { moveTicketsAdvance() { let ticketsToMove = []; - this.checked.forEach(ticket => { + // falta ver que hacer con los nulls + for (ticket of this.checked) { ticketsToMove.push({ originId: ticket.futureId, destinationId: ticket.id, @@ -144,7 +131,8 @@ export default class Controller extends Section { destinationShipped: ticket.shipped, workerFk: ticket.workerFk }); - }); + } + const params = {tickets: ticketsToMove}; return this.$http.post('Tickets/merge', params) .then(() => { @@ -152,6 +140,73 @@ export default class Controller extends Section { this.vnApp.showSuccess(this.$t('Success')); }); } + async getLanded(params) { + const query = `Agencies/getLanded`; + return this.$http.get(query, {params}).then(res => { + if (res.data) + return res.data; + else { + return this.vnApp.showError( + this.$t(`No delivery zone available for this landing date`) + ); + } + }); + } + + async splitTickets() { + this.progress = []; + this.splitErrors = []; + this.$.splitTickets.hide(); + this.$.splitProgress.enable = true; + this.$.splitProgress.cancel = false; + this.$.splitProgress.show(); + for (const ticket of this.checked) { + if (this.$.splitProgress.cancel) break; + try { + if (!ticket.landed) { + const newLanded = await this.getLanded({ + shipped: this.$.model.userParams.dateToAdvance, + addressFk: ticket.addressFk, + agencyModeFk: ticket.agencyModeFk, + warehouseFk: ticket.warehouseFk + }); + ticket.landed = newLanded?.landed; + ticket.zoneFk = newLanded?.zoneFk; + } + const query = `tickets/${ticket.futureId}/componentUpdate`; + const params = { + clientFk: ticket.clientFk, + nickname: ticket.nickname, + agencyModeFk: ticket.agencyModeFk, + addressFk: ticket.addressFk, + zoneFk: ticket.zoneFk, + warehouseFk: ticket.warehouseFk, + companyFk: ticket.companyFk, + shipped: this.$.model.userParams.dateToAdvance, + landed: ticket.landed, + isDeleted: false, + isWithoutNegatives: true, + withWarningAccept: false, + newTicket: ticket.id ?? undefined, + keepPrice: true + }; + + this.$http.post(query, params) + .catch(e => { + this.splitErrors.push({id: ticket.futureId, reason: e.message}); + }) + .finally(() => { + this.progress.push(ticket.futureId); + if (this.progress.length == this.checked.length) { + this.$.splitProgress.enable = false; + this.$.model.refresh(); + this.vnApp.showSuccess(this.$t('Success')); + this.$checkAll = null; + } + }); + } catch (e) {} + } + } exprBuilder(param, value) { switch (param) { diff --git a/modules/ticket/front/basic-data/step-two/index.js b/modules/ticket/front/basic-data/step-two/index.js index 74e2df074c..8ca9f053d7 100644 --- a/modules/ticket/front/basic-data/step-two/index.js +++ b/modules/ticket/front/basic-data/step-two/index.js @@ -114,7 +114,9 @@ class Controller extends Component { isDeleted: this.ticket.isDeleted, option: parseInt(this.ticket.option), isWithoutNegatives: this.ticket.withoutNegatives, - withWarningAccept: this.ticket.withWarningAccept + withWarningAccept: this.ticket.withWarningAccept, + newTicket: null, + keepPrice: false }; this.$http.post(query, params) From a80715583c0a7b7fae1c17f501cb178251d68253 Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 30 Oct 2023 14:16:25 +0100 Subject: [PATCH 15/81] refs #6335 feat(ticketAdvance): change the date if you can --- CHANGELOG.md | 5 + loopback/locale/es.json | 14 ++- .../back/methods/ticket/componentUpdate.js | 32 ++--- modules/ticket/front/advance/index.html | 24 ++-- modules/ticket/front/advance/index.js | 116 +++++++++++------- modules/ticket/front/advance/locale/es.yml | 6 +- 6 files changed, 123 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd6ed45222..e236dc2396 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2346.01] - 2023-11-16 ### Added +- (Ticket -> Adelantar) Permite mover lineas sin generar negativos +- (Ticket -> Adelantar) Permite modificar la fecha de los tickets + ### Changed ### Fixed +- (Ticket -> RocketChat) Arreglada detección de cambios + ## [2342.01] - 2023-11-02 diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 3cc9a96278..a796c44a72 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -224,7 +224,9 @@ "date in the future": "Fecha en el futuro", "reference duplicated": "Referencia duplicada", "This ticket is already a refund": "Este ticket ya es un abono", - "isWithoutNegatives": "isWithoutNegatives", + "isWithoutNegatives": "Sin negativos", + "newTicket": "Nuevo ticket", + "keepPrice": "Mantener precios", "routeFk": "routeFk", "Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador", "No hay un contrato en vigor": "No hay un contrato en vigor", @@ -321,9 +323,13 @@ "Select a different client": "Seleccione un cliente distinto", "Fill all the fields": "Rellene todos los campos", "The response is not a PDF": "La respuesta no es un PDF", - "Ticket without Route": "Ticket sin ruta", "Booking completed": "Reserva completada", "The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación", "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina", - "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina" -} + "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina", + "__cachedRelations": "__cachedRelations", + "__data": "__data", + "__strict": "__strict", + "__persisted": "__persisted", + "client": "client" +} \ No newline at end of file diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js index 483afdaeee..536166d7da 100644 --- a/modules/ticket/back/methods/ticket/componentUpdate.js +++ b/modules/ticket/back/methods/ticket/componentUpdate.js @@ -163,7 +163,7 @@ module.exports = Self => { } } - const originalTicket = await models.Ticket.findOne({ + const ticketToChange = await models.Ticket.findOne({ where: {id: args.id}, fields: [ 'id', @@ -196,13 +196,7 @@ module.exports = Self => { }, ] }, myOptions); - - args.routeFk = null; - if (args.isWithoutNegatives === false) delete args.isWithoutNegatives; - const updatedTicket = Object.assign({}, args); - delete updatedTicket.ctx; - delete updatedTicket.option; - + const originalTicket = JSON.parse(JSON.stringify(ticketToChange)); const ticketChages = { clientFk: args.clientFk, nickname: args.nickname, @@ -219,18 +213,21 @@ module.exports = Self => { let response; if (args.keepPrice) { ticketChages.routeFk = null; - response = await originalTicket.updateAttributes(ticketChages, myOptions); + response = await ticketToChange.updateAttributes(ticketChages, myOptions); } else { const hasToBeUnrouted = true; - const query = 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; response = await Self.rawSql( - query, + 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [args.id].concat(Object.values(ticketChages), [hasToBeUnrouted, args.option]), myOptions ); } + if (args.isWithoutNegatives === false) delete args.isWithoutNegatives; + const updatedTicket = Object.assign({}, args, JSON.parse(JSON.stringify(ticketToChange))); + delete updatedTicket.ctx; + delete updatedTicket.option; - if (originalTicket.addressFk != updatedTicket.addressFk && args.id) { + if (ticketToChange.addressFk != updatedTicket.addressFk && args.id) { await models.TicketObservation.destroyAll({ ticketFk: args.id }, myOptions); @@ -252,15 +249,20 @@ module.exports = Self => { await models.TicketObservation.create(clonedObservations, myOptions); } } - const changes = loggable.getChanges(originalTicket, updatedTicket); + const hasChanges = Object.keys(changes.old).length > 0 || Object.keys(changes.new).length > 0; if (hasChanges) { + delete changes.old.client; + delete changes.old.address; + delete changes.new.client; + delete changes.new.address; + const oldProperties = await loggable.translateValues(Self, changes.old); const newProperties = await loggable.translateValues(Self, changes.new); - const salesPersonId = originalTicket.client().salesPersonFk; + const salesPersonId = ticketToChange.client().salesPersonFk; if (salesPersonId) { const url = await Self.app.models.Url.getUrl(); @@ -283,7 +285,7 @@ module.exports = Self => { response.id = args.id; console.log(originalTicketId, args.newTicket ? ` TRANSFER TO → ` : ` CREATE NEW TICKET → `, args.id); - if (tx) await tx.rollback(); + if (tx) await tx.commit(); return response; } catch (e) { diff --git a/modules/ticket/front/advance/index.html b/modules/ticket/front/advance/index.html index 25664782ec..395fe7078e 100644 --- a/modules/ticket/front/advance/index.html +++ b/modules/ticket/front/advance/index.html @@ -29,7 +29,7 @@ @@ -121,7 +121,7 @@ - {{::ticket.id | dashIfEmpty}} + {{::ticket.id}} {{::ticket.ipt | dashIfEmpty}} @@ -137,7 +137,7 @@ - {{::(ticket.totalWithVat ? ticket.totalWithVat : 0) | currency: 'EUR': 2}} + {{::ticket.totalWithVat | currency: 'EUR': 2}} @@ -193,21 +193,23 @@ + message="Progress"> - - + label="Total progress" + value="[{{$ctrl.progress.length}}/{{::$ctrl.checked.length}}]"> + Error list + + + + diff --git a/modules/ticket/front/advance/index.js b/modules/ticket/front/advance/index.js index f98d0a6a9c..5e41f0f23b 100644 --- a/modules/ticket/front/advance/index.js +++ b/modules/ticket/front/advance/index.js @@ -120,10 +120,16 @@ export default class Controller extends Section { '\n' + this.$t(`Destination agency`, {agency: agency}); } - moveTicketsAdvance() { + async moveTicketsAdvance() { let ticketsToMove = []; - // falta ver que hacer con los nulls - for (ticket of this.checked) { + for (const ticket of this.checked) { + if (!ticket.id) { + try { + const {query, params} = await this.requestComponentUpdate(ticket, false); + this.$http.post(query, params); + } catch (e) {} + continue; + } ticketsToMove.push({ originId: ticket.futureId, destinationId: ticket.id, @@ -136,10 +142,12 @@ export default class Controller extends Section { const params = {tickets: ticketsToMove}; return this.$http.post('Tickets/merge', params) .then(() => { - this.$.model.refresh(); - this.vnApp.showSuccess(this.$t('Success')); + this.refresh(); + if (ticketsToMove.length) + this.vnApp.showSuccess(this.$t('Success', {tickets: ticketsToMove.length})); }); } + async getLanded(params) { const query = `Agencies/getLanded`; return this.$http.get(query, {params}).then(res => { @@ -160,54 +168,76 @@ export default class Controller extends Section { this.$.splitProgress.enable = true; this.$.splitProgress.cancel = false; this.$.splitProgress.show(); + for (const ticket of this.checked) { if (this.$.splitProgress.cancel) break; try { - if (!ticket.landed) { - const newLanded = await this.getLanded({ - shipped: this.$.model.userParams.dateToAdvance, - addressFk: ticket.addressFk, - agencyModeFk: ticket.agencyModeFk, - warehouseFk: ticket.warehouseFk - }); - ticket.landed = newLanded?.landed; - ticket.zoneFk = newLanded?.zoneFk; - } - const query = `tickets/${ticket.futureId}/componentUpdate`; - const params = { - clientFk: ticket.clientFk, - nickname: ticket.nickname, - agencyModeFk: ticket.agencyModeFk, - addressFk: ticket.addressFk, - zoneFk: ticket.zoneFk, - warehouseFk: ticket.warehouseFk, - companyFk: ticket.companyFk, - shipped: this.$.model.userParams.dateToAdvance, - landed: ticket.landed, - isDeleted: false, - isWithoutNegatives: true, - withWarningAccept: false, - newTicket: ticket.id ?? undefined, - keepPrice: true - }; - + const {query, params} = await this.requestComponentUpdate(ticket, true); this.$http.post(query, params) .catch(e => { this.splitErrors.push({id: ticket.futureId, reason: e.message}); }) - .finally(() => { - this.progress.push(ticket.futureId); - if (this.progress.length == this.checked.length) { - this.$.splitProgress.enable = false; - this.$.model.refresh(); - this.vnApp.showSuccess(this.$t('Success')); - this.$checkAll = null; - } - }); - } catch (e) {} + .finally(() => this.progressAdd(ticket.futureId)); + } catch (e) { + this.splitErrors.push({id: ticket.futureId, reason: e.message}); + this.progressAdd(ticket.futureId); + } } } + progressAdd(ticketId) { + this.progress.push(ticketId); + if (this.progress.length == this.checked.length) { + this.$.splitProgress.enable = false; + this.refresh(); + if ((this.progress.length - this.splitErrors.length) > 0) { + this.vnApp.showSuccess(this.$t('Success', { + tickets: this.progress.length - this.splitErrors.length + })); + } + } + } + + async requestComponentUpdate(ticket, isWithoutNegatives) { + const query = `tickets/${ticket.futureId}/componentUpdate`; + if (!ticket.landed) { + console.log(ticket); + const newLanded = await this.getLanded({ + shipped: this.$.model.userParams.dateToAdvance, + addressFk: ticket.addressFk, + agencyModeFk: ticket.agencyModeFk, + warehouseFk: ticket.warehouseFk + }); + if (!newLanded) + throw new Error(this.$t(`No delivery zone available for this landing date`)); + + ticket.landed = newLanded.landed; + ticket.zoneFk = newLanded.zoneFk; + } + const params = { + clientFk: ticket.clientFk, + nickname: ticket.nickname, + agencyModeFk: ticket.agencyModeFk, + addressFk: ticket.addressFk, + zoneFk: ticket.zoneFk, + warehouseFk: ticket.warehouseFk, + companyFk: ticket.companyFk, + shipped: this.$.model.userParams.dateToAdvance, + landed: ticket.landed, + isDeleted: false, + isWithoutNegatives, + newTicket: ticket.id ?? undefined, + keepPrice: true + }; + console.log(params); + return {query, params}; + } + + refresh() { + this.$.model.refresh(); + this.$checkAll = null; + } + exprBuilder(param, value) { switch (param) { case 'id': diff --git a/modules/ticket/front/advance/locale/es.yml b/modules/ticket/front/advance/locale/es.yml index 3111d9afd3..520e82009a 100644 --- a/modules/ticket/front/advance/locale/es.yml +++ b/modules/ticket/front/advance/locale/es.yml @@ -1,7 +1,7 @@ Advance tickets: Adelantar tickets Search advance tickets by date: Busca tickets para adelantar por fecha Advance confirmation: ¿Desea adelantar {{checked}} tickets? -Success: Tickets movidos correctamente +Success: "{{tickets}} Tickets movidos correctamente" Lines: Líneas Liters: Litros Item Packing Type: Encajado @@ -9,3 +9,7 @@ Origin agency: "Agencia origen: {{agency}}" Destination agency: "Agencia destino: {{agency}}" Less than 50€: Menor a 50€ Not Movable: No movibles +Error list: Lista de errores +Progress: Progreso +Total progress: Progreso total +Advance tickets (without negatives): Adelantar tickets (sin negativos) From 37dce9944790dc1b2c6c342629a50e8ad52f2546 Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 30 Oct 2023 15:00:05 +0100 Subject: [PATCH 16/81] refs #6335 test(ticketAdvance): add test to keep price --- loopback/locale/es.json | 9 +- loopback/server/boot/date.js | 4 +- .../back/methods/ticket/componentUpdate.js | 4 +- .../ticket/specs/componentUpdate.spec.js | 110 +++++++++++++++++- 4 files changed, 115 insertions(+), 12 deletions(-) diff --git a/loopback/locale/es.json b/loopback/locale/es.json index a796c44a72..7e2000344a 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -326,10 +326,5 @@ "Booking completed": "Reserva completada", "The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación", "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina", - "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina", - "__cachedRelations": "__cachedRelations", - "__data": "__data", - "__strict": "__strict", - "__persisted": "__persisted", - "client": "client" -} \ No newline at end of file + "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina" +} diff --git a/loopback/server/boot/date.js b/loopback/server/boot/date.js index 88505cf62c..8107455625 100644 --- a/loopback/server/boot/date.js +++ b/loopback/server/boot/date.js @@ -1,8 +1,8 @@ module.exports = () => { Date.vnUTC = () => { const env = process.env.NODE_ENV; - // if (!env || env === 'development') - // return new Date(Date.UTC(2001, 0, 1, 11)); + if (!env || env === 'development') + return new Date(Date.UTC(2001, 0, 1, 11)); return new Date(); }; diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js index 536166d7da..1b02fd201d 100644 --- a/modules/ticket/back/methods/ticket/componentUpdate.js +++ b/modules/ticket/back/methods/ticket/componentUpdate.js @@ -223,7 +223,7 @@ module.exports = Self => { ); } if (args.isWithoutNegatives === false) delete args.isWithoutNegatives; - const updatedTicket = Object.assign({}, args, JSON.parse(JSON.stringify(ticketToChange))); + const updatedTicket = Object.assign({}, args); delete updatedTicket.ctx; delete updatedTicket.option; @@ -249,8 +249,8 @@ module.exports = Self => { await models.TicketObservation.create(clonedObservations, myOptions); } } - const changes = loggable.getChanges(originalTicket, updatedTicket); + const changes = loggable.getChanges(originalTicket, updatedTicket); const hasChanges = Object.keys(changes.old).length > 0 || Object.keys(changes.new).length > 0; if (hasChanges) { diff --git a/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js b/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js index b51e1b47b8..e124999c93 100644 --- a/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js +++ b/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js @@ -1,7 +1,7 @@ const models = require('vn-loopback/server/server').models; const LoopBackContext = require('loopback-context'); -fdescribe('ticket componentUpdate()', () => { +describe('ticket componentUpdate()', () => { const userID = 1101; const ticketID = 11; const today = Date.vnNew(); @@ -209,4 +209,112 @@ fdescribe('ticket componentUpdate()', () => { throw e; } }); + + describe('ticket componentUpdate()', () => { + it('should change shipped and keep price', async() => { + const tx = await models.Ticket.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const saleId = 27; + const originDate = today; + const newDate = tomorrow; + const ticketID = 14; + newDate.setHours(0, 0, 0, 0, 0); + originDate.setHours(0, 0, 0, 0, 0); + + const args = { + id: ticketID, + clientFk: 1104, + agencyModeFk: 2, + addressFk: 4, + zoneFk: 9, + warehouseFk: 1, + companyFk: 442, + shipped: newDate, + landed: tomorrow, + isDeleted: false, + option: 1, + isWithoutNegatives: false, + keepPrice: true + }; + + const ctx = { + args: args, + req: { + accessToken: {userId: 9}, + headers: {origin: 'http://localhost'}, + __: value => { + return value; + } + } + }; + + const beforeSale = await models.Sale.findById(saleId, null, options); + await models.Ticket.componentUpdate(ctx, options); + const afterSale = await models.Sale.findById(saleId, null, options); + + expect(beforeSale.price).toEqual(afterSale.price); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should change shipped and not keep price', async() => { + const tx = await models.Ticket.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const saleId = 27; + const originDate = today; + const newDate = tomorrow; + const ticketID = 14; + newDate.setHours(0, 0, 0, 0, 0); + originDate.setHours(0, 0, 0, 0, 0); + + const args = { + id: ticketID, + clientFk: 1104, + agencyModeFk: 2, + addressFk: 4, + zoneFk: 9, + warehouseFk: 1, + companyFk: 442, + shipped: newDate, + landed: tomorrow, + isDeleted: false, + option: 1, + isWithoutNegatives: false, + keepPrice: false + }; + + const ctx = { + args: args, + req: { + accessToken: {userId: 9}, + headers: {origin: 'http://localhost'}, + __: value => { + return value; + } + } + }; + + const beforeSale = await models.Sale.findById(saleId, null, options); + await models.Ticket.componentUpdate(ctx, options); + const afterSale = await models.Sale.findById(saleId, null, options); + + expect(beforeSale.price).not.toEqual(afterSale.price); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + }); }); From d7542bcbb4c9c491a532b2345a0c8dda462ab4a2 Mon Sep 17 00:00:00 2001 From: alexm Date: Tue, 31 Oct 2023 15:11:03 +0100 Subject: [PATCH 17/81] refs #6335 fix(ticketAdvance): procedure --- ..._canAdvance_leftJoin.sql => 00-ticket_canAdvance_update.sql} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename db/changes/234601/{00-ticket_canAdvance_leftJoin.sql => 00-ticket_canAdvance_update.sql} (99%) diff --git a/db/changes/234601/00-ticket_canAdvance_leftJoin.sql b/db/changes/234601/00-ticket_canAdvance_update.sql similarity index 99% rename from db/changes/234601/00-ticket_canAdvance_leftJoin.sql rename to db/changes/234601/00-ticket_canAdvance_update.sql index 31472afd8e..272a0618a1 100644 --- a/db/changes/234601/00-ticket_canAdvance_leftJoin.sql +++ b/db/changes/234601/00-ticket_canAdvance_update.sql @@ -31,7 +31,7 @@ BEGIN SELECT itemFk, quantity FROM itemEntryIn WHERE landed >= vDateInventory - AND landed < vDateFuture + AND landed <= vDateToAdvance AND isVirtualStock = FALSE AND warehouseInFk = vWarehouseFk UNION ALL From 03e2f5730621e6ca2f481e455a6f693b450f7469 Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 2 Nov 2023 10:57:50 +0100 Subject: [PATCH 18/81] refs #6067 fix: emailVerify url --- back/models/vn-user.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/back/models/vn-user.js b/back/models/vn-user.js index de5bf7b63e..970497e4e8 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -220,8 +220,11 @@ module.exports = function(Self) { class Mailer { async send(verifyOptions, cb) { + const url = new URL(verifyOptions.verifyHref); + if (process.env.NODE_ENV) url.port = ''; + const params = { - url: verifyOptions.verifyHref, + url: url.href, recipient: verifyOptions.to }; From 728838334cf427806ae42acaccbcd7e5944f1065 Mon Sep 17 00:00:00 2001 From: sergiodt Date: Thu, 2 Nov 2023 14:42:22 +0100 Subject: [PATCH 19/81] hotFix_6312 fix originalQuantity --- modules/ticket/back/methods/sale/updateQuantity.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/ticket/back/methods/sale/updateQuantity.js b/modules/ticket/back/methods/sale/updateQuantity.js index 1a497da248..6108262838 100644 --- a/modules/ticket/back/methods/sale/updateQuantity.js +++ b/modules/ticket/back/methods/sale/updateQuantity.js @@ -64,7 +64,10 @@ module.exports = Self => { const sale = await models.Sale.findById(id, filter, myOptions); const oldQuantity = sale.quantity; - const result = await sale.updateAttributes({quantity: newQuantity}, myOptions); + const result = await sale.updateAttributes({ + quantity: newQuantity, + originalQuantity: newQuantity + }, myOptions); const salesPerson = sale.ticket().client().salesPersonUser(); if (salesPerson) { From a523e323735587a8192c4814b646440f5a93a5e7 Mon Sep 17 00:00:00 2001 From: alexm Date: Fri, 3 Nov 2023 07:57:53 +0100 Subject: [PATCH 20/81] refs #6335 style(ticketAdvance): fix with split-progress --- e2e/helpers/selectors.js | 2 +- loopback/locale/en.json | 5 ++-- .../back/methods/ticket/componentUpdate.js | 2 -- .../ticket/specs/componentUpdate.spec.js | 2 +- modules/ticket/front/advance/index.html | 10 ++------ modules/ticket/front/advance/index.js | 12 ++++----- modules/ticket/front/advance/index.spec.js | 25 ------------------- modules/ticket/front/advance/locale/es.yml | 2 ++ modules/ticket/front/advance/style.scss | 9 +++++++ .../ticket/front/basic-data/step-two/index.js | 1 - 10 files changed, 23 insertions(+), 47 deletions(-) diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js index cec0545a01..b5dbd42ac1 100644 --- a/e2e/helpers/selectors.js +++ b/e2e/helpers/selectors.js @@ -722,7 +722,7 @@ export default { isFullMovable: 'vn-check[ng-model="filter.isFullMovable"]', warehouseFk: 'vn-autocomplete[label="Warehouse"]', tableButtonSearch: 'vn-button[vn-tooltip="Search"]', - moveButton: 'vn-button[vn-tooltip="Advance tickets"]', + moveButton: 'vn-button[vn-tooltip="Advance tickets with negatives"]', acceptButton: '.vn-confirm.shown button[response="accept"]', firstCheck: 'tbody > tr:nth-child(2) > td > vn-check', tableId: 'vn-textfield[name="id"]', diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 26650175d6..9305c7f093 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -196,6 +196,7 @@ "Negative basis of tickets: 23": "Negative basis of tickets: 23", "Booking completed": "Booking complete", "The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation", - "You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets" + "You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets", + "newTicket": "New ticket", + "keepPrice": "Keep prices" } - diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js index 1b02fd201d..d8d1163daf 100644 --- a/modules/ticket/back/methods/ticket/componentUpdate.js +++ b/modules/ticket/back/methods/ticket/componentUpdate.js @@ -111,7 +111,6 @@ module.exports = Self => { Self.componentUpdate = async(ctx, options) => { const args = ctx.args; - const originalTicketId = args.id; const myOptions = {userId: ctx.req.accessToken.userId}; let tx; @@ -284,7 +283,6 @@ module.exports = Self => { } response.id = args.id; - console.log(originalTicketId, args.newTicket ? ` TRANSFER TO → ` : ` CREATE NEW TICKET → `, args.id); if (tx) await tx.commit(); return response; diff --git a/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js b/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js index e124999c93..40be4161c1 100644 --- a/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js +++ b/modules/ticket/back/methods/ticket/specs/componentUpdate.spec.js @@ -210,7 +210,7 @@ describe('ticket componentUpdate()', () => { } }); - describe('ticket componentUpdate()', () => { + describe('componentUpdate() keepPrice', () => { it('should change shipped and keep price', async() => { const tx = await models.Ticket.beginTransaction({}); diff --git a/modules/ticket/front/advance/index.html b/modules/ticket/front/advance/index.html index 395fe7078e..ca4d994d06 100644 --- a/modules/ticket/front/advance/index.html +++ b/modules/ticket/front/advance/index.html @@ -156,13 +156,7 @@ {{::ticket.futureLiters | dashIfEmpty}} {{::ticket.futureZoneName | dashIfEmpty}} - - - {{::ticket.notMovableLines | dashIfEmpty}} - - + {{::ticket.notMovableLines | dashIfEmpty}} {{::ticket.futureLines | dashIfEmpty}} - + diff --git a/modules/ticket/front/advance/index.js b/modules/ticket/front/advance/index.js index 5e41f0f23b..5ab6632569 100644 --- a/modules/ticket/front/advance/index.js +++ b/modules/ticket/front/advance/index.js @@ -153,11 +153,10 @@ export default class Controller extends Section { return this.$http.get(query, {params}).then(res => { if (res.data) return res.data; - else { - return this.vnApp.showError( - this.$t(`No delivery zone available for this landing date`) - ); - } + + return this.vnApp.showError( + this.$t(`No delivery zone available for this landing date`) + ); }); } @@ -201,7 +200,6 @@ export default class Controller extends Section { async requestComponentUpdate(ticket, isWithoutNegatives) { const query = `tickets/${ticket.futureId}/componentUpdate`; if (!ticket.landed) { - console.log(ticket); const newLanded = await this.getLanded({ shipped: this.$.model.userParams.dateToAdvance, addressFk: ticket.addressFk, @@ -229,7 +227,7 @@ export default class Controller extends Section { newTicket: ticket.id ?? undefined, keepPrice: true }; - console.log(params); + return {query, params}; } diff --git a/modules/ticket/front/advance/index.spec.js b/modules/ticket/front/advance/index.spec.js index da8614ca5d..883993c1cb 100644 --- a/modules/ticket/front/advance/index.spec.js +++ b/modules/ticket/front/advance/index.spec.js @@ -24,31 +24,6 @@ describe('Component vnTicketAdvance', () => { }]; })); - describe('compareDate()', () => { - it('should return warning when the date is the present', () => { - let today = Date.vnNew(); - let result = controller.compareDate(today); - - expect(result).toEqual('warning'); - }); - - it('should return sucess when the date is in the future', () => { - let futureDate = Date.vnNew(); - futureDate = futureDate.setDate(futureDate.getDate() + 10); - let result = controller.compareDate(futureDate); - - expect(result).toEqual('success'); - }); - - it('should return undefined when the date is in the past', () => { - let pastDate = Date.vnNew(); - pastDate = pastDate.setDate(pastDate.getDate() - 10); - let result = controller.compareDate(pastDate); - - expect(result).toEqual(undefined); - }); - }); - describe('checked()', () => { it('should return an array of checked tickets', () => { const result = controller.checked; diff --git a/modules/ticket/front/advance/locale/es.yml b/modules/ticket/front/advance/locale/es.yml index 520e82009a..e42e65b6e6 100644 --- a/modules/ticket/front/advance/locale/es.yml +++ b/modules/ticket/front/advance/locale/es.yml @@ -1,4 +1,6 @@ Advance tickets: Adelantar tickets +Advance tickets with negatives: Adelantar tickets con negativos +Advance tickets without negatives: Adelantar tickets sin negativos Search advance tickets by date: Busca tickets para adelantar por fecha Advance confirmation: ¿Desea adelantar {{checked}} tickets? Success: "{{tickets}} Tickets movidos correctamente" diff --git a/modules/ticket/front/advance/style.scss b/modules/ticket/front/advance/style.scss index 8fa9de4381..82ae901cd7 100644 --- a/modules/ticket/front/advance/style.scss +++ b/modules/ticket/front/advance/style.scss @@ -5,3 +5,12 @@ vn-ticket-advance{ color: #f7931e } } +.split-progress { + width: 40em; +} + +@media screen and (max-width: 600px) { + .split-progress { + width: 100%; + } +} diff --git a/modules/ticket/front/basic-data/step-two/index.js b/modules/ticket/front/basic-data/step-two/index.js index 8ca9f053d7..8c8a3a079e 100644 --- a/modules/ticket/front/basic-data/step-two/index.js +++ b/modules/ticket/front/basic-data/step-two/index.js @@ -115,7 +115,6 @@ class Controller extends Component { option: parseInt(this.ticket.option), isWithoutNegatives: this.ticket.withoutNegatives, withWarningAccept: this.ticket.withWarningAccept, - newTicket: null, keepPrice: false }; From 8c5e40c27fb8477027294feefb65cb6076e02a80 Mon Sep 17 00:00:00 2001 From: alexm Date: Fri, 3 Nov 2023 15:16:03 +0100 Subject: [PATCH 21/81] refs #4797 fix: correct sql folder --- db/changes/{232201 => 234601}/00-notificationSubscription.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename db/changes/{232201 => 234601}/00-notificationSubscription.sql (100%) diff --git a/db/changes/232201/00-notificationSubscription.sql b/db/changes/234601/00-notificationSubscription.sql similarity index 100% rename from db/changes/232201/00-notificationSubscription.sql rename to db/changes/234601/00-notificationSubscription.sql From 99c88e6f5ddea89ac38bc385ee47bae4cef0b445 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Sun, 5 Nov 2023 09:33:18 +0100 Subject: [PATCH 22/81] fix: refs #6390 Loggable set as base for ItemShelving --- modules/item/back/models/item-shelving.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/item/back/models/item-shelving.json b/modules/item/back/models/item-shelving.json index 61d05539e1..bb1a141c42 100644 --- a/modules/item/back/models/item-shelving.json +++ b/modules/item/back/models/item-shelving.json @@ -1,6 +1,6 @@ { "name": "ItemShelving", - "base": "VnModel", + "base": "Loggable", "options": { "mysql": { "table": "itemShelving" From 9d5461862026731a490f6a04aa11219e994379b0 Mon Sep 17 00:00:00 2001 From: jgallego Date: Wed, 8 Nov 2023 10:01:02 +0100 Subject: [PATCH 23/81] fix: refs: #6207 impide facturar si no tienes incoterm --- db/dump/fixtures.sql | 8 +++--- loopback/locale/es.json | 4 +-- .../ticket/back/methods/ticket/makeInvoice.js | 13 ++++++--- .../methods/ticket/specs/makeInvoice.spec.js | 27 +++++++++++++++++++ .../invoice-incoterms/sql/incoterms.sql | 4 +-- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index faf58fd78d..164723de12 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -367,7 +367,7 @@ INSERT INTO `vn`.`client`(`id`,`name`,`fi`,`socialName`,`contact`,`street`,`city (1105, 'Max Eisenhardt', '251628698', 'MAGNETO', 'Rogue', 'UNKNOWN WHEREABOUTS', 'Gotham', 46460, 1111111111, 222222222, 1, 'MaxEisenhardt@mydomain.com', NULL, 0, 1234567890, 0, 3, 1, 300, 8, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 18, 0, 1, 'florist','normal'), (1106, 'DavidCharlesHaller', '53136686Q', 'LEGION', 'Charles Xavier', 'CITY OF NEW YORK, NEW YORK, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'DavidCharlesHaller@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 0, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 0, NULL, 0, 0, 19, 0, 1, 'florist','normal'), (1107, 'Hank Pym', '09854837G', 'ANT MAN', 'Hawk', 'ANTHILL, SAN FRANCISCO, CALIFORNIA', 'Gotham', 46460, 1111111111, 222222222, 1, 'HankPym@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 19, 0, 1, 'florist','normal'), - (1108, 'Charles Xavier', '22641921P', 'PROFESSOR X', 'Beast', '3800 VICTORY PKWY, CINCINNATI, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist','normal'), + (1108, 'Charles Xavier', '22641921P', 'PROFESSOR X', 'Beast', '3800 VICTORY PKWY, CINCINNATI, OH 45207, USA', 'Gotham', 46460, 1111111111, 222222222, 1, 'CharlesXavier@mydomain.com', NULL, 0, 1234567890, 0, 5, 1, 300, 13, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 1, 1, NULL, 0, 0, 19, 0, 1, 'florist','normal'), (1109, 'Bruce Banner', '16104829E', 'HULK', 'Black widow', 'SOMEWHERE IN NEW YORK', 'Gotham', 46460, 1111111111, 222222222, 1, 'BruceBanner@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, 9, 0, 1, 'florist','normal'), (1110, 'Jessica Jones', '58282869H', 'JESSICA JONES', 'Luke Cage', 'NYCC 2015 POSTER', 'Gotham', 46460, 1111111111, 222222222, 1, 'JessicaJones@mydomain.com', NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 1, 1, 0, 0, NULL, 0, 0, NULL, 0, 1, 'florist','normal'), (1111, 'Missing', NULL, 'MISSING MAN', 'Anton', 'THE SPACE, UNIVERSE FAR AWAY', 'Gotham', 46460, 1111111111, 222222222, 1, NULL, NULL, 0, 1234567890, 0, 1, 1, 300, 1, 1, NULL, 10, 5, util.VN_CURDATE(), 1, 5, 1, 1, 1, '0000-00-00', 4, 0, 1, 0, NULL, 1, 0, NULL, 0, 1, 'others','normal'), @@ -405,7 +405,7 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr (5, 'Max Eisenhardt', 'Unknown Whereabouts', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 1), (6, 'DavidCharlesHaller', 'Evil hideout', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 1), (7, 'Hank Pym', 'Anthill', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 1), - (8, 'Charles Xavier', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1108, 2, NULL, NULL, 0, 1), + (8, 'Charles Xavier', '3800 Victory Pkwy, Cincinnati, OH 45207, USA', 'Gotham', 46460, 5, 1111111111, 222222222, 1, 1108, 2, NULL, NULL, 0, 1), (9, 'Bruce Banner', 'Somewhere in New York', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1109, 2, NULL, NULL, 0, 1), (10, 'Jessica Jones', 'NYCC 2015 Poster', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1110, 2, NULL, NULL, 0, 1), (11, 'Missing', 'The space', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1111, 10, NULL, NULL, 0, 1), @@ -437,7 +437,7 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr (125, 'The plastic cell', 'address 25', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 0), (126, 'Many places', 'address 26', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 0), (127, 'Your pocket', 'address 27', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 0), - (128, 'Cerebro', 'address 28', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1108, 2, NULL, NULL, 0, 0), + (128, 'Cerebro', 'address 28', 'Gotham', 46460, 5, 1111111111, 222222222, 1, 1108, 2, NULL, NULL, 0, 0), (129, 'Luke Cages Bar', 'address 29', 'Gotham', 'EC170150', 1, 1111111111, 222222222, 1, 1110, 2, NULL, NULL, 0, 0), (130, 'Non valid address', 'address 30', 'Gotham', 46460, 1, 1111111111, 222222222, 0, 1101, 2, NULL, NULL, 0, 0); @@ -2982,4 +2982,4 @@ INSERT INTO `vn`.`invoiceCorrectionType` (`id`, `description`) VALUES (1, 'Error in VAT calculation'), (2, 'Error in sales details'), - (3, 'Error in customer data'); \ No newline at end of file + (3, 'Error in customer data'); diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 3cc9a96278..e3e19d7e8a 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -321,9 +321,9 @@ "Select a different client": "Seleccione un cliente distinto", "Fill all the fields": "Rellene todos los campos", "The response is not a PDF": "La respuesta no es un PDF", - "Ticket without Route": "Ticket sin ruta", "Booking completed": "Reserva completada", "The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación", "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina", - "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina" + "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina", + "Incoterms data for consignee is missing": "Faltan los datos de los Incoterms para el consignatario" } diff --git a/modules/ticket/back/methods/ticket/makeInvoice.js b/modules/ticket/back/methods/ticket/makeInvoice.js index e18e58e0b4..e7ee806c79 100644 --- a/modules/ticket/back/methods/ticket/makeInvoice.js +++ b/modules/ticket/back/methods/ticket/makeInvoice.js @@ -49,7 +49,6 @@ module.exports = function(Self) { myOptions.transaction = tx; } - let serial; try { const ticketToInvoice = await Self.rawSql(` SELECT id @@ -60,7 +59,7 @@ module.exports = function(Self) { where: { id: {inq: ticketsIds} }, - fields: ['id', 'clientFk'] + fields: ['id', 'clientFk', 'addressFk'] }, myOptions); await models.Ticket.canBeInvoiced(ctx, ticketsIds, myOptions); @@ -72,13 +71,19 @@ module.exports = function(Self) { throw new UserError(`This client can't be invoiced`); const query = `SELECT vn.invoiceSerial(?, ?, ?) AS serial`; - const [result] = await Self.rawSql(query, [ + const [{serial}] = await Self.rawSql(query, [ clientId, companyFk, invoiceType, ], myOptions); - serial = result.serial; + const invoiceOutSerial = await models.InvoiceOutSerial.findById(serial); + if (invoiceOutSerial?.taxAreaFk == 'WORLD') { + const address = await models.Address.findById(firstTicket.addressFk); + + if (!address || !address.customsAgentFk || !address.incotermsFk) + throw new UserError('Incoterms data for consignee is missing'); + } await Self.rawSql('CALL invoiceOut_new(?, ?, null, @invoiceId)', [serial, invoiceDate], myOptions); const [resultInvoice] = await Self.rawSql('SELECT @invoiceId id', [], myOptions); diff --git a/modules/ticket/back/methods/ticket/specs/makeInvoice.spec.js b/modules/ticket/back/methods/ticket/specs/makeInvoice.spec.js index 9af6ad557e..9b1fd8f6f3 100644 --- a/modules/ticket/back/methods/ticket/specs/makeInvoice.spec.js +++ b/modules/ticket/back/methods/ticket/specs/makeInvoice.spec.js @@ -52,4 +52,31 @@ describe('ticket makeInvoice()', () => { throw e; } }); + + it('should throw an error when invoicing a client without incoterms', async() => { + const tx = await models.Ticket.beginTransaction({}); + let error; + try { + const options = {transaction: tx}; + + const ticketsId = [18]; + await models.Ticket.rawSql(` + DROP TEMPORARY TABLE IF EXISTS tmp.ticketToInvoice; + CREATE TEMPORARY TABLE tmp.ticketToInvoice + (PRIMARY KEY (id)) + ENGINE = MEMORY + SELECT id + FROM vn.ticket + WHERE id IN (?) + `, [ticketsId], options); + + await models.Ticket.makeInvoice(ctx, invoiceType, companyFk, invoiceDate, options); + await tx.rollback(); + } catch (e) { + error = e; + await tx.rollback(); + } + + expect(error.message).toEqual(`Incoterms data for consignee is missing`); + }); }); diff --git a/print/templates/reports/invoice-incoterms/sql/incoterms.sql b/print/templates/reports/invoice-incoterms/sql/incoterms.sql index 435b3a51ac..0c4af803d8 100644 --- a/print/templates/reports/invoice-incoterms/sql/incoterms.sql +++ b/print/templates/reports/invoice-incoterms/sql/incoterms.sql @@ -1,7 +1,7 @@ SELECT io.issued, c.socialName, c.street postalAddress, - IF (ios.taxAreaFk IS NOT NULL, CONCAT(cty.code, c.fi), c.fi) fi, + c.fi, io.clientFk, c.postcode, c.city, @@ -50,7 +50,7 @@ SELECT io.issued, ) sub2 ON TRUE JOIN vn.itemTaxCountry itc ON itc.countryFk = su.countryFk AND itc.itemFk = s.itemFk JOIN vn.taxClass tc ON tc.id = itc.taxClassFk - LEFT JOIN vn.invoiceOutSerial ios ON ios.code = io.serial AND ios.taxAreaFk = 'CEE' + JOIN vn.invoiceOutSerial ios ON ios.code = io.serial AND ios.taxAreaFk = 'WORLD' JOIN vn.country cty ON cty.id = c.countryFk JOIN vn.payMethod pm ON pm.id = c .payMethodFk JOIN vn.company co ON co.id=io.companyFk From 0de5bc510935f985078ca364d809f0ac75456f85 Mon Sep 17 00:00:00 2001 From: alexm Date: Wed, 8 Nov 2023 14:56:47 +0100 Subject: [PATCH 24/81] refs #4797 refactor: getList & notificationSubscription method --- .vscode/settings.json | 5 ++- back/methods/notification/getList.js | 51 ++++++++----------------- back/models/notificationSubscription.js | 40 ++++++++++++++++++- 3 files changed, 57 insertions(+), 39 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 05d23f3bbc..9ed1c8fc2a 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/back/methods/notification/getList.js b/back/methods/notification/getList.js index 780b8eb746..3881f0f63a 100644 --- a/back/methods/notification/getList.js +++ b/back/methods/notification/getList.js @@ -21,55 +21,34 @@ module.exports = Self => { }); Self.getList = async(id, options) => { - const notifications = new Map(); - const models = Self.app.models; + const activeNotificationsMap = new Map(); const myOptions = {}; if (typeof options == 'object') Object.assign(myOptions, options); - const roles = await models.RoleMapping.find({ - fields: ['roleId'], - where: {principalId: id} - }, myOptions); - - const availableNotifications = await models.NotificationAcl.find({ - fields: ['notificationFk', 'roleFk'], - include: {relation: 'notification'}, - where: { - roleFk: { - inq: roles.map(role => role.roleId), - }, - } - }, myOptions); - - const activeNotifications = await models.NotificationSubscription.find({ + 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 (acl of availableNotifications) { - notifications.set(acl.notificationFk, { - id: null, - notificationFk: acl.notificationFk, - name: acl.notification().name, - description: acl.notification().description, - active: false, + 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); } - for (subscription of activeNotifications) { - notifications.set(subscription.notificationFk, { - id: subscription.id, - notificationFk: subscription.notificationFk, - name: subscription.notification().name, - description: subscription.notification().description, - active: true, - }); - } - - return [...notifications.values()]; + return { + active: [...activeNotificationsMap.entries()], + available: [...availableNotificationsMap.entries()] + }; }; }; diff --git a/back/models/notificationSubscription.js b/back/models/notificationSubscription.js index f750a0528a..8efb83e7dc 100644 --- a/back/models/notificationSubscription.js +++ b/back/models/notificationSubscription.js @@ -29,10 +29,46 @@ module.exports = Self => { } const worker = await models.Worker.findById(workerId, {fields: ['id', 'bossFk']}); - const notificationsAvailables = await models.NotificationSubscription.getList(workerId); - const hasAcl = notificationsAvailables.some(available => available.notificationFk === notificationFk); + const available = await Self.getAvailable(workerId); + const hasAcl = available.has(notificationFk); 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; + }; }; From 204323ac69f448988f93d81ba22206d1500b1027 Mon Sep 17 00:00:00 2001 From: alexm Date: Wed, 8 Nov 2023 14:57:07 +0100 Subject: [PATCH 25/81] test(notification_getList): fix --- .../notification/specs/getList.spec.js | 30 ++++--------------- db/dump/fixtures.sql | 2 ++ 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/back/methods/notification/specs/getList.spec.js b/back/methods/notification/specs/getList.spec.js index 7c986eba2d..52ac497a56 100644 --- a/back/methods/notification/specs/getList.spec.js +++ b/back/methods/notification/specs/getList.spec.js @@ -2,30 +2,12 @@ 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 = 1109; - const activeNotifications = await models.NotificationSubscription.find({ - where: {userFk: userId} - }); - const roles = await models.RoleMapping.find({ - fields: ['roleId'], - where: {principalId: userId} - }); - const availableNotifications = await models.NotificationAcl.find({ - where: { - roleFk: { - inq: roles.map(role => { - return role.roleId; - }), - }, - } - }); + const userId = 9; + const {active, available} = await models.NotificationSubscription.getList(userId); + const notifications = await models.Notification.find({}); + const totalAvailable = notifications.length - active.length; - const result = await models.NotificationSubscription.getList(userId); - - expect(result.filter(notification => notification.active == true).length) - .toEqual(activeNotifications.length); - - expect(result.filter(notification => notification.active == false).length) - .toEqual(availableNotifications.length - activeNotifications.length); + expect(active.length).toEqual(2); + expect(available.length).toEqual(totalAvailable); }); }); diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 77440ab9f9..efee3bd282 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2790,7 +2790,9 @@ INSERT INTO `util`.`notificationAcl` (`notificationFk`, `roleFk`) (1, 9), (1, 1), (2, 1), + (3, 9), (4, 1), + (5, 9), (6, 9); INSERT INTO `util`.`notificationQueue` (`id`, `notificationFk`, `params`, `authorFk`, `status`, `created`) From dbe684441950b10151e7d9085e4024200571e397 Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 9 Nov 2023 09:31:32 +0100 Subject: [PATCH 26/81] refs #6415 feat: init version 23.48 --- CHANGELOG.md | 6 ++++++ db/changes/234801/.gitkeep | 0 package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 db/changes/234801/.gitkeep diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d270a3ed..30afaa69b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ 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). +## [2348.01] - 2023-11-30 + +### Added +### Changed +### Fixed + ## [2346.01] - 2023-11-16 ### Added diff --git a/db/changes/234801/.gitkeep b/db/changes/234801/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/package-lock.json b/package-lock.json index 5bf7a2cb1e..b66279ae36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "salix-back", - "version": "23.46.01", + "version": "23.48.01", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "salix-back", - "version": "23.46.01", + "version": "23.48.01", "license": "GPL-3.0", "dependencies": { "axios": "^1.2.2", diff --git a/package.json b/package.json index b1539f9a06..04fcb008bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salix-back", - "version": "23.46.01", + "version": "23.48.01", "author": "Verdnatura Levante SL", "description": "Salix backend", "license": "GPL-3.0", From a0c4573f4d09f16e21e8d043a04bea17d4092d1d Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 9 Nov 2023 09:34:37 +0100 Subject: [PATCH 27/81] refs #4797 fix: correct sql folder --- db/changes/{234601 => 234801}/00-notificationSubscription.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename db/changes/{234601 => 234801}/00-notificationSubscription.sql (100%) diff --git a/db/changes/234601/00-notificationSubscription.sql b/db/changes/234801/00-notificationSubscription.sql similarity index 100% rename from db/changes/234601/00-notificationSubscription.sql rename to db/changes/234801/00-notificationSubscription.sql From 01d3c23cf0bcb3d716be9e7a8a7b90fdfe75968b Mon Sep 17 00:00:00 2001 From: jorgep Date: Thu, 9 Nov 2023 11:57:52 +0100 Subject: [PATCH 28/81] ref #5914 renameTable --- db/changes/234601/00-transferInvoice.sql | 2 +- .../invoiceOut/back/methods/invoiceOut/transferInvoice.js | 4 ++-- modules/invoiceOut/back/model-config.json | 2 +- ...us-invoice-type-477.json => sii-type-invoice-out.json} | 7 +++++-- modules/invoiceOut/front/descriptor-menu/index.html | 8 ++++---- modules/invoiceOut/front/descriptor-menu/index.js | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) rename modules/invoiceOut/back/models/{cplus-invoice-type-477.json => sii-type-invoice-out.json} (68%) diff --git a/db/changes/234601/00-transferInvoice.sql b/db/changes/234601/00-transferInvoice.sql index 7a9890ae45..d9ae394641 100644 --- a/db/changes/234601/00-transferInvoice.sql +++ b/db/changes/234601/00-transferInvoice.sql @@ -1,6 +1,6 @@ INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId) VALUES ('CplusRectificationType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'), - ('CplusInvoiceType477', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'), + ('SiiTypeInvoiceOut', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'), ('InvoiceCorrectionType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'), ('InvoiceOut', 'transferInvoice', 'WRITE', 'ALLOW', 'ROLE', 'administrative'); diff --git a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js index 8a0609b8d2..dde535c998 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js +++ b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js @@ -27,7 +27,7 @@ module.exports = Self => { required: true }, { - arg: 'cplusInvoiceType477Id', + arg: 'siiTypeInvoiceOutId', type: 'number', required: true }, @@ -93,7 +93,7 @@ module.exports = Self => { correctingFk: invoiceId, correctedFk: args.id, cplusRectificationTypeFk: args.cplusRectificationId, - cplusInvoiceType477Fk: args.cplusInvoiceType477Id, + siiTypeInvoiceOutFk: args.siiTypeInvoiceOutId, invoiceCorrectionTypeFk: args.invoiceCorrectionTypeId }, myOptions); diff --git a/modules/invoiceOut/back/model-config.json b/modules/invoiceOut/back/model-config.json index 23246893b4..9c7512429f 100644 --- a/modules/invoiceOut/back/model-config.json +++ b/modules/invoiceOut/back/model-config.json @@ -41,7 +41,7 @@ "InvoiceCorrection": { "dataSource": "vn" }, - "CplusInvoiceType477": { + "SiiTypeInvoiceOut": { "dataSource": "vn" } } diff --git a/modules/invoiceOut/back/models/cplus-invoice-type-477.json b/modules/invoiceOut/back/models/sii-type-invoice-out.json similarity index 68% rename from modules/invoiceOut/back/models/cplus-invoice-type-477.json rename to modules/invoiceOut/back/models/sii-type-invoice-out.json index 840a9a7e4e..89f01bd749 100644 --- a/modules/invoiceOut/back/models/cplus-invoice-type-477.json +++ b/modules/invoiceOut/back/models/sii-type-invoice-out.json @@ -1,9 +1,9 @@ { - "name": "CplusInvoiceType477", + "name": "SiiTypeInvoiceOut", "base": "VnModel", "options": { "mysql": { - "table": "cplusInvoiceType477" + "table": "siiTypeInvoiceOut" } }, "properties": { @@ -12,6 +12,9 @@ "type": "number", "description": "Identifier" }, + "code": { + "type": "string" + }, "description": { "type": "string" } diff --git a/modules/invoiceOut/front/descriptor-menu/index.html b/modules/invoiceOut/front/descriptor-menu/index.html index 7d465f4ea5..d7537bc0a7 100644 --- a/modules/invoiceOut/front/descriptor-menu/index.html +++ b/modules/invoiceOut/front/descriptor-menu/index.html @@ -6,8 +6,8 @@ + url="SiiTypeInvoiceOuts" + data="siiTypeInvoiceOuts"> diff --git a/modules/invoiceOut/front/descriptor-menu/index.js b/modules/invoiceOut/front/descriptor-menu/index.js index 7d2644158e..d3862a753c 100644 --- a/modules/invoiceOut/front/descriptor-menu/index.js +++ b/modules/invoiceOut/front/descriptor-menu/index.js @@ -132,7 +132,7 @@ class Controller extends Section { ref: this.invoiceOut.ref, newClientFk: this.invoiceOut.client.id, cplusRectificationId: this.cplusRectificationType, - cplusInvoiceType477Id: this.cplusInvoiceType477, + siiTypeInvoiceOutId: this.siiTypeInvoiceOut, invoiceCorrectionTypeId: this.invoiceCorrectionType }; this.$http.post(`InvoiceOuts/transferInvoice`, params).then(res => { From b07858fddbaf04dc79f96799468cff2e528a2c6c Mon Sep 17 00:00:00 2001 From: jorgep Date: Thu, 9 Nov 2023 14:33:46 +0100 Subject: [PATCH 29/81] ref #5914 replace name --- db/.archive/225201/00-invoiceOut_new.sql | 2 +- db/.archive/231001/02-invoiceOut_new.sql | 2 +- db/.archive/232001/00-invoiceOut_new.sql | 2 +- db/dump/dumpedFixtures.sql | 10 ++++---- db/dump/structure.sql | 25 +++++++++---------- db/export-data.sh | 2 +- .../invoiceOut/specs/transferinvoice.spec.js | 8 +++--- .../back/models/invoice-correction.json | 2 +- .../back/models/sii-type-invoice-out.json | 3 --- 9 files changed, 26 insertions(+), 30 deletions(-) diff --git a/db/.archive/225201/00-invoiceOut_new.sql b/db/.archive/225201/00-invoiceOut_new.sql index 4c60b50bcb..8e23fb43b7 100644 --- a/db/.archive/225201/00-invoiceOut_new.sql +++ b/db/.archive/225201/00-invoiceOut_new.sql @@ -74,7 +74,7 @@ BEGIN clientFk, dued, companyFk, - cplusInvoiceType477Fk + siiTypeInvoiceOutFk ) SELECT 1, diff --git a/db/.archive/231001/02-invoiceOut_new.sql b/db/.archive/231001/02-invoiceOut_new.sql index d2b96eff78..d570dfb726 100644 --- a/db/.archive/231001/02-invoiceOut_new.sql +++ b/db/.archive/231001/02-invoiceOut_new.sql @@ -96,7 +96,7 @@ BEGIN clientFk, dued, companyFk, - cplusInvoiceType477Fk + siiTypeInvoiceOutFk ) SELECT 1, diff --git a/db/.archive/232001/00-invoiceOut_new.sql b/db/.archive/232001/00-invoiceOut_new.sql index b4fc5c824e..b497dffda6 100644 --- a/db/.archive/232001/00-invoiceOut_new.sql +++ b/db/.archive/232001/00-invoiceOut_new.sql @@ -96,7 +96,7 @@ BEGIN clientFk, dued, companyFk, - cplusInvoiceType477Fk + siiTypeInvoiceOutFk ) SELECT 1, diff --git a/db/dump/dumpedFixtures.sql b/db/dump/dumpedFixtures.sql index 2e1511b590..43eba7f241 100644 --- a/db/dump/dumpedFixtures.sql +++ b/db/dump/dumpedFixtures.sql @@ -275,13 +275,13 @@ INSERT INTO `cplusInvoiceType472` VALUES (1,'F1 - Factura'),(2,'F2 - Factura sim UNLOCK TABLES; -- --- Dumping data for table `cplusInvoiceType477` +-- Dumping data for table `siiTypeInvoiceOut` -- -LOCK TABLES `cplusInvoiceType477` WRITE; -/*!40000 ALTER TABLE `cplusInvoiceType477` DISABLE KEYS */; -INSERT INTO `cplusInvoiceType477` VALUES (1,'F1 - Factura'),(2,'F2 - Factura simplificada (ticket)'),(3,'F3 - Factura emitida en sustitución de facturas simplificadas facturadas y declaradas'),(4,'F4 - Asiento resumen de facturas'),(5,'R1 - Factura rectificativa (Art. 80.1, 80.2 y error fundado en derecho)'),(6,'R2 - Factura rectificativa (Art. 80.3)'),(7,'R3 - Factura rectificativa (Art. 80.4)'),(8,'R4 - Factura rectificativa (Resto)'),(9,'R5 - Factura rectificativa en facturas simplificadas'); -/*!40000 ALTER TABLE `cplusInvoiceType477` ENABLE KEYS */; +LOCK TABLES `siiTypeInvoiceOut` WRITE; +/*!40000 ALTER TABLE `siiTypeInvoiceOut` DISABLE KEYS */; +INSERT INTO `siiTypeInvoiceOut` VALUES (1,'F1 - Factura'),(2,'F2 - Factura simplificada (ticket)'),(3,'F3 - Factura emitida en sustitución de facturas simplificadas facturadas y declaradas'),(4,'F4 - Asiento resumen de facturas'),(5,'R1 - Factura rectificativa (Art. 80.1, 80.2 y error fundado en derecho)'),(6,'R2 - Factura rectificativa (Art. 80.3)'),(7,'R3 - Factura rectificativa (Art. 80.4)'),(8,'R4 - Factura rectificativa (Resto)'),(9,'R5 - Factura rectificativa en facturas simplificadas'); +/*!40000 ALTER TABLE `siiTypeInvoiceOut` ENABLE KEYS */; UNLOCK TABLES; -- diff --git a/db/dump/structure.sql b/db/dump/structure.sql index b242821fc6..f50868b195 100644 --- a/db/dump/structure.sql +++ b/db/dump/structure.sql @@ -25993,13 +25993,12 @@ CREATE TABLE `cplusInvoiceType472` ( /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `cplusInvoiceType477` +-- Table structure for table `siiTypeInvoiceOut` -- - -DROP TABLE IF EXISTS `cplusInvoiceType477`; +DROP TABLE IF EXISTS `siiTypeInvoiceOut`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `cplusInvoiceType477` ( +CREATE TABLE `siiTypeInvoiceOut` ( `id` int(10) unsigned NOT NULL, `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, PRIMARY KEY (`id`) @@ -29450,16 +29449,16 @@ CREATE TABLE `invoiceCorrection` ( `correctingFk` int(10) unsigned NOT NULL COMMENT 'Factura rectificativa', `correctedFk` int(10) unsigned NOT NULL COMMENT 'Factura rectificada', `cplusRectificationTypeFk` int(10) unsigned NOT NULL, - `cplusInvoiceType477Fk` int(10) unsigned NOT NULL, + `siiTypeInvoiceOutFk` int(10) unsigned NOT NULL, `invoiceCorrectionTypeFk` int(11) NOT NULL DEFAULT 3, PRIMARY KEY (`correctingFk`), KEY `correctedFk_idx` (`correctedFk`), KEY `invoiceCorrection_ibfk_1_idx` (`cplusRectificationTypeFk`), - KEY `cplusInvoiceTyoeFk_idx` (`cplusInvoiceType477Fk`), + KEY `cplusInvoiceTyoeFk_idx` (`siiTypeInvoiceOutFk`), KEY `invoiceCorrectionTypeFk_idx` (`invoiceCorrectionTypeFk`), CONSTRAINT `corrected_fk` FOREIGN KEY (`correctedFk`) REFERENCES `invoiceOut` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `correcting_fk` FOREIGN KEY (`correctingFk`) REFERENCES `invoiceOut` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `cplusInvoiceTyoeFk` FOREIGN KEY (`cplusInvoiceType477Fk`) REFERENCES `cplusInvoiceType477` (`id`) ON UPDATE CASCADE, + CONSTRAINT `cplusInvoiceTyoeFk` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceCorrectionType_Fk33` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `invoiceCorrectionType` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceCorrection_ibfk_1` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `cplusRectificationType` (`id`) ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='Relacion entre las facturas rectificativas y las rectificadas.'; @@ -30130,7 +30129,7 @@ CREATE TABLE `invoiceOut` ( `companyFk` int(10) unsigned NOT NULL DEFAULT 442, `hasPdf` tinyint(3) unsigned NOT NULL DEFAULT 0, `booked` date DEFAULT NULL, - `cplusInvoiceType477Fk` int(10) unsigned NOT NULL DEFAULT 1, + `siiTypeInvoiceOutFk` int(10) unsigned NOT NULL DEFAULT 1, `cplusTaxBreakFk` int(10) unsigned NOT NULL DEFAULT 1, `cplusSubjectOpFk` int(10) unsigned NOT NULL DEFAULT 1, `cplusTrascendency477Fk` int(10) unsigned NOT NULL DEFAULT 1, @@ -30140,13 +30139,13 @@ CREATE TABLE `invoiceOut` ( KEY `Id_Cliente` (`clientFk`), KEY `empresa_id` (`companyFk`), KEY `Fecha` (`issued`), - KEY `Facturas_ibfk_2_idx` (`cplusInvoiceType477Fk`), + KEY `Facturas_ibfk_2_idx` (`siiTypeInvoiceOutFk`), KEY `Facturas_ibfk_3_idx` (`cplusSubjectOpFk`), KEY `Facturas_ibfk_4_idx` (`cplusTaxBreakFk`), KEY `Facturas_ibfk_5_idx` (`cplusTrascendency477Fk`), KEY `Facturas_idx_Vencimiento` (`dued`), KEY `invoiceOut_serial` (`serial`), - CONSTRAINT `invoiceOut_ibfk_2` FOREIGN KEY (`cplusInvoiceType477Fk`) REFERENCES `cplusInvoiceType477` (`id`) ON UPDATE CASCADE, + CONSTRAINT `invoiceOut_ibfk_2` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceOut_ibfk_3` FOREIGN KEY (`cplusSubjectOpFk`) REFERENCES `cplusSubjectOp` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceOut_ibfk_4` FOREIGN KEY (`cplusTaxBreakFk`) REFERENCES `cplusTaxBreak` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceOut_serial` FOREIGN KEY (`serial`) REFERENCES `invoiceOutSerial` (`code`), @@ -30308,7 +30307,7 @@ CREATE TABLE `invoiceOutSerial` ( `isTaxed` tinyint(1) NOT NULL DEFAULT 1, `taxAreaFk` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT 'NATIONAL', `isCEE` tinyint(1) NOT NULL DEFAULT 0, - `cplusInvoiceType477Fk` int(10) unsigned DEFAULT 1, + `siiTypeInvoiceOutFk` int(10) unsigned DEFAULT 1, `footNotes` longtext DEFAULT NULL, `isRefEditable` tinyint(4) NOT NULL DEFAULT 0, `type` enum('global','quick') DEFAULT NULL, @@ -58288,7 +58287,7 @@ BEGIN io.cplusTrascendency477Fk AS TIPOCLAVE, io.cplusTaxBreakFk AS TIPOEXENCI, io.cplusSubjectOpFk AS TIPONOSUJE, - io.cplusInvoiceType477Fk AS TIPOFACT, + io.siiTypeInvoiceOutFk AS TIPOFACT, ic.cplusRectificationTypeFk AS TIPORECTIF, io.companyFk, RIGHT(io.ref, LENGTH(io.ref) - 1) AS invoiceNum, @@ -58868,7 +58867,7 @@ BEGIN clientFk, dued, companyFk, - cplusInvoiceType477Fk + siiTypeInvoiceOutFk ) SELECT 1, diff --git a/db/export-data.sh b/db/export-data.sh index 7d346b235e..a516992d34 100755 --- a/db/export-data.sh +++ b/db/export-data.sh @@ -46,7 +46,7 @@ TABLES=( bookingPlanner businessType cplusInvoiceType472 - cplusInvoiceType477 + siiTypeInvoiceOut cplusRectificationType cplusSubjectOp cplusTaxBreak diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js index 04f6df2995..8769484194 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js @@ -2,7 +2,7 @@ const models = require('vn-loopback/server/server').models; const LoopBackContext = require('loopback-context'); -describe('InvoiceOut tranferInvoice()', () => { +fdescribe('InvoiceOut tranferInvoice()', () => { const activeCtx = { accessToken: {userId: 5}, http: { @@ -19,7 +19,7 @@ describe('InvoiceOut tranferInvoice()', () => { }); }); - it('should return the id of the created issued invoice', async() => { + fit('should return the id of the created issued invoice', async() => { const tx = await models.InvoiceOut.beginTransaction({}); const options = {transaction: tx}; const args = { @@ -27,7 +27,7 @@ describe('InvoiceOut tranferInvoice()', () => { ref: 'T4444444', newClientFk: 1, cplusRectificationId: 1, - cplusInvoiceType477Id: 1, + siiTypeInvoiceOutId: 1, invoiceCorrectionTypeId: 1 }; ctx.args = args; @@ -52,7 +52,7 @@ describe('InvoiceOut tranferInvoice()', () => { ref: 'T1111111', newClientFk: 1101, cplusRectificationId: 1, - cplusInvoiceType477Id: 1, + siiTypeInvoiceOutId: 1, invoiceCorrectionTypeId: 1 }; ctx.args = args; diff --git a/modules/invoiceOut/back/models/invoice-correction.json b/modules/invoiceOut/back/models/invoice-correction.json index 48bd172a6d..7c6f015713 100644 --- a/modules/invoiceOut/back/models/invoice-correction.json +++ b/modules/invoiceOut/back/models/invoice-correction.json @@ -18,7 +18,7 @@ "cplusRectificationTypeFk": { "type": "number" }, - "cplusInvoiceType477Fk": { + "siiTypeInvoiceOutFk": { "type": "number" }, "invoiceCorrectionTypeFk": { diff --git a/modules/invoiceOut/back/models/sii-type-invoice-out.json b/modules/invoiceOut/back/models/sii-type-invoice-out.json index 89f01bd749..06934b1ccd 100644 --- a/modules/invoiceOut/back/models/sii-type-invoice-out.json +++ b/modules/invoiceOut/back/models/sii-type-invoice-out.json @@ -12,9 +12,6 @@ "type": "number", "description": "Identifier" }, - "code": { - "type": "string" - }, "description": { "type": "string" } From 0baa9c5a983ad2660e66ef0570cde9e74d05deb7 Mon Sep 17 00:00:00 2001 From: jorgep Date: Thu, 9 Nov 2023 14:34:53 +0100 Subject: [PATCH 30/81] ref #5914 fix summary --- modules/invoiceOut/front/descriptor-menu/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/invoiceOut/front/descriptor-menu/index.html b/modules/invoiceOut/front/descriptor-menu/index.html index d7537bc0a7..09bb4a2abe 100644 --- a/modules/invoiceOut/front/descriptor-menu/index.html +++ b/modules/invoiceOut/front/descriptor-menu/index.html @@ -7,7 +7,7 @@ + data="siiTypeInvoiceOut"> Date: Thu, 9 Nov 2023 15:00:49 +0100 Subject: [PATCH 31/81] ref #5835 due day new and fixtures --- db/dump/fixtures.sql | 4 +- .../back/methods/invoice-in-due-day/new.js | 37 +++++++++++++++++++ .../back/models/invoice-in-due-day.js | 3 ++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 modules/invoiceIn/back/methods/invoice-in-due-day/new.js create mode 100644 modules/invoiceIn/back/models/invoice-in-due-day.js diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index d70279e7de..f57b302f3c 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2880,7 +2880,9 @@ INSERT INTO `vn`.`report` (`id`, `name`, `paperSizeFk`, `method`) INSERT INTO `vn`.`payDemDetail` (`id`, `detail`) VALUES - (1, 1); + (1, 1), + (2, 20), + (7, 1); INSERT INTO `vn`.`workerConfig` (`id`, `businessUpdated`, `roleFk`, `payMethodFk`, `businessTypeFk`) VALUES diff --git a/modules/invoiceIn/back/methods/invoice-in-due-day/new.js b/modules/invoiceIn/back/methods/invoice-in-due-day/new.js new file mode 100644 index 0000000000..37d2f6d054 --- /dev/null +++ b/modules/invoiceIn/back/methods/invoice-in-due-day/new.js @@ -0,0 +1,37 @@ +module.exports = Self => { + Self.remoteMethodCtx('new', { + description: 'Creates a new invoiceIn due day', + accessType: 'WRITE', + accepts: { + arg: 'id', + type: 'number', + required: true, + description: 'The invoiceIn id', + }, + http: { + path: '/new', + verb: 'POST' + } + }); + + Self.new = async(ctx, id, options) => { + let tx; + const myOptions = {userId: ctx.req.accessToken.userId}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + await Self.rawSql(`CALL vn.invoiceInDueDay_calculate(?)`, [id], myOptions); + if (tx) await tx.commit(); + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } + }; +}; diff --git a/modules/invoiceIn/back/models/invoice-in-due-day.js b/modules/invoiceIn/back/models/invoice-in-due-day.js new file mode 100644 index 0000000000..2375c6566a --- /dev/null +++ b/modules/invoiceIn/back/models/invoice-in-due-day.js @@ -0,0 +1,3 @@ +module.exports = Self => { + require('../methods/invoice-in-due-day/new')(Self); +}; From fd142b9f23ba6ef4ab4adaed693f47e95eed476f Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 9 Nov 2023 15:05:36 +0100 Subject: [PATCH 32/81] refs #5881 warmFix: roleSync --- db/changes/234602/00-roleSync.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 db/changes/234602/00-roleSync.sql diff --git a/db/changes/234602/00-roleSync.sql b/db/changes/234602/00-roleSync.sql new file mode 100644 index 0000000000..7ce2777480 --- /dev/null +++ b/db/changes/234602/00-roleSync.sql @@ -0,0 +1 @@ +CALL `account`.`role_sync`(); From 513562955b1d7b11aef24d4e348f83250c70490b Mon Sep 17 00:00:00 2001 From: jorgep Date: Thu, 9 Nov 2023 15:53:41 +0100 Subject: [PATCH 33/81] ref #5835 test back --- .../invoice-in-due-day/specs/new.spec.js | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 modules/invoiceIn/back/methods/invoice-in-due-day/specs/new.spec.js diff --git a/modules/invoiceIn/back/methods/invoice-in-due-day/specs/new.spec.js b/modules/invoiceIn/back/methods/invoice-in-due-day/specs/new.spec.js new file mode 100644 index 0000000000..f2f04b993e --- /dev/null +++ b/modules/invoiceIn/back/methods/invoice-in-due-day/specs/new.spec.js @@ -0,0 +1,51 @@ +const LoopBackContext = require('loopback-context'); +const models = require('vn-loopback/server/server').models; + +describe('invoiceInDueDay new()', () => { + beforeAll(async() => { + const activeCtx = { + accessToken: {userId: 9}, + http: { + req: { + headers: {origin: 'http://localhost'} + } + } + }; + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + + it('should correctly create a new due day', async() => { + const userId = 9; + const invoiceInFk = 6; + + const ctx = { + req: { + + accessToken: {userId: userId}, + headers: {origin: 'http://localhost:5000'}, + } + }; + + const tx = await models.InvoiceIn.beginTransaction({}); + const options = {transaction: tx}; + + try { + await models.InvoiceInDueDay.destroyAll({ + invoiceInFk + }, options); + + await models.InvoiceInDueDay.new(ctx, invoiceInFk, options); + + const result = await models.InvoiceInDueDay.find({where: {invoiceInFk}}, options); + + expect(result).toBeDefined(); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); From 8d94a94e14f1396cd79737d0304c1af1b98ff7d0 Mon Sep 17 00:00:00 2001 From: jorgep Date: Thu, 9 Nov 2023 15:56:50 +0100 Subject: [PATCH 34/81] ref #5914 fix test back --- .../back/methods/invoiceOut/specs/transferinvoice.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js index 8769484194..800a4ea835 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js @@ -2,7 +2,7 @@ const models = require('vn-loopback/server/server').models; const LoopBackContext = require('loopback-context'); -fdescribe('InvoiceOut tranferInvoice()', () => { +describe('InvoiceOut tranferInvoice()', () => { const activeCtx = { accessToken: {userId: 5}, http: { @@ -19,7 +19,7 @@ fdescribe('InvoiceOut tranferInvoice()', () => { }); }); - fit('should return the id of the created issued invoice', async() => { + it('should return the id of the created issued invoice', async() => { const tx = await models.InvoiceOut.beginTransaction({}); const options = {transaction: tx}; const args = { From 2eacf7391ea1d03fdf441277b0c73ac37ddf54ce Mon Sep 17 00:00:00 2001 From: alexm Date: Fri, 10 Nov 2023 08:46:49 +0100 Subject: [PATCH 35/81] refs #6335 fix(db_ticket_canAdvance): not use unnecessary ifnulls --- CHANGELOG.md | 12 ++++++------ .../00-ticket_canAdvance_update.sql | 18 +++++++++--------- .../back/methods/ticket/componentUpdate.js | 8 ++++---- modules/ticket/front/advance/index.js | 4 ++-- 4 files changed, 21 insertions(+), 21 deletions(-) rename db/changes/{234601 => 234801}/00-ticket_canAdvance_update.sql (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09379e91ce..08a62f0449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2348.01] - 2023-11-30 -### Added -### Changed -### Fixed - -## [2346.01] - 2023-11-16 - ### Added - (Ticket -> Adelantar) Permite mover lineas sin generar negativos - (Ticket -> Adelantar) Permite modificar la fecha de los tickets @@ -22,6 +16,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - (Ticket -> RocketChat) Arreglada detección de cambios +## [2346.01] - 2023-11-16 + +### Added +### Changed +### Fixed + ## [2342.01] - 2023-11-02 diff --git a/db/changes/234601/00-ticket_canAdvance_update.sql b/db/changes/234801/00-ticket_canAdvance_update.sql similarity index 90% rename from db/changes/234601/00-ticket_canAdvance_update.sql rename to db/changes/234801/00-ticket_canAdvance_update.sql index 272a0618a1..afe0a4dc6d 100644 --- a/db/changes/234601/00-ticket_canAdvance_update.sql +++ b/db/changes/234801/00-ticket_canAdvance_update.sql @@ -13,8 +13,7 @@ BEGIN SELECT inventoried INTO vDateInventory FROM config; - DROP TEMPORARY TABLE IF EXISTS tmp.stock; - CREATE TEMPORARY TABLE tmp.stock + CREATE OR REPLACE TEMPORARY TABLE tmp.stock (itemFk INT PRIMARY KEY, amount INT) ENGINE = MEMORY; @@ -60,22 +59,23 @@ BEGIN dest.totalWithVat, origin.totalWithVat futureTotalWithVat, dest.agency, + dest.agencyModeFk, origin.futureAgency, + origin.agencyModeFk futureAgencyModeFk, dest.lines, dest.liters, origin.futureLines - origin.hasStock AS notMovableLines, (origin.futureLines = origin.hasStock) AS isFullMovable, + dest.zoneFk, origin.futureZoneFk, origin.futureZoneName, origin.classColor futureClassColor, dest.classColor, - IFNULL(dest.clientFk, origin.clientFk) clientFk, + origin.clientFk futureClientFk, + origin.addressFk futureAddressFk, + origin.warehouseFk futureWarehouseFk, + origin.companyFk futureCompanyFk, IFNULL(dest.nickname, origin.nickname) nickname, - IFNULL(dest.addressFk, origin.addressFk) addressFk, - IFNULL(dest.zoneFk, origin.futureZoneFk) zoneFk, - IFNULL(dest.warehouseFk, origin.warehouseFk) warehouseFk, - IFNULL(dest.companyFk, origin.companyFk) companyFk, - IFNULL(dest.agencyModeFk, origin.agencyModeFk) agencyModeFk, dest.landed FROM ( SELECT @@ -145,7 +145,7 @@ BEGIN AND st.order <= 5 GROUP BY t.id ) dest ON dest.addressFk = origin.addressFk - WHERE origin.hasStock != 0; + WHERE origin.hasStock; DROP TEMPORARY TABLE tmp.stock; END$$ diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js index d8d1163daf..3acd68cfb1 100644 --- a/modules/ticket/back/methods/ticket/componentUpdate.js +++ b/modules/ticket/back/methods/ticket/componentUpdate.js @@ -196,7 +196,7 @@ module.exports = Self => { ] }, myOptions); const originalTicket = JSON.parse(JSON.stringify(ticketToChange)); - const ticketChages = { + const ticketChanges = { clientFk: args.clientFk, nickname: args.nickname, agencyModeFk: args.agencyModeFk, @@ -211,13 +211,13 @@ module.exports = Self => { let response; if (args.keepPrice) { - ticketChages.routeFk = null; - response = await ticketToChange.updateAttributes(ticketChages, myOptions); + ticketChanges.routeFk = null; + response = await ticketToChange.updateAttributes(ticketChanges, myOptions); } else { const hasToBeUnrouted = true; response = await Self.rawSql( 'CALL vn.ticket_componentMakeUpdate(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', - [args.id].concat(Object.values(ticketChages), [hasToBeUnrouted, args.option]), + [args.id].concat(Object.values(ticketChanges), [hasToBeUnrouted, args.option]), myOptions ); } diff --git a/modules/ticket/front/advance/index.js b/modules/ticket/front/advance/index.js index 5ab6632569..fb539311f7 100644 --- a/modules/ticket/front/advance/index.js +++ b/modules/ticket/front/advance/index.js @@ -215,9 +215,9 @@ export default class Controller extends Section { const params = { clientFk: ticket.clientFk, nickname: ticket.nickname, - agencyModeFk: ticket.agencyModeFk, + agencyModeFk: ticket.agencyModeFk ?? ticket.futureAgencyModeFk, addressFk: ticket.addressFk, - zoneFk: ticket.zoneFk, + zoneFk: ticket.zoneFk ?? ticket.futureZoneFk, warehouseFk: ticket.warehouseFk, companyFk: ticket.companyFk, shipped: this.$.model.userParams.dateToAdvance, From 69255fe2fcacd1dc716b26dcf0c60b8face6882d Mon Sep 17 00:00:00 2001 From: alexm Date: Fri, 10 Nov 2023 10:58:58 +0100 Subject: [PATCH 36/81] refs #6014 feat(execute): use user_hasRoutinePriv. feat(execute): split in executeProc & executeFunc --- .vscode/settings.json | 3 +- db/changes/234201/00-ACL_executeRoutine.sql | 3 - db/changes/234801/00-ACL_executeRoutine.sql | 4 + db/dump/structure.sql | 84 ++++++++++++++ .../common/methods/application/execute.js | 34 ++++++ .../common/methods/application/executeFunc.js | 38 +++++++ .../common/methods/application/executeProc.js | 36 ++++++ .../methods/application/executeRoutine.js | 100 ----------------- ...executeRoutine.spec.js => execute.spec.js} | 103 +++++++++++------- loopback/common/models/application.js | 4 +- 10 files changed, 265 insertions(+), 144 deletions(-) delete mode 100644 db/changes/234201/00-ACL_executeRoutine.sql create mode 100644 db/changes/234801/00-ACL_executeRoutine.sql create mode 100644 loopback/common/methods/application/execute.js create mode 100644 loopback/common/methods/application/executeFunc.js create mode 100644 loopback/common/methods/application/executeProc.js delete mode 100644 loopback/common/methods/application/executeRoutine.js rename loopback/common/methods/application/spec/{executeRoutine.spec.js => execute.spec.js} (51%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ed1c8fc2a..899dfc7884 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,7 @@ "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "cSpell.words": [ - "salix" + "salix", + "fdescribe" ] } diff --git a/db/changes/234201/00-ACL_executeRoutine.sql b/db/changes/234201/00-ACL_executeRoutine.sql deleted file mode 100644 index dd112171a3..0000000000 --- a/db/changes/234201/00-ACL_executeRoutine.sql +++ /dev/null @@ -1,3 +0,0 @@ -INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId) - VALUES - ('Application', 'executeRoutine', '*', 'ALLOW', 'ROLE', 'employee'); diff --git a/db/changes/234801/00-ACL_executeRoutine.sql b/db/changes/234801/00-ACL_executeRoutine.sql new file mode 100644 index 0000000000..cfe7018e91 --- /dev/null +++ b/db/changes/234801/00-ACL_executeRoutine.sql @@ -0,0 +1,4 @@ +INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId) + VALUES + ('Application', 'executeProc', '*', 'ALLOW', 'ROLE', 'employee'), + ('Application', 'executeFunc', '*', 'ALLOW', 'ROLE', 'employee'); diff --git a/db/dump/structure.sql b/db/dump/structure.sql index b242821fc6..a8280dc1db 100644 --- a/db/dump/structure.sql +++ b/db/dump/structure.sql @@ -2352,6 +2352,90 @@ BEGIN END IF; END ;; DELIMITER ; + + +DELIMITER ;; +CREATE DEFINER=`root`@`localhost` FUNCTION `account`.`user_hasRoutinePriv`(vType ENUM('PROCEDURE', 'FUNCTION'), + vChain VARCHAR(100), + vUserFk INT +) RETURNS tinyint(1) + READS SQL DATA +BEGIN + +/** + * Search if the user has privileges on routines. + * + * @param vType procedure or function + * @param vChain string passed with this syntax dbName.tableName + * @param vUserFk user to ckeck + * @return vHasPrivilege + */ + DECLARE vHasPrivilege BOOL DEFAULT FALSE; + DECLARE vDb VARCHAR(50); + DECLARE vObject VARCHAR(50); + DECLARE vChainExists BOOL; + DECLARE vExecutePriv INT DEFAULT 262144; + -- 262144 = CONV(1000000000000000000, 2, 10) + -- 1000000000000000000 execution permission expressed in binary base + + SET vDb = SUBSTRING_INDEX(vChain, '.', 1); + SET vChain = SUBSTRING(vChain, LENGTH(vDb) + 2); + SET vObject = SUBSTRING_INDEX(vChain, '.', 1); + + SELECT COUNT(*) INTO vChainExists + FROM mysql.proc + WHERE db = vDb + AND `name` = vObject + AND `type` = vType + LIMIT 1; + + IF NOT vChainExists THEN + RETURN FALSE; + END IF; + + DROP TEMPORARY TABLE IF EXISTS tRole; + CREATE TEMPORARY TABLE tRole + (INDEX (`name`)) + ENGINE = MEMORY + SELECT r.`name` + FROM user u + JOIN roleRole rr ON rr.role = u.role + JOIN `role` r ON r.id = rr.inheritsFrom + WHERE u.id = vUserFk; + + SELECT TRUE INTO vHasPrivilege + FROM mysql.global_priv gp + JOIN tRole tr ON tr.name = gp.`User` + OR CONCAT('$', tr.name) = gp.`User` + WHERE JSON_VALUE(gp.Priv, '$.access') >= vExecutePriv + AND gp.Host = '' + LIMIT 1; + + IF NOT vHasPrivilege THEN + SELECT TRUE INTO vHasPrivilege + FROM mysql.db db + JOIN tRole tr ON tr.name = db.`User` + WHERE db.Db = vDb + AND db.Execute_priv = 'Y'; + END IF; + + IF NOT vHasPrivilege THEN + SELECT TRUE INTO vHasPrivilege + FROM mysql.procs_priv pp + JOIN tRole tr ON tr.name = pp.`User` + WHERE pp.Db = vDb + AND pp.Routine_name = vObject + AND pp.Routine_type = vType + AND pp.Proc_priv = 'Execute' + LIMIT 1; + END IF; + + DROP TEMPORARY TABLE tRole; + RETURN vHasPrivilege; +END ;; +DELIMITER ; + + /*!50003 SET sql_mode = @saved_sql_mode */ ; /*!50003 SET character_set_client = @saved_cs_client */ ; /*!50003 SET character_set_results = @saved_cs_results */ ; diff --git a/loopback/common/methods/application/execute.js b/loopback/common/methods/application/execute.js new file mode 100644 index 0000000000..7a24df0d40 --- /dev/null +++ b/loopback/common/methods/application/execute.js @@ -0,0 +1,34 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.execute = async(ctx, routine, params, schema, type, options) => { + const userId = ctx.req.accessToken.userId; + const models = Self.app.models; + let caller = 'CALL'; + + params = params ?? []; + schema = schema ?? 'vn'; + type = type ?? 'procedure'; + + const myOptions = {userId: ctx.req.accessToken.userId}; + if (typeof options == 'object') + Object.assign(myOptions, options); + + const chain = `${schema}.${routine}`; + const [canExecute] = await models.ProcsPriv.rawSql( + 'SELECT account.user_hasRoutinePriv(?,?,?)', + [type.toUpperCase(), chain, userId], + myOptions); + if (!Object.values(canExecute)[0]) throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED'); + + const isFunction = type == 'function'; + let argString = params.map(() => '?').join(','); + + if (isFunction) + caller = 'SELECT'; + const query = `${caller} ${chain}(${argString})`; + + const [response] = await models.ProcsPriv.rawSql(query, params, myOptions); + return response; + }; +}; diff --git a/loopback/common/methods/application/executeFunc.js b/loopback/common/methods/application/executeFunc.js new file mode 100644 index 0000000000..0a90e8639b --- /dev/null +++ b/loopback/common/methods/application/executeFunc.js @@ -0,0 +1,38 @@ +module.exports = Self => { + Self.remoteMethodCtx('executeFunc', { + description: 'Return result of function', + accessType: '*', + accepts: [ + { + arg: 'routine', + type: 'string', + description: 'The routine name', + required: true, + http: {source: 'path'} + }, + { + arg: 'params', + type: ['any'], + description: 'The params array', + }, + { + arg: 'schema', + type: 'string', + description: 'The routine schema', + } + ], + returns: { + type: 'any', + root: true + }, + http: { + path: `/:routine/execute-func`, + verb: 'POST' + } + }); + + Self.executeFunc = async(ctx, routine, params, schema, options) => { + const response = await Self.execute(ctx, routine, params, schema, 'function', options); + return Object.values(response)[0]; + }; +}; diff --git a/loopback/common/methods/application/executeProc.js b/loopback/common/methods/application/executeProc.js new file mode 100644 index 0000000000..635944eb74 --- /dev/null +++ b/loopback/common/methods/application/executeProc.js @@ -0,0 +1,36 @@ +module.exports = Self => { + Self.remoteMethodCtx('executeProc', { + description: 'Return result of procedure', + accessType: '*', + accepts: [ + { + arg: 'routine', + type: 'string', + description: 'The routine name', + required: true, + http: {source: 'path'} + }, + { + arg: 'params', + type: ['any'], + description: 'The params array', + }, + { + arg: 'schema', + type: 'string', + description: 'The routine schema', + } + ], + returns: { + type: 'any', + root: true + }, + http: { + path: `/:routine/execute-proc`, + verb: 'POST' + } + }); + + Self.executeProc = async(ctx, routine, params, schema, options) => + Self.execute(ctx, routine, params, schema, 'procedure', options); +}; diff --git a/loopback/common/methods/application/executeRoutine.js b/loopback/common/methods/application/executeRoutine.js deleted file mode 100644 index eed34b3441..0000000000 --- a/loopback/common/methods/application/executeRoutine.js +++ /dev/null @@ -1,100 +0,0 @@ -const UserError = require('vn-loopback/util/user-error'); - -module.exports = Self => { - Self.remoteMethodCtx('executeRoutine', { - description: 'Return the routes by worker', - accessType: '*', - accepts: [ - { - arg: 'routine', - type: 'string', - description: 'The routine name', - required: true, - http: {source: 'path'} - }, - { - arg: 'params', - type: ['any'], - description: 'The params array', - }, - { - arg: 'schema', - type: 'string', - description: 'The routine schema', - }, - { - arg: 'type', - type: 'string', - description: 'The routine type', - } - ], - returns: { - type: 'any', - root: true - }, - http: { - path: `/:routine/execute-routine`, - verb: 'POST' - } - }); - - Self.executeRoutine = async(ctx, routine, params, schema, type, options) => { - const userId = ctx.req.accessToken.userId; - const models = Self.app.models; - const isFunction = type == 'function'; - params = params ?? []; - schema = schema ?? 'vn'; - type = type ?? 'procedure'; - let caller = 'CALL'; - - if (isFunction) - caller = 'SELECT'; - - const myOptions = {userId: ctx.req.accessToken.userId}; - if (typeof options == 'object') - Object.assign(myOptions, options); - - const user = await models.VnUser.findById(userId, { - fields: ['id', 'roleFk'], - include: { - relation: 'role', - scope: { - fields: ['id', 'name'] - } - } - }); - - const inherits = await models.RoleRole.find({ - include: { - relation: 'inherits', - scope: { - fields: ['id', 'name'] - } - }, - where: { - role: user.role().id - } - }); - - const roles = inherits.map(inherit => inherit.inherits().name); - - const canExecute = await models.ProcsPriv.findOne({ - where: { - schema, - type: type.toUpperCase(), - name: routine, - host: process.env.NODE_ENV ? '' : '%', - role: {inq: roles} - } - }); - - if (!canExecute) throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED'); - - let argString = params.map(() => '?').join(','); - - const query = `${caller} ${schema}.${routine}(${argString})`; - - const [response] = await models.ProcsPriv.rawSql(query, params, myOptions); - return isFunction ? Object.values(response)[0] : response; - }; -}; diff --git a/loopback/common/methods/application/spec/executeRoutine.spec.js b/loopback/common/methods/application/spec/execute.spec.js similarity index 51% rename from loopback/common/methods/application/spec/executeRoutine.spec.js rename to loopback/common/methods/application/spec/execute.spec.js index 150c5d416d..26e648531f 100644 --- a/loopback/common/methods/application/spec/executeRoutine.spec.js +++ b/loopback/common/methods/application/spec/execute.spec.js @@ -1,6 +1,6 @@ const models = require('vn-loopback/server/server').models; -describe('Application executeRoutine()', () => { +describe('Application execute()/executeProc()/executeFunc()', () => { const userWithoutPrivileges = 1; const userWithPrivileges = 9; const userWithInheritedPrivileges = 120; @@ -48,7 +48,7 @@ describe('Application executeRoutine()', () => { try { const options = {transaction: tx}; - await models.Application.executeRoutine( + await models.Application.execute( ctx, 'myProcedure', [1], @@ -71,7 +71,7 @@ describe('Application executeRoutine()', () => { try { const options = {transaction: tx}; - const response = await models.Application.executeRoutine( + const response = await models.Application.execute( ctx, 'myProcedure', [1], @@ -90,49 +90,74 @@ describe('Application executeRoutine()', () => { } }); - it('should execute function and get data', async() => { - const ctx = getCtx(userWithPrivileges); - try { - const options = {transaction: tx}; + describe('Application executeProc()', () => { + it('should execute procedure and get data (executeProc)', async() => { + const ctx = getCtx(userWithPrivileges); + try { + const options = {transaction: tx}; - const response = await models.Application.executeRoutine( - ctx, - 'myFunction', - [1], - 'bs', - 'function', - options - ); + const response = await models.Application.executeProc( + ctx, + 'myProcedure', + [1], + null, + options + ); - expect(response).toEqual(1); + expect(response.length).toEqual(2); + expect(response[0].myParam).toEqual(1); - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); }); - it('should execute function and get data with user with inherited privileges', async() => { - const ctx = getCtx(userWithInheritedPrivileges); - try { - const options = {transaction: tx}; + describe('Application executeFunc()', () => { + it('should execute function and get data', async() => { + const ctx = getCtx(userWithPrivileges); + try { + const options = {transaction: tx}; - const response = await models.Application.executeRoutine( - ctx, - 'myFunction', - [1], - 'bs', - 'function', - options - ); + const response = await models.Application.executeFunc( + ctx, + 'myFunction', + [1], + 'bs', + options + ); - expect(response).toEqual(1); + expect(response).toEqual(1); - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should execute function and get data with user with inherited privileges', async() => { + const ctx = getCtx(userWithInheritedPrivileges); + try { + const options = {transaction: tx}; + + const response = await models.Application.executeFunc( + ctx, + 'myFunction', + [1], + 'bs', + options + ); + + expect(response).toEqual(1); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); }); }); diff --git a/loopback/common/models/application.js b/loopback/common/models/application.js index b9e639b1be..ac8ae78f0d 100644 --- a/loopback/common/models/application.js +++ b/loopback/common/models/application.js @@ -2,5 +2,7 @@ module.exports = function(Self) { require('../methods/application/status')(Self); require('../methods/application/post')(Self); - require('../methods/application/executeRoutine')(Self); + require('../methods/application/execute')(Self); + require('../methods/application/executeProc')(Self); + require('../methods/application/executeFunc')(Self); }; From a1c8bba4a56e3d0d2907755e6060be55237f24fd Mon Sep 17 00:00:00 2001 From: carlossa Date: Fri, 10 Nov 2023 11:29:18 +0100 Subject: [PATCH 37/81] hotfix addressId --- modules/ticket/back/methods/ticket/closure.js | 289 +++++++++--------- 1 file changed, 145 insertions(+), 144 deletions(-) diff --git a/modules/ticket/back/methods/ticket/closure.js b/modules/ticket/back/methods/ticket/closure.js index 9f9aec9bd1..1d04679d3a 100644 --- a/modules/ticket/back/methods/ticket/closure.js +++ b/modules/ticket/back/methods/ticket/closure.js @@ -5,177 +5,178 @@ const config = require('vn-print/core/config'); const storage = require('vn-print/core/storage'); module.exports = async function(ctx, Self, tickets, reqArgs = {}) { - const userId = ctx.req.accessToken.userId; - if (tickets.length == 0) return; + const userId = ctx.req.accessToken.userId; + if (tickets.length == 0) return; - const failedtickets = []; - for (const ticket of tickets) { - try { - await Self.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id], {userId}); + const failedtickets = []; + for (const ticket of tickets) { + try { + await Self.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id], {userId}); - const [invoiceOut] = await Self.rawSql(` - SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued - FROM ticket t - JOIN invoiceOut io ON io.ref = t.refFk - JOIN company cny ON cny.id = io.companyFk - WHERE t.id = ? - `, [ticket.id]); + const [invoiceOut] = await Self.rawSql(` + SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued + FROM ticket t + JOIN invoiceOut io ON io.ref = t.refFk + JOIN company cny ON cny.id = io.companyFk + WHERE t.id = ? + `, [ticket.id]); - const mailOptions = { - overrideAttachments: true, - attachments: [] - }; + const mailOptions = { + overrideAttachments: true, + attachments: [] + }; - const isToBeMailed = ticket.recipient && ticket.salesPersonFk && ticket.isToBeMailed; + const isToBeMailed = ticket.recipient && ticket.salesPersonFk && ticket.isToBeMailed; - if (invoiceOut) { - const args = { - reference: invoiceOut.ref, - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }; + if (invoiceOut) { + const args = { + reference: invoiceOut.ref, + recipientId: ticket.clientFk, + recipient: ticket.recipient, + replyTo: ticket.salesPersonEmail + }; - const invoiceReport = new Report('invoice', args); - const stream = await invoiceReport.toPdfStream(); + const invoiceReport = new Report('invoice', args); + const stream = await invoiceReport.toPdfStream(); - const issued = invoiceOut.issued; - const year = issued.getFullYear().toString(); - const month = (issued.getMonth() + 1).toString(); - const day = issued.getDate().toString(); + const issued = invoiceOut.issued; + const year = issued.getFullYear().toString(); + const month = (issued.getMonth() + 1).toString(); + const day = issued.getDate().toString(); - const fileName = `${year}${invoiceOut.ref}.pdf`; + const fileName = `${year}${invoiceOut.ref}.pdf`; - // Store invoice - await storage.write(stream, { - type: 'invoice', - path: `${year}/${month}/${day}`, - fileName: fileName - }); + // Store invoice + await storage.write(stream, { + type: 'invoice', + path: `${year}/${month}/${day}`, + fileName: fileName + }); - await Self.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id], {userId}); + await Self.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id], {userId}); - if (isToBeMailed) { - const invoiceAttachment = { - filename: fileName, - content: stream - }; + if (isToBeMailed) { + const invoiceAttachment = { + filename: fileName, + content: stream + }; - if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') { - const exportation = new Report('exportation', args); - const stream = await exportation.toPdfStream(); - const fileName = `CITES-${invoiceOut.ref}.pdf`; + if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') { + const exportation = new Report('exportation', args); + const stream = await exportation.toPdfStream(); + const fileName = `CITES-${invoiceOut.ref}.pdf`; - mailOptions.attachments.push({ - filename: fileName, - content: stream - }); - } + mailOptions.attachments.push({ + filename: fileName, + content: stream + }); + } - mailOptions.attachments.push(invoiceAttachment); + mailOptions.attachments.push(invoiceAttachment); - const email = new Email('invoice', args); - await email.send(mailOptions); - } - } else if (isToBeMailed) { - const args = { - id: ticket.id, - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }; + const email = new Email('invoice', args); + await email.send(mailOptions); + } + } else if (isToBeMailed) { + const args = { + id: ticket.id, + recipientId: ticket.clientFk, + recipient: ticket.recipient, + replyTo: ticket.salesPersonEmail + }; - const email = new Email('delivery-note-link', args); - await email.send(); - } + const email = new Email('delivery-note-link', args); + await email.send(); + } - // Incoterms authorization - const [{firstOrder}] = await Self.rawSql(` - SELECT COUNT(*) as firstOrder - FROM ticket t - JOIN client c ON c.id = t.clientFk - WHERE t.clientFk = ? - AND NOT t.isDeleted - AND c.isVies - `, [ticket.clientFk]); + // Incoterms authorization + const [{firstOrder}] = await Self.rawSql(` + SELECT COUNT(*) as firstOrder + FROM ticket t + JOIN client c ON c.id = t.clientFk + WHERE t.clientFk = ? + AND NOT t.isDeleted + AND c.isVies + `, [ticket.clientFk]); - if (firstOrder == 1) { - const args = { - id: ticket.clientFk, - companyId: ticket.companyFk, - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }; + if (firstOrder == 1) { + const args = { + id: ticket.clientFk, + companyId: ticket.companyFk, + recipientId: ticket.clientFk, + recipient: ticket.recipient, + replyTo: ticket.salesPersonEmail, + addressId: ticket.addressFk + }; - const email = new Email('incoterms-authorization', args); - await email.send(); + const email = new Email('incoterms-authorization', args); + await email.send(); - const [sample] = await Self.rawSql( - `SELECT id - FROM sample - WHERE code = 'incoterms-authorization' - `); + const [sample] = await Self.rawSql( + `SELECT id + FROM sample + WHERE code = 'incoterms-authorization' + `); - await Self.rawSql(` - INSERT INTO clientSample (clientFk, typeFk, companyFk) VALUES(?, ?, ?) - `, [ticket.clientFk, sample.id, ticket.companyFk], {userId}); - }; - } catch (error) { - // Domain not found - if (error.responseCode == 450) - return invalidEmail(ticket); + await Self.rawSql(` + INSERT INTO clientSample (clientFk, typeFk, companyFk) VALUES(?, ?, ?) + `, [ticket.clientFk, sample.id, ticket.companyFk], {userId}); + } + } catch (error) { + // Domain not found + if (error.responseCode == 450) + return invalidEmail(ticket); - // Save tickets on a list of failed ids - failedtickets.push({ - id: ticket.id, - stacktrace: error - }); - } - } + // Save tickets on a list of failed ids + failedtickets.push({ + id: ticket.id, + stacktrace: error + }); + } + } - // Send email with failed tickets - if (failedtickets.length > 0) { - let body = 'This following tickets have failed:

'; + // Send email with failed tickets + if (failedtickets.length > 0) { + let body = 'This following tickets have failed:

'; - for (const ticket of failedtickets) { - body += `Ticket: ${ticket.id} -
${ticket.stacktrace}

`; - } + for (const ticket of failedtickets) { + body += `Ticket: ${ticket.id} +
${ticket.stacktrace}

`; + } - smtp.send({ - to: config.app.reportEmail, - subject: '[API] Nightly ticket closure report', - html: body - }); - } + smtp.send({ + to: config.app.reportEmail, + subject: '[API] Nightly ticket closure report', + html: body + }); + } - async function invalidEmail(ticket) { - await Self.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [ - ticket.clientFk - ], {userId}); + async function invalidEmail(ticket) { + await Self.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [ + ticket.clientFk + ], {userId}); - const oldInstance = `{"email": "${ticket.recipient}"}`; - const newInstance = `{"email": ""}`; - await Self.rawSql(` - INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance) - VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [ - ticket.clientFk, - oldInstance, - newInstance - ], {userId}); + const oldInstance = `{"email": "${ticket.recipient}"}`; + const newInstance = `{"email": ""}`; + await Self.rawSql(` + INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance) + VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [ + ticket.clientFk, + oldInstance, + newInstance + ], {userId}); - const body = `No se ha podido enviar el albarán ${ticket.id} - al cliente ${ticket.clientFk} - ${ticket.clientName} - porque la dirección de email "${ticket.recipient}" no es correcta - o no está disponible.

- Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente. - Actualiza la dirección de email con una correcta.`; + const body = `No se ha podido enviar el albarán ${ticket.id} + al cliente ${ticket.clientFk} - ${ticket.clientName} + porque la dirección de email "${ticket.recipient}" no es correcta + o no está disponible.

+ Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente. + Actualiza la dirección de email con una correcta.`; - smtp.send({ - to: ticket.salesPersonEmail, - subject: 'No se ha podido enviar el albarán', - html: body - }); - } + smtp.send({ + to: ticket.salesPersonEmail, + subject: 'No se ha podido enviar el albarán', + html: body + }); + } }; From 59d2da24eb6d5329b0700453abaabb3215105f79 Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 13 Nov 2023 09:36:20 +0100 Subject: [PATCH 38/81] refs #6014 refactor(execute): split code --- .../common/methods/application/execute.js | 19 +++++++------------ .../common/methods/application/executeFunc.js | 6 +++++- .../common/methods/application/executeProc.js | 9 +++++++-- .../methods/application/spec/execute.spec.js | 8 ++------ 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/loopback/common/methods/application/execute.js b/loopback/common/methods/application/execute.js index 7a24df0d40..c0475dbfed 100644 --- a/loopback/common/methods/application/execute.js +++ b/loopback/common/methods/application/execute.js @@ -1,32 +1,27 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { - Self.execute = async(ctx, routine, params, schema, type, options) => { + Self.execute = async(ctx, query, params, options) => { const userId = ctx.req.accessToken.userId; const models = Self.app.models; - let caller = 'CALL'; - params = params ?? []; - schema = schema ?? 'vn'; - type = type ?? 'procedure'; const myOptions = {userId: ctx.req.accessToken.userId}; if (typeof options == 'object') Object.assign(myOptions, options); - const chain = `${schema}.${routine}`; + let [caller, chain] = query.split(' '); + if (!chain.includes('.')) chain = 'vn.' + chain; + const [canExecute] = await models.ProcsPriv.rawSql( 'SELECT account.user_hasRoutinePriv(?,?,?)', - [type.toUpperCase(), chain, userId], + [caller == 'CALL' ? 'PROCEDURE' : 'FUNCTION', chain, userId], myOptions); + if (!Object.values(canExecute)[0]) throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED'); - const isFunction = type == 'function'; let argString = params.map(() => '?').join(','); - - if (isFunction) - caller = 'SELECT'; - const query = `${caller} ${chain}(${argString})`; + query = `${query}(${argString})`; const [response] = await models.ProcsPriv.rawSql(query, params, myOptions); return response; diff --git a/loopback/common/methods/application/executeFunc.js b/loopback/common/methods/application/executeFunc.js index 0a90e8639b..49e2cdc21c 100644 --- a/loopback/common/methods/application/executeFunc.js +++ b/loopback/common/methods/application/executeFunc.js @@ -32,7 +32,11 @@ module.exports = Self => { }); Self.executeFunc = async(ctx, routine, params, schema, options) => { - const response = await Self.execute(ctx, routine, params, schema, 'function', options); + if (schema) + routine = schema + '.' + routine; + + const query = `SELECT ${routine}`; + const response = await Self.execute(ctx, query, params, options); return Object.values(response)[0]; }; }; diff --git a/loopback/common/methods/application/executeProc.js b/loopback/common/methods/application/executeProc.js index 635944eb74..524831e86d 100644 --- a/loopback/common/methods/application/executeProc.js +++ b/loopback/common/methods/application/executeProc.js @@ -31,6 +31,11 @@ module.exports = Self => { } }); - Self.executeProc = async(ctx, routine, params, schema, options) => - Self.execute(ctx, routine, params, schema, 'procedure', options); + Self.executeProc = async(ctx, routine, params, schema, options) => { + if (schema) + routine = schema + '.' + routine; + + const query = `CALL ${routine}`; + return Self.execute(ctx, query, params, options); + }; }; diff --git a/loopback/common/methods/application/spec/execute.spec.js b/loopback/common/methods/application/spec/execute.spec.js index 26e648531f..4c57279847 100644 --- a/loopback/common/methods/application/spec/execute.spec.js +++ b/loopback/common/methods/application/spec/execute.spec.js @@ -50,10 +50,8 @@ describe('Application execute()/executeProc()/executeFunc()', () => { await models.Application.execute( ctx, - 'myProcedure', + 'CALL myProcedure', [1], - null, - null, options ); @@ -73,10 +71,8 @@ describe('Application execute()/executeProc()/executeFunc()', () => { const response = await models.Application.execute( ctx, - 'myProcedure', + 'CALL myProcedure', [1], - null, - null, options ); From 1a66987cd0ff298ff392ad27d43a81bed410eb14 Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 13 Nov 2023 12:26:31 +0100 Subject: [PATCH 39/81] refs #5979 fix(vn-user): resetPassword url --- back/models/vn-user.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/back/models/vn-user.js b/back/models/vn-user.js index 970497e4e8..712ed7d167 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -92,7 +92,11 @@ module.exports = function(Self) { }; Self.on('resetPasswordRequest', async function(info) { - const url = await Self.app.models.Url.getUrl(); + const loopBackContext = LoopBackContext.getCurrentContext(); + const httpCtx = {req: loopBackContext.active}; + const httpRequest = httpCtx.req.http.req; + const headers = httpRequest.headers; + const origin = headers.origin; const defaultHash = '/reset-password?access_token=$token$'; const recoverHashes = { @@ -108,7 +112,7 @@ module.exports = function(Self) { const params = { recipient: info.email, lang: user.lang, - url: url.slice(0, -1) + recoverHash + url: origin + '/#!' + recoverHash }; const options = Object.assign({}, info.options); From 47d85c4c616f7decd460e4f8a3fca755f9e2587f Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 13 Nov 2023 12:54:02 +0100 Subject: [PATCH 40/81] refs #6335 fix: remove unnecessary translations --- loopback/locale/en.json | 4 +--- loopback/locale/es.json | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 9305c7f093..82cf5b40b8 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -196,7 +196,5 @@ "Negative basis of tickets: 23": "Negative basis of tickets: 23", "Booking completed": "Booking complete", "The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation", - "You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets", - "newTicket": "New ticket", - "keepPrice": "Keep prices" + "You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets" } diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 933a354680..0ee6194e4f 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -225,8 +225,6 @@ "reference duplicated": "Referencia duplicada", "This ticket is already a refund": "Este ticket ya es un abono", "isWithoutNegatives": "Sin negativos", - "newTicket": "Nuevo ticket", - "keepPrice": "Mantener precios", "routeFk": "routeFk", "Can't change the password of another worker": "No se puede cambiar la contraseña de otro trabajador", "No hay un contrato en vigor": "No hay un contrato en vigor", From cce61ae8ccf9b7d747a0f934a187b9adcfb8bc59 Mon Sep 17 00:00:00 2001 From: alexm Date: Tue, 14 Nov 2023 09:17:46 +0100 Subject: [PATCH 41/81] refs #6014 refactor(execute): schema required --- .../common/methods/application/execute.js | 12 +++++------ .../common/methods/application/executeFunc.js | 19 +++++++++--------- .../common/methods/application/executeProc.js | 20 +++++++++---------- .../methods/application/spec/execute.spec.js | 12 ++++++----- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/loopback/common/methods/application/execute.js b/loopback/common/methods/application/execute.js index c0475dbfed..7995b12e35 100644 --- a/loopback/common/methods/application/execute.js +++ b/loopback/common/methods/application/execute.js @@ -1,7 +1,7 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { - Self.execute = async(ctx, query, params, options) => { + Self.execute = async(ctx, type, query, params, options) => { const userId = ctx.req.accessToken.userId; const models = Self.app.models; params = params ?? []; @@ -10,20 +10,18 @@ module.exports = Self => { if (typeof options == 'object') Object.assign(myOptions, options); - let [caller, chain] = query.split(' '); - if (!chain.includes('.')) chain = 'vn.' + chain; + const chain = query.split(' ')[1]; const [canExecute] = await models.ProcsPriv.rawSql( 'SELECT account.user_hasRoutinePriv(?,?,?)', - [caller == 'CALL' ? 'PROCEDURE' : 'FUNCTION', chain, userId], + [type, chain, userId], myOptions); if (!Object.values(canExecute)[0]) throw new UserError(`You don't have enough privileges`, 'ACCESS_DENIED'); - let argString = params.map(() => '?').join(','); - query = `${query}(${argString})`; + const argString = params.map(() => '?').join(','); - const [response] = await models.ProcsPriv.rawSql(query, params, myOptions); + const [response] = await models.ProcsPriv.rawSql(query + `(${argString})`, params, myOptions); return response; }; }; diff --git a/loopback/common/methods/application/executeFunc.js b/loopback/common/methods/application/executeFunc.js index 49e2cdc21c..f915f44829 100644 --- a/loopback/common/methods/application/executeFunc.js +++ b/loopback/common/methods/application/executeFunc.js @@ -10,16 +10,17 @@ module.exports = Self => { required: true, http: {source: 'path'} }, + { + arg: 'schema', + type: 'string', + description: 'The routine schema', + required: true, + }, { arg: 'params', type: ['any'], description: 'The params array', }, - { - arg: 'schema', - type: 'string', - description: 'The routine schema', - } ], returns: { type: 'any', @@ -31,12 +32,10 @@ module.exports = Self => { } }); - Self.executeFunc = async(ctx, routine, params, schema, options) => { - if (schema) - routine = schema + '.' + routine; + Self.executeFunc = async(ctx, routine, schema, params, options) => { + const query = `SELECT ${schema}.${routine}`; - const query = `SELECT ${routine}`; - const response = await Self.execute(ctx, query, params, options); + const response = await Self.execute(ctx, 'FUNCTION', query, params, options); return Object.values(response)[0]; }; }; diff --git a/loopback/common/methods/application/executeProc.js b/loopback/common/methods/application/executeProc.js index 524831e86d..5859611d98 100644 --- a/loopback/common/methods/application/executeProc.js +++ b/loopback/common/methods/application/executeProc.js @@ -10,16 +10,17 @@ module.exports = Self => { required: true, http: {source: 'path'} }, + { + arg: 'schema', + type: 'string', + description: 'The routine schema', + required: true, + }, { arg: 'params', type: ['any'], description: 'The params array', }, - { - arg: 'schema', - type: 'string', - description: 'The routine schema', - } ], returns: { type: 'any', @@ -31,11 +32,8 @@ module.exports = Self => { } }); - Self.executeProc = async(ctx, routine, params, schema, options) => { - if (schema) - routine = schema + '.' + routine; - - const query = `CALL ${routine}`; - return Self.execute(ctx, query, params, options); + Self.executeProc = async(ctx, routine, schema, params, options) => { + const query = `CALL ${schema}.${routine}`; + return Self.execute(ctx, 'PROCEDURE', query, params, options); }; }; diff --git a/loopback/common/methods/application/spec/execute.spec.js b/loopback/common/methods/application/spec/execute.spec.js index 4c57279847..1a0a8ace9b 100644 --- a/loopback/common/methods/application/spec/execute.spec.js +++ b/loopback/common/methods/application/spec/execute.spec.js @@ -50,7 +50,8 @@ describe('Application execute()/executeProc()/executeFunc()', () => { await models.Application.execute( ctx, - 'CALL myProcedure', + 'PROCEDURE', + 'CALL vn.myProcedure', [1], options ); @@ -71,7 +72,8 @@ describe('Application execute()/executeProc()/executeFunc()', () => { const response = await models.Application.execute( ctx, - 'CALL myProcedure', + 'PROCEDURE', + 'CALL vn.myProcedure', [1], options ); @@ -95,8 +97,8 @@ describe('Application execute()/executeProc()/executeFunc()', () => { const response = await models.Application.executeProc( ctx, 'myProcedure', + 'vn', [1], - null, options ); @@ -120,8 +122,8 @@ describe('Application execute()/executeProc()/executeFunc()', () => { const response = await models.Application.executeFunc( ctx, 'myFunction', - [1], 'bs', + [1], options ); @@ -142,8 +144,8 @@ describe('Application execute()/executeProc()/executeFunc()', () => { const response = await models.Application.executeFunc( ctx, 'myFunction', - [1], 'bs', + [1], options ); From aec1c9d4475bfb0605145b3617af9acf4ec522f2 Mon Sep 17 00:00:00 2001 From: alexm Date: Tue, 14 Nov 2023 09:54:07 +0100 Subject: [PATCH 42/81] fix: correct align --- modules/ticket/back/methods/ticket/closure.js | 288 +++++++++--------- 1 file changed, 144 insertions(+), 144 deletions(-) diff --git a/modules/ticket/back/methods/ticket/closure.js b/modules/ticket/back/methods/ticket/closure.js index 9f9aec9bd1..7a2825a4d6 100644 --- a/modules/ticket/back/methods/ticket/closure.js +++ b/modules/ticket/back/methods/ticket/closure.js @@ -5,177 +5,177 @@ const config = require('vn-print/core/config'); const storage = require('vn-print/core/storage'); module.exports = async function(ctx, Self, tickets, reqArgs = {}) { - const userId = ctx.req.accessToken.userId; - if (tickets.length == 0) return; + const userId = ctx.req.accessToken.userId; + if (tickets.length == 0) return; - const failedtickets = []; - for (const ticket of tickets) { - try { - await Self.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id], {userId}); + const failedtickets = []; + for (const ticket of tickets) { + try { + await Self.rawSql(`CALL vn.ticket_closeByTicket(?)`, [ticket.id], {userId}); - const [invoiceOut] = await Self.rawSql(` - SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued - FROM ticket t - JOIN invoiceOut io ON io.ref = t.refFk - JOIN company cny ON cny.id = io.companyFk - WHERE t.id = ? - `, [ticket.id]); + const [invoiceOut] = await Self.rawSql(` + SELECT io.id, io.ref, io.serial, cny.code companyCode, io.issued + FROM ticket t + JOIN invoiceOut io ON io.ref = t.refFk + JOIN company cny ON cny.id = io.companyFk + WHERE t.id = ? + `, [ticket.id]); - const mailOptions = { - overrideAttachments: true, - attachments: [] - }; + const mailOptions = { + overrideAttachments: true, + attachments: [] + }; - const isToBeMailed = ticket.recipient && ticket.salesPersonFk && ticket.isToBeMailed; + const isToBeMailed = ticket.recipient && ticket.salesPersonFk && ticket.isToBeMailed; - if (invoiceOut) { - const args = { - reference: invoiceOut.ref, - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }; + if (invoiceOut) { + const args = { + reference: invoiceOut.ref, + recipientId: ticket.clientFk, + recipient: ticket.recipient, + replyTo: ticket.salesPersonEmail + }; - const invoiceReport = new Report('invoice', args); - const stream = await invoiceReport.toPdfStream(); + const invoiceReport = new Report('invoice', args); + const stream = await invoiceReport.toPdfStream(); - const issued = invoiceOut.issued; - const year = issued.getFullYear().toString(); - const month = (issued.getMonth() + 1).toString(); - const day = issued.getDate().toString(); + const issued = invoiceOut.issued; + const year = issued.getFullYear().toString(); + const month = (issued.getMonth() + 1).toString(); + const day = issued.getDate().toString(); - const fileName = `${year}${invoiceOut.ref}.pdf`; + const fileName = `${year}${invoiceOut.ref}.pdf`; - // Store invoice - await storage.write(stream, { - type: 'invoice', - path: `${year}/${month}/${day}`, - fileName: fileName - }); + // Store invoice + await storage.write(stream, { + type: 'invoice', + path: `${year}/${month}/${day}`, + fileName: fileName + }); - await Self.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id], {userId}); + await Self.rawSql('UPDATE invoiceOut SET hasPdf = true WHERE id = ?', [invoiceOut.id], {userId}); - if (isToBeMailed) { - const invoiceAttachment = { - filename: fileName, - content: stream - }; + if (isToBeMailed) { + const invoiceAttachment = { + filename: fileName, + content: stream + }; - if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') { - const exportation = new Report('exportation', args); - const stream = await exportation.toPdfStream(); - const fileName = `CITES-${invoiceOut.ref}.pdf`; + if (invoiceOut.serial == 'E' && invoiceOut.companyCode == 'VNL') { + const exportation = new Report('exportation', args); + const stream = await exportation.toPdfStream(); + const fileName = `CITES-${invoiceOut.ref}.pdf`; - mailOptions.attachments.push({ - filename: fileName, - content: stream - }); - } + mailOptions.attachments.push({ + filename: fileName, + content: stream + }); + } - mailOptions.attachments.push(invoiceAttachment); + mailOptions.attachments.push(invoiceAttachment); - const email = new Email('invoice', args); - await email.send(mailOptions); - } - } else if (isToBeMailed) { - const args = { - id: ticket.id, - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }; + const email = new Email('invoice', args); + await email.send(mailOptions); + } + } else if (isToBeMailed) { + const args = { + id: ticket.id, + recipientId: ticket.clientFk, + recipient: ticket.recipient, + replyTo: ticket.salesPersonEmail + }; - const email = new Email('delivery-note-link', args); - await email.send(); - } + const email = new Email('delivery-note-link', args); + await email.send(); + } - // Incoterms authorization - const [{firstOrder}] = await Self.rawSql(` - SELECT COUNT(*) as firstOrder - FROM ticket t - JOIN client c ON c.id = t.clientFk - WHERE t.clientFk = ? - AND NOT t.isDeleted - AND c.isVies - `, [ticket.clientFk]); + // Incoterms authorization + const [{firstOrder}] = await Self.rawSql(` + SELECT COUNT(*) as firstOrder + FROM ticket t + JOIN client c ON c.id = t.clientFk + WHERE t.clientFk = ? + AND NOT t.isDeleted + AND c.isVies + `, [ticket.clientFk]); - if (firstOrder == 1) { - const args = { - id: ticket.clientFk, - companyId: ticket.companyFk, - recipientId: ticket.clientFk, - recipient: ticket.recipient, - replyTo: ticket.salesPersonEmail - }; + if (firstOrder == 1) { + const args = { + id: ticket.clientFk, + companyId: ticket.companyFk, + recipientId: ticket.clientFk, + recipient: ticket.recipient, + replyTo: ticket.salesPersonEmail + }; - const email = new Email('incoterms-authorization', args); - await email.send(); + const email = new Email('incoterms-authorization', args); + await email.send(); - const [sample] = await Self.rawSql( - `SELECT id - FROM sample - WHERE code = 'incoterms-authorization' - `); + const [sample] = await Self.rawSql( + `SELECT id + FROM sample + WHERE code = 'incoterms-authorization' + `); - await Self.rawSql(` - INSERT INTO clientSample (clientFk, typeFk, companyFk) VALUES(?, ?, ?) - `, [ticket.clientFk, sample.id, ticket.companyFk], {userId}); - }; - } catch (error) { - // Domain not found - if (error.responseCode == 450) - return invalidEmail(ticket); + await Self.rawSql(` + INSERT INTO clientSample (clientFk, typeFk, companyFk) VALUES(?, ?, ?) + `, [ticket.clientFk, sample.id, ticket.companyFk], {userId}); + } + } catch (error) { + // Domain not found + if (error.responseCode == 450) + return invalidEmail(ticket); - // Save tickets on a list of failed ids - failedtickets.push({ - id: ticket.id, - stacktrace: error - }); - } - } + // Save tickets on a list of failed ids + failedtickets.push({ + id: ticket.id, + stacktrace: error + }); + } + } - // Send email with failed tickets - if (failedtickets.length > 0) { - let body = 'This following tickets have failed:

'; + // Send email with failed tickets + if (failedtickets.length > 0) { + let body = 'This following tickets have failed:

'; - for (const ticket of failedtickets) { - body += `Ticket: ${ticket.id} -
${ticket.stacktrace}

`; - } + for (const ticket of failedtickets) { + body += `Ticket: ${ticket.id} +
${ticket.stacktrace}

`; + } - smtp.send({ - to: config.app.reportEmail, - subject: '[API] Nightly ticket closure report', - html: body - }); - } + smtp.send({ + to: config.app.reportEmail, + subject: '[API] Nightly ticket closure report', + html: body + }); + } - async function invalidEmail(ticket) { - await Self.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [ - ticket.clientFk - ], {userId}); + async function invalidEmail(ticket) { + await Self.rawSql(`UPDATE client SET email = NULL WHERE id = ?`, [ + ticket.clientFk + ], {userId}); - const oldInstance = `{"email": "${ticket.recipient}"}`; - const newInstance = `{"email": ""}`; - await Self.rawSql(` - INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance) - VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [ - ticket.clientFk, - oldInstance, - newInstance - ], {userId}); + const oldInstance = `{"email": "${ticket.recipient}"}`; + const newInstance = `{"email": ""}`; + await Self.rawSql(` + INSERT INTO clientLog (originFk, userFk, action, changedModel, oldInstance, newInstance) + VALUES (?, NULL, 'UPDATE', 'Client', ?, ?)`, [ + ticket.clientFk, + oldInstance, + newInstance + ], {userId}); - const body = `No se ha podido enviar el albarán ${ticket.id} - al cliente ${ticket.clientFk} - ${ticket.clientName} - porque la dirección de email "${ticket.recipient}" no es correcta - o no está disponible.

- Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente. - Actualiza la dirección de email con una correcta.`; + const body = `No se ha podido enviar el albarán ${ticket.id} + al cliente ${ticket.clientFk} - ${ticket.clientName} + porque la dirección de email "${ticket.recipient}" no es correcta + o no está disponible.

+ Para evitar que se repita este error, se ha eliminado la dirección de email de la ficha del cliente. + Actualiza la dirección de email con una correcta.`; - smtp.send({ - to: ticket.salesPersonEmail, - subject: 'No se ha podido enviar el albarán', - html: body - }); - } + smtp.send({ + to: ticket.salesPersonEmail, + subject: 'No se ha podido enviar el albarán', + html: body + }); + } }; From f3faf37e1a0e77227402a2c41fa54b5fdd2f5f7c Mon Sep 17 00:00:00 2001 From: carlossa Date: Tue, 14 Nov 2023 12:34:00 +0100 Subject: [PATCH 43/81] hotfix addressFk --- .../ticket/back/methods/ticket/closeAll.js | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/modules/ticket/back/methods/ticket/closeAll.js b/modules/ticket/back/methods/ticket/closeAll.js index 660c168937..46c45aa927 100644 --- a/modules/ticket/back/methods/ticket/closeAll.js +++ b/modules/ticket/back/methods/ticket/closeAll.js @@ -32,31 +32,30 @@ module.exports = Self => { throw new UserError('You cannot close tickets for today'); const tickets = await Self.rawSql(` - SELECT - t.id, - t.clientFk, - t.companyFk, - c.name clientName, - c.email recipient, - c.salesPersonFk, - c.isToBeMailed, - c.hasToInvoice, - co.hasDailyInvoice, - eu.email salesPersonEmail - FROM ticket t - JOIN agencyMode am ON am.id = t.agencyModeFk - JOIN warehouse wh ON wh.id = t.warehouseFk AND wh.hasComission - JOIN ticketState ts ON ts.ticketFk = t.id - JOIN alertLevel al ON al.id = ts.alertLevel - JOIN client c ON c.id = t.clientFk - JOIN province p ON p.id = c.provinceFk - JOIN country co ON co.id = p.countryFk - LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk - WHERE (al.code = 'PACKED' OR (am.code = 'refund' AND al.code != 'delivered')) - AND DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY) - AND util.dayEnd(?) - AND t.refFk IS NULL - GROUP BY t.id + SELECT t.id, + t.clientFk, + t.companyFk, + c.name clientName, + c.email recipient, + c.salesPersonFk, + c.isToBeMailed, + c.hasToInvoice, + co.hasDailyInvoice, + eu.email salesPersonEmail, + t.addressFk + FROM ticket t + JOIN agencyMode am ON am.id = t.agencyModeFk + JOIN warehouse wh ON wh.id = t.warehouseFk AND wh.hasComission + JOIN ticketState ts ON ts.ticketFk = t.id + JOIN alertLevel al ON al.id = ts.alertLevel + JOIN client c ON c.id = t.clientFk + JOIN province p ON p.id = c.provinceFk + JOIN country co ON co.id = p.countryFk + LEFT JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk + WHERE (al.code = 'PACKED' OR (am.code = 'refund' AND al.code != 'delivered')) + AND DATE(t.shipped) BETWEEN DATE_ADD(?, INTERVAL -2 DAY) AND util.dayEnd(?) + AND t.refFk IS NULL + GROUP BY t.id `, [toDate, toDate]); await closure(ctx, Self, tickets); From 2bec1a2bed8fb261fe85ecf898467d78542de2ab Mon Sep 17 00:00:00 2001 From: sergiodt Date: Tue, 14 Nov 2023 14:02:40 +0100 Subject: [PATCH 44/81] refs #6014 fix: response type. fix: accessType EXECUTE --- loopback/common/methods/application/execute.js | 5 +++-- loopback/common/methods/application/executeFunc.js | 2 +- loopback/common/methods/application/executeProc.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/loopback/common/methods/application/execute.js b/loopback/common/methods/application/execute.js index 7995b12e35..a468dcd700 100644 --- a/loopback/common/methods/application/execute.js +++ b/loopback/common/methods/application/execute.js @@ -21,7 +21,8 @@ module.exports = Self => { const argString = params.map(() => '?').join(','); - const [response] = await models.ProcsPriv.rawSql(query + `(${argString})`, params, myOptions); - return response; + const response = await models.ProcsPriv.rawSql(query + `(${argString})`, params, myOptions); + if (!Array.isArray(response)) return; + return response[0]; }; }; diff --git a/loopback/common/methods/application/executeFunc.js b/loopback/common/methods/application/executeFunc.js index f915f44829..a42fdae677 100644 --- a/loopback/common/methods/application/executeFunc.js +++ b/loopback/common/methods/application/executeFunc.js @@ -1,7 +1,7 @@ module.exports = Self => { Self.remoteMethodCtx('executeFunc', { description: 'Return result of function', - accessType: '*', + accessType: 'EXECUTE', accepts: [ { arg: 'routine', diff --git a/loopback/common/methods/application/executeProc.js b/loopback/common/methods/application/executeProc.js index 5859611d98..a8825da0fc 100644 --- a/loopback/common/methods/application/executeProc.js +++ b/loopback/common/methods/application/executeProc.js @@ -1,7 +1,7 @@ module.exports = Self => { Self.remoteMethodCtx('executeProc', { description: 'Return result of procedure', - accessType: '*', + accessType: 'EXECUTE', accepts: [ { arg: 'routine', From a5682e9a0d663637c6f5b726569cd364e8b8a933 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Tue, 14 Nov 2023 15:39:50 +0100 Subject: [PATCH 45/81] doc: refs #6062 README abbreviation --- README.md | 40 ++-------------------------------------- 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 59f5dbcf78..b420bc44f6 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Salix is also the scientific name of a beautifull tree! :) Required applications. -* Node.js >= 16.x LTS +* Node.js * Docker * Git @@ -17,20 +17,7 @@ You will need to install globally the following items. $ sudo npm install -g jest gulp-cli ``` -For the usage of jest --watch on macOs. -``` -$ brew install watchman -``` -* [watchman](https://facebook.github.io/watchman/) - -## Linux Only Prerequisites - -Your user must be on the docker group to use it so you will need to run this command: -``` -$ sudo usermod -a -G docker yourusername -``` - -## Getting Started // Installing +## Installing dependencies and launching Pull from repository. @@ -76,29 +63,6 @@ In Visual Studio Code we use the ESLint extension. ext install dbaeumer.vscode-eslint ``` -Gitlens for visualization of code authorship -``` -ext install eamodio.gitlens -``` - -Spanish language pack -``` -ext install ms-ceintl.vscode-language-pack-es -``` - -### Recommended extensions - -Material icon Theme -``` -ext install pkief.material-icon-theme -``` - -Material UI Themes -``` -ext install equinusocio.vsc-material-theme -``` - - ## Built With * [angularjs](https://angularjs.org/) From 88ec0f24c3cf61c5b160ab4351c4519406fa4857 Mon Sep 17 00:00:00 2001 From: alexm Date: Tue, 14 Nov 2023 15:45:52 +0100 Subject: [PATCH 46/81] ref #5914 rename table --- db/.archive/225201/00-invoiceOut_new.sql | 2 +- db/.archive/231001/02-invoiceOut_new.sql | 2 +- db/.archive/232001/00-invoiceOut_new.sql | 2 +- db/changes/234601/00-transferInvoice.sql | 2 +- db/dump/dumpedFixtures.sql | 10 ++++---- db/dump/structure.sql | 24 +++++++++---------- db/export-data.sh | 2 +- .../invoiceOut/specs/transferinvoice.spec.js | 4 ++-- .../methods/invoiceOut/transferInvoice.js | 4 ++-- modules/invoiceOut/back/model-config.json | 2 +- .../back/models/cplus-invoice-type-477.json | 6 ++--- .../back/models/invoice-correction.json | 4 ++-- .../front/descriptor-menu/index.html | 14 +++++------ .../invoiceOut/front/descriptor-menu/index.js | 2 +- 14 files changed, 40 insertions(+), 40 deletions(-) diff --git a/db/.archive/225201/00-invoiceOut_new.sql b/db/.archive/225201/00-invoiceOut_new.sql index 4c60b50bcb..8e23fb43b7 100644 --- a/db/.archive/225201/00-invoiceOut_new.sql +++ b/db/.archive/225201/00-invoiceOut_new.sql @@ -74,7 +74,7 @@ BEGIN clientFk, dued, companyFk, - cplusInvoiceType477Fk + siiTypeInvoiceOutFk ) SELECT 1, diff --git a/db/.archive/231001/02-invoiceOut_new.sql b/db/.archive/231001/02-invoiceOut_new.sql index d2b96eff78..d570dfb726 100644 --- a/db/.archive/231001/02-invoiceOut_new.sql +++ b/db/.archive/231001/02-invoiceOut_new.sql @@ -96,7 +96,7 @@ BEGIN clientFk, dued, companyFk, - cplusInvoiceType477Fk + siiTypeInvoiceOutFk ) SELECT 1, diff --git a/db/.archive/232001/00-invoiceOut_new.sql b/db/.archive/232001/00-invoiceOut_new.sql index b4fc5c824e..b497dffda6 100644 --- a/db/.archive/232001/00-invoiceOut_new.sql +++ b/db/.archive/232001/00-invoiceOut_new.sql @@ -96,7 +96,7 @@ BEGIN clientFk, dued, companyFk, - cplusInvoiceType477Fk + siiTypeInvoiceOutFk ) SELECT 1, diff --git a/db/changes/234601/00-transferInvoice.sql b/db/changes/234601/00-transferInvoice.sql index 7a9890ae45..d9ae394641 100644 --- a/db/changes/234601/00-transferInvoice.sql +++ b/db/changes/234601/00-transferInvoice.sql @@ -1,6 +1,6 @@ INSERT INTO `salix`.`ACL` (model, property, accessType, permission, principalType, principalId) VALUES ('CplusRectificationType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'), - ('CplusInvoiceType477', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'), + ('SiiTypeInvoiceOut', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'), ('InvoiceCorrectionType', '*', 'READ', 'ALLOW', 'ROLE', 'administrative'), ('InvoiceOut', 'transferInvoice', 'WRITE', 'ALLOW', 'ROLE', 'administrative'); diff --git a/db/dump/dumpedFixtures.sql b/db/dump/dumpedFixtures.sql index 2e1511b590..43eba7f241 100644 --- a/db/dump/dumpedFixtures.sql +++ b/db/dump/dumpedFixtures.sql @@ -275,13 +275,13 @@ INSERT INTO `cplusInvoiceType472` VALUES (1,'F1 - Factura'),(2,'F2 - Factura sim UNLOCK TABLES; -- --- Dumping data for table `cplusInvoiceType477` +-- Dumping data for table `siiTypeInvoiceOut` -- -LOCK TABLES `cplusInvoiceType477` WRITE; -/*!40000 ALTER TABLE `cplusInvoiceType477` DISABLE KEYS */; -INSERT INTO `cplusInvoiceType477` VALUES (1,'F1 - Factura'),(2,'F2 - Factura simplificada (ticket)'),(3,'F3 - Factura emitida en sustitución de facturas simplificadas facturadas y declaradas'),(4,'F4 - Asiento resumen de facturas'),(5,'R1 - Factura rectificativa (Art. 80.1, 80.2 y error fundado en derecho)'),(6,'R2 - Factura rectificativa (Art. 80.3)'),(7,'R3 - Factura rectificativa (Art. 80.4)'),(8,'R4 - Factura rectificativa (Resto)'),(9,'R5 - Factura rectificativa en facturas simplificadas'); -/*!40000 ALTER TABLE `cplusInvoiceType477` ENABLE KEYS */; +LOCK TABLES `siiTypeInvoiceOut` WRITE; +/*!40000 ALTER TABLE `siiTypeInvoiceOut` DISABLE KEYS */; +INSERT INTO `siiTypeInvoiceOut` VALUES (1,'F1 - Factura'),(2,'F2 - Factura simplificada (ticket)'),(3,'F3 - Factura emitida en sustitución de facturas simplificadas facturadas y declaradas'),(4,'F4 - Asiento resumen de facturas'),(5,'R1 - Factura rectificativa (Art. 80.1, 80.2 y error fundado en derecho)'),(6,'R2 - Factura rectificativa (Art. 80.3)'),(7,'R3 - Factura rectificativa (Art. 80.4)'),(8,'R4 - Factura rectificativa (Resto)'),(9,'R5 - Factura rectificativa en facturas simplificadas'); +/*!40000 ALTER TABLE `siiTypeInvoiceOut` ENABLE KEYS */; UNLOCK TABLES; -- diff --git a/db/dump/structure.sql b/db/dump/structure.sql index b242821fc6..3dab0c5a49 100644 --- a/db/dump/structure.sql +++ b/db/dump/structure.sql @@ -25993,13 +25993,13 @@ CREATE TABLE `cplusInvoiceType472` ( /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `cplusInvoiceType477` +-- Table structure for table `siiTypeInvoiceOut` -- -DROP TABLE IF EXISTS `cplusInvoiceType477`; +DROP TABLE IF EXISTS `siiTypeInvoiceOut`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `cplusInvoiceType477` ( +CREATE TABLE `siiTypeInvoiceOut` ( `id` int(10) unsigned NOT NULL, `description` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, PRIMARY KEY (`id`) @@ -29450,16 +29450,16 @@ CREATE TABLE `invoiceCorrection` ( `correctingFk` int(10) unsigned NOT NULL COMMENT 'Factura rectificativa', `correctedFk` int(10) unsigned NOT NULL COMMENT 'Factura rectificada', `cplusRectificationTypeFk` int(10) unsigned NOT NULL, - `cplusInvoiceType477Fk` int(10) unsigned NOT NULL, + `siiTypeInvoiceOutFk` int(10) unsigned NOT NULL, `invoiceCorrectionTypeFk` int(11) NOT NULL DEFAULT 3, PRIMARY KEY (`correctingFk`), KEY `correctedFk_idx` (`correctedFk`), KEY `invoiceCorrection_ibfk_1_idx` (`cplusRectificationTypeFk`), - KEY `cplusInvoiceTyoeFk_idx` (`cplusInvoiceType477Fk`), + KEY `cplusInvoiceTyoeFk_idx` (`siiTypeInvoiceOutFk`), KEY `invoiceCorrectionTypeFk_idx` (`invoiceCorrectionTypeFk`), CONSTRAINT `corrected_fk` FOREIGN KEY (`correctedFk`) REFERENCES `invoiceOut` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `correcting_fk` FOREIGN KEY (`correctingFk`) REFERENCES `invoiceOut` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT `cplusInvoiceTyoeFk` FOREIGN KEY (`cplusInvoiceType477Fk`) REFERENCES `cplusInvoiceType477` (`id`) ON UPDATE CASCADE, + CONSTRAINT `cplusInvoiceTyoeFk` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceCorrectionType_Fk33` FOREIGN KEY (`invoiceCorrectionTypeFk`) REFERENCES `invoiceCorrectionType` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceCorrection_ibfk_1` FOREIGN KEY (`cplusRectificationTypeFk`) REFERENCES `cplusRectificationType` (`id`) ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci COMMENT='Relacion entre las facturas rectificativas y las rectificadas.'; @@ -30130,7 +30130,7 @@ CREATE TABLE `invoiceOut` ( `companyFk` int(10) unsigned NOT NULL DEFAULT 442, `hasPdf` tinyint(3) unsigned NOT NULL DEFAULT 0, `booked` date DEFAULT NULL, - `cplusInvoiceType477Fk` int(10) unsigned NOT NULL DEFAULT 1, + `siiTypeInvoiceOutFk` int(10) unsigned NOT NULL DEFAULT 1, `cplusTaxBreakFk` int(10) unsigned NOT NULL DEFAULT 1, `cplusSubjectOpFk` int(10) unsigned NOT NULL DEFAULT 1, `cplusTrascendency477Fk` int(10) unsigned NOT NULL DEFAULT 1, @@ -30140,13 +30140,13 @@ CREATE TABLE `invoiceOut` ( KEY `Id_Cliente` (`clientFk`), KEY `empresa_id` (`companyFk`), KEY `Fecha` (`issued`), - KEY `Facturas_ibfk_2_idx` (`cplusInvoiceType477Fk`), + KEY `Facturas_ibfk_2_idx` (`siiTypeInvoiceOutFk`), KEY `Facturas_ibfk_3_idx` (`cplusSubjectOpFk`), KEY `Facturas_ibfk_4_idx` (`cplusTaxBreakFk`), KEY `Facturas_ibfk_5_idx` (`cplusTrascendency477Fk`), KEY `Facturas_idx_Vencimiento` (`dued`), KEY `invoiceOut_serial` (`serial`), - CONSTRAINT `invoiceOut_ibfk_2` FOREIGN KEY (`cplusInvoiceType477Fk`) REFERENCES `cplusInvoiceType477` (`id`) ON UPDATE CASCADE, + CONSTRAINT `invoiceOut_ibfk_2` FOREIGN KEY (`siiTypeInvoiceOutFk`) REFERENCES `siiTypeInvoiceOut` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceOut_ibfk_3` FOREIGN KEY (`cplusSubjectOpFk`) REFERENCES `cplusSubjectOp` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceOut_ibfk_4` FOREIGN KEY (`cplusTaxBreakFk`) REFERENCES `cplusTaxBreak` (`id`) ON UPDATE CASCADE, CONSTRAINT `invoiceOut_serial` FOREIGN KEY (`serial`) REFERENCES `invoiceOutSerial` (`code`), @@ -30308,7 +30308,7 @@ CREATE TABLE `invoiceOutSerial` ( `isTaxed` tinyint(1) NOT NULL DEFAULT 1, `taxAreaFk` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT 'NATIONAL', `isCEE` tinyint(1) NOT NULL DEFAULT 0, - `cplusInvoiceType477Fk` int(10) unsigned DEFAULT 1, + `siiTypeInvoiceOutFk` int(10) unsigned DEFAULT 1, `footNotes` longtext DEFAULT NULL, `isRefEditable` tinyint(4) NOT NULL DEFAULT 0, `type` enum('global','quick') DEFAULT NULL, @@ -58288,7 +58288,7 @@ BEGIN io.cplusTrascendency477Fk AS TIPOCLAVE, io.cplusTaxBreakFk AS TIPOEXENCI, io.cplusSubjectOpFk AS TIPONOSUJE, - io.cplusInvoiceType477Fk AS TIPOFACT, + io.siiTypeInvoiceOutFk AS TIPOFACT, ic.cplusRectificationTypeFk AS TIPORECTIF, io.companyFk, RIGHT(io.ref, LENGTH(io.ref) - 1) AS invoiceNum, @@ -58868,7 +58868,7 @@ BEGIN clientFk, dued, companyFk, - cplusInvoiceType477Fk + siiTypeInvoiceOutFk ) SELECT 1, diff --git a/db/export-data.sh b/db/export-data.sh index 7d346b235e..a516992d34 100755 --- a/db/export-data.sh +++ b/db/export-data.sh @@ -46,7 +46,7 @@ TABLES=( bookingPlanner businessType cplusInvoiceType472 - cplusInvoiceType477 + siiTypeInvoiceOut cplusRectificationType cplusSubjectOp cplusTaxBreak diff --git a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js index 04f6df2995..800a4ea835 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js +++ b/modules/invoiceOut/back/methods/invoiceOut/specs/transferinvoice.spec.js @@ -27,7 +27,7 @@ describe('InvoiceOut tranferInvoice()', () => { ref: 'T4444444', newClientFk: 1, cplusRectificationId: 1, - cplusInvoiceType477Id: 1, + siiTypeInvoiceOutId: 1, invoiceCorrectionTypeId: 1 }; ctx.args = args; @@ -52,7 +52,7 @@ describe('InvoiceOut tranferInvoice()', () => { ref: 'T1111111', newClientFk: 1101, cplusRectificationId: 1, - cplusInvoiceType477Id: 1, + siiTypeInvoiceOutId: 1, invoiceCorrectionTypeId: 1 }; ctx.args = args; diff --git a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js index 8a0609b8d2..dde535c998 100644 --- a/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js +++ b/modules/invoiceOut/back/methods/invoiceOut/transferInvoice.js @@ -27,7 +27,7 @@ module.exports = Self => { required: true }, { - arg: 'cplusInvoiceType477Id', + arg: 'siiTypeInvoiceOutId', type: 'number', required: true }, @@ -93,7 +93,7 @@ module.exports = Self => { correctingFk: invoiceId, correctedFk: args.id, cplusRectificationTypeFk: args.cplusRectificationId, - cplusInvoiceType477Fk: args.cplusInvoiceType477Id, + siiTypeInvoiceOutFk: args.siiTypeInvoiceOutId, invoiceCorrectionTypeFk: args.invoiceCorrectionTypeId }, myOptions); diff --git a/modules/invoiceOut/back/model-config.json b/modules/invoiceOut/back/model-config.json index 23246893b4..9c7512429f 100644 --- a/modules/invoiceOut/back/model-config.json +++ b/modules/invoiceOut/back/model-config.json @@ -41,7 +41,7 @@ "InvoiceCorrection": { "dataSource": "vn" }, - "CplusInvoiceType477": { + "SiiTypeInvoiceOut": { "dataSource": "vn" } } diff --git a/modules/invoiceOut/back/models/cplus-invoice-type-477.json b/modules/invoiceOut/back/models/cplus-invoice-type-477.json index 840a9a7e4e..17b3126178 100644 --- a/modules/invoiceOut/back/models/cplus-invoice-type-477.json +++ b/modules/invoiceOut/back/models/cplus-invoice-type-477.json @@ -1,9 +1,9 @@ { - "name": "CplusInvoiceType477", + "name": "SiiTypeInvoiceOut", "base": "VnModel", "options": { "mysql": { - "table": "cplusInvoiceType477" + "table": "siiTypeInvoiceOut" } }, "properties": { @@ -16,4 +16,4 @@ "type": "string" } } -} \ No newline at end of file +} diff --git a/modules/invoiceOut/back/models/invoice-correction.json b/modules/invoiceOut/back/models/invoice-correction.json index 48bd172a6d..43e4f07ef5 100644 --- a/modules/invoiceOut/back/models/invoice-correction.json +++ b/modules/invoiceOut/back/models/invoice-correction.json @@ -18,11 +18,11 @@ "cplusRectificationTypeFk": { "type": "number" }, - "cplusInvoiceType477Fk": { + "siiTypeInvoiceOutFk": { "type": "number" }, "invoiceCorrectionTypeFk": { "type": "number" } } -} \ No newline at end of file +} diff --git a/modules/invoiceOut/front/descriptor-menu/index.html b/modules/invoiceOut/front/descriptor-menu/index.html index 7d465f4ea5..0052f0c037 100644 --- a/modules/invoiceOut/front/descriptor-menu/index.html +++ b/modules/invoiceOut/front/descriptor-menu/index.html @@ -6,8 +6,8 @@
+ url="SiiTypeInvoiceOuts" + data="siiTypeInvoiceOut"> Transfer invoice to... @@ -223,15 +223,15 @@ -
-
\ No newline at end of file + diff --git a/modules/invoiceOut/front/descriptor-menu/index.js b/modules/invoiceOut/front/descriptor-menu/index.js index 7d2644158e..d3862a753c 100644 --- a/modules/invoiceOut/front/descriptor-menu/index.js +++ b/modules/invoiceOut/front/descriptor-menu/index.js @@ -132,7 +132,7 @@ class Controller extends Section { ref: this.invoiceOut.ref, newClientFk: this.invoiceOut.client.id, cplusRectificationId: this.cplusRectificationType, - cplusInvoiceType477Id: this.cplusInvoiceType477, + siiTypeInvoiceOutId: this.siiTypeInvoiceOut, invoiceCorrectionTypeId: this.invoiceCorrectionType }; this.$http.post(`InvoiceOuts/transferInvoice`, params).then(res => { From 4061241650352d430082a5fdf1f29000df51df96 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 15 Nov 2023 09:37:03 +0100 Subject: [PATCH 47/81] refs #6434 feat: create signInLog table --- db/changes/234801/00-createSignInLogTable.sql | 19 ++++++++++ modules/account/back/model-config.json | 3 ++ modules/account/back/models/sign_in-log.json | 35 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 db/changes/234801/00-createSignInLogTable.sql create mode 100644 modules/account/back/models/sign_in-log.json diff --git a/db/changes/234801/00-createSignInLogTable.sql b/db/changes/234801/00-createSignInLogTable.sql new file mode 100644 index 0000000000..977de46463 --- /dev/null +++ b/db/changes/234801/00-createSignInLogTable.sql @@ -0,0 +1,19 @@ + + +-- +-- Table structure for table `signInLog` +-- + +DROP TABLE IF EXISTS `account`.`signInLog`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `account`.`signInLog` ( + `id` varchar(10) NOT NULL , + `userFk` int(10) unsigned DEFAULT NULL, + `creationDate` timestamp NULL DEFAULT current_timestamp(), + `ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `userFk` (`userFk`), + CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +); + diff --git a/modules/account/back/model-config.json b/modules/account/back/model-config.json index a4eb9fa571..b4bd6dbafd 100644 --- a/modules/account/back/model-config.json +++ b/modules/account/back/model-config.json @@ -35,6 +35,9 @@ "SambaConfig": { "dataSource": "vn" }, + "SignInLog": { + "dataSource": "vn" + }, "Sip": { "dataSource": "vn" }, diff --git a/modules/account/back/models/sign_in-log.json b/modules/account/back/models/sign_in-log.json new file mode 100644 index 0000000000..df9ad8153e --- /dev/null +++ b/modules/account/back/models/sign_in-log.json @@ -0,0 +1,35 @@ +{ + "name": "SignInLog", + "base": "VnModel", + "options": { + "mysql": { + "table": "account.signInLog" + } + }, + "properties": { + "id": { + "id": true, + "type": "string", + "forceId": false + }, + "creationDate": { + "type": "date" + }, + "userFk": { + "type": "number" + }, + "ip": { + "type": "string" + } + }, + "relations": { + "user": { + "type": "belongsTo", + "model": "VnUser", + "foreignKey": "userFk" + } + }, + "scope": { + "order": ["creationDate DESC", "id DESC"] + } +} From e25b7d0a12638801f7ab21eb80b70c252fdff668 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 15 Nov 2023 09:39:51 +0100 Subject: [PATCH 48/81] refs #6434 feat: show error for wrong login --- back/models/vn-user.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/back/models/vn-user.js b/back/models/vn-user.js index de5bf7b63e..00f5cd0b87 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -2,6 +2,7 @@ const vnModel = require('vn-loopback/common/models/vn-model'); const {Email} = require('vn-print'); const ForbiddenError = require('vn-loopback/util/forbiddenError'); const LoopBackContext = require('loopback-context'); +const UserError = require('vn-loopback/util/user-error'); module.exports = function(Self) { vnModel(Self); @@ -121,10 +122,16 @@ module.exports = function(Self) { }); Self.validateLogin = async function(user, password) { - let loginInfo = Object.assign({password}, Self.userUses(user)); - token = await Self.login(loginInfo, 'user'); + const loginInfo = Object.assign({password}, Self.userUses(user)); + const token = await Self.login(loginInfo, 'user'); const userToken = await token.user.get(); + + if (userToken.username !== user) { + console.error('ERROR!!! - Signin with other user', userToken, user); + throw new UserError('Try again'); + } + try { await Self.app.models.Account.sync(userToken.name, password); } catch (err) { From 11352798f09da689ab275f670ce2c55e426f00b1 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 15 Nov 2023 09:41:12 +0100 Subject: [PATCH 49/81] refs #6434 feat: save token in db for each login --- back/methods/vn-user/sign-in.js | 9 +++++++-- loopback/locale/es.json | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/back/methods/vn-user/sign-in.js b/back/methods/vn-user/sign-in.js index b9e0d2f705..25f708b8e5 100644 --- a/back/methods/vn-user/sign-in.js +++ b/back/methods/vn-user/sign-in.js @@ -49,8 +49,13 @@ module.exports = Self => { if (vnUser.twoFactor) throw new ForbiddenError(null, 'REQUIRES_2FA'); } - - return Self.validateLogin(user, password); + const validateLogin = await Self.validateLogin(user, password); + await Self.app.models.SignInLog.create({ + id: validateLogin.token, + userFk: vnUser.id, + ip: ctx.req.ip + }); + return validateLogin; }; Self.passExpired = async vnUser => { diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 3cc9a96278..da37d4005f 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -321,9 +321,10 @@ "Select a different client": "Seleccione un cliente distinto", "Fill all the fields": "Rellene todos los campos", "The response is not a PDF": "La respuesta no es un PDF", - "Ticket without Route": "Ticket sin ruta", "Booking completed": "Reserva completada", "The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación", "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina", - "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina" + "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina", + "The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada", + "User disabled": "Usuario desactivado" } From 4d677ccc899faec445f423339e937e71e4014d25 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 15 Nov 2023 10:02:48 +0100 Subject: [PATCH 50/81] refs #6434 perf: rename folder db/changes version --- db/changes/{234801 => 234603}/00-createSignInLogTable.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename db/changes/{234801 => 234603}/00-createSignInLogTable.sql (100%) diff --git a/db/changes/234801/00-createSignInLogTable.sql b/db/changes/234603/00-createSignInLogTable.sql similarity index 100% rename from db/changes/234801/00-createSignInLogTable.sql rename to db/changes/234603/00-createSignInLogTable.sql From f7b088297874d1a15aeb0175bb5cd1cf486da70e Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 15 Nov 2023 11:50:56 +0100 Subject: [PATCH 51/81] refs #6434 feat: remove forceId property --- modules/account/back/models/sign_in-log.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/account/back/models/sign_in-log.json b/modules/account/back/models/sign_in-log.json index df9ad8153e..44575b0137 100644 --- a/modules/account/back/models/sign_in-log.json +++ b/modules/account/back/models/sign_in-log.json @@ -9,8 +9,7 @@ "properties": { "id": { "id": true, - "type": "string", - "forceId": false + "type": "string" }, "creationDate": { "type": "date" From 692c5eb164279ae0a964bbceb70007f61db46fbc Mon Sep 17 00:00:00 2001 From: sergiodt Date: Wed, 15 Nov 2023 12:38:08 +0100 Subject: [PATCH 52/81] refs #5652 fix: setVisibleDiscard add parameter null --- modules/item/back/methods/item/setVisibleDiscard.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/item/back/methods/item/setVisibleDiscard.js b/modules/item/back/methods/item/setVisibleDiscard.js index 08cc507c32..16cef6baf7 100644 --- a/modules/item/back/methods/item/setVisibleDiscard.js +++ b/modules/item/back/methods/item/setVisibleDiscard.js @@ -20,8 +20,7 @@ module.exports = Self => { }, { arg: 'addressFk', - type: 'Number', - required: true, + type: 'Any', description: 'The address id' }], http: { From 89967b85383fd53c5232b1a0cfc92b670ab6bf56 Mon Sep 17 00:00:00 2001 From: alexm Date: Wed, 15 Nov 2023 13:16:31 +0100 Subject: [PATCH 53/81] fix(workerDescriptor): show account button --- modules/worker/front/descriptor/index.html | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/worker/front/descriptor/index.html b/modules/worker/front/descriptor/index.html index aa6b803003..8290e2a155 100644 --- a/modules/worker/front/descriptor/index.html +++ b/modules/worker/front/descriptor/index.html @@ -38,7 +38,7 @@ + > @@ -61,8 +61,6 @@
@@ -75,13 +73,13 @@ - - Date: Wed, 15 Nov 2023 13:49:41 +0100 Subject: [PATCH 54/81] refs #6434 feat: create signInLog table --- db/changes/234202/00-createSignInLogTable.sql | 19 +++++++++++ modules/account/back/model-config.json | 3 ++ modules/account/back/models/sign_in-log.json | 34 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 db/changes/234202/00-createSignInLogTable.sql create mode 100644 modules/account/back/models/sign_in-log.json diff --git a/db/changes/234202/00-createSignInLogTable.sql b/db/changes/234202/00-createSignInLogTable.sql new file mode 100644 index 0000000000..977de46463 --- /dev/null +++ b/db/changes/234202/00-createSignInLogTable.sql @@ -0,0 +1,19 @@ + + +-- +-- Table structure for table `signInLog` +-- + +DROP TABLE IF EXISTS `account`.`signInLog`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `account`.`signInLog` ( + `id` varchar(10) NOT NULL , + `userFk` int(10) unsigned DEFAULT NULL, + `creationDate` timestamp NULL DEFAULT current_timestamp(), + `ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `userFk` (`userFk`), + CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +); + diff --git a/modules/account/back/model-config.json b/modules/account/back/model-config.json index a4eb9fa571..b4bd6dbafd 100644 --- a/modules/account/back/model-config.json +++ b/modules/account/back/model-config.json @@ -35,6 +35,9 @@ "SambaConfig": { "dataSource": "vn" }, + "SignInLog": { + "dataSource": "vn" + }, "Sip": { "dataSource": "vn" }, diff --git a/modules/account/back/models/sign_in-log.json b/modules/account/back/models/sign_in-log.json new file mode 100644 index 0000000000..44575b0137 --- /dev/null +++ b/modules/account/back/models/sign_in-log.json @@ -0,0 +1,34 @@ +{ + "name": "SignInLog", + "base": "VnModel", + "options": { + "mysql": { + "table": "account.signInLog" + } + }, + "properties": { + "id": { + "id": true, + "type": "string" + }, + "creationDate": { + "type": "date" + }, + "userFk": { + "type": "number" + }, + "ip": { + "type": "string" + } + }, + "relations": { + "user": { + "type": "belongsTo", + "model": "VnUser", + "foreignKey": "userFk" + } + }, + "scope": { + "order": ["creationDate DESC", "id DESC"] + } +} From 0a31968cc7ad7b6883ec184dd5e3457760072807 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 15 Nov 2023 13:49:54 +0100 Subject: [PATCH 55/81] refs #6434 feat: show error for wrong login --- back/models/vn-user.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/back/models/vn-user.js b/back/models/vn-user.js index 712ed7d167..62bdfa2da2 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -2,6 +2,7 @@ const vnModel = require('vn-loopback/common/models/vn-model'); const {Email} = require('vn-print'); const ForbiddenError = require('vn-loopback/util/forbiddenError'); const LoopBackContext = require('loopback-context'); +const UserError = require('vn-loopback/util/user-error'); module.exports = function(Self) { vnModel(Self); @@ -125,10 +126,16 @@ module.exports = function(Self) { }); Self.validateLogin = async function(user, password) { - let loginInfo = Object.assign({password}, Self.userUses(user)); - token = await Self.login(loginInfo, 'user'); + const loginInfo = Object.assign({password}, Self.userUses(user)); + const token = await Self.login(loginInfo, 'user'); const userToken = await token.user.get(); + + if (userToken.username !== user) { + console.error('ERROR!!! - Signin with other user', userToken, user); + throw new UserError('Try again'); + } + try { await Self.app.models.Account.sync(userToken.name, password); } catch (err) { From 58edcb9055c56b84f08384aa135119b608158518 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 15 Nov 2023 13:50:06 +0100 Subject: [PATCH 56/81] refs #6434 feat: save token in db for each login --- back/methods/vn-user/sign-in.js | 9 +++++++-- loopback/locale/es.json | 5 +++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/back/methods/vn-user/sign-in.js b/back/methods/vn-user/sign-in.js index b9e0d2f705..25f708b8e5 100644 --- a/back/methods/vn-user/sign-in.js +++ b/back/methods/vn-user/sign-in.js @@ -49,8 +49,13 @@ module.exports = Self => { if (vnUser.twoFactor) throw new ForbiddenError(null, 'REQUIRES_2FA'); } - - return Self.validateLogin(user, password); + const validateLogin = await Self.validateLogin(user, password); + await Self.app.models.SignInLog.create({ + id: validateLogin.token, + userFk: vnUser.id, + ip: ctx.req.ip + }); + return validateLogin; }; Self.passExpired = async vnUser => { diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 4b297144fa..ce56a67291 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -318,9 +318,10 @@ "Social name should be uppercase": "La razón social debe ir en mayúscula", "Street should be uppercase": "La dirección fiscal debe ir en mayúscula", "The response is not a PDF": "La respuesta no es un PDF", - "Ticket without Route": "Ticket sin ruta", "Booking completed": "Reserva completada", "The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación", "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina", - "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina" + "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina", + "The notification subscription of this worker cant be modified": "La subscripción a la notificación de este trabajador no puede ser modificada", + "User disabled": "Usuario desactivado" } From 13720291b2802826ba55a06c2dc8ee0aa778e6d4 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Wed, 15 Nov 2023 15:18:02 +0100 Subject: [PATCH 57/81] refs #6434 perf: remove unique in token column --- modules/account/back/models/sign_in-log.json | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/account/back/models/sign_in-log.json b/modules/account/back/models/sign_in-log.json index 44575b0137..3cd971cb87 100644 --- a/modules/account/back/models/sign_in-log.json +++ b/modules/account/back/models/sign_in-log.json @@ -8,7 +8,6 @@ }, "properties": { "id": { - "id": true, "type": "string" }, "creationDate": { From 4896be51ef6516cf8ef430fa0d4ae475b5c6e250 Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 16 Nov 2023 07:27:42 +0100 Subject: [PATCH 58/81] fix: translation --- loopback/locale/es.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 4b297144fa..74dc2f15ad 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -321,6 +321,6 @@ "Ticket without Route": "Ticket sin ruta", "Booking completed": "Reserva completada", "The ticket is in preparation": "El ticket [{{ticketId}}]({{{ticketUrl}}}) del comercial {{salesPersonId}} está en preparación", - "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mímina", - "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mímina" + "The amount cannot be less than the minimum": "La cantidad no puede ser menor que la cantidad mínima", + "quantityLessThanMin": "La cantidad no puede ser menor que la cantidad mínima" } From c6b5d7f5a4a291dbfbcdc4d6187fb92bb0052912 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 08:30:01 +0100 Subject: [PATCH 59/81] refs #6434 feat: rename id key by token --- back/methods/vn-user/sign-in.js | 2 +- db/changes/234202/00-createSignInLogTable.sql | 2 +- modules/account/back/models/sign_in-log.json | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/back/methods/vn-user/sign-in.js b/back/methods/vn-user/sign-in.js index 25f708b8e5..9c2d568f4f 100644 --- a/back/methods/vn-user/sign-in.js +++ b/back/methods/vn-user/sign-in.js @@ -51,7 +51,7 @@ module.exports = Self => { } const validateLogin = await Self.validateLogin(user, password); await Self.app.models.SignInLog.create({ - id: validateLogin.token, + token: validateLogin.token, userFk: vnUser.id, ip: ctx.req.ip }); diff --git a/db/changes/234202/00-createSignInLogTable.sql b/db/changes/234202/00-createSignInLogTable.sql index 977de46463..6a648bb517 100644 --- a/db/changes/234202/00-createSignInLogTable.sql +++ b/db/changes/234202/00-createSignInLogTable.sql @@ -8,7 +8,7 @@ DROP TABLE IF EXISTS `account`.`signInLog`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `account`.`signInLog` ( - `id` varchar(10) NOT NULL , + `token` varchar(255) NOT NULL , `userFk` int(10) unsigned DEFAULT NULL, `creationDate` timestamp NULL DEFAULT current_timestamp(), `ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, diff --git a/modules/account/back/models/sign_in-log.json b/modules/account/back/models/sign_in-log.json index 3cd971cb87..9e6df2da57 100644 --- a/modules/account/back/models/sign_in-log.json +++ b/modules/account/back/models/sign_in-log.json @@ -7,13 +7,16 @@ } }, "properties": { - "id": { - "type": "string" + "token": { + "required": true, + "type": "string", + "description": "Token's user" }, "creationDate": { - "type": "date" + "type": "date" }, "userFk": { + "required": true, "type": "number" }, "ip": { From 3c8ed3ff51c7d80551cb068095f86d044fb3037d Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 08:30:27 +0100 Subject: [PATCH 60/81] refs #6434 feat: username.toLowerCase() --- back/models/vn-user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/models/vn-user.js b/back/models/vn-user.js index 62bdfa2da2..5845c2192c 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -131,7 +131,7 @@ module.exports = function(Self) { const userToken = await token.user.get(); - if (userToken.username !== user) { + if (userToken.username.toLowerCase() !== user.toLowerCase()) { console.error('ERROR!!! - Signin with other user', userToken, user); throw new UserError('Try again'); } From e73836cbdb551ee1f850955a7d99c768ede44d7c Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 08:31:01 +0100 Subject: [PATCH 61/81] refs #6434 feat: remove primary key constraint --- db/changes/234202/00-createSignInLogTable.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/db/changes/234202/00-createSignInLogTable.sql b/db/changes/234202/00-createSignInLogTable.sql index 6a648bb517..168c7b33fa 100644 --- a/db/changes/234202/00-createSignInLogTable.sql +++ b/db/changes/234202/00-createSignInLogTable.sql @@ -12,8 +12,7 @@ CREATE TABLE `account`.`signInLog` ( `userFk` int(10) unsigned DEFAULT NULL, `creationDate` timestamp NULL DEFAULT current_timestamp(), `ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, - PRIMARY KEY (`id`), - KEY `userFk` (`userFk`), + CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ); From d032b3a527da098541818cde90cb600188ec4189 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 08:42:17 +0100 Subject: [PATCH 62/81] refs #6434 perf: add description to signInLog table --- db/changes/234202/00-createSignInLogTable.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/db/changes/234202/00-createSignInLogTable.sql b/db/changes/234202/00-createSignInLogTable.sql index 168c7b33fa..918b871376 100644 --- a/db/changes/234202/00-createSignInLogTable.sql +++ b/db/changes/234202/00-createSignInLogTable.sql @@ -2,6 +2,7 @@ -- -- Table structure for table `signInLog` +-- Description: log to debug cross-login error -- DROP TABLE IF EXISTS `account`.`signInLog`; From 144b26cf4cd8f9fe17f5d4e435247930250c343c Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 08:47:08 +0100 Subject: [PATCH 63/81] refs #6434 perf: signInLog table id column AI --- db/changes/234202/00-createSignInLogTable.sql | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/changes/234202/00-createSignInLogTable.sql b/db/changes/234202/00-createSignInLogTable.sql index 918b871376..5253481357 100644 --- a/db/changes/234202/00-createSignInLogTable.sql +++ b/db/changes/234202/00-createSignInLogTable.sql @@ -9,11 +9,12 @@ DROP TABLE IF EXISTS `account`.`signInLog`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `account`.`signInLog` ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `token` varchar(255) NOT NULL , `userFk` int(10) unsigned DEFAULT NULL, `creationDate` timestamp NULL DEFAULT current_timestamp(), `ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, - + KEY `userFk` (`userFk`), CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ); From 54e8e6a27d60884a86b58256a722b08ccb0f7a21 Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 16 Nov 2023 09:10:45 +0100 Subject: [PATCH 64/81] refs #6417 fix(vnUser): toLowerCase --- back/models/vn-user.js | 2 +- loopback/locale/en.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/back/models/vn-user.js b/back/models/vn-user.js index 62bdfa2da2..5845c2192c 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -131,7 +131,7 @@ module.exports = function(Self) { const userToken = await token.user.get(); - if (userToken.username !== user) { + if (userToken.username.toLowerCase() !== user.toLowerCase()) { console.error('ERROR!!! - Signin with other user', userToken, user); throw new UserError('Try again'); } diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 26650175d6..9f9961f57b 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -196,6 +196,6 @@ "Negative basis of tickets: 23": "Negative basis of tickets: 23", "Booking completed": "Booking complete", "The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation", - "You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets" -} - + "You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets", + "Try again": "Try again" +} \ No newline at end of file From 67f5be474d44956fd3e7c2f7088cf7845d855559 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 09:21:22 +0100 Subject: [PATCH 65/81] refs #6434 perf: rename db/changes current folder --- db/changes/234603/00-createSignInLogTable.sql | 5 +++-- db/changes/{234202 => 234604}/00-createSignInLogTable.sql | 0 2 files changed, 3 insertions(+), 2 deletions(-) rename db/changes/{234202 => 234604}/00-createSignInLogTable.sql (100%) diff --git a/db/changes/234603/00-createSignInLogTable.sql b/db/changes/234603/00-createSignInLogTable.sql index 977de46463..5253481357 100644 --- a/db/changes/234603/00-createSignInLogTable.sql +++ b/db/changes/234603/00-createSignInLogTable.sql @@ -2,17 +2,18 @@ -- -- Table structure for table `signInLog` +-- Description: log to debug cross-login error -- DROP TABLE IF EXISTS `account`.`signInLog`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `account`.`signInLog` ( - `id` varchar(10) NOT NULL , + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, + `token` varchar(255) NOT NULL , `userFk` int(10) unsigned DEFAULT NULL, `creationDate` timestamp NULL DEFAULT current_timestamp(), `ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, - PRIMARY KEY (`id`), KEY `userFk` (`userFk`), CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ); diff --git a/db/changes/234202/00-createSignInLogTable.sql b/db/changes/234604/00-createSignInLogTable.sql similarity index 100% rename from db/changes/234202/00-createSignInLogTable.sql rename to db/changes/234604/00-createSignInLogTable.sql From 9c6b59442684085d6ab3a519de1afadcb4571fb3 Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 16 Nov 2023 10:04:34 +0100 Subject: [PATCH 66/81] hotFix: comment error logIn --- back/models/vn-user.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/back/models/vn-user.js b/back/models/vn-user.js index 5845c2192c..2ab97fce69 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -131,10 +131,10 @@ module.exports = function(Self) { const userToken = await token.user.get(); - if (userToken.username.toLowerCase() !== user.toLowerCase()) { - console.error('ERROR!!! - Signin with other user', userToken, user); - throw new UserError('Try again'); - } + // if (userToken.username.toLowerCase() !== user.toLowerCase()) { + // console.error('ERROR!!! - Signin with other user', userToken, user); + // throw new UserError('Try again'); + // } try { await Self.app.models.Account.sync(userToken.name, password); From 386b556216ec86e4c91a756b9f2c1e3f835c3c31 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 10:29:57 +0100 Subject: [PATCH 67/81] refs #6434 feat: new test user signIn with email --- back/methods/vn-user/specs/sign-in.spec.js | 10 ++++++++++ back/models/vn-user.js | 13 ++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/back/methods/vn-user/specs/sign-in.spec.js b/back/methods/vn-user/specs/sign-in.spec.js index f4cad88b9c..26abe7eda1 100644 --- a/back/methods/vn-user/specs/sign-in.spec.js +++ b/back/methods/vn-user/specs/sign-in.spec.js @@ -14,6 +14,16 @@ describe('VnUser Sign-in()', () => { }; const {VnUser, AccessToken} = models; describe('when credentials are correct', () => { + it('should return the token if user uses email', async() => { + let login = await VnUser.signIn(unauthCtx, 'salesAssistant@mydomain.com', 'nightmare'); + let accessToken = await AccessToken.findById(login.token); + let ctx = {req: {accessToken: accessToken}}; + + expect(login.token).toBeDefined(); + + await VnUser.logout(ctx.req.accessToken.id); + }); + it('should return the token', async() => { let login = await VnUser.signIn(unauthCtx, 'salesAssistant', 'nightmare'); let accessToken = await AccessToken.findById(login.token); diff --git a/back/models/vn-user.js b/back/models/vn-user.js index 2ab97fce69..bbfee4c515 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -124,17 +124,20 @@ module.exports = function(Self) { return email.send(); }); + Self.signInValidate = (user, userToken) => { + const [[key, value]] = Object.entries(Self.userUses(user)); + if (userToken[key].toLowerCase() !== value.toLowerCase()) { + console.error('ERROR!!! - Signin with other user', _userToken, _user); + throw new UserError('Try again'); + } + }; Self.validateLogin = async function(user, password) { const loginInfo = Object.assign({password}, Self.userUses(user)); const token = await Self.login(loginInfo, 'user'); const userToken = await token.user.get(); - - // if (userToken.username.toLowerCase() !== user.toLowerCase()) { - // console.error('ERROR!!! - Signin with other user', userToken, user); - // throw new UserError('Try again'); - // } + Self.signInValidate(user, userToken); try { await Self.app.models.Account.sync(userToken.name, password); From abf772ae870e238a4df0b42bfb082724ebf76dd1 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 10:43:00 +0100 Subject: [PATCH 68/81] refs #6434 feat: check if signIn inserts signInLog --- back/methods/vn-user/specs/sign-in.spec.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/back/methods/vn-user/specs/sign-in.spec.js b/back/methods/vn-user/specs/sign-in.spec.js index 26abe7eda1..ac2dfe2b23 100644 --- a/back/methods/vn-user/specs/sign-in.spec.js +++ b/back/methods/vn-user/specs/sign-in.spec.js @@ -12,13 +12,16 @@ describe('VnUser Sign-in()', () => { }, args: {} }; - const {VnUser, AccessToken} = models; + const {VnUser, AccessToken, SignInLog} = models; describe('when credentials are correct', () => { it('should return the token if user uses email', async() => { let login = await VnUser.signIn(unauthCtx, 'salesAssistant@mydomain.com', 'nightmare'); let accessToken = await AccessToken.findById(login.token); let ctx = {req: {accessToken: accessToken}}; + let signInLog = await SignInLog.find({where: {token: accessToken.id}}); + expect(signInLog.length).toEqual(1); + expect(signInLog[0].userFk).toEqual(accessToken.userId); expect(login.token).toBeDefined(); await VnUser.logout(ctx.req.accessToken.id); From 9e649398101ffff980ff7471fa375d885e8c5972 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 11:56:48 +0100 Subject: [PATCH 69/81] refs #6434 feat: add Id property in model --- modules/account/back/models/sign_in-log.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/account/back/models/sign_in-log.json b/modules/account/back/models/sign_in-log.json index 9e6df2da57..c5c014e60a 100644 --- a/modules/account/back/models/sign_in-log.json +++ b/modules/account/back/models/sign_in-log.json @@ -7,6 +7,11 @@ } }, "properties": { + "id": { + "type": "number", + "id": true, + "description": "Identifier" + }, "token": { "required": true, "type": "string", From a73453e9ed29736cbb35fd370ad29ae38068d071 Mon Sep 17 00:00:00 2001 From: Javier Segarra Date: Thu, 16 Nov 2023 11:56:59 +0100 Subject: [PATCH 70/81] refs #6434 feat: remove old sql file --- db/changes/234603/00-createSignInLogTable.sql | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 db/changes/234603/00-createSignInLogTable.sql diff --git a/db/changes/234603/00-createSignInLogTable.sql b/db/changes/234603/00-createSignInLogTable.sql deleted file mode 100644 index 5253481357..0000000000 --- a/db/changes/234603/00-createSignInLogTable.sql +++ /dev/null @@ -1,20 +0,0 @@ - - --- --- Table structure for table `signInLog` --- Description: log to debug cross-login error --- - -DROP TABLE IF EXISTS `account`.`signInLog`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `account`.`signInLog` ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - `token` varchar(255) NOT NULL , - `userFk` int(10) unsigned DEFAULT NULL, - `creationDate` timestamp NULL DEFAULT current_timestamp(), - `ip` varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, - KEY `userFk` (`userFk`), - CONSTRAINT `signInLog_ibfk_1` FOREIGN KEY (`userFk`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE -); - From 83ae0379b5feeb7922075fbbba14c7c1406a09de Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 16 Nov 2023 12:28:35 +0100 Subject: [PATCH 71/81] refs #6434 fix: console.log --- back/models/vn-user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/models/vn-user.js b/back/models/vn-user.js index bbfee4c515..e73c04bc00 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -127,7 +127,7 @@ module.exports = function(Self) { Self.signInValidate = (user, userToken) => { const [[key, value]] = Object.entries(Self.userUses(user)); if (userToken[key].toLowerCase() !== value.toLowerCase()) { - console.error('ERROR!!! - Signin with other user', _userToken, _user); + console.error('ERROR!!! - Signin with other user', userToken, user); throw new UserError('Try again'); } }; From aea3f7b2b9bbecaa6f16d0ff241c57a0c766ba32 Mon Sep 17 00:00:00 2001 From: alexm Date: Thu, 16 Nov 2023 15:10:47 +0100 Subject: [PATCH 72/81] refs #6434 fix: use trim --- back/models/vn-user.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/models/vn-user.js b/back/models/vn-user.js index e73c04bc00..719e96cbf7 100644 --- a/back/models/vn-user.js +++ b/back/models/vn-user.js @@ -126,7 +126,7 @@ module.exports = function(Self) { }); Self.signInValidate = (user, userToken) => { const [[key, value]] = Object.entries(Self.userUses(user)); - if (userToken[key].toLowerCase() !== value.toLowerCase()) { + if (userToken[key].toLowerCase().trim() !== value.toLowerCase().trim()) { console.error('ERROR!!! - Signin with other user', userToken, user); throw new UserError('Try again'); } From 4f5242e3ae30d0e9b2ef35d7368ce0aab1052595 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Thu, 16 Nov 2023 23:07:26 +0100 Subject: [PATCH 73/81] fix: refs #6432 account sync fixes --- Dockerfile | 10 +- db/changes/234604/00-sambaConfigUserDn.sql | 2 + modules/account/back/methods/account/sync.js | 8 +- modules/account/back/models/ldap-config.js | 3 +- modules/account/back/models/role-config.js | 20 ++- modules/account/back/models/samba-config.js | 149 +++++++++++------- modules/account/back/models/samba-config.json | 4 + modules/account/front/accounts/index.html | 40 +---- modules/account/front/accounts/index.js | 18 --- modules/account/front/accounts/locale/es.yml | 2 - modules/account/front/descriptor/index.html | 35 ++++ modules/account/front/descriptor/index.js | 14 ++ .../account/front/descriptor/locale/es.yml | 4 + 13 files changed, 186 insertions(+), 123 deletions(-) create mode 100644 db/changes/234604/00-sambaConfigUserDn.sql diff --git a/Dockerfile b/Dockerfile index e1173ad738..814a5e1d81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,15 +17,19 @@ RUN apt-get update \ # Puppeteer -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ +RUN apt-get install -y --no-install-recommends \ libfontconfig lftp xvfb gconf-service libasound2 libatk1.0-0 libc6 \ libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 \ libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 \ libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 \ libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 \ libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \ - fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \ + fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget + +# Extra dependencies + +RUN apt-get install -y --no-install-recommends \ + samba-common-bin \ && rm -rf /var/lib/apt/lists/* \ && npm -g install pm2 diff --git a/db/changes/234604/00-sambaConfigUserDn.sql b/db/changes/234604/00-sambaConfigUserDn.sql new file mode 100644 index 0000000000..cacb30e975 --- /dev/null +++ b/db/changes/234604/00-sambaConfigUserDn.sql @@ -0,0 +1,2 @@ +ALTER TABLE account.sambaConfig + ADD userDn varchar(255) NOT NULL COMMENT 'Base DN for users without domain DN part'; diff --git a/modules/account/back/methods/account/sync.js b/modules/account/back/methods/account/sync.js index a5befc22c5..0eab0ef63a 100644 --- a/modules/account/back/methods/account/sync.js +++ b/modules/account/back/methods/account/sync.js @@ -1,3 +1,4 @@ +const ForbiddenError = require('vn-loopback/util/forbiddenError'); module.exports = Self => { Self.remoteMethod('sync', { @@ -32,9 +33,13 @@ module.exports = Self => { const models = Self.app.models; const user = await models.VnUser.findOne({ - fields: ['id'], + fields: ['id', 'password'], where: {name: userName} }, myOptions); + + if (user && password && !await user.hasPassword(password)) + throw new ForbiddenError('Wrong password'); + const isSync = !await models.UserSync.exists(userName, myOptions); if (!force && isSync && user) return; @@ -42,4 +47,3 @@ module.exports = Self => { await models.UserSync.destroyById(userName, myOptions); }; }; - diff --git a/modules/account/back/models/ldap-config.js b/modules/account/back/models/ldap-config.js index f9ae7562b6..9dcc4136d3 100644 --- a/modules/account/back/models/ldap-config.js +++ b/modules/account/back/models/ldap-config.js @@ -5,7 +5,7 @@ const crypto = require('crypto'); const nthash = require('smbhash').nthash; module.exports = Self => { - const shouldSync = process.env.NODE_ENV === 'production'; + const shouldSync = process.env.NODE_ENV !== 'test'; Self.getSynchronizer = async function() { return await Self.findOne({ @@ -140,6 +140,7 @@ module.exports = Self => { try { if (shouldSync) await client.del(dn); + // eslint-disable-next-line no-console console.log(` -> User '${userName}' removed from LDAP`); } catch (e) { if (e.name !== 'NoSuchObjectError') throw e; diff --git a/modules/account/back/models/role-config.js b/modules/account/back/models/role-config.js index ba7bf9d523..b90ef75fb6 100644 --- a/modules/account/back/models/role-config.js +++ b/modules/account/back/models/role-config.js @@ -27,8 +27,7 @@ module.exports = Self => { const [row] = await Self.rawSql( `SELECT COUNT(*) AS nRows FROM mysql.user - WHERE User = ? - AND Host = ?`, + WHERE User = ? AND Host = ?`, [mysqlUser, this.userHost] ); let userExists = row.nRows > 0; @@ -38,8 +37,7 @@ module.exports = Self => { const [row] = await Self.rawSql( `SELECT Priv AS priv FROM mysql.global_priv - WHERE User = ? - AND Host = ?`, + WHERE User = ? AND Host = ?`, [mysqlUser, this.userHost] ); const priv = row && JSON.parse(row.priv); @@ -88,10 +86,18 @@ module.exports = Self => { else throw err; } - await Self.rawSql('GRANT ? TO ?@?', - [role, mysqlUser, this.userHost]); - if (role) { + const [row] = await Self.rawSql( + `SELECT COUNT(*) AS nRows + FROM mysql.user + WHERE User = ? AND Host = ''`, + [role] + ); + const roleExists = row.nRows > 0; + + if (roleExists) { + await Self.rawSql('GRANT ? TO ?@?', + [role, mysqlUser, this.userHost]); await Self.rawSql('SET DEFAULT ROLE ? FOR ?@?', [role, mysqlUser, this.userHost]); } else { diff --git a/modules/account/back/models/samba-config.js b/modules/account/back/models/samba-config.js index 168b5ffb47..1abac489c8 100644 --- a/modules/account/back/models/samba-config.js +++ b/modules/account/back/models/samba-config.js @@ -1,6 +1,6 @@ const ldap = require('../util/ldapjs-extra'); -const ssh = require('node-ssh'); +const execFile = require('child_process').execFile; /** * Summary of userAccountControl flags: @@ -11,6 +11,8 @@ const UserAccountControlFlags = { }; module.exports = Self => { + const shouldSync = process.env.NODE_ENV !== 'test'; + Self.getSynchronizer = async function() { return await Self.findOne({ fields: [ @@ -19,6 +21,7 @@ module.exports = Self => { 'adController', 'adUser', 'adPassword', + 'userDn', 'verifyCert' ] }); @@ -26,88 +29,119 @@ module.exports = Self => { Object.assign(Self.prototype, { async init() { - let sshClient = new ssh.NodeSSH(); - await sshClient.connect({ - host: this.adController, - username: this.adUser, - password: this.adPassword - }); + const baseDn = this.adDomain + .split('.') + .map(part => `dc=${part}`) + .join(','); + const ldapUser = `cn=${this.adUser},cn=Users,${baseDn}`; - let adUser = `cn=${this.adUser},${this.usersDn()}`; - - let adClient = ldap.createClient({ + const adClient = ldap.createClient({ url: `ldaps://${this.adController}:636`, tlsOptions: {rejectUnauthorized: this.verifyCert} }); - await adClient.bind(adUser, this.adPassword); - + await adClient.bind(ldapUser, this.adPassword); Object.assign(this, { - sshClient, - adClient + adClient, + fullUsersDn: `${this.userDn},${baseDn}` }); }, async deinit() { - await this.sshClient.dispose(); await this.adClient.unbind(); }, - usersDn() { - let dnBase = this.adDomain - .split('.') - .map(part => `dc=${part}`) - .join(','); - return `cn=Users,${dnBase}`; + async sambaTool(command, args = []) { + const authArgs = [ + '--URL', `ldap://${this.adController}`, + '--username', this.adUser, + '--password', this.adPassword + ]; + const allArgs = [command].concat( + args, authArgs + ); + + if (!shouldSync) return; + return await new Promise((resolve, reject) => { + execFile('samba-tool', allArgs, (err, stdout, stderr) => { + if (err) + reject(err); + else + resolve({stdout, stderr}); + }); + }); }, - async syncUser(userName, info, password) { - let {sshClient} = this; - - let sambaUser = await this.adClient.searchOne(this.usersDn(), { + async getAdUser(userName) { + const sambaUser = await this.adClient.searchOne(this.fullUsersDn, { scope: 'sub', - attributes: ['userAccountControl'], + attributes: [ + 'dn', + 'userAccountControl', + 'uidNumber', + 'accountExpires', + 'mail' + ], filter: `(&(objectClass=user)(sAMAccountName=${userName}))` }); - let isEnabled = sambaUser - && !(sambaUser.userAccountControl & UserAccountControlFlags.ACCOUNTDISABLE); - - if (process.env.NODE_ENV === 'test') - return; + if (sambaUser) { + for (const intProp of ['uidNumber', 'userAccountControl']) { + if (sambaUser[intProp] != null) + sambaUser[intProp] = parseInt(sambaUser[intProp]); + } + } + return sambaUser; + }, + + async syncUser(userName, info, password) { + let sambaUser = await this.getAdUser(userName); + let entry; if (info.hasAccount) { if (!sambaUser) { - await sshClient.exec('samba-tool user create', [ - userName, - '--uid-number', `${info.uidNumber}`, - '--mail-address', info.corporateMail, + await this.sambaTool('user', [ + 'create', userName, + '--userou', this.userDn, '--random-password' ]); - await sshClient.exec('samba-tool user setexpiry', [ - userName, - '--noexpiry' - ]); - await sshClient.exec('mkhomedir_helper', [ - userName, - '0027' - ]); - } - if (!isEnabled) { - await sshClient.exec('samba-tool user enable', [ - userName - ]); + sambaUser = await this.getAdUser(userName); } if (password) { - await sshClient.exec('samba-tool user setpassword', [ - userName, + await this.sambaTool('user', [ + 'setpassword', userName, '--newpassword', password ]); } - } else if (isEnabled) { - await sshClient.exec('samba-tool user disable', [ - userName - ]); + + entry = { + userAccountControl: sambaUser.userAccountControl + & ~UserAccountControlFlags.ACCOUNTDISABLE, + uidNumber: info.uidNumber, + accountExpires: 0, + mail: info.corporateMail + }; + } else if (sambaUser) { + entry = { + userAccountControl: sambaUser.userAccountControl + | UserAccountControlFlags.ACCOUNTDISABLE + }; + // eslint-disable-next-line no-console console.log(` -> User '${userName}' disabled on Samba`); } + + if (sambaUser && entry) { + const changes = []; + for (const prop in entry) { + if (sambaUser[prop] == entry[prop]) continue; + changes.push(new ldap.Change({ + operation: 'replace', + modification: { + [prop]: entry[prop] + } + })); + } + if (changes.length) + await this.adClient.modify(sambaUser.dn, changes); + } }, /** @@ -117,14 +151,15 @@ module.exports = Self => { */ async getUsers(usersToSync) { const LDAP_MATCHING_RULE_BIT_AND = '1.2.840.113556.1.4.803'; - let filter = `!(userAccountControl:${LDAP_MATCHING_RULE_BIT_AND}:=${UserAccountControlFlags.ACCOUNTDISABLE})`; + // eslint-disable-next-line max-len + const filter = `!(userAccountControl:${LDAP_MATCHING_RULE_BIT_AND}:=${UserAccountControlFlags.ACCOUNTDISABLE})`; - let opts = { + const opts = { scope: 'sub', attributes: ['sAMAccountName'], filter: `(&(objectClass=user)(${filter}))` }; - await this.adClient.searchForeach(this.usersDn(), opts, + await this.adClient.searchForeach(this.fullUsersDn, opts, o => usersToSync.add(o.sAMAccountName)); } }); diff --git a/modules/account/back/models/samba-config.json b/modules/account/back/models/samba-config.json index 732c9b071c..28cbb26890 100644 --- a/modules/account/back/models/samba-config.json +++ b/modules/account/back/models/samba-config.json @@ -28,6 +28,10 @@ "adPassword": { "type": "string" }, + "userDn": { + "type": "string", + "required": true + }, "verifyCert": { "type": "boolean" } diff --git a/modules/account/front/accounts/index.html b/modules/account/front/accounts/index.html index 6941bb15b5..6847e68d11 100644 --- a/modules/account/front/accounts/index.html +++ b/modules/account/front/accounts/index.html @@ -12,40 +12,40 @@ @@ -61,10 +61,6 @@ label="Synchronize all" ng-click="$ctrl.onSynchronizeAll()"> - - @@ -77,25 +73,3 @@ - - - - - - - - - - - - diff --git a/modules/account/front/accounts/index.js b/modules/account/front/accounts/index.js index 0e78ab8d6d..ab19126a1b 100644 --- a/modules/account/front/accounts/index.js +++ b/modules/account/front/accounts/index.js @@ -1,6 +1,5 @@ import ngModule from '../module'; import Section from 'salix/components/section'; -import UserError from 'core/lib/user-error'; export default class Controller extends Section { onSynchronizeAll() { @@ -8,27 +7,10 @@ export default class Controller extends Section { this.$http.patch(`Accounts/syncAll`); } - onUserSync() { - if (!this.syncUser) - throw new UserError('Please enter the username'); - - let params = { - password: this.syncPassword, - force: true - }; - return this.$http.patch(`Accounts/${this.syncUser}/sync`, params) - .then(() => this.vnApp.showSuccess(this.$t('User synchronized!'))); - } - onSynchronizeRoles() { this.$http.patch(`RoleInherits/sync`) .then(() => this.vnApp.showSuccess(this.$t('Roles synchronized!'))); } - - onSyncClose() { - this.syncUser = ''; - this.syncPassword = ''; - } } ngModule.component('vnAccountAccounts', { diff --git a/modules/account/front/accounts/locale/es.yml b/modules/account/front/accounts/locale/es.yml index 9a6bb50737..614ade3eb0 100644 --- a/modules/account/front/accounts/locale/es.yml +++ b/modules/account/front/accounts/locale/es.yml @@ -3,7 +3,6 @@ Homedir base: Directorio base para carpetas de usuario Shell: Intérprete de línea de comandos User and role base id: Id base usuarios y roles Synchronize all: Sincronizar todo -Synchronize user: Sincronizar usuario Synchronize roles: Sincronizar roles If password is not specified, just user attributes are synchronized: >- Si la contraseña no se especifica solo se sincronizarán lo atributos del usuario @@ -12,5 +11,4 @@ Users synchronized!: ¡Usuarios sincronizados! Username: Nombre de usuario Synchronize: Sincronizar Please enter the username: Por favor introduce el nombre de usuario -User synchronized!: ¡Usuario sincronizado! Roles synchronized!: ¡Roles sincronizados! diff --git a/modules/account/front/descriptor/index.html b/modules/account/front/descriptor/index.html index 94497aaa93..b0a70edd11 100644 --- a/modules/account/front/descriptor/index.html +++ b/modules/account/front/descriptor/index.html @@ -67,6 +67,15 @@ translate> Deactivate user + + Synchronize +
@@ -153,6 +162,32 @@ + + + Do you want to synchronize user? + + + + + + + + + + + + diff --git a/modules/account/front/descriptor/index.js b/modules/account/front/descriptor/index.js index 786870d366..c327b0836c 100644 --- a/modules/account/front/descriptor/index.js +++ b/modules/account/front/descriptor/index.js @@ -120,6 +120,20 @@ class Controller extends Descriptor { this.vnApp.showSuccess(this.$t(message)); }); } + + onSync() { + let params = { + password: this.syncPassword, + force: true + }; + return this.$http.patch(`Accounts/${this.user.name}/sync`, params) + .then(() => this.vnApp.showSuccess(this.$t('User synchronized!'))); + } + + onSyncClose() { + this.shouldSyncPassword = false; + this.syncPassword = undefined; + } } ngModule.component('vnUserDescriptor', { diff --git a/modules/account/front/descriptor/locale/es.yml b/modules/account/front/descriptor/locale/es.yml index 5e82428193..98ced7694a 100644 --- a/modules/account/front/descriptor/locale/es.yml +++ b/modules/account/front/descriptor/locale/es.yml @@ -22,6 +22,10 @@ Old password: Contraseña antigua New password: Nueva contraseña Repeat password: Repetir contraseña Password changed succesfully!: ¡Contraseña modificada correctamente! +Synchronize: Sincronizar +Do you want to synchronize user?: ¿Quieres sincronizar el usuario? +Synchronize password: Sincronizar contraseña +User synchronized!: ¡Usuario sincronizado! Role changed succesfully!: ¡Rol modificado correctamente! Password requirements: > La contraseña debe tener al menos {{ length }} caracteres de longitud, From ea5a881d1f4f35473e3984d161908fd3029f9000 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Thu, 16 Nov 2023 23:10:30 +0100 Subject: [PATCH 74/81] fix: refs #6432 account sync fixes --- modules/account/back/models/samba-config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/account/back/models/samba-config.js b/modules/account/back/models/samba-config.js index 1abac489c8..b6692be023 100644 --- a/modules/account/back/models/samba-config.js +++ b/modules/account/back/models/samba-config.js @@ -139,7 +139,7 @@ module.exports = Self => { } })); } - if (changes.length) + if (changes.length && shouldSync) await this.adClient.modify(sambaUser.dn, changes); } }, From 6930c2d52e1e068e1437ed89f511e9d1b8d616d1 Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Thu, 16 Nov 2023 23:13:16 +0100 Subject: [PATCH 75/81] fix: #6432 account sync fixes --- Dockerfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 814a5e1d81..d46a1a84ce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,7 +17,8 @@ RUN apt-get update \ # Puppeteer -RUN apt-get install -y --no-install-recommends \ +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ libfontconfig lftp xvfb gconf-service libasound2 libatk1.0-0 libc6 \ libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 \ libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 \ @@ -28,7 +29,8 @@ RUN apt-get install -y --no-install-recommends \ # Extra dependencies -RUN apt-get install -y --no-install-recommends \ +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ samba-common-bin \ && rm -rf /var/lib/apt/lists/* \ && npm -g install pm2 From 14eb14ad3f2540fd89fc4172e85d90f3b34bf8ae Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Fri, 17 Nov 2023 00:00:36 +0100 Subject: [PATCH 76/81] fix: refs #6432 bookworm upgrade --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d46a1a84ce..ce63da8ab7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bullseye-slim +FROM debian:bookworm-slim ENV TZ Europe/Madrid ARG DEBIAN_FRONTEND=noninteractive From c2e295f777026ae621864b1d31936a228997b2ce Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Fri, 17 Nov 2023 00:08:29 +0100 Subject: [PATCH 77/81] fix: refs #6432 account sync fixes --- modules/account/front/descriptor/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/account/front/descriptor/index.js b/modules/account/front/descriptor/index.js index c327b0836c..18d93b9240 100644 --- a/modules/account/front/descriptor/index.js +++ b/modules/account/front/descriptor/index.js @@ -122,10 +122,10 @@ class Controller extends Descriptor { } onSync() { - let params = { - password: this.syncPassword, - force: true - }; + const params = {force: true}; + if (this.shouldSyncPassword) + params.password = this.syncPassword; + return this.$http.patch(`Accounts/${this.user.name}/sync`, params) .then(() => this.vnApp.showSuccess(this.$t('User synchronized!'))); } From bb6f3b4cfde1f3bf7608384d8987371c39bcb9ee Mon Sep 17 00:00:00 2001 From: Juan Ferrer Toribio Date: Fri, 17 Nov 2023 08:56:25 +0100 Subject: [PATCH 78/81] fix: refs #6432 account sync fixes --- Dockerfile | 2 +- modules/account/back/models/samba-config.js | 20 ++++++++++++-------- modules/account/front/samba/index.html | 19 ++++++++++++------- modules/account/front/samba/locale/es.yml | 1 + 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/Dockerfile b/Dockerfile index ce63da8ab7..61dd758b8b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ RUN apt-get update \ RUN apt-get update \ && apt-get install -y --no-install-recommends \ - samba-common-bin \ + samba-common-bin samba-dsdb-modules\ && rm -rf /var/lib/apt/lists/* \ && npm -g install pm2 diff --git a/modules/account/back/models/samba-config.js b/modules/account/back/models/samba-config.js index b6692be023..7714fb01cc 100644 --- a/modules/account/back/models/samba-config.js +++ b/modules/account/back/models/samba-config.js @@ -33,16 +33,17 @@ module.exports = Self => { .split('.') .map(part => `dc=${part}`) .join(','); - const ldapUser = `cn=${this.adUser},cn=Users,${baseDn}`; + const bindDn = `cn=${this.adUser},cn=Users,${baseDn}`; const adClient = ldap.createClient({ url: `ldaps://${this.adController}:636`, tlsOptions: {rejectUnauthorized: this.verifyCert} }); - await adClient.bind(ldapUser, this.adPassword); + await adClient.bind(bindDn, this.adPassword); Object.assign(this, { adClient, - fullUsersDn: `${this.userDn},${baseDn}` + fullUsersDn: `${this.userDn},${baseDn}`, + bindDn }); }, @@ -51,11 +52,14 @@ module.exports = Self => { }, async sambaTool(command, args = []) { - const authArgs = [ - '--URL', `ldap://${this.adController}`, - '--username', this.adUser, + let authArgs = [ + '--URL', `ldaps://${this.adController}`, + '--simple-bind-dn', this.bindDn, '--password', this.adPassword ]; + if (!this.verifyCert) + authArgs.push('--option', 'tls verify peer = no_check'); + const allArgs = [command].concat( args, authArgs ); @@ -151,8 +155,8 @@ module.exports = Self => { */ async getUsers(usersToSync) { const LDAP_MATCHING_RULE_BIT_AND = '1.2.840.113556.1.4.803'; - // eslint-disable-next-line max-len - const filter = `!(userAccountControl:${LDAP_MATCHING_RULE_BIT_AND}:=${UserAccountControlFlags.ACCOUNTDISABLE})`; + const filter = `!(userAccountControl:${LDAP_MATCHING_RULE_BIT_AND}` + + `:=${UserAccountControlFlags.ACCOUNTDISABLE})`; const opts = { scope: 'sub', diff --git a/modules/account/front/samba/index.html b/modules/account/front/samba/index.html index 4379f10a28..0186cac7c8 100644 --- a/modules/account/front/samba/index.html +++ b/modules/account/front/samba/index.html @@ -12,7 +12,7 @@ @@ -20,28 +20,33 @@ ng-if="watcher.hasData" class="vn-mt-md"> + + @@ -63,4 +68,4 @@ ng-click="watcher.loadOriginalData()"> - \ No newline at end of file + diff --git a/modules/account/front/samba/locale/es.yml b/modules/account/front/samba/locale/es.yml index d098a4fbec..efa3b15979 100644 --- a/modules/account/front/samba/locale/es.yml +++ b/modules/account/front/samba/locale/es.yml @@ -3,6 +3,7 @@ Domain controller: Controlador de dominio AD domain: Dominio AD AD user: Usuario AD AD password: Contraseña AD +User DN (without domain part): DN usuarios (sin la parte del dominio) Verify certificate: Verificar certificado Test connection: Probar conexión Samba connection established!: ¡Conexión con Samba establecida! From dc100f795989f449c60344040ec96eecb715963c Mon Sep 17 00:00:00 2001 From: pablone Date: Fri, 17 Nov 2023 11:29:57 +0100 Subject: [PATCH 79/81] fix(filter): refs #6461 now is mockDate in local --- modules/invoiceOut/front/negative-bases/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/invoiceOut/front/negative-bases/index.js b/modules/invoiceOut/front/negative-bases/index.js index f60668b200..7ce6105135 100644 --- a/modules/invoiceOut/front/negative-bases/index.js +++ b/modules/invoiceOut/front/negative-bases/index.js @@ -7,7 +7,7 @@ export default class Controller extends Section { super($element, $); this.vnReport = vnReport; - const now = new Date(); + const now = Date.vnNew(); const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1); const lastDayOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0); this.params = { From fc653746f0d5e6e0769b1ffa335ca447549f96a8 Mon Sep 17 00:00:00 2001 From: pablone Date: Tue, 21 Nov 2023 07:54:25 +0100 Subject: [PATCH 80/81] remove(barcode): refs #5228 remove barcode from report --- .../expedition-pallet-label/assets/css/style.css | 3 --- .../expedition-pallet-label.html | 1 - .../expedition-pallet-label.js | 14 -------------- 3 files changed, 18 deletions(-) diff --git a/print/templates/reports/expedition-pallet-label/assets/css/style.css b/print/templates/reports/expedition-pallet-label/assets/css/style.css index bd73664869..178ce79d2d 100644 --- a/print/templates/reports/expedition-pallet-label/assets/css/style.css +++ b/print/templates/reports/expedition-pallet-label/assets/css/style.css @@ -56,9 +56,6 @@ html { margin-left: 35px; float:left; } -#barcode{ - text-align: center; -} #right { float: right; margin-top: 20px; diff --git a/print/templates/reports/expedition-pallet-label/expedition-pallet-label.html b/print/templates/reports/expedition-pallet-label/expedition-pallet-label.html index 0cb25613e6..a34755f911 100644 --- a/print/templates/reports/expedition-pallet-label/expedition-pallet-label.html +++ b/print/templates/reports/expedition-pallet-label/expedition-pallet-label.html @@ -9,7 +9,6 @@ -
diff --git a/print/templates/reports/expedition-pallet-label/expedition-pallet-label.js b/print/templates/reports/expedition-pallet-label/expedition-pallet-label.js index bcd90418f3..00f1f8ba11 100644 --- a/print/templates/reports/expedition-pallet-label/expedition-pallet-label.js +++ b/print/templates/reports/expedition-pallet-label/expedition-pallet-label.js @@ -39,19 +39,5 @@ module.exports = { const data = String(id); return qrcode.toDataURL(data, {margin: 0}); }, - getBarcode(id) { - const xmlSerializer = new XMLSerializer(); - const document = new DOMImplementation().createDocument('http://www.w3.org/1999/xhtml', 'html', null); - const svgNode = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); - - jsBarcode(svgNode, id, { - xmlDocument: document, - format: 'code128', - displayValue: false, - width: 6, - height: 90, - }); - return xmlSerializer.serializeToString(svgNode); - }, }, }; From dd11cf53273e6f52e0d27c37afe2ed61a23adcb5 Mon Sep 17 00:00:00 2001 From: jorgep Date: Tue, 21 Nov 2023 09:03:25 +0100 Subject: [PATCH 81/81] ref #5835 fix test --- .../back/methods/invoice-in-due-day/specs/new.spec.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/invoiceIn/back/methods/invoice-in-due-day/specs/new.spec.js b/modules/invoiceIn/back/methods/invoice-in-due-day/specs/new.spec.js index f2f04b993e..c188a511da 100644 --- a/modules/invoiceIn/back/methods/invoice-in-due-day/specs/new.spec.js +++ b/modules/invoiceIn/back/methods/invoice-in-due-day/specs/new.spec.js @@ -5,11 +5,6 @@ describe('invoiceInDueDay new()', () => { beforeAll(async() => { const activeCtx = { accessToken: {userId: 9}, - http: { - req: { - headers: {origin: 'http://localhost'} - } - } }; spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ active: activeCtx @@ -24,7 +19,6 @@ describe('invoiceInDueDay new()', () => { req: { accessToken: {userId: userId}, - headers: {origin: 'http://localhost:5000'}, } };