refs #5423 añadido 3r decimal #1392

Merged
joan merged 4 commits from #5423-tercerDecimalCoste into dev 2023-03-21 10:22:39 +00:00
14 changed files with 398 additions and 388 deletions
Showing only changes of commit da05140635 - Show all commits

View File

@ -5,14 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2312.01] - 2023-04-06
### Added
-
### Changed
-
### Fixed
-
## [2310.01] - 2023-03-23 ## [2310.01] - 2023-03-23
### Added ### Added
- (Trabajadores -> Control de horario) Ahora se puede confirmar/no confirmar el registro horario de cada semana desde esta sección - (Trabajadores -> Control de horario) Ahora se puede confirmar/no confirmar el registro horario de cada semana desde esta sección
### Changed
-
### Fixed ### Fixed
- (Clientes -> Listado extendido) Resuelto error al filtrar por clientes inactivos desde la columna "Activo" - (Clientes -> Listado extendido) Resuelto error al filtrar por clientes inactivos desde la columna "Activo"
- (General) Al pasar el ratón por encima del icono de "Borrar" en un campo, se hacía más grande afectando a la interfaz - (General) Al pasar el ratón por encima del icono de "Borrar" en un campo, se hacía más grande afectando a la interfaz

View File

View File

@ -81220,3 +81220,4 @@ USE `vn`;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2023-02-21 8:14:30 -- Dump completed on 2023-02-21 8:14:30

View File

@ -524,7 +524,7 @@ export default {
}, },
itemLog: { itemLog: {
anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr', anyLineCreated: 'vn-item-log > vn-log vn-tbody > vn-tr',
fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(2) td.after', fifthLineCreatedProperty: 'vn-item-log > vn-log vn-tbody > vn-tr:nth-child(5) table tr:nth-child(4) td.after',
}, },
ticketSummary: { ticketSummary: {
header: 'vn-ticket-summary > vn-card > h5', header: 'vn-ticket-summary > vn-card > h5',

View File

@ -59,6 +59,6 @@ describe('Item log path', () => {
const fifthLineCreatedProperty = await page const fifthLineCreatedProperty = await page
.waitToGetProperty(selectors.itemLog.fifthLineCreatedProperty, 'innerText'); .waitToGetProperty(selectors.itemLog.fifthLineCreatedProperty, 'innerText');
expect(fifthLineCreatedProperty).toEqual('Coral y materiales similares'); expect(fifthLineCreatedProperty).toEqual('05080000');
}); });
}); });

View File

@ -37,6 +37,6 @@ describe('Zone descriptor path', () => {
await page.accessToSection('ticket.card.log'); await page.accessToSection('ticket.card.log');
const lastChanges = await page.waitToGetProperty(selectors.ticketLog.changes, 'innerText'); const lastChanges = await page.waitToGetProperty(selectors.ticketLog.changes, 'innerText');
expect(lastChanges).toContain('Arreglar'); expect(lastChanges).toContain('1');
}); });
}); });

View File

