From 77d0dcae0de7cc7ad26c88824a446cb5cc315ba3 Mon Sep 17 00:00:00 2001 From: alexm Date: Wed, 2 Feb 2022 08:28:07 +0100 Subject: [PATCH 1/9] feat(setQuantitySale): back route and test --- back/methods/collection/setQuantitySale.js | 33 +++++++ .../collection/spec/setQuantitySale.spec.js | 15 ++++ .../spec/updateCollectionSale.spec.js | 62 ------------- .../collection/updateCollectionSale.js | 90 ------------------- back/models/collection.js | 2 +- 5 files changed, 49 insertions(+), 153 deletions(-) create mode 100644 back/methods/collection/setQuantitySale.js create mode 100644 back/methods/collection/spec/setQuantitySale.spec.js delete mode 100644 back/methods/collection/spec/updateCollectionSale.spec.js delete mode 100644 back/methods/collection/updateCollectionSale.js diff --git a/back/methods/collection/setQuantitySale.js b/back/methods/collection/setQuantitySale.js new file mode 100644 index 0000000000..f9448f46aa --- /dev/null +++ b/back/methods/collection/setQuantitySale.js @@ -0,0 +1,33 @@ +module.exports = Self => { + Self.remoteMethodCtx('setQuantitySale', { + description: 'Update sale quantity', + accessType: 'WRITE', + accepts: [{ + arg: 'sale', + type: 'Number', + required: true, + description: 'The sale id' + }, + { + arg: 'quantity', + type: 'Number', + required: true, + description: 'The quantity to picked' + }], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/setQuantitySale`, + verb: 'POST' + } + }); + + Self.setQuantitySale = async(sale, quantity) => { + query = `CALL vn.sale_setQuantity(?,?)`; + const result = await Self.rawSql(query, [sale, quantity]); + + return result; + }; +}; diff --git a/back/methods/collection/spec/setQuantitySale.spec.js b/back/methods/collection/spec/setQuantitySale.spec.js new file mode 100644 index 0000000000..31c674b18f --- /dev/null +++ b/back/methods/collection/spec/setQuantitySale.spec.js @@ -0,0 +1,15 @@ +const models = require('vn-loopback/server/server').models; + +fdescribe('setQuantitySale()', () => { + it('should change quantity sale', async() => { + const saleId = 1; + const newQuantity = 1; + const originalSale = await models.Sale.findById(saleId); + + await models.Collection.setQuantitySale(saleId, newQuantity); + const updateSale = await models.Sale.findById(saleId); + + expect(updateSale.quantity).toBeLessThan(originalSale.quantity); + expect(updateSale.quantity).toEqual(newQuantity); + }); +}); diff --git a/back/methods/collection/spec/updateCollectionSale.spec.js b/back/methods/collection/spec/updateCollectionSale.spec.js deleted file mode 100644 index 951115e545..0000000000 --- a/back/methods/collection/spec/updateCollectionSale.spec.js +++ /dev/null @@ -1,62 +0,0 @@ -const app = require('vn-loopback/server/server'); - -describe('updateCollectionSale()', () => { - it('should return a new collection', async() => { - const sectorOneWarehouseID = 1; - let ctx = {req: {accessToken: {userId: 1106}}}; - ctx.args = { - sale: 1, - originalQuantity: 5, - quantity: 5, - quantityPicked: 5, - ticketFk: 1, - stateFk: 4, - isNicho: false, - shelvingFk: 'UXN', - itemFk: 1, - sectorFk: 1 - }; - let originalSaleTracking = await app.models.SaleTracking.findOne({ - where: { - saleFk: ctx.args.sale, - stateFk: ctx.args.stateFk - } - }); - let itemPlacement = await app.models.ItemPlacement.findOne({ - where: { - itemFk: ctx.args.itemFk, - warehouseFk: sectorOneWarehouseID - } - }); - const originalSale = await app.models.Sale.findById(ctx.args.sale); - const originalItemShelving = await app.models.ItemShelving.findOne({where: {shelvingFk: ctx.args.shelvingFk, itemFk: ctx.args.itemFk}}); - const originalTicketLastState = await app.models.TicketLastState.findById(ctx.args.ticketFk); - - let response = await app.models.Collection.updateCollectionSale(ctx); - - expect(response.length).toBeGreaterThan(0); - expect(response[0][0].id).toEqual(1); - expect(response[0][0].quantity).toEqual(5); - - // restores - if (originalSaleTracking) - await originalSaleTracking.save(); - else { - originalSaleTracking = await app.models.SaleTracking.findOne({ - where: { - saleFk: ctx.args.sale, - stateFk: ctx.args.stateFk - } - }); - await originalSaleTracking.destroy(); - } - await originalSale.save(); - const itemShelvingsToDestroy = await app.models.ItemShelving.find({where: {shelvingFk: ctx.args.shelvingFk, itemFk: ctx.args.itemFk}}); - for (itemShelving of itemShelvingsToDestroy) - await itemShelving.destroy(); - - await originalItemShelving.save(); - await originalTicketLastState.save(); - await itemPlacement.save(); - }); -}); diff --git a/back/methods/collection/updateCollectionSale.js b/back/methods/collection/updateCollectionSale.js deleted file mode 100644 index 608250d186..0000000000 --- a/back/methods/collection/updateCollectionSale.js +++ /dev/null @@ -1,90 +0,0 @@ -module.exports = Self => { - Self.remoteMethodCtx('updateCollectionSale', { - description: 'Update sale of a collection', - accessType: 'WRITE', - accepts: [{ - arg: 'sale', - type: 'Number', - required: true, - description: 'The sale id' - }, { - arg: 'originalQuantity', - type: 'Number', - required: true, - description: 'The quantity to sale' - }, - { - arg: 'quantity', - type: 'Number', - required: true, - description: 'The quantity to picked' - }, - { - arg: 'quantityPicked', - type: 'Number', - required: true, - description: 'The quantity to picked' - }, { - arg: 'ticketFk', - type: 'Number', - required: true, - description: 'The ticket id' - }, { - arg: 'stateFk', - type: 'Number', - required: true, - description: 'The state id' - }, { - arg: 'isNicho', - type: 'Boolean', - required: true, - description: 'Determine if sale is picked from nicho or not' - }, { - arg: 'shelvingFk', - type: 'String', - required: false, - description: 'The shelving id' - }, { - arg: 'itemFk', - type: 'Number', - required: true, - description: 'The item id' - }, { - arg: 'sectorFk', - type: 'Number', - required: true, - description: 'The sector id' - }], - returns: { - type: 'Object', - root: true - }, - http: { - path: `/updateCollectionSale`, - verb: 'POST' - } - }); - - Self.updateCollectionSale = async ctx => { - const userId = ctx.req.accessToken.userId; - const args = ctx.args; - - if (args.originalQuantity == args.quantity) { - query = `CALL vn.collection_updateSale(?,?,?,?,?)`; - await Self.rawSql(query, [args.sale, args.originalQuantity, userId, args.stateFk, args.ticketFk]); - } - - if (!args.isNicho) { - query = `CALL vn.collection_faults(?,?,?)`; - await Self.rawSql(query, [args.shelvingFk, args.quantityPicked, args.itemFk]); - } else { - query = `CALL vn.sector_getWarehouse(?)`; - const [result] = await Self.rawSql(query, [args.sectorFk]); - - query = `CALL vn.itemPlacementSave(?,?,?)`; - await Self.rawSql(query, [args.shelvingFk, args.quantityPicked, result[0]['warehouseFk']]); - } - query = `CALL vn.sale_updateOriginalQuantity(?,?)`; - return await Self.rawSql(query, [args.sale, args.quantity]); - }; -}; diff --git a/back/models/collection.js b/back/models/collection.js index d3670d56b5..7ac46f2634 100644 --- a/back/models/collection.js +++ b/back/models/collection.js @@ -2,6 +2,6 @@ module.exports = Self => { require('../methods/collection/getCollection')(Self); require('../methods/collection/newCollection')(Self); require('../methods/collection/getSectors')(Self); - require('../methods/collection/updateCollectionSale')(Self); + require('../methods/collection/setQuantitySale')(Self); require('../methods/collection/collectionFaults')(Self); }; From 386155f2424931ac8b3950d016e3c7735562ea01 Mon Sep 17 00:00:00 2001 From: alexm Date: Wed, 2 Feb 2022 14:55:21 +0100 Subject: [PATCH 2/9] feat(setQuantitySale): transaction --- back/methods/collection/setQuantitySale.js | 26 ++++++++++++++++--- .../collection/spec/setQuantitySale.spec.js | 26 +++++++++++++------ 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/back/methods/collection/setQuantitySale.js b/back/methods/collection/setQuantitySale.js index f9448f46aa..9ddec1c18f 100644 --- a/back/methods/collection/setQuantitySale.js +++ b/back/methods/collection/setQuantitySale.js @@ -24,10 +24,28 @@ module.exports = Self => { } }); - Self.setQuantitySale = async(sale, quantity) => { - query = `CALL vn.sale_setQuantity(?,?)`; - const result = await Self.rawSql(query, [sale, quantity]); + Self.setQuantitySale = async(sale, quantity, options) => { + const myOptions = {}; + let tx; - return result; + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + try { + query = `CALL vn.sale_setQuantity(?,?)`; + const result = await Self.rawSql(query, [sale, quantity]); + + if (tx) await tx.commit(); + + return result; + } catch (e) { + if (tx) await tx.rollback(); + throw e; + } }; }; diff --git a/back/methods/collection/spec/setQuantitySale.spec.js b/back/methods/collection/spec/setQuantitySale.spec.js index 31c674b18f..09fd30e30c 100644 --- a/back/methods/collection/spec/setQuantitySale.spec.js +++ b/back/methods/collection/spec/setQuantitySale.spec.js @@ -1,15 +1,25 @@ const models = require('vn-loopback/server/server').models; -fdescribe('setQuantitySale()', () => { +describe('setQuantitySale()', () => { it('should change quantity sale', async() => { - const saleId = 1; - const newQuantity = 1; - const originalSale = await models.Sale.findById(saleId); + const tx = await models.Sale.beginTransaction({}); - await models.Collection.setQuantitySale(saleId, newQuantity); - const updateSale = await models.Sale.findById(saleId); + try { + const options = {transaction: tx}; + const saleId = 30; + const newQuantity = 5; + const originalSale = await models.Sale.findById(saleId); - expect(updateSale.quantity).toBeLessThan(originalSale.quantity); - expect(updateSale.quantity).toEqual(newQuantity); + await models.Collection.setQuantitySale(saleId, newQuantity, options); + const updateSale = await models.Sale.findById(saleId); + + expect(updateSale.quantity).toBeLessThan(originalSale.quantity); + expect(updateSale.quantity).toEqual(newQuantity); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } }); }); From dc78cd2dc524b7e870d90182f32fb17f96b8a520 Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 7 Feb 2022 08:05:05 +0100 Subject: [PATCH 3/9] feat(collection): setQuantitySale --- back/methods/collection/setQuantitySale.js | 18 +++++++++++------- .../collection/spec/setQuantitySale.spec.js | 19 +++++++++++++------ .../10420-valentines/00-aclCollection.sql | 3 +++ 3 files changed, 27 insertions(+), 13 deletions(-) create mode 100644 db/changes/10420-valentines/00-aclCollection.sql diff --git a/back/methods/collection/setQuantitySale.js b/back/methods/collection/setQuantitySale.js index 9ddec1c18f..a364c5a746 100644 --- a/back/methods/collection/setQuantitySale.js +++ b/back/methods/collection/setQuantitySale.js @@ -3,19 +3,19 @@ module.exports = Self => { description: 'Update sale quantity', accessType: 'WRITE', accepts: [{ - arg: 'sale', - type: 'Number', + arg: 'saleId', + type: 'number', required: true, description: 'The sale id' }, { arg: 'quantity', - type: 'Number', + type: 'number', required: true, description: 'The quantity to picked' }], returns: { - type: 'Object', + type: 'object', root: true }, http: { @@ -24,8 +24,11 @@ module.exports = Self => { } }); - Self.setQuantitySale = async(sale, quantity, options) => { + Self.setQuantitySale = async(ctx, options) => { + const args = ctx.args; + const models = Self.app.models; const myOptions = {}; + let tx; if (typeof options == 'object') @@ -37,8 +40,9 @@ module.exports = Self => { } try { - query = `CALL vn.sale_setQuantity(?,?)`; - const result = await Self.rawSql(query, [sale, quantity]); + const sale = await models.Sale.findById(args.saleId, null, myOptions); + + const result = await sale.updateAttribute('quantity', args.quantity, myOptions); if (tx) await tx.commit(); diff --git a/back/methods/collection/spec/setQuantitySale.spec.js b/back/methods/collection/spec/setQuantitySale.spec.js index 09fd30e30c..2962a4c1dd 100644 --- a/back/methods/collection/spec/setQuantitySale.spec.js +++ b/back/methods/collection/spec/setQuantitySale.spec.js @@ -1,17 +1,24 @@ const models = require('vn-loopback/server/server').models; -describe('setQuantitySale()', () => { +fdescribe('setQuantitySale()', () => { it('should change quantity sale', async() => { const tx = await models.Sale.beginTransaction({}); + const saleId = 30; + const newQuantity = 10; try { const options = {transaction: tx}; - const saleId = 30; - const newQuantity = 5; - const originalSale = await models.Sale.findById(saleId); + const ctx = { + args: { + saleId: saleId, + quantity: newQuantity + } + }; - await models.Collection.setQuantitySale(saleId, newQuantity, options); - const updateSale = await models.Sale.findById(saleId); + const originalSale = await models.Sale.findById(30, null, options); + + await models.Collection.setQuantitySale(ctx, options); + const updateSale = await models.Sale.findById(30, null, options); expect(updateSale.quantity).toBeLessThan(originalSale.quantity); expect(updateSale.quantity).toEqual(newQuantity); diff --git a/db/changes/10420-valentines/00-aclCollection.sql b/db/changes/10420-valentines/00-aclCollection.sql new file mode 100644 index 0000000000..5c2f1856e9 --- /dev/null +++ b/db/changes/10420-valentines/00-aclCollection.sql @@ -0,0 +1,3 @@ +INSERT INTO salix.ACL +(model, property, accessType, permission, principalType, principalId) +VALUES('Collection', 'setQuantitySale', '*', 'ALLOW', 'ROLE', 'employee'); From e08a3d31bc1bdffdb2d7df2ca20178a49e81f60e Mon Sep 17 00:00:00 2001 From: alexm Date: Mon, 7 Feb 2022 08:23:19 +0100 Subject: [PATCH 4/9] remove f of focus --- back/methods/collection/spec/setQuantitySale.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/methods/collection/spec/setQuantitySale.spec.js b/back/methods/collection/spec/setQuantitySale.spec.js index 2962a4c1dd..19c317ae39 100644 --- a/back/methods/collection/spec/setQuantitySale.spec.js +++ b/back/methods/collection/spec/setQuantitySale.spec.js @@ -1,6 +1,6 @@ const models = require('vn-loopback/server/server').models; -fdescribe('setQuantitySale()', () => { +describe('setQuantitySale()', () => { it('should change quantity sale', async() => { const tx = await models.Sale.beginTransaction({}); const saleId = 30; From e09e84c36b5f53e7606b602b7eba70bd2fbe01c4 Mon Sep 17 00:00:00 2001 From: alexm Date: Tue, 8 Feb 2022 12:41:55 +0100 Subject: [PATCH 5/9] refactor(setSaleQuantity): rename, remove transaction --- back/methods/collection/setQuantitySale.js | 55 ------------------- back/methods/collection/setSaleQuantity.js | 34 ++++++++++++ .../collection/spec/setQuantitySale.spec.js | 32 ----------- .../collection/spec/setSaleQuantity.spec.js | 23 ++++++++ back/models/collection.js | 2 +- .../10420-valentines/00-aclCollection.sql | 2 +- loopback/locale/en.json | 3 +- 7 files changed, 61 insertions(+), 90 deletions(-) delete mode 100644 back/methods/collection/setQuantitySale.js create mode 100644 back/methods/collection/setSaleQuantity.js delete mode 100644 back/methods/collection/spec/setQuantitySale.spec.js create mode 100644 back/methods/collection/spec/setSaleQuantity.spec.js diff --git a/back/methods/collection/setQuantitySale.js b/back/methods/collection/setQuantitySale.js deleted file mode 100644 index a364c5a746..0000000000 --- a/back/methods/collection/setQuantitySale.js +++ /dev/null @@ -1,55 +0,0 @@ -module.exports = Self => { - Self.remoteMethodCtx('setQuantitySale', { - description: 'Update sale quantity', - accessType: 'WRITE', - accepts: [{ - arg: 'saleId', - type: 'number', - required: true, - description: 'The sale id' - }, - { - arg: 'quantity', - type: 'number', - required: true, - description: 'The quantity to picked' - }], - returns: { - type: 'object', - root: true - }, - http: { - path: `/setQuantitySale`, - verb: 'POST' - } - }); - - Self.setQuantitySale = async(ctx, options) => { - const args = ctx.args; - const models = Self.app.models; - const myOptions = {}; - - let tx; - - if (typeof options == 'object') - Object.assign(myOptions, options); - - if (!myOptions.transaction) { - tx = await Self.beginTransaction({}); - myOptions.transaction = tx; - } - - try { - const sale = await models.Sale.findById(args.saleId, null, myOptions); - - const result = await sale.updateAttribute('quantity', args.quantity, myOptions); - - if (tx) await tx.commit(); - - return result; - } catch (e) { - if (tx) await tx.rollback(); - throw e; - } - }; -}; diff --git a/back/methods/collection/setSaleQuantity.js b/back/methods/collection/setSaleQuantity.js new file mode 100644 index 0000000000..82451b8be1 --- /dev/null +++ b/back/methods/collection/setSaleQuantity.js @@ -0,0 +1,34 @@ +module.exports = Self => { + Self.remoteMethodCtx('setSaleQuantity', { + description: 'Update sale quantity', + accessType: 'WRITE', + accepts: [{ + arg: 'saleId', + type: 'number', + required: true, + description: 'The sale id' + }, + { + arg: 'quantity', + type: 'number', + required: true, + description: 'The quantity to picked' + }], + returns: { + type: 'object', + root: true + }, + http: { + path: `/setSaleQuantity`, + verb: 'POST' + } + }); + + Self.setSaleQuantity = async ctx => { + const args = ctx.args; + const models = Self.app.models; + + const sale = await models.Sale.findById(args.saleId,); + return await sale.updateAttribute('quantity', args.quantity); + }; +}; diff --git a/back/methods/collection/spec/setQuantitySale.spec.js b/back/methods/collection/spec/setQuantitySale.spec.js deleted file mode 100644 index 19c317ae39..0000000000 --- a/back/methods/collection/spec/setQuantitySale.spec.js +++ /dev/null @@ -1,32 +0,0 @@ -const models = require('vn-loopback/server/server').models; - -describe('setQuantitySale()', () => { - it('should change quantity sale', async() => { - const tx = await models.Sale.beginTransaction({}); - const saleId = 30; - const newQuantity = 10; - - try { - const options = {transaction: tx}; - const ctx = { - args: { - saleId: saleId, - quantity: newQuantity - } - }; - - const originalSale = await models.Sale.findById(30, null, options); - - await models.Collection.setQuantitySale(ctx, options); - const updateSale = await models.Sale.findById(30, null, options); - - expect(updateSale.quantity).toBeLessThan(originalSale.quantity); - expect(updateSale.quantity).toEqual(newQuantity); - - await tx.rollback(); - } catch (e) { - await tx.rollback(); - throw e; - } - }); -}); diff --git a/back/methods/collection/spec/setSaleQuantity.spec.js b/back/methods/collection/spec/setSaleQuantity.spec.js new file mode 100644 index 0000000000..4e3c8c4aad --- /dev/null +++ b/back/methods/collection/spec/setSaleQuantity.spec.js @@ -0,0 +1,23 @@ +const models = require('vn-loopback/server/server').models; + +describe('setSaleQuantity()', () => { + it('should change quantity sale', async() => { + const saleId = 30; + const newQuantity = 10; + + const ctx = { + args: { + saleId: saleId, + quantity: newQuantity + } + }; + + const originalSale = await models.Sale.findById(saleId); + + await models.Collection.setSaleQuantity(ctx); + const updateSale = await models.Sale.findById(saleId); + + expect(updateSale.quantity).toBeLessThan(originalSale.quantity); + expect(updateSale.quantity).toEqual(newQuantity); + }); +}); diff --git a/back/models/collection.js b/back/models/collection.js index 7ac46f2634..29a0c4c32d 100644 --- a/back/models/collection.js +++ b/back/models/collection.js @@ -2,6 +2,6 @@ module.exports = Self => { require('../methods/collection/getCollection')(Self); require('../methods/collection/newCollection')(Self); require('../methods/collection/getSectors')(Self); - require('../methods/collection/setQuantitySale')(Self); + require('../methods/collection/setSaleQuantity')(Self); require('../methods/collection/collectionFaults')(Self); }; diff --git a/db/changes/10420-valentines/00-aclCollection.sql b/db/changes/10420-valentines/00-aclCollection.sql index 5c2f1856e9..81e53049ca 100644 --- a/db/changes/10420-valentines/00-aclCollection.sql +++ b/db/changes/10420-valentines/00-aclCollection.sql @@ -1,3 +1,3 @@ INSERT INTO salix.ACL (model, property, accessType, permission, principalType, principalId) -VALUES('Collection', 'setQuantitySale', '*', 'ALLOW', 'ROLE', 'employee'); +VALUES('Collection', 'setSaleQuantity', '*', 'ALLOW', 'ROLE', 'employee'); \ No newline at end of file diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 7d675cb13d..599febc48e 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -120,5 +120,6 @@ "This item is not available": "This item is not available", "Deny buy request": "Purchase request for ticket id [{{ticketId}}]({{{url}}}) has been rejected. Reason: {{observation}}", "The type of business must be filled in basic data": "The type of business must be filled in basic data", - "The worker has hours recorded that day": "The worker has hours recorded that day" + "The worker has hours recorded that day": "The worker has hours recorded that day", + "isWithoutNegatives": "isWithoutNegatives" } \ No newline at end of file From 2e649e81d642ec239310b839e57ee8c29d255358 Mon Sep 17 00:00:00 2001 From: alexm Date: Wed, 2 Feb 2022 14:33:16 +0100 Subject: [PATCH 6/9] fix(test): fixed bugs non compatible ECMA options --- e2e/paths/05-ticket/06_basic_data_steps.spec.js | 2 +- modules/ticket/back/methods/ticket/componentUpdate.js | 3 +-- modules/ticket/back/methods/ticket/priceDifference.js | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/e2e/paths/05-ticket/06_basic_data_steps.spec.js b/e2e/paths/05-ticket/06_basic_data_steps.spec.js index 7a09edf06d..fa118c25dd 100644 --- a/e2e/paths/05-ticket/06_basic_data_steps.spec.js +++ b/e2e/paths/05-ticket/06_basic_data_steps.spec.js @@ -94,7 +94,7 @@ describe('Ticket Edit basic data path', () => { it(`should split ticket without negatives`, async() => { const newAgency = 'Silla247'; const newDate = new Date(); - newDate.setDate(newDate.getDate() + 1); + newDate.setDate(newDate.getDate() - 1); await page.accessToSearchResult('14'); await page.accessToSection('ticket.card.basicData.stepOne'); diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js index 0fa26c9ea5..2294e6d254 100644 --- a/modules/ticket/back/methods/ticket/componentUpdate.js +++ b/modules/ticket/back/methods/ticket/componentUpdate.js @@ -138,9 +138,8 @@ module.exports = Self => { const params = [args.id, args.shipped, args.warehouseFk]; const [salesMovable] = await Self.rawSql(query, params, myOptions); - const saleMovable = sale.movable ? sale.movable : 0; + const salesNewTicket = salesMovable.filter(sale => (sale.movable ? sale.movable : 0) >= sale.quantity); - const salesNewTicket = salesMovable.filter(sale => saleMovable >= sale.quantity); if (salesNewTicket.length) { const newTicket = await models.Ticket.transferSales(ctx, args.id, null, salesNewTicket, myOptions); args.id = newTicket.id; diff --git a/modules/ticket/back/methods/ticket/priceDifference.js b/modules/ticket/back/methods/ticket/priceDifference.js index d47fabd51b..e0ffac55a2 100644 --- a/modules/ticket/back/methods/ticket/priceDifference.js +++ b/modules/ticket/back/methods/ticket/priceDifference.js @@ -112,7 +112,7 @@ module.exports = Self => { // Get items movable const ticketOrigin = await models.Ticket.findById(args.id, null, myOptions); - const differenceShipped = ticketOrigin.shipped.getTime() != args.shipped.getTime(); + const differenceShipped = ticketOrigin.shipped.getTime() > args.shipped.getTime(); const differenceWarehouse = ticketOrigin.warehouseFk != args.warehouseId; salesObj.haveDifferences = differenceShipped || differenceWarehouse; From ef281bb663eff16b4e8cbbee46034b5f9d316c0b Mon Sep 17 00:00:00 2001 From: alexm Date: Wed, 9 Feb 2022 12:39:08 +0100 Subject: [PATCH 7/9] feat(order_summary): change salesPersonFk for clientFk --- modules/order/front/summary/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/order/front/summary/index.html b/modules/order/front/summary/index.html index b4562c3176..2813aeb9b8 100644 --- a/modules/order/front/summary/index.html +++ b/modules/order/front/summary/index.html @@ -8,7 +8,7 @@ Basket #{{$ctrl.summary.id}} - {{$ctrl.summary.client.name}} - ({{$ctrl.summary.client.salesPersonFk}}) + ({{$ctrl.summary.client.id}}) Date: Wed, 9 Feb 2022 14:06:06 +0100 Subject: [PATCH 8/9] feat(notification): notify client metrics Refs: 3315 --- modules/client/front/index.js | 1 + modules/client/front/notification/index.html | 155 +++++++++++++++++++ modules/client/front/notification/index.js | 129 +++++++++++++++ modules/client/front/routes.json | 7 + print/methods/notify/consumption.js | 45 ++++++ print/methods/notify/index.js | 6 + print/methods/routes.js | 4 + 7 files changed, 347 insertions(+) create mode 100644 modules/client/front/notification/index.html create mode 100644 modules/client/front/notification/index.js create mode 100644 print/methods/notify/consumption.js create mode 100644 print/methods/notify/index.js diff --git a/modules/client/front/index.js b/modules/client/front/index.js index 6b35d392a3..d9f3a8a177 100644 --- a/modules/client/front/index.js +++ b/modules/client/front/index.js @@ -45,3 +45,4 @@ import './dms/edit'; import './consumption'; import './consumption-search-panel'; import './defaulter'; +import './notification'; diff --git a/modules/client/front/notification/index.html b/modules/client/front/notification/index.html new file mode 100644 index 0000000000..f4b23a7542 --- /dev/null +++ b/modules/client/front/notification/index.html @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Identifier + + Social name + + City + + Phone + + Email +
+ + + + + {{::client.id}} + + {{::client.socialName}}{{::client.city}}{{::client.phone}}{{::client.email}}
+
+
+
+ + + +
+
+

Client consumption

+ + + + {{code}} {{dated | date: 'yyyy'}} + + + + + + + + + + + + +
+
+
+ + + + Filter by selection + + + Exclude selection + + + Remove filter + + + Remove all filters + + + Copy value + + + diff --git a/modules/client/front/notification/index.js b/modules/client/front/notification/index.js new file mode 100644 index 0000000000..56cf196a0e --- /dev/null +++ b/modules/client/front/notification/index.js @@ -0,0 +1,129 @@ +import ngModule from '../module'; +import Section from 'salix/components/section'; + +export default class Controller extends Section { + constructor($element, $) { + super($element, $); + this.$checkAll = false; + this.smartTableOptions = { + activeButtons: { + search: true + }, + columns: [ + { + field: 'socialName', + autocomplete: { + url: 'Clients', + showField: 'socialName', + valueField: 'socialName' + } + }, + { + field: 'city', + autocomplete: { + url: 'Towns', + valueField: 'name', + showField: 'name' + } + } + ] + }; + + this.campaign = { + id: null, + from: null, + to: null + }; + + // if (!this.dateParams) + this.getUpcomingCampaing(); + } + + get checked() { + const clients = this.$.model.data || []; + const checkedClients = []; + for (const buy of clients) { + if (buy.$checked) + checkedClients.push(buy); + } + + return checkedClients; + } + + onChangeDate(value) { + if (value) + this.campaign.id = null; + } + + getUpcomingCampaing() { + this.$http.get('Campaigns/upcoming').then(res => { + this.campaign.id = res.data.id; + }); + } + + get campaignSelection() { + return this._campaignSelection; + } + + set campaignSelection(value) { + this._campaignSelection = value; + + if (value) { + const from = new Date(value.dated); + from.setDate(from.getDate() - value.scopeDays); + + this.campaign.to = value.dated; + this.campaign.from = from; + } + } + + get clientConsumptionParams() { + const userParams = this.$.model.userParams; + return Object.assign({ + recipient: this.client.email, + recipientId: this.client.id + }, userParams); + } + + onSendClientConsumption() { + const clientIds = this.checked.map(client => client.id); + const params = Object.assign({ + clientIds: clientIds + }, this.campaign); + + this.$http.post('notify/consumption', params) + .then(() => this.$.filters.hide()) + .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))); + } + + exprBuilder(param, value) { + switch (param) { + case 'search': + return /^\d+$/.test(value) + ? {id: value} + : {or: [{name: {like: `%${value}%`}}, {socialName: {like: `%${value}%`}}]}; + case 'phone': + return { + or: [ + {phone: value}, + {mobile: value} + ] + }; + case 'name': + case 'socialName': + case 'city': + case 'email': + return {[param]: {like: `%${value}%`}}; + case 'id': + case 'fi': + case 'postcode': + case 'salesPersonFk': + return {[param]: value}; + } + } +} + +ngModule.vnComponent('vnClientNotification', { + template: require('./index.html'), + controller: Controller +}); diff --git a/modules/client/front/routes.json b/modules/client/front/routes.json index 753948f8eb..d6f8a70bdb 100644 --- a/modules/client/front/routes.json +++ b/modules/client/front/routes.json @@ -7,6 +7,7 @@ "menus": { "main": [ {"state": "client.index", "icon": "person"}, + {"state": "client.notification", "icon": "campaign"}, {"state": "client.defaulter.index", "icon": "icon-defaulter"} ], "card": [ @@ -373,6 +374,12 @@ "state": "client.defaulter.index", "component": "vn-client-defaulter-index", "description": "Defaulter" + }, + { + "url" : "/notification", + "state": "client.notification", + "component": "vn-client-notification", + "description": "Notifications" } ] } diff --git a/print/methods/notify/consumption.js b/print/methods/notify/consumption.js new file mode 100644 index 0000000000..e828f3c5b6 --- /dev/null +++ b/print/methods/notify/consumption.js @@ -0,0 +1,45 @@ +const db = require('vn-print/core/database'); +const Email = require('vn-print/core/email'); + +module.exports = async function(request, response, next) { + const reqArgs = request.body; + + if (!reqArgs.clientIds) + throw new Error('The argument clientIds is required'); + if (!reqArgs.from) + throw new Error('The argument from is required'); + if (!reqArgs.to) + throw new Error('The argument to is required'); + + response.status(200).json({ + message: 'Success' + }); + + for (const clientId of reqArgs.clientIds) { + const client = await db.findOne(` + SELECT + c.email, + eu.email salesPersonEmail + FROM client c + JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk + JOIN ticket t ON t.clientFk = c.id + JOIN sale s ON s.ticketFk = t.id + JOIN item i ON i.id = s.itemFk + JOIN itemType it ON it.id = i.typeFk + WHERE c.id = ? + AND it.isPackaging = FALSE + AND DATE(t.shipped) BETWEEN ? AND ? + GROUP BY c.id`, [clientId, reqArgs.from, reqArgs.to]); + + if (client) { + const args = Object.assign({ + recipientId: clientId, + recipient: client.email, + replyTo: client.salesPersonEmail + }, response.locals); + + const email = new Email('campaign-metrics', args); + await email.send(); + } + } +}; diff --git a/print/methods/notify/index.js b/print/methods/notify/index.js new file mode 100644 index 0000000000..df4705d1e4 --- /dev/null +++ b/print/methods/notify/index.js @@ -0,0 +1,6 @@ +const express = require('express'); +const router = new express.Router(); + +router.post('/consumption', require('./consumption')); + +module.exports = router; diff --git a/print/methods/routes.js b/print/methods/routes.js index 0c452028e5..ea40e07433 100644 --- a/print/methods/routes.js +++ b/print/methods/routes.js @@ -15,4 +15,8 @@ module.exports = [ url: '/api/closure', cb: require('./closure') }, + { + url: '/api/notify', + cb: require('./notify') + } ]; From 5518fac357a4e145d6da9cc26300adb61ca4e554 Mon Sep 17 00:00:00 2001 From: joan Date: Mon, 14 Feb 2022 08:44:22 +0100 Subject: [PATCH 9/9] Updated unit tests --- modules/client/front/notification/index.html | 6 +- modules/client/front/notification/index.js | 16 +-- .../client/front/notification/index.spec.js | 99 +++++++++++++++++++ .../client/front/notification/locale/es.yml | 2 + print/methods/notify/consumption.js | 79 ++++++++------- 5 files changed, 153 insertions(+), 49 deletions(-) create mode 100644 modules/client/front/notification/index.spec.js create mode 100644 modules/client/front/notification/locale/es.yml diff --git a/modules/client/front/notification/index.html b/modules/client/front/notification/index.html index f4b23a7542..f503c95c8a 100644 --- a/modules/client/front/notification/index.html +++ b/modules/client/front/notification/index.html @@ -23,7 +23,7 @@ + vn-tooltip="Campaign consumption"> @@ -87,7 +87,7 @@
-

Client consumption

+

Campaign consumption

- +
diff --git a/modules/client/front/notification/index.js b/modules/client/front/notification/index.js index 56cf196a0e..12a1a4acb4 100644 --- a/modules/client/front/notification/index.js +++ b/modules/client/front/notification/index.js @@ -35,7 +35,6 @@ export default class Controller extends Section { to: null }; - // if (!this.dateParams) this.getUpcomingCampaing(); } @@ -56,9 +55,8 @@ export default class Controller extends Section { } getUpcomingCampaing() { - this.$http.get('Campaigns/upcoming').then(res => { - this.campaign.id = res.data.id; - }); + this.$http.get('Campaigns/upcoming') + .then(res => this.campaign.id = res.data.id); } get campaignSelection() { @@ -77,14 +75,6 @@ export default class Controller extends Section { } } - get clientConsumptionParams() { - const userParams = this.$.model.userParams; - return Object.assign({ - recipient: this.client.email, - recipientId: this.client.id - }, userParams); - } - onSendClientConsumption() { const clientIds = this.checked.map(client => client.id); const params = Object.assign({ @@ -93,7 +83,7 @@ export default class Controller extends Section { this.$http.post('notify/consumption', params) .then(() => this.$.filters.hide()) - .then(() => this.vnApp.showSuccess(this.$t('Data saved!'))); + .then(() => this.vnApp.showSuccess(this.$t('Notifications sent!'))); } exprBuilder(param, value) { diff --git a/modules/client/front/notification/index.spec.js b/modules/client/front/notification/index.spec.js new file mode 100644 index 0000000000..13c6bc513b --- /dev/null +++ b/modules/client/front/notification/index.spec.js @@ -0,0 +1,99 @@ +import './index'; +import crudModel from 'core/mocks/crud-model'; + +describe('Client notification', () => { + describe('Component vnClientNotification', () => { + let controller; + let $httpBackend; + + beforeEach(ngModule('client')); + + beforeEach(inject(($componentController, _$httpBackend_) => { + $httpBackend = _$httpBackend_; + const $element = angular.element(''); + controller = $componentController('vnClientNotification', {$element}); + controller.$.model = crudModel; + controller.$.model.data = [ + {id: 1101}, + {id: 1102}, + {id: 1103} + ]; + $httpBackend.expect('GET', `Campaigns/upcoming`).respond(200, {id: 1}); + })); + + describe('checked() getter', () => { + it('should return the checked lines', () => { + const data = controller.$.model.data; + data[1].$checked = true; + data[2].$checked = true; + + const checkedRows = controller.checked; + + const firstCheckedRow = checkedRows[0]; + const secondCheckedRow = checkedRows[1]; + + expect(firstCheckedRow.id).toEqual(1102); + expect(secondCheckedRow.id).toEqual(1103); + }); + }); + + describe('campaignSelection() setter', () => { + it('should set the campaign from and to properties', () => { + const dated = new Date(); + controller.campaignSelection = { + dated: dated, + scopeDays: 14 + }; + + const expectedDateTo = new Date(dated); + expectedDateTo.setDate(expectedDateTo.getDate() - 14); + + const campaign = controller.campaign; + + expect(campaign.from).toEqual(expectedDateTo); + expect(campaign.to).toEqual(dated); + }); + }); + + describe('onSendClientConsumption()', () => { + it('should return saved message', () => { + jest.spyOn(controller.vnApp, 'showSuccess'); + + controller.$.filters = {hide: () => {}}; + controller.campaign = { + id: 1, + from: new Date(), + to: new Date() + }; + + const data = controller.$.model.data; + data[0].$checked = true; + data[1].$checked = true; + + const params = Object.assign({ + clientIds: [1101, 1102] + }, controller.campaign); + + $httpBackend.expect('POST', `notify/consumption`, params).respond(200, params); + controller.onSendClientConsumption(); + $httpBackend.flush(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Notifications sent!'); + }); + }); + + describe('exprBuilder()', () => { + it('should search by sales person', () => { + let expr = controller.exprBuilder('salesPersonFk', '5'); + + expect(expr).toEqual({'salesPersonFk': '5'}); + }); + + it('should search by client social name', () => { + let expr = controller.exprBuilder('socialName', '1foo'); + + expect(expr).toEqual({'socialName': {like: '%1foo%'}}); + }); + }); + }); +}); diff --git a/modules/client/front/notification/locale/es.yml b/modules/client/front/notification/locale/es.yml new file mode 100644 index 0000000000..dd89d7c6eb --- /dev/null +++ b/modules/client/front/notification/locale/es.yml @@ -0,0 +1,2 @@ +Campaign consumption: Consumo campaƱa +Send: Enviar \ No newline at end of file diff --git a/print/methods/notify/consumption.js b/print/methods/notify/consumption.js index e828f3c5b6..39d39105bf 100644 --- a/print/methods/notify/consumption.js +++ b/print/methods/notify/consumption.js @@ -2,44 +2,57 @@ const db = require('vn-print/core/database'); const Email = require('vn-print/core/email'); module.exports = async function(request, response, next) { - const reqArgs = request.body; + try { + const reqArgs = request.body; - if (!reqArgs.clientIds) - throw new Error('The argument clientIds is required'); - if (!reqArgs.from) - throw new Error('The argument from is required'); - if (!reqArgs.to) - throw new Error('The argument to is required'); + if (!reqArgs.clientIds) + throw new Error('The argument clientIds is required'); + if (!reqArgs.from) + throw new Error('The argument from is required'); + if (!reqArgs.to) + throw new Error('The argument to is required'); - response.status(200).json({ - message: 'Success' - }); + response.status(200).json({ + message: 'Success' + }); - for (const clientId of reqArgs.clientIds) { - const client = await db.findOne(` - SELECT - c.email, - eu.email salesPersonEmail - FROM client c - JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk - JOIN ticket t ON t.clientFk = c.id - JOIN sale s ON s.ticketFk = t.id - JOIN item i ON i.id = s.itemFk - JOIN itemType it ON it.id = i.typeFk - WHERE c.id = ? - AND it.isPackaging = FALSE - AND DATE(t.shipped) BETWEEN ? AND ? - GROUP BY c.id`, [clientId, reqArgs.from, reqArgs.to]); + const clientIds = reqArgs.clientIds; - if (client) { - const args = Object.assign({ - recipientId: clientId, - recipient: client.email, - replyTo: client.salesPersonEmail - }, response.locals); + const clients = await db.rawSql(` + SELECT + c.id, + c.email, + eu.email salesPersonEmail + FROM client c + JOIN account.emailUser eu ON eu.userFk = c.salesPersonFk + JOIN ticket t ON t.clientFk = c.id + JOIN sale s ON s.ticketFk = t.id + JOIN item i ON i.id = s.itemFk + JOIN itemType it ON it.id = i.typeFk + WHERE c.id IN(?) + AND it.isPackaging = FALSE + AND DATE(t.shipped) BETWEEN ? AND ? + GROUP BY c.id`, [clientIds, reqArgs.from, reqArgs.to]); - const email = new Email('campaign-metrics', args); - await email.send(); + const clientData = new Map(); + for (const client of clients) + clientData.set(client.id, client); + + for (const clientId of reqArgs.clientIds) { + const client = clientData.get(clientId); + + if (client) { + const args = Object.assign({ + recipientId: clientId, + recipient: client.email, + replyTo: client.salesPersonEmail + }, response.locals); + + const email = new Email('campaign-metrics', args); + await email.send(); + } } + } catch (error) { + next(error); } };