Merge branch 'dev' of https://gitea.verdnatura.es/verdnatura/salix into 2840-ticket_card_sale_deprecations
gitea/salix/pipeline/head This commit looks good Details

This commit is contained in:
Carlos Jimenez Ruiz 2021-03-24 16:45:57 +01:00
commit 1cc4e60bad
9 changed files with 180 additions and 39 deletions

View File

@ -25,8 +25,8 @@ module.exports = function(Self) {
if (ctx.data) { if (ctx.data) {
const changes = pick(ctx.currentInstance, Object.keys(ctx.data)); const changes = pick(ctx.currentInstance, Object.keys(ctx.data));
newInstance = await fkToValue(ctx.data, ctx); newInstance = ctx.data;
oldInstance = await fkToValue(changes, ctx); oldInstance = changes;
if (ctx.where && !ctx.currentInstance) { if (ctx.where && !ctx.currentInstance) {
const fields = Object.keys(ctx.data); const fields = Object.keys(ctx.data);
@ -41,7 +41,7 @@ module.exports = function(Self) {
// Get changes from created instance // Get changes from created instance
if (ctx.isNewInstance) if (ctx.isNewInstance)
newInstance = await fkToValue(ctx.instance.__data, ctx); newInstance = ctx.instance.__data;
ctx.hookState.oldInstance = oldInstance; ctx.hookState.oldInstance = oldInstance;
ctx.hookState.newInstance = newInstance; ctx.hookState.newInstance = newInstance;
@ -261,6 +261,9 @@ module.exports = function(Self) {
removeUnloggable(definition, oldInstance); removeUnloggable(definition, oldInstance);
removeUnloggable(definition, newInstance); removeUnloggable(definition, newInstance);
oldInstance = await fkToValue(oldInstance, ctx);
newInstance = await fkToValue(newInstance, ctx);
// Prevent log with no new changes // Prevent log with no new changes
const hasNewChanges = Object.keys(newInstance).length; const hasNewChanges = Object.keys(newInstance).length;
if (!hasNewChanges) return; if (!hasNewChanges) return;

View File

@ -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}}*", "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}})", "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", "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"
} }

View File

@ -167,8 +167,8 @@
"Action not allowed on the test environment": "Esta acción no está permitida en el entorno de pruebas", "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", "The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta",
"Sorts whole route": "Reordena ruta entera", "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 <strong>{{shipped}}</strong>, con una cantidad de <strong>{{quantity}}</strong> y un precio de <strong>{{price}} €</strong>", "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 <strong>{{shipped}}</strong>, con una cantidad de <strong>{{quantity}}</strong>", "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", "Swift / BIC cannot be empty": "Swift / BIC no puede estar vacío",
"This BIC already exist.": "Este BIC ya existe.", "This BIC already exist.": "Este BIC ya existe.",
"That item doesn't exists": "Ese artículo no existe", "That item doesn't exists": "Ese artículo no existe",
@ -176,5 +176,7 @@
"Invalid account": "Cuenta inválida", "Invalid account": "Cuenta inválida",
"Compensation account is empty": "La cuenta para compensar está vacia", "Compensation account is empty": "La cuenta para compensar está vacia",
"This genus already exist": "Este genus ya existe", "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"
} }

View File

@ -227,36 +227,6 @@ module.exports = Self => {
await Self.app.models.ClientCredit.create(newCredit); 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 => { Self.observe('after save', async ctx => {
if (ctx.isNewInstance) return; if (ctx.isNewInstance) return;
@ -303,8 +273,58 @@ module.exports = Self => {
query: params 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) { async function validateCreditChange(ctx, finalState) {
let models = Self.app.models; let models = Self.app.models;
let userId = ctx.options.accessToken.userId; let userId = ctx.options.accessToken.userId;
@ -341,4 +361,34 @@ module.exports = Self => {
if (count <= 0) if (count <= 0)
throw new UserError('The role cannot set this credit amount'); 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);
}
});
});
}; };

View File

@ -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`);
});
});
});

View File

@ -1,5 +1,5 @@
<div class="vn-mx-xs" v-if="attachment.component"> <div class="vn-mx-xs" v-if="attachment.component">
<a target="_blank" class="vn-py-sm vn-px-md" v-bind:href="path"> <a target="_blank" class="vn-py-sm vn-px-md" v-bind:href="attachmentPath">
<div class="text">{{attachment.filename}}</div> <div class="text">{{attachment.filename}}</div>
<div class="icon vn-pl-md">&#x25BC;</div> <div class="icon vn-pl-md">&#x25BC;</div>
</a> </a>

View File

@ -1,7 +1,7 @@
module.exports = { module.exports = {
name: 'attachment', name: 'attachment',
computed: { computed: {
path() { attachmentPath() {
const filename = this.attachment.filename; const filename = this.attachment.filename;
const component = this.attachment.component; const component = this.attachment.component;
if (this.attachment.cid) if (this.attachment.cid)

View File

@ -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

View File

@ -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}