2891 - Get ticket problems for every sale #629

Merged
carlosjr merged 7 commits from 2891-ticket_sale_problems into dev 2021-05-27 09:49:44 +00:00
15 changed files with 171 additions and 73 deletions
Showing only changes of commit a24cfe0087 - Show all commits

View File

@ -86,6 +86,7 @@
"The current ticket can't be modified": "El ticket actual no puede ser modificado", "The current ticket can't be modified": "El ticket actual no puede ser modificado",
"The current claim can't be modified": "La reclamación actual no puede ser modificada", "The current claim can't be modified": "La reclamación actual no puede ser modificada",
"The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas", "The sales of this ticket can't be modified": "Las lineas de este ticket no pueden ser modificadas",
"Sale(s) blocked, contact production": "Linea(s) bloqueada(s), contacte con produccion",
"Please select at least one sale": "Por favor selecciona al menos una linea", "Please select at least one sale": "Por favor selecciona al menos una linea",
"All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket", "All sales must belong to the same ticket": "Todas las lineas deben pertenecer al mismo ticket",
"NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada", "NO_ZONE_FOR_THIS_PARAMETERS": "Para este día no hay ninguna zona configurada",

View File

@ -0,0 +1,40 @@
module.exports = Self => {
Self.remoteMethodCtx('canEdit', {
description: 'Check if all the received sales are aditable',
accessType: 'READ',
accepts: [{
arg: 'sales',
type: ['object'],
required: true
}],
returns: {
type: 'boolean',
root: true
},
http: {
path: `/isEditable`,
verb: 'get'
}
});
Self.canEdit = async(ctx, sales, options) => {
const models = Self.app.models;
const userId = ctx.req.accessToken.userId;
const myOptions = {};
if (typeof options == 'object')
Object.assign(myOptions, options);
const idsCollection = sales.map(sale => sale.id);
const saleTracking = await models.SaleTracking.find({where: {saleFk: {inq: idsCollection}}}, myOptions);
const hasSaleTracking = saleTracking.length;
const isProductionRole = await models.Account.hasRole(userId, 'production', myOptions);
const canEdit = (isProductionRole || !hasSaleTracking);
return canEdit;
};
};

View File

