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) => {
|
||||
if (!recipientId) return false;
|
||||
|
||||
const models = Self.app.models;
|
||||
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
const sender = await models.VnUser.findById(userId, {fields: ['id']});
|
||||
const recipient = await models.VnUser.findById(recipientId, null);
|
||||
|
||||
// Prevent sending messages to yourself
|
||||
if (recipientId == userId) return false;
|
||||
|
||||
if (!recipient)
|
||||
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) => {
|
||||
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 myOptions = {};
|
||||
|
||||
|
@ -36,7 +36,6 @@ module.exports = Self => {
|
|||
myOptions.userId = userId;
|
||||
|
||||
const promises = [];
|
||||
|
||||
const [tickets] = await Self.rawSql(`CALL vn.collection_getTickets(?)`, [id], myOptions);
|
||||
const sales = await Self.rawSql(`
|
||||
SELECT s.ticketFk,
|
||||
|
@ -86,24 +85,19 @@ module.exports = Self => {
|
|||
if (tickets && tickets.length) {
|
||||
for (const ticket of tickets) {
|
||||
const ticketId = ticket.ticketFk;
|
||||
|
||||
// SEND ROCKET
|
||||
if (ticket.observaciones != '') {
|
||||
for (observation of ticket.observaciones.split(' ')) {
|
||||
if (['#', '@'].includes(observation.charAt(0))) {
|
||||
promises.push(Self.app.models.Chat.send(ctx, observation,
|
||||
$t('The ticket is in preparation', {
|
||||
ticketId: ticketId,
|
||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/summary`,
|
||||
ticketUrl: `${url}ticket/${ticketId}/summary`,
|
||||
salesPersonId: ticket.salesPersonFk
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SET COLLECTION
|
||||
if (sales && sales.length) {
|
||||
// GET BARCODES
|
||||
const barcodes = await Self.rawSql(`
|
||||
SELECT s.id saleFk, b.code, c.id
|
||||
FROM vn.sale s
|
||||
|
@ -114,13 +108,10 @@ module.exports = Self => {
|
|||
WHERE s.ticketFk = ?
|
||||
AND tr.landed >= util.VN_CURDATE() - INTERVAL 1 YEAR`,
|
||||
[ticketId], myOptions);
|
||||
|
||||
// BINDINGS
|
||||
ticket.sales = [];
|
||||
for (const sale of sales) {
|
||||
if (sale.ticketFk === ticketId) {
|
||||
sale.Barcodes = [];
|
||||
|
||||
if (barcodes && barcodes.length) {
|
||||
for (const barcode of barcodes) {
|
||||
if (barcode.saleFk === sale.saleFk) {
|
||||
|
@ -131,7 +122,6 @@ module.exports = Self => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ticket.sales.push(sale);
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +130,6 @@ module.exports = Self => {
|
|||
}
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
return collection;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -18,7 +18,14 @@ describe('setSaleQuantity()', () => {
|
|||
|
||||
it('should change quantity sale', async() => {
|
||||
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 {
|
||||
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) {
|
||||
if (!ctx.isNewInstance) return;
|
||||
|
||||
let {message} = ctx.instance;
|
||||
if (!message) return;
|
||||
|
||||
const parts = message.match(/(?<=\[)[a-zA-Z0-9_\-+!@#$%^&*()={};':"\\|,.<>/?\s]*(?=])/g);
|
||||
if (!parts) return;
|
||||
|
||||
const replacedParts = parts.map(part => {
|
||||
return part.replace(/[!$%^&*()={};':"\\,.<>/?]/g, '');
|
||||
});
|
||||
|
||||
for (const [index, part] of parts.entries())
|
||||
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 LoopBackContext = require('loopback-context');
|
||||
const {Email} = require('vn-print');
|
||||
|
||||
module.exports = function(Self) {
|
||||
|
@ -90,11 +89,7 @@ module.exports = function(Self) {
|
|||
};
|
||||
|
||||
Self.on('resetPasswordRequest', async function(info) {
|
||||
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 = await Self.app.models.Url.getUrl();
|
||||
|
||||
const defaultHash = '/reset-password?access_token=$token$';
|
||||
const recoverHashes = {
|
||||
|
@ -110,7 +105,7 @@ module.exports = function(Self) {
|
|||
const params = {
|
||||
recipient: info.email,
|
||||
lang: user.lang,
|
||||
url: origin + '/#!' + recoverHash
|
||||
url: url.slice(0, -1) + recoverHash
|
||||
};
|
||||
|
||||
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
|
||||
.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);
|
||||
});
|
||||
|
||||
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 stmt;
|
||||
|
||||
|
|
|
@ -134,14 +134,6 @@ describe('Ticket Edit sale path', () => {
|
|||
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() => {
|
||||
await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
|
||||
await page.waitForSelector(selectors.ticketSales.firstSaleQuantity);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"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",
|
||||
"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",
|
||||
"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",
|
||||
|
|
|
@ -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",
|
||||
"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",
|
||||
"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 be a number": "El valor debe ser un numero",
|
||||
"This order is not editable": "Esta orden no se puede modificar",
|
||||
|
|
|
@ -43,9 +43,8 @@ module.exports = Self => {
|
|||
|
||||
Self.claimPickupEmail = async ctx => {
|
||||
const models = Self.app.models;
|
||||
const userId = ctx.req.accessToken.userId;
|
||||
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 params = {
|
||||
|
@ -70,9 +69,8 @@ module.exports = Self => {
|
|||
const message = $t('Claim pickup order sent', {
|
||||
claimId: args.id,
|
||||
clientName: claim.client().name,
|
||||
claimUrl: `${origin}/#!/claim/${args.id}/summary`,
|
||||
claimUrl: `${url}claim/${args.id}/summary`,
|
||||
});
|
||||
|
||||
const salesPersonId = claim.client().salesPersonFk;
|
||||
if (salesPersonId)
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||
|
|
|
@ -94,13 +94,13 @@ module.exports = Self => {
|
|||
|
||||
const salesPerson = ticket.client().salesPersonUser();
|
||||
if (salesPerson) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
|
||||
const message = $t('Created claim', {
|
||||
claimId: newClaim.id,
|
||||
ticketId: ticketId,
|
||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
||||
claimUrl: `${origin}/#!/claim/${newClaim.id}/summary`,
|
||||
ticketUrl: `${url}ticket/${ticketId}/sale`,
|
||||
claimUrl: `${url}claim/${newClaim.id}/summary`,
|
||||
changes: changesMade
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
||||
|
|
|
@ -56,15 +56,15 @@ module.exports = Self => {
|
|||
const salesPerson = sale.ticket().client().salesPersonUser();
|
||||
if (salesPerson) {
|
||||
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', {
|
||||
quantity: sale.quantity,
|
||||
concept: sale.concept,
|
||||
itemId: sale.itemFk,
|
||||
ticketId: sale.ticketFk,
|
||||
nickname: nickname,
|
||||
ticketUrl: `${origin}/#!/ticket/${sale.ticketFk}/sale`,
|
||||
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
|
||||
ticketUrl: `${url}ticket/${sale.ticketFk}/sale`,
|
||||
itemUrl: `${url}item/${sale.itemFk}/summary`
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ const app = require('vn-loopback/server/server');
|
|||
const LoopBackContext = require('loopback-context');
|
||||
|
||||
describe('Update Claim', () => {
|
||||
let url;
|
||||
beforeAll(async() => {
|
||||
url = await app.models.Url.getUrl();
|
||||
const activeCtx = {
|
||||
accessToken: {userId: 9},
|
||||
http: {
|
||||
|
@ -29,7 +31,6 @@ describe('Update Claim', () => {
|
|||
|
||||
it(`should throw an error as the user doesn't have rights`, async() => {
|
||||
const tx = await app.models.Claim.beginTransaction({});
|
||||
|
||||
let error;
|
||||
|
||||
try {
|
||||
|
@ -77,7 +78,7 @@ describe('Update Claim', () => {
|
|||
const ctx = {
|
||||
req: {
|
||||
accessToken: {userId: claimManagerId},
|
||||
headers: {origin: 'http://localhost'}
|
||||
headers: {origin: url}
|
||||
},
|
||||
args: {
|
||||
observation: 'valid observation',
|
||||
|
@ -118,7 +119,7 @@ describe('Update Claim', () => {
|
|||
const ctx = {
|
||||
req: {
|
||||
accessToken: {userId: claimManagerId},
|
||||
headers: {origin: 'http://localhost'}
|
||||
headers: {origin: url}
|
||||
},
|
||||
args: {
|
||||
observation: 'valid observation',
|
||||
|
|
|
@ -91,16 +91,16 @@ module.exports = Self => {
|
|||
|
||||
// When hasToPickUp has been changed
|
||||
if (salesPerson && changedHasToPickUp && updatedClaim.hasToPickUp)
|
||||
notifyPickUp(ctx, salesPerson.id, claim);
|
||||
await notifyPickUp(ctx, salesPerson.id, claim);
|
||||
|
||||
// When claimState has been changed
|
||||
if (args.claimStateFk) {
|
||||
const newState = await models.ClaimState.findById(args.claimStateFk, null, myOptions);
|
||||
if (newState.hasToNotify) {
|
||||
if (newState.code == 'incomplete')
|
||||
notifyStateChange(ctx, salesPerson.id, claim, newState.code);
|
||||
await notifyStateChange(ctx, salesPerson.id, claim, newState.code);
|
||||
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) {
|
||||
const models = Self.app.models;
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await models.Url.getUrl();
|
||||
const $t = ctx.req.__; // $translate
|
||||
|
||||
const message = $t(`Claim state has changed to ${state}`, {
|
||||
claimId: claim.id,
|
||||
clientName: claim.client().name,
|
||||
claimUrl: `${origin}/#!/claim/${claim.id}/summary`
|
||||
claimUrl: `${url}claim/${claim.id}/summary`
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, workerId, message);
|
||||
}
|
||||
|
||||
async function notifyPickUp(ctx, workerId, claim) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
const models = Self.app.models;
|
||||
const url = await models.Url.getUrl();
|
||||
const $t = ctx.req.__; // $translate
|
||||
|
||||
const message = $t('Claim will be picked', {
|
||||
claimId: claim.id,
|
||||
clientName: claim.client().name,
|
||||
claimUrl: `${origin}/#!/claim/${claim.id}/summary`
|
||||
claimUrl: `${url}claim/${claim.id}/summary`
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, workerId, message);
|
||||
}
|
||||
|
|
|
@ -250,7 +250,12 @@ module.exports = Self => {
|
|||
const loopBackContext = LoopBackContext.getCurrentContext();
|
||||
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 isTaxDataChecked = hasChanges && (changes.isTaxDataChecked || orgData.isTaxDataChecked);
|
||||
|
@ -263,7 +268,9 @@ module.exports = Self => {
|
|||
const sageTransactionTypeChanged = hasChanges && orgData.sageTransactionTypeFk != sageTransactionType;
|
||||
|
||||
const cantEditVerifiedData = isTaxDataCheckedChanged && !editVerifiedDataWithoutTaxDataChecked;
|
||||
const cantChangeSageData = (sageTaxTypeChanged || sageTransactionTypeChanged) && !editVerifiedDataWithoutTaxDataChecked;
|
||||
const cantChangeSageData = (sageTaxTypeChanged ||
|
||||
sageTransactionTypeChanged
|
||||
) && !editVerifiedDataWithoutTaxDataChecked;
|
||||
|
||||
if (cantEditVerifiedData || cantChangeSageData)
|
||||
throw new UserError(`You don't have enough privileges`);
|
||||
|
@ -346,8 +353,7 @@ module.exports = Self => {
|
|||
const httpCtx = {req: loopBackContext.active};
|
||||
const httpRequest = httpCtx.req.http.req;
|
||||
const $t = httpRequest.__;
|
||||
const headers = httpRequest.headers;
|
||||
const origin = headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
|
||||
const salesPersonId = instance.salesPersonFk;
|
||||
|
||||
|
@ -366,7 +372,7 @@ module.exports = Self => {
|
|||
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', {
|
||||
clientId: instance.id,
|
||||
clientName: instance.name,
|
||||
|
@ -389,8 +395,7 @@ module.exports = Self => {
|
|||
const httpCtx = {req: loopBackContext.active};
|
||||
const httpRequest = httpCtx.req.http.req;
|
||||
const $t = httpRequest.__;
|
||||
const headers = httpRequest.headers;
|
||||
const origin = headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
const models = Self.app.models;
|
||||
|
||||
let previousWorker = {name: $t('None')};
|
||||
|
@ -411,7 +416,7 @@ module.exports = Self => {
|
|||
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', {
|
||||
clientId: client.id,
|
||||
clientName: client.name,
|
||||
|
|
|
@ -57,8 +57,8 @@ module.exports = function(Self) {
|
|||
|
||||
const httpRequest = httpCtx.req.http.req;
|
||||
const $t = httpRequest.__;
|
||||
const origin = httpRequest.headers.origin;
|
||||
const fullPath = `${origin}/#!/client/${client.id}/credit-insurance/index`;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
const fullPath = `${url}client/${client.id}/credit-insurance/index`;
|
||||
const message = $t('MESSAGE_INSURANCE_CHANGE', {
|
||||
clientId: client.id,
|
||||
clientName: client.name,
|
||||
|
|
|
@ -59,10 +59,10 @@ module.exports = Self => {
|
|||
};
|
||||
await Self.invoiceEmail(ctx, ref);
|
||||
} catch (err) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
const message = ctx.req.__('Mail not sent', {
|
||||
clientId: client.id,
|
||||
clientUrl: `${origin}/#!/claim/${id}/summary`
|
||||
clientUrl: `${url}claim/${id}/summary`
|
||||
});
|
||||
const salesPersonId = client.salesPersonFk;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
let UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('deleteSales', {
|
||||
description: 'Deletes the selected sales',
|
||||
|
@ -70,11 +68,11 @@ module.exports = Self => {
|
|||
|
||||
const salesPerson = ticket.client().salesPersonUser();
|
||||
if (salesPerson) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
|
||||
const message = $t('Deleted sales from ticket', {
|
||||
ticketId: ticketId,
|
||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
||||
ticketUrl: `${url}ticket/${ticketId}/sale`,
|
||||
deletions: deletions
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
|
||||
let UserError = require('vn-loopback/util/user-error');
|
||||
|
||||
module.exports = Self => {
|
||||
Self.remoteMethodCtx('reserve', {
|
||||
description: 'Change the state of a ticket',
|
||||
|
@ -65,7 +62,8 @@ module.exports = Self => {
|
|||
|
||||
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();
|
||||
if (salesPerson) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
|
||||
const message = $t('Changed sale reserved state', {
|
||||
ticketId: ticketId,
|
||||
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
|
||||
ticketUrl: `${url}ticket/${ticketId}/sale`,
|
||||
changes: changesMade
|
||||
});
|
||||
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 => {
|
||||
Self.remoteMethodCtx('updatePrice', {
|
||||
description: 'Changes the price of a sale',
|
||||
|
@ -100,7 +98,7 @@ module.exports = Self => {
|
|||
|
||||
const salesPerson = sale.ticket().client().salesPersonUser();
|
||||
if (salesPerson) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
const message = $t('Changed sale price', {
|
||||
ticketId: sale.ticket().id,
|
||||
itemId: sale.itemFk,
|
||||
|
@ -108,8 +106,8 @@ module.exports = Self => {
|
|||
quantity: sale.quantity,
|
||||
oldPrice: oldPrice,
|
||||
newPrice: newPrice,
|
||||
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
|
||||
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
|
||||
ticketUrl: `${url}ticket/${sale.ticket().id}/sale`,
|
||||
itemUrl: `${url}item/${sale.itemFk}/summary`
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||
}
|
||||
|
|
|
@ -68,16 +68,17 @@ module.exports = Self => {
|
|||
|
||||
const salesPerson = sale.ticket().client().salesPersonUser();
|
||||
if (salesPerson) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
const message = $t('Changed sale quantity', {
|
||||
ticketId: sale.ticket().id,
|
||||
itemId: sale.itemFk,
|
||||
concept: sale.concept,
|
||||
oldQuantity: oldQuantity,
|
||||
newQuantity: newQuantity,
|
||||
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
|
||||
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
|
||||
ticketUrl: `${url}ticket/${sale.ticket().id}/sale`,
|
||||
itemUrl: `${url}item/${sale.itemFk}/summary`
|
||||
});
|
||||
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ module.exports = Self => {
|
|||
const query = `CALL vn.sale_calculateComponent(?, NULL)`;
|
||||
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 message = $t('Bought units from buy request', {
|
||||
|
@ -92,8 +92,8 @@ module.exports = Self => {
|
|||
concept: sale.concept,
|
||||
itemId: sale.itemFk,
|
||||
ticketId: sale.ticketFk,
|
||||
url: `${origin}/#!/ticket/${sale.ticketFk}/summary`,
|
||||
urlItem: `${origin}/#!/item/${sale.itemFk}/summary`
|
||||
url: `${url}ticket/${sale.ticketFk}/summary`,
|
||||
urlItem: `${url}item/${sale.itemFk}/summary`
|
||||
});
|
||||
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);
|
||||
await request.updateAttributes(params, myOptions);
|
||||
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
const requesterId = request.requesterFk;
|
||||
|
||||
const message = $t('Deny buy request', {
|
||||
ticketId: request.ticketFk,
|
||||
url: `${origin}/#!/ticket/${request.ticketFk}/request/index`,
|
||||
url: `${url}ticket/${request.ticketFk}/request/index`,
|
||||
observation: params.response
|
||||
});
|
||||
|
||||
|
|
|
@ -83,11 +83,11 @@ module.exports = Self => {
|
|||
|
||||
const salesPerson = ticket.client().salesPersonUser();
|
||||
if (salesPerson) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
|
||||
const message = $t('Added sale to ticket', {
|
||||
ticketId: id,
|
||||
ticketUrl: `${origin}/#!/ticket/${id}/sale`,
|
||||
ticketUrl: `${url}ticket/${id}/sale`,
|
||||
addition: addition
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
|
||||
|
|
|
@ -237,7 +237,7 @@ module.exports = Self => {
|
|||
|
||||
const salesPersonId = originalTicket.client().salesPersonFk;
|
||||
if (salesPersonId) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
|
||||
let changesMade = '';
|
||||
for (let change in newProperties) {
|
||||
|
@ -249,7 +249,7 @@ module.exports = Self => {
|
|||
|
||||
const message = $t('Changed this data from the ticket', {
|
||||
ticketId: args.id,
|
||||
ticketUrl: `${origin}/#!/ticket/${args.id}/sale`,
|
||||
ticketUrl: `${url}ticket/${args.id}/sale`,
|
||||
changes: changesMade
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||
|
|
|
@ -25,7 +25,7 @@ module.exports = Self => {
|
|||
Self.merge = async(ctx, tickets, options) => {
|
||||
const httpRequest = ctx.req;
|
||||
const $t = httpRequest.__;
|
||||
const origin = httpRequest.headers.origin;
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
const models = Self.app.models;
|
||||
const myOptions = {};
|
||||
let tx;
|
||||
|
@ -40,8 +40,8 @@ module.exports = Self => {
|
|||
|
||||
try {
|
||||
for (let ticket of tickets) {
|
||||
const originFullPath = `${origin}/#!/ticket/${ticket.originId}/summary`;
|
||||
const destinationFullPath = `${origin}/#!/ticket/${ticket.destinationId}/summary`;
|
||||
const originFullPath = `${url}ticket/${ticket.originId}/summary`;
|
||||
const destinationFullPath = `${url}ticket/${ticket.destinationId}/summary`;
|
||||
const message = $t('Ticket merged', {
|
||||
originDated: dateUtil.toString(new Date(ticket.originShipped)),
|
||||
destinationDated: dateUtil.toString(new Date(ticket.destinationShipped)),
|
||||
|
|
|
@ -48,7 +48,7 @@ module.exports = Self => {
|
|||
description: `The route id filter`
|
||||
}],
|
||||
returns: {
|
||||
type: 'number',
|
||||
type: 'object',
|
||||
root: true
|
||||
},
|
||||
http: {
|
||||
|
|
|
@ -48,10 +48,10 @@ module.exports = Self => {
|
|||
// Send notification to salesPerson
|
||||
const salesPersonId = ticket.client().salesPersonFk;
|
||||
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`, {
|
||||
id: id,
|
||||
url: `${origin}/#!/ticket/${id}/summary`
|
||||
url: `${url}ticket/${id}/summary`
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPersonId, message);
|
||||
}
|
||||
|
|
|
@ -119,10 +119,10 @@ module.exports = Self => {
|
|||
// Send notification to salesPerson
|
||||
const salesPersonUser = ticket.client().salesPersonUser();
|
||||
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`, {
|
||||
id: id,
|
||||
url: `${origin}/#!/ticket/${id}/summary`
|
||||
url: `${url}ticket/${id}/summary`
|
||||
});
|
||||
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.saleGroupDetail sgd ON sgd.saleGroupFk = sg.id
|
||||
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();
|
||||
|
||||
|
|
|
@ -165,11 +165,10 @@ module.exports = Self => {
|
|||
|
||||
const salesPerson = ticket.client().salesPersonUser();
|
||||
if (salesPerson) {
|
||||
const origin = ctx.req.headers.origin;
|
||||
|
||||
const url = await Self.app.models.Url.getUrl();
|
||||
const message = $t('Changed sale discount', {
|
||||
ticketId: id,
|
||||
ticketUrl: `${origin}/#!/ticket/${id}/sale`,
|
||||
ticketUrl: `${url}ticket/${id}/sale`,
|
||||
changes: changesMade
|
||||
});
|
||||
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
|
||||
|
|
|
@ -31,11 +31,14 @@ module.exports = Self => {
|
|||
|
||||
const ticketId = changes?.ticketFk || instance?.ticketFk;
|
||||
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(
|
||||
ticketId,
|
||||
{
|
||||
fields: ['id', 'clientFk', 'warehouseFk', 'shipped'],
|
||||
fields: ['id', 'clientFk', 'warehouseFk', 'addressFk', 'agencyModeFk', 'shipped', 'landed'],
|
||||
include: {
|
||||
relation: 'client',
|
||||
scope: {
|
||||
|
@ -65,28 +68,50 @@ module.exports = Self => {
|
|||
fields: ['family', 'minQuantity'],
|
||||
where: {id: itemId},
|
||||
}, ctx.options);
|
||||
|
||||
if (item.family == 'EMB') return;
|
||||
|
||||
const itemInfo = await models.Item.getVisibleAvailable(
|
||||
itemId,
|
||||
ticket.warehouseFk,
|
||||
ticket.shipped,
|
||||
ctx.options
|
||||
);
|
||||
if (await models.ACL.checkAccessAcl(ctx, 'Sale', 'isInPreparing', '*')) return;
|
||||
|
||||
const oldQuantity = instance?.quantity ?? null;
|
||||
const quantityAdded = newQuantity - oldQuantity;
|
||||
if (itemInfo.available < quantityAdded)
|
||||
await models.Sale.rawSql(`CALL catalog_calcFromItem(?,?,?,?)`, [
|
||||
ticket.landed,
|
||||
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`);
|
||||
|
||||
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');
|
||||
|
||||
if (!ctx.isNewInstance && newQuantity > oldQuantity)
|
||||
throw new UserError('The new quantity should be smaller than the old one');
|
||||
if (ctx.isNewInstance || isReduction) return;
|
||||
|
||||
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 => {
|
||||
for (let ipt of res.data) {
|
||||
itemPackingTypes.push({
|
||||
code: ipt.code,
|
||||
description: this.$t(ipt.description)
|
||||
description: this.$t(ipt.description),
|
||||
code: ipt.code
|
||||
});
|
||||
}
|
||||
this.itemPackingTypes = itemPackingTypes;
|
||||
|
|
|
@ -163,26 +163,16 @@ export default class Controller extends Section {
|
|||
return {'futureId': value};
|
||||
case 'liters':
|
||||
return {'liters': value};
|
||||
case 'lines':
|
||||
return {'lines': value};
|
||||
case 'futureLiters':
|
||||
return {'futureLiters': value};
|
||||
case 'lines':
|
||||
return {'lines': value};
|
||||
case 'futureLines':
|
||||
return {'futureLines': value};
|
||||
case 'ipt':
|
||||
return {or:
|
||||
[
|
||||
{'ipt': {like: `%${value}%`}},
|
||||
{'ipt': null}
|
||||
]
|
||||
};
|
||||
return {'ipt': {like: `%${value}%`}};
|
||||
case 'futureIpt':
|
||||
return {or:
|
||||
[
|
||||
{'futureIpt': {like: `%${value}%`}},
|
||||
{'futureIpt': null}
|
||||
]
|
||||
};
|
||||
return {'futureIpt': {like: `%${value}%`}};
|
||||
case 'totalWithVat':
|
||||
return {'totalWithVat': value};
|
||||
case 'futureTotalWithVat':
|
||||
|
|
|
@ -109,13 +109,13 @@ module.exports = Self => {
|
|||
const absenceType = await models.AbsenceType.findById(args.absenceTypeId, null, myOptions);
|
||||
const account = await models.VnUser.findById(userId, 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', {
|
||||
author: account.nickname,
|
||||
employee: subordinated.nickname,
|
||||
absenceType: absenceType.name,
|
||||
dated: formatDate(args.dated),
|
||||
workerUrl: `${origin}/#!/worker/${id}/calendar`
|
||||
workerUrl: `${url}worker/${id}/calendar`
|
||||
});
|
||||
await models.Mail.create({
|
||||
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 account = await models.VnUser.findById(userId, 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', {
|
||||
author: account.nickname,
|
||||
employee: subordinated.nickname,
|
||||
absenceType: absenceType.name,
|
||||
dated: formatDate(absence.dated),
|
||||
workerUrl: `${origin}/#!/worker/${id}/calendar`
|
||||
workerUrl: `${url}worker/${id}/calendar`
|
||||
});
|
||||
await models.Mail.create({
|
||||
subject: $t('Absence change notification on the labour calendar'),
|
||||
|
|
Loading…
Reference in New Issue