From 1419acb8a7d9463e886e42fb0f39e09b6bed2f3f Mon Sep 17 00:00:00 2001 From: joan Date: Mon, 21 Dec 2020 14:48:47 +0100 Subject: [PATCH] 2663 - Drag & drop ticket to route index --- loopback/locale/es.json | 3 +- .../{ticketToRoute.js => insertTicket.js} | 15 ++-- ...etToRoute.spec.js => insertTicket.spec.js} | 6 +- modules/route/back/models/route.js | 2 +- modules/route/front/index/index.html | 3 +- modules/route/front/index/index.js | 36 ++++++++ modules/route/front/index/index.spec.js | 83 ++++++++++++++++++- modules/route/front/tickets/index.js | 8 +- modules/route/front/tickets/index.spec.js | 4 +- 9 files changed, 140 insertions(+), 20 deletions(-) rename modules/route/back/methods/route/{ticketToRoute.js => insertTicket.js} (83%) rename modules/route/back/methods/route/specs/{ticketToRoute.spec.js => insertTicket.spec.js} (86%) diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 6764ca1f1..84347550c 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -162,5 +162,6 @@ "You need to fill sage information before you check verified data": "Debes rellenar la información de sage antes de marcar datos comprobados", "ASSIGN_ZONE_FIRST": "Asigna una zona primero", "You can't upload images on the test environment": "No puedes subir imágenes en el entorno de pruebas", - "You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria" + "You can not select this payment method without a registered bankery account": "No se puede utilizar este método de pago si no has registrado una cuenta bancaria", + "The selected ticket is not suitable for this route": "El ticket seleccionado no es apto para esta ruta" } \ No newline at end of file diff --git a/modules/route/back/methods/route/ticketToRoute.js b/modules/route/back/methods/route/insertTicket.js similarity index 83% rename from modules/route/back/methods/route/ticketToRoute.js rename to modules/route/back/methods/route/insertTicket.js index 93b270015..ca047aaab 100644 --- a/modules/route/back/methods/route/ticketToRoute.js +++ b/modules/route/back/methods/route/insertTicket.js @@ -1,18 +1,18 @@ const UserError = require('vn-loopback/util/user-error'); module.exports = Self => { - Self.remoteMethod('ticketToRoute', { + Self.remoteMethod('insertTicket', { description: 'Check if the ticket can be insert into the route and insert it', accessType: 'READ', accepts: [{ - arg: 'ticketId', + arg: 'routeId', type: 'number', required: true, - description: 'ticketId ', + description: 'The route id', http: {source: 'path'} }, { - arg: 'routeId', + arg: 'ticketId', type: 'number', required: true }], @@ -21,12 +21,12 @@ module.exports = Self => { root: true }, http: { - path: `/:ticketId/ticketToRoute`, + path: `/:routeId/insertTicket`, verb: 'PATCH' } }); - Self.ticketToRoute = async(ticketId, routeId) => { + Self.insertTicket = async(routeId, ticketId) => { const models = Self.app.models; const route = await models.Route.findById(routeId); @@ -43,9 +43,10 @@ module.exports = Self => { landed: {between: [minDate, maxDate]}, } }); + if (!ticket) throw new UserError('The selected ticket is not suitable for this route'); - return await ticket.updateAttribute('routeFk', route.id); + return ticket.updateAttribute('routeFk', route.id); }; }; diff --git a/modules/route/back/methods/route/specs/ticketToRoute.spec.js b/modules/route/back/methods/route/specs/insertTicket.spec.js similarity index 86% rename from modules/route/back/methods/route/specs/ticketToRoute.spec.js rename to modules/route/back/methods/route/specs/insertTicket.spec.js index 83c1d5080..49bf04bd3 100644 --- a/modules/route/back/methods/route/specs/ticketToRoute.spec.js +++ b/modules/route/back/methods/route/specs/insertTicket.spec.js @@ -1,7 +1,7 @@ const app = require('vn-loopback/server/server'); const LoopBackContext = require('loopback-context'); -describe('route ticketToRoute()', () => { +describe('route insertTicket()', () => { const deliveryId = 56; let originalTicket; const routeId = 2; @@ -30,7 +30,7 @@ describe('route ticketToRoute()', () => { originalTicket = await app.models.Ticket.findById(14); const ticketId = 14; - const result = await app.models.Route.ticketToRoute(ticketId, routeId); + const result = await app.models.Route.insertTicket(routeId, ticketId); expect(result.routeFk).toEqual(2); }); @@ -40,7 +40,7 @@ describe('route ticketToRoute()', () => { let error; try { - await app.models.Route.ticketToRoute(ticketId, routeId); + await app.models.Route.insertTicket(routeId, ticketId); } catch (e) { error = e.message; } diff --git a/modules/route/back/models/route.js b/modules/route/back/models/route.js index cdb51c609..6320a888c 100644 --- a/modules/route/back/models/route.js +++ b/modules/route/back/models/route.js @@ -5,7 +5,7 @@ module.exports = Self => { require('../methods/route/guessPriority')(Self); require('../methods/route/updateVolume')(Self); require('../methods/route/getDeliveryPoint')(Self); - require('../methods/route/ticketToRoute')(Self); + require('../methods/route/insertTicket')(Self); Self.validate('kmStart', validateDistance, { message: 'Distance must be lesser than 1000' diff --git a/modules/route/front/index/index.html b/modules/route/front/index/index.html index 66e30d839..d71bcbc48 100644 --- a/modules/route/front/index/index.html +++ b/modules/route/front/index/index.html @@ -26,7 +26,8 @@ + ui-sref="route.card.summary({id: {{::route.id}}})" + ng-attr-id="{{::route.id}}" vn-droppable="$ctrl.onDrop($event)"> { + this.vnApp.showSuccess(this.$t('Data saved!')); + this.$.model.refresh(); + }).catch(error => { + if (error.status == 404) + return this.vnApp.showError(this.$t('Ticket not found')); + throw error; + }); + } } Controller.$inject = ['$element', '$scope', 'vnReport']; diff --git a/modules/route/front/index/index.spec.js b/modules/route/front/index/index.spec.js index e90fc7164..b66ecaf00 100644 --- a/modules/route/front/index/index.spec.js +++ b/modules/route/front/index/index.spec.js @@ -3,10 +3,12 @@ import crudModel from 'core/mocks/crud-model'; describe('Component vnRouteIndex', () => { let controller; + let $httpBackend; beforeEach(ngModule('route')); - beforeEach(inject($componentController => { + beforeEach(inject(($componentController, _$httpBackend_) => { + $httpBackend = _$httpBackend_; const $element = angular.element(''); controller = $componentController('vnRouteIndex', {$element}); controller.$.model = crudModel; @@ -57,4 +59,83 @@ describe('Component vnRouteIndex', () => { expect(controller.vnReport.show).toHaveBeenCalledWith('driver-route', expectedParams); }); }); + + describe('onDrop()', () => { + it('should call the insert method when dragging a ticket number', () => { + jest.spyOn(controller, 'insert'); + + const routeId = '1'; + const expectedTicketId = '16'; + const draggedElement = '16'; + const droppable = document.createElement('a'); + droppable.setAttribute('id', 1); + droppable.classList.add('vn-tr'); + + const $event = { + dataTransfer: { + getData: () => draggedElement + }, + target: droppable + }; + controller.onDrop($event); + + expect(controller.insert).toHaveBeenCalledWith(routeId, expectedTicketId); + }); + + it('should call the insert method when dragging a ticket link', () => { + jest.spyOn(controller, 'insert'); + + const routeId = '1'; + const expectedTicketId = '11'; + const draggedElement = 'http://arkamcity.com/#!/ticket/11/summary'; + const droppable = document.createElement('a'); + droppable.setAttribute('id', 1); + droppable.classList.add('vn-tr'); + + const $event = { + dataTransfer: { + getData: () => draggedElement + }, + target: droppable + }; + controller.onDrop($event); + + expect(controller.insert).toHaveBeenCalledWith(routeId, expectedTicketId); + }); + + it('should throw an error when dragging an invalid ticket link', () => { + jest.spyOn(controller.vnApp, 'showError'); + + const draggedElement = 'http://arkamcity.com/#!/item/11/summary'; + const droppable = document.createElement('a'); + droppable.setAttribute('id', 1); + droppable.classList.add('vn-tr'); + const $event = { + dataTransfer: { + getData: () => draggedElement + }, + target: droppable + }; + controller.onDrop($event); + + expect(controller.vnApp.showError).toHaveBeenCalledWith('Ticket not found'); + }); + }); + + describe('insert()', () => { + it('should make a HTTP patch query and then call both refresh and showSuccess methods', () => { + jest.spyOn(controller.$.model, 'refresh').mockReturnThis(); + jest.spyOn(controller.vnApp, 'showSuccess'); + + const routeId = 1; + const ticketId = 11; + const data = {ticketId}; + $httpBackend.expect('PATCH', `Routes/1/insertTicket`, data).respond(); + controller.insert(routeId, ticketId); + $httpBackend.flush(); + + expect(controller.vnApp.showSuccess).toHaveBeenCalled(); + expect(controller.$.model.refresh).toHaveBeenCalledWith(); + }); + }); }); diff --git a/modules/route/front/tickets/index.js b/modules/route/front/tickets/index.js index 49ca8d60f..0264faf35 100644 --- a/modules/route/front/tickets/index.js +++ b/modules/route/front/tickets/index.js @@ -161,11 +161,11 @@ class Controller extends Section { this.insert(ticketId); } - insert(id) { - const params = {routeId: this.route.id}; - const query = `Routes/${id}/ticketToRoute`; + insert(ticketId) { + ticketId = parseInt(ticketId); - return this.$http.patch(query, params).then(() => { + const query = `Routes/${this.route.id}/insertTicket`; + return this.$http.patch(query, {ticketId}).then(() => { this.vnApp.showSuccess(this.$t('Data saved!')); this.$.model.refresh(); this.card.reload(); diff --git a/modules/route/front/tickets/index.spec.js b/modules/route/front/tickets/index.spec.js index a1b9229b7..8a8f09489 100644 --- a/modules/route/front/tickets/index.spec.js +++ b/modules/route/front/tickets/index.spec.js @@ -309,8 +309,8 @@ describe('Route', () => { jest.spyOn(controller.vnApp, 'showSuccess'); const ticketId = 11; - const data = {routeId: 1}; - $httpBackend.expect('PATCH', `Routes/11/ticketToRoute`, data).respond(); + const data = {ticketId}; + $httpBackend.expect('PATCH', `Routes/1/insertTicket`, data).respond(); controller.insert(ticketId); $httpBackend.flush();