diff --git a/loopback/common/models/loggable.js b/loopback/common/models/loggable.js index 557aad66a..956762be2 100644 --- a/loopback/common/models/loggable.js +++ b/loopback/common/models/loggable.js @@ -25,8 +25,8 @@ module.exports = function(Self) { if (ctx.data) { const changes = pick(ctx.currentInstance, Object.keys(ctx.data)); - newInstance = await fkToValue(ctx.data, ctx); - oldInstance = await fkToValue(changes, ctx); + newInstance = ctx.data; + oldInstance = changes; if (ctx.where && !ctx.currentInstance) { const fields = Object.keys(ctx.data); @@ -41,7 +41,7 @@ module.exports = function(Self) { // Get changes from created instance if (ctx.isNewInstance) - newInstance = await fkToValue(ctx.instance.__data, ctx); + newInstance = ctx.instance.__data; ctx.hookState.oldInstance = oldInstance; ctx.hookState.newInstance = newInstance; @@ -261,6 +261,9 @@ module.exports = function(Self) { removeUnloggable(definition, oldInstance); removeUnloggable(definition, newInstance); + oldInstance = await fkToValue(oldInstance, ctx); + newInstance = await fkToValue(newInstance, ctx); + // Prevent log with no new changes const hasNewChanges = Object.keys(newInstance).length; if (!hasNewChanges) return; diff --git a/loopback/locale/en.json b/loopback/locale/en.json index 65028a8a8..c147e6c10 100644 --- a/loopback/locale/en.json +++ b/loopback/locale/en.json @@ -93,5 +93,7 @@ "New ticket request has been created": "New ticket request has been created *'{{description}}'* for day *{{shipped}}*, with a quantity of *{{quantity}}*", "There's a new urgent ticket": "There's a new urgent ticket: [{{title}}](https://cau.verdnatura.es/WorkOrder.do?woMode=viewWO&woID={{issueId}})", "Swift / BIC cannot be empty": "Swift / BIC cannot be empty", - "Role name must be written in camelCase": "Role name must be written in camelCase" + "Role name must be written in camelCase": "Role name must be written in camelCase", + "Client assignment has changed": "I did change the salesperson ~*\"<{{previousWorkerName}}>\"*~ by *\"<{{currentWorkerName}}>\"* from the client [{{clientName}} ({{clientId}})]({{{url}}})", + "None": "None" } \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 96858eb4a..79ebc1e54 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -167,8 +167,8 @@ "Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas", "The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta", "Sorts whole route": "Reordena ruta entera", - "New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día {{shipped}}, con una cantidad de {{quantity}} y un precio de {{price}} €", - "New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día {{shipped}}, con una cantidad de {{quantity}}", + "New ticket request has been created with price": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}* y un precio de *{{price}} €*", + "New ticket request has been created": "Se ha creado una nueva petición de compra '{{description}}' para el día *{{shipped}}*, con una cantidad de *{{quantity}}*", "Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío", "This BIC already exist.": "Este BIC ya existe.", "That item doesn't exists": "Ese artículo no existe", @@ -176,5 +176,7 @@ "Invalid account": "Cuenta inválida", "Compensation account is empty": "La cuenta para compensar está vacia", "This genus already exist": "Este genus ya existe", - "This specie already exist": "Esta especie ya existe" + "This specie already exist": "Esta especie ya existe", + "Client assignment has changed": "He cambiado el comercial ~*\"<{{previousWorkerName}}>\"*~ por *\"<{{currentWorkerName}}>\"* del cliente [{{clientName}} ({{clientId}})]({{{url}}})", + "None": "Ninguno" } \ No newline at end of file diff --git a/modules/client/back/models/client.js b/modules/client/back/models/client.js index 1e535a5d7..e1de363b2 100644 --- a/modules/client/back/models/client.js +++ b/modules/client/back/models/client.js @@ -227,36 +227,6 @@ module.exports = Self => { await Self.app.models.ClientCredit.create(newCredit); } }); - const app = require('vn-loopback/server/server'); - - app.on('started', function() { - let account = app.models.Account; - - account.observe('before save', async ctx => { - if (ctx.isNewInstance) return; - ctx.hookState.oldInstance = JSON.parse(JSON.stringify(ctx.currentInstance)); - }); - - account.observe('after save', async ctx => { - let changes = ctx.data || ctx.instance; - if (!ctx.isNewInstance && changes) { - let oldData = ctx.hookState.oldInstance; - let hasChanges = oldData.name != changes.name || oldData.active != changes.active; - if (!hasChanges) return; - - let userId = ctx.options.accessToken.userId; - let logRecord = { - originFk: oldData.id, - userFk: userId, - action: 'update', - changedModel: 'Account', - oldInstance: {name: oldData.name, active: oldData.active}, - newInstance: {name: changes.name, active: changes.active} - }; - await Self.app.models.ClientLog.create(logRecord); - } - }); - }); Self.observe('after save', async ctx => { if (ctx.isNewInstance) return; @@ -303,8 +273,58 @@ module.exports = Self => { query: params }); } + + const workerIdBefore = oldInstance.salesPersonFk; + const workerIdAfter = newInstance.salesPersonFk; + const assignmentChanged = workerIdBefore != workerIdAfter; + if (assignmentChanged) + await Self.notifyAssignment(instance, workerIdBefore, workerIdAfter); }); + // Send notification on client worker assignment + Self.notifyAssignment = async function notifyAssignment(client, previousWorkerId, currentWorkerId) { + const loopBackContext = LoopBackContext.getCurrentContext(); + const httpCtx = {req: loopBackContext.active}; + const httpRequest = httpCtx.req.http.req; + const $t = httpRequest.__; + const headers = httpRequest.headers; + const origin = headers.origin; + const models = Self.app.models; + + let previousWorker = {name: $t('None')}; + let currentWorker = {name: $t('None')}; + if (previousWorkerId) { + const worker = await models.Worker.findById(previousWorkerId, { + include: {relation: 'user'} + }); + previousWorker.user = worker && worker.user().name; + previousWorker.name = worker && worker.user().nickname; + } + + if (currentWorkerId) { + const worker = await models.Worker.findById(currentWorkerId, { + include: {relation: 'user'} + }); + currentWorker.user = worker && worker.user().name; + currentWorker.name = worker && worker.user().nickname; + } + + const fullUrl = `${origin}/#!/client/${client.id}/basic-data`; + const message = $t('Client assignment has changed', { + clientId: client.id, + clientName: client.name, + url: fullUrl, + previousWorkerName: previousWorker.name, + currentWorkerName: currentWorker.name + }); + + if (previousWorkerId) + await models.Chat.send(httpCtx, `@${previousWorker.user}`, message); + + if (currentWorkerId) + await models.Chat.send(httpCtx, `@${currentWorker.user}`, message); + }; + async function validateCreditChange(ctx, finalState) { let models = Self.app.models; let userId = ctx.options.accessToken.userId; @@ -341,4 +361,34 @@ module.exports = Self => { if (count <= 0) throw new UserError('The role cannot set this credit amount'); } + + const app = require('vn-loopback/server/server'); + app.on('started', function() { + let account = app.models.Account; + + account.observe('before save', async ctx => { + if (ctx.isNewInstance) return; + ctx.hookState.oldInstance = JSON.parse(JSON.stringify(ctx.currentInstance)); + }); + + account.observe('after save', async ctx => { + let changes = ctx.data || ctx.instance; + if (!ctx.isNewInstance && changes) { + let oldData = ctx.hookState.oldInstance; + let hasChanges = oldData.name != changes.name || oldData.active != changes.active; + if (!hasChanges) return; + + let userId = ctx.options.accessToken.userId; + let logRecord = { + originFk: oldData.id, + userFk: userId, + action: 'update', + changedModel: 'Account', + oldInstance: {name: oldData.name, active: oldData.active}, + newInstance: {name: changes.name, active: changes.active} + }; + await Self.app.models.ClientLog.create(logRecord); + } + }); + }); }; diff --git a/modules/client/back/models/specs/client.spec.js b/modules/client/back/models/specs/client.spec.js new file mode 100644 index 000000000..a9d479516 --- /dev/null +++ b/modules/client/back/models/specs/client.spec.js @@ -0,0 +1,54 @@ +const app = require('vn-loopback/server/server'); +const LoopBackContext = require('loopback-context'); + +describe('Client Model', () => { + const activeCtx = { + accessToken: {userId: 9}, + http: { + req: { + headers: {origin: 'http://localhost'}, + [`__`]: value => { + return value; + } + } + } + }; + const ctx = {req: activeCtx}; + const chatModel = app.models.Chat; + const client = {id: 101, name: 'Bruce Banner'}; + const previousWorkerId = 106; // DavidCharlesHaller + const currentWorkerId = 107; // HankPym + + beforeEach(() => { + spyOn(LoopBackContext, 'getCurrentContext').and.returnValue({ + active: activeCtx + }); + }); + + describe('notifyAssignment()', () => { + it('should call to the Chat send() method for both workers', async() => { + spyOn(chatModel, 'send').and.callThrough(); + + await app.models.Client.notifyAssignment(client, previousWorkerId, currentWorkerId); + + expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`); + expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`); + }); + + it('should call to the Chat send() method for the previous worker', async() => { + spyOn(chatModel, 'send').and.callThrough(); + + await app.models.Client.notifyAssignment(client, null, currentWorkerId); + + expect(chatModel.send).toHaveBeenCalledWith(ctx, '@HankPym', `Client assignment has changed`); + }); + + it('should call to the Chat send() method for the current worker', async() => { + spyOn(chatModel, 'send').and.callThrough(); + + await app.models.Client.notifyAssignment(client, previousWorkerId, null); + + expect(chatModel.send).toHaveBeenCalledWith(ctx, '@DavidCharlesHaller', `Client assignment has changed`); + }); + }); +}); diff --git a/print/core/components/attachment/attachment.html b/print/core/components/attachment/attachment.html index d66746e99..88fa64434 100644 --- a/print/core/components/attachment/attachment.html +++ b/print/core/components/attachment/attachment.html @@ -1,5 +1,5 @@
- +
{{attachment.filename}}
diff --git a/print/core/components/attachment/attachment.js b/print/core/components/attachment/attachment.js index 0f2a6d103..5c78a895c 100755 --- a/print/core/components/attachment/attachment.js +++ b/print/core/components/attachment/attachment.js @@ -1,7 +1,7 @@ module.exports = { name: 'attachment', computed: { - path() { + attachmentPath() { const filename = this.attachment.filename; const component = this.attachment.component; if (this.attachment.cid) diff --git a/print/templates/email/letter-debtor-st/locale/fr.yml b/print/templates/email/letter-debtor-st/locale/fr.yml new file mode 100644 index 000000000..a8da98a80 --- /dev/null +++ b/print/templates/email/letter-debtor-st/locale/fr.yml @@ -0,0 +1,20 @@ +subject: Avis initial de solde débiteur +title: Avis initial de solde débiteur +sections: + introduction: + title: Madame, Monsieur, + description: Sauf erreur ou omission de notre part, nous constatons que votre + compte client présente à ce jour un solde débiteur. +checkExtract: Ce montant correspond à nos factures restées impayées, ci-joint en annexe. + Notre service administratif se fera un plaisir de clarifier toutes les questions que vous + pourriez avoir, et vous fournira également tout document que vous nous demandez. +checkValidData: Si lors de la vérification des données fournies, elles sont correctes et + l’échéance étant dépassée, nous vous demandons de bien vouloir régulariser cette situation. +payMethod: Si vous ne souhaitez pas vous rendre personnellement à nos bureaux, vous + pouvez effectuer le paiement par virement bancaire sur le compte qui apparaît en + bas du relevé, en indiquant votre numéro de client, ou vous pouvez effectuer le + paiement en ligne avec une carte bleue sur notre site Internet. +conclusion: Dans le cas où votre règlement aurait été adressé entre temps, + nous vous prions de ne pas tenir compte de la présente. + Nous vous remercions par avance de votre aimable coopération. +transferAccount: Coordonées pour virement bancaire diff --git a/print/templates/reports/letter-debtor/locale/fr.yml b/print/templates/reports/letter-debtor/locale/fr.yml new file mode 100644 index 000000000..eff39d783 --- /dev/null +++ b/print/templates/reports/letter-debtor/locale/fr.yml @@ -0,0 +1,10 @@ +title: Relevé de compte +claimId: Réclamation +clientId: Client +clientData: Données client +date: Date +concept: Objet +invoiced: Facturé +payed: Payé +balance: Solde +client: Client {0} \ No newline at end of file