fixes #5036 Regularizar históricos #1244

Merged
alexandre merged 32 commits from 5036-regularizar-historicos into dev 2023-03-16 07:26:33 +00:00
11 changed files with 386 additions and 384 deletions

View File

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

View File

@ -524,7 +524,7 @@ export default {
},
itemLog: {
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: {
header: 'vn-ticket-summary > vn-card > h5',

View File

@ -59,6 +59,6 @@ describe('Item log path', () => {
const fifthLineCreatedProperty = await page
.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');
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');
module.exports = function(Self) {
@ -6,344 +5,11 @@ module.exports = function(Self) {
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) {
const appModels = ctx.Model.app.models;
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;
ctx.options.httpCtx = LoopBackContext.getCurrentContext();
});
Self.observe('before delete', async function(ctx) {
const appModels = ctx.Model.app.models;
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;
}
}
ctx.options.httpCtx = LoopBackContext.getCurrentContext();
});
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 MySQL = require('loopback-connector-mysql').MySQL;
const EnumFactory = require('loopback-connector-mysql').EnumFactory;
const Transaction = require('loopback-connector').Transaction;
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 {
/**
* Promisified version of execute().
@ -219,6 +252,277 @@ class VnMySQL extends MySQL {
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';
alexandre marked this conversation as resolved
Review

Llevar updateAttributes

Llevar `updateAttributes`
}
alexandre marked this conversation as resolved
Review

Declarar-ho fora de la funció per a que no ho faça cada vegada.

Declarar-ho fora de la funció per a que no ho faça cada vegada.
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);
}
alexandre marked this conversation as resolved
Review

Llevar updateAttributes

Llevar `updateAttributes`
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 {
alexandre marked this conversation as resolved Outdated
Outdated
Review

Si grabUser està habilitat, els disparadors de la taula ja creen els logs, despres de açò no has de fer res mes a part de logout i finalitzar transacció

Si grabUser està habilitat, els disparadors de la taula ja creen els logs, despres de açò no has de fer res mes a part de logout i finalitzar transacció
// 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 {
alexandre marked this conversation as resolved Outdated
Outdated
Review

Declarar opOpts i opMap fora de la funció per a que no ho faça cada vegada.

Declarar `opOpts` i `opMap` fora de la funció per a que no ho faça cada vegada.
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);
}
}
alexandre marked this conversation as resolved
Review

Pots afegir els dos arguments en una instrucció fnArgs.push(opts, (...args) => resolve(args));

Pots afegir els dos arguments en una instrucció `fnArgs.push(opts, (...args) => resolve(args));`
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));
alexandre marked this conversation as resolved
Review

El funcionamiento de esta función es confuso y además su código no se reutiliza.

Quitar la función y mover cada bloque de código a su sitio correspondiente.

El funcionamiento de esta función es confuso y además su código no se reutiliza. Quitar la función y mover cada bloque de código a su sitio correspondiente.
else {
const log = Model.definition.settings.log;
fields.push(idName);
alexandre marked this conversation as resolved Outdated
Outdated
Review

Nomes es gasta en la línia 419, no es necesari definir la variable

Nomes es gasta en la línia 419, no es necesari definir la variable
if (log.relation) fields.push(Model.relations[log.relation].keyFrom);
if (log.showField) fields.push(log.showField);
alexandre marked this conversation as resolved
Review

Nomes es gasta en el else, declarar dins del else: const log = Model.definition.settings.log;

Nomes es gasta en el `else`, declarar dins del else: `const log = Model.definition.settings.log;`
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
alexandre marked this conversation as resolved Outdated
Outdated
Review

Ficar igual estricte ===, no està tenint en compte les dates

Ficar igual estricte `===`, no està tenint en compte les dates
Outdated
Review

Com has resolt açò? Continue vejent ===, les dates no es poden comparar aixina

Com has resolt açò? Continue vejent `===`, les dates no es poden comparar aixina
insertValuesLogEntry(logEntry, oldI);
logEntry.oldInstance = oldI;
logEntry.newInstance = newI;
logEntries.push(logEntry);
}
await models[log.model].create(logEntries, opts);
}
}
exports.VnMySQL = VnMySQL;

View File

@ -91,7 +91,11 @@ exports.getChanges = (original, changes) => {
const isPrivate = firstChar == '$';
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];
if (original[property] != undefined)

View File

@ -279,6 +279,18 @@ module.exports = Self => {
// Credit changes
if (changes.credit !== undefined)
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 => {

View File

@ -165,18 +165,29 @@ module.exports = Self => {
'shipped',
'landed',
'isDeleted',
'routeFk'
'routeFk',
'nickname'
],
include: [
{
relation: 'client',
scope: {
fields: 'salesPersonFk'
},
include: [
{
relation: 'address',
scope: {
fields: 'nickname'
}
}]
}
]
},
]
}, myOptions);
args.routeFk = null;
if (args.isWithoutNegatives === false) delete args.isWithoutNegatives;
const updatedTicket = Object.assign({}, args);
delete updatedTicket.ctx;
delete updatedTicket.option;
@ -224,6 +235,9 @@ module.exports = Self => {
}
const changes = loggable.getChanges(originalTicket, updatedTicket);
const hasChanges = Object.keys(changes.old).length > 0 || Object.keys(changes.new).length > 0;
if (hasChanges) {
const oldProperties = await loggable.translateValues(Self, changes.old);
const newProperties = await loggable.translateValues(Self, changes.new);
@ -256,6 +270,7 @@ module.exports = Self => {
});
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
}
}
res.id = args.id;
if (tx) await tx.commit();

View File

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

View File

@ -105,8 +105,8 @@ module.exports = Self => {
originFk: id,
userFk: userId,
action: 'update',
changedModel: 'Ticket',
changedModelId: id,
changedModel: 'Sale',
changedModelId: sale.id,
oldInstance: {
item: originalSaleData.itemFk,
quantity: originalSaleData.quantity,
@ -126,8 +126,8 @@ module.exports = Self => {
originFk: ticketId,
userFk: userId,
action: 'update',
changedModel: 'Ticket',
changedModelId: ticketId,
changedModel: 'Sale',
changedModelId: sale.id,
oldInstance: {
item: originalSaleData.itemFk,
quantity: originalSaleData.quantity,