diff --git a/db/changes/10110-postCampaign/00-ticket_recalcComponents.sql b/db/changes/10110-postCampaign/00-ticket_recalcComponents.sql
new file mode 100644
index 000000000..885e2998a
--- /dev/null
+++ b/db/changes/10110-postCampaign/00-ticket_recalcComponents.sql
@@ -0,0 +1,77 @@
+USE `vn`;
+DROP procedure IF EXISTS `ticket_recalcComponents`;
+
+DELIMITER $$
+USE `vn`$$
+CREATE DEFINER=`root`@`%` PROCEDURE `ticket_recalcComponents`(IN vTicketFk BIGINT )
+proc: BEGIN
+
+/**
+ * Este procedimiento trata de "rebionizar" un ticket,
+ * eliminando los componentes existentes e insertandolos de nuevo
+ *
+ * @param vTicketFk Id del ticket
+ * @return tmp.buyUltimate
+ */
+ DECLARE vShipped DATE;
+ DECLARE vWarehouseFk SMALLINT;
+ DECLARE vAgencyModeFk INT;
+ DECLARE vAddressFk INT;
+ DECLARE vLanded DATE;
+ DECLARE vIsTicketEditable BOOLEAN;
+ DECLARE vZoneFk INTEGER;
+
+ SELECT (IFNULL(ts.alertLevel,0) >0 or IFNULL(t.refFk,"") != "") = FALSE, t.zoneFk
+ INTO vIsTicketEditable, vZoneFk
+ FROM ticket t LEFT JOIN ticketState ts ON t.id = ts.ticket
+ WHERE id = vTicketFk;
+
+ SELECT warehouseFk, date(shipped), addressFk, agencyModeFk, landed
+ INTO vWarehouseFk, vShipped, vAddressFk, vAgencyModeFk, vLanded
+ FROM ticket
+ WHERE id = vTicketFk;
+
+ CALL zoneGetShippedWarehouse(vLanded, vAddressFk , vAgencyModeFk);
+
+ CALL vn.buyUltimate (vWarehouseFk, vShipped); -- rellena la tabla buyUltimate con la ultima compra
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot;
+ CREATE TEMPORARY TABLE tmp.ticketLot
+ SELECT vWarehouseFk warehouseFk, NULL available,
+ s.itemFk, bu.buyFk
+ FROM sale s
+ LEFT JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk
+ WHERE s.ticketFk = vTicketFk
+ GROUP BY s.itemFk;
+
+ CALL vn.catalog_componentCalculate(vZoneFk, vAddressFk, vShipped);
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.sale;
+ CREATE TEMPORARY TABLE tmp.sale
+ (PRIMARY KEY (saleFk)) ENGINE = MEMORY
+ SELECT id saleFk, vWarehouseFk warehouseFk
+ FROM sale s
+ WHERE s.ticketFk = vTicketFk;
+
+ CALL vn.ticketComponentUpdateSale(IF(vIsTicketEditable,1,6)); -- si el ticket esta facturado, respeta los precios
+
+ IF vLanded IS NULL THEN
+
+ CALL zoneGetLanded(vShipped, vAddressFk, vAgencyModeFk, vWarehouseFk);
+
+ UPDATE vn2008.Tickets t
+ SET t.landing = (SELECT landed FROM tmp.zoneGetLanded)
+ WHERE Id_Ticket = vTicketFk;
+
+ DROP TEMPORARY TABLE tmp.zoneGetLanded;
+
+ END IF;
+
+ DROP TEMPORARY TABLE tmp.buyUltimate;
+ DROP TEMPORARY TABLE tmp.ticketComponentPrice;
+ DROP TEMPORARY TABLE tmp.ticketComponent;
+ DROP TEMPORARY TABLE tmp.sale;
+END$$
+
+DELIMITER ;
+
diff --git a/db/changes/10110-postCampaign/00-ticket_recalcComponentsForcePrice.sql b/db/changes/10110-postCampaign/00-ticket_recalcComponentsForcePrice.sql
new file mode 100644
index 000000000..90d65fcc1
--- /dev/null
+++ b/db/changes/10110-postCampaign/00-ticket_recalcComponentsForcePrice.sql
@@ -0,0 +1,77 @@
+USE `vn`;
+DROP procedure IF EXISTS `ticket_recalcComponentsForcePrice`;
+
+DELIMITER $$
+USE `vn`$$
+CREATE DEFINER=`root`@`%` PROCEDURE `ticket_recalcComponentsForcePrice`(IN vTicketFk BIGINT, vIsTicketEditable BOOLEAN )
+proc: BEGIN
+
+/**
+ * Este procedimiento trata de "rebionizar" un ticket,
+ * eliminando los componentes existentes e insertandolos de nuevo
+ *
+ * @param vTicketFk Id del ticket
+ * @return tmp.buyUltimate
+ */
+ DECLARE vShipped DATE;
+ DECLARE vWarehouseFk SMALLINT;
+ DECLARE vAgencyModeFk INT;
+ DECLARE vAddressFk INT;
+ DECLARE vLanded DATE;
+ DECLARE vZoneFk INTEGER;
+
+ IF vIsTicketEditable IS NULL THEN
+ SELECT (IFNULL(ts.alertLevel,0) >0 or IFNULL(t.refFk,"") != "") = FALSE, t.zoneFk
+ INTO vIsTicketEditable, vZoneFk
+ FROM ticket t LEFT JOIN ticketState ts ON t.id = ts.ticket
+ WHERE id = vTicketFk;
+ END IF;
+ SELECT warehouseFk, date(shipped), addressFk, agencyModeFk, landed
+ INTO vWarehouseFk, vShipped, vAddressFk, vAgencyModeFk, vLanded
+ FROM ticket
+ WHERE id = vTicketFk;
+
+ CALL zoneGetShippedWarehouse(vLanded, vAddressFk , vAgencyModeFk);
+
+ CALL vn.buyUltimate (vWarehouseFk, vShipped); -- rellena la tabla buyUltimate con la ultima compra
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.ticketLot;
+ CREATE TEMPORARY TABLE tmp.ticketLot
+ SELECT vWarehouseFk warehouseFk, NULL available,
+ s.itemFk, bu.buyFk
+ FROM sale s
+ LEFT JOIN tmp.buyUltimate bu ON bu.itemFk = s.itemFk
+ WHERE s.ticketFk = vTicketFk
+ GROUP BY s.itemFk;
+
+ CALL vn.catalog_componentCalculate(vZoneFk, vAddressFk, vShipped);
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.sale;
+ CREATE TEMPORARY TABLE tmp.sale
+ (PRIMARY KEY (saleFk)) ENGINE = MEMORY
+ SELECT id saleFk, vWarehouseFk warehouseFk
+ FROM sale s
+ WHERE s.ticketFk = vTicketFk;
+
+ CALL vn.ticketComponentUpdateSale(IF(vIsTicketEditable,1,6)); -- si el ticket esta facturado, respeta los precios
+
+ IF vLanded IS NULL THEN
+
+ CALL zoneGetLanded(vShipped, vAddressFk, vAgencyModeFk, vWarehouseFk);
+
+ UPDATE vn2008.Tickets t
+ SET t.landing = (SELECT landed FROM tmp.zoneGetLanded)
+ WHERE Id_Ticket = vTicketFk;
+
+ DROP TEMPORARY TABLE tmp.zoneGetLanded;
+
+ END IF;
+
+ DROP TEMPORARY TABLE tmp.buyUltimate;
+ DROP TEMPORARY TABLE tmp.ticketComponentPrice;
+ DROP TEMPORARY TABLE tmp.ticketComponent;
+ DROP TEMPORARY TABLE tmp.sale;
+END$$
+
+DELIMITER ;
+
diff --git a/front/core/components/confirm/confirm.js b/front/core/components/confirm/confirm.js
index 9d53df798..f187a3cb4 100644
--- a/front/core/components/confirm/confirm.js
+++ b/front/core/components/confirm/confirm.js
@@ -1,6 +1,7 @@
import ngModule from '../../module';
import Dialog from '../dialog';
import template from './confirm.html';
+import './style.scss';
export default class Confirm extends Dialog {
constructor($element, $, $transclude) {
diff --git a/front/core/components/confirm/style.scss b/front/core/components/confirm/style.scss
new file mode 100644
index 000000000..d3cea6cb1
--- /dev/null
+++ b/front/core/components/confirm/style.scss
@@ -0,0 +1,3 @@
+.vn-confirm .window {
+ max-width: 30em
+}
\ No newline at end of file
diff --git a/modules/ticket/back/methods/sale/calculate.js b/modules/ticket/back/methods/sale/recalculatePrice.js
similarity index 86%
rename from modules/ticket/back/methods/sale/calculate.js
rename to modules/ticket/back/methods/sale/recalculatePrice.js
index 5a2ac39b8..1bd754f6a 100644
--- a/modules/ticket/back/methods/sale/calculate.js
+++ b/modules/ticket/back/methods/sale/recalculatePrice.js
@@ -1,6 +1,6 @@
const UserError = require('vn-loopback/util/user-error');
module.exports = Self => {
- Self.remoteMethodCtx('calculate', {
+ Self.remoteMethodCtx('recalculatePrice', {
description: 'Calculates the price of a sale and its components',
accessType: 'WRITE',
accepts: [{
@@ -15,12 +15,12 @@ module.exports = Self => {
root: true
},
http: {
- path: `/:id/calculate`,
+ path: `/:id/recalculatePrice`,
verb: 'post'
}
});
- Self.calculate = async(ctx, id) => {
+ Self.recalculatePrice = async(ctx, id) => {
const models = Self.app.models;
const sale = await Self.findById(id);
diff --git a/modules/ticket/back/methods/sale/specs/calculate.spec.js b/modules/ticket/back/methods/sale/specs/recalculatePrice.spec.js
similarity index 77%
rename from modules/ticket/back/methods/sale/specs/calculate.spec.js
rename to modules/ticket/back/methods/sale/specs/recalculatePrice.spec.js
index 48b14e972..23d7d721b 100644
--- a/modules/ticket/back/methods/sale/specs/calculate.spec.js
+++ b/modules/ticket/back/methods/sale/specs/recalculatePrice.spec.js
@@ -1,11 +1,11 @@
const app = require('vn-loopback/server/server');
-describe('sale calculate()', () => {
+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.calculate(ctx, saleId);
+ const response = await app.models.Sale.recalculatePrice(ctx, saleId);
expect(response.affectedRows).toBeDefined();
});
@@ -13,7 +13,7 @@ describe('sale calculate()', () => {
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.calculate(ctx, immutableSaleId)
+ 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;
diff --git a/modules/ticket/back/methods/ticket/recalculateComponents.js b/modules/ticket/back/methods/ticket/recalculateComponents.js
new file mode 100644
index 000000000..81fee4d70
--- /dev/null
+++ b/modules/ticket/back/methods/ticket/recalculateComponents.js
@@ -0,0 +1,31 @@
+const UserError = require('vn-loopback/util/user-error');
+module.exports = Self => {
+ Self.remoteMethodCtx('recalculateComponents', {
+ description: 'Calculates the price of a sale and its components',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'id',
+ description: 'The ticket id',
+ type: 'number',
+ required: true,
+ http: {source: 'path'}
+ }],
+ returns: {
+ type: 'Number',
+ root: true
+ },
+ http: {
+ path: `/:id/recalculateComponents`,
+ verb: 'POST'
+ }
+ });
+
+ Self.recalculateComponents = async(ctx, id) => {
+ const isEditable = await Self.isEditable(ctx, id);
+
+ if (!isEditable)
+ throw new UserError(`The current ticket can't be modified`);
+
+ return Self.rawSql('CALL vn.ticket_recalcComponents(?)', [id]);
+ };
+};
diff --git a/modules/ticket/back/methods/ticket/specs/recalculateComponents.spec.js b/modules/ticket/back/methods/ticket/specs/recalculateComponents.spec.js
new file mode 100644
index 000000000..72be4c3b4
--- /dev/null
+++ b/modules/ticket/back/methods/ticket/specs/recalculateComponents.spec.js
@@ -0,0 +1,24 @@
+const app = require('vn-loopback/server/server');
+
+describe('ticket recalculateComponents()', () => {
+ const ticketId = 11;
+
+ it('should update the ticket components', async() => {
+ const ctx = {req: {accessToken: {userId: 9}}};
+ const response = await app.models.Ticket.recalculateComponents(ctx, ticketId);
+
+ expect(response.affectedRows).toBeDefined();
+ });
+
+ it('should throw an error if the ticket is not editable', async() => {
+ const ctx = {req: {accessToken: {userId: 9}}};
+ const immutableTicketId = 1;
+ await app.models.Ticket.recalculateComponents(ctx, immutableTicketId)
+ .catch(response => {
+ expect(response).toEqual(new Error(`The current ticket can't be modified`));
+ error = response;
+ });
+
+ expect(error).toBeDefined();
+ });
+});
diff --git a/modules/ticket/back/models/sale.js b/modules/ticket/back/models/sale.js
index 1b352bcff..4ffde08b3 100644
--- a/modules/ticket/back/models/sale.js
+++ b/modules/ticket/back/models/sale.js
@@ -5,7 +5,7 @@ module.exports = Self => {
require('../methods/sale/updatePrice')(Self);
require('../methods/sale/updateQuantity')(Self);
require('../methods/sale/updateConcept')(Self);
- require('../methods/sale/calculate')(Self);
+ require('../methods/sale/recalculatePrice')(Self);
Self.validatesPresenceOf('concept', {
message: `Concept cannot be blank`
diff --git a/modules/ticket/back/models/ticket.js b/modules/ticket/back/models/ticket.js
index ed3a85da2..c42df62ef 100644
--- a/modules/ticket/back/models/ticket.js
+++ b/modules/ticket/back/models/ticket.js
@@ -26,6 +26,7 @@ module.exports = Self => {
require('../methods/ticket/addSale')(Self);
require('../methods/ticket/transferSales')(Self);
require('../methods/ticket/canHaveStowaway')(Self);
+ require('../methods/ticket/recalculateComponents')(Self);
Self.observe('before save', async function(ctx) {
if (ctx.isNewInstance) return;
diff --git a/modules/ticket/front/descriptor/index.html b/modules/ticket/front/descriptor/index.html
index 12bd5105d..decc0b6d4 100644
--- a/modules/ticket/front/descriptor/index.html
+++ b/modules/ticket/front/descriptor/index.html
@@ -198,6 +198,13 @@
+ question="Are you sure you want to send it?"
+ message="Send Delivery Note">
+
+
+
\ No newline at end of file
diff --git a/modules/ticket/front/descriptor/index.js b/modules/ticket/front/descriptor/index.js
index 9cb320a31..9dac78406 100644
--- a/modules/ticket/front/descriptor/index.js
+++ b/modules/ticket/front/descriptor/index.js
@@ -35,6 +35,11 @@ class Controller extends Component {
callback: this.showRegenerateInvoiceDialog,
show: () => this.hasInvoice()
},
+ {
+ name: 'Recalculate components',
+ callback: this.comfirmRecalculateComponents,
+ show: () => this.isEditable
+ },
];
}
@@ -287,9 +292,26 @@ class Controller extends Component {
return this.ticket.refFk !== null;
}
+ /**
+ * Shows a delivery-note send confirmation
+ */
confirmDeliveryNote() {
this.$.confirmDeliveryNote.show();
}
+
+ /**
+ * Shows an invoice confirmation
+ */
+ comfirmRecalculateComponents() {
+ this.$.recalculateComponentsConfirmation.show();
+ }
+
+ recalculateComponents() {
+ const query = `Tickets/${this.ticket.id}/recalculateComponents`;
+ this.$http.post(query).then(res => {
+ this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
+ });
+ }
}
Controller.$inject = ['$element', '$scope', 'aclService', '$httpParamSerializer'];
diff --git a/modules/ticket/front/descriptor/index.spec.js b/modules/ticket/front/descriptor/index.spec.js
index 62aefa6cf..df0e95c02 100644
--- a/modules/ticket/front/descriptor/index.spec.js
+++ b/modules/ticket/front/descriptor/index.spec.js
@@ -1,6 +1,6 @@
import './index.js';
-describe('Ticket Component vnTicketDescriptor', () => {
+fdescribe('Ticket Component vnTicketDescriptor', () => {
let $httpParamSerializer;
let $httpBackend;
let controller;
@@ -210,4 +210,17 @@ describe('Ticket Component vnTicketDescriptor', () => {
expect(controller.isTicketModule).toHaveBeenCalledWith();
});
});
+
+ describe('recalculateComponents()', () => {
+ it('should make a query and show a success snackbar', () => {
+ spyOn(controller.vnApp, 'showSuccess');
+
+ $httpBackend.when('POST', 'Tickets/2/recalculateComponents').respond();
+ $httpBackend.expect('POST', 'Tickets/2/recalculateComponents').respond();
+ controller.recalculateComponents();
+ $httpBackend.flush();
+
+ expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
+ });
+ });
});
diff --git a/modules/ticket/front/descriptor/locale/es.yml b/modules/ticket/front/descriptor/locale/es.yml
index 1703c5ffb..2f72e226e 100644
--- a/modules/ticket/front/descriptor/locale/es.yml
+++ b/modules/ticket/front/descriptor/locale/es.yml
@@ -25,4 +25,6 @@ You are going to regenerate the invoice: Vas a regenerar la factura
Are you sure you want to regenerate the invoice?: ¿Seguro que quieres regenerar la factura?
Invoice sent for a regeneration, will be available in a few minutes: La factura ha sido enviada para ser regenerada, estará disponible en unos minutos
Shipped hour updated: Hora de envio modificada
-Deleted ticket: Ticket eliminado
\ No newline at end of file
+Deleted ticket: Ticket eliminado
+Recalculate components: Recalcular componentes
+Are you sure you want to recalculate the components?: ¿Seguro que quieres recalcular los componentes?
\ No newline at end of file
diff --git a/modules/ticket/front/sale/index.js b/modules/ticket/front/sale/index.js
index fb940a8ef..7113732e1 100644
--- a/modules/ticket/front/sale/index.js
+++ b/modules/ticket/front/sale/index.js
@@ -548,7 +548,7 @@ class Controller {
calculateSalePrice() {
const sale = this.checkedLines()[0];
- const query = `Sales/${sale.id}/calculate`;
+ const query = `Sales/${sale.id}/recalculatePrice`;
this.$http.post(query).then(res => {
this.vnApp.showSuccess(this.$translate.instant('Data saved!'));
this.$scope.model.refresh();
diff --git a/modules/ticket/front/sale/specs/index.spec.js b/modules/ticket/front/sale/specs/index.spec.js
index 0606d81a4..a23c6f204 100644
--- a/modules/ticket/front/sale/specs/index.spec.js
+++ b/modules/ticket/front/sale/specs/index.spec.js
@@ -349,7 +349,7 @@ describe('Ticket', () => {
it('should make an HTTP post query ', () => {
controller.sales[0].checked = true;
- $httpBackend.when('POST', `Sales/4/calculate`).respond(200);
+ $httpBackend.when('POST', `Sales/4/recalculatePrice`).respond(200);
$httpBackend.whenGET(`Tickets/1/subtotal`).respond(200, 227.5);
$httpBackend.whenGET(`Tickets/1/getVAT`).respond(200, 10.5);
$httpBackend.whenGET(`Tickets/1/isEditable`).respond();