diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 619e5ae2c..7d2da8658 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -351,16 +351,6 @@ INSERT INTO `vn`.`creditInsurance`(`id`, `creditClassification`, `credit`, `crea (2, 2, 6000, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), NULL), (3, 3, 10000, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), NULL); -INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agencyModeFk`, `description`, `m3`, `cost`, `started`, `finished`) - VALUES - (1, '1899-12-30 12:15:00', 56, CURDATE(), 1, 7, 'first route', 2.7, 10, CURDATE(), CURDATE()), - (2, '1899-12-30 13:20:00', 56, CURDATE(), 1, 7, 'second route', 0.9, 20, CURDATE(), CURDATE()), - (3, '1899-12-30 14:30:00', 56, CURDATE(), 2, 7, 'third route', 1.1, 30, CURDATE(), CURDATE()), - (4, '1899-12-30 15:45:00', 56, CURDATE(), 3, 7, 'fourth route', 0.1, 40, CURDATE(), CURDATE()), - (5, '1899-12-30 16:00:00', 56, CURDATE(), 4, 8, 'fifth route', NULL, 50, CURDATE(), CURDATE()), - (6, NULL, 57, CURDATE(), 5, 8, 'sixth route', NULL, 60, CURDATE(), CURDATE()), - (7, NULL, 57, CURDATE(), 6, NULL, 'seventh route', NULL, 70, CURDATE(), CURDATE()); - INSERT INTO `vn2008`.`empresa_grupo`(`empresa_grupo_id`, `grupo`) VALUES (1, 'Wayne Industries'); @@ -456,32 +446,42 @@ INSERT INTO `vn`.`zone` (`id`, `name`, `hour`, `warehouseFk`, `agencyModeFk`, `t (12, 'Zone entanglement', CONCAT(CURRENT_DATE(), ' ', TIME('22:00')), 4, 4, 0, 0, 0), (13, 'Zone quantum break', CONCAT(CURRENT_DATE(), ' ', TIME('22:00')), 5, 5, 0, 0, 0); +INSERT INTO `vn`.`route`(`id`, `time`, `workerFk`, `created`, `vehicleFk`, `agencyModeFk`, `description`, `m3`, `cost`, `started`, `finished`, `zoneFk`) + VALUES + (1, '1899-12-30 12:15:00', 56, CURDATE(), 1, 1, 'first route', 1.8, 10, CURDATE(), CURDATE(), 1), + (2, '1899-12-30 13:20:00', 56, CURDATE(), 1, 2, 'second route', 0.2, 20, CURDATE(), CURDATE(), 9), + (3, '1899-12-30 14:30:00', 56, CURDATE(), 2, 3, 'third route', 0.5, 30, CURDATE(), CURDATE(), 10), + (4, '1899-12-30 15:45:00', 56, CURDATE(), 3, 4, 'fourth route', 0, 40, CURDATE(), CURDATE(), 12), + (5, '1899-12-30 16:00:00', 56, CURDATE(), 4, 5, 'fifth route', 0.1, 50, CURDATE(), CURDATE(), 13), + (6, NULL, 57, CURDATE(), 5, 7, 'sixth route', 1.7, 60, CURDATE(), CURDATE(), 3), + (7, NULL, 57, CURDATE(), 6, 8, 'seventh route', 0, 70, CURDATE(), CURDATE(), 5); + INSERT INTO `vn`.`ticket`(`id`, `priority`, `agencyModeFk`,`warehouseFk`,`routeFk`, `shipped`, `landed`, `clientFk`,`nickname`, `addressFk`, `refFk`, `isDeleted`, `zoneFk`, `created`) VALUES - (1 , 3, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 101, 'Bat cave', 121, 'T1111111', 0, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), - (2 , 1, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T1111111', 0, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), - (3 , 1, 7, 1, 1, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T2222222', 0, 3, DATE_ADD(CURDATE(), INTERVAL -2 MONTH)), - (4 , 3, 2, 1, 1, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T3333333', 0, 9, DATE_ADD(CURDATE(), INTERVAL -3 MONTH)), - (5 , 3, 3, 3, 1, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T4444444', 0, 10, DATE_ADD(CURDATE(), INTERVAL -4 MONTH)), - (6 , 1, 3, 3, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 101, 'Mountain Drive Gotham', 1, 'A1111111', 0, 10, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), - (7 , NULL, 7, 1, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'Mountain Drive Gotham', 1, NULL, 0, 3, CURDATE()), - (8 , NULL, 7, 1, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'Bat cave', 121, NULL, 0, 3, CURDATE()), - (9 , NULL, 7, 1, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Stark tower', 124, NULL, 0, 3, CURDATE()), - (10, 1, 1, 5, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 102, 'Ingram Street', 2, NULL, 0, 11, CURDATE()), - (11, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 102, 'NY roofs', 122, NULL, 0, 3, CURDATE()), - (12, 1, 1, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 103, 'Phone Box', 123, NULL, 0, 1, CURDATE()), - (13, 1, 7, 1, 2, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 103, 'Phone Box', 123, NULL, 0, 1, CURDATE()), - (14, 1, 2, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Malibu Point', 4, NULL, 0, 9, CURDATE()), - (15, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 105, 'Plastic Cell', 125, NULL, 0, 3, CURDATE()), - (16, 1, 7, 1, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()), - (17, 1, 7, 2, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 106, 'Many Places', 126, NULL, 0, 3, CURDATE()), - (18, 1, 4, 4, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 108, 'Cerebro', 128, NULL, 0, 12, CURDATE()), - (19, 1, 5, 5, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 1, 13, CURDATE()), - (20, 1, 5, 5, 3, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), - (21, NULL, 5, 5, NULL, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Holland', 102, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), - (22, NULL, 5, 5, NULL, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Japan', 103, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), - (23, NULL, 10, 1, NULL, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'address 21', 121, NULL, 0, 8, CURDATE()), - (24 ,NULL, 10, 1, NULL, CURDATE(), CURDATE(), 101, 'Bruce Wayne', 1, NULL, 0, 8, CURDATE()); + (1 , 3, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 101, 'Bat cave', 121, 'T1111111', 0, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), + (2 , 1, 1, 1, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T1111111', 0, 1, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), + (3 , 1, 7, 1, 6, DATE_ADD(CURDATE(), INTERVAL -2 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -2 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T2222222', 0, 3, DATE_ADD(CURDATE(), INTERVAL -2 MONTH)), + (4 , 3, 2, 1, 2, DATE_ADD(CURDATE(), INTERVAL -3 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -3 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T3333333', 0, 9, DATE_ADD(CURDATE(), INTERVAL -3 MONTH)), + (5 , 3, 3, 3, 3, DATE_ADD(CURDATE(), INTERVAL -4 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -4 MONTH), INTERVAL +1 DAY), 104, 'Stark tower', 124, 'T4444444', 0, 10, DATE_ADD(CURDATE(), INTERVAL -4 MONTH)), + (6 , 1, 3, 3, 3, DATE_ADD(CURDATE(), INTERVAL -1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL -1 MONTH), INTERVAL +1 DAY), 101, 'Mountain Drive Gotham', 1, 'A1111111', 0, 10, DATE_ADD(CURDATE(), INTERVAL -1 MONTH)), + (7 , NULL, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'Mountain Drive Gotham', 1, NULL, 0, 3, CURDATE()), + (8 , NULL, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'Bat cave', 121, NULL, 0, 3, CURDATE()), + (9 , NULL, 7, 1, 6, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 104, 'Stark tower', 124, NULL, 0, 3, CURDATE()), + (10, 1, 1, 5, 1, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 102, 'Ingram Street', 2, NULL, 0, 1, CURDATE()), + (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()), + (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()), + (18, 1, 4, 4, 4, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 108, 'Cerebro', 128, NULL, 0, 12, CURDATE()), + (19, 1, 5, 5, 3, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 1, 13, CURDATE()), + (20, 1, 5, 5, 3, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Thailand', 129, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), + (21, NULL, 5, 5, 5, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Holland', 102, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), + (22, NULL, 5, 5, 5, DATE_ADD(CURDATE(), INTERVAL +1 MONTH), DATE_ADD(DATE_ADD(CURDATE(),INTERVAL +1 MONTH), INTERVAL +1 DAY), 109, 'Somewhere in Japan', 103, NULL, 0, 13, DATE_ADD(CURDATE(), INTERVAL +1 MONTH)), + (23, NULL, 8, 1, 7, CURDATE(), DATE_ADD(CURDATE(), INTERVAL + 1 DAY), 101, 'address 21', 121, NULL, 0, 5, CURDATE()), + (24 ,NULL, 8, 1, 7, CURDATE(), CURDATE(), 101, 'Bruce Wayne', 1, NULL, 0, 5, CURDATE()); INSERT INTO `vn`.`ticketObservation`(`id`, `ticketFk`, `observationTypeFk`, `description`) VALUES diff --git a/e2e/paths/03-worker-module/02_time_control.spec.js b/e2e/paths/03-worker-module/02_time_control.spec.js index 42be3e9e0..be48b13dd 100644 --- a/e2e/paths/03-worker-module/02_time_control.spec.js +++ b/e2e/paths/03-worker-module/02_time_control.spec.js @@ -358,7 +358,7 @@ describe('Worker time control path', () => { it(`should check Hank Pym doesn't have hours set on the next months first week`, async() => { const wholeWeekHours = await nightmare .waitToClick(selectors.workerTimeControl.nextMonthButton) - .waitToClick(selectors.workerTimeControl.nextMonthButton) + .waitForTextInElement(selectors.workerTimeControl.weekWorkedHours, '00:00 Hours') .waitToGetProperty(selectors.workerTimeControl.weekWorkedHours, 'innerText'); expect(wholeWeekHours).toEqual('00:00 Hours'); diff --git a/e2e/paths/05-ticket-module/06_basic_data_steps.spec.js b/e2e/paths/05-ticket-module/06_basic_data_steps.spec.js index 0f02c6af7..3dcac0765 100644 --- a/e2e/paths/05-ticket-module/06_basic_data_steps.spec.js +++ b/e2e/paths/05-ticket-module/06_basic_data_steps.spec.js @@ -30,6 +30,7 @@ describe('Ticket Edit basic data path', () => { it(`should confirm the zone autocomplete is enabled for the role productionBoss`, async() => { const disabled = await nightmare + .waitForSpinnerLoad() .wait(selectors.ticketBasicData.zoneAutocomplete) .evaluate(selector => { return document.querySelector(selector).disabled; diff --git a/front/core/components/treeview/child.html b/front/core/components/treeview/child.html new file mode 100644 index 000000000..1b928d5ea --- /dev/null +++ b/front/core/components/treeview/child.html @@ -0,0 +1,20 @@ +
+ + +
+
+ + + + +
+
diff --git a/front/core/components/treeview/content.js b/front/core/components/treeview/child.js similarity index 51% rename from front/core/components/treeview/content.js rename to front/core/components/treeview/child.js index 506117d4f..9e4edef35 100644 --- a/front/core/components/treeview/content.js +++ b/front/core/components/treeview/child.js @@ -2,22 +2,33 @@ import ngModule from '../../module'; class Controller { constructor($element, $scope, $compile) { - this.$element = $element; this.$scope = $scope; this.$compile = $compile; + this.element = $element[0]; + + this.element.$ctrl = this; + this.element.droppable = true; + this.dropCount = 0; + this.element.classList.add('vn-droppable'); } $onInit() { + const transcludeElement = this.element.querySelector('.content'); + const content = angular.element(transcludeElement); + if (this.item.parent) { this.treeview.$transclude(($clone, $scope) => { this.$contentScope = $scope; $scope.item = this.item; - this.$element.append($clone); + content.append($clone); }); + + this.element.draggable = true; + this.element.classList.add('vn-draggable'); } else { let template = `{{$ctrl.treeview.rootLabel}}`; let $clone = this.$compile(template)(this.$scope); - this.$element.append($clone); + content.append($clone); } } @@ -25,10 +36,21 @@ class Controller { if (this.$contentScope) this.$contentScope.$destroy(); } + + dragEnter() { + this.dropCount++; + + if (element != this.dropping) { + this.undrop(); + if (element) element.classList.add('dropping'); + this.dropping = element; + } + } } Controller.$inject = ['$element', '$scope', '$compile']; -ngModule.component('vnTreeviewContent', { +ngModule.component('vnTreeviewChild', { + template: require('./child.html'), controller: Controller, bindings: { item: '<' diff --git a/front/core/components/treeview/childs.html b/front/core/components/treeview/childs.html index 2dd7e77ef..de69ffb89 100644 --- a/front/core/components/treeview/childs.html +++ b/front/core/components/treeview/childs.html @@ -1,34 +1,11 @@ \ No newline at end of file diff --git a/front/core/components/treeview/childs.js b/front/core/components/treeview/childs.js index a9bd42077..d94c88df9 100644 --- a/front/core/components/treeview/childs.js +++ b/front/core/components/treeview/childs.js @@ -7,10 +7,6 @@ class Controller extends Component { event.preventDefault(); this.treeview.onToggle(item); } - - onDrop(item, dragged, dropped) { - this.treeview.onDrop(item, dragged, dropped); - } } ngModule.component('vnTreeviewChilds', { diff --git a/front/core/components/treeview/index.js b/front/core/components/treeview/index.js index d9da39215..728ea182e 100644 --- a/front/core/components/treeview/index.js +++ b/front/core/components/treeview/index.js @@ -1,18 +1,110 @@ import ngModule from '../../module'; import Component from '../../lib/component'; import './style.scss'; - import './childs'; -import './content'; +import './child'; /** * Treeview */ export default class Treeview extends Component { - constructor($element, $scope, $transclude) { + constructor($element, $scope, $transclude, $window) { super($element, $scope); this.$transclude = $transclude; + this.$window = $window; this.readOnly = true; + + this.element.addEventListener('dragstart', + event => this.dragStart(event)); + this.element.addEventListener('dragend', + event => this.dragEnd(event)); + + this.element.addEventListener('dragover', + event => this.dragOver(event)); + this.element.addEventListener('drop', + event => this.drop(event)); + this.element.addEventListener('dragenter', + event => this.dragEnter(event)); + this.element.addEventListener('dragleave', + event => this.dragLeave(event)); + + this.dropCount = 0; + } + + undrop() { + if (!this.dropping) return; + this.dropping.classList.remove('dropping'); + this.dropping = null; + } + + findDroppable(event) { + let element = event.target; + while (element != this.element && !element.droppable) + element = element.parentNode; + if (element == this.element) + return null; + return element; + } + + dragOver(event) { + this.dragClientY = event.clientY; + + // Prevents page reload + event.preventDefault(); + } + + onDragInterval() { + if (this.dragClientY > 0 && this.dragClientY < 75) + this.$window.scrollTo(0, this.$window.scrollY - 25); + } + + dragStart(event) { + event.target.classList.add('dragging'); + event.dataTransfer.setData('text', event.target.id); + + const element = this.findDroppable(event); + this.dragging = element; + + this.interval = setInterval(() => this.onDragInterval(), 100); + } + + dragEnd(event) { + event.target.classList.remove('dragging'); + this.undrop(); + this.dropCount = 0; + this.dragging = null; + clearInterval(this.interval); + } + + dragEnter(event) { + let element = this.findDroppable(event); + if (element) this.dropCount++; + + if (element != this.dropping) { + this.undrop(); + if (element) element.classList.add('dropping'); + this.dropping = element; + } + } + + dragLeave(event) { + let element = this.findDroppable(event); + + if (element) { + this.dropCount--; + if (this.dropCount == 0) this.undrop(); + } + } + + drop(event) { + event.preventDefault(); + this.element.classList.remove('dropping'); + + const $dropped = this.dropping.$ctrl.item; + const $dragged = this.dragging.$ctrl.item; + + if ($dropped != $dragged.parent) + this.emit('drop', {$dropped, $dragged}); } get data() { @@ -109,7 +201,10 @@ export default class Treeview extends Component { let childs = parent.childs; if (!childs) childs = []; - childs.push(item); + if (!parent.active) + this.unfold(parent); + else + childs.push(item); if (this.sortFunc) { childs = childs.sort((a, b) => @@ -120,12 +215,30 @@ export default class Treeview extends Component { if (parent) parent.sons++; } - onDrop(item, dragged, dropped) { - this.emit('drop', {item, dragged, dropped}); + move(item, newParent) { + if (newParent == item) return; + + if (item.parent) { + const parent = item.parent; + const childs = parent.childs; + const index = childs.indexOf(item); + parent.sons--; + + childs.splice(index, 1); + } + + item.parent = newParent; + + if (!newParent.active) { + this.unfold(newParent).then(() => { + item.parent.sons++; + }); + } else + this.create(item); } } -Treeview.$inject = ['$element', '$scope', '$transclude']; +Treeview.$inject = ['$element', '$scope', '$transclude', '$window']; ngModule.component('vnTreeview', { template: require('./index.html'), diff --git a/front/core/components/treeview/index.spec.js b/front/core/components/treeview/index.spec.js new file mode 100644 index 000000000..12cdc16e7 --- /dev/null +++ b/front/core/components/treeview/index.spec.js @@ -0,0 +1,260 @@ +describe('Component vnTreeview', () => { + let controller; + let $element; + + beforeEach(angular.mock.module('vnCore', $translateProvider => { + $translateProvider.translations('en', {}); + })); + + beforeEach(inject(($compile, $rootScope) => { + $element = $compile(``)($rootScope); + controller = $element.controller('vnTreeview'); + + const promise = new Promise(() => { + return {name: 'My item'}; + }); + + controller.fetchFunc = () => { + return promise; + }; + + controller.createFunc = () => { + return promise; + }; + + controller.removeFunc = () => { + return promise; + }; + + controller.sortFunc = () => { + return promise; + }; + })); + + afterEach(() => { + $element.remove(); + }); + + // See how to test DOM element in Jest + xdescribe('undrop()', () => { + it(`should reset all drop events and properties`, () => { + controller.dropping = angular.element(``); + spyOn(controller.dropping.classList, 'remove'); + + controller.undrop(); + + expect(controller.dropping).toBeNull(); + }); + }); + + describe('dragOver()', () => { + it(`should set the dragClientY property`, () => { + const event = new Event('dragover'); + event.clientY = 100; + + controller.dragOver(event); + + expect(controller.dragClientY).toEqual(100); + }); + }); + + describe('data() setter', () => { + it(`should set the items property nested into a root element`, () => { + const items = [{name: 'Item1'}, {name: 'Item2'}]; + controller.data = items; + + const rootItem = controller.items[0]; + + expect(rootItem.childs).toEqual(items); + }); + }); + + describe('fetch()', () => { + it(`should call the fetchFunc() method`, () => { + spyOn(controller, 'fetchFunc').and.returnValue( + new Promise(resolve => resolve([{name: 'My item'}])) + ); + controller.fetch().then(() => { + expect(controller.data).toBeDefined(); + }); + + expect(controller.fetchFunc).toHaveBeenCalledWith(); + }); + }); + + describe('setParent()', () => { + it(`should set the parent property recursively to each element of an item list`, () => { + spyOn(controller, 'setParent').and.callThrough(); + const items = [{name: 'Item1'}, {name: 'Item2', childs: [ + {name: 'Item3'} + ]}]; + const rootItem = {name: 'Nested tree', sons: items}; + controller.setParent(rootItem, items); + + expect(items[0].parent).toEqual(rootItem); + expect(items[1].parent).toEqual(rootItem); + expect(controller.setParent).toHaveBeenCalledWith(rootItem, items[1].childs); + }); + }); + + describe('onToggle()', () => { + it(`should call the fold() or unfold() methods`, () => { + spyOn(controller, 'fold'); + spyOn(controller, 'unfold'); + + const item = {name: 'My item'}; + + controller.onToggle(item); + item.active = true; + controller.onToggle(item); + + expect(controller.unfold).toHaveBeenCalledWith(item); + expect(controller.fold).toHaveBeenCalledWith(item); + }); + }); + + describe('fold()', () => { + it(`should remove the childs and set the active property to false`, () => { + const item = {name: 'My item', childs: [{name: 'Item 1'}], active: true}; + + controller.fold(item); + + expect(item.childs).toBeUndefined(); + expect(item.active).toBeFalsy(); + }); + }); + + describe('unfold()', () => { + it(`should unfold a parent item`, () => { + const expectedResponse = [{name: 'Item 1'}, {name: 'Item 2'}]; + spyOn(controller, 'fetchFunc').and.returnValue( + new Promise(resolve => resolve(expectedResponse)) + ); + spyOn(controller, 'setParent'); + spyOn(controller, 'sortFunc'); + const parent = {name: 'My item', sons: 1}; + const child = {name: 'Item 1'}; + child.parent = parent; + parent.childs = [child]; + + controller.unfold(parent).then(() => { + expect(controller.fetchFunc).toHaveBeenCalledWith({$item: parent}); + expect(controller.setParent).toHaveBeenCalledWith(parent, expectedResponse); + expect(controller.sortFunc).toHaveBeenCalledWith(jasmine.any(Object)); + expect(parent.active).toBeTruthy(); + }); + }); + }); + + describe('onRemove()', () => { + it(`should call the removeFunc() method`, () => { + spyOn(controller, 'removeFunc'); + const item = {name: 'My item'}; + controller.onRemove(item); + + expect(controller.removeFunc).toHaveBeenCalledWith({$item: item}); + }); + }); + + describe('remove()', () => { + it(`should remove a child element`, () => { + const parent = {name: 'My item', sons: 1}; + const child = {name: 'Item 1'}; + child.parent = parent; + parent.childs = [child]; + + controller.remove(child); + + expect(parent.childs).toEqual([]); + expect(parent.sons).toEqual(0); + }); + }); + + describe('onCreate()', () => { + it(`should call the createFunc() method`, () => { + spyOn(controller, 'createFunc'); + const parent = {name: 'My item'}; + controller.onCreate(parent); + + expect(controller.createFunc).toHaveBeenCalledWith({$parent: parent}); + }); + }); + + describe('create()', () => { + it(`should unfold an inactive parent and then create a child`, () => { + spyOn(controller, 'unfold'); + spyOn(controller, 'sortFunc'); + const parent = {name: 'My item', sons: 2, childs: [ + {name: 'Item 1'}, + {name: 'Item 2'} + ]}; + const child = {name: 'Item 3'}; + child.parent = parent; + parent.childs.push(child); + + controller.create(child); + + expect(parent.sons).toEqual(3); + expect(controller.unfold).toHaveBeenCalledWith(parent); + expect(controller.sortFunc).toHaveBeenCalledWith(jasmine.any(Object)); + expect(controller.sortFunc).toHaveBeenCalledTimes(2); + }); + + it(`should create a child on an active parent`, () => { + spyOn(controller, 'unfold'); + spyOn(controller, 'sortFunc'); + const parent = {name: 'My item', sons: 2, childs: [ + {name: 'Item 1'}, + {name: 'Item 2'} + ], active: true}; + const child = {name: 'Item 3'}; + child.parent = parent; + + controller.create(child); + + expect(parent.sons).toEqual(3); + expect(controller.unfold).not.toHaveBeenCalledWith(parent); + expect(controller.sortFunc).toHaveBeenCalledWith(jasmine.any(Object)); + expect(controller.sortFunc).toHaveBeenCalledTimes(2); + }); + }); + + describe('move()', () => { + it(`should move an item to anocher parent and then unfold the parent`, () => { + spyOn(controller, 'unfold').and.returnValue( + new Promise(resolve => resolve()) + ); + const newParent = {name: 'My item 2', sons: 0}; + const parent = {name: 'My item', sons: 3, childs: [ + {name: 'Item 1'}, + {name: 'Item 2'} + ]}; + const child = {name: 'Item 3'}; + child.parent = parent; + parent.childs.push(child); + + controller.move(child, newParent); + + expect(parent.sons).toEqual(2); + expect(controller.unfold).toHaveBeenCalledWith(newParent); + }); + + it(`should move an item to anocher parent`, () => { + spyOn(controller, 'unfold'); + spyOn(controller, 'create'); + const newParent = {name: 'My item 2', sons: 0, active: true}; + const parent = {name: 'My item', sons: 3, childs: [ + {name: 'Item 1'}, + {name: 'Item 2'} + ]}; + const child = {name: 'Item 3'}; + child.parent = parent; + parent.childs.push(child); + + controller.move(child, newParent); + + expect(parent.sons).toEqual(2); + expect(controller.unfold).not.toHaveBeenCalledWith(newParent); + }); + }); +}); diff --git a/front/core/components/treeview/style.scss b/front/core/components/treeview/style.scss index b3724a9f1..bd6175021 100644 --- a/front/core/components/treeview/style.scss +++ b/front/core/components/treeview/style.scss @@ -10,22 +10,6 @@ vn-treeview-childs { li { list-style: none; - & > .node { - @extend %clickable; - display: flex; - padding: 5px; - align-items: center; - } - - & > div > .arrow { - min-width: 24px; - margin-right: 10px; - transition: transform 200ms; - } - - & > div.expanded > .arrow { - transform: rotate(180deg); - } ul { padding-left: 2.2em; } @@ -45,8 +29,28 @@ vn-treeview-childs { .node:hover > .buttons { display: block } + + .content { + flex-grow: 1 + } } -vn-treeview-content { - flex-grow: 1 -} \ No newline at end of file +vn-treeview-child { + font-size: 16px; + display: block; + + .node { + @extend %clickable; + display: flex; + padding: 5px; + align-items: center; + } + & > div > .arrow { + min-width: 24px; + margin-right: 10px; + transition: transform 200ms; + } + &.expanded > div > .arrow { + transform: rotate(180deg); + } +} \ No newline at end of file diff --git a/front/core/directives/draggable.js b/front/core/directives/draggable.js deleted file mode 100644 index 3b68a6cb6..000000000 --- a/front/core/directives/draggable.js +++ /dev/null @@ -1,43 +0,0 @@ -import ngModule from '../module'; - -/** - * Enables a draggable element and his drag events - * - * @return {Object} The directive - */ -export function directive() { - return { - restrict: 'A', - link: function($scope, $element, $attrs) { - const element = $element[0]; - const isDraggable = $attrs.vnDraggable === 'true'; - - if (!isDraggable) return; - - // Set draggable style properties - element.style.cursor = 'move'; - - - // Enable as draggable element - element.setAttribute('draggable', true); - - /** - * Fires when a drag event starts - */ - element.addEventListener('dragstart', event => { - element.style.opacity = 0.5; - event.stopPropagation(); - }); - - /** - * Fires when a drag event ends - */ - element.addEventListener('dragend', event => { - element.style.opacity = 1; - event.stopPropagation(); - }); - } - }; -} - -ngModule.directive('vnDraggable', directive); diff --git a/front/core/directives/droppable.js b/front/core/directives/droppable.js index 483282418..929b64be7 100644 --- a/front/core/directives/droppable.js +++ b/front/core/directives/droppable.js @@ -1,68 +1,43 @@ import ngModule from '../module'; import './droppable.scss'; -export function directive($parse) { - return { - restrict: 'A', - link: function($scope, $element, $attrs) { - const element = $element[0]; - const onDropEvent = $parse($attrs.onDrop); - const isDroppable = $attrs.vnDroppable === 'true'; +class Controller { + constructor($element, $, $attrs) { + this.element = $element[0]; + this.$ = $; + this.$attrs = $attrs; - if (!isDroppable) return; + this.element.addEventListener('dragover', + event => event.preventDefault()); // Prevents page reload + this.element.addEventListener('drop', + event => this.drop(event)); + this.element.addEventListener('dragenter', + event => this.dragEnter(event)); + this.element.addEventListener('dragleave', + event => this.dragLeave(event)); + } - /** - * Captures current dragging element - */ - element.addEventListener('dragstart', () => { - this.dragged = element; - }); + dragEnter(event) { + this.droppedElement = event.target; + this.element.classList.add('dropping'); + } - /** - * Enter droppable area event - */ - element.addEventListener('dragenter', event => { - element.classList.add('active'); + dragLeave(event) { + if (this.droppedElement === event.target) + this.element.classList.remove('dropping'); + } - event.stopImmediatePropagation(); - event.preventDefault(); - }, false); - - - /** - * Exit droppable area event - */ - element.addEventListener('dragleave', event => { - element.classList.remove('active'); - - event.stopImmediatePropagation(); - event.preventDefault(); - }); - - /** - * Prevent dragover for allowing - * dispatch drop event - */ - element.addEventListener('dragover', event => { - event.stopPropagation(); - event.preventDefault(); - }); - - /** - * Fires when a drop events - */ - element.addEventListener('drop', event => { - element.classList.remove('active'); - - onDropEvent($scope, {event}); - - event.stopPropagation(); - event.preventDefault(); - }); - } - }; + drop(event) { + if (event.defaultPrevented) return; + event.preventDefault(); + this.element.classList.remove('dropping'); + this.$.$eval(this.$attrs.vnDroppable, {$event: event}); + } } +Controller.$inject = ['$element', '$scope', '$attrs']; -directive.$inject = ['$parse']; - -ngModule.directive('vnDroppable', directive); +ngModule.directive('vnDroppable', () => { + return { + controller: Controller + }; +}); diff --git a/front/core/directives/droppable.scss b/front/core/directives/droppable.scss index 749bc9a12..97e6f8a19 100644 --- a/front/core/directives/droppable.scss +++ b/front/core/directives/droppable.scss @@ -1,11 +1,25 @@ @import "./variables"; + +.vn-droppable, +.vn-draggable, [vn-droppable] { border: 2px dashed transparent; + border-radius: 0.5em; transition: all 0.5s; +} - &.active { +.vn-droppable, +[vn-droppable] { + display: block; + + &.dropping { background-color: $color-hover-cd; - border: 2px dashed $color-bg-dark; + border-color: $color-bg-dark; } +} + +.vn-draggable.dragging { + background-color: $color-main-light; + border-color: $color-main; } \ No newline at end of file diff --git a/front/core/directives/index.js b/front/core/directives/index.js index 05f21b5cd..185046a3c 100644 --- a/front/core/directives/index.js +++ b/front/core/directives/index.js @@ -11,7 +11,6 @@ import './bind'; import './repeat-last'; import './title'; import './uvc'; -import './draggable'; import './droppable'; import './http-click'; import './http-submit'; diff --git a/front/core/lib/event-emitter.js b/front/core/lib/event-emitter.js index 2dede42ab..022e4e98c 100644 --- a/front/core/lib/event-emitter.js +++ b/front/core/lib/event-emitter.js @@ -24,10 +24,12 @@ export default class EventEmitter { */ off(callback) { if (!this.$events) return; - for (let event in this.$events) - for (let i = 0; i < event.length; i++) + for (let event in this.$events) { + for (let i = 0; i < event.length; i++) { if (event[i].callback === callback) event.splice(i--, 1); + } + } } /** @@ -37,10 +39,12 @@ export default class EventEmitter { */ disconnect(thisArg) { if (!this.$events) return; - for (let event in this.$events) - for (let i = 0; i < event.length; i++) + for (let event in this.$events) { + for (let i = 0; i < event.length; i++) { if (event[i].thisArg === thisArg) event.splice(i--, 1); + } + } } /** @@ -72,9 +76,10 @@ export default class EventEmitter { if (this[prop]) this[prop].disconnect(this); this[prop] = value; - if (value) + if (value) { for (let event in handlers) value.on(event, handlers[event], this); + } } } } diff --git a/loopback/locale/es.json b/loopback/locale/es.json index 587d5e9c4..dffa15af6 100644 --- a/loopback/locale/es.json +++ b/loopback/locale/es.json @@ -109,6 +109,8 @@ "is invalid": "is invalid", "The postcode doesn't exists. Ensure you put the correct format": "El código postal no existe. Asegúrate de ponerlo con el formato correcto", "The department name can't be repeated": "El nombre del departamento no puede repetirse", - "You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado", - "This phone already exists": "Este teléfono ya existe" + "This phone already exists": "Este teléfono ya existe", + "You cannot move a parent to any of its sons": "You cannot move a parent to any of its sons", + "You cannot move a parent to its own sons": "You cannot move a parent to its own sons", + "You can't create a claim for a removed ticket": "No puedes crear una reclamación para un ticket eliminado" } \ No newline at end of file diff --git a/modules/agency/front/location/style.scss b/modules/agency/front/location/style.scss index a3b237b13..d1597e460 100644 --- a/modules/agency/front/location/style.scss +++ b/modules/agency/front/location/style.scss @@ -1,14 +1,14 @@ @import "variables"; -vn-treeview-content { - & > vn-check:not(.indeterminate) { +vn-treeview-child { + .content > vn-check:not(.indeterminate) { color: $color-main; & > .btn { border-color: $color-main; } } - & > vn-check.checked { + .content > vn-check.checked { color: $color-main; } } \ No newline at end of file diff --git a/modules/claim/front/action/index.html b/modules/claim/front/action/index.html index 65467d349..d12399c55 100644 --- a/modules/claim/front/action/index.html +++ b/modules/claim/front/action/index.html @@ -1,121 +1,116 @@ - - - - - -
- - -
-
- - - - - - - - - - - - - - - - - - Id - Ticket - Destination - Landed - Quantity - Description - Price - Disc. - Total - - - - - - - {{saleClaimed.sale.itemFk | zeroFill:6}} - - - - - {{::saleClaimed.sale.ticketFk}} + + + + + + + +
+ + + + + + + + + + +
+ + + + + + Id + Ticket + Destination + Landed + Quantity + Description + Price + Disc. + Total + + + + + + + {{::saleClaimed.sale.itemFk | zeroFill:6}} - - - - - - {{saleClaimed.sale.ticket.landed | dateTime: 'dd/MM/yyyy'}} - {{saleClaimed.sale.quantity}} - {{saleClaimed.sale.concept}} - {{saleClaimed.sale.price | currency: 'EUR':2}} - {{saleClaimed.sale.discount}} % - - {{saleClaimed.sale.quantity * saleClaimed.sale.price * - ((100 - saleClaimed.sale.discount) / 100) | currency: 'EUR':2}} - - - - - - - - -
-
+ + + + {{::saleClaimed.sale.ticketFk}} + + + + + + + {{::saleClaimed.sale.ticket.landed | dateTime: 'dd/MM/yyyy'}} + {{::saleClaimed.sale.quantity}} + {{::saleClaimed.sale.concept}} + {{::saleClaimed.sale.price | currency: 'EUR':2}} + {{::saleClaimed.sale.discount}} % + + {{saleClaimed.sale.quantity * saleClaimed.sale.price * + ((100 - saleClaimed.sale.discount) / 100) | currency: 'EUR':2}} + + + + + + + + + + - + diff --git a/modules/claim/front/action/style.scss b/modules/claim/front/action/style.scss index 3d54485e7..0124b716b 100644 --- a/modules/claim/front/action/style.scss +++ b/modules/claim/front/action/style.scss @@ -1,4 +1,19 @@ vn-claim-action { + .header { + display: flex; + justify-content: space-between; + align-items: center; + align-content: center; + + vn-tool-bar { + flex: 1 + } + + vn-check { + flex: none; + } + } + vn-dialog[vn-id=addSales] { tpl-body { width: 950px; diff --git a/modules/claim/front/dms/index/index.html b/modules/claim/front/dms/index/index.html index 89d1e0b22..fcf5c4346 100644 --- a/modules/claim/front/dms/index/index.html +++ b/modules/claim/front/dms/index/index.html @@ -5,7 +5,7 @@ data="$ctrl.photos"> -
+
Drag & Drop files here...
diff --git a/modules/claim/front/dms/index/index.js b/modules/claim/front/dms/index/index.js index 12d22bb2a..0aa48b7cc 100644 --- a/modules/claim/front/dms/index/index.js +++ b/modules/claim/front/dms/index/index.js @@ -36,8 +36,9 @@ class Controller { } } - onDrop(event) { - const files = event.dataTransfer.files; + onDrop($event) { + console.log($event); + const files = $event.dataTransfer.files; this.setDefaultParams().then(() => { this.dms.files = files; this.create(); diff --git a/modules/route/back/methods/route/specs/filter.spec.js b/modules/route/back/methods/route/specs/filter.spec.js index ba32c956c..a4742c128 100644 --- a/modules/route/back/methods/route/specs/filter.spec.js +++ b/modules/route/back/methods/route/specs/filter.spec.js @@ -117,6 +117,6 @@ describe('Route filter()', () => { let result = await app.models.Route.filter(ctx); - expect(result.length).toEqual(4); + expect(result.length).toEqual(1); }); }); diff --git a/modules/route/back/methods/route/specs/getTickets.spec.js b/modules/route/back/methods/route/specs/getTickets.spec.js index 3888156b6..15028309f 100644 --- a/modules/route/back/methods/route/specs/getTickets.spec.js +++ b/modules/route/back/methods/route/specs/getTickets.spec.js @@ -4,6 +4,6 @@ describe('route getTickets()', () => { it('should return the tickets for a given route', async() => { let result = await app.models.Route.getTickets(2); - expect(result.length).toEqual(4); + expect(result.length).toEqual(1); }); }); diff --git a/modules/route/back/methods/route/specs/guessPriority.spec.js b/modules/route/back/methods/route/specs/guessPriority.spec.js index cc536787a..075ea05c8 100644 --- a/modules/route/back/methods/route/specs/guessPriority.spec.js +++ b/modules/route/back/methods/route/specs/guessPriority.spec.js @@ -1,7 +1,7 @@ const app = require('vn-loopback/server/server'); describe('route guessPriority()', () => { - const targetRouteId = 2; + const targetRouteId = 7; let routeTicketsToRestore; afterAll(async done => { @@ -17,25 +17,23 @@ describe('route guessPriority()', () => { it('should confirm the tickets in the target route have no priority yet', async() => { routeTicketsToRestore = await app.models.Ticket.find({where: {routeFk: targetRouteId}}); - expect(routeTicketsToRestore.length).toEqual(4); + expect(routeTicketsToRestore.length).toEqual(2); + + expect(routeTicketsToRestore[0].id).toEqual(23); expect(routeTicketsToRestore[0].priority).toBeNull(); - expect(routeTicketsToRestore[0].id).toEqual(7); + expect(routeTicketsToRestore[1].id).toEqual(24); expect(routeTicketsToRestore[1].priority).toBeNull(); - expect(routeTicketsToRestore[1].id).toEqual(8); - expect(routeTicketsToRestore[2].priority).toBeNull(); - expect(routeTicketsToRestore[2].id).toEqual(9); }); it('should call guessPriority() and then check the tickets in the target route now have their priorities defined', async() => { await app.models.Route.guessPriority(targetRouteId); let routeTickets = await app.models.Ticket.find({where: {routeFk: targetRouteId}, fields: ['id', 'priority']}); - expect(routeTickets.length).toEqual(4); - expect(routeTickets[0].priority).toEqual(1); - expect(routeTickets[0].id).toEqual(7); - expect(routeTickets[1].priority).toEqual(3); - expect(routeTickets[1].id).toEqual(8); - expect(routeTickets[2].priority).toEqual(2); - expect(routeTickets[2].id).toEqual(9); + expect(routeTickets.length).toEqual(2); + + expect(routeTickets[0].id).toEqual(23); + expect(routeTickets[0].priority).toEqual(3); + expect(routeTickets[1].id).toEqual(24); + expect(routeTickets[1].priority).toEqual(1); }); }); diff --git a/modules/route/back/methods/route/specs/summary.spec.js b/modules/route/back/methods/route/specs/summary.spec.js index 5b31ff19f..ba976ae94 100644 --- a/modules/route/back/methods/route/specs/summary.spec.js +++ b/modules/route/back/methods/route/specs/summary.spec.js @@ -11,7 +11,7 @@ describe('route summary()', () => { const result = await app.models.Route.summary(1); const agency = result.route.agencyMode(); - expect(agency.name).toEqual('Silla247'); + expect(agency.name).toEqual('inhouse pickup'); }); it(`should return a summary object containing it's vehicle`, async() => { @@ -31,6 +31,6 @@ describe('route summary()', () => { it(`should return a summary object containing data from the tickets`, async() => { const result = await app.models.Route.summary(2); - expect(result.tickets.length).toEqual(4); + expect(result.tickets.length).toEqual(1); }); }); diff --git a/modules/route/back/methods/route/specs/updateVolume.spec.js b/modules/route/back/methods/route/specs/updateVolume.spec.js index 75ee3c139..a1ff67acc 100644 --- a/modules/route/back/methods/route/specs/updateVolume.spec.js +++ b/modules/route/back/methods/route/specs/updateVolume.spec.js @@ -10,8 +10,8 @@ describe('route updateVolume()', () => { afterAll(async done => { - await originalRoute.updateAttributes({m3: 2.7}); - await ticketToRestore.updateAttributes({routeFk: 2}); + await originalRoute.updateAttributes({m3: 1.8}); + await ticketToRestore.updateAttributes({routeFk: null}); await app.models.RouteLog.destroyById(logIdToDestroy); done(); }); @@ -19,12 +19,12 @@ describe('route updateVolume()', () => { it('should confirm the original volume of the route is the expected', async() => { originalRoute = await app.models.Route.findById(routeId); - expect(originalRoute.m3).toEqual(2.7); + expect(originalRoute.m3).toEqual(1.8); }); it('should confirm the route volume is updated when a ticket is added', async() => { - ticketToRestore = await app.models.Ticket.findById(8); - let updatedTicket = await app.models.Ticket.findById(8); + ticketToRestore = await app.models.Ticket.findById(14); + let updatedTicket = await app.models.Ticket.findById(14); await updatedTicket.updateAttributes({routeFk: routeId}); await app.models.Route.updateVolume(ctx, routeId); @@ -36,8 +36,9 @@ describe('route updateVolume()', () => { it('should confirm the change is logged', async() => { let logs = await app.models.RouteLog.find({fields: ['id', 'newInstance']}); + let m3Log = logs.filter(log => { - return log.newInstance.m3 === 3.1; + return log.newInstance.m3 === 1.9; }); logIdToDestroy = m3Log[0].id; diff --git a/modules/ticket/front/services/index.spec.js b/modules/ticket/front/services/index.spec.js index 97ce890d9..cf416f84d 100644 --- a/modules/ticket/front/services/index.spec.js +++ b/modules/ticket/front/services/index.spec.js @@ -1,6 +1,6 @@ import './index.js'; -fdescribe('Ticket component vnTicketService', () => { +describe('Ticket component vnTicketService', () => { let controller; let $httpBackend; let $httpParamSerializer; diff --git a/modules/worker/back/methods/department/moveChild.js b/modules/worker/back/methods/department/moveChild.js new file mode 100644 index 000000000..97206f198 --- /dev/null +++ b/modules/worker/back/methods/department/moveChild.js @@ -0,0 +1,42 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethod('moveChild', { + description: 'Changes the parent of a child department', + accessType: 'WRITE', + accepts: [{ + arg: 'id', + type: 'Number', + description: 'The department id', + http: {source: 'path'} + }, { + arg: 'parentId', + type: 'Number', + description: 'New parent id', + }], + returns: { + type: 'Object', + root: true + }, + http: { + path: `/:id/moveChild`, + verb: 'POST' + } + }); + + Self.moveChild = async(id, parentId = null) => { + const models = Self.app.models; + const child = await models.Department.findById(id); + + if (id == parentId) return; + + if (parentId) { + const parent = await models.Department.findById(parentId); + + if (child.lft < parent.lft && child.rgt > parent.rgt) + throw new UserError('You cannot move a parent to its own sons'); + } + + return child.updateAttribute('parentFk', parentId); + }; +}; diff --git a/modules/worker/back/methods/department/specs/createChild.spec.js b/modules/worker/back/methods/department/specs/createChild.spec.js new file mode 100644 index 000000000..305732f58 --- /dev/null +++ b/modules/worker/back/methods/department/specs/createChild.spec.js @@ -0,0 +1,18 @@ +const app = require('vn-loopback/server/server'); + +describe('department createChild()', () => { + let createdChild; + + afterAll(async done => { + await createdChild.destroy(); + done(); + }); + + it('should create a new child', async() => { + const parentId = null; + createdChild = await app.models.Department.createChild(parentId, 'My new department'); + + expect(createdChild.name).toEqual('My new department'); + expect(createdChild.parentFk).toBeNull(); + }); +}); diff --git a/modules/worker/back/methods/department/specs/moveChild.spec.js b/modules/worker/back/methods/department/specs/moveChild.spec.js new file mode 100644 index 000000000..3358ebf77 --- /dev/null +++ b/modules/worker/back/methods/department/specs/moveChild.spec.js @@ -0,0 +1,23 @@ +const app = require('vn-loopback/server/server'); + +describe('department moveChild()', () => { + let updatedChild; + + afterAll(async done => { + const child = await app.models.Department.findById(updatedChild.id); + await child.updateAttribute('parentFk', null); + done(); + }); + + it('should move a child department to a new parent', async() => { + const childId = 22; + const parentId = 1; + + const child = await app.models.Department.findById(childId); + + expect(child.parentFk).toBeNull(); + updatedChild = await app.models.Department.moveChild(childId, parentId); + + expect(updatedChild.parentFk).toEqual(1); + }); +}); diff --git a/modules/worker/back/methods/department/specs/removeChild.spec.js b/modules/worker/back/methods/department/specs/removeChild.spec.js new file mode 100644 index 000000000..1fe3d10ef --- /dev/null +++ b/modules/worker/back/methods/department/specs/removeChild.spec.js @@ -0,0 +1,21 @@ +const app = require('vn-loopback/server/server'); + +describe('department removeChild()', () => { + let removedChild; + + afterAll(async done => { + await app.models.Department.create(removedChild); + done(); + }); + + it('should remove a child department', async() => { + const childId = 1; + + removedChild = await app.models.Department.findById(childId); + const result = await app.models.Department.removeChild(childId); + const existsChild = await app.models.Department.findById(childId); + + expect(result.count).toEqual(1); + expect(existsChild).toBeNull(); + }); +}); diff --git a/modules/worker/back/models/department.js b/modules/worker/back/models/department.js index e6905d273..5a927fc64 100644 --- a/modules/worker/back/models/department.js +++ b/modules/worker/back/models/department.js @@ -2,4 +2,5 @@ module.exports = Self => { require('../methods/department/getLeaves')(Self); require('../methods/department/createChild')(Self); require('../methods/department/removeChild')(Self); + require('../methods/department/moveChild')(Self); }; diff --git a/modules/worker/back/models/department.json b/modules/worker/back/models/department.json index bb5d5e943..7de76e039 100644 --- a/modules/worker/back/models/department.json +++ b/modules/worker/back/models/department.json @@ -16,6 +16,15 @@ }, "parentFk": { "type": "Number" + }, + "lft": { + "type": "Number" + }, + "rgt": { + "type": "Number" + }, + "sons": { + "type": "Number" } } } diff --git a/modules/worker/front/department/index.html b/modules/worker/front/department/index.html index 5093fe570..2abb81b04 100644 --- a/modules/worker/front/department/index.html +++ b/modules/worker/front/department/index.html @@ -10,7 +10,10 @@ fetch-func="$ctrl.onFetch($item)" remove-func="$ctrl.onRemove($item)" create-func="$ctrl.onCreate($parent)" - sort-func="$ctrl.onSort($a, $b)"> + sort-func="$ctrl.onSort($a, $b)" + on-drop="$ctrl.onDrop($dropped, $dragged)" + on-drag-start="$ctrl.onDragStart(item)" + on-drag-end="$ctrl.onDragEnd(item)"> {{::item.name}} diff --git a/modules/worker/front/department/index.js b/modules/worker/front/department/index.js index 1a72681bc..383fee74b 100644 --- a/modules/worker/front/department/index.js +++ b/modules/worker/front/department/index.js @@ -23,19 +23,13 @@ class Controller { return a.name.localeCompare(b.name); } - /* onDrop(item, dragged, dropped) { - if (dropped.scope.item) { - const droppedItem = dropped.scope.item; - const draggedItem = dragged.scope.item; - - if (droppedItem.childs) - droppedItem.childs.push(Object.assign({}, draggedItem)); - - dragged.element.remove(); - - this.$scope.$apply(); - } - } */ + onDrop(dropped, dragged) { + const params = dropped ? {parentId: dropped.id} : null; + const query = `/api/departments/${dragged.id}/moveChild`; + this.$http.post(query, params).then(() => { + this.$.treeview.move(dragged, dropped); + }); + } onCreate(parent) { this.newChild = { @@ -62,12 +56,8 @@ class Controller { if (parent && parent.id) params.parentId = parent.id; - if (!parent.active) - this.$.treeview.unfold(parent); - const query = `/api/departments/createChild`; this.$http.post(query, params).then(res => { - const parent = this.newChild.parent; const item = res.data; item.parent = parent;