Merge pull request 'refs #6199 feat(sale_updateQuantity): can upload' (!1804) from 6199-saleUploadQuantity into test
gitea/salix/pipeline/head This commit looks good
Details
gitea/salix/pipeline/head This commit looks good
Details
Reviewed-on: #1804 Reviewed-by: Javi Gallego <jgallego@verdnatura.es>
This commit is contained in:
commit
2b3fb4f89e
|
@ -18,7 +18,14 @@ describe('setSaleQuantity()', () => {
|
||||||
|
|
||||||
it('should change quantity sale', async() => {
|
it('should change quantity sale', async() => {
|
||||||
const tx = await models.Ticket.beginTransaction({});
|
const tx = await models.Ticket.beginTransaction({});
|
||||||
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: 100}))));
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
|
|
@ -134,14 +134,6 @@ describe('Ticket Edit sale path', () => {
|
||||||
await page.accessToSection('ticket.card.sale');
|
await page.accessToSection('ticket.card.sale');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should try to add a higher quantity value and then receive an error', async() => {
|
|
||||||
await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
|
|
||||||
await page.type(selectors.ticketSales.firstSaleQuantity, '11\u000d');
|
|
||||||
const message = await page.waitForSnackbar();
|
|
||||||
|
|
||||||
expect(message.text).toContain('The new quantity should be smaller than the old one');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should remove 1 from the first sale quantity', async() => {
|
it('should remove 1 from the first sale quantity', async() => {
|
||||||
await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
|
await page.waitToClick(selectors.ticketSales.firstSaleQuantityCell);
|
||||||
await page.waitForSelector(selectors.ticketSales.firstSaleQuantity);
|
await page.waitForSelector(selectors.ticketSales.firstSaleQuantity);
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"The default consignee can not be unchecked": "The default consignee can not be unchecked",
|
"The default consignee can not be unchecked": "The default consignee can not be unchecked",
|
||||||
"Enter an integer different to zero": "Enter an integer different to zero",
|
"Enter an integer different to zero": "Enter an integer different to zero",
|
||||||
"Package cannot be blank": "Package cannot be blank",
|
"Package cannot be blank": "Package cannot be blank",
|
||||||
"The new quantity should be smaller than the old one": "The new quantity should be smaller than the old one",
|
"The price of the item changed": "The price of the item changed",
|
||||||
"The sales of this ticket can't be modified": "The sales of this ticket can't be modified",
|
"The sales of this ticket can't be modified": "The sales of this ticket can't be modified",
|
||||||
"Cannot check Equalization Tax in this NIF/CIF": "Cannot check Equalization Tax in this NIF/CIF",
|
"Cannot check Equalization Tax in this NIF/CIF": "Cannot check Equalization Tax in this NIF/CIF",
|
||||||
"You can't create an order for a frozen client": "You can't create an order for a frozen client",
|
"You can't create an order for a frozen client": "You can't create an order for a frozen client",
|
||||||
|
@ -191,4 +191,4 @@
|
||||||
"Booking completed": "Booking complete",
|
"Booking completed": "Booking complete",
|
||||||
"The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation",
|
"The ticket is in preparation": "The ticket [{{ticketId}}]({{{ticketUrl}}}) of the sales person {{salesPersonId}} is in preparation",
|
||||||
"You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets"
|
"You can only add negative amounts in refund tickets": "You can only add negative amounts in refund tickets"
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
"The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero",
|
"The grade must be an integer greater than or equal to zero": "El grade debe ser un entero mayor o igual a cero",
|
||||||
"Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco",
|
"Sample type cannot be blank": "El tipo de plantilla no puede quedar en blanco",
|
||||||
"Description cannot be blank": "Se debe rellenar el campo de texto",
|
"Description cannot be blank": "Se debe rellenar el campo de texto",
|
||||||
"The new quantity should be smaller than the old one": "La nueva cantidad debe de ser menor que la anterior",
|
"The price of the item changed": "El precio del artículo cambió",
|
||||||
"The value should not be greater than 100%": "El valor no debe de ser mayor de 100%",
|
"The value should not be greater than 100%": "El valor no debe de ser mayor de 100%",
|
||||||
"The value should be a number": "El valor debe ser un numero",
|
"The value should be a number": "El valor debe ser un numero",
|
||||||
"This order is not editable": "Esta orden no se puede modificar",
|
"This order is not editable": "Esta orden no se puede modificar",
|
||||||
|
|
|
@ -24,34 +24,6 @@ describe('sale updateQuantity()', () => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should throw an error if the quantity is greater than it should be', async() => {
|
|
||||||
const ctx = {
|
|
||||||
req: {
|
|
||||||
accessToken: {userId: 1},
|
|
||||||
headers: {origin: 'localhost:5000'},
|
|
||||||
__: () => {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
|
||||||
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: 100}))));
|
|
||||||
|
|
||||||
const tx = await models.Sale.beginTransaction({});
|
|
||||||
|
|
||||||
let error;
|
|
||||||
try {
|
|
||||||
const options = {transaction: tx};
|
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, 17, 31, options);
|
|
||||||
|
|
||||||
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 add quantity if the quantity is greater than it should be and is role advanced', async() => {
|
it('should add quantity if the quantity is greater than it should be and is role advanced', async() => {
|
||||||
const saleId = 17;
|
const saleId = 17;
|
||||||
const buyerId = 35;
|
const buyerId = 35;
|
||||||
|
@ -64,7 +36,14 @@ describe('sale updateQuantity()', () => {
|
||||||
};
|
};
|
||||||
const tx = await models.Sale.beginTransaction({});
|
const tx = await models.Sale.beginTransaction({});
|
||||||
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(buyerId));
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(buyerId));
|
||||||
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: 100}))));
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const options = {transaction: tx};
|
const options = {transaction: tx};
|
||||||
|
@ -193,6 +172,14 @@ describe('sale updateQuantity()', () => {
|
||||||
|
|
||||||
const item = await models.Item.findById(itemId, null, options);
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
await item.updateAttribute('minQuantity', minQuantity, options);
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
@ -226,7 +213,15 @@ describe('sale updateQuantity()', () => {
|
||||||
|
|
||||||
const item = await models.Item.findById(itemId, null, options);
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
await item.updateAttribute('minQuantity', minQuantity, options);
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
spyOn(models.Item, 'getVisibleAvailable').and.returnValue((new Promise(resolve => resolve({available: newQuantity}))));
|
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
@ -236,4 +231,87 @@ describe('sale updateQuantity()', () => {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should increase quantity if you have enough available and the new price is the same as the previous one', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const itemId = 2;
|
||||||
|
const saleId = 17;
|
||||||
|
const minQuantity = 30;
|
||||||
|
const newQuantity = 31;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
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, 8 as price;`;
|
||||||
|
params = null;
|
||||||
|
}
|
||||||
|
return models.Ticket.rawSql(sqlStatement, params, options);
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw error when increase quantity and the new price is lower than the previous one', async() => {
|
||||||
|
const ctx = {
|
||||||
|
req: {
|
||||||
|
accessToken: {userId: 1},
|
||||||
|
headers: {origin: 'localhost:5000'},
|
||||||
|
__: () => {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spyOn(LoopBackContext, 'getCurrentContext').and.returnValue(getActiveCtx(1));
|
||||||
|
|
||||||
|
const tx = await models.Sale.beginTransaction({});
|
||||||
|
const itemId = 2;
|
||||||
|
const saleId = 17;
|
||||||
|
const minQuantity = 30;
|
||||||
|
const newQuantity = 31;
|
||||||
|
|
||||||
|
let error;
|
||||||
|
try {
|
||||||
|
const options = {transaction: tx};
|
||||||
|
|
||||||
|
const item = await models.Item.findById(itemId, null, options);
|
||||||
|
await item.updateAttribute('minQuantity', minQuantity, options);
|
||||||
|
spyOn(models.Sale, 'rawSql').and.callFake((sqlStatement, params, options) => {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
await models.Sale.updateQuantity(ctx, saleId, newQuantity, options);
|
||||||
|
|
||||||
|
await tx.rollback();
|
||||||
|
} catch (e) {
|
||||||
|
await tx.rollback();
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).toEqual(new Error('The price of the item changed'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,11 +31,13 @@ module.exports = Self => {
|
||||||
|
|
||||||
const ticketId = changes?.ticketFk || instance?.ticketFk;
|
const ticketId = changes?.ticketFk || instance?.ticketFk;
|
||||||
const itemId = changes?.itemFk || instance?.itemFk;
|
const itemId = changes?.itemFk || instance?.itemFk;
|
||||||
|
const oldQuantity = instance?.quantity ?? null;
|
||||||
|
const quantityAdded = newQuantity - oldQuantity;
|
||||||
|
|
||||||
const ticket = await models.Ticket.findById(
|
const ticket = await models.Ticket.findById(
|
||||||
ticketId,
|
ticketId,
|
||||||
{
|
{
|
||||||
fields: ['id', 'clientFk', 'warehouseFk', 'shipped'],
|
fields: ['id', 'clientFk', 'warehouseFk', 'addressFk', 'agencyModeFk', 'shipped', 'landed'],
|
||||||
include: {
|
include: {
|
||||||
relation: 'client',
|
relation: 'client',
|
||||||
scope: {
|
scope: {
|
||||||
|
@ -65,28 +67,46 @@ module.exports = Self => {
|
||||||
fields: ['family', 'minQuantity'],
|
fields: ['family', 'minQuantity'],
|
||||||
where: {id: itemId},
|
where: {id: itemId},
|
||||||
}, ctx.options);
|
}, ctx.options);
|
||||||
|
|
||||||
if (item.family == 'EMB') return;
|
if (item.family == 'EMB') return;
|
||||||
|
|
||||||
const itemInfo = await models.Item.getVisibleAvailable(
|
await models.Sale.rawSql(`CALL catalog_calcFromItem(?,?,?,?)`, [
|
||||||
itemId,
|
ticket.landed,
|
||||||
ticket.warehouseFk,
|
ticket.addressFk,
|
||||||
ticket.shipped,
|
ticket.agencyModeFk,
|
||||||
ctx.options
|
itemId
|
||||||
);
|
],
|
||||||
|
ctx.options);
|
||||||
|
|
||||||
const oldQuantity = instance?.quantity ?? null;
|
const [itemInfo] = await models.Sale.rawSql(`SELECT available FROM tmp.ticketCalculateItem`, null, ctx.options);
|
||||||
const quantityAdded = newQuantity - oldQuantity;
|
|
||||||
if (itemInfo.available < quantityAdded)
|
if (!itemInfo?.available || itemInfo.available < quantityAdded)
|
||||||
throw new UserError(`This item is not available`);
|
throw new UserError(`This item is not available`);
|
||||||
|
|
||||||
if (await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*')) return;
|
if (await models.ACL.checkAccessAcl(ctx, 'Ticket', 'isRoleAdvanced', '*')) return;
|
||||||
|
|
||||||
if (newQuantity < item.minQuantity && itemInfo.available != newQuantity)
|
if (newQuantity < item.minQuantity && newQuantity != itemInfo?.available)
|
||||||
throw new UserError('The amount cannot be less than the minimum');
|
throw new UserError('The amount cannot be less than the minimum');
|
||||||
|
|
||||||
if (!ctx.isNewInstance && newQuantity > oldQuantity)
|
if (ctx.isNewInstance || newQuantity <= oldQuantity) return;
|
||||||
throw new UserError('The new quantity should be smaller than the old one');
|
|
||||||
|
const [saleGrouping] = await models.Sale.rawSql(`
|
||||||
|
SELECT MAX(t.grouping), t.price newPrice
|
||||||
|
FROM tmp.ticketComponentPrice t
|
||||||
|
WHERE t.grouping <= ?`,
|
||||||
|
[quantityAdded],
|
||||||
|
ctx.options);
|
||||||
|
|
||||||
|
await models.Sale.rawSql(`
|
||||||
|
DROP TEMPORARY TABLE IF EXISTS
|
||||||
|
tmp.ticketCalculateItem,
|
||||||
|
tmp.ticketComponentPrice,
|
||||||
|
tmp.ticketComponent,
|
||||||
|
tmp.ticketLot,
|
||||||
|
tmp.zoneGetShipped;
|
||||||
|
`, null, ctx.options);
|
||||||
|
|
||||||
|
if (!saleGrouping?.newPrice || saleGrouping.newPrice < instance.price)
|
||||||
|
throw new UserError('The price of the item changed');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue