diff --git a/back/methods/account/aclFunc.js b/back/methods/account/aclFunc.js deleted file mode 100644 index 47dcd6f7a..000000000 --- a/back/methods/account/aclFunc.js +++ /dev/null @@ -1,33 +0,0 @@ -module.exports = Self => { - Self.remoteMethodCtx('aclFunc', { - description: 'Get the user information and permissions', - accepts: [ - { - arg: 'property', - type: 'String', - description: 'The user name or email', - required: true - } - ], - returns: { - type: 'Object', - root: true - }, - http: { - path: `/aclFunc`, - verb: 'GET' - } - }); - - Self.aclFunc = async function(ctx, property) { - const userId = ctx.req.accessToken.userId; - const models = Self.app.models; - - const [acl] = await Self.rawSql( - `SELECT a.principalId - FROM salix.ACL a - WHERE a.property = ?`, [property]); - - return await models.Account.hasRole(userId, acl.principalId); - }; -}; diff --git a/back/methods/account/funcionalityAcl.js b/back/methods/account/funcionalityAcl.js new file mode 100644 index 000000000..3a5e09720 --- /dev/null +++ b/back/methods/account/funcionalityAcl.js @@ -0,0 +1,40 @@ +module.exports = Self => { + Self.remoteMethod('funcionalityAcl', { + description: 'Return if user has permissions', + accepts: [ + { + arg: 'model', + type: 'String', + description: 'The model', + required: true + }, + { + arg: 'property', + type: 'String', + description: 'The property', + required: true + } + ], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/funcionalityAcl`, + verb: 'GET' + } + }); + + Self.funcionalityAcl = async function(ctx, model, property) { + const userId = ctx.req.accessToken.userId; + const models = Self.app.models; + + const [acl] = await Self.rawSql( + `SELECT f.role + FROM salix.funcionalityAcl f + WHERE f.model = ? + AND f.property = ?`, [model, property]); + + return await models.Account.hasRole(userId, acl.role); + }; +}; diff --git a/back/models/funcionalityAcl.json b/back/models/funcionalityAcl.json new file mode 100644 index 000000000..840c4f6f3 --- /dev/null +++ b/back/models/funcionalityAcl.json @@ -0,0 +1,24 @@ +{ + "name": "FuncionalityAcl", + "base": "VnModel", + "options": { + "mysql": { + "table": "salix.funcionalityAcl" + } + }, + "properties": { + "id": { + "type": "number", + "id": true + }, + "model": { + "type": "string" + }, + "property": { + "type": "string" + }, + "role": { + "type": "string" + } + } +} diff --git a/db/changes/10490-august/00-funcionalityAcl.sql b/db/changes/10490-august/00-funcionalityAcl.sql new file mode 100644 index 000000000..889a92c48 --- /dev/null +++ b/db/changes/10490-august/00-funcionalityAcl.sql @@ -0,0 +1,7 @@ +CREATE TABLE `funcionalityAcl` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `model` varchar(255) COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `property` varchar(255) COLLATE utf8mb3_unicode_ci DEFAULT NULL, + `role` varchar(45) COLLATE utf8mb3_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB AUTO_INCREMENT=65 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index a09755479..cde9345ef 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -14,10 +14,10 @@ INSERT INTO `salix`.`AccessToken` (`id`, `ttl`, `created`, `userId`) ('DEFAULT_TOKEN', '1209600', util.VN_CURDATE(), 66); INSERT INTO `salix`.`printConfig` (`id`, `itRecipient`, `incidencesEmail`) - VALUES + VALUES (1, 'it@gotamcity.com', 'incidences@gotamcity.com'); -INSERT INTO `vn`.`ticketConfig` (`id`, `scopeDays`) +INSERT INTO `vn`.`ticketConfig` (`id`, `scopeDays`) VALUES ('1', '6'); @@ -1146,10 +1146,11 @@ INSERT INTO `vncontrol`.`accion`(`accion_id`, `accion`) INSERT INTO `vn`.`saleTracking`(`saleFk`, `isChecked`, `created`, `originalQuantity`, `workerFk`, `actionFk`, `id`, `stateFk`) VALUES - (1, 0, util.VN_CURDATE(), 5, 55, 3, 1, 14), - (1, 1, util.VN_CURDATE(), 5, 54, 3, 2, 8), - (2, 1, util.VN_CURDATE(), 10, 40, 4, 3, 8), - (3, 1, util.VN_CURDATE(), 2, 40, 4, 4, 8); + (1, 0, util.VN_CURDATE(), 5, 55, 3, 1, 14), + (1, 1, util.VN_CURDATE(), 5, 54, 3, 2, 8), + (2, 1, util.VN_CURDATE(), 10, 40, 4, 3, 8), + (3, 1, util.VN_CURDATE(), 2, 40, 4, 4, 8), + (31, 1, util.VN_CURDATE(), -5, 40, 4, 5, 8); INSERT INTO `vn`.`itemBarcode`(`id`, `itemFk`, `code`) VALUES @@ -2664,8 +2665,11 @@ INSERT INTO `vn`.`ticketCollection` (`ticketFk`, `collectionFk`, `created`, `lev VALUES (9, 3, util.VN_NOW(), NULL, 0, NULL, NULL, NULL, NULL); -INSERT INTO salix.ACL -(model, property, accessType, permission, principalType, principalId) -VALUES -('Sale', 'editTracked', 'WRITE', 'ALLOW', 'ROLE', 'production'), -('Sale', 'editCloned', 'WRITE', 'ALLOW', 'ROLE', 'production'); +INSERT INTO `salix`.`funcionalityAcl` (`model`, `property`, `role`) + VALUES + ('Sale', 'editTracked', 'production'), + ('Sale', 'editCloned', 'production'); + +INSERT INTO `vn`.`saleCloned` (`saleClonedFk`, `saleOriginalFk`) + VALUES + ('26', '25'); diff --git a/modules/ticket/back/methods/sale/canEdit.js b/modules/ticket/back/methods/sale/canEdit.js index 94c2fc19a..1b9e471ef 100644 --- a/modules/ticket/back/methods/sale/canEdit.js +++ b/modules/ticket/back/methods/sale/canEdit.js @@ -37,8 +37,8 @@ module.exports = Self => { const saleCloned = await models.SaleCloned.find({where: {saleClonedFk: {inq: sales}}}, myOptions); const hasSaleCloned = saleCloned.length; - const canEditTracked = await models.Account.aclFunc(ctx, 'editTracked'); - const canEditCloned = await models.Account.aclFunc(ctx, 'editCloned'); + const canEditTracked = await models.Account.funcionalityAcl(ctx, 'Sale', 'editTracked'); + const canEditCloned = await models.Account.funcionalityAcl(ctx, 'Sale', 'editCloned'); const canEdit = (canEditTracked || !hasSaleTracking) && (canEditCloned || !hasSaleCloned); diff --git a/modules/ticket/back/methods/sale/specs/canEdit.spec.js b/modules/ticket/back/methods/sale/specs/canEdit.spec.js index 667ce98da..9533d6464 100644 --- a/modules/ticket/back/methods/sale/specs/canEdit.spec.js +++ b/modules/ticket/back/methods/sale/specs/canEdit.spec.js @@ -10,7 +10,7 @@ describe('sale canEdit()', () => { const productionUserID = 49; const ctx = {req: {accessToken: {userId: productionUserID}}}; - const sales = [3]; + const sales = [25]; const result = await models.Sale.canEdit(ctx, sales, options); @@ -51,10 +51,10 @@ describe('sale canEdit()', () => { try { const options = {transaction: tx}; - const salesPersonUserID = 18; - const ctx = {req: {accessToken: {userId: salesPersonUserID}}}; + const buyerId = 35; + const ctx = {req: {accessToken: {userId: buyerId}}}; - const sales = [3]; + const sales = [31]; const result = await models.Sale.canEdit(ctx, sales, options); @@ -66,4 +66,48 @@ describe('sale canEdit()', () => { throw e; } }); + + it('should return false if any of the sales is cloned', async() => { + const tx = await models.Sale.beginTransaction({}); + + try { + const options = {transaction: tx}; + + const buyerId = 35; + const ctx = {req: {accessToken: {userId: buyerId}}}; + + const sales = [26]; + + const result = await models.Sale.canEdit(ctx, sales, options); + + expect(result).toEqual(false); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should return true if any of the sales is cloned and has the correct role', async() => { + const tx = await models.Sale.beginTransaction({}); + // modify? + try { + const options = {transaction: tx}; + + const productionId = 49; + const ctx = {req: {accessToken: {userId: productionId}}}; + + const sales = [26]; + + const result = await models.Sale.canEdit(ctx, sales, options); + + expect(result).toEqual(true); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); }); diff --git a/modules/ticket/back/methods/sale/specs/updateConcept.spec.js b/modules/ticket/back/methods/sale/specs/updateConcept.spec.js index 01f610d36..0e7e9bf0f 100644 --- a/modules/ticket/back/methods/sale/specs/updateConcept.spec.js +++ b/modules/ticket/back/methods/sale/specs/updateConcept.spec.js @@ -2,7 +2,7 @@ const models = require('vn-loopback/server/server').models; describe('sale updateConcept()', () => { const ctx = {req: {accessToken: {userId: 9}}}; - const saleId = 1; + const saleId = 25; it('should throw if ID was undefined', async() => { const tx = await models.Sale.beginTransaction({}); diff --git a/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js b/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js index 4c961faab..bf139ab11 100644 --- a/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js +++ b/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js @@ -28,13 +28,20 @@ describe('sale updateQuantity()', () => { }); it('should throw an error if the quantity is greater than it should be', async() => { + const ctx = { + req: { + accessToken: {userId: 1}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const tx = await models.Sale.beginTransaction({}); let error; try { const options = {transaction: tx}; - await models.Sale.updateQuantity(ctx, 1, 99, options); + await models.Sale.updateQuantity(ctx, 17, 99, options); await tx.rollback(); } catch (e) { @@ -45,21 +52,60 @@ describe('sale updateQuantity()', () => { expect(error).toEqual(new Error('The new quantity should be smaller than the old one')); }); - it('should update the quantity of a given sale current line', async() => { + it('should add quantity if the quantity is greater than it should be and is role advanced', async() => { const tx = await models.Sale.beginTransaction({}); + const saleId = 17; + const buyerId = 35; + const ctx = { + req: { + accessToken: {userId: buyerId}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; try { const options = {transaction: tx}; - const originalLine = await models.Sale.findOne({where: {id: 1}, fields: ['quantity']}, options); + const isRoleAdvanced = await models.Ticket.isRoleAdvanced(ctx, options); - expect(originalLine.quantity).toEqual(5); + expect(isRoleAdvanced).toEqual(true); - await models.Sale.updateQuantity(ctx, 1, 4, options); + const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); - const modifiedLine = await models.Sale.findOne({where: {id: 1}, fields: ['quantity']}, options); + expect(originalLine.quantity).toEqual(30); - expect(modifiedLine.quantity).toEqual(4); + const newQuantity = originalLine.quantity + 1; + await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + + const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + + expect(modifiedLine.quantity).toEqual(newQuantity); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); + + it('should update the quantity of a given sale current line', async() => { + const tx = await models.Sale.beginTransaction({}); + const saleId = 25; + const newQuantity = 4; + + try { + const options = {transaction: tx}; + + const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + + expect(originalLine.quantity).toEqual(20); + + await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + + const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + + expect(modifiedLine.quantity).toEqual(newQuantity); await tx.rollback(); } catch (e) { diff --git a/modules/ticket/back/methods/sale/updateQuantity.js b/modules/ticket/back/methods/sale/updateQuantity.js index 24e3736f3..f3bc0ca38 100644 --- a/modules/ticket/back/methods/sale/updateQuantity.js +++ b/modules/ticket/back/methods/sale/updateQuantity.js @@ -41,14 +41,13 @@ module.exports = Self => { } try { - const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions); - - if (!canEditSale) - throw new UserError(`Sale(s) blocked, please contact production`); - if (isNaN(newQuantity)) throw new UserError(`The value should be a number`); + const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions); + if (!canEditSale) + throw new UserError(`Sale(s) blocked, please contact production`); + const filter = { include: { relation: 'ticket', @@ -70,7 +69,8 @@ module.exports = Self => { const sale = await models.Sale.findById(id, filter, myOptions); - if (newQuantity > sale.quantity) + const isRoleAdvanced = await models.Ticket.isRoleAdvanced(ctx, myOptions); + if (newQuantity > sale.quantity && !isRoleAdvanced) throw new UserError('The new quantity should be smaller than the old one'); const oldQuantity = sale.quantity; diff --git a/modules/ticket/back/models/ticket-methods.js b/modules/ticket/back/models/ticket-methods.js index 9255e52e6..8ab1845d9 100644 --- a/modules/ticket/back/models/ticket-methods.js +++ b/modules/ticket/back/models/ticket-methods.js @@ -33,4 +33,5 @@ module.exports = function(Self) { require('../methods/ticket/closeByTicket')(Self); require('../methods/ticket/closeByAgency')(Self); require('../methods/ticket/closeByRoute')(Self); + require('../methods/ticket/isRoleAdvanced')(Self); };