diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql
index 2d3353f15..7a7083313 100644
--- a/db/dump/fixtures.sql
+++ b/db/dump/fixtures.sql
@@ -471,7 +471,7 @@ INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeF
(11, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 102, 'NY roofs', 122, NULL, 0, 3, CURDATE()),
(12, 1, 1, 1, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 103, 'Phone Box', 123, NULL, 0, 1, CURDATE()),
(13, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 103, 'Phone Box', 123, NULL, 0, 3, CURDATE()),
- (14, 1, 2, 1, NULL, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Malibu Point', 4, NULL, 0, 9, CURDATE()),
+ (14, 1, 2, 1, NULL, CURDATE(), CURDATE(), 104, 'Malibu Point', 4, NULL, 0, 9, CURDATE()),
(15, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 105, 'Plastic Cell', 125, NULL, 0, 3, CURDATE()),
(16, 1, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()),
(17, 1, 7, 2, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()),
diff --git a/e2e/paths/05-ticket-module/16_summary.spec.js b/e2e/paths/05-ticket-module/16_summary.spec.js
index 0fd3b8b7c..2832da428 100644
--- a/e2e/paths/05-ticket-module/16_summary.spec.js
+++ b/e2e/paths/05-ticket-module/16_summary.spec.js
@@ -17,7 +17,7 @@ describe('Ticket Summary path', () => {
it(`should display details from the ticket and it's client on the top of the header`, async() => {
let result = await nightmare
- .waitForSpinnerLoad()
+ .waitForTextInElement(selectors.ticketSummary.header, 'Bruce Banner')
.waitToGetProperty(selectors.ticketSummary.header, 'innerText');
expect(result).toContain(`Ticket #${ticketId}`);
diff --git a/modules/route/back/methods/route/getTickets.js b/modules/route/back/methods/route/getTickets.js
index cbe7db9f8..a0014a60d 100644
--- a/modules/route/back/methods/route/getTickets.js
+++ b/modules/route/back/methods/route/getTickets.js
@@ -28,12 +28,6 @@ module.exports = Self => {
fields: ['id', 'packages', 'warehouseFk', 'nickname', 'clientFk', 'priority', 'addressFk'],
order: 'priority',
include: [
- {
- relation: 'client',
- scope: {
- fields: ['id', 'street', 'postcode'],
- }
- },
{
relation: 'state',
scope: {
diff --git a/modules/route/front/tickets/__snapshots__/index.spec.js.snap b/modules/route/front/tickets/__snapshots__/index.spec.js.snap
new file mode 100644
index 000000000..9476a8e09
--- /dev/null
+++ b/modules/route/front/tickets/__snapshots__/index.spec.js.snap
@@ -0,0 +1,18 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Route getSelectedItems() should return the selected items 1`] = `
+Array [
+ Object {
+ "checked": true,
+ "id": 1,
+ },
+ Object {
+ "checked": true,
+ "id": 3,
+ },
+ Object {
+ "checked": true,
+ "id": 5,
+ },
+]
+`;
diff --git a/modules/route/front/tickets/index.html b/modules/route/front/tickets/index.html
index 3df0bc346..16b67ed75 100644
--- a/modules/route/front/tickets/index.html
+++ b/modules/route/front/tickets/index.html
@@ -1,104 +1,105 @@
-
-
+
+
+
@@ -109,4 +110,72 @@
vn-id="confirm"
question="Delete ticket from route?"
on-response="$ctrl.removeTicketFromRoute(response)">
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ticket
+ Client
+ Packages
+ Warehouse
+ Postcode
+ Address
+
+
+
+
+
+
+
+
+ {{ticket.id}}
+
+
+ {{ticket.nickname}}
+
+
+ {{ticket.packages}}
+ {{ticket.warehouse.name}}
+ {{ticket.address.postalCode}}
+ {{ticket.address.street}}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/route/front/tickets/index.js b/modules/route/front/tickets/index.js
index ce12d88ac..9b9a49222 100644
--- a/modules/route/front/tickets/index.js
+++ b/modules/route/front/tickets/index.js
@@ -2,12 +2,23 @@ import ngModule from '../module';
import './style.scss';
class Controller {
- constructor($stateParams, $, $translate, $http, vnApp) {
+ constructor($stateParams, $scope, $translate, $http, vnApp, $filter) {
this.$translate = $translate;
this.$stateParams = $stateParams;
- this.$ = $;
+ this.$ = $scope;
this.$http = $http;
this.vnApp = vnApp;
+ this.$filter = $filter;
+ }
+
+ set route(value) {
+ this._route = value;
+ if (value)
+ this.buildPossibleTicketsFilter();
+ }
+
+ get route() {
+ return this._route;
}
get isChecked() {
@@ -19,13 +30,37 @@ class Controller {
return false;
}
+ buildPossibleTicketsFilter() {
+ let minDate = new Date(this.route.finished);
+ minDate.setHours(0, 0, 0, 0);
+
+ let maxDate = new Date(this.route.finished);
+ maxDate.setHours(23, 59, 59, 59);
+
+ this.possibleTicketsFilter = {
+ where: {
+ zoneFk: this.route.zoneFk,
+ routeFk: null,
+ landed: {between: [minDate, maxDate]},
+ },
+ include: [
+ {
+ relation: 'warehouse',
+ scope: {
+ fields: ['name']
+ },
+ }, {
+ relation: 'address'
+ }
+ ]
+ };
+ }
+
getHighestPriority() {
- let max = 0;
- this.$.model.data.forEach(tag => {
- if (tag.priority > max)
- max = tag.priority;
- });
- return max + 1;
+ let highestPriority = Math.max(...this.$.model.data.map(tag => {
+ return tag.priority;
+ }));
+ return highestPriority + 1;
}
setPriority(id, priority) {
@@ -37,16 +72,16 @@ class Controller {
});
}
- getCheckedLines() {
- let lines = [];
- let data = this.tickets;
- if (data) {
- for (let i = 0; i < data.length; i++) {
- if (data[i].checked)
- lines.push(data[i]);
+ getSelectedItems(items) {
+ const selectedItems = [];
+
+ if (items) {
+ for (let i = 0; i < items.length; i++) {
+ if (items[i].checked)
+ selectedItems.push(items[i]);
}
}
- return lines;
+ return selectedItems;
}
goToBuscaman() {
@@ -54,7 +89,7 @@ class Controller {
let firstAddress = `46460 Av Espioca 100-46460 Silla`;
let addresses = firstAddress;
- let lines = this.getCheckedLines();
+ let lines = this.getSelectedItems(this.tickets);
let url = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=';
lines.forEach(line => {
@@ -64,8 +99,8 @@ class Controller {
window.open(url + addresses, '_blank');
}
- showDeleteConfirm(ticket) {
- this.selectedTicket = ticket;
+ showDeleteConfirm(id) {
+ this.selectedTicket = id;
this.$.confirm.show();
}
@@ -109,14 +144,38 @@ class Controller {
this.$.clientDescriptor.show();
event.preventDefault();
}
+
+ openPossibleTicketsDialog() {
+ this.$.possibleTicketsModel.refresh();
+ this.$.possibleTicketsDialog.show();
+ }
+
+ setTicketsRoute(response) {
+ if (response === 'ACCEPT') {
+ let tickets = this.getSelectedItems(this.possibleTickets);
+
+ for (let i = 0; i < tickets.length; i++) {
+ delete tickets[i].checked;
+ tickets[i].routeFk = this.route.id;
+ }
+
+ return this.$.possibleTicketsModel.save().then(() => {
+ this.$.model.data = this.$.model.data.concat(tickets);
+ });
+ }
+ return Promise.resolve();
+ }
}
-Controller.$inject = ['$stateParams', '$scope', '$translate', '$http', 'vnApp'];
+Controller.$inject = ['$stateParams', '$scope', '$translate', '$http', 'vnApp', '$filter'];
ngModule.component('vnRouteTickets', {
template: require('./index.html'),
+ controller: Controller,
require: {
card: '^vnRouteCard'
},
- controller: Controller
+ bindings: {
+ route: '<'
+ }
});
diff --git a/modules/route/front/tickets/index.spec.js b/modules/route/front/tickets/index.spec.js
new file mode 100644
index 000000000..96a9c4b45
--- /dev/null
+++ b/modules/route/front/tickets/index.spec.js
@@ -0,0 +1,288 @@
+import './index.js';
+
+describe('Route', () => {
+ let controller;
+ let $httpBackend;
+
+ beforeEach(angular.mock.module('route', $translateProvider => {
+ $translateProvider.translations('en', {});
+ }));
+
+ beforeEach(angular.mock.inject(($componentController, _$httpBackend_) => {
+ $httpBackend = _$httpBackend_;
+ controller = $componentController('vnRouteTickets');
+ }));
+
+ describe('route setter/getter', () => {
+ it('should return the route id', () => {
+ controller.route = 2;
+
+ expect(controller.route).toEqual(2);
+ });
+ });
+
+ describe('isChecked getter', () => {
+ it('should return false if none of the tickets is checked or there are no tickets', () => {
+ expect(controller.isChecked).toBeFalsy();
+ });
+
+ it('should return true if any of the tickets is checked', () => {
+ controller.tickets = [{checked: true}];
+
+ expect(controller.isChecked).toBeTruthy();
+ });
+ });
+
+ describe('buildPossibleTicketsFilter()', () => {
+ it('should build the possible tickets filter', () => {
+ let expectedFilter = {
+ include: [
+ {
+ relation: 'warehouse',
+ scope: {
+ fields: ['name']
+ }
+ }, {
+ relation: 'address'
+ }
+ ],
+ where: {
+ landed: {
+ between: [
+ jasmine.any(Date),
+ jasmine.any(Date)
+ ]
+ },
+ routeFk: null,
+ zoneFk: 67
+ }
+ };
+ controller.route = {
+ finished: new Date(),
+ routeFk: null,
+ zoneFk: 67
+ };
+
+ controller.buildPossibleTicketsFilter();
+
+ expect(controller.possibleTicketsFilter).toEqual(expectedFilter);
+ });
+ });
+
+ describe('getHighestPriority()', () => {
+ it('should return the highest value found in priorities plus 1', () => {
+ controller.$.model = {data: [
+ {priority: 99},
+ {priority: 1},
+ {priority: 2},
+ {priority: 3},
+ {priority: 4},
+ {priority: 5},
+ ]};
+
+ let result = controller.getHighestPriority();
+
+ expect(result).toEqual(100);
+ });
+ });
+
+ describe('setPriority()', () => {
+ it('should set a ticket priority', () => {
+ controller.$.model = {refresh: () => {}};
+ spyOn(controller.$.model, 'refresh');
+ spyOn(controller.vnApp, 'showSuccess');
+ const ticketId = 1;
+ const priority = 999;
+
+ $httpBackend.expectPATCH(`/api/Tickets/${ticketId}/`).respond('ok');
+ controller.setPriority(ticketId, priority);
+ $httpBackend.flush();
+
+ expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Data saved!');
+ expect(controller.$.model.refresh).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('getSelectedItems()', () => {
+ it('should return the selected items', () => {
+ let items = [
+ {id: 1, checked: true},
+ {id: 2, checked: false},
+ {id: 3, checked: true},
+ {id: 4, checked: false},
+ {id: 5, checked: true},
+ ];
+
+ let selectedItems = controller.getSelectedItems(items);
+
+ expect(selectedItems).toMatchSnapshot();
+ });
+ });
+
+ describe('goToBuscaman()', () => {
+ it('should open buscaman with the given arguments', () => {
+ spyOn(window, 'open');
+ const expectedUrl = 'http://gps.buscalia.com/usuario/localizar.aspx?bmi=true&addr=46460 Av Espioca 100-46460 Silla+to:n19 my street-n19 London';
+ controller.tickets = [
+ {
+ id: 1,
+ checked: true,
+ address: {
+ street: 'my street',
+ postalCode: 'n19',
+ city: 'London'
+ }
+ },
+ ];
+
+ controller.goToBuscaman();
+
+ expect(window.open).toHaveBeenCalledWith(expectedUrl, '_blank');
+ });
+ });
+
+ describe('showDeleteConfirm()', () => {
+ it('should open a confirm dialog after setting the selected ticket into the controller', () => {
+ controller.$.confirm = {show: () => {}};
+ spyOn(controller.$.confirm, 'show');
+ let ticketId = 1;
+
+ controller.showDeleteConfirm(ticketId);
+
+ expect(controller.selectedTicket).toEqual(ticketId);
+ expect(controller.$.confirm.show).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('removeTicketFromRoute()', () => {
+ it('should perform a patch query then call showSuccess and updateVolume methods', () => {
+ spyOn(controller, 'updateVolume');
+ spyOn(controller.vnApp, 'showSuccess');
+ let ticketId = 1;
+ controller.selectedTicket = ticketId;
+
+ $httpBackend.expectPATCH(`/api/Tickets/${ticketId}/`).respond('ok');
+ controller.removeTicketFromRoute('ACCEPT');
+ $httpBackend.flush();
+
+ expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Ticket removed from route');
+ expect(controller.updateVolume).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('updateVolume()', () => {
+ it('should perform a POST query then call both reload and refresh methods', () => {
+ controller.$.model = {refresh: () => {}};
+ controller.card = {reload: () => {}};
+ controller.$stateParamds = {id: 999};
+ spyOn(controller.$.model, 'refresh');
+ spyOn(controller.card, 'reload');
+
+ let ticketId = 1;
+ controller.selectedTicket = ticketId;
+
+ const url = `/route/api/Routes/${controller.$stateParams.id}/updateVolume`;
+ $httpBackend.expectPOST(url).respond('ok');
+ controller.updateVolume();
+ $httpBackend.flush();
+
+ expect(controller.$.model.refresh).toHaveBeenCalledWith();
+ expect(controller.card.reload).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('guessPriority()', () => {
+ it('should perform a GET query then call both refresh and showSuccess methods', () => {
+ controller.$.model = {refresh: () => {}};
+ spyOn(controller.$.model, 'refresh');
+ spyOn(controller.vnApp, 'showSuccess');
+ controller.$stateParamds = {id: 999};
+
+ const url = `/api/Routes/${controller.$stateParams.id}/guessPriority/`;
+ $httpBackend.expectGET(url).respond('ok');
+ controller.guessPriority();
+ $httpBackend.flush();
+
+ expect(controller.vnApp.showSuccess).toHaveBeenCalledWith('Order changed');
+ expect(controller.$.model.refresh).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('showTicketDescriptor()', () => {
+ it('should call the descriptor show function after setting the parent and the ticket id', () => {
+ controller.$.ticketDescriptor = {show: () => {}};
+ spyOn(controller.$.ticketDescriptor, 'show');
+ const event = {target: {}, preventDefault: () => {}};
+ spyOn(event, 'preventDefault');
+ const ticketId = 999;
+ controller.showTicketDescriptor(event, ticketId);
+
+ expect(controller.$.ticketDescriptor.ticketFk).toEqual(ticketId);
+ expect(controller.$.ticketDescriptor.show).toHaveBeenCalledWith();
+ expect(event.preventDefault).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('showClientDescriptor()', () => {
+ it('should call the descriptor show method after setting the parent and the client id', () => {
+ controller.$.clientDescriptor = {show: () => {}};
+ spyOn(controller.$.clientDescriptor, 'show');
+ const event = {target: {}, preventDefault: () => {}};
+ spyOn(event, 'preventDefault');
+ const clientId = 999;
+ controller.showClientDescriptor(event, clientId);
+
+ expect(controller.$.clientDescriptor.clientFk).toEqual(clientId);
+ expect(controller.$.clientDescriptor.show).toHaveBeenCalledWith();
+ expect(event.preventDefault).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('openPossibleTicketsDialog()', () => {
+ it('should call both refresh and show methods in posible tickets model and dialog', () => {
+ controller.$.possibleTicketsModel = {refresh: () => {}};
+ spyOn(controller.$.possibleTicketsModel, 'refresh');
+ controller.$.possibleTicketsDialog = {show: () => {}};
+ spyOn(controller.$.possibleTicketsDialog, 'show');
+
+ controller.openPossibleTicketsDialog();
+
+ expect(controller.$.possibleTicketsModel.refresh).toHaveBeenCalledWith();
+ expect(controller.$.possibleTicketsDialog.show).toHaveBeenCalledWith();
+ });
+ });
+
+ describe('setTicketsRoute()', () => {
+ it('should perform a POST query to add tickets to the route', done => {
+ controller.$.possibleTicketsModel = {save: () => {}};
+ spyOn(controller.$.possibleTicketsModel, 'save').and.returnValue(Promise.resolve());
+ controller.$.model = {data: [
+ {id: 1, checked: false}
+ ]};
+
+ controller.route = {id: 111};
+
+ controller.possibleTickets = [
+ {id: 2, checked: false},
+ {id: 3, checked: true},
+ {id: 4, checked: false},
+ {id: 5, checked: true},
+ ];
+
+ let expectedResult = [
+ {checked: false, id: 1},
+ {id: 3, routeFk: 111},
+ {id: 5, routeFk: 111}
+ ];
+
+ controller.setTicketsRoute('ACCEPT').then(() => {
+ expect(controller.$.model.data).toEqual(expectedResult);
+ done();
+ }).catch(done.fail);
+ });
+
+ it('should just return a promise', () => {
+ expect(controller.setTicketsRoute('CANCEL')).toEqual(jasmine.any(Promise));
+ });
+ });
+});
diff --git a/modules/route/front/tickets/locale/es.yml b/modules/route/front/tickets/locale/es.yml
index d668e0a8a..7edeff17b 100644
--- a/modules/route/front/tickets/locale/es.yml
+++ b/modules/route/front/tickets/locale/es.yml
@@ -3,4 +3,6 @@ Open buscaman: Abrir buscaman
Ticket removed from route: Ticket borrado de la ruta
Order changed: Orden cambiado
Delete ticket from route?: ¿Borrar ticket de la ruta?
-Sort routes: Ordenar rutas
\ No newline at end of file
+Sort routes: Ordenar rutas
+Add ticket: Añadir ticket
+Tickets to add: Tickets a añadir
\ No newline at end of file