@ -28,10 +28,16 @@ module.exports = Self => {
Self.deleteSales = async(ctx, sales, ticketId) => { Self.deleteSales = async(ctx, sales, ticketId) => {
const models = Self.app.models; const models = Self.app.models;
const canEditSales = await models.Sale.canEdit(ctx, sales);
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId); const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId);
if (!isTicketEditable) if (!isTicketEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
if (!canEditSales)
throw new UserError(`Sale(s) blocked, please contact production`);
const promises = []; const promises = [];
for (let sale of sales) { for (let sale of sales) {
const deletedSale = models.Sale.destroyById(sale.id); const deletedSale = models.Sale.destroyById(sale.id);

View File

@ -29,6 +29,11 @@ module.exports = Self => {
if (!isEditable) if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
const canEditSale = await models.Sale.canEdit(ctx, [id]);
if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`);
return Self.rawSql('CALL vn.sale_calculateComponent(?, null)', [id]); return Self.rawSql('CALL vn.sale_calculateComponent(?, null)', [id]);
}; };
}; };

View File

@ -37,6 +37,11 @@ module.exports = Self => {
if (!isTicketEditable) if (!isTicketEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
const canEditSale = await models.Sale.canEdit(ctx, sales);
if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`);
const promises = []; const promises = [];
for (let sale of sales) { for (let sale of sales) {
const reservedSale = models.Sale.update({id: sale.id}, {reserved: reserved}); const reservedSale = models.Sale.update({id: sale.id}, {reserved: reserved});

View File

@ -0,0 +1,36 @@
const app = require('vn-loopback/server/server');
describe('sale canEdit()', () => {
it('should return true if the role is production regardless of the saleTrackings', async() => {
const productionUserID = 49;
let ctx = {req: {accessToken: {userId: productionUserID}}};
const sales = [{id: 3}];
const result = await app.models.Sale.canEdit(ctx, sales);
expect(result).toEqual(true);
});
it('should return true if the role is not production and none of the sales has saleTracking', async() => {
const salesPersonUserID = 18;
let ctx = {req: {accessToken: {userId: salesPersonUserID}}};
const sales = [{id: 10}];
const result = await app.models.Sale.canEdit(ctx, sales);
expect(result).toEqual(true);
});
it('should return false if any of the sales has a saleTracking record', async() => {
const salesPersonUserID = 18;
let ctx = {req: {accessToken: {userId: salesPersonUserID}}};
const sales = [{id: 3}];
const result = await app.models.Sale.canEdit(ctx, sales);
expect(result).toEqual(false);
});
});

View File

@ -1,6 +1,7 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('sale updateConcept()', () => { describe('sale updateConcept()', () => {
const ctx = {req: {accessToken: {userId: 9}}};
const saleId = 1; const saleId = 1;
let originalSale; let originalSale;
@ -21,7 +22,7 @@ describe('sale updateConcept()', () => {
const newConcept = 'I am he new concept'; const newConcept = 'I am he new concept';
try { try {
await app.models.Sale.updateConcept(undefined, newConcept); await app.models.Sale.updateConcept(ctx, undefined, newConcept);
} catch (e) { } catch (e) {
err = e; err = e;
} }
@ -32,7 +33,7 @@ describe('sale updateConcept()', () => {
it('should update the sale concept', async() => { it('should update the sale concept', async() => {
const newConcept = 'I am the new concept'; const newConcept = 'I am the new concept';
let response = await app.models.Sale.updateConcept(saleId, newConcept); let response = await app.models.Sale.updateConcept(ctx, saleId, newConcept);
expect(response.concept).toEqual(newConcept); expect(response.concept).toEqual(newConcept);
}); });

View File

@ -1,10 +1,12 @@
const app = require('vn-loopback/server/server'); const app = require('vn-loopback/server/server');
describe('sale updateQuantity()', () => { describe('sale updateQuantity()', () => {
const ctx = {req: {accessToken: {userId: 9}}};
it('should throw an error if the quantity is not a number', async() => { it('should throw an error if the quantity is not a number', async() => {
let error; let error;
await app.models.Sale.updateQuantity(1, 'wrong quantity!') await app.models.Sale.updateQuantity(ctx, 1, 'wrong quantity!')
.catch(response => { .catch(response => {
expect(response).toEqual(new Error('The value should be a number')); expect(response).toEqual(new Error('The value should be a number'));
error = response; error = response;
@ -16,7 +18,7 @@ describe('sale updateQuantity()', () => {
it('should throw an error if the quantity is greater than it should be', async() => { it('should throw an error if the quantity is greater than it should be', async() => {
let error; let error;
await app.models.Sale.updateQuantity(1, 99) await app.models.Sale.updateQuantity(ctx, 1, 99)
.catch(response => { .catch(response => {
expect(response).toEqual(new Error('The new quantity should be smaller than the old one')); expect(response).toEqual(new Error('The new quantity should be smaller than the old one'));
error = response; error = response;
@ -30,7 +32,7 @@ describe('sale updateQuantity()', () => {
expect(originalLineData.quantity).toEqual(5); expect(originalLineData.quantity).toEqual(5);
await app.models.Sale.updateQuantity(1, 4); await app.models.Sale.updateQuantity(ctx, 1, 4);
let modifiedLineData = await app.models.Sale.findOne({where: {id: 1}, fields: ['quantity']}); let modifiedLineData = await app.models.Sale.findOne({where: {id: 1}, fields: ['quantity']});

View File

@ -1,5 +1,5 @@
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('updateConcept', { Self.remoteMethodCtx('updateConcept', {
description: 'Updates the concept of a sale', description: 'Updates the concept of a sale',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
@ -24,8 +24,14 @@ module.exports = Self => {
} }
}); });
Self.updateConcept = async(id, newConcept) => { Self.updateConcept = async(ctx, id, newConcept) => {
let currentLine = await Self.app.models.Sale.findById(id); const models = Self.app.models;
const currentLine = await models.Sale.findById(id);
const canEditSale = await models.Sale.canEdit(ctx, [id]);
if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`);
return await currentLine.updateAttributes({concept: newConcept}); return await currentLine.updateAttributes({concept: newConcept});
}; };

View File

@ -55,6 +55,11 @@ module.exports = Self => {
if (!isEditable) if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`); throw new UserError(`The sales of this ticket can't be modified`);
const canEditSale = await models.Sale.canEdit(ctx, [id]);
if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`);
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
let usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, options); let usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, options);

View File

@ -1,7 +1,7 @@
let UserError = require('vn-loopback/util/user-error'); let UserError = require('vn-loopback/util/user-error');
module.exports = Self => { module.exports = Self => {
Self.remoteMethod('updateQuantity', { Self.remoteMethodCtx('updateQuantity', {
description: 'Changes the quantity of a sale', description: 'Changes the quantity of a sale',
accessType: 'WRITE', accessType: 'WRITE',
accepts: [{ accepts: [{
@ -26,11 +26,18 @@ module.exports = Self => {
} }
}); });
Self.updateQuantity = async(id, quantity) => { Self.updateQuantity = async(ctx, id, quantity) => {
const models = Self.app.models;
const canEditSale = await models.Sale.canEdit(ctx, [id]);
if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`);
if (isNaN(quantity)) if (isNaN(quantity))
throw new UserError(`The value should be a number`); throw new UserError(`The value should be a number`);
let currentLine = await Self.app.models.Sale.findOne({where: {id: id}}); let currentLine = await models.Sale.findOne({where: {id: id}});
if (quantity > currentLine.quantity) if (quantity > currentLine.quantity)
throw new UserError('The new quantity should be smaller than the old one'); throw new UserError('The new quantity should be smaller than the old one');

View File

@ -6,6 +6,7 @@ module.exports = Self => {
require('../methods/sale/updateQuantity')(Self); require('../methods/sale/updateQuantity')(Self);
require('../methods/sale/updateConcept')(Self); require('../methods/sale/updateConcept')(Self);
require('../methods/sale/recalculatePrice')(Self); require('../methods/sale/recalculatePrice')(Self);
require('../methods/sale/canEdit')(Self);
Self.validatesPresenceOf('concept', { Self.validatesPresenceOf('concept', {
message: `Concept cannot be blank` message: `Concept cannot be blank`

View File

@ -3,7 +3,6 @@ const Report = require(`${appPath}/core/report`);
const reportHeader = new Component('report-header'); const reportHeader = new Component('report-header');
const reportFooter = new Component('report-footer'); const reportFooter = new Component('report-footer');
const invoiceIncoterms = new Report('invoice-incoterms'); const invoiceIncoterms = new Report('invoice-incoterms');
const db = require(`${appPath}/core/database`);
module.exports = { module.exports = {
name: 'invoice', name: 'invoice',
@ -72,17 +71,8 @@ module.exports = {
fetchTickets(invoiceId) { fetchTickets(invoiceId) {
return this.rawSqlFromDef('tickets', [invoiceId]); return this.rawSqlFromDef('tickets', [invoiceId]);
}, },
async fetchSales(invoiceId) { fetchSales(invoiceId) {
const connection = await db.getConnection('default'); return this.rawSqlFromDef('sales', [invoiceId, invoiceId]);
await this.rawSql(`DROP TEMPORARY TABLE IF EXISTS tmp.invoiceTickets`, connection);
await this.rawSqlFromDef('invoiceTickets', [invoiceId], connection);
const sales = this.rawSqlFromDef('sales', connection);
await this.rawSql(`DROP TEMPORARY TABLE tmp.invoiceTickets`, connection);
await connection.release();
return sales;
}, },
fetchTaxes(invoiceId) { fetchTaxes(invoiceId) {
return this.rawSqlFromDef(`taxes`, [invoiceId]); return this.rawSqlFromDef(`taxes`, [invoiceId]);

View File

@ -1,20 +0,0 @@
CREATE TEMPORARY TABLE tmp.invoiceTickets
ENGINE = MEMORY
SELECT
t.id AS ticketFk,
t.clientFk,
t.shipped,
t.nickname,
io.ref,
c.socialName,
sa.iban,
pm.name AS payMethod,
su.countryFk AS supplierCountryFk
FROM vn.invoiceOut io
JOIN vn.supplier su ON su.id = io.companyFk
JOIN vn.ticket t ON t.refFk = io.ref
JOIN vn.client c ON c.id = t.clientFk
JOIN vn.payMethod pm ON pm.id = c.payMethodFk
JOIN vn.company co ON co.id = io.companyFk
JOIN vn.supplierAccount sa ON sa.id = co.supplierAccountFk
WHERE io.id = ?

View File

@ -1,11 +1,11 @@
SELECT SELECT
it.ref, io.ref,
it.socialName, c.socialName,
it.iban, sa.iban,
it.payMethod, pm.name AS payMethod,
it.clientFk, t.clientFk,
it.shipped, t.shipped,
it.nickname, t.nickname,
s.ticketFk, s.ticketFk,
s.itemFk, s.itemFk,
s.concept, s.concept,
@ -20,27 +20,34 @@ SELECT
i.value7, i.value7,
tc.code AS vatType, tc.code AS vatType,
ib.ediBotanic botanical ib.ediBotanic botanical
FROM tmp.invoiceTickets it FROM vn.invoiceOut io
JOIN vn.sale s ON s.ticketFk = it.ticketFk JOIN vn.ticket t ON t.refFk = io.ref
JOIN item i ON i.id = s.itemFk JOIN vn.supplier su ON su.id = io.companyFk
LEFT JOIN itemType it ON it.id = i.typeFk JOIN vn.client c ON c.id = t.clientFk
LEFT JOIN itemCategory ic ON ic.id = it.categoryFk JOIN vn.payMethod pm ON pm.id = c.payMethodFk
LEFT JOIN itemBotanicalWithGenus ib ON ib.itemFk = i.id JOIN vn.company co ON co.id = io.companyFk
AND ic.code = 'plant' JOIN vn.supplierAccount sa ON sa.id = co.supplierAccountFk
AND ib.ediBotanic IS NOT NULL JOIN vn.sale s ON s.ticketFk = t.id
JOIN vn.itemTaxCountry itc ON itc.countryFk = it.supplierCountryFk JOIN item i ON i.id = s.itemFk
AND itc.itemFk = s.itemFk LEFT JOIN itemType it ON it.id = i.typeFk
JOIN vn.taxClass tc ON tc.id = itc.taxClassFk LEFT JOIN itemCategory ic ON ic.id = it.categoryFk
UNION ALL LEFT JOIN itemBotanicalWithGenus ib ON ib.itemFk = i.id
AND ic.code = 'plant'
AND ib.ediBotanic IS NOT NULL
JOIN vn.itemTaxCountry itc ON itc.countryFk = su.countryFk
AND itc.itemFk = s.itemFk
JOIN vn.taxClass tc ON tc.id = itc.taxClassFk
WHERE io.id = ?
UNION ALL
SELECT SELECT
it.ref, io.ref,
it.socialName, c.socialName,
it.iban, sa.iban,
it.payMethod, pm.name AS payMethod,
it.clientFk, t.clientFk,
it.shipped, t.shipped,
it.nickname, t.nickname,
it.ticketFk, t.id AS ticketFk,
'', '',
ts.description concept, ts.description concept,
ts.quantity, ts.quantity,
@ -54,6 +61,12 @@ SELECT
NULL AS value7, NULL AS value7,
tc.code AS vatType, tc.code AS vatType,
NULL AS botanical NULL AS botanical
FROM tmp.invoiceTickets it FROM vn.invoiceOut io
JOIN vn.ticketService ts ON ts.ticketFk = it.ticketFk JOIN vn.ticket t ON t.refFk = io.ref
JOIN vn.taxClass tc ON tc.id = ts.taxClassFk JOIN vn.ticketService ts ON ts.ticketFk = t.id
JOIN vn.client c ON c.id = t.clientFk
JOIN vn.payMethod pm ON pm.id = c.payMethodFk
JOIN vn.company co ON co.id = io.companyFk
JOIN vn.supplierAccount sa ON sa.id = co.supplierAccountFk
JOIN vn.taxClass tc ON tc.id = ts.taxClassFk
WHERE io.id = ?