diff --git a/back/methods/mrw-config/cancelShipment.ejs b/back/methods/mrw-config/cancelShipment.ejs
new file mode 100644
index 000000000..9ef401bc8
--- /dev/null
+++ b/back/methods/mrw-config/cancelShipment.ejs
@@ -0,0 +1,20 @@
+
+
+
+ <%= mrw.franchiseCode %>
+ <%= mrw.subscriberCode %>
+
+ <%= mrw.user %>
+ <%= mrw.password %>
+
+
+
+
+
+
+ <%= externalId %>
+
+
+
+
+
\ No newline at end of file
diff --git a/back/methods/mrw-config/cancelShipment.js b/back/methods/mrw-config/cancelShipment.js
new file mode 100644
index 000000000..218b6a96b
--- /dev/null
+++ b/back/methods/mrw-config/cancelShipment.js
@@ -0,0 +1,46 @@
+const axios = require('axios');
+const fs = require('fs');
+const ejs = require('ejs');
+const {DOMParser} = require('xmldom');
+
+module.exports = Self => {
+ Self.remoteMethod('cancelShipment', {
+ description: 'Cancel a shipment by providing the expedition ID, interacting with MRW WebService',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'expeditionFk',
+ type: 'number',
+ required: true
+ }],
+ returns: {
+ type: ['object'],
+ root: true
+ },
+ http: {
+ path: `/cancelShipment`,
+ verb: 'POST'
+ }
+ });
+
+ Self.cancelShipment = async expeditionFk => {
+ const models = Self.app.models;
+
+ const mrw = await models.MrwConfig.findOne();
+ const {externalId} = await models.Expedition.findById(expeditionFk);
+
+ const template = fs.readFileSync(__dirname + '/cancelShipment.ejs', 'utf-8');
+ const renderedXml = ejs.render(template, {mrw, externalId});
+ const response = await axios.post(mrw.url, renderedXml, {
+ headers: {
+ 'Content-Type': 'application/soap+xml; charset=utf-8'
+ }
+ });
+
+ const xmlString = response.data;
+ const parser = new DOMParser();
+ const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
+ const [resultElement] = xmlDoc.getElementsByTagName('Mensaje');
+
+ return resultElement.textContent;
+ };
+};
diff --git a/back/methods/mrw-config/createShipment.ejs b/back/methods/mrw-config/createShipment.ejs
new file mode 100644
index 000000000..bf8a07dab
--- /dev/null
+++ b/back/methods/mrw-config/createShipment.ejs
@@ -0,0 +1,43 @@
+
+
+
+
+ <%= mrw.franchiseCode %>
+ <%= mrw.subscriberCode %>
+
+ <%= mrw.user %>
+ <%= mrw.password %>
+
+
+
+
+
+
+
+
+ <%= expeditionData.street %>
+
+
+ <%= expeditionData.postalCode %>
+ <%= expeditionData.city %>
+
+
+
+ <%= expeditionData.fi %>
+ <%= expeditionData.clientName %>
+ <%= expeditionData.phone %>
+
+
+ <%= expeditionData.created %>
+ <%= expeditionData.expeditionDataId %>
+ <%= expeditionData.serviceType %>
+ 1
+ <%= expeditionData.weekDays %>
+ <%= expeditionData.kg %>
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/back/methods/mrw-config/createShipment.js b/back/methods/mrw-config/createShipment.js
new file mode 100644
index 000000000..f0f7d66a2
--- /dev/null
+++ b/back/methods/mrw-config/createShipment.js
@@ -0,0 +1,109 @@
+const axios = require('axios');
+const {DOMParser} = require('xmldom');
+const fs = require('fs');
+const ejs = require('ejs');
+const UserError = require('vn-loopback/util/user-error');
+
+module.exports = Self => {
+ Self.remoteMethod('createShipment', {
+ description: 'Create an expedition and return a base64Binary label from de MRW WebService',
+ accessType: 'WRITE',
+ accepts: [{
+ arg: 'expeditionFk',
+ type: 'number',
+ required: true
+ }],
+ returns: {
+ type: ['object'],
+ root: true
+ },
+ http: {
+ path: `/createShipment`,
+ verb: 'POST'
+ }
+ });
+
+ Self.createShipment = async(expeditionFk, options) => {
+ const myOptions = {};
+
+ if (typeof options == 'object')
+ Object.assign(myOptions, options);
+
+ if (!myOptions.transaction)
+ myOptions.transaction = await Self.beginTransaction({});
+
+ const models = Self.app.models;
+ const mrw = await models.MrwConfig.findOne(null, myOptions);
+
+ if (!mrw)
+ throw new UserError(`Some mrwConfig parameters are not set`);
+
+ const query =
+ `SELECT CASE co.code
+ WHEN 'ES' THEN a.postalCode
+ WHEN 'PT' THEN LEFT(a.postalCode, 4)
+ WHEN 'AD' THEN REPLACE(a.postalCode, 'AD', '00')
+ END postalCode,
+ a.city,
+ a.street,
+ co.code countryCode,
+ c.fi,
+ c.name clientName,
+ c.phone,
+ DATE_FORMAT(t.shipped, '%d/%m/%Y') created,
+ t.shipped,
+ e.id expeditionId,
+ LPAD(IF(mw.params IS NULL, ms.serviceType, mw.serviceType), 4 ,'0') serviceType,
+ IF(mw.weekdays, 'S', 'N') weekDays
+ FROM expedition e
+ JOIN ticket t ON e.ticketFk = t.id
+ JOIN agencyMode am ON am.id = t.agencyModeFk
+ JOIN mrwService ms ON ms.agencyModeCodeFk = am.code
+ LEFT JOIN mrwServiceWeekday mw ON mw.weekdays = DATE_FORMAT(t.shipped, '%a')
+ JOIN client c ON t.clientFk = c.id
+ JOIN address a ON t.addressFk = a.id
+ JOIN province p ON a.provinceFk = p.id
+ JOIN country co ON co.id = p.countryFk
+ WHERE e.id = ?
+ LIMIT 1`;
+
+ const [expeditionData] = await Self.rawSql(query, [expeditionFk], myOptions);
+
+ if (!expeditionData)
+ throw new UserError(`This expedition is not a MRW shipment`);
+
+ const today = Date.vnNew();
+ today.setHours(0, 0, 0, 0);
+ if (expeditionData?.shipped.setHours(0, 0, 0, 0) < today)
+ throw new UserError(`This ticket has a shipped date earlier than today`);
+
+ const shipmentResponse = await sendXmlDoc('createShipment', {mrw, expeditionData}, 'application/soap+xml');
+ const shipmentId = getTextByTag(shipmentResponse, 'NumeroEnvio');
+
+ if (!shipmentId)
+ throw new UserError(getTextByTag(shipmentResponse, 'Mensaje'));
+
+ const getLabelResponse = await sendXmlDoc('getLabel', {mrw, shipmentId}, 'text/xml');
+ const file = getTextByTag(getLabelResponse, 'EtiquetaFile');
+
+ await models.Expedition.updateAll({id: expeditionFk}, {externalId: shipmentId}, myOptions);
+ return file;
+ };
+
+ function getTextByTag(xmlDoc, tag) {
+ return xmlDoc?.getElementsByTagName(tag)[0]?.textContent;
+ }
+
+ async function sendXmlDoc(xmlDock, params, contentType) {
+ const parser = new DOMParser();
+
+ const xmlTemplate = fs.readFileSync(__dirname + `/${xmlDock}.ejs`, 'utf-8');
+ const renderedTemplate = ejs.render(xmlTemplate, params);
+ const data = await axios.post(params.mrw.url, renderedTemplate, {
+ headers: {
+ 'Content-Type': `${contentType}; charset=utf-8`
+ }
+ });
+ return parser.parseFromString(data.data, 'text/xml');
+ }
+};
diff --git a/back/methods/mrw-config/getLabel.ejs b/back/methods/mrw-config/getLabel.ejs
new file mode 100644
index 000000000..09bdb3f6c
--- /dev/null
+++ b/back/methods/mrw-config/getLabel.ejs
@@ -0,0 +1,25 @@
+
+
+
+ <%= mrw.franchiseCode %>
+ <%= mrw.subscriberCode %>
+
+ <%= mrw.user %>
+ <%= mrw.password %>
+
+
+
+
+
+ <%= shipmentId %>
+ 1
+
+
+
+ 0
+ 0
+ 0
+
+
+
+
\ No newline at end of file
diff --git a/back/methods/mrw-config/specs/createShipment.spec.js b/back/methods/mrw-config/specs/createShipment.spec.js
new file mode 100644
index 000000000..e7bba524a
--- /dev/null
+++ b/back/methods/mrw-config/specs/createShipment.spec.js
@@ -0,0 +1,120 @@
+const models = require('vn-loopback/server/server').models;
+const axios = require('axios');
+const fs = require('fs');
+
+const mockBase64Binary = 'base64BinaryString';
+const ticket1 = {
+ 'id': '44',
+ 'clientFk': 1101,
+ 'shipped': Date.vnNew(),
+ 'nickname': 'MRW',
+ 'addressFk': 1,
+ 'agencyModeFk': 999
+};
+
+const expedition1 = {
+ 'id': 17,
+ 'agencyModeFk': 999,
+ 'ticketFk': 44,
+ 'freightItemFk': 71,
+ 'created': '2001-01-01',
+ 'counter': 1,
+ 'workerFk': 18,
+ 'packagingFk': '94',
+ 'hostFk': '',
+ 'stateTypeFk': 3,
+ 'hasNewRoute': 0,
+ 'isBox': 71,
+ 'editorFk': 100
+};
+
+let tx;
+let options;
+
+describe('MRWConfig createShipment()', () => {
+ beforeEach(async() => {
+ options = tx = undefined;
+ tx = await models.MrwConfig.beginTransaction({});
+ options = {transaction: tx};
+
+ await models.Agency.create(
+ {'id': 999, 'name': 'mrw'},
+ options
+ );
+
+ await models.AgencyMode.create(
+ {'id': 999, 'name': 'mrw', 'agencyFk': 999, 'code': 'mrw'},
+ options
+ );
+
+ await models.MrwConfig.create(
+ {
+ 'url': 'https://url.com',
+ 'user': 'user',
+ 'password': 'password',
+ 'franchiseCode': 'franchiseCode',
+ 'subscriberCode': 'subscriberCode'
+ }, options
+ );
+
+ await models.Application.rawSql(
+ `INSERT INTO vn.mrwService
+ SET agencyModeCodeFk = 'mrw',
+ clientType = 1,
+ serviceType = 1,
+ kg = 1`, null, options
+ );
+ await models.Ticket.create(ticket1, options);
+ await models.Expedition.create(expedition1, options);
+ });
+
+ afterEach(async() => {
+ await tx.rollback();
+ });
+
+ it('should create a shipment and return a base64Binary label', async() => {
+ const mockPostResponses = [
+ {data: fs.readFileSync(__dirname + '/mockGetLabel.xml', 'utf-8')},
+ {data: fs.readFileSync(__dirname + '/mockCreateShipment.xml', 'utf-8')}
+ ];
+
+ spyOn(axios, 'post').and.callFake(() => Promise.resolve(mockPostResponses.pop()));
+
+ const base64Binary = await models.MrwConfig.createShipment(expedition1.id, options);
+
+ expect(base64Binary).toEqual(mockBase64Binary);
+ });
+
+ it('should fail if mrwConfig has no data', async() => {
+ let error;
+ await models.MrwConfig.createShipment(expedition1.id).catch(e => {
+ error = e;
+ }).finally(async() => {
+ expect(error.message).toEqual(`Some mrwConfig parameters are not set`);
+ });
+
+ expect(error).toBeDefined();
+ });
+
+ it('should fail if expeditionFk is not a MrwExpedition', async() => {
+ let error;
+ await models.MrwConfig.createShipment(undefined, options).catch(e => {
+ error = e;
+ }).finally(async() => {
+ expect(error.message).toEqual(`This expedition is not a MRW shipment`);
+ });
+ });
+
+ it(' should fail if the creation date of this ticket is before the current date it', async() => {
+ let error;
+ const yesterday = Date.vnNew();
+
+ yesterday.setDate(yesterday.getDate() - 1);
+ await models.Ticket.updateAll({id: ticket1.id}, {shipped: yesterday}, options);
+ await models.MrwConfig.createShipment(expedition1.id, options).catch(e => {
+ error = e;
+ }).finally(async() => {
+ expect(error.message).toEqual(`This ticket has a shipped date earlier than today`);
+ });
+ });
+});
diff --git a/back/methods/mrw-config/specs/mockCreateShipment.xml b/back/methods/mrw-config/specs/mockCreateShipment.xml
new file mode 100644
index 000000000..dacc5d3f3
--- /dev/null
+++ b/back/methods/mrw-config/specs/mockCreateShipment.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ 1
+
+ 1
+ 1
+ http://url.com
+
+
+
+
\ No newline at end of file
diff --git a/back/methods/mrw-config/specs/mockGetLabel.xml b/back/methods/mrw-config/specs/mockGetLabel.xml
new file mode 100644
index 000000000..0401ce2c7
--- /dev/null
+++ b/back/methods/mrw-config/specs/mockGetLabel.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ 1
+
+ base64BinaryString
+
+
+
+
\ No newline at end of file
diff --git a/back/model-config.json b/back/model-config.json
index 27a94498c..c4eefd932 100644
--- a/back/model-config.json
+++ b/back/model-config.json
@@ -159,6 +159,9 @@
},
"VnRole": {
"dataSource": "vn"
+ },
+ "MrwConfig": {
+ "dataSource": "vn"
}
}
diff --git a/back/models/mrw-config.js b/back/models/mrw-config.js
new file mode 100644
index 000000000..f764b91cc
--- /dev/null
+++ b/back/models/mrw-config.js
@@ -0,0 +1,4 @@
+module.exports = Self => {
+ require('../methods/mrw-config/createShipment')(Self);
+ require('../methods/mrw-config/cancelShipment')(Self);
+};
diff --git a/back/models/mrw-config.json b/back/models/mrw-config.json
new file mode 100644
index 000000000..f0cf799b1
--- /dev/null
+++ b/back/models/mrw-config.json
@@ -0,0 +1,31 @@
+{
+ "name": "MrwConfig",
+ "base": "VnModel",
+ "options": {
+ "mysql": {
+ "table": "mrwConfig"
+ }
+ },
+ "properties": {
+ "id": {
+ "type": "number",
+ "required": true
+ },
+ "url": {
+ "type": "string",
+ "required": true
+ },
+ "user": {
+ "type": "string"
+ },
+ "password": {
+ "type": "string"
+ },
+ "franchiseCode": {
+ "type": "string"
+ },
+ "subscriberCode": {
+ "type": "string"
+ }
+ }
+}
diff --git a/db/versions/10889-redMedeola/00-firstScript.sql b/db/versions/10889-redMedeola/00-firstScript.sql
new file mode 100644
index 000000000..ecae0234f
--- /dev/null
+++ b/db/versions/10889-redMedeola/00-firstScript.sql
@@ -0,0 +1,24 @@
+-- Place your SQL code here
+CREATE TABLE IF NOT EXISTS `vn`.`mrwConfig` (
+ `id` INT auto_increment NULL,
+ `url` varchar(100) NULL,
+ `user` varchar(100) NULL,
+ `password` varchar(100) NULL,
+ `franchiseCode` varchar(100) NULL,
+ `subscriberCode` varchar(100) NULL,
+ CONSTRAINT mrwConfig_pk PRIMARY KEY (id)
+)
+ENGINE=InnoDB
+DEFAULT CHARSET=utf8mb3
+COLLATE=utf8mb3_unicode_ci;
+
+ALTER TABLE `vn`.`packingSite` ADD `hasNewLabelMrwMethod` BOOL NULL;
+
+INSERT INTO `salix`.`ACL` (`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`)
+ VALUES('MrwConfig', 'cancelShipment', 'WRITE', 'ALLOW', 'ROLE', 'employee');
+
+INSERT INTO `salix`.`ACL` (`model`,`property`,`accessType`,`permission`,`principalType`,`principalId`)
+ VALUES ('MrwConfig','createShipment','WRITE','ALLOW','ROLE','employee');
+
+
+
diff --git a/loopback/locale/es.json b/loopback/locale/es.json
index aea0c311c..b14358e85 100644
--- a/loopback/locale/es.json
+++ b/loopback/locale/es.json
@@ -345,4 +345,4 @@
"Cmr file does not exist": "El archivo del cmr no existe",
"You are not allowed to modify the alias": "No estás autorizado a modificar el alias",
"No tickets to invoice": "No hay tickets para facturar"
-}
+}
\ No newline at end of file