2891 - Get ticket problems for every sale #629
|
@ -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",
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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);
|
||||||
|
|
|
@ -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]);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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});
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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']});
|
||||||
|
|
||||||
|
|
|
@ -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});
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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');
|
||||||
|
|
||||||
|
|
|
@ -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`
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
|
@ -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 = ?
|
|
|
@ -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 = ?
|
Loading…
Reference in New Issue