Merge branch '4707-test-workerTimeControl' of https://gitea.verdnatura.es/verdnatura/salix into 4707-test-workerTimeControl
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
This commit is contained in:
commit
2fa2e7f099
|
@ -26,15 +26,14 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.sendCheckingPresence = async(ctx, recipientId, message) => {
|
Self.sendCheckingPresence = async(ctx, recipientId, message) => {
|
||||||
if (!recipientId) return false;
|
if (!recipientId) return false;
|
||||||
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
const sender = await models.VnUser.findById(userId, {fields: ['id']});
|
const sender = await models.VnUser.findById(userId, {fields: ['id']});
|
||||||
const recipient = await models.VnUser.findById(recipientId, null);
|
const recipient = await models.VnUser.findById(recipientId, null);
|
||||||
|
|
||||||
// Prevent sending messages to yourself
|
// Prevent sending messages to yourself
|
||||||
if (recipientId == userId) return false;
|
if (recipientId == userId) return false;
|
||||||
|
|
||||||
if (!recipient)
|
if (!recipient)
|
||||||
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
throw new Error(`Could not send message "${message}" to worker id ${recipientId} from user ${userId}`);
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.getTickets = async(ctx, id, print, options) => {
|
Self.getTickets = async(ctx, id, print, options) => {
|
||||||
const userId = ctx.req.accessToken.userId;
|
const userId = ctx.req.accessToken.userId;
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const $t = ctx.req.__;
|
const $t = ctx.req.__;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
|
|
||||||
|
@ -36,7 +36,6 @@ module.exports = Self => {
|
||||||
myOptions.userId = userId;
|
myOptions.userId = userId;
|
||||||
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
const [tickets] = await Self.rawSql(`CALL vn.collection_getTickets(?)`, [id], myOptions);
|
const [tickets] = await Self.rawSql(`CALL vn.collection_getTickets(?)`, [id], myOptions);
|
||||||
const sales = await Self.rawSql(`
|
const sales = await Self.rawSql(`
|
||||||
SELECT s.ticketFk,
|
SELECT s.ticketFk,
|
||||||
|
@ -86,24 +85,19 @@ module.exports = Self => {
|
||||||
if (tickets && tickets.length) {
|
if (tickets && tickets.length) {
|
||||||
for (const ticket of tickets) {
|
for (const ticket of tickets) {
|
||||||
const ticketId = ticket.ticketFk;
|
const ticketId = ticket.ticketFk;
|
||||||
|
|
||||||
// SEND ROCKET
|
|
||||||
if (ticket.observaciones != '') {
|
if (ticket.observaciones != '') {
|
||||||
for (observation of ticket.observaciones.split(' ')) {
|
for (observation of ticket.observaciones.split(' ')) {
|
||||||
if (['#', '@'].includes(observation.charAt(0))) {
|
if (['#', '@'].includes(observation.charAt(0))) {
|
||||||
promises.push(Self.app.models.Chat.send(ctx, observation,
|
promises.push(Self.app.models.Chat.send(ctx, observation,
|
||||||
$t('The ticket is in preparation', {
|
$t('The ticket is in preparation', {
|
||||||
ticketId: ticketId,
|
ticketId: ticketId,
|
||||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/summary`,
|
ticketUrl: `${url}ticket/${ticketId}/summary`,
|
||||||
salesPersonId: ticket.salesPersonFk
|
salesPersonId: ticket.salesPersonFk
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SET COLLECTION
|
|
||||||
if (sales && sales.length) {
|
if (sales && sales.length) {
|
||||||
// GET BARCODES
|
|
||||||
const barcodes = await Self.rawSql(`
|
const barcodes = await Self.rawSql(`
|
||||||
SELECT s.id saleFk, b.code, c.id
|
SELECT s.id saleFk, b.code, c.id
|
||||||
FROM vn.sale s
|
FROM vn.sale s
|
||||||
|
@ -114,13 +108,10 @@ module.exports = Self => {
|
||||||
WHERE s.ticketFk = ?
|
WHERE s.ticketFk = ?
|
||||||
AND tr.landed >= util.VN_CURDATE() - INTERVAL 1 YEAR`,
|
AND tr.landed >= util.VN_CURDATE() - INTERVAL 1 YEAR`,
|
||||||
[ticketId], myOptions);
|
[ticketId], myOptions);
|
||||||
|
|
||||||
// BINDINGS
|
|
||||||
ticket.sales = [];
|
ticket.sales = [];
|
||||||
for (const sale of sales) {
|
for (const sale of sales) {
|
||||||
if (sale.ticketFk === ticketId) {
|
if (sale.ticketFk === ticketId) {
|
||||||
sale.Barcodes = [];
|
sale.Barcodes = [];
|
||||||
|
|
||||||
if (barcodes && barcodes.length) {
|
if (barcodes && barcodes.length) {
|
||||||
for (const barcode of barcodes) {
|
for (const barcode of barcodes) {
|
||||||
if (barcode.saleFk === sale.saleFk) {
|
if (barcode.saleFk === sale.saleFk) {
|
||||||
|
@ -131,7 +122,6 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket.sales.push(sale);
|
ticket.sales.push(sale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +130,6 @@ module.exports = Self => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
return collection;
|
return collection;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,7 +18,14 @@ describe('setSaleQuantity()', () => {
|
||||||
|
|
||||||
it('should change quantity sale', async() => {
|
it('should change quantity sale', async() => {
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: 100}))));
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
if (sqlStatement.includes('catalog_calcFromItem')) {
|
||||||
|
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
|
||||||
|
SELECT 100 as available;`;
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
Self.remoteMethod('getUrl', {
|
||||||
|
description: 'Returns the colling app name',
|
||||||
|
accessType: 'READ',
|
||||||
|
accepts: [
|
||||||
|
{
|
||||||
|
arg: 'app',
|
||||||
|
type: 'string',
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
returns: {
|
||||||
|
type: 'object',
|
||||||
|
root: true
|
||||||
|
},
|
||||||
|
http: {
|
||||||
|
path: `/getUrl`,
|
||||||
|
verb: 'get'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Self.getUrl = async(appName = 'salix') => {
|
||||||
|
const {url} = await Self.app.models.Url.findOne({
|
||||||
|
where: {
|
||||||
|
appName,
|
||||||
|
enviroment: process.env.NODE_ENV || 'development'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
};
|
|
@ -7,17 +7,14 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.observe('before save', async function(ctx) {
|
Self.observe('before save', async function(ctx) {
|
||||||
if (!ctx.isNewInstance) return;
|
if (!ctx.isNewInstance) return;
|
||||||
|
|
||||||
let {message} = ctx.instance;
|
let {message} = ctx.instance;
|
||||||
if (!message) return;
|
if (!message) return;
|
||||||
|
|
||||||
const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g);
|
const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g);
|
||||||
if (!parts) return;
|
if (!parts) return;
|
||||||
|
|
||||||
const replacedParts = parts.map(part => {
|
const replacedParts = parts.map(part => {
|
||||||
return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, '');
|
return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, '');
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const [index, part] of parts.entries())
|
for (const [index, part] of parts.entries())
|
||||||
message = message.replace(part, replacedParts[index]);
|
message = message.replace(part, replacedParts[index]);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = Self => {
|
||||||
|
require('../methods/url/getUrl')(Self);
|
||||||
|
};
|
|
@ -1,5 +1,4 @@
|
||||||
const vnModel = require('vn-loopback/common/models/vn-model');
|
const vnModel = require('vn-loopback/common/models/vn-model');
|
||||||
const LoopBackContext = require('loopback-context');
|
|
||||||
const {Email} = require('vn-print');
|
const {Email} = require('vn-print');
|
||||||
|
|
||||||
module.exports = function(Self) {
|
module.exports = function(Self) {
|
||||||
|
@ -90,11 +89,7 @@ module.exports = function(Self) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Self.on('resetPasswordRequest', async function(info) {
|
Self.on('resetPasswordRequest', async function(info) {
|
||||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const httpCtx = {req: loopBackContext.active};
|
|
||||||
const httpRequest = httpCtx.req.http.req;
|
|
||||||
const headers = httpRequest.headers;
|
|
||||||
const origin = headers.origin;
|
|
||||||
|
|
||||||
const defaultHash = '/reset-password?access_token=$token$';
|
const defaultHash = '/reset-password?access_token=$token$';
|
||||||
const recoverHashes = {
|
const recoverHashes = {
|
||||||
|
@ -110,7 +105,7 @@ module.exports = function(Self) {
|
||||||
const params = {
|
const params = {
|
||||||
recipient: info.email,
|
recipient: info.email,
|
||||||
lang: user.lang,
|
lang: user.lang,
|
||||||
url: origin + '/#!' + recoverHash
|
url: url.slice(0, -1) + recoverHash
|
||||||
};
|
};
|
||||||
|
|
||||||
const options = Object.assign({}, info.options);
|
const options = Object.assign({}, info.options);
|
||||||
|
@ -177,46 +172,4 @@ module.exports = function(Self) {
|
||||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
|
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls =
|
||||||
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
Self.sharedClass._methods.find(method => method.name == 'changePassword').ctor.settings.acls
|
||||||
.filter(acl => acl.property != 'changePassword');
|
.filter(acl => acl.property != 'changePassword');
|
||||||
|
|
||||||
// FIXME: https://redmine.verdnatura.es/issues/5761
|
|
||||||
// Self.afterRemote('prototype.patchAttributes', async(ctx, instance) => {
|
|
||||||
// if (!ctx.args || !ctx.args.data.email) return;
|
|
||||||
|
|
||||||
// const loopBackContext = LoopBackContext.getCurrentContext();
|
|
||||||
// const httpCtx = {req: loopBackContext.active};
|
|
||||||
// const httpRequest = httpCtx.req.http.req;
|
|
||||||
// const headers = httpRequest.headers;
|
|
||||||
// const origin = headers.origin;
|
|
||||||
// const url = origin.split(':');
|
|
||||||
|
|
||||||
// class Mailer {
|
|
||||||
// async send(verifyOptions, cb) {
|
|
||||||
// const params = {
|
|
||||||
// url: verifyOptions.verifyHref,
|
|
||||||
// recipient: verifyOptions.to,
|
|
||||||
// lang: ctx.req.getLocale()
|
|
||||||
// };
|
|
||||||
|
|
||||||
// const email = new Email('email-verify', params);
|
|
||||||
// email.send();
|
|
||||||
|
|
||||||
// cb(null, verifyOptions.to);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const options = {
|
|
||||||
// type: 'email',
|
|
||||||
// to: instance.email,
|
|
||||||
// from: {},
|
|
||||||
// redirect: `${origin}/#!/account/${instance.id}/basic-data?emailConfirmed`,
|
|
||||||
// template: false,
|
|
||||||
// mailer: new Mailer,
|
|
||||||
// host: url[1].split('/')[2],
|
|
||||||
// port: url[2],
|
|
||||||
// protocol: url[0],
|
|
||||||
// user: Self
|
|
||||||
// };
|
|
||||||
|
|
||||||
// await instance.verify(options);
|
|
||||||
// });
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,7 +53,8 @@ describe('ticket ticketCalculateClon()', () => {
|
||||||
expect(result[orderIndex][0].ticketFk).toBeGreaterThan(newestTicketIdInFixtures);
|
expect(result[orderIndex][0].ticketFk).toBeGreaterThan(newestTicketIdInFixtures);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add the ticket to the order containing the original ticket and generate landed value if it was null', async() => {
|
it('should add the ticket to the order containing the original ' +
|
||||||
|
'ticket and generate landed value if it was null', async() => {
|
||||||
let stmts = [];
|
let stmts = [];
|
||||||
let stmt;
|
let stmt;
|
||||||
|
|
||||||
|
|
|
@ -134,14 +134,6 @@ describe('Ticket Edit sale path', () => {
|
||||||
await page.accessToSection('ticket.card.sale');
|
await page.accessToSection('ticket.card.sale');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should try to add a higher quantity value and then receive an error', async() => {
|
|
||||||
await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
|
|
||||||
await page.type(selectors.ticketSales.firstSaleQuantity, '11\u000d');
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('The new quantity should be smaller than the old one');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove 1 from the first sale quantity', async() => {
|
it('should remove 1 from the first sale quantity', async() => {
|
||||||
await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
|
await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
|
||||||
await page.waitForSelector(selectors.ticketSales.firstSaleQuantity);
|
await page.waitForSelector(selectors.ticketSales.firstSaleQuantity);
|
||||||
|
|
|
@ -13,6 +13,6 @@
|
||||||
"principalType": "ROLE",
|
"principalType": "ROLE",
|
||||||
"principalId": "$everyone",
|
"principalId": "$everyone",
|
||||||
"permission": "ALLOW"
|
"permission": "ALLOW"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"The default consignee can not be unchecked": "The default consignee can not be unchecked",
|
"The default consignee can not be unchecked": "The default consignee can not be unchecked",
|
||||||
"Enter an integer different to zero": "Enter an integer different to zero",
|
"Enter an integer different to zero": "Enter an integer different to zero",
|
||||||
"Package cannot be blank": "Package cannot be blank",
|
"Package cannot be blank": "Package cannot be blank",
|
||||||
"The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one",
|
"The price of the item changed": "The price of the item changed",
|
||||||
"The sales of this ticket can't be modified": "The sales of this ticket can't be modified",
|
"The sales of this ticket can't be modified": "The sales of this ticket can't be modified",
|
||||||
"Cannot check Equalization Tax in this NIF/CIF": "Cannot check Equalization Tax in this NIF/CIF",
|
"Cannot check Equalization Tax in this NIF/CIF": "Cannot check Equalization Tax in this NIF/CIF",
|
||||||
"You can't create an order for a frozen client": "You can't create an order for a frozen client",
|
"You can't create an order for a frozen client": "You can't create an order for a frozen client",
|
||||||
|
@ -191,4 +191,4 @@
|
||||||
"Booking completed": "Booking complete",
|
"Booking completed": "Booking complete",
|
||||||
"The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation",
|
"The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation",
|
||||||
"You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets"
|
"You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets"
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
"The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero",
|
"The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero",
|
||||||
"Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco",
|
"Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco",
|
||||||
"Description cannot be blank": "Se debe rellenar el campo de texto",
|
"Description cannot be blank": "Se debe rellenar el campo de texto",
|
||||||
"The new quantity should be smaller than the old one": "La nueva cantidad debe de ser menor que la anterior",
|
"The price of the item changed": "El precio del artículo cambió",
|
||||||
"The value should not be greater than 100%": "El valor no debe de ser mayor de 100%",
|
"The value should not be greater than 100%": "El valor no debe de ser mayor de 100%",
|
||||||
"The value should be a number": "El valor debe ser un numero",
|
"The value should be a number": "El valor debe ser un numero",
|
||||||
"This order is not editable": "Esta orden no se puede modificar",
|
"This order is not editable": "Esta orden no se puede modificar",
|
||||||
|
|
|
@ -43,9 +43,8 @@ module.exports = Self => {
|
||||||
|
|
||||||
Self.claimPickupEmail = async ctx => {
|
Self.claimPickupEmail = async ctx => {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const userId = ctx.req.accessToken.userId;
|
|
||||||
const $t = ctx.req.__; // $translate
|
const $t = ctx.req.__; // $translate
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
|
|
||||||
const args = Object.assign({}, ctx.args);
|
const args = Object.assign({}, ctx.args);
|
||||||
const params = {
|
const params = {
|
||||||
|
@ -70,9 +69,8 @@ module.exports = Self => {
|
||||||
const message = $t('Claim pickup order sent', {
|
const message = $t('Claim pickup order sent', {
|
||||||
claimId: args.id,
|
claimId: args.id,
|
||||||
clientName: claim.client().name,
|
clientName: claim.client().name,
|
||||||
claimUrl: `${origin}/#!/claim/${args.id}/summary`,
|
claimUrl: `${url}claim/${args.id}/summary`,
|
||||||
});
|
});
|
||||||
|
|
||||||
const salesPersonId = claim.client().salesPersonFk;
|
const salesPersonId = claim.client().salesPersonFk;
|
||||||
if (salesPersonId)
|
if (salesPersonId)
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||||
|
|
|
@ -94,13 +94,13 @@ module.exports = Self => {
|
||||||
|
|
||||||
const salesPerson = ticket.client().salesPersonUser();
|
const salesPerson = ticket.client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
|
|
||||||
const message = $t('Created claim', {
|
const message = $t('Created claim', {
|
||||||
claimId: newClaim.id,
|
claimId: newClaim.id,
|
||||||
ticketId: ticketId,
|
ticketId: ticketId,
|
||||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
ticketUrl: `${url}ticket/${ticketId}/sale`,
|
||||||
claimUrl: `${origin}/#!/claim/${newClaim.id}/summary`,
|
claimUrl: `${url}claim/${newClaim.id}/summary`,
|
||||||
changes: changesMade
|
changes: changesMade
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
||||||
|
|
|
@ -56,15 +56,15 @@ module.exports = Self => {
|
||||||
const salesPerson = sale.ticket().client().salesPersonUser();
|
const salesPerson = sale.ticket().client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
const nickname = address && address.nickname || destination.description;
|
const nickname = address && address.nickname || destination.description;
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const message = $t('Sent units from ticket', {
|
const message = $t('Sent units from ticket', {
|
||||||
quantity: sale.quantity,
|
quantity: sale.quantity,
|
||||||
concept: sale.concept,
|
concept: sale.concept,
|
||||||
itemId: sale.itemFk,
|
itemId: sale.itemFk,
|
||||||
ticketId: sale.ticketFk,
|
ticketId: sale.ticketFk,
|
||||||
nickname: nickname,
|
nickname: nickname,
|
||||||
ticketUrl: `${origin}/#!/ticket/${sale.ticketFk}/sale`,
|
ticketUrl: `${url}ticket/${sale.ticketFk}/sale`,
|
||||||
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
|
itemUrl: `${url}item/${sale.itemFk}/summary`
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@ const app = require('vn-loopback/server/server');
|
||||||
const LoopBackContext = require('loopback-context');
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
describe('Update Claim', () => {
|
describe('Update Claim', () => {
|
||||||
|
let url;
|
||||||
beforeAll(async() => {
|
beforeAll(async() => {
|
||||||
|
url = await app.models.Url.getUrl();
|
||||||
const activeCtx = {
|
const activeCtx = {
|
||||||
accessToken: {userId: 9},
|
accessToken: {userId: 9},
|
||||||
http: {
|
http: {
|
||||||
|
@ -29,7 +31,6 @@ describe('Update Claim', () => {
|
||||||
|
|
||||||
it(`should throw an error as the user doesn't have rights`, async() => {
|
it(`should throw an error as the user doesn't have rights`, async() => {
|
||||||
const tx = await app.models.Claim.beginTransaction({});
|
const tx = await app.models.Claim.beginTransaction({});
|
||||||
|
|
||||||
let error;
|
let error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -77,7 +78,7 @@ describe('Update Claim', () => {
|
||||||
const ctx = {
|
const ctx = {
|
||||||
req: {
|
req: {
|
||||||
accessToken: {userId: claimManagerId},
|
accessToken: {userId: claimManagerId},
|
||||||
headers: {origin: 'http://localhost'}
|
headers: {origin: url}
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
observation: 'valid observation',
|
observation: 'valid observation',
|
||||||
|
@ -118,7 +119,7 @@ describe('Update Claim', () => {
|
||||||
const ctx = {
|
const ctx = {
|
||||||
req: {
|
req: {
|
||||||
accessToken: {userId: claimManagerId},
|
accessToken: {userId: claimManagerId},
|
||||||
headers: {origin: 'http://localhost'}
|
headers: {origin: url}
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
observation: 'valid observation',
|
observation: 'valid observation',
|
||||||
|
|
|
@ -91,16 +91,16 @@ module.exports = Self => {
|
||||||
|
|
||||||
// When hasToPickUp has been changed
|
// When hasToPickUp has been changed
|
||||||
if (salesPerson && changedHasToPickUp && updatedClaim.hasToPickUp)
|
if (salesPerson && changedHasToPickUp && updatedClaim.hasToPickUp)
|
||||||
notifyPickUp(ctx, salesPerson.id, claim);
|
await notifyPickUp(ctx, salesPerson.id, claim);
|
||||||
|
|
||||||
// When claimState has been changed
|
// When claimState has been changed
|
||||||
if (args.claimStateFk) {
|
if (args.claimStateFk) {
|
||||||
const newState = await models.ClaimState.findById(args.claimStateFk, null, myOptions);
|
const newState = await models.ClaimState.findById(args.claimStateFk, null, myOptions);
|
||||||
if (newState.hasToNotify) {
|
if (newState.hasToNotify) {
|
||||||
if (newState.code == 'incomplete')
|
if (newState.code == 'incomplete')
|
||||||
notifyStateChange(ctx, salesPerson.id, claim, newState.code);
|
await notifyStateChange(ctx, salesPerson.id, claim, newState.code);
|
||||||
if (newState.code == 'canceled')
|
if (newState.code == 'canceled')
|
||||||
notifyStateChange(ctx, claim.workerFk, claim, newState.code);
|
await notifyStateChange(ctx, claim.workerFk, claim, newState.code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,26 +115,26 @@ module.exports = Self => {
|
||||||
|
|
||||||
async function notifyStateChange(ctx, workerId, claim, state) {
|
async function notifyStateChange(ctx, workerId, claim, state) {
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await models.Url.getUrl();
|
||||||
const $t = ctx.req.__; // $translate
|
const $t = ctx.req.__; // $translate
|
||||||
|
|
||||||
const message = $t(`Claim state has changed to ${state}`, {
|
const message = $t(`Claim state has changed to ${state}`, {
|
||||||
claimId: claim.id,
|
claimId: claim.id,
|
||||||
clientName: claim.client().name,
|
clientName: claim.client().name,
|
||||||
claimUrl: `${origin}/#!/claim/${claim.id}/summary`
|
claimUrl: `${url}claim/${claim.id}/summary`
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, workerId, message);
|
await models.Chat.sendCheckingPresence(ctx, workerId, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function notifyPickUp(ctx, workerId, claim) {
|
async function notifyPickUp(ctx, workerId, claim) {
|
||||||
const origin = ctx.req.headers.origin;
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
const url = await models.Url.getUrl();
|
||||||
const $t = ctx.req.__; // $translate
|
const $t = ctx.req.__; // $translate
|
||||||
|
|
||||||
const message = $t('Claim will be picked', {
|
const message = $t('Claim will be picked', {
|
||||||
claimId: claim.id,
|
claimId: claim.id,
|
||||||
clientName: claim.client().name,
|
clientName: claim.client().name,
|
||||||
claimUrl: `${origin}/#!/claim/${claim.id}/summary`
|
claimUrl: `${url}claim/${claim.id}/summary`
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, workerId, message);
|
await models.Chat.sendCheckingPresence(ctx, workerId, message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,7 +250,12 @@ module.exports = Self => {
|
||||||
const loopBackContext = LoopBackContext.getCurrentContext();
|
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||||
const accessToken = {req: loopBackContext.active.accessToken};
|
const accessToken = {req: loopBackContext.active.accessToken};
|
||||||
|
|
||||||
const editVerifiedDataWithoutTaxDataChecked = models.ACL.checkAccessAcl(accessToken, 'Client', 'editVerifiedDataWithoutTaxDataCheck', 'WRITE');
|
const editVerifiedDataWithoutTaxDataChecked = models.ACL.checkAccessAcl(
|
||||||
|
accessToken,
|
||||||
|
'Client',
|
||||||
|
'editVerifiedDataWithoutTaxDataCheck',
|
||||||
|
'WRITE'
|
||||||
|
);
|
||||||
const hasChanges = orgData && changes;
|
const hasChanges = orgData && changes;
|
||||||
|
|
||||||
const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
|
const isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
|
||||||
|
@ -263,7 +268,9 @@ module.exports = Self => {
|
||||||
const sageTransactionTypeChanged = hasChanges && orgData.sageTransactionTypeFk != sageTransactionType;
|
const sageTransactionTypeChanged = hasChanges && orgData.sageTransactionTypeFk != sageTransactionType;
|
||||||
|
|
||||||
const cantEditVerifiedData = isTaxDataCheckedChanged && !editVerifiedDataWithoutTaxDataChecked;
|
const cantEditVerifiedData = isTaxDataCheckedChanged && !editVerifiedDataWithoutTaxDataChecked;
|
||||||
const cantChangeSageData = (sageTaxTypeChanged || sageTransactionTypeChanged) && !editVerifiedDataWithoutTaxDataChecked;
|
const cantChangeSageData = (sageTaxTypeChanged ||
|
||||||
|
sageTransactionTypeChanged
|
||||||
|
) && !editVerifiedDataWithoutTaxDataChecked;
|
||||||
|
|
||||||
if (cantEditVerifiedData || cantChangeSageData)
|
if (cantEditVerifiedData || cantChangeSageData)
|
||||||
throw new UserError(`You don't have enough privileges`);
|
throw new UserError(`You don't have enough privileges`);
|
||||||
|
@ -346,8 +353,7 @@ module.exports = Self => {
|
||||||
const httpCtx = {req: loopBackContext.active};
|
const httpCtx = {req: loopBackContext.active};
|
||||||
const httpRequest = httpCtx.req.http.req;
|
const httpRequest = httpCtx.req.http.req;
|
||||||
const $t = httpRequest.__;
|
const $t = httpRequest.__;
|
||||||
const headers = httpRequest.headers;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const origin = headers.origin;
|
|
||||||
|
|
||||||
const salesPersonId = instance.salesPersonFk;
|
const salesPersonId = instance.salesPersonFk;
|
||||||
|
|
||||||
|
@ -366,7 +372,7 @@ module.exports = Self => {
|
||||||
await email.send();
|
await email.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullUrl = `${origin}/#!/client/${instance.id}/billing-data`;
|
const fullUrl = `${url}client/${instance.id}/billing-data`;
|
||||||
const message = $t('Changed client paymethod', {
|
const message = $t('Changed client paymethod', {
|
||||||
clientId: instance.id,
|
clientId: instance.id,
|
||||||
clientName: instance.name,
|
clientName: instance.name,
|
||||||
|
@ -389,8 +395,7 @@ module.exports = Self => {
|
||||||
const httpCtx = {req: loopBackContext.active};
|
const httpCtx = {req: loopBackContext.active};
|
||||||
const httpRequest = httpCtx.req.http.req;
|
const httpRequest = httpCtx.req.http.req;
|
||||||
const $t = httpRequest.__;
|
const $t = httpRequest.__;
|
||||||
const headers = httpRequest.headers;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const origin = headers.origin;
|
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
|
|
||||||
let previousWorker = {name: $t('None')};
|
let previousWorker = {name: $t('None')};
|
||||||
|
@ -411,7 +416,7 @@ module.exports = Self => {
|
||||||
currentWorker.name = worker && worker.user().nickname;
|
currentWorker.name = worker && worker.user().nickname;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fullUrl = `${origin}/#!/client/${client.id}/basic-data`;
|
const fullUrl = `${url}client/${client.id}/basic-data`;
|
||||||
const message = $t('Client assignment has changed', {
|
const message = $t('Client assignment has changed', {
|
||||||
clientId: client.id,
|
clientId: client.id,
|
||||||
clientName: client.name,
|
clientName: client.name,
|
||||||
|
|
|
@ -57,8 +57,8 @@ module.exports = function(Self) {
|
||||||
|
|
||||||
const httpRequest = httpCtx.req.http.req;
|
const httpRequest = httpCtx.req.http.req;
|
||||||
const $t = httpRequest.__;
|
const $t = httpRequest.__;
|
||||||
const origin = httpRequest.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const fullPath = `${origin}/#!/client/${client.id}/credit-insurance/index`;
|
const fullPath = `${url}client/${client.id}/credit-insurance/index`;
|
||||||
const message = $t('MESSAGE_INSURANCE_CHANGE', {
|
const message = $t('MESSAGE_INSURANCE_CHANGE', {
|
||||||
clientId: client.id,
|
clientId: client.id,
|
||||||
clientName: client.name,
|
clientName: client.name,
|
||||||
|
|
|
@ -59,10 +59,10 @@ module.exports = Self => {
|
||||||
};
|
};
|
||||||
await Self.invoiceEmail(ctx, ref);
|
await Self.invoiceEmail(ctx, ref);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const message = ctx.req.__('Mail not sent', {
|
const message = ctx.req.__('Mail not sent', {
|
||||||
clientId: client.id,
|
clientId: client.id,
|
||||||
clientUrl: `${origin}/#!/claim/${id}/summary`
|
clientUrl: `${url}claim/${id}/summary`
|
||||||
});
|
});
|
||||||
const salesPersonId = client.salesPersonFk;
|
const salesPersonId = client.salesPersonFk;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
let UserError = require('vn-loopback/util/user-error');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('deleteSales', {
|
Self.remoteMethodCtx('deleteSales', {
|
||||||
description: 'Deletes the selected sales',
|
description: 'Deletes the selected sales',
|
||||||
|
@ -70,11 +68,11 @@ module.exports = Self => {
|
||||||
|
|
||||||
const salesPerson = ticket.client().salesPersonUser();
|
const salesPerson = ticket.client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
|
|
||||||
const message = $t('Deleted sales from ticket', {
|
const message = $t('Deleted sales from ticket', {
|
||||||
ticketId: ticketId,
|
ticketId: ticketId,
|
||||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
ticketUrl: `${url}ticket/${ticketId}/sale`,
|
||||||
deletions: deletions
|
deletions: deletions
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
|
|
||||||
let UserError = require('vn-loopback/util/user-error');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('reserve', {
|
Self.remoteMethodCtx('reserve', {
|
||||||
description: 'Change the state of a ticket',
|
description: 'Change the state of a ticket',
|
||||||
|
@ -65,7 +62,8 @@ module.exports = Self => {
|
||||||
|
|
||||||
promises.push(reservedSale);
|
promises.push(reservedSale);
|
||||||
|
|
||||||
changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`;
|
changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})
|
||||||
|
${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,11 +85,11 @@ module.exports = Self => {
|
||||||
|
|
||||||
const salesPerson = ticket.client().salesPersonUser();
|
const salesPerson = ticket.client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
|
|
||||||
const message = $t('Changed sale reserved state', {
|
const message = $t('Changed sale reserved state', {
|
||||||
ticketId: ticketId,
|
ticketId: ticketId,
|
||||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
ticketUrl: `${url}ticket/${ticketId}/sale`,
|
||||||
changes: changesMade
|
changes: changesMade
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||||
|
|
|
@ -1,239 +0,0 @@
|
||||||
/* eslint max-len: ["error", { "code": 150 }]*/
|
|
||||||
|
|
||||||
const models = require('vn-loopback/server/server').models;
|
|
||||||
const LoopBackContext = require('loopback-context');
|
|
||||||
|
|
||||||
describe('sale updateQuantity()', () => {
|
|
||||||
const ctx = {
|
|
||||||
req: {
|
|
||||||
accessToken: {userId: 9},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
function getActiveCtx(userId) {
|
|
||||||
return {
|
|
||||||
active: {
|
|
||||||
accessToken: {userId},
|
|
||||||
http: {
|
|
||||||
req: {
|
|
||||||
headers: {origin: 'http://localhost'}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should throw an error if the quantity is greater than it should be', async() => {
|
|
||||||
const ctx = {
|
|
||||||
req: {
|
|
||||||
accessToken: {userId: 1},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
|
||||||
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: 100}))));
|
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
|
||||||
|
|
||||||
let error;
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, 17, 31, options);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error).toEqual(new Error('The new quantity should be smaller than the old one'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add quantity if the quantity is greater than it should be and is role advanced', async() => {
|
|
||||||
const saleId = 17;
|
|
||||||
const buyerId = 35;
|
|
||||||
const ctx = {
|
|
||||||
req: {
|
|
||||||
accessToken: {userId: buyerId},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(buyerId));
|
|
||||||
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: 100}))));
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
|
|
||||||
|
|
||||||
expect(isRoleAdvanced).toEqual(true);
|
|
||||||
|
|
||||||
const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
|
||||||
|
|
||||||
expect(originalLine.quantity).toEqual(30);
|
|
||||||
|
|
||||||
const newQuantity = originalLine.quantity + 1;
|
|
||||||
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
|
||||||
|
|
||||||
const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
|
||||||
|
|
||||||
expect(modifiedLine.quantity).toEqual(newQuantity);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update the quantity of a given sale current line', async() => {
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
|
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
|
||||||
const saleId = 25;
|
|
||||||
const newQuantity = 4;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
|
||||||
|
|
||||||
expect(originalLine.quantity).toEqual(20);
|
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
|
||||||
|
|
||||||
const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
|
||||||
|
|
||||||
expect(modifiedLine.quantity).toEqual(newQuantity);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the quantity is negative and it is not a refund ticket', async() => {
|
|
||||||
const ctx = {
|
|
||||||
req: {
|
|
||||||
accessToken: {userId: 1},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
|
||||||
|
|
||||||
const saleId = 17;
|
|
||||||
const newQuantity = -10;
|
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
|
||||||
|
|
||||||
let error;
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error).toEqual(new Error('You can only add negative amounts in refund tickets'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should update a negative quantity when is a ticket refund', async() => {
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
|
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
|
||||||
const saleId = 13;
|
|
||||||
const newQuantity = -10;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
|
||||||
|
|
||||||
const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
|
||||||
|
|
||||||
expect(modifiedLine.quantity).toEqual(newQuantity);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw an error if the quantity is less than the minimum quantity of the item', async() => {
|
|
||||||
const ctx = {
|
|
||||||
req: {
|
|
||||||
accessToken: {userId: 1},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
|
||||||
const itemId = 2;
|
|
||||||
const saleId = 17;
|
|
||||||
const minQuantity = 30;
|
|
||||||
const newQuantity = minQuantity - 1;
|
|
||||||
|
|
||||||
let error;
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
const item = await models.Item.findById(itemId, null, options);
|
|
||||||
await item.updateAttribute('minQuantity', minQuantity, options);
|
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
error = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(error).toEqual(new Error('The amount cannot be less than the minimum'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should change quantity if has minimum quantity and new quantity is equal than item available', async() => {
|
|
||||||
const ctx = {
|
|
||||||
req: {
|
|
||||||
accessToken: {userId: 1},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
|
||||||
const itemId = 2;
|
|
||||||
const saleId = 17;
|
|
||||||
const minQuantity = 30;
|
|
||||||
const newQuantity = minQuantity - 1;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
const item = await models.Item.findById(itemId, null, options);
|
|
||||||
await item.updateAttribute('minQuantity', minQuantity, options);
|
|
||||||
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: newQuantity}))));
|
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
|
||||||
|
|
||||||
await tx.rollback();
|
|
||||||
} catch (e) {
|
|
||||||
await tx.rollback();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,5 +1,3 @@
|
||||||
let UserError = require('vn-loopback/util/user-error');
|
|
||||||
|
|
||||||
module.exports = Self => {
|
module.exports = Self => {
|
||||||
Self.remoteMethodCtx('updatePrice', {
|
Self.remoteMethodCtx('updatePrice', {
|
||||||
description: 'Changes the price of a sale',
|
description: 'Changes the price of a sale',
|
||||||
|
@ -100,7 +98,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
const salesPerson = sale.ticket().client().salesPersonUser();
|
const salesPerson = sale.ticket().client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const message = $t('Changed sale price', {
|
const message = $t('Changed sale price', {
|
||||||
ticketId: sale.ticket().id,
|
ticketId: sale.ticket().id,
|
||||||
itemId: sale.itemFk,
|
itemId: sale.itemFk,
|
||||||
|
@ -108,8 +106,8 @@ module.exports = Self => {
|
||||||
quantity: sale.quantity,
|
quantity: sale.quantity,
|
||||||
oldPrice: oldPrice,
|
oldPrice: oldPrice,
|
||||||
newPrice: newPrice,
|
newPrice: newPrice,
|
||||||
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
|
ticketUrl: `${url}ticket/${sale.ticket().id}/sale`,
|
||||||
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
|
itemUrl: `${url}item/${sale.itemFk}/summary`
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,16 +68,17 @@ module.exports = Self => {
|
||||||
|
|
||||||
const salesPerson = sale.ticket().client().salesPersonUser();
|
const salesPerson = sale.ticket().client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const message = $t('Changed sale quantity', {
|
const message = $t('Changed sale quantity', {
|
||||||
ticketId: sale.ticket().id,
|
ticketId: sale.ticket().id,
|
||||||
itemId: sale.itemFk,
|
itemId: sale.itemFk,
|
||||||
concept: sale.concept,
|
concept: sale.concept,
|
||||||
oldQuantity: oldQuantity,
|
oldQuantity: oldQuantity,
|
||||||
newQuantity: newQuantity,
|
newQuantity: newQuantity,
|
||||||
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
|
ticketUrl: `${url}ticket/${sale.ticket().id}/sale`,
|
||||||
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
|
itemUrl: `${url}item/${sale.itemFk}/summary`
|
||||||
});
|
});
|
||||||
|
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ module.exports = Self => {
|
||||||
const query = `CALL vn.sale_calculateComponent(?, NULL)`;
|
const query = `CALL vn.sale_calculateComponent(?, NULL)`;
|
||||||
await Self.rawSql(query, [sale.id], myOptions);
|
await Self.rawSql(query, [sale.id], myOptions);
|
||||||
|
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const requesterId = request.requesterFk;
|
const requesterId = request.requesterFk;
|
||||||
|
|
||||||
const message = $t('Bought units from buy request', {
|
const message = $t('Bought units from buy request', {
|
||||||
|
@ -92,8 +92,8 @@ module.exports = Self => {
|
||||||
concept: sale.concept,
|
concept: sale.concept,
|
||||||
itemId: sale.itemFk,
|
itemId: sale.itemFk,
|
||||||
ticketId: sale.ticketFk,
|
ticketId: sale.ticketFk,
|
||||||
url: `${origin}/#!/ticket/${sale.ticketFk}/summary`,
|
url: `${url}ticket/${sale.ticketFk}/summary`,
|
||||||
urlItem: `${origin}/#!/item/${sale.itemFk}/summary`
|
urlItem: `${url}item/${sale.itemFk}/summary`
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
|
await models.Chat.sendCheckingPresence(ctx, requesterId, message, myOptions);
|
||||||
|
|
||||||
|
|
|
@ -50,12 +50,12 @@ module.exports = Self => {
|
||||||
const request = await Self.app.models.TicketRequest.findById(ctx.args.id, null, myOptions);
|
const request = await Self.app.models.TicketRequest.findById(ctx.args.id, null, myOptions);
|
||||||
await request.updateAttributes(params, myOptions);
|
await request.updateAttributes(params, myOptions);
|
||||||
|
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const requesterId = request.requesterFk;
|
const requesterId = request.requesterFk;
|
||||||
|
|
||||||
const message = $t('Deny buy request', {
|
const message = $t('Deny buy request', {
|
||||||
ticketId: request.ticketFk,
|
ticketId: request.ticketFk,
|
||||||
url: `${origin}/#!/ticket/${request.ticketFk}/request/index`,
|
url: `${url}ticket/${request.ticketFk}/request/index`,
|
||||||
observation: params.response
|
observation: params.response
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -83,11 +83,11 @@ module.exports = Self => {
|
||||||
|
|
||||||
const salesPerson = ticket.client().salesPersonUser();
|
const salesPerson = ticket.client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
|
|
||||||
const message = $t('Added sale to ticket', {
|
const message = $t('Added sale to ticket', {
|
||||||
ticketId: id,
|
ticketId: id,
|
||||||
ticketUrl: `${origin}/#!/ticket/${id}/sale`,
|
ticketUrl: `${url}ticket/${id}/sale`,
|
||||||
addition: addition
|
addition: addition
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
||||||
|
|
|
@ -237,7 +237,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
const salesPersonId = originalTicket.client().salesPersonFk;
|
const salesPersonId = originalTicket.client().salesPersonFk;
|
||||||
if (salesPersonId) {
|
if (salesPersonId) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
|
|
||||||
let changesMade = '';
|
let changesMade = '';
|
||||||
for (let change in newProperties) {
|
for (let change in newProperties) {
|
||||||
|
@ -249,7 +249,7 @@ module.exports = Self => {
|
||||||
|
|
||||||
const message = $t('Changed this data from the ticket', {
|
const message = $t('Changed this data from the ticket', {
|
||||||
ticketId: args.id,
|
ticketId: args.id,
|
||||||
ticketUrl: `${origin}/#!/ticket/${args.id}/sale`,
|
ticketUrl: `${url}ticket/${args.id}/sale`,
|
||||||
changes: changesMade
|
changes: changesMade
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||||
|
|
|
@ -25,7 +25,7 @@ module.exports = Self => {
|
||||||
Self.merge = async(ctx, tickets, options) => {
|
Self.merge = async(ctx, tickets, options) => {
|
||||||
const httpRequest = ctx.req;
|
const httpRequest = ctx.req;
|
||||||
const $t = httpRequest.__;
|
const $t = httpRequest.__;
|
||||||
const origin = httpRequest.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const models = Self.app.models;
|
const models = Self.app.models;
|
||||||
const myOptions = {};
|
const myOptions = {};
|
||||||
let tx;
|
let tx;
|
||||||
|
@ -40,8 +40,8 @@ module.exports = Self => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (let ticket of tickets) {
|
for (let ticket of tickets) {
|
||||||
const originFullPath = `${origin}/#!/ticket/${ticket.originId}/summary`;
|
const originFullPath = `${url}ticket/${ticket.originId}/summary`;
|
||||||
const destinationFullPath = `${origin}/#!/ticket/${ticket.destinationId}/summary`;
|
const destinationFullPath = `${url}ticket/${ticket.destinationId}/summary`;
|
||||||
const message = $t('Ticket merged', {
|
const message = $t('Ticket merged', {
|
||||||
originDated: dateUtil.toString(new Date(ticket.originShipped)),
|
originDated: dateUtil.toString(new Date(ticket.originShipped)),
|
||||||
destinationDated: dateUtil.toString(new Date(ticket.destinationShipped)),
|
destinationDated: dateUtil.toString(new Date(ticket.destinationShipped)),
|
||||||
|
|
|
@ -48,7 +48,7 @@ module.exports = Self => {
|
||||||
description: `The route id filter`
|
description: `The route id filter`
|
||||||
}],
|
}],
|
||||||
returns: {
|
returns: {
|
||||||
type: 'number',
|
type: 'object',
|
||||||
root: true
|
root: true
|
||||||
},
|
},
|
||||||
http: {
|
http: {
|
||||||
|
|
|
@ -48,10 +48,10 @@ module.exports = Self => {
|
||||||
// Send notification to salesPerson
|
// Send notification to salesPerson
|
||||||
const salesPersonId = ticket.client().salesPersonFk;
|
const salesPersonId = ticket.client().salesPersonFk;
|
||||||
if (salesPersonId) {
|
if (salesPersonId) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const message = $t(`I have restored the ticket id`, {
|
const message = $t(`I have restored the ticket id`, {
|
||||||
id: id,
|
id: id,
|
||||||
url: `${origin}/#!/ticket/${id}/summary`
|
url: `${url}ticket/${id}/summary`
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,10 +119,10 @@ module.exports = Self => {
|
||||||
// Send notification to salesPerson
|
// Send notification to salesPerson
|
||||||
const salesPersonUser = ticket.client().salesPersonUser();
|
const salesPersonUser = ticket.client().salesPersonUser();
|
||||||
if (salesPersonUser && sales.length) {
|
if (salesPersonUser && sales.length) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const message = $t(`I have deleted the ticket id`, {
|
const message = $t(`I have deleted the ticket id`, {
|
||||||
id: id,
|
id: id,
|
||||||
url: `${origin}/#!/ticket/${id}/summary`
|
url: `${url}ticket/${id}/summary`
|
||||||
});
|
});
|
||||||
await models.Chat.send(ctx, `@${salesPersonUser.name}`, message);
|
await models.Chat.send(ctx, `@${salesPersonUser.name}`, message);
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,8 @@ module.exports = Self => {
|
||||||
JOIN vn.sectorCollection sc ON sc.id = scsg.sectorCollectionFk
|
JOIN vn.sectorCollection sc ON sc.id = scsg.sectorCollectionFk
|
||||||
JOIN vn.saleGroupDetail sgd ON sgd.saleGroupFk = sg.id
|
JOIN vn.saleGroupDetail sgd ON sgd.saleGroupFk = sg.id
|
||||||
JOIN vn.sale s ON s.id = sgd.saleFk
|
JOIN vn.sale s ON s.id = sgd.saleFk
|
||||||
WHERE s.ticketFk = ?;`, [ticket.id], myOptions);
|
WHERE s.ticketFk = ?;`, [ticket.id], myOptions
|
||||||
|
);
|
||||||
|
|
||||||
if (tx) await tx.commit();
|
if (tx) await tx.commit();
|
||||||
|
|
||||||
|
|
|
@ -165,11 +165,10 @@ module.exports = Self => {
|
||||||
|
|
||||||
const salesPerson = ticket.client().salesPersonUser();
|
const salesPerson = ticket.client().salesPersonUser();
|
||||||
if (salesPerson) {
|
if (salesPerson) {
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
|
|
||||||
const message = $t('Changed sale discount', {
|
const message = $t('Changed sale discount', {
|
||||||
ticketId: id,
|
ticketId: id,
|
||||||
ticketUrl: `${origin}/#!/ticket/${id}/sale`,
|
ticketUrl: `${url}ticket/${id}/sale`,
|
||||||
changes: changesMade
|
changes: changesMade
|
||||||
});
|
});
|
||||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||||
|
|
|
@ -31,11 +31,14 @@ module.exports = Self => {
|
||||||
|
|
||||||
const ticketId = changes?.ticketFk || instance?.ticketFk;
|
const ticketId = changes?.ticketFk || instance?.ticketFk;
|
||||||
const itemId = changes?.itemFk || instance?.itemFk;
|
const itemId = changes?.itemFk || instance?.itemFk;
|
||||||
|
const oldQuantity = instance?.quantity ?? null;
|
||||||
|
const quantityAdded = newQuantity - oldQuantity;
|
||||||
|
const isReduction = oldQuantity && newQuantity <= oldQuantity;
|
||||||
|
|
||||||
const ticket = await models.Ticket.findById(
|
const ticket = await models.Ticket.findById(
|
||||||
ticketId,
|
ticketId,
|
||||||
{
|
{
|
||||||
fields: ['id', 'clientFk', 'warehouseFk', 'shipped'],
|
fields: ['id', 'clientFk', 'warehouseFk', 'addressFk', 'agencyModeFk', 'shipped', 'landed'],
|
||||||
include: {
|
include: {
|
||||||
relation: 'client',
|
relation: 'client',
|
||||||
scope: {
|
scope: {
|
||||||
|
@ -65,28 +68,50 @@ module.exports = Self => {
|
||||||
fields: ['family', 'minQuantity'],
|
fields: ['family', 'minQuantity'],
|
||||||
where: {id: itemId},
|
where: {id: itemId},
|
||||||
}, ctx.options);
|
}, ctx.options);
|
||||||
|
|
||||||
if (item.family == 'EMB') return;
|
if (item.family == 'EMB') return;
|
||||||
|
|
||||||
const itemInfo = await models.Item.getVisibleAvailable(
|
if (await models.ACL.checkAccessAcl(ctx, 'Sale', 'isInPreparing', '*')) return;
|
||||||
itemId,
|
|
||||||
ticket.warehouseFk,
|
|
||||||
ticket.shipped,
|
|
||||||
ctx.options
|
|
||||||
);
|
|
||||||
|
|
||||||
const oldQuantity = instance?.quantity ?? null;
|
await models.Sale.rawSql(`CALL catalog_calcFromItem(?,?,?,?)`, [
|
||||||
const quantityAdded = newQuantity - oldQuantity;
|
ticket.landed,
|
||||||
if (itemInfo.available < quantityAdded)
|
ticket.addressFk,
|
||||||
|
ticket.agencyModeFk,
|
||||||
|
itemId
|
||||||
|
],
|
||||||
|
ctx.options);
|
||||||
|
|
||||||
|
const [itemInfo] = await models.Sale.rawSql(`SELECT available FROM tmp.ticketCalculateItem`, null, ctx.options);
|
||||||
|
const available = itemInfo?.available;
|
||||||
|
|
||||||
|
if ((!isReduction && !available) || available < quantityAdded)
|
||||||
throw new UserError(`This item is not available`);
|
throw new UserError(`This item is not available`);
|
||||||
|
|
||||||
if (await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*')) return;
|
if (await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*')) return;
|
||||||
|
|
||||||
if (newQuantity < item.minQuantity && itemInfo.available != newQuantity)
|
if (newQuantity < item.minQuantity && newQuantity != available)
|
||||||
throw new UserError('The amount cannot be less than the minimum');
|
throw new UserError('The amount cannot be less than the minimum');
|
||||||
|
|
||||||
if (!ctx.isNewInstance && newQuantity > oldQuantity)
|
if (ctx.isNewInstance || isReduction) return;
|
||||||
throw new UserError('The new quantity should be smaller than the old one');
|
|
||||||
|
const [saleGrouping] = await models.Sale.rawSql(`
|
||||||
|
SELECT t.price newPrice
|
||||||
|
FROM tmp.ticketComponentPrice t
|
||||||
|
ORDER BY (t.grouping <= ?) DESC, t.grouping ASC
|
||||||
|
LIMIT 1`,
|
||||||
|
[quantityAdded],
|
||||||
|
ctx.options);
|
||||||
|
|
||||||
|
await models.Sale.rawSql(`
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS
|
||||||
|
tmp.ticketCalculateItem,
|
||||||
|
tmp.ticketComponentPrice,
|
||||||
|
tmp.ticketComponent,
|
||||||
|
tmp.ticketLot,
|
||||||
|
tmp.zoneGetShipped;
|
||||||
|
`, null, ctx.options);
|
||||||
|
|
||||||
|
if (!saleGrouping?.newPrice || saleGrouping.newPrice > instance.price)
|
||||||
|
throw new UserError('The price of the item changed');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,361 @@
|
||||||
|
/* eslint max-len: ["error", { "code": 150 }]*/
|
||||||
|
|
||||||
|
const models = require('vn-loopback/server/server').models;
|
||||||
|
const LoopBackContext = require('loopback-context');
|
||||||
|
|
||||||
|
describe('sale model ', () => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 9},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function getActiveCtx(userId) {
|
||||||
|
return {
|
||||||
|
active: {
|
||||||
|
accessToken: {userId},
|
||||||
|
http: {
|
||||||
|
req: {
|
||||||
|
headers: {origin: 'http://localhost'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('quantity field ', () => {
|
||||||
|
it('should add quantity if the quantity is greater than it should be and is role advanced', async() => {
|
||||||
|
const saleId = 17;
|
||||||
|
const buyerId = 35;
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: buyerId},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(buyerId));
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
if (sqlStatement.includes('catalog_calcFromItem')) {
|
||||||
|
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
|
||||||
|
SELECT 100 as available;`;
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
|
||||||
|
|
||||||
|
expect(isRoleAdvanced).toEqual(true);
|
||||||
|
|
||||||
|
const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
||||||
|
|
||||||
|
expect(originalLine.quantity).toEqual(30);
|
||||||
|
|
||||||
|
const newQuantity = originalLine.quantity + 1;
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
||||||
|
|
||||||
|
expect(modifiedLine.quantity).toEqual(newQuantity);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update the quantity of a given sale current line', async() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const saleId = 25;
|
||||||
|
const newQuantity = 4;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
||||||
|
|
||||||
|
expect(originalLine.quantity).toEqual(20);
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
||||||
|
|
||||||
|
expect(modifiedLine.quantity).toEqual(newQuantity);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the quantity is negative and it is not a refund ticket', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const saleId = 17;
|
||||||
|
const newQuantity = -10;
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toEqual(new Error('You can only add negative amounts in refund tickets'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update a negative quantity when is a ticket refund', async() => {
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(9));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const saleId = 13;
|
||||||
|
const newQuantity = -10;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options);
|
||||||
|
|
||||||
|
expect(modifiedLine.quantity).toEqual(newQuantity);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error if the quantity is less than the minimum quantity of the item', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const itemId = 2;
|
||||||
|
const saleId = 17;
|
||||||
|
const minQuantity = 30;
|
||||||
|
const newQuantity = minQuantity - 1;
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
if (sqlStatement.includes('catalog_calcFromItem')) {
|
||||||
|
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
|
||||||
|
SELECT 100 as available;`;
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toEqual(new Error('The amount cannot be less than the minimum'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change quantity if has minimum quantity and new quantity is equal than item available', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const itemId = 2;
|
||||||
|
const saleId = 17;
|
||||||
|
const minQuantity = 30;
|
||||||
|
const newQuantity = minQuantity - 1;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
if (sqlStatement.includes('catalog_calcFromItem')) {
|
||||||
|
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
|
||||||
|
SELECT ${newQuantity} as available;`;
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('newPrice', () => {
|
||||||
|
it('should increase quantity if you have enough available and the new price is the same as the previous one', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const itemId = 2;
|
||||||
|
const saleId = 17;
|
||||||
|
const minQuantity = 30;
|
||||||
|
const newQuantity = 31;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
if (sqlStatement.includes('catalog_calcFromItem')) {
|
||||||
|
sqlStatement = `
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 7.07 as price;`;
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should increase quantity when the new price is lower than the previous one', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const itemId = 2;
|
||||||
|
const saleId = 17;
|
||||||
|
const minQuantity = 30;
|
||||||
|
const newQuantity = 31;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
if (sqlStatement.includes('catalog_calcFromItem')) {
|
||||||
|
sqlStatement = `
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 1 as price;`;
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when increase quantity and the new price is higher than the previous one', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const itemId = 2;
|
||||||
|
const saleId = 17;
|
||||||
|
const minQuantity = 30;
|
||||||
|
const newQuantity = 31;
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
if (sqlStatement.includes('catalog_calcFromItem')) {
|
||||||
|
sqlStatement = `
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
|
||||||
|
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 100000 as price;`;
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toEqual(new Error('The price of the item changed'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -16,8 +16,8 @@ class Controller extends SearchPanel {
|
||||||
this.$http.get('ItemPackingTypes', {filter}).then(res => {
|
this.$http.get('ItemPackingTypes', {filter}).then(res => {
|
||||||
for (let ipt of res.data) {
|
for (let ipt of res.data) {
|
||||||
itemPackingTypes.push({
|
itemPackingTypes.push({
|
||||||
code: ipt.code,
|
description: this.$t(ipt.description),
|
||||||
description: this.$t(ipt.description)
|
code: ipt.code
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.itemPackingTypes = itemPackingTypes;
|
this.itemPackingTypes = itemPackingTypes;
|
||||||
|
|
|
@ -163,26 +163,16 @@ export default class Controller extends Section {
|
||||||
return {'futureId': value};
|
return {'futureId': value};
|
||||||
case 'liters':
|
case 'liters':
|
||||||
return {'liters': value};
|
return {'liters': value};
|
||||||
case 'lines':
|
|
||||||
return {'lines': value};
|
|
||||||
case 'futureLiters':
|
case 'futureLiters':
|
||||||
return {'futureLiters': value};
|
return {'futureLiters': value};
|
||||||
|
case 'lines':
|
||||||
|
return {'lines': value};
|
||||||
case 'futureLines':
|
case 'futureLines':
|
||||||
return {'futureLines': value};
|
return {'futureLines': value};
|
||||||
case 'ipt':
|
case 'ipt':
|
||||||
return {or:
|
return {'ipt': {like: `%${value}%`}};
|
||||||
[
|
|
||||||
{'ipt': {like: `%${value}%`}},
|
|
||||||
{'ipt': null}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
case 'futureIpt':
|
case 'futureIpt':
|
||||||
return {or:
|
return {'futureIpt': {like: `%${value}%`}};
|
||||||
[
|
|
||||||
{'futureIpt': {like: `%${value}%`}},
|
|
||||||
{'futureIpt': null}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
case 'totalWithVat':
|
case 'totalWithVat':
|
||||||
return {'totalWithVat': value};
|
return {'totalWithVat': value};
|
||||||
case 'futureTotalWithVat':
|
case 'futureTotalWithVat':
|
||||||
|
|
|
@ -109,13 +109,13 @@ module.exports = Self => {
|
||||||
const absenceType = await models.AbsenceType.findById(args.absenceTypeId, null, myOptions);
|
const absenceType = await models.AbsenceType.findById(args.absenceTypeId, null, myOptions);
|
||||||
const account = await models.VnUser.findById(userId, null, myOptions);
|
const account = await models.VnUser.findById(userId, null, myOptions);
|
||||||
const subordinated = await models.VnUser.findById(id, null, myOptions);
|
const subordinated = await models.VnUser.findById(id, null, myOptions);
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const body = $t('Created absence', {
|
const body = $t('Created absence', {
|
||||||
author: account.nickname,
|
author: account.nickname,
|
||||||
employee: subordinated.nickname,
|
employee: subordinated.nickname,
|
||||||
absenceType: absenceType.name,
|
absenceType: absenceType.name,
|
||||||
dated: formatDate(args.dated),
|
dated: formatDate(args.dated),
|
||||||
workerUrl: `${origin}/#!/worker/${id}/calendar`
|
workerUrl: `${url}worker/${id}/calendar`
|
||||||
});
|
});
|
||||||
await models.Mail.create({
|
await models.Mail.create({
|
||||||
subject: $t('Absence change notification on the labour calendar'),
|
subject: $t('Absence change notification on the labour calendar'),
|
||||||
|
|
|
@ -60,13 +60,13 @@ module.exports = Self => {
|
||||||
const absenceType = await models.AbsenceType.findById(absence.dayOffTypeFk, null, myOptions);
|
const absenceType = await models.AbsenceType.findById(absence.dayOffTypeFk, null, myOptions);
|
||||||
const account = await models.VnUser.findById(userId, null, myOptions);
|
const account = await models.VnUser.findById(userId, null, myOptions);
|
||||||
const subordinated = await models.VnUser.findById(labour.workerFk, null, myOptions);
|
const subordinated = await models.VnUser.findById(labour.workerFk, null, myOptions);
|
||||||
const origin = ctx.req.headers.origin;
|
const url = await Self.app.models.Url.getUrl();
|
||||||
const body = $t('Deleted absence', {
|
const body = $t('Deleted absence', {
|
||||||
author: account.nickname,
|
author: account.nickname,
|
||||||
employee: subordinated.nickname,
|
employee: subordinated.nickname,
|
||||||
absenceType: absenceType.name,
|
absenceType: absenceType.name,
|
||||||
dated: formatDate(absence.dated),
|
dated: formatDate(absence.dated),
|
||||||
workerUrl: `${origin}/#!/worker/${id}/calendar`
|
workerUrl: `${url}worker/${id}/calendar`
|
||||||
});
|
});
|
||||||
await models.Mail.create({
|
await models.Mail.create({
|
||||||
subject: $t('Absence change notification on the labour calendar'),
|
subject: $t('Absence change notification on the labour calendar'),
|
||||||
|
|
Loading…
Reference in New Issue