@ -1,4 +1,3 @@
const pick = require('object.pick');
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
module.exports = function(Self) { module.exports = function(Self) {
@ -6,344 +5,11 @@ module.exports = function(Self) {
Self.super_.setup.call(this); Self.super_.setup.call(this);
}; };
Self.observe('after save', async function(ctx) {
const loopBackContext = LoopBackContext.getCurrentContext();
await logInModel(ctx, loopBackContext);
});
Self.observe('before save', async function(ctx) { Self.observe('before save', async function(ctx) {
const appModels = ctx.Model.app.models; ctx.options.httpCtx = LoopBackContext.getCurrentContext();
const definition = ctx.Model.definition;
const options = {};
// Check for transactions
if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction;
let oldInstance;
let newInstance;
if (ctx.data) {
const changes = pick(ctx.currentInstance, Object.keys(ctx.data));
newInstance = ctx.data;
oldInstance = changes;
if (ctx.where && !ctx.currentInstance) {
const fields = Object.keys(ctx.data);
const modelName = definition.name;
ctx.oldInstances = await appModels[modelName].find({
where: ctx.where,
fields: fields
}, options);
}
}
// Get changes from created instance
if (ctx.isNewInstance)
newInstance = ctx.instance.__data;
ctx.hookState.oldInstance = oldInstance;
ctx.hookState.newInstance = newInstance;
}); });
Self.observe('before delete', async function(ctx) { Self.observe('before delete', async function(ctx) {
const appModels = ctx.Model.app.models; ctx.options.httpCtx = LoopBackContext.getCurrentContext();
const definition = ctx.Model.definition;
const relations = ctx.Model.relations;
let options = {};
if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction;
if (ctx.where) {
let affectedModel = definition.name;
let deletedInstances = await appModels[affectedModel].find({
where: ctx.where
}, options);
let relation = definition.settings.log.relation;
if (relation) {
let primaryKey = relations[relation].keyFrom;
let arrangedDeletedInstances = [];
for (let i = 0; i < deletedInstances.length; i++) {
if (primaryKey)
deletedInstances[i].originFk = deletedInstances[i][primaryKey];
let arrangedInstance = await fkToValue(deletedInstances[i], ctx);
arrangedDeletedInstances[i] = arrangedInstance;
}
ctx.hookState.oldInstance = arrangedDeletedInstances;
}
}
}); });
Self.observe('after delete', async function(ctx) {
const loopBackContext = LoopBackContext.getCurrentContext();
if (ctx.hookState.oldInstance)
logDeletedInstances(ctx, loopBackContext);
});
async function logDeletedInstances(ctx, loopBackContext) {
const appModels = ctx.Model.app.models;
const definition = ctx.Model.definition;
let options = {};
if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction;
ctx.hookState.oldInstance.forEach(async instance => {
let userFk;
if (loopBackContext)
userFk = loopBackContext.active.accessToken.userId;
let changedModelValue = definition.settings.log.changedModelValue;
let logRecord = {
originFk: instance.originFk,
userFk: userFk,
action: 'delete',
changedModel: definition.name,
changedModelId: instance.id,
changedModelValue: instance[changedModelValue],
oldInstance: instance,
newInstance: {}
};
delete instance.originFk;
let logModel = definition.settings.log.model;
await appModels[logModel].create(logRecord, options);
});
}
// Get log values from a foreign key
async function fkToValue(instance, ctx) {
const appModels = ctx.Model.app.models;
const relations = ctx.Model.relations;
let options = {};
// Check for transactions
if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction;
const instanceCopy = JSON.parse(JSON.stringify(instance));
const result = {};
for (const key in instanceCopy) {
let value = instanceCopy[key];
if (value instanceof Object)
continue;
if (value === undefined) continue;
if (value) {
for (let relationName in relations) {
const relation = relations[relationName];
if (relation.keyFrom == key && key != 'id') {
const model = relation.modelTo;
const modelName = relation.modelTo.modelName;
const properties = model && model.definition.properties;
const settings = model && model.definition.settings;
const recordSet = await appModels[modelName].findById(value, null, options);
const hasShowField = settings.log && settings.log.showField;
let showField = hasShowField && recordSet
&& recordSet[settings.log.showField];
if (!showField) {
const showFieldNames = [
'name',
'description',
'code',
'nickname'
];
for (field of showFieldNames) {
const propField = properties && properties[field];
const recordField = recordSet && recordSet[field];
if (propField && recordField) {
showField = field;
break;
}
}
}
if (showField && recordSet && recordSet[showField]) {
value = recordSet[showField];
break;
}
value = recordSet && recordSet.id || value;
break;
}
}
}
result[key] = value;
}
return result;
}
async function logInModel(ctx, loopBackContext) {
const appModels = ctx.Model.app.models;
const definition = ctx.Model.definition;
const defSettings = ctx.Model.definition.settings;
const relations = ctx.Model.relations;
const options = {};
if (ctx.options && ctx.options.transaction)
options.transaction = ctx.options.transaction;
let primaryKey;
for (let property in definition.properties) {
if (definition.properties[property].id) {
primaryKey = property;
break;
}
}
if (!primaryKey) throw new Error('Primary key not found');
let originId;
// RELATIONS LOG
let changedModelId;
if (ctx.instance && !defSettings.log.relation) {
originId = ctx.instance.id;
changedModelId = ctx.instance.id;
} else if (defSettings.log.relation) {
primaryKey = relations[defSettings.log.relation].keyFrom;
if (ctx.where && ctx.where[primaryKey])
originId = ctx.where[primaryKey];
else if (ctx.instance) {
originId = ctx.instance[primaryKey];
changedModelId = ctx.instance.id;
}
} else {
originId = ctx.currentInstance.id;
changedModelId = ctx.currentInstance.id;
}
// Sets the changedModelValue to save and the instances changed in case its an updateAll
let showField = defSettings.log.showField;
let where;
if (showField && (!ctx.instance || !ctx.instance[showField]) && ctx.where) {
changedModelId = [];
where = [];
let changedInstances = await appModels[definition.name].find({
where: ctx.where,
fields: ['id', showField, primaryKey]
}, options);
changedInstances.forEach(element => {
where.push(element[showField]);
changedModelId.push(element.id);
originId = element[primaryKey];
});
} else if (ctx.hookState.oldInstance)
where = ctx.instance[showField];
// Set oldInstance, newInstance, userFk and action
let oldInstance = {};
if (ctx.hookState.oldInstance)
Object.assign(oldInstance, ctx.hookState.oldInstance);
let newInstance = {};
if (ctx.hookState.newInstance)
Object.assign(newInstance, ctx.hookState.newInstance);
let userFk;
if (loopBackContext)
userFk = loopBackContext.active.accessToken.userId;
let action = setActionType(ctx);
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;
let logRecord = {
originFk: originId,
userFk: userFk,
action: action,
changedModel: definition.name,
changedModelId: changedModelId, // Model property with an different data type will throw a NaN error
changedModelValue: where,
oldInstance: oldInstance,
newInstance: newInstance
};
let logsToSave = setLogsToSave(where, changedModelId, logRecord, ctx);
let logModel = defSettings.log.model;
await appModels[logModel].create(logsToSave, options);
}
/**
* Removes unwanted properties
* @param {*} definition Model definition
* @param {*} properties Modified object properties
*/
function removeUnloggable(definition, properties) {
const objectCopy = Object.assign({}, properties);
const propList = Object.keys(objectCopy);
const propDefs = new Map();
for (let property in definition.properties) {
const propertyDef = definition.properties[property];
propDefs.set(property, propertyDef);
}
for (let property of propList) {
const propertyDef = propDefs.get(property);
const firstChar = property.substring(0, 1);
const isPrivate = firstChar == '$';
if (isPrivate || !propertyDef)
delete properties[property];
if (!propertyDef) continue;
if (propertyDef.log === false || isPrivate)
delete properties[property];
else if (propertyDef.logValue === false)
properties[property] = null;
}
}
// this function retuns all the instances changed in case this is an updateAll
function setLogsToSave(changedInstances, changedInstancesIds, logRecord, ctx) {
let promises = [];
if (changedInstances && typeof changedInstances == 'object') {
for (let i = 0; i < changedInstances.length; i++) {
logRecord.changedModelId = changedInstancesIds[i];
logRecord.changedModelValue = changedInstances[i];
if (ctx.oldInstances)
logRecord.oldInstance = ctx.oldInstances[i];
promises.push(JSON.parse(JSON.stringify(logRecord)));
}
} else
return logRecord;
return promises;
}
function setActionType(ctx) {
let oldInstance = ctx.hookState.oldInstance;
let newInstance = ctx.hookState.newInstance;
if (oldInstance && newInstance)
return 'update';
else if (!oldInstance && newInstance)
return 'insert';
return 'delete';
}
}; };

