salix/modules/ticket/back/models/specs/sale.spec.js

319 lines
15 KiB
JavaScript
Raw Normal View History

/* eslint max-len: ["error", { "code": 150 }]*/
2024-05-31 11:58:26 +00:00
const {models} = require('vn-loopback/server/server');
const LoopBackContext = require('loopback-context');
2024-05-31 14:31:36 +00:00
describe('sale model ', () => {
2024-05-31 11:58:26 +00:00
const developerId = 9;
const buyerId = 35;
const employeeId = 1;
2024-05-31 14:31:36 +00:00
const productionId = 49;
const salesPersonId = 18;
const reviewerId = 130;
const barcode = '4444444444';
const ticketCollectionProd = 34;
const ticketCollectionSalesPerson = 35;
const previaTicketSalesPerson = 36;
const previaTicketProd = 37;
const notEditableError = 'This ticket is not editable.';
2024-05-31 11:58:26 +00:00
const ctx = getCtx(developerId);
let tx;
2024-05-31 14:31:36 +00:00
let opts;
2024-05-31 11:58:26 +00:00
function getCtx(userId, active = false) {
const accessToken = {userId};
const headers = {origin: 'localhost:5000'};
if (!active) return {req: {accessToken, headers, __: () => {}}};
return {active: {accessToken, http: {req: {headers}}}};
}
2024-05-31 11:58:26 +00:00
beforeEach(async() => {
tx = await models.Sale.beginTransaction({});
2024-05-31 14:31:36 +00:00
opts = {transaction: tx};
2024-05-31 11:58:26 +00:00
});
afterEach(async() => await tx.rollback());
describe('quantity field ', () => {
it('should add quantity if the quantity is greater than it should be and is role advanced', async() => {
const saleId = 17;
2024-05-31 11:58:26 +00:00
const ctx = getCtx(buyerId);
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(buyerId, true));
2024-05-31 14:31:36 +00:00
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
if (sqlStatement.includes('catalog_calcFromItem')) {
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
SELECT 100 as available;`;
params = null;
}
2024-05-31 14:31:36 +00:00
return models.Ticket.rawSql(sqlStatement, params, opts);
});
2024-05-31 11:58:26 +00:00
const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*');
2024-05-31 11:58:26 +00:00
expect(isRoleAdvanced).toEqual(true);
2024-05-31 14:31:36 +00:00
const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
2024-05-31 11:58:26 +00:00
expect(originalLine.quantity).toEqual(30);
2024-05-31 11:58:26 +00:00
const newQuantity = originalLine.quantity + 1;
2024-05-31 14:31:36 +00:00
await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
2024-05-31 14:31:36 +00:00
const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
2024-05-31 11:58:26 +00:00
expect(modifiedLine.quantity).toEqual(newQuantity);
});
it('should update the quantity of a given sale current line', async() => {
2024-05-31 11:58:26 +00:00
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(developerId, true));
const saleId = 25;
const newQuantity = 4;
2024-05-31 14:31:36 +00:00
const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
2024-05-31 11:58:26 +00:00
expect(originalLine.quantity).toEqual(20);
2024-05-31 14:31:36 +00:00
await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
2024-05-31 14:31:36 +00:00
const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
2024-05-31 11:58:26 +00:00
expect(modifiedLine.quantity).toEqual(newQuantity);
});
it('should throw an error if the quantity is negative and it is not a refund ticket', async() => {
2024-05-31 11:58:26 +00:00
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
const saleId = 17;
const newQuantity = -10;
try {
2024-05-31 14:31:36 +00:00
await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
} catch (e) {
2024-05-31 11:58:26 +00:00
expect(e).toEqual(new Error('You can only add negative amounts in refund tickets'));
}
});
it('should update a negative quantity when is a ticket refund', async() => {
2024-05-31 11:58:26 +00:00
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(developerId, true));
const saleId = 32;
const newQuantity = -10;
2024-05-31 14:31:36 +00:00
await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
2024-05-31 14:31:36 +00:00
const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts);
2024-05-31 11:58:26 +00:00
expect(modifiedLine.quantity).toEqual(newQuantity);
});
it('should throw an error if the quantity is less than the minimum quantity of the item', async() => {
2024-05-31 11:58:26 +00:00
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = minQuantity - 1;
try {
2024-05-31 14:31:36 +00:00
const item = await models.Item.findById(itemId, null, opts);
await item.updateAttribute('minQuantity', minQuantity, opts);
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
if (sqlStatement.includes('catalog_calcFromItem')) {
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
SELECT 100 as available;`;
params = null;
}
2024-05-31 14:31:36 +00:00
return models.Ticket.rawSql(sqlStatement, params, opts);
});
2024-05-31 14:31:36 +00:00
await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
} catch (e) {
2024-05-31 11:58:26 +00:00
expect(e).toEqual(new Error('The amount cannot be less than the minimum'));
}
});
it('should change quantity if has minimum quantity and new quantity is equal than item available', async() => {
2024-05-31 11:58:26 +00:00
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = minQuantity - 1;
2024-05-31 14:31:36 +00:00
const item = await models.Item.findById(itemId, null, opts);
await item.updateAttribute('minQuantity', minQuantity, opts);
2024-05-31 14:31:36 +00:00
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
2024-05-31 11:58:26 +00:00
if (sqlStatement.includes('catalog_calcFromItem')) {
sqlStatement = `CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY
SELECT ${newQuantity} as available;`;
2024-05-31 11:58:26 +00:00
params = null;
}
2024-05-31 14:31:36 +00:00
return models.Ticket.rawSql(sqlStatement, params, opts);
2024-05-31 11:58:26 +00:00
});
2024-05-31 14:31:36 +00:00
await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
});
describe('newPrice', () => {
it('should increase quantity if you have enough available and the new price is the same as the previous one', async() => {
2024-05-31 11:58:26 +00:00
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = 31;
2024-05-31 14:31:36 +00:00
const item = await models.Item.findById(itemId, null, opts);
await item.updateAttribute('minQuantity', minQuantity, opts);
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
2024-05-31 11:58:26 +00:00
if (sqlStatement.includes('catalog_calcFromItem')) {
sqlStatement = `
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 7.07 as price;`;
2024-05-31 11:58:26 +00:00
params = null;
}
2024-05-31 14:31:36 +00:00
return models.Ticket.rawSql(sqlStatement, params, opts);
2024-05-31 11:58:26 +00:00
});
2024-05-31 14:31:36 +00:00
await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
});
it('should increase quantity when the new price is lower than the previous one', async() => {
2024-05-31 11:58:26 +00:00
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = 31;
2024-05-31 14:31:36 +00:00
const item = await models.Item.findById(itemId, null, opts);
await item.updateAttribute('minQuantity', minQuantity, opts);
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
2024-05-31 11:58:26 +00:00
if (sqlStatement.includes('catalog_calcFromItem')) {
sqlStatement = `
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 1 as price;`;
2024-05-31 11:58:26 +00:00
params = null;
}
2024-05-31 14:31:36 +00:00
return models.Ticket.rawSql(sqlStatement, params, opts);
2024-05-31 11:58:26 +00:00
});
2024-05-31 14:31:36 +00:00
await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
});
it('should throw error when increase quantity and the new price is higher than the previous one', async() => {
2024-05-31 11:58:26 +00:00
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(employeeId, true));
const itemId = 2;
const saleId = 17;
const minQuantity = 30;
const newQuantity = 31;
try {
2024-05-31 14:31:36 +00:00
const item = await models.Item.findById(itemId, null, opts);
await item.updateAttribute('minQuantity', minQuantity, opts);
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, opts) => {
if (sqlStatement.includes('catalog_calcFromItem')) {
sqlStatement = `
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketCalculateItem ENGINE = MEMORY SELECT ${newQuantity} as available;
CREATE OR REPLACE TEMPORARY TABLE tmp.ticketComponentPrice ENGINE = MEMORY SELECT 1 as grouping, 100000 as price;`;
params = null;
}
2024-05-31 14:31:36 +00:00
return models.Ticket.rawSql(sqlStatement, params, opts);
});
2024-05-31 14:31:36 +00:00
await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts);
} catch (e) {
2024-05-31 11:58:26 +00:00
expect(e).toEqual(new Error('The price of the item changed'));
}
});
});
});
2024-05-31 14:31:36 +00:00
describe('add a sale from a collection or previa ticket', () => {
it('if is allocated to them and alert level higher than 0 as Production role', async() => {
const ctx = getCtx(productionId);
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(productionId, true));
const beforeSalesCollection = await models.Sale.count({ticketFk: ticketCollectionProd}, opts);
await models.Ticket.addSale(ctx, ticketCollectionProd, barcode, 20, opts);
const afterSalesCollection = await models.Sale.count({ticketFk: ticketCollectionProd}, opts);
expect(afterSalesCollection).toEqual(beforeSalesCollection + 1);
const beforeSalesPrevia = await models.Sale.count({ticketFk: previaTicketProd}, opts);
await models.Ticket.addSale(ctx, previaTicketProd, barcode, 20, opts);
const afterSalesPrevia = await models.Sale.count({ticketFk: previaTicketProd}, opts);
expect(afterSalesPrevia).toEqual(beforeSalesPrevia + 1);
});
it('should throw an error if is not allocated to them and alert level higher than 0 as Production role', async() => {
const ctx = getCtx(productionId);
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(productionId, true));
try {
await models.Ticket.addSale(ctx, ticketCollectionSalesPerson, barcode, 20, opts);
} catch ({message}) {
expect(message).toEqual(notEditableError);
}
try {
await models.Ticket.addSale(ctx, previaTicketSalesPerson, barcode, 20, opts);
} catch ({message}) {
expect(message).toEqual(notEditableError);
}
});
it('if is allocated to them and alert level higher than 0 as salesPerson role', async() => {
const ctx = getCtx(salesPersonId);
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(salesPersonId, true));
const beforeSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts);
await models.Ticket.addSale(ctx, ticketCollectionSalesPerson, barcode, 20, opts);
const afterSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts);
expect(afterSalesCollection).toEqual(beforeSalesCollection + 1);
const beforeSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts);
await models.Ticket.addSale(ctx, previaTicketSalesPerson, barcode, 20, opts);
const afterSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts);
expect(afterSalesPrevia).toEqual(beforeSalesPrevia + 1);
});
it('should throw an error if is not allocated to them and alert level higher than 0 as salesPerson role', async() => {
const ctx = getCtx(salesPersonId);
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(salesPersonId, true));
try {
await models.Ticket.addSale(ctx, ticketCollectionProd, barcode, 20, opts);
} catch ({message}) {
expect(message).toEqual(notEditableError);
}
});
it('if is a reviewer', async() => {
const ctx = getCtx(reviewerId);
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(reviewerId, true));
const beforeSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts);
await models.Ticket.addSale(ctx, ticketCollectionSalesPerson, barcode, 20, opts);
const afterSalesCollection = await models.Sale.count({ticketFk: ticketCollectionSalesPerson}, opts);
expect(afterSalesCollection).toEqual(beforeSalesCollection + 1);
const beforeSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts);
await models.Ticket.addSale(ctx, previaTicketSalesPerson, barcode, 20, opts);
const afterSalesPrevia = await models.Sale.count({ticketFk: previaTicketSalesPerson}, opts);
expect(afterSalesPrevia).toEqual(beforeSalesPrevia + 1);
});
});
});