diff --git a/db/changes/10390-constitution/00-sale_recalcComponent.sql b/db/changes/10390-constitution/00-sale_recalcComponent.sql
new file mode 100644
index 000000000..c28115470
--- /dev/null
+++ b/db/changes/10390-constitution/00-sale_recalcComponent.sql
@@ -0,0 +1,120 @@
+DROP PROCEDURE IF EXISTS `vn`.`sale_recalcComponent`;
+
+DELIMITER $$
+$$
+CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`sale_recalcComponent`(vOption INT)
+proc: BEGIN
+/**
+ * Actualiza los componentes
+ *
+ * @table tmp.recalculateSales
+ */
+ DECLARE vShipped DATE;
+ DECLARE vWarehouseFk SMALLINT;
+ DECLARE vAgencyModeFk INT;
+ DECLARE vAddressFk INT;
+ DECLARE vTicketFk BIGINT;
+ DECLARE vItemFk BIGINT;
+ DECLARE vLanded DATE;
+ DECLARE vIsEditable BOOLEAN;
+ DECLARE vZoneFk INTEGER;
+ DECLARE vOption INTEGER;
+
+ DECLARE vSale INTEGER;
+ DECLARE vDone BOOL DEFAULT FALSE;
+
+ DECLARE vCur CURSOR FOR
+ SELECT id from tmp.recalculateSales;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND SET vDone = TRUE;
+
+ OPEN vCur;
+
+ l: LOOP
+ SET vDone = FALSE;
+ FETCH vCur INTO vSale;
+
+ IF vDone THEN
+ LEAVE l;
+ END IF;
+
+ SELECT t.refFk IS NULL AND (IFNULL(ts.alertLevel, 0) = 0 OR s.price = 0),
+ s.ticketFk,
+ s.itemFk ,
+ t.zoneFk,
+ t.warehouseFk,
+ t.shipped,
+ t.addressFk,
+ t.agencyModeFk,
+ t.landed
+ INTO vIsEditable,
+ vTicketFk,
+ vItemFk,
+ vZoneFk,
+ vWarehouseFk,
+ vShipped,
+ vAddressFk,
+ vAgencyModeFk,
+ vLanded
+ FROM ticket t
+ JOIN sale s ON s.ticketFk = t.id
+ LEFT JOIN ticketState ts ON ts.ticketFk = t.id
+ WHERE s.id = vSale;
+
+ CALL zone_getLanded(vShipped, vAddressFk, vAgencyModeFk, vWarehouseFk, TRUE);
+
+ IF (SELECT COUNT(*) FROM tmp.zoneGetLanded LIMIT 1) = 0 THEN
+ CALL util.throw('There is no zone for these parameters');
+ END IF;
+
+ IF vLanded IS NULL OR vZoneFk IS NULL THEN
+
+ UPDATE ticket t
+ SET t.landed = (SELECT landed FROM tmp.zoneGetLanded LIMIT 1)
+ WHERE t.id = vTicketFk AND t.landed IS NULL;
+
+ IF vZoneFk IS NULL THEN
+ SELECT zoneFk INTO vZoneFk FROM tmp.zoneGetLanded LIMIT 1;
+ UPDATE ticket t
+ SET t.zoneFk = vZoneFk
+ WHERE t.id = vTicketFk AND t.zoneFk IS NULL;
+ END IF;
+
+ END IF;
+
+ DROP TEMPORARY TABLE tmp.zoneGetLanded;
+
+ -- rellena la tabla buyUltimate con la ultima compra
+ CALL buyUltimate (vWarehouseFk, vShipped);
+
+ DELETE FROM tmp.buyUltimate WHERE itemFk != vItemFk;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot;
+ CREATE TEMPORARY TABLE tmp.ticketLot
+ SELECT vWarehouseFk warehouseFk, NULL available, vItemFk itemFk, buyFk, vZoneFk zoneFk
+ FROM tmp.buyUltimate
+ WHERE itemFk = vItemFk;
+
+ CALL catalog_componentPrepare();
+ CALL catalog_componentCalculate(vZoneFk, vAddressFk, vShipped, vWarehouseFk);
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.sale;
+ CREATE TEMPORARY TABLE tmp.sale
+ (PRIMARY KEY (saleFk)) ENGINE = MEMORY
+ SELECT vSale saleFk,vWarehouseFk warehouseFk;
+
+ IF vOption IS NULL THEN
+ SET vOption = IF(vIsEditable, 1, 6);
+ END IF;
+
+ CALL ticketComponentUpdateSale(vOption);
+ CALL catalog_componentPurge();
+
+ DROP TEMPORARY TABLE tmp.buyUltimate;
+ DROP TEMPORARY TABLE tmp.sale;
+
+ END LOOP;
+ CLOSE vCur;
+
+END$$
+DELIMITER ;
diff --git a/db/changes/10390-constitution/01-sale_calculateComponent.sql b/db/changes/10390-constitution/01-sale_calculateComponent.sql
new file mode 100644
index 000000000..97a154362
--- /dev/null
+++ b/db/changes/10390-constitution/01-sale_calculateComponent.sql
@@ -0,0 +1,23 @@
+DROP PROCEDURE IF EXISTS `vn`.`sale_calculateComponent`;
+
+DELIMITER $$
+$$
+CREATE DEFINER=`root`@`%` PROCEDURE `vn`.`sale_calculateComponent`(vSale INT, vOption INT)
+proc: BEGIN
+/**
+ * Crea tabla temporal para vn.sale_recalcComponent() para recalcular los componentes
+ *
+ * @param vSale Id de la venta
+ * @param vOption indica en que componente pone el descuadre, NULL en casos habituales
+ */
+ DROP TEMPORARY TABLE IF EXISTS tmp.recalculateSales;
+ CREATE TEMPORARY TABLE tmp.recalculateSales
+ SELECT s.id
+ FROM sale s
+ WHERE s.id = vSale;
+
+ CALL vn.sale_recalcComponent(vOption);
+
+ DROP TEMPORARY TABLE tmp.recalculateSales;
+END$$
+DELIMITER ;
\ No newline at end of file
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index b95ecbd7f..24b87b398 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -557,6 +557,7 @@ export default {
moreMenuReserve: 'vn-item[name="reserve"]',
moreMenuUnmarkReseved: 'vn-item[name="unreserve"]',
moreMenuUpdateDiscount: 'vn-item[name="discount"]',
+ moreMenuRecalculatePrice: 'vn-item[name="calculatePrice"]',
moreMenuUpdateDiscountInput: 'vn-input-number[ng-model="$ctrl.edit.discount"] input',
transferQuantityInput: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable > span > text',
transferQuantityCell: '.vn-popover.shown vn-table > div > vn-tbody > vn-tr > vn-td-editable',
diff --git a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
index 55bbdf9ff..dfda4dcfb 100644
--- a/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
+++ b/e2e/paths/05-ticket/01-sale/02_edit_sale.spec.js
@@ -195,6 +195,17 @@ describe('Ticket Edit sale path', () => {
expect(result).toContain('22.50');
});
+ it('should recalculate price of sales', async() => {
+ await page.waitToClick(selectors.ticketSales.firstSaleCheckbox);
+ await page.waitToClick(selectors.ticketSales.secondSaleCheckbox);
+
+ await page.waitToClick(selectors.ticketSales.moreMenu);
+ await page.waitToClick(selectors.ticketSales.moreMenuRecalculatePrice);
+ const message = await page.waitForSnackbar();
+
+ expect(message.text).toContain('Data saved!');
+ });
+
it('should select the third sale and create a claim of it', async() => {
await page.waitToClick(selectors.ticketSales.thirdSaleCheckbox);
await page.waitToClick(selectors.ticketSales.moreMenu);
diff --git a/modules/ticket/back/methods/sale/recalculatePrice.js b/modules/ticket/back/methods/sale/recalculatePrice.js
index 3ffc23c3b..8a390223d 100644
--- a/modules/ticket/back/methods/sale/recalculatePrice.js
+++ b/modules/ticket/back/methods/sale/recalculatePrice.js
@@ -1,26 +1,26 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
Self.remoteMethodCtx('recalculatePrice', {
- description: 'Calculates the price of a sale and its components',
+ description: 'Calculates the price of sales and its components',
accessType: 'WRITE',
accepts: [{
- arg: 'id',
- description: 'The sale id',
- type: 'number',
+ arg: 'sales',
+ description: 'The sales',
+ type: ['object'],
required: true,
- http: {source: 'path'}
+ http: {source: 'body'}
}],
returns: {
- type: 'Number',
+ type: 'number',
root: true
},
http: {
- path: `/:id/recalculatePrice`,
+ path: `/recalculatePrice`,
verb: 'post'
}
});
- Self.recalculatePrice = async(ctx, id, options) => {
+ Self.recalculatePrice = async(ctx, sales, options) => {
const models = Self.app.models;
const myOptions = {};
let tx;
@@ -34,18 +34,33 @@ module.exports = Self => {
}
try {
- const sale = await Self.findById(id, null, myOptions);
- const isEditable = await models.Ticket.isEditable(ctx, sale.ticketFk, myOptions);
+ const salesIds = [];
+ const params = [];
+ sales.forEach(sale => {
+ salesIds.push(sale.id);
+ params.push('?');
+ });
+ const isEditable = await models.Ticket.isEditable(ctx, sales[0].ticketFk, myOptions);
if (!isEditable)
throw new UserError(`The sales of this ticket can't be modified`);
- const canEditSale = await models.Sale.canEdit(ctx, [id], myOptions);
-
+ const canEditSale = await models.Sale.canEdit(ctx, sales, myOptions);
if (!canEditSale)
throw new UserError(`Sale(s) blocked, please contact production`);
- const recalculation = await Self.rawSql('CALL vn.sale_calculateComponent(?, null)', [id], myOptions);
+ const paramsString = params.join();
+
+ const query = `
+ DROP TEMPORARY TABLE IF EXISTS tmp.recalculateSales;
+ CREATE TEMPORARY TABLE tmp.recalculateSales
+ SELECT s.id
+ FROM sale s
+ WHERE s.id IN (${paramsString});
+ CALL vn.sale_recalcComponent(null);
+ DROP TEMPORARY TABLE tmp.recalculateSales;`;
+
+ const recalculation = await Self.rawSql(query, salesIds, myOptions);
if (tx) await tx.commit();
diff --git a/modules/ticket/back/methods/sale/specs/recalculatePrice.spec.js b/modules/ticket/back/methods/sale/specs/recalculatePrice.spec.js
index 4f6579d32..0fc68dee7 100644
--- a/modules/ticket/back/methods/sale/specs/recalculatePrice.spec.js
+++ b/modules/ticket/back/methods/sale/specs/recalculatePrice.spec.js
@@ -1,18 +1,20 @@
const models = require('vn-loopback/server/server').models;
describe('sale recalculatePrice()', () => {
- const saleId = 7;
-
it('should update the sale price', async() => {
const tx = await models.Sale.beginTransaction({});
-
+ const sales = [
+ {id: 7, ticketFk: 11},
+ {id: 8, ticketFk: 11}
+ ];
try {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}};
- const response = await models.Sale.recalculatePrice(ctx, saleId, options);
+ const response = await models.Sale.recalculatePrice(ctx, sales, options);
- expect(response.affectedRows).toBeDefined();
+ expect(response[0].affectedRows).toBeDefined();
+ expect(response[1].affectedRows).toBeDefined();
await tx.rollback();
} catch (e) {
@@ -29,8 +31,8 @@ describe('sale recalculatePrice()', () => {
const options = {transaction: tx};
const ctx = {req: {accessToken: {userId: 9}}};
- const immutableSaleId = 1;
- await models.Sale.recalculatePrice(ctx, immutableSaleId, options);
+ const immutableSale = [{id: 1, ticketFk: 1}];
+ await models.Sale.recalculatePrice(ctx, immutableSale, options);
await tx.rollback();
} catch (e) {
diff --git a/modules/ticket/front/sale/index.html b/modules/ticket/front/sale/index.html
index 996135c1e..f7a279d9a 100644
--- a/modules/ticket/front/sale/index.html
+++ b/modules/ticket/front/sale/index.html
@@ -464,7 +464,7 @@
+ ng-if="$ctrl.isEditable">
Recalculate price
{
+ const sales = this.selectedValidSales();
+ if (!sales) return;
+
+ const query = `Sales/recalculatePrice`;
+ this.$http.post(query, sales).then(() => {
this.vnApp.showSuccess(this.$t('Data saved!'));
this.$.model.refresh();
});
diff --git a/modules/ticket/front/sale/index.spec.js b/modules/ticket/front/sale/index.spec.js
index d543c1b81..673e9a3f9 100644
--- a/modules/ticket/front/sale/index.spec.js
+++ b/modules/ticket/front/sale/index.spec.js
@@ -684,14 +684,15 @@ describe('Ticket', () => {
});
describe('calculateSalePrice()', () => {
- it('should make an HTTP post query ', () => {
+ it('should make an HTTP post query', () => {
jest.spyOn(controller.vnApp, 'showSuccess').mockReturnThis();
jest.spyOn(controller.$.model, 'refresh').mockReturnThis();
- const selectedSale = controller.sales[0];
- selectedSale.checked = true;
+ controller.sales.forEach(sale => {
+ sale.checked = true;
+ });
- $httpBackend.expect('POST', `Sales/1/recalculatePrice`).respond(200);
+ $httpBackend.expect('POST', `Sales/recalculatePrice`).respond(200);
controller.calculateSalePrice();
$httpBackend.flush();