diff --git a/db/changes/10121-zoneClosure/00-zoneClosure.sql b/db/changes/10121-zoneClosure/00-zoneClosure.sql
new file mode 100644
index 0000000000..07db5b1678
--- /dev/null
+++ b/db/changes/10121-zoneClosure/00-zoneClosure.sql
@@ -0,0 +1,5 @@
+CREATE TABLE `vn`.`zoneClosure` (
+ `zoneFk` INT NOT NULL,
+ `dated` DATE NOT NULL,
+ `hour` TIME NOT NULL,
+ PRIMARY KEY (`zoneFk`, `dated`));
diff --git a/db/changes/10121-zoneClosure/00-zoneClosure_recalc.sql b/db/changes/10121-zoneClosure/00-zoneClosure_recalc.sql
new file mode 100644
index 0000000000..cb313cdec7
--- /dev/null
+++ b/db/changes/10121-zoneClosure/00-zoneClosure_recalc.sql
@@ -0,0 +1,50 @@
+
+DROP procedure IF EXISTS vn.`zoneClosure_recalc`;
+
+DELIMITER $$
+CREATE DEFINER=`root`@`%` PROCEDURE vn.`zoneClosure_recalc`()
+proc: BEGIN
+/**
+ * Recalculates the delivery time (hour) for every zone in days + scope in future
+ */
+ DECLARE vScope INT;
+ DECLARE vCounter INT DEFAULT 0;
+ DECLARE vShipped DATE DEFAULT CURDATE();
+
+ DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
+ BEGIN
+ DO RELEASE_LOCK('vn.zoneClosure_recalc');
+ RESIGNAL;
+ END;
+
+ IF NOT GET_LOCK('vn.zoneClosure_recalc', 0) THEN
+ LEAVE proc;
+ END IF;
+
+ SELECT scope INTO vScope
+ FROM zoneConfig;
+
+ DROP TEMPORARY TABLE IF EXISTS tmp.zone;
+ CREATE TEMPORARY TABLE tmp.zone
+ (INDEX (id))
+ ENGINE = MEMORY
+ SELECT id FROM zone;
+
+ TRUNCATE TABLE zoneClosure;
+
+ REPEAT
+ CALL zone_getOptionsForShipment(vShipped);
+ INSERT INTO zoneClosure(zoneFk, dated, `hour`)
+ SELECT zoneFk, vShipped, `hour` FROM tmp.zoneOption;
+
+ SET vCounter = vCounter + 1;
+ SET vShipped = TIMESTAMPADD(DAY, 1, vShipped);
+ UNTIL vCounter > vScope
+ END REPEAT;
+
+ DROP TEMPORARY TABLE tmp.zone;
+ DO RELEASE_LOCK('vn.zoneClosure_recalc');
+END$$
+
+DELIMITER ;
+
diff --git a/db/changes/10121-zoneClosure/00-zoneConfig.sql b/db/changes/10121-zoneClosure/00-zoneConfig.sql
new file mode 100644
index 0000000000..915fb8894c
--- /dev/null
+++ b/db/changes/10121-zoneClosure/00-zoneConfig.sql
@@ -0,0 +1,11 @@
+CREATE TABLE `vn`.`zoneConfig` (
+ `id` INT UNSIGNED NOT NULL,
+ `scope` INT UNSIGNED NOT NULL,
+ PRIMARY KEY (`id`));
+
+ALTER TABLE `vn`.`zoneConfig`
+CHANGE COLUMN `id` `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT ;
+
+INSERT INTO `vn`.`zoneConfig` (`scope`) VALUES ('1');
+
+INSERT INTO `bs`.`nightTask` (`order`, `schema`, `procedure`) VALUES ('100', 'vn', 'zoneClosure_recalc');
diff --git a/db/changes/10140-kings/01-order_confirmWithUser.sql b/db/changes/10140-kings/01-order_confirmWithUser.sql
new file mode 100644
index 0000000000..c398edc4c0
--- /dev/null
+++ b/db/changes/10140-kings/01-order_confirmWithUser.sql
@@ -0,0 +1,240 @@
+
+DROP procedure IF EXISTS `hedera`.`order_confirmWithUser`;
+
+DELIMITER $$
+CREATE DEFINER=`root`@`%` PROCEDURE `hedera`.`order_confirmWithUser`(IN `vOrder` INT, IN `vUserId` INT)
+BEGIN
+/**
+ * Confirms an order, creating each of its tickets on the corresponding
+ * date, store and user.
+ *
+ * @param vOrder The order identifier
+ * @param vUser The user identifier
+ */
+ DECLARE vOk BOOL;
+ DECLARE vDone BOOL DEFAULT FALSE;
+ DECLARE vWarehouse INT;
+ DECLARE vShipment DATETIME;
+ DECLARE vTicket INT;
+ DECLARE vNotes VARCHAR(255);
+ DECLARE vItem INT;
+ DECLARE vConcept VARCHAR(30);
+ DECLARE vAmount INT;
+ DECLARE vPrice DECIMAL(10,2);
+ DECLARE vSale INT;
+ DECLARE vRate INT;
+ DECLARE vRowId INT;
+ DECLARE vDelivery DATE;
+ DECLARE vAddress INT;
+ DECLARE vIsConfirmed BOOL;
+ DECLARE vClientId INT;
+ DECLARE vCompanyId INT;
+ DECLARE vAgencyModeId INT;
+
+ DECLARE TICKET_FREE INT DEFAULT 2;
+
+ DECLARE cDates CURSOR FOR
+ SELECT zgs.shipped, r.warehouse_id
+ FROM `order` o
+ JOIN order_row r ON r.order_id = o.id
+ LEFT JOIN tmp.zoneGetShipped zgs ON zgs.warehouseFk = r.warehouse_id
+ WHERE o.id = vOrder AND r.amount != 0
+ GROUP BY r.warehouse_id;
+
+ DECLARE cRows CURSOR FOR
+ SELECT r.id, r.item_id, i.name, r.amount, r.price, r.rate
+ FROM order_row r
+ JOIN vn.item i ON i.id = r.item_id
+ WHERE r.amount != 0
+ AND r.warehouse_id = vWarehouse
+ AND r.order_id = vOrder
+ ORDER BY r.rate DESC;
+
+ DECLARE CONTINUE HANDLER FOR NOT FOUND
+ SET vDone = TRUE;
+
+ DECLARE EXIT HANDLER FOR SQLEXCEPTION
+ BEGIN
+ ROLLBACK;
+ RESIGNAL;
+ END;
+
+ -- Carga los datos del pedido
+
+ SELECT o.date_send, o.address_id, o.note,
+ o.confirmed, a.clientFk, o.company_id, o.agency_id
+ INTO vDelivery, vAddress, vNotes,
+ vIsConfirmed, vClientId, vCompanyId, vAgencyModeId
+ FROM hedera.`order` o
+ JOIN vn.address a ON a.id = o.address_id
+ WHERE o.id = vOrder;
+
+ -- Comprueba que el pedido no está confirmado
+
+ IF vIsConfirmed THEN
+ CALL util.throw ('ORDER_ALREADY_CONFIRMED');
+ END IF;
+
+ -- Comprueba que el pedido no está vacío
+
+ SELECT COUNT(*) > 0 INTO vOk
+ FROM order_row WHERE order_id = vOrder AND amount > 0;
+
+ IF !vOk THEN
+ CALL util.throw ('ORDER_EMPTY');
+ END IF;
+
+ -- Carga las fechas de salida de cada almacén
+
+ CALL vn.zone_getShippedWarehouse (vDelivery, vAddress, vAgencyModeId);
+
+ -- Trabajador que realiza la acción
+
+ IF vUserId IS NULL THEN
+ SELECT employeeFk INTO vUserId FROM orderConfig;
+ END IF;
+
+ -- Crea los tickets del pedido
+
+ START TRANSACTION;
+
+ OPEN cDates;
+
+ lDates:
+ LOOP
+ SET vTicket = NULL;
+ SET vDone = FALSE;
+ FETCH cDates INTO vShipment, vWarehouse;
+
+ IF vDone THEN
+ LEAVE lDates;
+ END IF;
+
+ -- Busca un ticket existente que coincida con los parametros
+
+ SELECT t.id INTO vTicket
+ FROM vn.ticket t
+ LEFT JOIN vn.ticketState tls on tls.ticket = t.id
+ JOIN `order` o
+ ON o.address_id = t.addressFk
+ AND vWarehouse = t.warehouseFk
+ AND o.agency_id = t.agencyModeFk
+ AND o.date_send = t.landed
+ AND vShipment = DATE(t.shipped)
+ WHERE o.id = vOrder
+ AND t.invoiceOutFk IS NULL
+ AND IFNULL(tls.alertLevel,0) = 0
+ AND t.clientFk <> 1118
+ LIMIT 1;
+
+ -- Crea el ticket en el caso de no existir uno adecuado
+
+ IF vTicket IS NULL
+ THEN
+ CALL vn.ticketCreateWithUser(
+ vClientId,
+ IFNULL(vShipment, CURDATE()),
+ vWarehouse,
+ vCompanyId,
+ vAddress,
+ vAgencyModeId,
+ NULL,
+ vDelivery,
+ vUserId,
+ vTicket
+ );
+ ELSE
+ INSERT INTO vncontrol.inter
+ SET Id_Ticket = vTicket,
+ Id_Trabajador = vUserId,
+ state_id = TICKET_FREE;
+ END IF;
+
+ INSERT IGNORE INTO vn.orderTicket
+ SET orderFk = vOrder,
+ ticketFk = vTicket;
+
+ -- Añade las notas
+
+ IF vNotes IS NOT NULL AND vNotes != ''
+ THEN
+ INSERT INTO vn.ticketObservation SET
+ ticketFk = vTicket,
+ observationTypeFk = 4 /* salesperson */ ,
+ `description` = vNotes
+ ON DUPLICATE KEY UPDATE
+ `description` = CONCAT(VALUES(`description`),'. ', `description`);
+ END IF;
+
+ -- Añade los movimientos y sus componentes
+
+ OPEN cRows;
+
+ lRows:
+ LOOP
+ SET vDone = FALSE;
+ FETCH cRows INTO vRowId, vItem, vConcept, vAmount, vPrice, vRate;
+
+ IF vDone THEN
+ LEAVE lRows;
+ END IF;
+
+ INSERT INTO vn.sale
+ SET
+ itemFk = vItem,
+ ticketFk = vTicket,
+ concept = vConcept,
+ quantity = vAmount,
+ price = vPrice,
+ priceFixed = 0,
+ isPriceFixed = TRUE;
+
+ SET vSale = LAST_INSERT_ID();
+
+ INSERT INTO vn.saleComponent
+ (saleFk, componentFk, `value`)
+ SELECT vSale, cm.component_id, cm.price
+ FROM order_component cm
+ JOIN vn.component c ON c.id = cm.component_id
+ WHERE cm.order_row_id = vRowId
+ GROUP BY vSale, cm.component_id;
+
+ UPDATE order_row SET Id_Movimiento = vSale
+ WHERE id = vRowId;
+
+ END LOOP;
+
+ CLOSE cRows;
+
+ -- Fija el coste
+
+ DROP TEMPORARY TABLE IF EXISTS tComponents;
+ CREATE TEMPORARY TABLE tComponents
+ (INDEX (saleFk))
+ ENGINE = MEMORY
+ SELECT SUM(sc.`value`) valueSum, sc.saleFk
+ FROM vn.saleComponent sc
+ JOIN vn.component c ON c.id = sc.componentFk
+ JOIN vn.componentType ct ON ct.id = c.typeFk AND ct.isBase
+ JOIN vn.sale s ON s.id = sc.saleFk
+ WHERE s.ticketFk = vTicket
+ GROUP BY sc.saleFk;
+
+ UPDATE vn.sale s
+ JOIN tComponents mc ON mc.saleFk = s.id
+ SET s.priceFixed = valueSum;
+
+ DROP TEMPORARY TABLE tComponents;
+ END LOOP;
+
+ CLOSE cDates;
+
+ DELETE FROM basketOrder WHERE orderFk = vOrder;
+ UPDATE `order` SET confirmed = TRUE, confirm_date = NOW()
+ WHERE id = vOrder;
+
+ COMMIT;
+END$$
+
+DELIMITER ;
+
diff --git a/e2e/helpers/selectors.js b/e2e/helpers/selectors.js
index 32372b853e..38b5110d54 100644
--- a/e2e/helpers/selectors.js
+++ b/e2e/helpers/selectors.js
@@ -487,7 +487,7 @@ export default {
priceInput: 'vn-ticket-request-create [ng-model="$ctrl.ticketRequest.price"]',
firstRemoveRequestButton: 'vn-ticket-request-index vn-icon[icon="delete"]:nth-child(1)',
saveButton: 'vn-ticket-request-create button[type=submit]',
- firstDescription: 'vn-ticket-request-index vn-table vn-tr:nth-child(1) > vn-td:nth-child(2)',
+ firstDescription: 'vn-ticket-request-index vn-table vn-tr:nth-child(1) > vn-td:nth-child(2) vn-textfield',
},
ticketLog: {
diff --git a/e2e/paths/05-ticket-module/10_request.spec.js b/e2e/paths/05-ticket-module/10_request.spec.js
index d1ed7a9779..f4fea9c2be 100644
--- a/e2e/paths/05-ticket-module/10_request.spec.js
+++ b/e2e/paths/05-ticket-module/10_request.spec.js
@@ -38,7 +38,7 @@ describe('Ticket purchase request path', () => {
it(`should confirm the new request was added`, async() => {
await page.reloadSection('ticket.card.request.index');
- const result = await page.waitToGetProperty(selectors.ticketRequests.firstDescription, 'innerText');
+ const result = await page.waitToGetProperty(`${selectors.ticketRequests.firstDescription} input`, 'value');
expect(result).toEqual('New stuff');
});
diff --git a/e2e/paths/07-order-module/03_lines.spec.js b/e2e/paths/07-order-module/03_lines.spec.js
index 50570b4e81..99c8a221b8 100644
--- a/e2e/paths/07-order-module/03_lines.spec.js
+++ b/e2e/paths/07-order-module/03_lines.spec.js
@@ -1,8 +1,7 @@
import selectors from '../../helpers/selectors.js';
import getBrowser from '../../helpers/puppeteer';
-// #2050 Order.lines confirm error
-xdescribe('Order lines', () => {
+describe('Order lines', () => {
let browser;
let page;
beforeAll(async() => {
diff --git a/modules/agency/back/model-config.json b/modules/agency/back/model-config.json
index cfdbb83d31..7638e3f6c5 100644
--- a/modules/agency/back/model-config.json
+++ b/modules/agency/back/model-config.json
@@ -11,7 +11,7 @@
"Zone": {
"dataSource": "vn"
},
- "ZoneGeo": {
+ "ZoneClosure": {
"dataSource": "vn"
},
"ZoneEvent": {
@@ -20,6 +20,9 @@
"ZoneExclusion": {
"dataSource": "vn"
},
+ "ZoneGeo": {
+ "dataSource": "vn"
+ },
"ZoneIncluded": {
"dataSource": "vn"
},
diff --git a/modules/agency/back/models/zone-closure.js b/modules/agency/back/models/zone-closure.js
new file mode 100644
index 0000000000..8b66e31ae7
--- /dev/null
+++ b/modules/agency/back/models/zone-closure.js
@@ -0,0 +1,13 @@
+module.exports = Self => {
+ Self.doRecalc = async function() {
+ try {
+ await Self.rawSql(`
+ CREATE EVENT zoneClosure_doRecalc
+ ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 15 SECOND
+ DO CALL zoneClosure_recalc;
+ `);
+ } catch (err) {
+ if (err.code != 'ER_EVENT_ALREADY_EXISTS') throw err;
+ }
+ };
+};
diff --git a/modules/agency/back/models/zone-closure.json b/modules/agency/back/models/zone-closure.json
new file mode 100644
index 0000000000..8953748389
--- /dev/null
+++ b/modules/agency/back/models/zone-closure.json
@@ -0,0 +1,30 @@
+{
+ "name": "ZoneClosure",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "zoneClosure"
+ }
+ },
+ "properties": {
+ "zoneFk": {
+ "id": true,
+ "type": "Number"
+ },
+ "dated": {
+ "type": "Date",
+ "required": true
+ },
+ "hour": {
+ "type": "date",
+ "required": true
+ }
+ },
+ "relations": {
+ "zone": {
+ "type": "belongsTo",
+ "model": "Zone",
+ "foreignKey": "zoneFk"
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/agency/back/models/zone-event.js b/modules/agency/back/models/zone-event.js
index 6af031a239..46b2df2029 100644
--- a/modules/agency/back/models/zone-event.js
+++ b/modules/agency/back/models/zone-event.js
@@ -1,3 +1,5 @@
+const app = require('vn-loopback/server/server');
+
module.exports = Self => {
Self.validate('range', function(err) {
if (this.type == 'range'
@@ -32,4 +34,12 @@ module.exports = Self => {
}, {
message: `You should mark at least one week day`
});
+
+ Self.observe('after save', async function() {
+ await app.models.ZoneClosure.doRecalc();
+ });
+
+ Self.observe('after delete', async function() {
+ await app.models.ZoneClosure.doRecalc();
+ });
};
diff --git a/modules/agency/back/models/zone-exclusion.js b/modules/agency/back/models/zone-exclusion.js
new file mode 100644
index 0000000000..51998aab87
--- /dev/null
+++ b/modules/agency/back/models/zone-exclusion.js
@@ -0,0 +1,11 @@
+const app = require('vn-loopback/server/server');
+
+module.exports = Self => {
+ Self.observe('after save', async function() {
+ await app.models.ZoneClosure.doRecalc();
+ });
+
+ Self.observe('after delete', async function() {
+ await app.models.ZoneClosure.doRecalc();
+ });
+};
diff --git a/modules/agency/back/models/zone.js b/modules/agency/back/models/zone.js
index 0c3ac24f66..9d715a8d88 100644
--- a/modules/agency/back/models/zone.js
+++ b/modules/agency/back/models/zone.js
@@ -1,3 +1,5 @@
+const app = require('vn-loopback/server/server');
+
module.exports = Self => {
require('../methods/zone/clone')(Self);
require('../methods/zone/getLeaves')(Self);
@@ -7,4 +9,12 @@ module.exports = Self => {
Self.validatesPresenceOf('agencyModeFk', {
message: `Agency cannot be blank`
});
+
+ Self.observe('after save', async function() {
+ await app.models.ZoneClosure.doRecalc();
+ });
+
+ Self.observe('after delete', async function() {
+ await app.models.ZoneClosure.doRecalc();
+ });
};
diff --git a/modules/agency/front/warehouses/index.html b/modules/agency/front/warehouses/index.html
index 1a9fee32e4..72d88fa0a1 100644
--- a/modules/agency/front/warehouses/index.html
+++ b/modules/agency/front/warehouses/index.html
@@ -11,7 +11,7 @@
+ ng-click="$ctrl.onDelete(row)">
@@ -49,5 +49,5 @@
vn-id="confirm"
message="This item will be deleted"
question="Are you sure you want to continue?"
- on-response="$ctrl.delete($response)">
+ on-accept="$ctrl.delete()">
diff --git a/modules/agency/front/warehouses/index.js b/modules/agency/front/warehouses/index.js
index fa99c505a1..328f3a1b49 100644
--- a/modules/agency/front/warehouses/index.js
+++ b/modules/agency/front/warehouses/index.js
@@ -34,19 +34,19 @@ class Controller extends Component {
return false;
}
- onDelete(index) {
+ onDelete(row) {
this.$.confirm.show();
- this.deleteIndex = index;
+ this.deleteRow = row;
}
- delete(response) {
- if (response != 'accept') return;
- let id = this.$.data[this.deleteIndex].id;
- if (!id) return;
- this.$http.delete(`${this.path}/${id}`)
+ delete() {
+ let row = this.deleteRow;
+ if (!row) return;
+ return this.$http.delete(`${this.path}/${row.id}`)
.then(() => {
- this.$.data.splice(this.deleteIndex, 1);
- this.deleteIndex = null;
+ let index = this.$.data.indexOf(row);
+ if (index !== -1) this.$.data.splice(index, 1);
+ this.deleteRow = null;
});
}
}
diff --git a/modules/client/front/summary/index.html b/modules/client/front/summary/index.html
index 6678eaa65f..eccf458734 100644
--- a/modules/client/front/summary/index.html
+++ b/modules/client/front/summary/index.html
@@ -17,6 +17,7 @@
+
diff --git a/modules/ticket/front/package/index.html b/modules/ticket/front/package/index.html
index 87c424ff11..12ac138621 100644
--- a/modules/ticket/front/package/index.html
+++ b/modules/ticket/front/package/index.html
@@ -3,7 +3,7 @@
url="TicketPackagings"
fields="['id', 'ticketFk', 'packagingFk', 'quantity', 'created']"
link="{ticketFk: $ctrl.$stateParams.id}"
- data="packages" on-data-change="$ctrl.onDataChange()"
+ data="packages"
auto-load="true">
{{::request.id}}
- {{::request.description}}
- {{::request.created | date: 'dd/MM/yyyy'}}
+
+
+
+ {{::request.created | date: 'dd/MM/yyyy'}}
+
{{::request.requester.user.nickname | dashIfEmpty}}
-
+
{{::request.atender.user.nickname | dashIfEmpty}}
- {{::request.quantity}}
- {{::request.price | currency: 'EUR': 2}}
+
+
+
+
+
+
+
+
- {{request.saleFk | zeroFill:6}}
+ {{::request.saleFk | zeroFill:6}}
diff --git a/modules/ticket/front/request/index/index.js b/modules/ticket/front/request/index/index.js
index 2c89967604..38d800aebe 100644
--- a/modules/ticket/front/request/index/index.js
+++ b/modules/ticket/front/request/index/index.js
@@ -78,6 +78,18 @@ class Controller {
return 'Acepted';
}
}
+
+ updateData() {
+ this.$.model.save().then(() => {
+ this.$.watcher.notifySaved();
+ this.$.watcher.updateOriginalData();
+ });
+ }
+
+ isEditable(isOk) {
+ if (isOk != null)
+ return true;
+ }
}
Controller.$inject = ['$scope', '$stateParams'];
diff --git a/modules/ticket/front/sale/index.html b/modules/ticket/front/sale/index.html
index bd9b390b57..998f481ead 100644
--- a/modules/ticket/front/sale/index.html
+++ b/modules/ticket/front/sale/index.html
@@ -168,7 +168,7 @@
- {{sale.discount | percentage}}
+ {{(sale.discount / 100) | percentage}}