refactor(sale): ticket.sale endpoints now use transactions

This commit is contained in:
Carlos Jimenez Ruiz 2021-09-29 08:27:18 +02:00
parent e690febc7f
commit 392c4dcba3
15 changed files with 589 additions and 395 deletions

View File

@ -26,54 +26,73 @@ module.exports = Self => {
} }
}); });
Self.deleteSales = async(ctx, sales, ticketId) => { Self.deleteSales = async(ctx, sales, ticketId, options) => {
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const models = Self.app.models; const models = Self.app.models;
const myOptions = {};
let tx;
const canEditSales = await models.Sale.canEdit(ctx, sales); if (typeof options == 'object')
Object.assign(myOptions, options);
const ticket = await models.Ticket.findById(ticketId, { if (!myOptions.transaction) {
include: { tx = await Self.beginTransaction({});
relation: 'client', myOptions.transaction = tx;
scope: { }
include: {
relation: 'salesPersonUser', try {
scope: { const canEditSales = await models.Sale.canEdit(ctx, sales, myOptions);
fields: ['id', 'name']
const ticket = await models.Ticket.findById(ticketId, {
include: {
relation: 'client',
scope: {
include: {
relation: 'salesPersonUser',
scope: {
fields: ['id', 'name']
}
} }
} }
} }
}, myOptions);
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);
if (!isTicketEditable)
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 = [];
let deletions = '';
for (let sale of sales) {
const deletedSale = models.Sale.destroyById(sale.id, myOptions);
deletions += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`;
promises.push(deletedSale);
} }
});
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId); const salesPerson = ticket.client().salesPersonUser();
if (!isTicketEditable) if (salesPerson) {
throw new UserError(`The sales of this ticket can't be modified`); const origin = ctx.req.headers.origin;
if (!canEditSales) const message = $t('Deleted sales from ticket', {
throw new UserError(`Sale(s) blocked, please contact production`); ticketId: ticketId,
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
deletions: deletions
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
}
const promises = []; const deletedSales = await Promise.all(promises);
let deletions = '';
for (let sale of sales) {
const deletedSale = models.Sale.destroyById(sale.id);
deletions += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`;
promises.push(deletedSale); if (tx) await tx.commit();
return deletedSales;
} catch (e) {
if (tx) await tx.rollback();
throw e;
} }
const salesPerson = ticket.client().salesPersonUser();
if (salesPerson) {
const origin = ctx.req.headers.origin;
const message = $t('Deleted sales from ticket', {
ticketId: ticketId,
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
deletions: deletions
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
}
return Promise.all(promises);
}; };
}; };

View File

@ -18,26 +18,32 @@ module.exports = Self => {
} }
}); });
Self.getClaimableFromTicket = async ticketFk => { Self.getClaimableFromTicket = async(ticketFk, options) => {
let query = `SELECT const myOptions = {};
s.id AS saleFk,
t.id AS ticketFk,
t.landed,
s.concept,
s.itemFk,
s.quantity,
s.price,
s.discount,
t.nickname
FROM vn.ticket t
INNER JOIN vn.sale s ON s.ticketFk = t.id
LEFT JOIN vn.claimBeginning cb ON cb.saleFk = s.id
WHERE (t.landed) >= TIMESTAMPADD(DAY, -7, CURDATE()) if (typeof options == 'object')
AND t.id = ? AND cb.id IS NULL Object.assign(myOptions, options);
ORDER BY t.landed DESC, t.id DESC`;
let claimableSales = await Self.rawSql(query, [ticketFk]); const query = `
SELECT
s.id AS saleFk,
t.id AS ticketFk,
t.landed,
s.concept,
s.itemFk,
s.quantity,
s.price,
s.discount,
t.nickname
FROM vn.ticket t
INNER JOIN vn.sale s ON s.ticketFk = t.id
LEFT JOIN vn.claimBeginning cb ON cb.saleFk = s.id
WHERE (t.landed) >= TIMESTAMPADD(DAY, -7, CURDATE())
AND t.id = ? AND cb.id IS NULL
ORDER BY t.landed DESC, t.id DESC`;
const claimableSales = await Self.rawSql(query, [ticketFk], myOptions);
return claimableSales; return claimableSales;
}; };

View File

@ -20,20 +20,39 @@ module.exports = Self => {
} }
}); });
Self.recalculatePrice = async(ctx, id) => { Self.recalculatePrice = async(ctx, id, options) => {
const models = Self.app.models; const models = Self.app.models;
const myOptions = {};
let tx;
const sale = await Self.findById(id); if (typeof options == 'object')
const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); Object.assign(myOptions, options);
if (!isEditable) if (!myOptions.transaction) {
throw new UserError(`The sales of this ticket can't be modified`); tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
const canEditSale = await models.Sale.canEdit(ctx, [id]); try {
const sale = await Self.findById(id, null, myOptions);
const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk, myOptions);
if (!canEditSale) if (!isEditable)
throw new UserError(`Sale(s) blocked, please contact production`); throw new UserError(`The sales of this ticket can't be modified`);
return Self.rawSql('CALL vn.sale_calculateComponent(?, null)', [id]); const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`);
const recalculation = await Self.rawSql('CALL vn.sale_calculateComponent(?, null)', [id], myOptions);
if (tx) await tx.commit();
return recalculation;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -34,63 +34,80 @@ module.exports = Self => {
} }
}); });
Self.reserve = async(ctx, ticketId, sales, reserved) => { Self.reserve = async(ctx, ticketId, sales, reserved, options) => {
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const models = Self.app.models; const models = Self.app.models;
const myOptions = {};
let tx;
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId); if (typeof options == 'object')
if (!isTicketEditable) Object.assign(myOptions, options);
throw new UserError(`The sales of this ticket can't be modified`);
const canEditSale = await models.Sale.canEdit(ctx, sales); if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
if (!canEditSale) myOptions.transaction = tx;
throw new UserError(`Sale(s) blocked, please contact production`);
let changesMade = '';
const promises = [];
for (let sale of sales) {
if (sale.reserved != reserved) {
const oldState = sale.reserved ? 'reserved' : 'regular';
const newState = reserved ? 'reserved' : 'regular';
const reservedSale = models.Sale.update({id: sale.id}, {reserved: reserved});
promises.push(reservedSale);
changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`;
}
} }
const result = await Promise.all(promises); try {
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId, myOptions);
if (!isTicketEditable)
throw new UserError(`The sales of this ticket can't be modified`);
const ticket = await models.Ticket.findById(ticketId, { const canEditSale = await models.Sale.canEdit(ctx, sales, myOptions);
include: {
relation: 'client', if (!canEditSale)
scope: { throw new UserError(`Sale(s) blocked, please contact production`);
include: {
relation: 'salesPersonUser', let changesMade = '';
scope: { const promises = [];
fields: ['id', 'name']
for (let sale of sales) {
if (sale.reserved != reserved) {
const oldState = sale.reserved ? 'reserved' : 'regular';
const newState = reserved ? 'reserved' : 'regular';
const reservedSale = models.Sale.updateAll({id: sale.id}, {reserved: reserved}, myOptions);
promises.push(reservedSale);
changesMade += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity}) ${$t('State')}: ${$t(oldState)} ➔ *${$t(newState)}*`;
}
}
const result = await Promise.all(promises);
const ticket = await models.Ticket.findById(ticketId, {
include: {
relation: 'client',
scope: {
include: {
relation: 'salesPersonUser',
scope: {
fields: ['id', 'name']
}
} }
} }
} }
}, myOptions);
const salesPerson = ticket.client().salesPersonUser();
if (salesPerson) {
const origin = ctx.req.headers.origin;
const message = $t('Changed sale reserved state', {
ticketId: ticketId,
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
changes: changesMade
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
} }
});
const salesPerson = ticket.client().salesPersonUser(); if (tx) await tx.commit();
if (salesPerson) {
const origin = ctx.req.headers.origin;
const message = $t('Changed sale reserved state', { return result;
ticketId: ticketId, } catch (e) {
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`, if (tx) await tx.rollback();
changes: changesMade throw e;
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
} }
return result;
}; };
}; };

View File

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

View File

@ -1,38 +1,29 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale deleteSales()', () => { describe('sale deleteSales()', () => {
let sale;
let newSale;
beforeAll(async done => {
try {
sale = await app.models.Sale.findOne({where: {id: 9}});
sale.id = null;
newSale = await app.models.Sale.create(sale);
} catch (error) {
console.error(error);
}
done();
});
it('should throw an error if the ticket of the given sales is not editable', async() => { it('should throw an error if the ticket of the given sales is not editable', async() => {
let ctx = { const tx = await models.Sale.beginTransaction({});
req: {
accessToken: {userId: 9},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
let error; let error;
const sales = [{id: 1, instance: 0}, {id: 2, instance: 1}];
const ticketId = 2;
try { try {
await app.models.Sale.deleteSales(ctx, sales, ticketId); const options = {transaction: tx};
const ctx = {
req: {
accessToken: {userId: 9},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
const sales = [{id: 1, instance: 0}, {id: 2, instance: 1}];
const ticketId = 2;
await models.Sale.deleteSales(ctx, sales, ticketId, options);
await tx.rollback();
} catch (e) { } catch (e) {
await tx.rollback();
error = e; error = e;
} }
@ -40,19 +31,33 @@ describe('sale deleteSales()', () => {
}); });
it('should delete the sale', async() => { it('should delete the sale', async() => {
let ctx = { const tx = await models.Sale.beginTransaction({});
req: {
accessToken: {userId: 9},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
const sales = [{id: newSale.id, instance: 0}]; try {
const ticketId = 16; const options = {transaction: tx};
let res = await app.models.Sale.deleteSales(ctx, sales, ticketId); const ctx = {
req: {
accessToken: {userId: 9},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
const sale = await models.Sale.findOne({where: {id: 9}}, options);
sale.id = null;
const newSale = await models.Sale.create(sale, options);
expect(res).toEqual([{count: 1}]); const sales = [{id: newSale.id, instance: 0}];
const ticketId = 16;
const deletions = await models.Sale.deleteSales(ctx, sales, ticketId, options);
expect(deletions).toEqual([{count: 1}]);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,10 +1,21 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale getClaimableFromTicket()', () => { describe('sale getClaimableFromTicket()', () => {
it('should return the claimable sales of a given ticket', async() => { it('should return the claimable sales of a given ticket', async() => {
let claimableFromTicket = await app.models.Sale.getClaimableFromTicket(16); const tx = await models.Sale.beginTransaction({});
expect(claimableFromTicket[0].concept).toBe('Ranged weapon longbow 2m'); try {
expect(claimableFromTicket.length).toBe(3); const options = {transaction: tx};
const claimableFromTicket = await models.Sale.getClaimableFromTicket(16, options);
expect(claimableFromTicket[0].concept).toBe('Ranged weapon longbow 2m');
expect(claimableFromTicket.length).toBe(3);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,24 +1,43 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale recalculatePrice()', () => { describe('sale recalculatePrice()', () => {
const saleId = 7; const saleId = 7;
it('should update the sale price', async() => { it('should update the sale price', async() => {
const ctx = {req: {accessToken: {userId: 9}}}; const tx = await models.Sale.beginTransaction({});
const response = await app.models.Sale.recalculatePrice(ctx, saleId);
expect(response.affectedRows).toBeDefined(); try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}};
const response = await models.Sale.recalculatePrice(ctx, saleId, options);
expect(response.affectedRows).toBeDefined();
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should throw an error if the ticket is not editable', async() => { it('should throw an error if the ticket is not editable', async() => {
const ctx = {req: {accessToken: {userId: 9}}}; const tx = await models.Sale.beginTransaction({});
const immutableSaleId = 1;
await app.models.Sale.recalculatePrice(ctx, immutableSaleId)
.catch(response => {
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
error = response;
});
expect(error).toBeDefined(); let error;
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}};
const immutableSaleId = 1;
await models.Sale.recalculatePrice(ctx, immutableSaleId, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error(`The sales of this ticket can't be modified`));
}); });
}); });

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale reserve()', () => { describe('sale reserve()', () => {
const ctx = { const ctx = {
@ -9,51 +9,54 @@ describe('sale reserve()', () => {
} }
}; };
afterAll(async done => {
let ctx = {req: {accessToken: {userId: 9}}};
let params = {
sales: [
{id: 7},
{id: 8}],
ticketFk: 11,
reserved: false
};
await app.models.Sale.reserve(ctx, params);
done();
});
it('should throw an error if the ticket can not be modified', async() => { it('should throw an error if the ticket can not be modified', async() => {
const tx = await models.Sale.beginTransaction({});
let error; let error;
const ticketId = 2; try {
const sales = [{id: 5}]; const options = {transaction: tx};
const reserved = false;
await app.models.Sale.reserve(ctx, ticketId, sales, reserved) const ticketId = 2;
.catch(response => { const sales = [{id: 5}];
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`)); const reserved = false;
error = response;
});
expect(error).toBeDefined(); await models.Sale.reserve(ctx, ticketId, sales, reserved, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error(`The sales of this ticket can't be modified`));
}); });
it('should update the given sales of a ticket to reserved', async() => { it('should update the given sales of a ticket to reserved', async() => {
originalTicketSales = await app.models.Ticket.getSales(11); const tx = await models.Sale.beginTransaction({});
expect(originalTicketSales[0].reserved).toEqual(false); try {
expect(originalTicketSales[1].reserved).toEqual(false); const options = {transaction: tx};
const ticketId = 11; originalTicketSales = await models.Ticket.getSales(11, options);
const sales = [{id: 7}, {id: 8}];
const reserved = true;
await app.models.Sale.reserve(ctx, ticketId, sales, reserved); expect(originalTicketSales[0].reserved).toEqual(false);
expect(originalTicketSales[1].reserved).toEqual(false);
const reservedTicketSales = await app.models.Ticket.getSales(ticketId); const ticketId = 11;
const sales = [{id: 7}, {id: 8}];
const reserved = true;
expect(reservedTicketSales[0].reserved).toEqual(true); await models.Sale.reserve(ctx, ticketId, sales, reserved, options);
expect(reservedTicketSales[1].reserved).toEqual(true);
const reservedTicketSales = await models.Ticket.getSales(ticketId, options);
expect(reservedTicketSales[0].reserved).toEqual(true);
expect(reservedTicketSales[1].reserved).toEqual(true);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,40 +1,45 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale updateConcept()', () => { describe('sale updateConcept()', () => {
const ctx = {req: {accessToken: {userId: 9}}}; const ctx = {req: {accessToken: {userId: 9}}};
const saleId = 1; const saleId = 1;
let originalSale;
beforeAll(async done => {
originalSale = await app.models.Sale.findById(saleId);
done();
});
afterAll(async done => {
await originalSale.save();
done();
});
it('should throw if ID was undefined', async() => { it('should throw if ID was undefined', async() => {
let err; const tx = await models.Sale.beginTransaction({});
const newConcept = 'I am he new concept';
let error;
try { try {
await app.models.Sale.updateConcept(ctx, undefined, newConcept); const options = {transaction: tx};
const newConcept = 'not going to heppen';
await models.Sale.updateConcept(ctx, undefined, newConcept, options);
await tx.rollback();
} catch (e) { } catch (e) {
err = e; await tx.rollback();
error = e;
} }
expect(err).toBeDefined(); expect(error).toBeDefined();
}); });
it('should update the sale concept', async() => { it('should update the sale concept', async() => {
const newConcept = 'I am the new concept'; const tx = await models.Sale.beginTransaction({});
let response = await app.models.Sale.updateConcept(ctx, saleId, newConcept); try {
const options = {transaction: tx};
expect(response.concept).toEqual(newConcept); const newConcept = 'I am the new concept';
let response = await models.Sale.updateConcept(ctx, saleId, newConcept, options);
expect(response.concept).toEqual(newConcept);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,104 +1,100 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale updatePrice()', () => { describe('sale updatePrice()', () => {
let originalSale; const ctx = {
let originalSalesPersonMana; req: {
let createdSaleComponent; accessToken: {userId: 18},
let saleId = 7; headers: {origin: 'localhost:5000'},
let manaComponentId; __: () => {}
}
beforeAll(async done => { };
let component = await app.models.Component.findOne({where: {code: 'mana'}}); const saleId = 7;
manaComponentId = component.id;
originalSale = await app.models.Sale.findById(saleId);
originalSalesPersonMana = await app.models.WorkerMana.findById(18);
done();
});
it('should throw an error if the ticket is not editable', async() => { it('should throw an error if the ticket is not editable', async() => {
const ctx = { const tx = await models.Sale.beginTransaction({});
req: {
accessToken: {userId: 18},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
let immutableSaleId = 1;
let price = 5;
await app.models.Sale.updatePrice(ctx, immutableSaleId, price) try {
.catch(response => { const options = {transaction: tx};
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
error = response;
});
expect(error).toBeDefined(); const immutableSaleId = 1;
const price = 5;
await models.Sale.updatePrice(ctx, immutableSaleId, price, options);
await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error(`The sales of this ticket can't be modified`));
}); });
it('should return 0 if the price is an empty string', async() => { it('should return 0 if the price is an empty string', async() => {
const ctx = { const tx = await models.Sale.beginTransaction({});
req: {
accessToken: {userId: 18},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
let price = ''; try {
const options = {transaction: tx};
await app.models.Sale.updatePrice(ctx, saleId, price); const price = '';
let updatedSale = await app.models.Sale.findById(saleId);
expect(updatedSale.price).toEqual(0); await models.Sale.updatePrice(ctx, saleId, price, options);
const updatedSale = await models.Sale.findById(saleId, null, options);
// restores expect(updatedSale.price).toEqual(0);
await originalSale.updateAttributes(originalSale);
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0}); await tx.rollback();
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana); } catch (e) {
await tx.rollback();
throw e;
}
}); });
it('should now set price as a number in a string', async() => { it('should now set price as a number in a string', async() => {
const ctx = { const tx = await models.Sale.beginTransaction({});
req: {
accessToken: {userId: 18},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
let price = '8'; try {
const options = {transaction: tx};
await app.models.Sale.updatePrice(ctx, saleId, price); const price = '8';
let updatedSale = await app.models.Sale.findById(saleId);
expect(updatedSale.price).toEqual(8); await models.Sale.updatePrice(ctx, saleId, price, options);
const updatedSale = await models.Sale.findById(saleId, null, options);
// restores expect(updatedSale.price).toEqual(8);
await originalSale.updateAttributes(originalSale);
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0}); await tx.rollback();
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana); } catch (e) {
await tx.rollback();
throw e;
}
}); });
// #2736 sale updatePrice() returns inconsistent values it('should set price as a decimal number and check the sale has the mana component changing the salesPersonMana', async() => {
xit('should set price as a decimal number and check the sale has the mana component changing the salesPersonMana', async() => { const tx = await models.Sale.beginTransaction({});
let ctx = {req: {accessToken: {userId: 18}}};
let price = 5.4;
await app.models.Sale.updatePrice(ctx, saleId, price); try {
let updatedSale = await app.models.Sale.findById(saleId); const options = {transaction: tx};
createdSaleComponent = await app.models.SaleComponent.findOne({where: {saleFk: saleId, componentFk: manaComponentId}});
expect(updatedSale.price).toBe(price); const price = 5.4;
expect(createdSaleComponent.value).toEqual(-2.04); const originalSalesPersonMana = await models.WorkerMana.findById(18, null, options);
const manaComponent = await models.Component.findOne({where: {code: 'mana'}}, options);
let updatedSalesPersonMana = await app.models.WorkerMana.findById(18); await models.Sale.updatePrice(ctx, saleId, price, options);
const updatedSale = await models.Sale.findById(saleId, null, options);
createdSaleComponent = await models.SaleComponent.findOne({where: {saleFk: saleId, componentFk: manaComponent.id}}, options);
expect(updatedSalesPersonMana.amount).not.toEqual(originalSalesPersonMana.amount); expect(updatedSale.price).toBe(price);
expect(createdSaleComponent.value).toEqual(-2.04);
// restores const updatedSalesPersonMana = await models.WorkerMana.findById(18, null, options);
await originalSale.updateAttributes(originalSale);
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0}); expect(updatedSalesPersonMana.amount).not.toEqual(originalSalesPersonMana.amount);
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -1,4 +1,4 @@
const app = require('vn-loopback/server/server'); const models = require('vn-loopback/server/server').models;
describe('sale updateQuantity()', () => { describe('sale updateQuantity()', () => {
const ctx = { const ctx = {
@ -10,44 +10,61 @@ describe('sale updateQuantity()', () => {
}; };
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() => {
const tx = await models.Sale.beginTransaction({});
let error; let error;
try {
const options = {transaction: tx};
await app.models.Sale.updateQuantity(ctx, 1, 'wrong quantity!') await models.Sale.updateQuantity(ctx, 1, 'wrong quantity!', options);
.catch(response => {
expect(response).toEqual(new Error('The value should be a number'));
error = response;
});
expect(error).toBeDefined(); await tx.rollback();
} catch (e) {
await tx.rollback();
error = e;
}
expect(error).toEqual(new Error('The value should be a number'));
}); });
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() => {
const tx = await models.Sale.beginTransaction({});
let error; let error;
try {
const options = {transaction: tx};
await app.models.Sale.updateQuantity(ctx, 1, 99) await models.Sale.updateQuantity(ctx, 1, 99, options);
.catch(response => {
expect(response).toEqual(new Error('The new quantity should be smaller than the old one'));
error = response;
});
expect(error).toBeDefined(); 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 update the quantity of a given sale current line', async() => { it('should update the quantity of a given sale current line', async() => {
let originalLineData = await app.models.Sale.findOne({where: {id: 1}, fields: ['quantity']}); const tx = await models.Sale.beginTransaction({});
expect(originalLineData.quantity).toEqual(5); try {
const options = {transaction: tx};
await app.models.Sale.updateQuantity(ctx, 1, 4); const originalLine = await models.Sale.findOne({where: {id: 1}, fields: ['quantity']}, options);
let modifiedLineData = await app.models.Sale.findOne({where: {id: 1}, fields: ['quantity']}); expect(originalLine.quantity).toEqual(5);
expect(modifiedLineData.quantity).toEqual(4); await models.Sale.updateQuantity(ctx, 1, 4, options);
await app.models.Sale.update({id: 1}, {quantity: 5}); const modifiedLine = await models.Sale.findOne({where: {id: 1}, fields: ['quantity']}, options);
let resetLineDataValues = await app.models.Sale.findOne({where: {id: 1}, fields: ['quantity']}); expect(modifiedLine.quantity).toEqual(4);
expect(resetLineDataValues.quantity).toEqual(5); await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
}); });
}); });

View File

@ -24,15 +24,35 @@ module.exports = Self => {
} }
}); });
Self.updateConcept = async(ctx, id, newConcept) => { Self.updateConcept = async(ctx, id, newConcept, options) => {
const models = Self.app.models; const models = Self.app.models;
const currentLine = await models.Sale.findById(id); const myOptions = {};
let tx;
const canEditSale = await models.Sale.canEdit(ctx, [id]); if (typeof options == 'object')
Object.assign(myOptions, options);
if (!canEditSale) if (!myOptions.transaction) {
throw new UserError(`Sale(s) blocked, please contact production`); tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
return await currentLine.updateAttributes({concept: newConcept}); try {
const currentLine = await models.Sale.findById(id, null, myOptions);
const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`);
const line = await currentLine.updateAttributes({concept: newConcept}, myOptions);
if (tx) await tx.commit();
return line;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
}; };
}; };

View File

@ -28,14 +28,21 @@ module.exports = Self => {
} }
}); });
Self.updatePrice = async(ctx, id, newPrice) => { Self.updatePrice = async(ctx, id, newPrice, options) => {
const $t = ctx.req.__; // $translate const $t = ctx.req.__; // $translate
const models = Self.app.models; const models = Self.app.models;
const tx = await Self.beginTransaction({}); const myOptions = {};
let tx;
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try { try {
const options = {transaction: tx};
const filter = { const filter = {
include: { include: {
relation: 'ticket', relation: 'ticket',
@ -57,22 +64,22 @@ module.exports = Self => {
} }
}; };
const sale = await models.Sale.findById(id, filter, options); const sale = await models.Sale.findById(id, filter, myOptions);
const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk); const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk, myOptions);
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]); const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
if (!canEditSale) if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`); throw new UserError(`Sale(s) blocked, please contact production`);
const oldPrice = sale.price; const oldPrice = sale.price;
const userId = ctx.req.accessToken.userId; const userId = ctx.req.accessToken.userId;
const usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, options); const usesMana = await models.WorkerMana.findOne({where: {workerFk: userId}, fields: 'amount'}, myOptions);
const componentCode = usesMana ? 'mana' : 'buyerDiscount'; const componentCode = usesMana ? 'mana' : 'buyerDiscount';
const discount = await models.Component.findOne({where: {code: componentCode}}, options); const discount = await models.Component.findOne({where: {code: componentCode}}, myOptions);
const componentId = discount.id; const componentId = discount.id;
const componentValue = newPrice - sale.price; const componentValue = newPrice - sale.price;
@ -80,23 +87,23 @@ module.exports = Self => {
componentFk: componentId, componentFk: componentId,
saleFk: id saleFk: id
}; };
const saleComponent = await models.SaleComponent.findOne({where}, options); const saleComponent = await models.SaleComponent.findOne({where}, myOptions);
if (saleComponent) { if (saleComponent) {
await models.SaleComponent.updateAll(where, { await models.SaleComponent.updateAll(where, {
value: saleComponent.value + componentValue value: saleComponent.value + componentValue
}, options); }, myOptions);
} else { } else {
await models.SaleComponent.create({ await models.SaleComponent.create({
saleFk: id, saleFk: id,
componentFk: componentId, componentFk: componentId,
value: componentValue value: componentValue
}, options); }, myOptions);
} }
await sale.updateAttributes({price: newPrice}, options); await sale.updateAttributes({price: newPrice}, myOptions);
query = `CALL vn.manaSpellersRequery(?)`; query = `CALL vn.manaSpellersRequery(?)`;
await Self.rawSql(query, [userId], options); await Self.rawSql(query, [userId], myOptions);
const salesPerson = sale.ticket().client().salesPersonUser(); const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) { if (salesPerson) {
@ -111,14 +118,14 @@ module.exports = Self => {
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`, ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
itemUrl: `${origin}/#!/item/${sale.itemFk}/summary` itemUrl: `${origin}/#!/item/${sale.itemFk}/summary`
}); });
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message); await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
} }
await tx.commit(); if (tx) await tx.commit();
return sale; return sale;
} catch (error) { } catch (error) {
await tx.rollback(); if (tx) await tx.rollback();
throw error; throw error;
} }
}; };

View File

@ -26,60 +26,77 @@ module.exports = Self => {
} }
}); });
Self.updateQuantity = async(ctx, id, newQuantity) => { Self.updateQuantity = async(ctx, id, newQuantity, options) => {
const $t = ctx.req.__; // $translate
const models = Self.app.models; const models = Self.app.models;
const $t = ctx.req.__; // $translate
const myOptions = {};
let tx;
const canEditSale = await models.Sale.canEdit(ctx, [id]); if (typeof options == 'object')
Object.assign(myOptions, options);
if (!canEditSale) if (!myOptions.transaction) {
throw new UserError(`Sale(s) blocked, please contact production`); tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
if (isNaN(newQuantity)) try {
throw new UserError(`The value should be a number`); const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
const filter = { if (!canEditSale)
include: { throw new UserError(`Sale(s) blocked, please contact production`);
relation: 'ticket',
scope: { if (isNaN(newQuantity))
include: { throw new UserError(`The value should be a number`);
relation: 'client',
scope: { const filter = {
include: { include: {
relation: 'salesPersonUser', relation: 'ticket',
scope: { scope: {
fields: ['id', 'name'] include: {
relation: 'client',
scope: {
include: {
relation: 'salesPersonUser',
scope: {
fields: ['id', 'name']
}
} }
} }
} }
} }
} }
};
const sale = await models.Sale.findById(id, filter, myOptions);
if (newQuantity > sale.quantity)
throw new UserError('The new quantity should be smaller than the old one');
const oldQuantity = sale.quantity;
const result = await sale.updateAttributes({quantity: newQuantity}, myOptions);
const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) {
const origin = ctx.req.headers.origin;
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`
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
} }
};
const sale = await models.Sale.findById(id, filter); if (tx) await tx.commit();
if (newQuantity > sale.quantity) return result;
throw new UserError('The new quantity should be smaller than the old one'); } catch (error) {
if (tx) await tx.rollback();
const oldQuantity = sale.quantity; throw error;
const result = await sale.updateAttributes({quantity: newQuantity});
const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) {
const origin = ctx.req.headers.origin;
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`
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
} }
return result;
}; };
}; };