diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index 24225c99a..6818e7200 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -763,9 +763,10 @@ INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeF (31, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL), (32, 1, 8, 1, 1, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL + 2 DAY), 1103, 'Phone Box', 123, NULL, 0, 1, 5, 1, util.VN_CURDATE(), NULL, NULL), (33, 1, 7, 1, 6, util.VN_CURDATE(), DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), 1102, 'NY roofs', 122, NULL, 0, 3, 5, 1, util.VN_CURDATE(), NULL, NULL), - (34, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1103, 'BEJAR', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL), - (35, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1102, 'Somewhere in Philippines', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL), - (36, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1102, 'Ant-Man Adventure', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL); + (34, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1103, 'BEJAR', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL), + (35, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1102, 'Somewhere in Philippines', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL), + (36, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1102, 'Ant-Man Adventure', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL), + (37, 1, 1, 1, 3, util.VN_CURDATE(), util.VN_CURDATE(), 1110, 'Deadpool swords', 123, NULL, 0, 1, 16, 0, util.VN_CURDATE(), NULL, NULL); INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`) VALUES @@ -3834,3 +3835,17 @@ INSERT INTO vn.sectorCollectionSaleGroup SET id = 8, sectorCollectionFk = 2, saleGroupFk = 4; + +INSERT INTO vn.saleGroup (userFk, parkingFk, sectorFk, ticketFk) + VALUES + (1, 1, 1, 37); + +INSERT INTO vn.sectorCollection + SET id = 3, + userFk = 18, + sectorFk = 1; + +INSERT INTO vn.sectorCollectionSaleGroup + SET id = 9, + sectorCollectionFk = 3, + saleGroupFk = 6; \ No newline at end of file diff --git a/modules/ticket/back/models/specs/sale.spec.js b/modules/ticket/back/models/specs/sale.spec.js index d50c039db..1aa40802b 100644 --- a/modules/ticket/back/models/specs/sale.spec.js +++ b/modules/ticket/back/models/specs/sale.spec.js @@ -2,14 +2,24 @@ const {models} = require('vn-loopback/server/server'); const LoopBackContext = require('loopback-context'); -fdescribe('sale model ', () => { +describe('sale model ', () => { const developerId = 9; const buyerId = 35; const employeeId = 1; + 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.'; const ctx = getCtx(developerId); let tx; - let options; + let opts; function getCtx(userId, active = false) { const accessToken = {userId}; @@ -20,7 +30,7 @@ fdescribe('sale model ', () => { beforeEach(async() => { tx = await models.Sale.beginTransaction({}); - options = {transaction: tx}; + opts = {transaction: tx}; }); afterEach(async() => await tx.rollback()); @@ -31,27 +41,27 @@ fdescribe('sale model ', () => { const ctx = getCtx(buyerId); spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getCtx(buyerId, true)); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { + 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; } - return models.Ticket.rawSql(sqlStatement, params, options); + return models.Ticket.rawSql(sqlStatement, params, opts); }); const isRoleAdvanced = await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*'); expect(isRoleAdvanced).toEqual(true); - const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); expect(originalLine.quantity).toEqual(30); const newQuantity = originalLine.quantity + 1; - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); - const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); expect(modifiedLine.quantity).toEqual(newQuantity); }); @@ -62,13 +72,13 @@ fdescribe('sale model ', () => { const saleId = 25; const newQuantity = 4; - const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + const originalLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); expect(originalLine.quantity).toEqual(20); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); - const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); expect(modifiedLine.quantity).toEqual(newQuantity); }); @@ -80,7 +90,7 @@ fdescribe('sale model ', () => { const newQuantity = -10; try { - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); } catch (e) { expect(e).toEqual(new Error('You can only add negative amounts in refund tickets')); } @@ -92,9 +102,9 @@ fdescribe('sale model ', () => { const saleId = 32; const newQuantity = -10; - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); - const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, options); + const modifiedLine = await models.Sale.findOne({where: {id: saleId}, fields: ['quantity']}, opts); expect(modifiedLine.quantity).toEqual(newQuantity); }); @@ -108,18 +118,18 @@ fdescribe('sale model ', () => { const newQuantity = minQuantity - 1; try { - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { + 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; } - return models.Ticket.rawSql(sqlStatement, params, options); + return models.Ticket.rawSql(sqlStatement, params, opts); }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); } catch (e) { expect(e).toEqual(new Error('The amount cannot be less than the minimum')); } @@ -133,19 +143,19 @@ fdescribe('sale model ', () => { const minQuantity = 30; const newQuantity = minQuantity - 1; - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); + const item = await models.Item.findById(itemId, null, opts); + await item.updateAttribute('minQuantity', minQuantity, opts); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { + 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;`; params = null; } - return models.Ticket.rawSql(sqlStatement, params, options); + return models.Ticket.rawSql(sqlStatement, params, opts); }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); }); describe('newPrice', () => { @@ -157,19 +167,19 @@ fdescribe('sale model ', () => { const minQuantity = 30; const newQuantity = 31; - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { + 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, 7.07 as price;`; params = null; } - return models.Ticket.rawSql(sqlStatement, params, options); + return models.Ticket.rawSql(sqlStatement, params, opts); }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); }); it('should increase quantity when the new price is lower than the previous one', async() => { @@ -180,19 +190,19 @@ fdescribe('sale model ', () => { const minQuantity = 30; const newQuantity = 31; - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { + 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, 1 as price;`; params = null; } - return models.Ticket.rawSql(sqlStatement, params, options); + return models.Ticket.rawSql(sqlStatement, params, opts); }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + 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() => { @@ -204,23 +214,105 @@ fdescribe('sale model ', () => { const newQuantity = 31; try { - const item = await models.Item.findById(itemId, null, options); - await item.updateAttribute('minQuantity', minQuantity, options); - spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => { + 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; } - return models.Ticket.rawSql(sqlStatement, params, options); + return models.Ticket.rawSql(sqlStatement, params, opts); }); - await models.Sale.updateQuantity(ctx, saleId, newQuantity, options); + await models.Sale.updateQuantity(ctx, saleId, newQuantity, opts); } catch (e) { expect(e).toEqual(new Error('The price of the item changed')); } }); }); }); + + 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); + }); + }); });