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 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, {
include: {
relation: 'client',
scope: {
include: {
relation: 'salesPersonUser',
scope: {
fields: ['id', 'name']
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
try {
const canEditSales = await models.Sale.canEdit(ctx, sales, myOptions);
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);
if (!isTicketEditable)
throw new UserError(`The sales of this ticket can't be modified`);
const salesPerson = ticket.client().salesPersonUser();
if (salesPerson) {
const origin = ctx.req.headers.origin;
if (!canEditSales)
throw new UserError(`Sale(s) blocked, please contact production`);
const message = $t('Deleted sales from ticket', {
ticketId: ticketId,
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
deletions: deletions
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message, myOptions);
}
const promises = [];
let deletions = '';
for (let sale of sales) {
const deletedSale = models.Sale.destroyById(sale.id);
deletions += `\r\n-${sale.itemFk}: ${sale.concept} (${sale.quantity})`;
const deletedSales = await Promise.all(promises);
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 => {
let 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
Self.getClaimableFromTicket = async(ticketFk, options) => {
const myOptions = {};
WHERE (t.landed) >= TIMESTAMPADD(DAY, -7, CURDATE())
AND t.id = ? AND cb.id IS NULL
ORDER BY t.landed DESC, t.id DESC`;
if (typeof options == 'object')
Object.assign(myOptions, options);
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;
};

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 myOptions = {};
let tx;
const sale = await Self.findById(id);
const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk);
if (typeof options == 'object')
Object.assign(myOptions, options);
if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`);
if (!myOptions.transaction) {
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)
throw new UserError(`Sale(s) blocked, please contact production`);
if (!isEditable)
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 models = Self.app.models;
const myOptions = {};
let tx;
const isTicketEditable = await models.Ticket.isEditable(ctx, ticketId);
if (!isTicketEditable)
throw new UserError(`The sales of this ticket can't be modified`);
if (typeof options == 'object')
Object.assign(myOptions, options);
const canEditSale = await models.Sale.canEdit(ctx, sales);
if (!canEditSale)
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)}*`;
}
if (!myOptions.transaction) {
tx = await Self.beginTransaction({});
myOptions.transaction = tx;
}
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, {
include: {
relation: 'client',
scope: {
include: {
relation: 'salesPersonUser',
scope: {
fields: ['id', 'name']
const canEditSale = await models.Sale.canEdit(ctx, sales, myOptions);
if (!canEditSale)
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.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 (salesPerson) {
const origin = ctx.req.headers.origin;
if (tx) await tx.commit();
const message = $t('Changed sale reserved state', {
ticketId: ticketId,
ticketUrl: `${origin}/#!/ticket/${ticketId}/sale`,
changes: changesMade
});
await models.Chat.sendCheckingPresence(ctx, salesPerson.id, message);
return result;
} catch (e) {
if (tx) await tx.rollback();
throw e;
}
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()', () => {
it('should return true if the role is production regardless of the saleTrackings', async() => {
const productionUserID = 49;
let ctx = {req: {accessToken: {userId: productionUserID}}};
const tx = await models.Sale.beginTransaction({});
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() => {
const salesPersonUserID = 18;
let ctx = {req: {accessToken: {userId: salesPersonUserID}}};
const tx = await models.Sale.beginTransaction({});
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() => {
const salesPersonUserID = 18;
let ctx = {req: {accessToken: {userId: salesPersonUserID}}};
const tx = await models.Sale.beginTransaction({});
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()', () => {
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() => {
let ctx = {
req: {
accessToken: {userId: 9},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
const tx = await models.Sale.beginTransaction({});
let error;
const sales = [{id: 1, instance: 0}, {id: 2, instance: 1}];
const ticketId = 2;
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) {
await tx.rollback();
error = e;
}
@ -40,19 +31,33 @@ describe('sale deleteSales()', () => {
});
it('should delete the sale', async() => {
let ctx = {
req: {
accessToken: {userId: 9},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
const tx = await models.Sale.beginTransaction({});
const sales = [{id: newSale.id, instance: 0}];
const ticketId = 16;
try {
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()', () => {
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');
expect(claimableFromTicket.length).toBe(3);
try {
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()', () => {
const saleId = 7;
it('should update the sale price', async() => {
const ctx = {req: {accessToken: {userId: 9}}};
const response = await app.models.Sale.recalculatePrice(ctx, saleId);
const tx = await models.Sale.beginTransaction({});
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() => {
const ctx = {req: {accessToken: {userId: 9}}};
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;
});
const tx = await models.Sale.beginTransaction({});
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()', () => {
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() => {
const tx = await models.Sale.beginTransaction({});
let error;
const ticketId = 2;
const sales = [{id: 5}];
const reserved = false;
try {
const options = {transaction: tx};
await app.models.Sale.reserve(ctx, ticketId, sales, reserved)
.catch(response => {
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
error = response;
});
const ticketId = 2;
const sales = [{id: 5}];
const reserved = false;
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() => {
originalTicketSales = await app.models.Ticket.getSales(11);
const tx = await models.Sale.beginTransaction({});
expect(originalTicketSales[0].reserved).toEqual(false);
expect(originalTicketSales[1].reserved).toEqual(false);
try {
const options = {transaction: tx};
const ticketId = 11;
const sales = [{id: 7}, {id: 8}];
const reserved = true;
originalTicketSales = await models.Ticket.getSales(11, options);
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);
expect(reservedTicketSales[1].reserved).toEqual(true);
await models.Sale.reserve(ctx, ticketId, sales, reserved, options);
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()', () => {
const ctx = {req: {accessToken: {userId: 9}}};
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() => {
let err;
const newConcept = 'I am he new concept';
const tx = await models.Sale.beginTransaction({});
let error;
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) {
err = e;
await tx.rollback();
error = e;
}
expect(err).toBeDefined();
expect(error).toBeDefined();
});
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()', () => {
let originalSale;
let originalSalesPersonMana;
let createdSaleComponent;
let saleId = 7;
let manaComponentId;
beforeAll(async done => {
let component = await app.models.Component.findOne({where: {code: 'mana'}});
manaComponentId = component.id;
originalSale = await app.models.Sale.findById(saleId);
originalSalesPersonMana = await app.models.WorkerMana.findById(18);
done();
});
const ctx = {
req: {
accessToken: {userId: 18},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
const saleId = 7;
it('should throw an error if the ticket is not editable', async() => {
const ctx = {
req: {
accessToken: {userId: 18},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
let immutableSaleId = 1;
let price = 5;
const tx = await models.Sale.beginTransaction({});
await app.models.Sale.updatePrice(ctx, immutableSaleId, price)
.catch(response => {
expect(response).toEqual(new Error(`The sales of this ticket can't be modified`));
error = response;
});
try {
const options = {transaction: tx};
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() => {
const ctx = {
req: {
accessToken: {userId: 18},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
const tx = await models.Sale.beginTransaction({});
let price = '';
try {
const options = {transaction: tx};
await app.models.Sale.updatePrice(ctx, saleId, price);
let updatedSale = await app.models.Sale.findById(saleId);
const price = '';
expect(updatedSale.price).toEqual(0);
await models.Sale.updatePrice(ctx, saleId, price, options);
const updatedSale = await models.Sale.findById(saleId, null, options);
// restores
await originalSale.updateAttributes(originalSale);
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
expect(updatedSale.price).toEqual(0);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
it('should now set price as a number in a string', async() => {
const ctx = {
req: {
accessToken: {userId: 18},
headers: {origin: 'localhost:5000'},
__: () => {}
}
};
const tx = await models.Sale.beginTransaction({});
let price = '8';
try {
const options = {transaction: tx};
await app.models.Sale.updatePrice(ctx, saleId, price);
let updatedSale = await app.models.Sale.findById(saleId);
const price = '8';
expect(updatedSale.price).toEqual(8);
await models.Sale.updatePrice(ctx, saleId, price, options);
const updatedSale = await models.Sale.findById(saleId, null, options);
// restores
await originalSale.updateAttributes(originalSale);
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
expect(updatedSale.price).toEqual(8);
await tx.rollback();
} catch (e) {
await tx.rollback();
throw e;
}
});
// #2736 sale updatePrice() returns inconsistent values
xit('should set price as a decimal number and check the sale has the mana component changing the salesPersonMana', async() => {
let ctx = {req: {accessToken: {userId: 18}}};
let price = 5.4;
it('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({});
await app.models.Sale.updatePrice(ctx, saleId, price);
let updatedSale = await app.models.Sale.findById(saleId);
createdSaleComponent = await app.models.SaleComponent.findOne({where: {saleFk: saleId, componentFk: manaComponentId}});
try {
const options = {transaction: tx};
expect(updatedSale.price).toBe(price);
expect(createdSaleComponent.value).toEqual(-2.04);
const price = 5.4;
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
await originalSale.updateAttributes(originalSale);
await app.models.SaleComponent.updateAll({componentFk: manaComponentId, saleFk: saleId}, {value: 0});
await originalSalesPersonMana.updateAttributes(originalSalesPersonMana);
const updatedSalesPersonMana = await models.WorkerMana.findById(18, null, options);
expect(updatedSalesPersonMana.amount).not.toEqual(originalSalesPersonMana.amount);
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()', () => {
const ctx = {
@ -10,44 +10,61 @@ describe('sale updateQuantity()', () => {
};
it('should throw an error if the quantity is not a number', async() => {
const tx = await models.Sale.beginTransaction({});
let error;
try {
const options = {transaction: tx};
await app.models.Sale.updateQuantity(ctx, 1, 'wrong quantity!')
.catch(response => {
expect(response).toEqual(new Error('The value should be a number'));
error = response;
});
await models.Sale.updateQuantity(ctx, 1, 'wrong quantity!', options);
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() => {
const tx = await models.Sale.beginTransaction({});
let error;
try {
const options = {transaction: tx};
await app.models.Sale.updateQuantity(ctx, 1, 99)
.catch(response => {
expect(response).toEqual(new Error('The new quantity should be smaller than the old one'));
error = response;
});
await models.Sale.updateQuantity(ctx, 1, 99, options);
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() => {
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 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)
throw new UserError(`Sale(s) blocked, please contact production`);
if (!myOptions.transaction) {
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 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 {
const options = {transaction: tx};
const filter = {
include: {
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)
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)
throw new UserError(`Sale(s) blocked, please contact production`);
const oldPrice = sale.price;
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 discount = await models.Component.findOne({where: {code: componentCode}}, options);
const discount = await models.Component.findOne({where: {code: componentCode}}, myOptions);
const componentId = discount.id;
const componentValue = newPrice - sale.price;
@ -80,23 +87,23 @@ module.exports = Self => {
componentFk: componentId,
saleFk: id
};
const saleComponent = await models.SaleComponent.findOne({where}, options);
const saleComponent = await models.SaleComponent.findOne({where}, myOptions);
if (saleComponent) {
await models.SaleComponent.updateAll(where, {
value: saleComponent.value + componentValue
}, options);
}, myOptions);
} else {
await models.SaleComponent.create({
saleFk: id,
componentFk: componentId,
value: componentValue
}, options);
}, myOptions);
}
await sale.updateAttributes({price: newPrice}, options);
await sale.updateAttributes({price: newPrice}, myOptions);
query = `CALL vn.manaSpellersRequery(?)`;
await Self.rawSql(query, [userId], options);
await Self.rawSql(query, [userId], myOptions);
const salesPerson = sale.ticket().client().salesPersonUser();
if (salesPerson) {
@ -111,14 +118,14 @@ module.exports = Self => {
ticketUrl: `${origin}/#!/ticket/${sale.ticket().id}/sale`,
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;
} catch (error) {
await tx.rollback();
if (tx) await tx.rollback();
throw error;
}
};

View File

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