From 8960b546649da0a6ff99df0836d76784bdd25509 Mon Sep 17 00:00:00 2001 From: guillermo Date: Sun, 8 Dec 2024 14:30:05 +0100 Subject: [PATCH 01/12] feat: refs #7882 Added locationiq service --- back/methods/locationiq-config/optimize.js | 77 +++++++++++++++++++ back/model-config.json | 3 + back/models/locationiq-config.js | 4 + back/models/locationiq-config.json | 28 +++++++ .../11379-yellowCordyline/00-firstScript.sql | 8 ++ loopback/locale/es.json | 11 +-- 6 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 back/methods/locationiq-config/optimize.js create mode 100644 back/models/locationiq-config.js create mode 100644 back/models/locationiq-config.json create mode 100644 db/versions/11379-yellowCordyline/00-firstScript.sql diff --git a/back/methods/locationiq-config/optimize.js b/back/methods/locationiq-config/optimize.js new file mode 100644 index 000000000..cd1f6e7ff --- /dev/null +++ b/back/methods/locationiq-config/optimize.js @@ -0,0 +1,77 @@ +const UserError = require('vn-loopback/util/user-error'); +const axios = require('axios'); + +module.exports = Self => { + Self.remoteMethod('optimize', { + description: 'Return optimized coords', + accessType: 'READ', + accepts: [{ + arg: 'addressIds', + type: 'array', + required: true + }], + returns: { + type: 'string', + root: true + }, + http: { + path: `/optimize`, + verb: 'GET' + } + }); + + Self.optimize = async addressIds => { + const models = Self.app.models; + try { + const locationiqConfig = await models.LocationiqConfig.findOne(); + if (!locationiqConfig) throw new UserError(`LocationIQ service is not configured`); + + let coords = []; + for (const addressId of addressIds) { + const address = await models.Address.findById(addressId); + coords.push({ + addressId, + latitude: address.latitude.toFixed(6), + longitude: address.longitude.toFixed(6) + }); + } + const concatCoords = coords + .map(coord => `${coord.longitude},${coord.latitude}`) + .join(';'); + const response = await axios.post(`${locationiqConfig.url}${concatCoords}?key=${locationiqConfig.key}`); + const tolerance = locationiqConfig.tolerance; + + for (waypoint of response.data.waypoints) { + const longitude = waypoint.location[0]; + const latitude = waypoint.location[1]; + + const matchedAddress = coords.find(coord => + Math.abs(coord.latitude - latitude) <= tolerance && + Math.abs(coord.longitude - longitude) <= tolerance + ); + if (matchedAddress) matchedAddress.position = waypoint.waypoint_index; + } + coords.sort((a, b) => { + const posA = a.position !== undefined ? a.position : Infinity; + const posB = b.position !== undefined ? b.position : Infinity; + return posA - posB; + }); + // Temporal para abrir en maps + const coordsString = coords + .map(item => `${item.latitude},${item.longitude}`) + .join('/'); + console.log(`https://www.google.es/maps/dir/${coordsString}`); + // --------- + return coords; + } catch (err) { + switch (err.response?.data?.code) { + case 'NoTrips': + throw new UserError('No trips found because input coordinates are not connected'); + case 'NotImplemented': + throw new UserError('This request is not supported'); + default: + throw err; + } + } + }; +}; diff --git a/back/model-config.json b/back/model-config.json index c1682f29a..f48e5f2eb 100644 --- a/back/model-config.json +++ b/back/model-config.json @@ -88,6 +88,9 @@ "Language": { "dataSource": "vn" }, + "LocationiqConfig": { + "dataSource": "vn" + }, "Machine": { "dataSource": "vn" }, diff --git a/back/models/locationiq-config.js b/back/models/locationiq-config.js new file mode 100644 index 000000000..e0fe736bb --- /dev/null +++ b/back/models/locationiq-config.js @@ -0,0 +1,4 @@ +module.exports = Self => { + require('../methods/locationiq-config/optimize')(Self); +}; + diff --git a/back/models/locationiq-config.json b/back/models/locationiq-config.json new file mode 100644 index 000000000..624341615 --- /dev/null +++ b/back/models/locationiq-config.json @@ -0,0 +1,28 @@ +{ + "name": "LocationiqConfig", + "base": "VnModel", + "options": { + "mysql": { + "table": "locationiqConfig" + } + }, + "properties": { + "id": { + "type": "number", + "id": true, + "required": true + }, + "url": { + "type": "string", + "required": true + }, + "key": { + "type": "string", + "required": true + }, + "tolerance": { + "type": "number", + "required": false + } + } +} diff --git a/db/versions/11379-yellowCordyline/00-firstScript.sql b/db/versions/11379-yellowCordyline/00-firstScript.sql new file mode 100644 index 000000000..001ef6049 --- /dev/null +++ b/db/versions/11379-yellowCordyline/00-firstScript.sql @@ -0,0 +1,8 @@ +CREATE TABLE `vn`.`locationiqConfig` ( + `id` int(10) unsigned NOT NULL, + `url` varchar(100) NOT NULL, + `key` varchar(100) NOT NULL, + `tolerance` decimal(6,6) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + CONSTRAINT `locationiqConfig_check` CHECK (`id` = 1) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 1eb953d89..fb495dfb1 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -388,8 +388,9 @@ "You do not have permission to modify the booked field": "No tienes permisos para modificar el campo contabilizada", "ticketLostExpedition": "El ticket [{{ticketId}}]({{{ticketUrl}}}) tiene la siguiente expedición perdida:{{ expeditionId }}", "The web user's email already exists": "El correo del usuario web ya existe", - "Sales already moved": "Ya han sido transferidas", - "The raid information is not correct": "La información de la redada no es correcta", - "There are tickets to be invoiced": "Hay tickets para esta zona, borralos primero" -} - + "Sales already moved": "Ya han sido transferidas", + "The raid information is not correct": "La información de la redada no es correcta", + "There are tickets to be invoiced": "Hay tickets para esta zona, borralos primero", + "No trips found because input coordinates are not connected": "No se encontraron rutas porque las coordenadas de entrada no están conectadas", + "This request is not supported": "Esta solicitud no es compatible" +} \ No newline at end of file From 0bb9282953cacb199b6451e778fb22bda28baeae Mon Sep 17 00:00:00 2001 From: guillermo Date: Mon, 9 Dec 2024 09:11:55 +0100 Subject: [PATCH 02/12] feat: refs #7882 Added locationiq service --- back/methods/locationiq-config/optimize.js | 18 +++++++++++++----- .../11379-yellowCordyline/00-firstScript.sql | 7 ++++--- loopback/locale/es.json | 3 ++- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/back/methods/locationiq-config/optimize.js b/back/methods/locationiq-config/optimize.js index cd1f6e7ff..97ea33089 100644 --- a/back/methods/locationiq-config/optimize.js +++ b/back/methods/locationiq-config/optimize.js @@ -38,7 +38,9 @@ module.exports = Self => { const concatCoords = coords .map(coord => `${coord.longitude},${coord.latitude}`) .join(';'); - const response = await axios.post(`${locationiqConfig.url}${concatCoords}?key=${locationiqConfig.key}`); + const response = await axios.post(` + ${locationiqConfig.url}optimize/driving/${concatCoords}?key=${locationiqConfig.key} + `); const tolerance = locationiqConfig.tolerance; for (waypoint of response.data.waypoints) { @@ -46,10 +48,14 @@ module.exports = Self => { const latitude = waypoint.location[1]; const matchedAddress = coords.find(coord => + !coord.position && Math.abs(coord.latitude - latitude) <= tolerance && Math.abs(coord.longitude - longitude) <= tolerance ); - if (matchedAddress) matchedAddress.position = waypoint.waypoint_index; + if (matchedAddress) + matchedAddress.position = waypoint.waypoint_index; + else + console.log(`Las coordenadas no se han podido asociar: ${latitude} | ${longitude}`); } coords.sort((a, b) => { const posA = a.position !== undefined ? a.position : Infinity; @@ -58,9 +64,9 @@ module.exports = Self => { }); // Temporal para abrir en maps const coordsString = coords - .map(item => `${item.latitude},${item.longitude}`) - .join('/'); - console.log(`https://www.google.es/maps/dir/${coordsString}`); + .map(item => `point=${item.latitude},${item.longitude}`) + .join('&'); + console.log(`https://graphhopper.com/maps/?${coordsString}&profile=small_truck`); // --------- return coords; } catch (err) { @@ -69,6 +75,8 @@ module.exports = Self => { throw new UserError('No trips found because input coordinates are not connected'); case 'NotImplemented': throw new UserError('This request is not supported'); + case 'InvalidOptions': + throw new UserError('Invalid options or too many coordinates'); default: throw err; } diff --git a/db/versions/11379-yellowCordyline/00-firstScript.sql b/db/versions/11379-yellowCordyline/00-firstScript.sql index 001ef6049..363c32dfa 100644 --- a/db/versions/11379-yellowCordyline/00-firstScript.sql +++ b/db/versions/11379-yellowCordyline/00-firstScript.sql @@ -1,8 +1,9 @@ CREATE TABLE `vn`.`locationiqConfig` ( `id` int(10) unsigned NOT NULL, - `url` varchar(100) NOT NULL, - `key` varchar(100) NOT NULL, - `tolerance` decimal(6,6) NOT NULL DEFAULT 0, + `url` varchar(100) NOT NULL COMMENT 'Dirección base de la API', + `key` varchar(100) NOT NULL COMMENT 'Access token', + `tolerance` decimal(6,6) NOT NULL DEFAULT 0 COMMENT 'Tolerancia entre las coordenadas enviadas y las retornadas', + `maxCoordsRequest` int(10) unsigned DEFAULT NULL COMMENT 'Número máximo de coordenadas por petición', PRIMARY KEY (`id`), CONSTRAINT `locationiqConfig_check` CHECK (`id` = 1) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; \ No newline at end of file diff --git a/loopback/locale/es.json b/loopback/locale/es.json index fb495dfb1..8013a7fe6 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -392,5 +392,6 @@ "The raid information is not correct": "La información de la redada no es correcta", "There are tickets to be invoiced": "Hay tickets para esta zona, borralos primero", "No trips found because input coordinates are not connected": "No se encontraron rutas porque las coordenadas de entrada no están conectadas", - "This request is not supported": "Esta solicitud no es compatible" + "This request is not supported": "Esta solicitud no es compatible", + "Invalid options or too many coordinates": "Opciones invalidas o demasiadas coordenadas" } \ No newline at end of file From e279cc4b471c8e2d2efe85d32e25897d615a860d Mon Sep 17 00:00:00 2001 From: guillermo Date: Mon, 9 Dec 2024 10:01:59 +0100 Subject: [PATCH 03/12] feat: refs #7882 Added osrm service --- .../{locationiq-config => osrm-config}/optimize.js | 9 ++++----- back/model-config.json | 2 +- back/models/locationiq-config.js | 4 ---- back/models/osrm-config.js | 4 ++++ back/models/{locationiq-config.json => osrm-config.json} | 8 ++------ db/versions/11379-yellowCordyline/00-firstScript.sql | 6 ++---- 6 files changed, 13 insertions(+), 20 deletions(-) rename back/methods/{locationiq-config => osrm-config}/optimize.js (89%) delete mode 100644 back/models/locationiq-config.js create mode 100644 back/models/osrm-config.js rename back/models/{locationiq-config.json => osrm-config.json} (69%) diff --git a/back/methods/locationiq-config/optimize.js b/back/methods/osrm-config/optimize.js similarity index 89% rename from back/methods/locationiq-config/optimize.js rename to back/methods/osrm-config/optimize.js index 97ea33089..9440800f6 100644 --- a/back/methods/locationiq-config/optimize.js +++ b/back/methods/osrm-config/optimize.js @@ -23,8 +23,8 @@ module.exports = Self => { Self.optimize = async addressIds => { const models = Self.app.models; try { - const locationiqConfig = await models.LocationiqConfig.findOne(); - if (!locationiqConfig) throw new UserError(`LocationIQ service is not configured`); + const osrmConfig = await models.OsrmConfig.findOne(); + if (!osrmConfig) throw new UserError(`OSRM service is not configured`); let coords = []; for (const addressId of addressIds) { @@ -39,10 +39,9 @@ module.exports = Self => { .map(coord => `${coord.longitude},${coord.latitude}`) .join(';'); const response = await axios.post(` - ${locationiqConfig.url}optimize/driving/${concatCoords}?key=${locationiqConfig.key} + ${osrmConfig.url}/trip/v1/driving/${concatCoords} `); - const tolerance = locationiqConfig.tolerance; - + const tolerance = osrmConfig.tolerance; for (waypoint of response.data.waypoints) { const longitude = waypoint.location[0]; const latitude = waypoint.location[1]; diff --git a/back/model-config.json b/back/model-config.json index f48e5f2eb..2ced867f7 100644 --- a/back/model-config.json +++ b/back/model-config.json @@ -88,7 +88,7 @@ "Language": { "dataSource": "vn" }, - "LocationiqConfig": { + "OsrmConfig": { "dataSource": "vn" }, "Machine": { diff --git a/back/models/locationiq-config.js b/back/models/locationiq-config.js deleted file mode 100644 index e0fe736bb..000000000 --- a/back/models/locationiq-config.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = Self => { - require('../methods/locationiq-config/optimize')(Self); -}; - diff --git a/back/models/osrm-config.js b/back/models/osrm-config.js new file mode 100644 index 000000000..f738f305c --- /dev/null +++ b/back/models/osrm-config.js @@ -0,0 +1,4 @@ +module.exports = Self => { + require('../methods/osrm-config/optimize')(Self); +}; + diff --git a/back/models/locationiq-config.json b/back/models/osrm-config.json similarity index 69% rename from back/models/locationiq-config.json rename to back/models/osrm-config.json index 624341615..ae712ba05 100644 --- a/back/models/locationiq-config.json +++ b/back/models/osrm-config.json @@ -1,9 +1,9 @@ { - "name": "LocationiqConfig", + "name": "OsrmConfig", "base": "VnModel", "options": { "mysql": { - "table": "locationiqConfig" + "table": "osrmConfig" } }, "properties": { @@ -16,10 +16,6 @@ "type": "string", "required": true }, - "key": { - "type": "string", - "required": true - }, "tolerance": { "type": "number", "required": false diff --git a/db/versions/11379-yellowCordyline/00-firstScript.sql b/db/versions/11379-yellowCordyline/00-firstScript.sql index 363c32dfa..6c0263f7f 100644 --- a/db/versions/11379-yellowCordyline/00-firstScript.sql +++ b/db/versions/11379-yellowCordyline/00-firstScript.sql @@ -1,9 +1,7 @@ -CREATE TABLE `vn`.`locationiqConfig` ( +CREATE TABLE `vn`.`osrmConfig` ( `id` int(10) unsigned NOT NULL, `url` varchar(100) NOT NULL COMMENT 'Dirección base de la API', - `key` varchar(100) NOT NULL COMMENT 'Access token', `tolerance` decimal(6,6) NOT NULL DEFAULT 0 COMMENT 'Tolerancia entre las coordenadas enviadas y las retornadas', - `maxCoordsRequest` int(10) unsigned DEFAULT NULL COMMENT 'Número máximo de coordenadas por petición', PRIMARY KEY (`id`), - CONSTRAINT `locationiqConfig_check` CHECK (`id` = 1) + CONSTRAINT `osrmConfig_check` CHECK (`id` = 1) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; \ No newline at end of file From 85baf151d9a8cab26aa57116a040232cd10a0d32 Mon Sep 17 00:00:00 2001 From: guillermo Date: Mon, 9 Dec 2024 10:21:23 +0100 Subject: [PATCH 04/12] feat: refs #7882 Fixed problems osrm service --- back/methods/osrm-config/optimize.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/back/methods/osrm-config/optimize.js b/back/methods/osrm-config/optimize.js index 9440800f6..a7c81f33a 100644 --- a/back/methods/osrm-config/optimize.js +++ b/back/methods/osrm-config/optimize.js @@ -47,27 +47,26 @@ module.exports = Self => { const latitude = waypoint.location[1]; const matchedAddress = coords.find(coord => - !coord.position && + coord.position === undefined && Math.abs(coord.latitude - latitude) <= tolerance && Math.abs(coord.longitude - longitude) <= tolerance ); if (matchedAddress) matchedAddress.position = waypoint.waypoint_index; - else - console.log(`Las coordenadas no se han podido asociar: ${latitude} | ${longitude}`); } coords.sort((a, b) => { const posA = a.position !== undefined ? a.position : Infinity; const posB = b.position !== undefined ? b.position : Infinity; return posA - posB; }); - // Temporal para abrir en maps + const coordsString = coords .map(item => `point=${item.latitude},${item.longitude}`) .join('&'); - console.log(`https://graphhopper.com/maps/?${coordsString}&profile=small_truck`); - // --------- - return coords; + return { + coords, + view: `https://graphhopper.com/maps/?${coordsString}&profile=small_truck` + }; } catch (err) { switch (err.response?.data?.code) { case 'NoTrips': From cba5d88c5e36c92f7a35d6c48091cc0754f8e83f Mon Sep 17 00:00:00 2001 From: guillermo Date: Mon, 9 Dec 2024 14:45:31 +0100 Subject: [PATCH 05/12] feat: refs #7882 Osrm service --- back/methods/osrm-config/optimize.js | 20 ++++++++++++--- .../route/back/methods/route/optimizeStops.js | 25 +++++++++++++++++++ modules/route/back/models/route.js | 1 + 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 modules/route/back/methods/route/optimizeStops.js diff --git a/back/methods/osrm-config/optimize.js b/back/methods/osrm-config/optimize.js index a7c81f33a..0c570b95a 100644 --- a/back/methods/osrm-config/optimize.js +++ b/back/methods/osrm-config/optimize.js @@ -27,19 +27,31 @@ module.exports = Self => { if (!osrmConfig) throw new UserError(`OSRM service is not configured`); let coords = []; - for (const addressId of addressIds) { - const address = await models.Address.findById(addressId); + + const address = await models.Address.findById(32308); // Aquí irá el address asociada a la zona + if (address.latitude && address.longitude) { coords.push({ - addressId, + addressId: address.id, latitude: address.latitude.toFixed(6), longitude: address.longitude.toFixed(6) }); } + + for (const addressId of addressIds) { + const address = await models.Address.findById(addressId); + if (address.latitude && address.longitude) { + coords.push({ + addressId, + latitude: address.latitude.toFixed(6), + longitude: address.longitude.toFixed(6) + }); + } + } const concatCoords = coords .map(coord => `${coord.longitude},${coord.latitude}`) .join(';'); const response = await axios.post(` - ${osrmConfig.url}/trip/v1/driving/${concatCoords} + ${osrmConfig.url}/trip/v1/driving/${concatCoords}?source=first&roundtrip=true `); const tolerance = osrmConfig.tolerance; for (waypoint of response.data.waypoints) { diff --git a/modules/route/back/methods/route/optimizeStops.js b/modules/route/back/methods/route/optimizeStops.js new file mode 100644 index 000000000..9666f201e --- /dev/null +++ b/modules/route/back/methods/route/optimizeStops.js @@ -0,0 +1,25 @@ +module.exports = Self => { + Self.remoteMethod('optimizeStops', { + description: 'Updates the ticket priority of tickets without priority', + accepts: [ + { + arg: 'routeFk', + type: 'number', + required: true + } + ], + returns: { + type: 'object', + root: true + }, + http: { + path: '/optimizeStops', + verb: 'post' + } + }); + + Self.optimizeStops = async(routeFk, options) => { + return; + }; +}; + diff --git a/modules/route/back/models/route.js b/modules/route/back/models/route.js index cd8685cec..e28b19a61 100644 --- a/modules/route/back/models/route.js +++ b/modules/route/back/models/route.js @@ -16,4 +16,5 @@ module.exports = Self => { require('../methods/route/downloadZip')(Self); require('../methods/route/getExpeditionSummary')(Self); require('../methods/route/getByWorker')(Self); + require('../methods/route/optimizeStops')(Self); }; From c8ec94bed9116fa3242c566e36eb1335b51834d0 Mon Sep 17 00:00:00 2001 From: guillermo Date: Wed, 11 Dec 2024 09:03:14 +0100 Subject: [PATCH 06/12] feat: refs #7882 Osrm service --- back/methods/osrm-config/optimize.js | 28 +++-- .../11379-yellowCordyline/00-firstScript.sql | 25 +++- loopback/locale/es.json | 3 +- .../back/methods/route/optimizePriority.js | 119 ++++++++++++++++++ .../route/back/methods/route/optimizeStops.js | 25 ---- modules/route/back/models/route.js | 2 +- modules/zone/back/model-config.json | 3 + modules/zone/back/models/zone-config.json | 28 +++++ modules/zone/back/models/zone.json | 37 +++--- 9 files changed, 211 insertions(+), 59 deletions(-) create mode 100644 modules/route/back/methods/route/optimizePriority.js delete mode 100644 modules/route/back/methods/route/optimizeStops.js create mode 100644 modules/zone/back/models/zone-config.json diff --git a/back/methods/osrm-config/optimize.js b/back/methods/osrm-config/optimize.js index 0c570b95a..c03b70315 100644 --- a/back/methods/osrm-config/optimize.js +++ b/back/methods/osrm-config/optimize.js @@ -9,9 +9,13 @@ module.exports = Self => { arg: 'addressIds', type: 'array', required: true + }, { + arg: 'firstAddressId', + type: 'number', + required: false }], returns: { - type: 'string', + type: 'object', root: true }, http: { @@ -20,21 +24,22 @@ module.exports = Self => { } }); - Self.optimize = async addressIds => { + Self.optimize = async(addressIds, firstAddressId) => { const models = Self.app.models; try { const osrmConfig = await models.OsrmConfig.findOne(); if (!osrmConfig) throw new UserError(`OSRM service is not configured`); let coords = []; - - const address = await models.Address.findById(32308); // Aquí irá el address asociada a la zona - if (address.latitude && address.longitude) { - coords.push({ - addressId: address.id, - latitude: address.latitude.toFixed(6), - longitude: address.longitude.toFixed(6) - }); + if (firstAddressId) { + const firstAddress = await models.Address.findById(firstAddressId); + if (firstAddress.latitude && firstAddress.longitude) { + coords.push({ + addressId: firstAddress.id, + latitude: firstAddress.latitude.toFixed(6), + longitude: firstAddress.longitude.toFixed(6) + }); + } } for (const addressId of addressIds) { @@ -47,6 +52,9 @@ module.exports = Self => { }); } } + + if (!coords.length) throw new UserError('No address has coordinates'); + const concatCoords = coords .map(coord => `${coord.longitude},${coord.latitude}`) .join(';'); diff --git a/db/versions/11379-yellowCordyline/00-firstScript.sql b/db/versions/11379-yellowCordyline/00-firstScript.sql index 6c0263f7f..8d90ee90c 100644 --- a/db/versions/11379-yellowCordyline/00-firstScript.sql +++ b/db/versions/11379-yellowCordyline/00-firstScript.sql @@ -1,7 +1,20 @@ CREATE TABLE `vn`.`osrmConfig` ( - `id` int(10) unsigned NOT NULL, - `url` varchar(100) NOT NULL COMMENT 'Dirección base de la API', - `tolerance` decimal(6,6) NOT NULL DEFAULT 0 COMMENT 'Tolerancia entre las coordenadas enviadas y las retornadas', - PRIMARY KEY (`id`), - CONSTRAINT `osrmConfig_check` CHECK (`id` = 1) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; \ No newline at end of file + `id` int(10) unsigned NOT NULL, + `url` varchar(100) NOT NULL COMMENT 'Dirección base de la API', + `tolerance` decimal(6,6) NOT NULL DEFAULT 0 COMMENT 'Tolerancia entre las coordenadas enviadas y las retornadas', + PRIMARY KEY (`id`), + CONSTRAINT `osrmConfig_check` CHECK (`id` = 1) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; + +-- Para que no de error al añadir la FK de zone +UPDATE vn.zone + SET price = 0.1 + WHERE price = 0; + +ALTER TABLE vn.`zone` + ADD addressFk int(11) DEFAULT NULL COMMENT 'Punto de distribución de donde salen para repartir', + ADD CONSTRAINT zone_address_FK FOREIGN KEY (addressFk) REFERENCES vn.address(id) ON DELETE RESTRICT ON UPDATE CASCADE; + +ALTER TABLE vn.zoneConfig + ADD defaultAddressFk int(11) DEFAULT NULL NULL COMMENT 'Punto de distribución por defecto', + ADD CONSTRAINT zoneConfig_address_FK FOREIGN KEY (defaultAddressFk) REFERENCES vn.address(id) ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 8013a7fe6..03cccbe1a 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -393,5 +393,6 @@ "There are tickets to be invoiced": "Hay tickets para esta zona, borralos primero", "No trips found because input coordinates are not connected": "No se encontraron rutas porque las coordenadas de entrada no están conectadas", "This request is not supported": "Esta solicitud no es compatible", - "Invalid options or too many coordinates": "Opciones invalidas o demasiadas coordenadas" + "Invalid options or too many coordinates": "Opciones invalidas o demasiadas coordenadas", + "No address has coordinates": "Ninguna dirección tiene coordenadas" } \ No newline at end of file diff --git a/modules/route/back/methods/route/optimizePriority.js b/modules/route/back/methods/route/optimizePriority.js new file mode 100644 index 000000000..f84af50f8 --- /dev/null +++ b/modules/route/back/methods/route/optimizePriority.js @@ -0,0 +1,119 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethod('optimizePriority', { + description: 'Updates the ticket priority of tickets without priority', + accepts: { + arg: 'id', + type: 'number', + required: true, + description: 'Route id', + http: {source: 'path'} + }, + returns: { + type: 'object', + root: true + }, + http: { + path: '/:id/optimizePriority', + verb: 'POST' + } + }); + + Self.optimizePriority = async(id, options) => { + const models = Self.app.models; + const myOptions = {}; + let tx; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + const ticketsIds = await models.Ticket.find({ + where: {routeFk: id} + }, myOptions); + + let ticketAddress = []; + for (const ticketId of ticketsIds) { + ticketAddress.push({ + ticketId: ticketId.id, + addressId: ticketId.addressFk, + zoneId: ticketId.zoneFk, + priority: ticketId.priority + }); + } + + // Igualamos los priority del mismo addressId + const addressPriorityMap = ticketAddress.reduce((acc, {addressId, priority}) => { + if (priority !== null) { + acc[addressId] = acc[addressId] === undefined + ? priority + : Math.max(acc[addressId], priority); + } + return acc; + }); + ticketAddress.forEach(item => { + const maxPriority = addressPriorityMap[item.addressId]; + if (maxPriority) item.priority = maxPriority; + }); + + // Añadimos las direcciones a optimizar + let addressIds = []; + ticketAddress.forEach(h => { + if (!addressIds.includes(h.addressId) && !h.priority) + addressIds.push(h.addressId); + }); + if (!addressIds.length) throw new UserError('All tickets have a route order'); + + // Obtenemos el zoneId más frecuente + const zoneFrequency = ticketAddress.reduce((acc, {zoneId}) => { + if (zoneId != null) acc[zoneId] = (acc[zoneId] || 0) + 1; + return acc; + }, {}); + const [mostFrequentZoneId] = Object.entries(zoneFrequency) + .reduce((maxEntry, entry) => entry[1] > maxEntry[1] ? entry : maxEntry, [null, 0]); + const zone = await models.Zone.findById(mostFrequentZoneId, myOptions); + let firstAddress = zone.addressFk; + if (!firstAddress) firstAddress = (await models.ZoneConfig.findOne()).defaultAddressFk; + + // Revisamos las coincidencias y actualizamos la prioridad en el array + const addressPositions = await models.OsrmConfig.optimize(addressIds, firstAddress, myOptions); + const maxPosition = Math.max(...ticketAddress.map(g => g.priority)); + await Promise.all(ticketAddress.map(async i => { + const foundPosition = addressPositions.coords.find(item => item.addressId === i.addressId); + if (foundPosition) i.priority = foundPosition.position + (maxPosition + 1); + })); + + // Suavizado de prioridad para que no hayan escalones + const allPriorities = ticketAddress + .map(item => item.priority) + .filter(p => p !== null); + const uniquePriorities = [...new Set(allPriorities)].sort((a, b) => a - b); + const priorityMap = {}; + uniquePriorities.forEach((p, index) => { + priorityMap[p] = index + 1; + }); + ticketAddress.forEach(item => { + if (item.priority !== null) item.priority = priorityMap[item.priority]; + }); + + if (!myOptions.transaction) { + tx = await Self.beginTransaction({}); + myOptions.transaction = tx; + } + + // Realizamos el update en la base de datos + try { + await Promise.all(ticketAddress.map(async y => { + if (y.priority) { + const ticket = await models.Ticket.findById(y.ticketId); + await ticket.updateAttribute('priority', y.priority, myOptions); + } + })); + if (tx) await tx.commit(); + return; + } catch (err) { + if (tx) await tx.rollback(); + throw err; + } + }; +}; diff --git a/modules/route/back/methods/route/optimizeStops.js b/modules/route/back/methods/route/optimizeStops.js deleted file mode 100644 index 9666f201e..000000000 --- a/modules/route/back/methods/route/optimizeStops.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = Self => { - Self.remoteMethod('optimizeStops', { - description: 'Updates the ticket priority of tickets without priority', - accepts: [ - { - arg: 'routeFk', - type: 'number', - required: true - } - ], - returns: { - type: 'object', - root: true - }, - http: { - path: '/optimizeStops', - verb: 'post' - } - }); - - Self.optimizeStops = async(routeFk, options) => { - return; - }; -}; - diff --git a/modules/route/back/models/route.js b/modules/route/back/models/route.js index e28b19a61..f73ff3e51 100644 --- a/modules/route/back/models/route.js +++ b/modules/route/back/models/route.js @@ -16,5 +16,5 @@ module.exports = Self => { require('../methods/route/downloadZip')(Self); require('../methods/route/getExpeditionSummary')(Self); require('../methods/route/getByWorker')(Self); - require('../methods/route/optimizeStops')(Self); + require('../methods/route/optimizePriority')(Self); }; diff --git a/modules/zone/back/model-config.json b/modules/zone/back/model-config.json index 3bbbe0d1b..2cd3f9d01 100644 --- a/modules/zone/back/model-config.json +++ b/modules/zone/back/model-config.json @@ -17,6 +17,9 @@ "ZoneClosure": { "dataSource": "vn" }, + "ZoneConfig": { + "dataSource": "vn" + }, "ZoneEvent": { "dataSource": "vn" }, diff --git a/modules/zone/back/models/zone-config.json b/modules/zone/back/models/zone-config.json new file mode 100644 index 000000000..a5da7fe55 --- /dev/null +++ b/modules/zone/back/models/zone-config.json @@ -0,0 +1,28 @@ +{ + "name": "ZoneConfig", + "options": { + "mysql": { + "table": "zoneConfig" + } + }, + "properties": { + "id": { + "type": "number", + "id": true, + "description": "Identifier" + }, + "scope": { + "type": "number" + }, + "forwardDays": { + "type": "number" + } + }, + "relations": { + "address": { + "type": "belongsTo", + "model": "Address", + "foreignKey": "defaultAddressFk" + } + } +} diff --git a/modules/zone/back/models/zone.json b/modules/zone/back/models/zone.json index 5b25e40d1..141b28750 100644 --- a/modules/zone/back/models/zone.json +++ b/modules/zone/back/models/zone.json @@ -1,9 +1,9 @@ { "name": "Zone", "base": "VnModel", - "mixins": { - "Loggable": true - }, + "mixins": { + "Loggable": true + }, "options": { "mysql": { "table": "zone" @@ -48,30 +48,35 @@ } }, "relations": { - "agencyMode": { - "type": "belongsTo", - "model": "AgencyMode", - "foreignKey": "agencyModeFk" + "agencyMode": { + "type": "belongsTo", + "model": "AgencyMode", + "foreignKey": "agencyModeFk" }, "events": { - "type": "hasMany", - "model": "ZoneEvent", - "foreignKey": "zoneFk" - }, + "type": "hasMany", + "model": "ZoneEvent", + "foreignKey": "zoneFk" + }, "exclusions": { - "type": "hasMany", - "model": "ZoneExclusion", + "type": "hasMany", + "model": "ZoneExclusion", "foreignKey": "zoneFk" }, "warehouses": { "type": "hasMany", "model": "ZoneWarehouse", "foreignKey": "zoneFk" - }, + }, "closures": { "type": "hasMany", "model": "ZoneClosure", "foreignKey": "zoneFk" - } - } + }, + "address": { + "type": "belongsTo", + "model": "Address", + "foreignKey": "addressFk" + } + } } From 252a029c988453229b53008e1902d2b276414ce3 Mon Sep 17 00:00:00 2001 From: guillermo Date: Wed, 11 Dec 2024 11:41:42 +0100 Subject: [PATCH 07/12] feat: refs #7882 Osrm service --- modules/client/back/methods/client/updateAddress.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/client/back/methods/client/updateAddress.js b/modules/client/back/methods/client/updateAddress.js index 7342b28f1..797aefe11 100644 --- a/modules/client/back/methods/client/updateAddress.js +++ b/modules/client/back/methods/client/updateAddress.js @@ -72,6 +72,14 @@ module.exports = function(Self) { { arg: 'isLogifloraAllowed', type: 'boolean' + }, + { + arg: 'longitude', + type: 'number', + }, + { + arg: 'latitude', + type: 'number', } ], returns: { From 186ef5251832650c939f7437fd6d740857801370 Mon Sep 17 00:00:00 2001 From: guillermo Date: Wed, 11 Dec 2024 14:55:26 +0100 Subject: [PATCH 08/12] feat: refs #7882 Osrm service --- back/methods/osrm-config/optimize.js | 15 +++++++++++++-- loopback/locale/es.json | 6 +++--- .../route/back/methods/route/optimizePriority.js | 13 ++++++++----- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/back/methods/osrm-config/optimize.js b/back/methods/osrm-config/optimize.js index c03b70315..ae700d1cc 100644 --- a/back/methods/osrm-config/optimize.js +++ b/back/methods/osrm-config/optimize.js @@ -24,7 +24,7 @@ module.exports = Self => { } }); - Self.optimize = async(addressIds, firstAddressId) => { + Self.optimize = async(addressIds, firstAddressId, lastAddressId) => { const models = Self.app.models; try { const osrmConfig = await models.OsrmConfig.findOne(); @@ -53,13 +53,24 @@ module.exports = Self => { } } + if (lastAddressId) { + const firstAddress = await models.Address.findById(lastAddressId); + if (firstAddress.latitude && firstAddress.longitude) { + coords.push({ + addressId: firstAddress.id, + latitude: firstAddress.latitude.toFixed(6), + longitude: firstAddress.longitude.toFixed(6) + }); + } + } + if (!coords.length) throw new UserError('No address has coordinates'); const concatCoords = coords .map(coord => `${coord.longitude},${coord.latitude}`) .join(';'); const response = await axios.post(` - ${osrmConfig.url}/trip/v1/driving/${concatCoords}?source=first&roundtrip=true + ${osrmConfig.url}/trip/v1/driving/${concatCoords}?source=first&destination=last&roundtrip=true `); const tolerance = osrmConfig.tolerance; for (waypoint of response.data.waypoints) { diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 7e2253059..5f595e1ae 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -396,6 +396,6 @@ "Invalid options or too many coordinates": "Opciones invalidas o demasiadas coordenadas", "No address has coordinates": "Ninguna dirección tiene coordenadas", "An item type with the same code already exists": "Un tipo con el mismo código ya existe", - "Holidays to past days not available": "Las vacaciones a días pasados no están disponibles" -} - + "Holidays to past days not available": "Las vacaciones a días pasados no están disponibles", + "All tickets have a route order": "Todos los tickets tienen orden de ruta" +} \ No newline at end of file diff --git a/modules/route/back/methods/route/optimizePriority.js b/modules/route/back/methods/route/optimizePriority.js index f84af50f8..c31913c66 100644 --- a/modules/route/back/methods/route/optimizePriority.js +++ b/modules/route/back/methods/route/optimizePriority.js @@ -71,13 +71,16 @@ module.exports = Self => { }, {}); const [mostFrequentZoneId] = Object.entries(zoneFrequency) .reduce((maxEntry, entry) => entry[1] > maxEntry[1] ? entry : maxEntry, [null, 0]); - const zone = await models.Zone.findById(mostFrequentZoneId, myOptions); - let firstAddress = zone.addressFk; - if (!firstAddress) firstAddress = (await models.ZoneConfig.findOne()).defaultAddressFk; + + // Obtenemos los address inicio y fin + const maxPosition = Math.max(...ticketAddress.map(g => g.priority)); + let firstAddress = (await models.Zone.findById(mostFrequentZoneId, myOptions))?.addressFk + || (await models.ZoneConfig.findOne())?.defaultAddressFk; + const lastAddress = firstAddress; + if (maxPosition) firstAddress = ticketAddress.find(g => g.priority === maxPosition)?.addressId; // Revisamos las coincidencias y actualizamos la prioridad en el array - const addressPositions = await models.OsrmConfig.optimize(addressIds, firstAddress, myOptions); - const maxPosition = Math.max(...ticketAddress.map(g => g.priority)); + const addressPositions = await models.OsrmConfig.optimize(addressIds, firstAddress, lastAddress, myOptions); await Promise.all(ticketAddress.map(async i => { const foundPosition = addressPositions.coords.find(item => item.addressId === i.addressId); if (foundPosition) i.priority = foundPosition.position + (maxPosition + 1); From 65e195d77d7bd51c451677e45d162fd5ef08d7b0 Mon Sep 17 00:00:00 2001 From: guillermo Date: Thu, 12 Dec 2024 08:23:02 +0100 Subject: [PATCH 09/12] feat: refs #7882 Requested changes --- back/methods/osrm-config/optimize.js | 12 ++++++------ modules/client/back/methods/client/updateAddress.js | 4 ++-- modules/route/back/methods/route/optimizePriority.js | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/back/methods/osrm-config/optimize.js b/back/methods/osrm-config/optimize.js index ae700d1cc..3a68c7ef4 100644 --- a/back/methods/osrm-config/optimize.js +++ b/back/methods/osrm-config/optimize.js @@ -32,12 +32,12 @@ module.exports = Self => { let coords = []; if (firstAddressId) { - const firstAddress = await models.Address.findById(firstAddressId); - if (firstAddress.latitude && firstAddress.longitude) { + const address = await models.Address.findById(firstAddressId); + if (address.latitude && address.longitude) { coords.push({ - addressId: firstAddress.id, - latitude: firstAddress.latitude.toFixed(6), - longitude: firstAddress.longitude.toFixed(6) + addressId: address.id, + latitude: address.latitude.toFixed(6), + longitude: address.longitude.toFixed(6) }); } } @@ -73,7 +73,7 @@ module.exports = Self => { ${osrmConfig.url}/trip/v1/driving/${concatCoords}?source=first&destination=last&roundtrip=true `); const tolerance = osrmConfig.tolerance; - for (waypoint of response.data.waypoints) { + for (const waypoint of response.data.waypoints) { const longitude = waypoint.location[0]; const latitude = waypoint.location[1]; diff --git a/modules/client/back/methods/client/updateAddress.js b/modules/client/back/methods/client/updateAddress.js index 797aefe11..efef83d6b 100644 --- a/modules/client/back/methods/client/updateAddress.js +++ b/modules/client/back/methods/client/updateAddress.js @@ -75,11 +75,11 @@ module.exports = function(Self) { }, { arg: 'longitude', - type: 'number', + type: 'any', }, { arg: 'latitude', - type: 'number', + type: 'any', } ], returns: { diff --git a/modules/route/back/methods/route/optimizePriority.js b/modules/route/back/methods/route/optimizePriority.js index c31913c66..f0ac76d8a 100644 --- a/modules/route/back/methods/route/optimizePriority.js +++ b/modules/route/back/methods/route/optimizePriority.js @@ -28,17 +28,17 @@ module.exports = Self => { if (typeof options == 'object') Object.assign(myOptions, options); - const ticketsIds = await models.Ticket.find({ + const tickets = await models.Ticket.find({ where: {routeFk: id} }, myOptions); let ticketAddress = []; - for (const ticketId of ticketsIds) { + for (const ticket of tickets) { ticketAddress.push({ - ticketId: ticketId.id, - addressId: ticketId.addressFk, - zoneId: ticketId.zoneFk, - priority: ticketId.priority + ticketId: ticket.id, + addressId: ticket.addressFk, + zoneId: ticket.zoneFk, + priority: ticket.priority }); } From 05ba85219b5f91a2351eb4256c11f3211bad0838 Mon Sep 17 00:00:00 2001 From: guillermo Date: Thu, 12 Dec 2024 14:35:58 +0100 Subject: [PATCH 10/12] feat: refs #7882 Osrm service --- back/methods/osrm-config/optimize.js | 8 +------- db/dump/fixtures.before.sql | 3 +++ modules/route/back/methods/route/optimizePriority.js | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/back/methods/osrm-config/optimize.js b/back/methods/osrm-config/optimize.js index 3a68c7ef4..5d2ade935 100644 --- a/back/methods/osrm-config/optimize.js +++ b/back/methods/osrm-config/optimize.js @@ -91,13 +91,7 @@ module.exports = Self => { return posA - posB; }); - const coordsString = coords - .map(item => `point=${item.latitude},${item.longitude}`) - .join('&'); - return { - coords, - view: `https://graphhopper.com/maps/?${coordsString}&profile=small_truck` - }; + return coords; } catch (err) { switch (err.response?.data?.code) { case 'NoTrips': diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index 663705ff5..84e007695 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -4035,3 +4035,6 @@ INSERT IGNORE INTO vn.saySimpleConfig (url, defaultChannel) INSERT INTO vn.workerIrpf (workerFk,spouseNif, geographicMobilityDate) VALUES (1106,'26493101E','2019-09-20'); + +INSERT INTO vn.osrmConfig (id,url,tolerance) + VALUES (1,'https://router.project-osrm.org', 0.002); diff --git a/modules/route/back/methods/route/optimizePriority.js b/modules/route/back/methods/route/optimizePriority.js index f0ac76d8a..56db4ffb5 100644 --- a/modules/route/back/methods/route/optimizePriority.js +++ b/modules/route/back/methods/route/optimizePriority.js @@ -82,7 +82,7 @@ module.exports = Self => { // Revisamos las coincidencias y actualizamos la prioridad en el array const addressPositions = await models.OsrmConfig.optimize(addressIds, firstAddress, lastAddress, myOptions); await Promise.all(ticketAddress.map(async i => { - const foundPosition = addressPositions.coords.find(item => item.addressId === i.addressId); + const foundPosition = addressPositions.find(item => item.addressId === i.addressId); if (foundPosition) i.priority = foundPosition.position + (maxPosition + 1); })); From 00b5333ab7edbb02d04df8ca053217462e79147b Mon Sep 17 00:00:00 2001 From: guillermo Date: Fri, 13 Dec 2024 08:07:13 +0100 Subject: [PATCH 11/12] feat: refs #7882 Added tests --- .../osrm-config/specs/optimize.spec.js | 33 +++++++++++++++++ db/dump/fixtures.before.sql | 10 +++--- .../route/specs/optimizePriority.spec.js | 36 +++++++++++++++++++ 3 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 back/methods/osrm-config/specs/optimize.spec.js create mode 100644 modules/route/back/methods/route/specs/optimizePriority.spec.js diff --git a/back/methods/osrm-config/specs/optimize.spec.js b/back/methods/osrm-config/specs/optimize.spec.js new file mode 100644 index 000000000..9f2adccc6 --- /dev/null +++ b/back/methods/osrm-config/specs/optimize.spec.js @@ -0,0 +1,33 @@ +const models = require('vn-loopback/server/server').models; + +describe('osrmConfig optimize()', function() { + it('should send coords, receive OSRM response, and return a correctly ordered result', async function() { + const result = await models.OsrmConfig.optimize([4, 3], 1, 2); + + // Verifications + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBe(4); + + // Check the order + expect(result[0].addressId).toBe(1); + expect(result[1].addressId).toBe(4); + expect(result[2].addressId).toBe(3); + expect(result[3].addressId).toBe(2); + + // Check the coordinates format + expect(result[0].latitude).toBe('10.111111'); + expect(result[0].longitude).toBe('-74.111111'); + }); + + it('should throw an error if no addresses are provided', async function() { + let error; + try { + await models.OsrmConfig.optimize([], null); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.message).toBe('No address has coordinates'); + }); +}); diff --git a/db/dump/fixtures.before.sql b/db/dump/fixtures.before.sql index d3823a5b7..c108bc0f6 100644 --- a/db/dump/fixtures.before.sql +++ b/db/dump/fixtures.before.sql @@ -428,10 +428,10 @@ INSERT INTO `vn`.`clientConfig`(`id`, `riskTolerance`, `maxCreditRows`, `maxPric INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `provinceFk`, `phone`, `mobile`, `isActive`, `clientFk`, `agencyModeFk`, `longitude`, `latitude`, `isEqualizated`, `isDefaultAddress`) VALUES - (1, 'Bruce Wayne', '1007 Mountain Drive, Gotham', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1101, 2, NULL, NULL, 0, 1), - (2, 'Petter Parker', '20 Ingram Street', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1102, 2, NULL, NULL, 0, 1), - (3, 'Clark Kent', '344 Clinton Street', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1103, 2, NULL, NULL, 0, 1), - (4, 'Tony Stark', '10880 Malibu Point', 'Gotham', 46460, 1, 1111111111, 222222222, 1 , 1104, 2, NULL, NULL, 0, 1), + (1, 'Bruce Wayne', '1007 Mountain Drive, Gotham', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1101, 2, -74.1111111, 10.1111111, 0, 1), + (2, 'Petter Parker', '20 Ingram Street', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1102, 2, -74.2222222, 10.2222222, 0, 1), + (3, 'Clark Kent', '344 Clinton Street', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1103, 2, -74.3333333, 10.3333333, 0, 1), + (4, 'Tony Stark', '10880 Malibu Point', 'Gotham', 46460, 1, 1111111111, 222222222, 1 , 1104, 2, -74.4444444, 10.4444444, 0, 1), (5, 'Max Eisenhardt', 'Unknown Whereabouts', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 1), (6, 'DavidCharlesHaller', 'Evil hideout', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 1), (7, 'Hank Pym', 'Anthill', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1107, 2, NULL, NULL, 0, 1), @@ -462,7 +462,7 @@ INSERT INTO `vn`.`address`(`id`, `nickname`, `street`, `city`, `postalCode`, `pr (120, 'Somewhere in Montortal', 'address 20', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1109, 2, NULL, NULL, 0, 0), (121, 'the bat cave', 'address 21', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1101, 2, NULL, NULL, 0, 0), (122, 'NY roofs', 'address 22', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1102, 2, NULL, NULL, 0, 0), - (123, 'The phone box', 'address 23', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1103, 2, NULL, NULL, 0, 0), + (123, 'The phone box', 'address 23', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1103, 2, -74.555555, 10.555555, 0, 0), (124, 'Stark tower Gotham', 'address 24', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1104, 2, NULL, NULL, 0, 0), (125, 'The plastic cell', 'address 25', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1105, 2, NULL, NULL, 0, 0), (126, 'Many places', 'address 26', 'Gotham', 46460, 1, 1111111111, 222222222, 1, 1106, 2, NULL, NULL, 0, 0), diff --git a/modules/route/back/methods/route/specs/optimizePriority.spec.js b/modules/route/back/methods/route/specs/optimizePriority.spec.js new file mode 100644 index 000000000..c1c2dc45e --- /dev/null +++ b/modules/route/back/methods/route/specs/optimizePriority.spec.js @@ -0,0 +1,36 @@ +const models = require('vn-loopback/server/server').models; +const routeId = 1; + +describe('route optimizePriority())', function() { + it('should execute without throwing errors', async function() { + const tx = await models.Route.beginTransaction({}); + let error; + try { + const options = {transaction: tx}; + await models.Ticket.updateAll( + {routeFk: routeId}, + {priority: null}, + options + ); + await models.Route.optimizePriority(routeId, options); + await tx.rollback(); + } catch (e) { + error = e; + await tx.rollback(); + } + + expect(error).toBeUndefined(); + }); + + it('should execute with error', async function() { + let error; + try { + await models.Route.optimizePriority(routeId); + } catch (e) { + error = e; + } + + expect(error).toBeDefined(); + expect(error.message).toBe('All tickets have a route order'); + }); +}); From 233f45a07a1e45493dd8a360d83758e06bcbc2fb Mon Sep 17 00:00:00 2001 From: guillermo Date: Mon, 16 Dec 2024 07:15:47 +0100 Subject: [PATCH 12/12] feat: refs #7882 Reequested changes --- back/methods/osrm-config/optimize.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/back/methods/osrm-config/optimize.js b/back/methods/osrm-config/optimize.js index 5d2ade935..e0412c74c 100644 --- a/back/methods/osrm-config/optimize.js +++ b/back/methods/osrm-config/optimize.js @@ -13,6 +13,10 @@ module.exports = Self => { arg: 'firstAddressId', type: 'number', required: false + }, { + arg: 'lastAddressId', + type: 'number', + required: false }], returns: { type: 'object',