View File

@ -2,8 +2,41 @@ const mysql = require('mysql');
const ParameterizedSQL = require('loopback-connector').ParameterizedSQL; const ParameterizedSQL = require('loopback-connector').ParameterizedSQL;
const MySQL = require('loopback-connector-mysql').MySQL; const MySQL = require('loopback-connector-mysql').MySQL;
const EnumFactory = require('loopback-connector-mysql').EnumFactory; const EnumFactory = require('loopback-connector-mysql').EnumFactory;
const Transaction = require('loopback-connector').Transaction;
const fs = require('fs'); const fs = require('fs');
const limitSet = new Set([
'save',
'updateOrCreate',
'replaceOrCreate',
'replaceById',
'update'
]);
const opOpts = {
update: [
'update',
'replaceById',
// |insert
'save',
'updateOrCreate',
'replaceOrCreate'
],
delete: [
'destroy',
'destroyAll'
],
insert: [
'create'
]
};
const opMap = new Map();
for (const op in opOpts) {
for (const met of opOpts[op])
opMap.set(met, op);
}
class VnMySQL extends MySQL { class VnMySQL extends MySQL {
/** /**
* Promisified version of execute(). * Promisified version of execute().
@ -219,6 +252,277 @@ class VnMySQL extends MySQL {
this.makePagination(filter) this.makePagination(filter)
]); ]);
} }
create(model, data, opts, cb) {
const ctx = {data};
this.invokeMethod('create',
arguments, model, ctx, opts, cb);
}
createAll(model, data, opts, cb) {
const ctx = {data};
this.invokeMethod('createAll',
arguments, model, ctx, opts, cb);
}
save(model, data, opts, cb) {
const ctx = {data};
this.invokeMethod('save',
arguments, model, ctx, opts, cb);
}
updateOrCreate(model, data, opts, cb) {
const ctx = {data};
this.invokeMethod('updateOrCreate',
arguments, model, ctx, opts, cb);
}
replaceOrCreate(model, data, opts, cb) {
const ctx = {data};
this.invokeMethod('replaceOrCreate',
arguments, model, ctx, opts, cb);
}
destroyAll(model, where, opts, cb) {
const ctx = {where};
this.invokeMethod('destroyAll',
arguments, model, ctx, opts, cb);
}
update(model, where, data, opts, cb) {
const ctx = {where, data};
this.invokeMethod('update',
arguments, model, ctx, opts, cb);
}
replaceById(model, id, data, opts, cb) {
const ctx = {id, data};
this.invokeMethod('replaceById',
arguments, model, ctx, opts, cb);
}
isLoggable(model) {
const Model = this.getModelDefinition(model).model;
const settings = Model.definition.settings;
return settings.base && settings.base === 'Loggable';
}
invokeMethod(method, args, model, ctx, opts, cb) {
if (!this.isLoggable(model))
return super[method].apply(this, args);
this.invokeMethodP(method, [...args], model, ctx, opts)
.then(res => cb(...res), cb);
}
async invokeMethodP(method, args, model, ctx, opts) {
const Model = this.getModelDefinition(model).model;
const settings = Model.definition.settings;
let tx;
if (!opts.transaction) {
tx = await Transaction.begin(this, {});
opts = Object.assign({transaction: tx, httpCtx: opts.httpCtx}, opts);
}
try {
// Fetch old values (update|delete) or login
let where, id, data, idName, limit, op, oldInstances, newInstances;
const hasGrabUser = settings.log && settings.log.grabUser;
if(hasGrabUser){
const userId = opts.httpCtx && opts.httpCtx.active.accessToken.userId;
const user = await Model.app.models.Account.findById(userId, {fields: ['name']}, opts);
await this.executeP(`CALL account.myUser_loginWithName(?)`, [user.name], opts);
}
else {
where = ctx.where;
id = ctx.id;
data = ctx.data;
idName = this.idName(model);
limit = limitSet.has(method);
op = opMap.get(method);
if (!where) {
if (id) where = {[idName]: id};
else where = {[idName]: data[idName]};
}
// Fetch old values
switch (op) {
case 'update':
case 'delete':
// Single entity operation
const stmt = this.buildSelectStmt(op, data, idName, model, where, limit);
stmt.merge(`FOR UPDATE`);
oldInstances = await this.executeStmt(stmt, opts);
}
}
const res = await new Promise(resolve => {
const fnArgs = args.slice(0, -2);
fnArgs.push(opts, (...args) => resolve(args));
super[method].apply(this, fnArgs);
});
if(hasGrabUser)
await this.executeP(`CALL account.myUser_logout()`, null, opts);
else {
// Fetch new values
const ids = [];
switch (op) {
case 'insert':
case 'update': {
switch (method) {
case 'createAll':
for (const row of res[1])
ids.push(row[idName]);
break;
case 'create':
ids.push(res[1]);
break;
case 'update':
if (data[idName] != null)
ids.push(data[idName]);
break;
}
const newWhere = ids.length ? {[idName]: ids} : where;
const stmt = this.buildSelectStmt(op, data, idName, model, newWhere, limit);
newInstances = await this.executeStmt(stmt, opts);
}
}
await this.createLogRecord(oldInstances, newInstances, model, opts);
}
if (tx) await tx.commit();
return res;
} catch (err) {
if (tx) tx.rollback();
throw err;
}
}
buildSelectStmt(op, data, idName, model, where, limit) {
const Model = this.getModelDefinition(model).model;
const properties = Object.keys(Model.definition.properties);
const fields = data ? Object.keys(data) : [];
if (op == 'delete')
properties.forEach(property => fields.push(property));
else {
const log = Model.definition.settings.log;
fields.push(idName);
if (log.relation) fields.push(Model.relations[log.relation].keyFrom);
if (log.showField) fields.push(log.showField);
else {
const showFieldNames = ['name', 'description', 'code', 'nickname'];
for (const field of showFieldNames) {
if (properties.includes(field)) {
log.showField = field;
fields.push(field);
break;
}
}
}
}
const stmt = new ParameterizedSQL(
'SELECT ' +
this.buildColumnNames(model, {fields}) +
' FROM ' +
this.tableEscaped(model)
);
stmt.merge(this.buildWhere(model, where));
if (limit) stmt.merge(`LIMIT 1`);
return stmt;
}
async createLogRecord(oldInstances, newInstances, model, opts) {
function setActionType() {
if (oldInstances && newInstances)
return 'update';
else if (!oldInstances && newInstances)
return 'insert';
return 'delete';
}
const action = setActionType();
if (!newInstances && action != 'delete') return;
const Model = this.getModelDefinition(model).model;
const models = Model.app.models;
const definition = Model.definition;
const log = definition.settings.log;
const primaryKey = this.idName(model);
const originRelation = log.relation;
const originFkField = originRelation
? Model.relations[originRelation].keyFrom
: primaryKey;
// Prevent adding logs when deleting a principal entity (Client, Zone...)
if (action == 'delete' && !originRelation) return;
function map(instances) {
const map = new Map();
if (!instances) return;
for (const instance of instances)
map.set(instance[primaryKey], instance);
return map;
}
const changedModel = definition.name;
const userFk = opts.httpCtx && opts.httpCtx.active.accessToken.userId;
const oldMap = map(oldInstances);
const newMap = map(newInstances);
const ids = (oldMap || newMap).keys();
const logEntries = [];
function insertValuesLogEntry(logEntry, instance) {
logEntry.originFk = instance[originFkField];
logEntry.changedModelId = instance[primaryKey];
if (log.showField) logEntry.changedModelValue = instance[log.showField];
}
for (const id of ids) {
const oldI = oldMap && oldMap.get(id);
const newI = newMap && newMap.get(id);
const logEntry = {
action,
userFk,
changedModel,
};
if (newI) {
insertValuesLogEntry(logEntry, newI);
// Delete unchanged properties
if (oldI) {
Object.keys(oldI).forEach(prop => {
const hasChanges = oldI[prop] instanceof Date ?
oldI[prop]?.getTime() != newI[prop]?.getTime() :
oldI[prop] != newI[prop];
if (!hasChanges) {
delete oldI[prop];
delete newI[prop];
}
});
}
} else
insertValuesLogEntry(logEntry, oldI);
logEntry.oldInstance = oldI;
logEntry.newInstance = newI;
logEntries.push(logEntry);
}
await models[log.model].create(logEntries, opts);
}
} }
exports.VnMySQL = VnMySQL; exports.VnMySQL = VnMySQL;

View File

@ -91,7 +91,11 @@ exports.getChanges = (original, changes) => {
const isPrivate = firstChar == '$'; const isPrivate = firstChar == '$';
if (isPrivate) return; if (isPrivate) return;
if (changes[property] != original[property]) { const hasChanges = original[property] instanceof Date ?
changes[property]?.getTime() != original[property]?.getTime() :
changes[property] != original[property];
if (hasChanges) {
newChanges[property] = changes[property]; newChanges[property] = changes[property];
if (original[property] != undefined) if (original[property] != undefined)

View File

@ -279,6 +279,18 @@ module.exports = Self => {
// Credit changes // Credit changes
if (changes.credit !== undefined) if (changes.credit !== undefined)
await Self.changeCredit(ctx, finalState, changes); await Self.changeCredit(ctx, finalState, changes);
const oldInstance = {};
if (!ctx.isNewInstance) {
const newProps = Object.keys(changes);
Object.keys(orgData.__data).forEach(prop => {
if (newProps.includes(prop))
oldInstance[prop] = orgData[prop];
});
}
ctx.hookState.oldInstance = oldInstance;
ctx.hookState.newInstance = changes;
}); });
Self.observe('after save', async ctx => { Self.observe('after save', async ctx => {

View File

@ -165,18 +165,29 @@ module.exports = Self => {
'shipped', 'shipped',
'landed', 'landed',
'isDeleted', 'isDeleted',
'routeFk' 'routeFk',
'nickname'
], ],
include: [ include: [
{ {
relation: 'client', relation: 'client',
scope: { scope: {
fields: 'salesPersonFk' fields: 'salesPersonFk'
} },
}] include: [
{
relation: 'address',
scope: {
fields: 'nickname'
}
}
]
},
]
}, myOptions); }, myOptions);
args.routeFk = null; args.routeFk = null;
if (args.isWithoutNegatives === false) delete args.isWithoutNegatives;
const updatedTicket = Object.assign({}, args); const updatedTicket = Object.assign({}, args);
delete updatedTicket.ctx; delete updatedTicket.ctx;
delete updatedTicket.option; delete updatedTicket.option;
@ -224,37 +235,41 @@ module.exports = Self => {
} }
const changes = loggable.getChanges(originalTicket, updatedTicket); const changes = loggable.getChanges(originalTicket, updatedTicket);
const oldProperties = await loggable.translateValues(Self, changes.old); const hasChanges = Object.keys(changes.old).length > 0 || Object.keys(changes.new).length > 0;
const newProperties = await loggable.translateValues(Self, changes.new);
await models.TicketLog.create({ if (hasChanges) {
originFk: args.id, const oldProperties = await loggable.translateValues(Self, changes.old);
userFk: userId, const newProperties = await loggable.translateValues(Self, changes.new);
action: 'update',
changedModel: 'Ticket',
changedModelId: args.id,
oldInstance: oldProperties,
newInstance: newProperties
}, myOptions);
const salesPersonId = originalTicket.client().salesPersonFk; await models.TicketLog.create({
if (salesPersonId) { originFk: args.id,
const origin = ctx.req.headers.origin; userFk: userId,
action: 'update',
changedModel: 'Ticket',
changedModelId: args.id,
oldInstance: oldProperties,
newInstance: newProperties
}, myOptions);
let changesMade = ''; const salesPersonId = originalTicket.client().salesPersonFk;
for (let change in newProperties) { if (salesPersonId) {
let value = newProperties[change]; const origin = ctx.req.headers.origin;
let oldValue = oldProperties[change];
changesMade += `\r\n~${$t(change)}: ${oldValue}~ ➔ *${$t(change)}: ${value}*`; let changesMade = '';
for (let change in newProperties) {
let value = newProperties[change];
let oldValue = oldProperties[change];
changesMade += `\r\n~${$t(change)}: ${oldValue}~ ➔ *${$t(change)}: ${value}*`;
}
const message = $t('Changed this data from the ticket', {
ticketId: args.id,
ticketUrl: `${origin}/#!/ticket/${args.id}/sale`,
changes: changesMade
});
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
} }
const message = $t('Changed this data from the ticket', {
ticketId: args.id,
ticketUrl: `${origin}/#!/ticket/${args.id}/sale`,
changes: changesMade
});
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
} }
res.id = args.id; res.id = args.id;

View File

@ -2,13 +2,13 @@ const models = require('vn-loopback/server/server').models;
const LoopBackContext = require('loopback-context'); const LoopBackContext = require('loopback-context');
describe('ticket merge()', () => { describe('ticket merge()', () => {
const tickets = [{ const tickets = {
originId: 13, originId: 13,
destinationId: 12, destinationId: 12,
originShipped: Date.vnNew(), originShipped: Date.vnNew(),
destinationShipped: Date.vnNew(), destinationShipped: Date.vnNew(),
workerFk: 1 workerFk: 1
}]; };
const activeCtx = { const activeCtx = {
accessToken: {userId: 9}, accessToken: {userId: 9},
@ -37,14 +37,14 @@ describe('ticket merge()', () => {
const options = {transaction: tx}; const options = {transaction: tx};
const chatNotificationBeforeMerge = await models.Chat.find(); const chatNotificationBeforeMerge = await models.Chat.find();
await models.Ticket.merge(ctx, tickets, options); await models.Ticket.merge(ctx, [tickets], options);
const createdTicketLog = await models.TicketLog.find({where: {originFk: tickets[0].originId}}, options); const createdTicketLog = await models.TicketLog.find({where: {originFk: tickets.originId}}, options);
const deletedTicket = await models.Ticket.findOne({where: {id: tickets[0].originId}}, options); const deletedTicket = await models.Ticket.findOne({where: {id: tickets.originId}}, options);
const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets[0].destinationId}}, options); const salesTicketFuture = await models.Sale.find({where: {ticketFk: tickets.destinationId}}, options);
const chatNotificationAfterMerge = await models.Chat.find(); const chatNotificationAfterMerge = await models.Chat.find();
expect(createdTicketLog.length).toEqual(1); expect(createdTicketLog.length).toEqual(2);
expect(deletedTicket.isDeleted).toEqual(true); expect(deletedTicket.isDeleted).toEqual(true);
expect(salesTicketFuture.length).toEqual(2); expect(salesTicketFuture.length).toEqual(2);
expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2); expect(chatNotificationBeforeMerge.length).toEqual(chatNotificationAfterMerge.length - 2);

View File

@ -105,8 +105,8 @@ module.exports = Self => {
originFk: id, originFk: id,
userFk: userId, userFk: userId,
action: 'update', action: 'update',
changedModel: 'Ticket', changedModel: 'Sale',
changedModelId: id, changedModelId: sale.id,
oldInstance: { oldInstance: {
item: originalSaleData.itemFk, item: originalSaleData.itemFk,
quantity: originalSaleData.quantity, quantity: originalSaleData.quantity,
@ -126,8 +126,8 @@ module.exports = Self => {
originFk: ticketId, originFk: ticketId,
userFk: userId, userFk: userId,
action: 'update', action: 'update',
changedModel: 'Ticket', changedModel: 'Sale',
changedModelId: ticketId, changedModelId: sale.id,
oldInstance: { oldInstance: {
item: originalSaleData.itemFk, item: originalSaleData.itemFk,
quantity: originalSaleData.quantity, quantity: originalSaleData.quantity,
@ -177,16 +177,16 @@ module.exports = Self => {
// Update original sale // Update original sale
const rest = originalSale.quantity - sale.quantity; const rest = originalSale.quantity - sale.quantity;
query = `UPDATE sale query = `UPDATE sale
SET quantity = ? SET quantity = ?
WHERE id = ?`; WHERE id = ?`;
await Self.rawSql(query, [rest, sale.id], options); await Self.rawSql(query, [rest, sale.id], options);
// Clone sale with new quantity // Clone sale with new quantity
query = `INSERT INTO sale (itemFk, ticketFk, concept, quantity, originalQuantity, price, discount, priceFixed, query = `INSERT INTO sale (itemFk, ticketFk, concept, quantity, originalQuantity, price, discount, priceFixed,
reserved, isPicked, isPriceFixed, isAdded) reserved, isPicked, isPriceFixed, isAdded)
SELECT itemFk, ?, concept, ?, originalQuantity, price, discount, priceFixed, SELECT itemFk, ?, concept, ?, originalQuantity, price, discount, priceFixed,
reserved, isPicked, isPriceFixed, isAdded reserved, isPicked, isPriceFixed, isAdded
FROM sale FROM sale
WHERE id = ?`; WHERE id = ?`;
await Self.rawSql(query, [ticketId, sale.quantity, sale.id], options); await Self.rawSql(query, [ticketId, sale.quantity, sale.id], options);

View File

@ -1,6 +1,6 @@
{ {
"name": "salix-back", "name": "salix-back",
"version": "23.10.01", "version": "23.12.01",
"author": "Verdnatura Levante SL", "author": "Verdnatura Levante SL",
"description": "Salix backend", "description": "Salix backend",
"license": "GPL-3.0", "license": "GPL-3.0",