diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 2484990ed..d77b0c26d 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -57,7 +57,14 @@ "The postcode doesn't exist. Please enter a correct one": "The postcode doesn't exist. Please enter a correct one", "Can't create stowaway for this ticket": "Can't create stowaway for this ticket", "Swift / BIC can't be empty": "Swift / BIC can't be empty", - "Bought units from buy request": "Bought {{quantity}} units of {{concept}} [{{itemId}}]({{{urlItem}}}) for the ticket id [{{ticketId}}]({{{url}}})", + "Deleted sales from ticket": "I have deleted the following lines from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}", + "Added sale to ticket": "I have added the following line to the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}", + "Changed sale discount": "I have changed the following lines discounts from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Created claim": "I have created the claim [{{claimId}}]({{{claimUrl}}}) for the following lines from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Changed sale price": "I have changed the price of [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) from {{oldPrice}}€ ➔ *{{newPrice}}€* of the ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale quantity": "I have changed the quantity of [{{itemId}} {{concept}}]({{{itemUrl}}}) from {{oldQuantity}} ➔ *{{newQuantity}}* of the ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale reserved state": "I have changed the following lines reserved state from the ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Bought units from buy request": "Bought {{quantity}} units of [{{itemId}} {{concept}}]({{{urlItem}}}) for the ticket id [{{ticketId}}]({{{url}}})", "MESSAGE_INSURANCE_CHANGE": "I have changed the insurence credit of client [{{clientName}} ({{clientId}})]({{{url}}}) to *{{credit}} €*", "Changed client paymethod": "I have changed the pay method for client [{{clientName}} ({{clientId}})]({{{url}}})", "Sent units from ticket": "I sent *{{quantity}}* units of [{{concept}} ({{itemId}})]({{{itemUrl}}}) to *\"{{nickname}}\"* coming from ticket id [{{ticketId}}]({{{ticketUrl}}})", @@ -98,6 +105,9 @@ "Client assignment has changed": "I did change the salesperson ~*\"<{{previousWorkerName}}>\"*~ by *\"<{{currentWorkerName}}>\"* from the client [{{clientName}} ({{clientId}})]({{{url}}})", "None": "None", "error densidad = 0": "error densidad = 0", + "nickname": "nickname", "This document already exists on this ticket": "This document already exists on this ticket", - "serial non editable": "This serial doesn't allow to set a reference" + "State": "State", + "regular": "regular", + "reserved": "reserved" } \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 1d73cbbf3..53213adae 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -54,7 +54,7 @@ "This address doesn't exist": "Este consignatario no existe", "You can't create an order for a inactive client": "You can't create an order for a inactive client", "You can't create an order for a client that doesn't has tax data verified": "You can't create an order for a client that doesn't has tax data verified", - "You must delete the claim id %d first": "Antes debes borrar la reclamacion %d", + "You must delete the claim id %d first": "Antes debes borrar la reclamación %d", "You don't have enough privileges": "No tienes suficientes permisos", "Cannot check Equalization Tax in this NIF/CIF": "No se puede marcar RE en este NIF/CIF", "You can't make changes on the basic data of an confirmed order or with rows": "No puedes cambiar los datos basicos de una orden con artículos", @@ -122,7 +122,17 @@ "Swift / BIC can't be empty": "Swift / BIC no puede estar vacío", "Customs agent is required for a non UEE member": "El agente de aduanas es requerido para los clientes extracomunitarios", "Incoterms is required for a non UEE member": "El incoterms es requerido para los clientes extracomunitarios", - "Bought units from buy request": "Se ha comprado {{quantity}} unidades de {{concept}} [{{itemId}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})", + "Deleted sales from ticket": "He eliminado las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{deletions}}}", + "Added sale to ticket": "He añadido la siguiente linea al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{addition}}}", + "Changed sale discount": "He cambiado el descuento de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Created claim": "He creado la reclamación [{{claimId}}]({{{claimUrl}}}) de las siguientes lineas del ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Changed sale price": "He cambiado el precio de [{{itemId}} {{concept}}]({{{itemUrl}}}) ({{quantity}}) de {{oldPrice}}€ ➔ *{{newPrice}}€* del ticket [{{ticketId}}]({{{ticketUrl}}})", + "Changed sale quantity": "He cambiado la cantidad de [{{itemId}} {{concept}}]({{{itemUrl}}}) de {{oldQuantity}} ➔ *{{newQuantity}}* del ticket [{{ticketId}}]({{{ticketUrl}}})", + "State": "Estado", + "regular": "normal", + "reserved": "reservado", + "Changed sale reserved state": "He cambiado el estado reservado de las siguientes lineas al ticket [{{ticketId}}]({{{ticketUrl}}}): {{{changes}}}", + "Bought units from buy request": "Se ha comprado {{quantity}} unidades de [{{itemId}} {{concept}}]({{{urlItem}}}) para el ticket id [{{ticketId}}]({{{url}}})", "MESSAGE_INSURANCE_CHANGE": "He cambiado el crédito asegurado del cliente [{{clientName}} ({{clientId}})]({{{url}}}) a *{{credit}} €*", "Changed client paymethod": "He cambiado la forma de pago del cliente [{{clientName}} ({{clientId}})]({{{url}}})", "Sent units from ticket": "Envio *{{quantity}}* unidades de [{{concept}} ({{itemId}})]({{{itemUrl}}}) a *\"{{nickname}}\"* provenientes del ticket id [{{ticketId}}]({{{ticketUrl}}})", diff --git a/modules/claim/back/methods/claim/createFromSales.js b/modules/claim/back/methods/claim/createFromSales.js index 2dd1b75c2..f22aabbf3 100644 --- a/modules/claim/back/methods/claim/createFromSales.js +++ b/modules/claim/back/methods/claim/createFromSales.js @@ -3,17 +3,20 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { Self.remoteMethodCtx('createFromSales', { description: 'Create a claim', - accepts: [{ - arg: 'ticketId', - type: 'Number', - required: true, - description: 'The origin ticket id' - }, { - arg: 'sales', - type: ['Object'], - required: true, - description: 'The claimed sales' - }], + accepts: [ + { + arg: 'ticketId', + type: 'number', + required: true, + description: 'The origin ticket id' + }, + { + arg: 'sales', + type: ['object'], + required: true, + description: 'The claimed sales' + } + ], returns: { type: 'object', root: true @@ -25,6 +28,7 @@ module.exports = Self => { }); Self.createFromSales = async(ctx, ticketId, sales, options) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; let tx; const myOptions = {}; @@ -39,7 +43,20 @@ module.exports = Self => { const userId = ctx.req.accessToken.userId; try { - const ticket = await models.Ticket.findById(ticketId, null, myOptions); + const ticket = await models.Ticket.findById(ticketId, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }, myOptions); + if (ticket.isDeleted) throw new UserError(`You can't create a claim for a removed ticket`); @@ -49,6 +66,8 @@ module.exports = Self => { ticketCreated: ticket.shipped, workerFk: userId }, myOptions); + + let changesMade = ''; const promises = []; for (const sale of sales) { @@ -59,10 +78,25 @@ module.exports = Self => { }, myOptions); promises.push(newClaimBeginning); + changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`; } await Promise.all(promises); + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Created claim', { + claimId: newClaim.id, + ticketId: ticketId, + ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, + claimUrl: `${origin}/#!/claim/${newClaim.id}/summary`, + changes: changesMade + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + if (tx) await tx.commit(); return newClaim; diff --git a/modules/claim/back/methods/claim/specs/createFromSales.spec.js b/modules/claim/back/methods/claim/specs/createFromSales.spec.js index f08914025..849ccf8f5 100644 --- a/modules/claim/back/methods/claim/specs/createFromSales.spec.js +++ b/modules/claim/back/methods/claim/specs/createFromSales.spec.js @@ -7,7 +7,13 @@ describe('Claim createFromSales()', () => { instance: 0, quantity: 10 }]; - const ctx = {req: {accessToken: {userId: 1}}}; + const ctx = { + req: { + accessToken: {userId: 1}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; it('should create a new claim', async() => { const tx = await app.models.Claim.beginTransaction({}); diff --git a/modules/invoiceOut/front/summary/index.html b/modules/invoiceOut/front/summary/index.html index 0406a39c5..924972299 100644 --- a/modules/invoiceOut/front/summary/index.html +++ b/modules/invoiceOut/front/summary/index.html @@ -1,6 +1,7 @@ diff --git a/modules/invoiceOut/front/summary/index.js b/modules/invoiceOut/front/summary/index.js index 2446e986a..af0b5365e 100644 --- a/modules/invoiceOut/front/summary/index.js +++ b/modules/invoiceOut/front/summary/index.js @@ -7,7 +7,7 @@ class Controller extends Summary { this._invoiceOut = value; if (value && value.id) { this.getSummary(); - this.getTickets(); + this.$.ticketsModel.url = `InvoiceOuts/${this.invoiceOut.id}/getTickets`; } } @@ -19,14 +19,6 @@ class Controller extends Summary { return this.$http.get(`InvoiceOuts/${this.invoiceOut.id}/summary`) .then(res => this.summary = res.data); } - - getTickets() { - this.$.$applyAsync(() => { - const query = `InvoiceOuts/${this.invoiceOut.id}/getTickets`; - this.$.ticketsModel.url = query; - this.$.ticketsModel.refresh(); - }); - } } ngModule.vnComponent('vnInvoiceOutSummary', { diff --git a/modules/invoiceOut/front/summary/index.spec.js b/modules/invoiceOut/front/summary/index.spec.js index be6ad0a18..675cc02c3 100644 --- a/modules/invoiceOut/front/summary/index.spec.js +++ b/modules/invoiceOut/front/summary/index.spec.js @@ -27,17 +27,5 @@ describe('InvoiceOut', () => { expect(controller.summary).toEqual('the data you are looking for'); }); }); - - describe('getTickets()', () => { - it('should perform a and then call to the ticketModel refresh() method', () => { - jest.spyOn(controller.$.ticketsModel, 'refresh'); - - controller.getTickets(); - $scope.$apply(); - - expect(controller.$.ticketsModel.url).toEqual('InvoiceOuts/1/getTickets'); - expect(controller.$.ticketsModel.refresh).toHaveBeenCalledWith(); - }); - }); }); }); diff --git a/modules/ticket/back/methods/sale/deleteSales.js b/modules/ticket/back/methods/sale/deleteSales.js index 31899a501..a604da858 100644 --- a/modules/ticket/back/methods/sale/deleteSales.js +++ b/modules/ticket/back/methods/sale/deleteSales.js @@ -6,18 +6,18 @@ module.exports = Self => { accessType: 'WRITE', accepts: [{ arg: 'sales', - type: ['Object'], + type: ['object'], required: true, description: 'The sales to remove' }, { arg: 'ticketId', - type: 'Number', + type: 'number', required: true, description: 'The ticket id' }], returns: { - type: ['Object'], + type: ['object'], root: true }, http: { @@ -27,10 +27,25 @@ module.exports = Self => { }); Self.deleteSales = async(ctx, sales, ticketId) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; const canEditSales = await models.Sale.canEdit(ctx, sales); + const ticket = await models.Ticket.findById(ticketId, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }); + const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId); if (!isTicketEditable) throw new UserError(`The sales of this ticket can't be modified`); @@ -39,11 +54,26 @@ module.exports = Self => { throw new UserError(`Sale(s) blocked, please contact production`); const promises = []; + let deletions = ''; for (let sale of sales) { const deletedSale = models.Sale.destroyById(sale.id); + deletions += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`; + promises.push(deletedSale); } + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Deleted sales from ticket', { + ticketId: ticketId, + ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, + deletions: deletions + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + return Promise.all(promises); }; }; diff --git a/modules/ticket/back/methods/sale/reserve.js b/modules/ticket/back/methods/sale/reserve.js index 96c794e7c..639827fa7 100644 --- a/modules/ticket/back/methods/sale/reserve.js +++ b/modules/ticket/back/methods/sale/reserve.js @@ -5,24 +5,27 @@ module.exports = Self => { Self.remoteMethodCtx('reserve', { description: 'Change the state of a ticket', accessType: 'WRITE', - accepts: [{ - arg: 'ticketId', - type: 'Number', - required: true, - description: 'The ticket id' - }, { - arg: 'sales', - type: ['Object'], - required: true, - description: 'The sale to reserve' - }, - { - arg: 'reserved', - type: 'Boolean', - required: true - }], + accepts: [ + { + arg: 'ticketId', + type: 'number', + required: true, + description: 'The ticket id' + }, + { + arg: 'sales', + type: ['object'], + required: true, + description: 'The sale to reserve' + }, + { + arg: 'reserved', + type: 'boolean', + required: true + } + ], returns: { - type: ['Object'], + type: ['object'], root: true }, http: { @@ -32,7 +35,9 @@ module.exports = Self => { }); Self.reserve = async(ctx, ticketId, sales, reserved) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; + const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId); if (!isTicketEditable) throw new UserError(`The sales of this ticket can't be modified`); @@ -42,12 +47,50 @@ module.exports = Self => { if (!canEditSale) throw new UserError(`Sale(s) blocked, please contact production`); + let changesMade = ''; const promises = []; + for (let sale of sales) { - const reservedSale = models.Sale.update({id: sale.id}, {reserved: reserved}); - promises.push(reservedSale); + if (sale.reserved != reserved) { + const oldState = sale.reserved ? 'reserved' : 'regular'; + const newState = reserved ? 'reserved' : 'regular'; + + const reservedSale = models.Sale.update({id: sale.id}, {reserved: reserved}); + + promises.push(reservedSale); + + changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`; + } } - return Promise.all(promises); + const result = await Promise.all(promises); + + const ticket = await models.Ticket.findById(ticketId, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }); + + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Changed sale reserved state', { + ticketId: ticketId, + ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, + changes: changesMade + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + + return result; }; }; diff --git a/modules/ticket/back/methods/sale/specs/deleteSales.spec.js b/modules/ticket/back/methods/sale/specs/deleteSales.spec.js index 22ac49452..aabc38375 100644 --- a/modules/ticket/back/methods/sale/specs/deleteSales.spec.js +++ b/modules/ticket/back/methods/sale/specs/deleteSales.spec.js @@ -17,7 +17,14 @@ describe('sale deleteSales()', () => { }); it('should throw an error if the ticket of the given sales is not editable', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + let ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; + let error; const sales = [{id: 1, instance: 0}, {id: 2, instance: 1}]; @@ -33,7 +40,13 @@ describe('sale deleteSales()', () => { }); it('should delete the sale', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + let ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const sales = [{id: newSale.id, instance: 0}]; const ticketId = 16; diff --git a/modules/ticket/back/methods/sale/specs/reserve.spec.js b/modules/ticket/back/methods/sale/specs/reserve.spec.js index fce7bd95e..3752a2b6b 100644 --- a/modules/ticket/back/methods/sale/specs/reserve.spec.js +++ b/modules/ticket/back/methods/sale/specs/reserve.spec.js @@ -1,7 +1,14 @@ const app = require('vn-loopback/server/server'); describe('sale reserve()', () => { - const ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; + afterAll(async done => { let ctx = {req: {accessToken: {userId: 9}}}; let params = { diff --git a/modules/ticket/back/methods/sale/specs/updatePrice.spec.js b/modules/ticket/back/methods/sale/specs/updatePrice.spec.js index ec4376adb..ea8a65c27 100644 --- a/modules/ticket/back/methods/sale/specs/updatePrice.spec.js +++ b/modules/ticket/back/methods/sale/specs/updatePrice.spec.js @@ -17,7 +17,13 @@ describe('sale updatePrice()', () => { }); it('should throw an error if the ticket is not editable', async() => { - let ctx = {req: {accessToken: {userId: 18}}}; + const ctx = { + req: { + accessToken: {userId: 18}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; let immutableSaleId = 1; let price = 5; @@ -31,7 +37,14 @@ describe('sale updatePrice()', () => { }); it('should return 0 if the price is an empty string', async() => { - let ctx = {req: {accessToken: {userId: 18}}}; + const ctx = { + req: { + accessToken: {userId: 18}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; + let price = ''; await app.models.Sale.updatePrice(ctx, saleId, price); @@ -46,7 +59,14 @@ describe('sale updatePrice()', () => { }); it('should now set price as a number in a string', async() => { - let ctx = {req: {accessToken: {userId: 18}}}; + const ctx = { + req: { + accessToken: {userId: 18}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; + let price = '8'; await app.models.Sale.updatePrice(ctx, saleId, price); diff --git a/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js b/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js index 16221b55c..dabdac384 100644 --- a/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js +++ b/modules/ticket/back/methods/sale/specs/updateQuantity.spec.js @@ -1,7 +1,13 @@ const app = require('vn-loopback/server/server'); describe('sale updateQuantity()', () => { - const ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; it('should throw an error if the quantity is not a number', async() => { let error; diff --git a/modules/ticket/back/methods/sale/updatePrice.js b/modules/ticket/back/methods/sale/updatePrice.js index 2195c2b7b..cb86296c9 100644 --- a/modules/ticket/back/methods/sale/updatePrice.js +++ b/modules/ticket/back/methods/sale/updatePrice.js @@ -19,7 +19,7 @@ module.exports = Self => { } ], returns: { - type: 'Number', + type: 'number', root: true }, http: { @@ -29,29 +29,37 @@ module.exports = Self => { }); Self.updatePrice = async(ctx, id, newPrice) => { - let models = Self.app.models; - let tx = await Self.beginTransaction({}); + const $t = ctx.req.__; // $translate + const models = Self.app.models; + const tx = await Self.beginTransaction({}); try { - let options = {transaction: tx}; + const options = {transaction: tx}; - let filter = { + const filter = { include: { relation: 'ticket', scope: { include: { relation: 'client', scope: { - fields: ['salesPersonFk'] + fields: ['salesPersonFk'], + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } } }, fields: ['id', 'clientFk'] } } }; - let sale = await models.Sale.findById(id, filter, options); - let isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); + const sale = await models.Sale.findById(id, filter, options); + + const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); if (!isEditable) throw new UserError(`The sales of this ticket can't be modified`); @@ -60,21 +68,19 @@ module.exports = Self => { if (!canEditSale) throw new UserError(`Sale(s) blocked, please contact production`); + const oldPrice = sale.price; const userId = ctx.req.accessToken.userId; + const usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, options); + const componentCode = usesMana ? 'mana' : 'buyerDiscount'; + const discount = await models.Component.findOne({where: {code: componentCode}}, options); + const componentId = discount.id; + const componentValue = newPrice - sale.price; - let usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, options); - - let componentCode = usesMana ? 'mana' : 'buyerDiscount'; - - let discount = await models.Component.findOne({where: {code: componentCode}}, options); - let componentId = discount.id; - let componentValue = newPrice - sale.price; - - let where = { + const where = { componentFk: componentId, saleFk: id }; - let saleComponent = await models.SaleComponent.findOne({where}, options); + const saleComponent = await models.SaleComponent.findOne({where}, options); if (saleComponent) { await models.SaleComponent.updateAll(where, { @@ -92,6 +98,22 @@ module.exports = Self => { query = `CALL vn.manaSpellersRequery(?)`; await Self.rawSql(query, [userId], options); + const salesPerson = sale.ticket().client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + const message = $t('Changed sale price', { + ticketId: sale.ticket().id, + itemId: sale.itemFk, + concept: sale.concept, + quantity: sale.quantity, + oldPrice: oldPrice, + newPrice: newPrice, + ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`, + itemUrl: `${origin}/#!/item/${sale.itemFk}/summary` + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + await tx.commit(); return sale; diff --git a/modules/ticket/back/methods/sale/updateQuantity.js b/modules/ticket/back/methods/sale/updateQuantity.js index 00df49b9f..62e09f1f5 100644 --- a/modules/ticket/back/methods/sale/updateQuantity.js +++ b/modules/ticket/back/methods/sale/updateQuantity.js @@ -26,7 +26,8 @@ module.exports = Self => { } }); - Self.updateQuantity = async(ctx, id, quantity) => { + Self.updateQuantity = async(ctx, id, newQuantity) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; const canEditSale = await models.Sale.canEdit(ctx, [id]); @@ -34,13 +35,51 @@ module.exports = Self => { if (!canEditSale) throw new UserError(`Sale(s) blocked, please contact production`); - if (isNaN(quantity)) + if (isNaN(newQuantity)) throw new UserError(`The value should be a number`); - let currentLine = await models.Sale.findOne({where: {id: id}}); - if (quantity > currentLine.quantity) + const filter = { + include: { + relation: 'ticket', + scope: { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + } + } + }; + + const sale = await models.Sale.findById(id, filter); + + if (newQuantity > sale.quantity) throw new UserError('The new quantity should be smaller than the old one'); - return await currentLine.updateAttributes({quantity: quantity}); + const oldQuantity = sale.quantity; + const result = await sale.updateAttributes({quantity: newQuantity}); + + const salesPerson = sale.ticket().client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + const message = $t('Changed sale quantity', { + ticketId: sale.ticket().id, + itemId: sale.itemFk, + concept: sale.concept, + oldQuantity: oldQuantity, + newQuantity: newQuantity, + ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`, + itemUrl: `${origin}/#!/item/${sale.itemFk}/summary` + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + + return result; }; }; diff --git a/modules/ticket/back/methods/ticket/addSale.js b/modules/ticket/back/methods/ticket/addSale.js index 365355df2..dc45e5de9 100644 --- a/modules/ticket/back/methods/ticket/addSale.js +++ b/modules/ticket/back/methods/ticket/addSale.js @@ -33,6 +33,7 @@ module.exports = Self => { }); Self.addSale = async(ctx, id, itemId, quantity) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; const isEditable = await models.Ticket.isEditable(ctx, id); @@ -40,7 +41,19 @@ module.exports = Self => { throw new UserError(`The sales of this ticket can't be modified`); const item = await models.Item.findById(itemId); - const ticket = await models.Ticket.findById(id); + const ticket = await models.Ticket.findById(id, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }); const res = await models.Item.getVisibleAvailable(itemId, ticket.warehouseFk, ticket.shipped); @@ -63,6 +76,20 @@ module.exports = Self => { } }); + const addition = `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`; + + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Added sale to ticket', { + ticketId: id, + ticketUrl: `${origin}/#!/ticket/${id}/sale`, + addition: addition + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + return sale; }; }; diff --git a/modules/ticket/back/methods/ticket/componentUpdate.js b/modules/ticket/back/methods/ticket/componentUpdate.js index b66179eb8..9ebd51bf4 100644 --- a/modules/ticket/back/methods/ticket/componentUpdate.js +++ b/modules/ticket/back/methods/ticket/componentUpdate.js @@ -230,7 +230,7 @@ module.exports = Self => { ticketUrl: `${origin}/#!/ticket/${args.id}/sale`, changes: changesMade }); - await models.Chat.sendCheckingPresence(ctx, salesPersonId, message, myOptions); + await models.Chat.sendCheckingPresence(ctx, salesPersonId, message); } if (tx) await tx.commit(); diff --git a/modules/ticket/back/methods/ticket/specs/addSale.spec.js b/modules/ticket/back/methods/ticket/specs/addSale.spec.js index c2650bf4b..9f6da7ed2 100644 --- a/modules/ticket/back/methods/ticket/specs/addSale.spec.js +++ b/modules/ticket/back/methods/ticket/specs/addSale.spec.js @@ -12,7 +12,13 @@ describe('ticket addSale()', () => { }); it('should create a new sale for the ticket with id 13', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const itemId = 4; const quantity = 10; newSale = await app.models.Ticket.addSale(ctx, ticketId, itemId, quantity); @@ -21,7 +27,13 @@ describe('ticket addSale()', () => { }); it('should not be able to add a sale if the item quantity is not available', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const itemId = 11; const quantity = 10; @@ -36,7 +48,13 @@ describe('ticket addSale()', () => { }); it('should not be able to add a sale if the ticket is not editable', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const notEditableTicketId = 1; const itemId = 4; const quantity = 10; diff --git a/modules/ticket/back/methods/ticket/specs/updateDiscount.spec.js b/modules/ticket/back/methods/ticket/specs/updateDiscount.spec.js index 2e71842b4..406a3f3ee 100644 --- a/modules/ticket/back/methods/ticket/specs/updateDiscount.spec.js +++ b/modules/ticket/back/methods/ticket/specs/updateDiscount.spec.js @@ -35,7 +35,13 @@ describe('sale updateDiscount()', () => { }); it('should throw an error if no sales were selected', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; let error; const ticketId = 11; const sales = []; @@ -51,7 +57,13 @@ describe('sale updateDiscount()', () => { }); it('should throw an error if no sales belong to different tickets', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; let error; const ticketId = 11; const sales = [1, 14]; @@ -67,7 +79,13 @@ describe('sale updateDiscount()', () => { }); it('should throw an error if the ticket is invoiced already', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; let error; const ticketId = 1; const sales = [1]; @@ -83,7 +101,13 @@ describe('sale updateDiscount()', () => { }); it('should update the discount if the salesPerson has mana', async() => { - let ctx = {req: {accessToken: {userId: 18}}}; + const ctx = { + req: { + accessToken: {userId: 18}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const ticketId = 11; const sales = [originalSaleId]; const newDiscount = 100; @@ -105,7 +129,13 @@ describe('sale updateDiscount()', () => { }); it('should update the discount and add company discount component if the worker does not have mana', async() => { - let ctx = {req: {accessToken: {userId: 9}}}; + const ctx = { + req: { + accessToken: {userId: 9}, + headers: {origin: 'localhost:5000'}, + __: () => {} + } + }; const ticketId = 11; const sales = [originalSaleId]; const newDiscount = 100; diff --git a/modules/ticket/back/methods/ticket/updateDiscount.js b/modules/ticket/back/methods/ticket/updateDiscount.js index 1bebaae8d..c9424395d 100644 --- a/modules/ticket/back/methods/ticket/updateDiscount.js +++ b/modules/ticket/back/methods/ticket/updateDiscount.js @@ -17,7 +17,8 @@ module.exports = Self => { description: 'The sales id', type: ['number'], required: true, - }, { + }, + { arg: 'newDiscount', description: 'The new discount', type: 'number', @@ -35,6 +36,7 @@ module.exports = Self => { }); Self.updateDiscount = async(ctx, id, salesIds, newDiscount) => { + const $t = ctx.req.__; // $translate const models = Self.app.models; const tx = await Self.beginTransaction({}); @@ -59,7 +61,7 @@ module.exports = Self => { } }; - let sales = await models.Sale.find(filter, options); + const sales = await models.Sale.find(filter, options); if (sales.length === 0) throw new UserError('Please select at least one sale'); @@ -94,8 +96,11 @@ module.exports = Self => { where: {code: componentCode}}, options); const componentId = discountComponent.id; - let promises = []; + const promises = []; + let changesMade = ''; + for (let sale of sales) { + const oldDiscount = sale.discount; const value = ((-sale.price * newDiscount) / 100); const newComponent = models.SaleComponent.upsert({ saleFk: sale.id, @@ -105,6 +110,7 @@ module.exports = Self => { const updatedSale = sale.updateAttribute('discount', newDiscount, options); promises.push(newComponent, updatedSale); + changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${oldDiscount}% ➔ *${newDiscount}%*`; } await Promise.all(promises); @@ -112,6 +118,32 @@ module.exports = Self => { const query = `call vn.manaSpellersRequery(?)`; await Self.rawSql(query, [userId], options); + const ticket = await models.Ticket.findById(id, { + include: { + relation: 'client', + scope: { + include: { + relation: 'salesPersonUser', + scope: { + fields: ['id', 'name'] + } + } + } + } + }, options); + + const salesPerson = ticket.client().salesPersonUser(); + if (salesPerson) { + const origin = ctx.req.headers.origin; + + const message = $t('Changed sale discount', { + ticketId: id, + ticketUrl: `${origin}/#!/ticket/${id}/sale`, + changes: changesMade + }); + await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); + } + await tx.commit(); } catch (error) { await tx.rollback(); diff --git a/modules/ticket/front/component/index.js b/modules/ticket/front/component/index.js index 307f8af15..3f262f457 100644 --- a/modules/ticket/front/component/index.js +++ b/modules/ticket/front/component/index.js @@ -82,7 +82,10 @@ class Controller extends Section { if (!this.ticket) return; this.$http.get(`Tickets/${this.ticket.id}/getVolume`) - .then(res => this.ticketVolume = res.data[0].volume); + .then(res => { + if (res.data.length) + this.ticketVolume = res.data[0].volume; + }); } } diff --git a/modules/ticket/front/sale/index.js b/modules/ticket/front/sale/index.js index 46cbe43c9..7e7654038 100644 --- a/modules/ticket/front/sale/index.js +++ b/modules/ticket/front/sale/index.js @@ -47,7 +47,13 @@ class Controller extends Section { getMana() { this.$http.get(`Tickets/${this.$params.id}/getSalesPersonMana`) - .then(res => this.edit.mana = res.data); + .then(res => { + this.edit.mana = res.data; + this.$.$applyAsync(() => { + this.$.editDiscount.relocate(); + this.$.editPricePopover.relocate(); + }); + }); } /** diff --git a/modules/ticket/front/sale/index.spec.js b/modules/ticket/front/sale/index.spec.js index e5368468a..169b41c5f 100644 --- a/modules/ticket/front/sale/index.spec.js +++ b/modules/ticket/front/sale/index.spec.js @@ -42,6 +42,8 @@ describe('Ticket', () => { $scope.sms = {open: () => {}}; $scope.ticket = ticket; $scope.model = crudModel; + $scope.editDiscount = {relocate: () => {}}; + $scope.editPricePopover = {relocate: () => {}}; $httpBackend = _$httpBackend_; Object.defineProperties($state.params, { id: {