diff --git a/db/changes/10480-june/00-aclZoneExclusionGeos.sql b/db/changes/10480-june/00-aclZoneExclusionGeos.sql new file mode 100644 index 000000000..4c0f6c991 --- /dev/null +++ b/db/changes/10480-june/00-aclZoneExclusionGeos.sql @@ -0,0 +1,4 @@ +INSERT INTO `salix`.`ACL`(`model`, `property`, `accessType`, `permission`, `principalType`, `principalId`) + VALUES + ('ZoneExclusionGeo', '*', 'READ', 'ALLOW', 'ROLE', 'employee'), + ('ZoneExclusionGeo', '*', 'WRITE', 'ALLOW', 'ROLE', 'deliveryBoss'); \ No newline at end of file diff --git a/db/dump/fixtures.sql b/db/dump/fixtures.sql index 0609a6a6a..239137a9c 100644 --- a/db/dump/fixtures.sql +++ b/db/dump/fixtures.sql @@ -2582,6 +2582,15 @@ INSERT INTO `vn`.`machineWorker` (`workerFk`, `machineFk`, `inTimed`, `outTimed` (1106, 2, util.VN_CURDATE(), NULL), (1106, 2, DATE_ADD(util.VN_CURDATE(), INTERVAL + 1 DAY), DATE_ADD(util.VN_CURDATE(), INTERVAL +1 DAY)); +INSERT INTO `vn`.`zoneExclusion` (`id`, `zoneFk`, `dated`, `created`, `userFk`) +VALUES + (1, 1, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=7, 7, 14) - DAYOFWEEK(CURDATE())) DAY), CURDATE(), 100), + (2, 1, DATE_ADD(CURDATE(), INTERVAL (IF(DAYOFWEEK(CURDATE())<=8, 8, 15) - DAYOFWEEK(CURDATE())) DAY), CURDATE(), 100); + +INSERT INTO `vn`.`zoneExclusionGeo` (`zoneExclusionFk`, `geoFk`) + VALUES + (2, 1); + INSERT INTO `vn`.`mdbBranch` (`name`) VALUES ('test'), @@ -2610,4 +2619,4 @@ INSERT INTO `vn`.`sectorCollectionSaleGroup` (`sectorCollectionFk`, `saleGroupFk INSERT INTO `vn`.`workerTimeControlConfig` (`id`, `dayBreak`, `dayBreakDriver`, `shortWeekBreak`, `longWeekBreak`, `weekScope`, `mailPass`, `mailHost`, `mailSuccessFolder`, `mailErrorFolder`, `mailUser`, `minHoursToBreak`, `breakHours`, `hoursCompleteWeek`, `startNightlyHours`, `endNightlyHours`, `maxTimePerDay`, `breakTime`, `timeToBreakTime`, `dayMaxTime`, `shortWeekDays`, `longWeekDays`) VALUES - (1, 43200, 32400, 129600, 259200, 604800, '', '', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.33, 0.33, 40, '22:00:00', '06:00:00', 57600, 1200, 18000, 57600, 6, 13); \ No newline at end of file + (1, 43200, 32400, 129600, 259200, 604800, '', '', 'Leidos.exito', 'Leidos.error', 'timeControl', 5.33, 0.33, 40, '22:00:00', '06:00:00', 57600, 1200, 18000, 57600, 6, 13); diff --git a/modules/zone/back/methods/zone/exclusionGeo.js b/modules/zone/back/methods/zone/exclusionGeo.js new file mode 100644 index 000000000..5026c58c5 --- /dev/null +++ b/modules/zone/back/methods/zone/exclusionGeo.js @@ -0,0 +1,64 @@ + +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethod('exclusionGeo', { + description: 'Exclude a geo from a zone', + accepts: [ + { + arg: 'zoneFk', + type: 'number', + description: 'The zone id' + }, + { + arg: 'date', + type: 'date', + description: 'The date to exclude' + }, + { + arg: 'geoIds', + type: ['number'], + description: 'The geos id' + } + + ], + returns: { + type: 'object', + root: true + }, + http: { + path: `/exclusionGeo`, + verb: 'POST' + } + }); + + Self.exclusionGeo = async(zoneFk, date, geoIds, options) => { + const models = Self.app.models; + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!geoIds[0]) throw new UserError(`You must select a location`); + + const newZoneExclusion = await models.ZoneExclusion.create({ + zoneFk: zoneFk, + dated: date + }, myOptions); + + const promises = []; + + for (const geoId of geoIds) { + const newZoneExclusionGeo = await models.ZoneExclusionGeo.create({ + zoneExclusionFk: newZoneExclusion.id, + geoFk: geoId + }, myOptions); + + promises.push(newZoneExclusionGeo); + } + + const newZoneExclusionGeos = await Promise.all(promises); + + return newZoneExclusionGeos; + }; +}; diff --git a/modules/zone/back/methods/zone/getEventsFiltered.js b/modules/zone/back/methods/zone/getEventsFiltered.js index 316652fa3..b7875785d 100644 --- a/modules/zone/back/methods/zone/getEventsFiltered.js +++ b/modules/zone/back/methods/zone/getEventsFiltered.js @@ -56,12 +56,23 @@ module.exports = Self => { [zoneFk, started, ended, started, ended, started, ended, started, ended], myOptions); query = ` - SELECT * - FROM vn.zoneExclusion - WHERE zoneFk = ? - AND dated BETWEEN ? AND ?;`; + SELECT e.* + FROM vn.zoneExclusion e + LEFT JOIN vn.zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id + WHERE e.zoneFk = ? + AND e.dated BETWEEN ? AND ? + AND eg.zoneExclusionFk IS NULL;`; const exclusions = await Self.rawSql(query, [zoneFk, started, ended], myOptions); - return {events, exclusions}; + query = ` + SELECT eg.*, e.zoneFk, e.dated, e.created, e.userFk + FROM vn.zoneExclusion e + LEFT JOIN vn.zoneExclusionGeo eg ON eg.zoneExclusionFk = e.id + WHERE e.zoneFk = ? + AND e.dated BETWEEN ? AND ? + AND eg.zoneExclusionFk IS NOT NULL;`; + const geoExclusions = await Self.rawSql(query, [zoneFk, started, ended], myOptions); + + return {events, exclusions, geoExclusions}; }; }; diff --git a/modules/zone/back/methods/zone/specs/exclusionGeo.spec.js b/modules/zone/back/methods/zone/specs/exclusionGeo.spec.js new file mode 100644 index 000000000..3a345f2ce --- /dev/null +++ b/modules/zone/back/methods/zone/specs/exclusionGeo.spec.js @@ -0,0 +1,41 @@ +const models = require('vn-loopback/server/server').models; + +describe('zone exclusionGeo()', () => { + const zoneId = 1; + const today = new Date(); + + it(`should show an error when location isn't selected`, async() => { + const tx = await models.Zone.beginTransaction({}); + + try { + const options = {transaction: tx}; + const geoIds = []; + + await models.Zone.exclusionGeo(zoneId, today, geoIds, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toContain(`You must select a location`); + }); + + it('should create two exclusion by geo', async() => { + const tx = await models.Zone.beginTransaction({}); + + try { + const options = {transaction: tx}; + const geoIds = [1, 2]; + const result = await models.Zone.exclusionGeo(zoneId, today, geoIds, options); + + expect(result.length).toEqual(2); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); diff --git a/modules/zone/back/methods/zone/specs/updateExclusionGeo.spec.js b/modules/zone/back/methods/zone/specs/updateExclusionGeo.spec.js new file mode 100644 index 000000000..9db2e24be --- /dev/null +++ b/modules/zone/back/methods/zone/specs/updateExclusionGeo.spec.js @@ -0,0 +1,40 @@ +const models = require('vn-loopback/server/server').models; + +describe('zone updateExclusionGeo()', () => { + it(`should show an error when location isn't selected`, async() => { + const tx = await models.Zone.beginTransaction({}); + + try { + const options = {transaction: tx}; + const zoneId = 1; + const geoIds = []; + + await models.Zone.updateExclusionGeo(zoneId, geoIds, options); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + error = e; + } + + expect(error.message).toContain(`You must select a location`); + }); + + it('should delete all exclusion and then create two exclusion by geo for a zone', async() => { + const tx = await models.Zone.beginTransaction({}); + + try { + const options = {transaction: tx}; + const zoneId = 2; + const geoIds = [1, 2]; + const result = await models.Zone.updateExclusionGeo(zoneId, geoIds, options); + + expect(result.length).toEqual(2); + + await tx.rollback(); + } catch (e) { + await tx.rollback(); + throw e; + } + }); +}); diff --git a/modules/zone/back/methods/zone/updateExclusionGeo.js b/modules/zone/back/methods/zone/updateExclusionGeo.js new file mode 100644 index 000000000..237e336e0 --- /dev/null +++ b/modules/zone/back/methods/zone/updateExclusionGeo.js @@ -0,0 +1,55 @@ +const UserError = require('vn-loopback/util/user-error'); + +module.exports = Self => { + Self.remoteMethod('updateExclusionGeo', { + description: 'Update the geos excluded from a zone', + accepts: [ + { + arg: 'zoneExclusionFk', + type: 'number', + description: 'The zoneExclusion id' + }, + { + arg: 'geoIds', + type: ['number'], + description: 'The geos id' + } + + ], + returns: { + type: 'object', + root: true + }, + http: { + path: `/updateExclusionGeo`, + verb: 'POST' + } + }); + + Self.updateExclusionGeo = async(zoneExclusionFk, geoIds, options) => { + const models = Self.app.models; + const myOptions = {}; + + if (typeof options == 'object') + Object.assign(myOptions, options); + + if (!geoIds[0]) throw new UserError(`You must select a location`); + + await models.ZoneExclusionGeo.destroyAll({ + zoneExclusionFk: zoneExclusionFk + }, myOptions); + + const promises = []; + + for (const geoId of geoIds) { + const params = { + zoneExclusionFk: zoneExclusionFk, + geoFk: geoId + }; + const deletedZoneExclusionGeos = models.ZoneExclusionGeo.create(params, myOptions); + promises.push(deletedZoneExclusionGeos); + } + + return Promise.all(promises); + }; +}; diff --git a/modules/zone/back/model-config.json b/modules/zone/back/model-config.json index ee555f3f4..261a89902 100644 --- a/modules/zone/back/model-config.json +++ b/modules/zone/back/model-config.json @@ -23,6 +23,9 @@ "ZoneExclusion": { "dataSource": "vn" }, + "ZoneExclusionGeo": { + "dataSource": "vn" + }, "ZoneGeo": { "dataSource": "vn" }, diff --git a/modules/zone/back/models/zone-exclusion-geo.json b/modules/zone/back/models/zone-exclusion-geo.json new file mode 100644 index 000000000..816e4b650 --- /dev/null +++ b/modules/zone/back/models/zone-exclusion-geo.json @@ -0,0 +1,21 @@ +{ + "name": "ZoneExclusionGeo", + "base": "VnModel", + "options": { + "mysql": { + "table": "zoneExclusionGeo" + } + }, + "properties": { + "id": { + "id": true, + "type": "number" + }, + "zoneExclusionFk": { + "type": "number" + }, + "geoFk": { + "type": "number" + } + } +} \ No newline at end of file diff --git a/modules/zone/back/models/zone.js b/modules/zone/back/models/zone.js index ef1c8c5d9..6d5a6cdca 100644 --- a/modules/zone/back/models/zone.js +++ b/modules/zone/back/models/zone.js @@ -8,6 +8,8 @@ module.exports = Self => { require('../methods/zone/deleteZone')(Self); require('../methods/zone/includingExpired')(Self); require('../methods/zone/getZoneClosing')(Self); + require('../methods/zone/exclusionGeo')(Self); + require('../methods/zone/updateExclusionGeo')(Self); Self.validatesPresenceOf('agencyModeFk', { message: `Agency cannot be blank` diff --git a/modules/zone/front/calendar/index.js b/modules/zone/front/calendar/index.js index a24d10aef..3bc7158ef 100644 --- a/modules/zone/front/calendar/index.js +++ b/modules/zone/front/calendar/index.js @@ -75,6 +75,17 @@ class Controller extends Component { } } + this.geoExclusions = {}; + let geoExclusions = value.geoExclusions; + + if (geoExclusions) { + for (let geoExclusion of geoExclusions) { + let stamp = toStamp(geoExclusion.dated); + if (!this.geoExclusions[stamp]) this.geoExclusions[stamp] = []; + this.geoExclusions[stamp].push(geoExclusion); + } + } + let events = value.events; if (events) { @@ -135,11 +146,13 @@ class Controller extends Component { onSelection($event, $days, $type, $weekday) { let $events = []; let $exclusions = []; + let $geoExclusions = []; for (let day of $days) { let stamp = day.getTime(); $events = $events.concat(this.days[stamp] || []); $exclusions = $exclusions.concat(this.exclusions[stamp] || []); + $geoExclusions = $geoExclusions.concat(this.geoExclusions[stamp] || []); } this.emit('selection', { @@ -148,19 +161,23 @@ class Controller extends Component { $type, $weekday, $events, - $exclusions + $exclusions, + $geoExclusions }); } hasEvents(day) { let stamp = day.getTime(); - return this.days[stamp] || this.exclusions[stamp]; + return this.days[stamp] || this.exclusions[stamp] || this.geoExclusions[stamp]; } getClass(day) { let stamp = day.getTime(); - return this.exclusions[stamp] && !this.days[stamp] - ? 'excluded' : ''; + if (this.geoExclusions[stamp]) + return 'geoExcluded'; + else if (this.exclusions[stamp]) + return 'excluded'; + else return ''; } } Controller.$inject = ['$element', '$scope', 'vnWeekDays']; diff --git a/modules/zone/front/calendar/index.spec.js b/modules/zone/front/calendar/index.spec.js index be002925e..338f47917 100644 --- a/modules/zone/front/calendar/index.spec.js +++ b/modules/zone/front/calendar/index.spec.js @@ -15,6 +15,7 @@ describe('component vnZoneCalendar', () => { controller.zone = {id: 1}; controller.days = []; controller.exclusions = []; + controller.geoExclusions = []; })); describe('date() setter', () => { @@ -57,7 +58,7 @@ describe('component vnZoneCalendar', () => { }); describe('data() setter', () => { - it('should set the events and exclusions and then call the refreshEvents() method', () => { + it('should set the events, exclusions and geoExclusions and then call the refreshEvents() method', () => { jest.spyOn(controller, 'refreshEvents').mockReturnThis(); controller.data = { @@ -66,13 +67,17 @@ describe('component vnZoneCalendar', () => { }], events: [{ dated: new Date() - }] + }], + geoExclusions: [{ + dated: new Date() + }], }; expect(controller.refreshEvents).toHaveBeenCalledWith(); expect(controller.events).toBeDefined(); expect(controller.events.length).toEqual(1); expect(controller.exclusions).toBeDefined(); + expect(controller.geoExclusions).toBeDefined(); expect(Object.keys(controller.exclusions).length).toEqual(1); }); }); @@ -122,7 +127,8 @@ describe('component vnZoneCalendar', () => { $events: [], $exclusions: [], $type: 'day', - $weekday: 1 + $weekday: 1, + $geoExclusions: [], } ); }); @@ -151,5 +157,16 @@ describe('component vnZoneCalendar', () => { expect(result).toEqual('excluded'); }); + + it('should return the className "geoExcluded" for a date with geo excluded', () => { + const dated = new Date(); + + controller.geoExclusions = []; + controller.geoExclusions[dated.getTime()] = true; + + const result = controller.getClass(dated); + + expect(result).toEqual('geoExcluded'); + }); }); }); diff --git a/modules/zone/front/calendar/style.scss b/modules/zone/front/calendar/style.scss index 25b6a87d1..38491af58 100644 --- a/modules/zone/front/calendar/style.scss +++ b/modules/zone/front/calendar/style.scss @@ -33,6 +33,9 @@ vn-zone-calendar { &.excluded .day-number { background-color: $color-alert; } + &.geoExcluded .day-number { + background-color: $color-main; + } } } } diff --git a/modules/zone/front/events/index.html b/modules/zone/front/events/index.html index e71a1ae26..46ba87dea 100644 --- a/modules/zone/front/events/index.html +++ b/modules/zone/front/events/index.html @@ -2,7 +2,7 @@ id="calendar" vn-id="calendar" data="data" - on-selection="$ctrl.onSelection($days, $type, $weekday, $events, $exclusions)" + on-selection="$ctrl.onSelection($days, $type, $weekday, $events, $exclusions, $geoExclusions)" on-step="$ctrl.refresh()" class="vn-w-md"> @@ -98,7 +98,7 @@ fixed-bottom-right> @@ -198,3 +198,80 @@ message="This item will be deleted" question="Are you sure you want to continue?"> + + + + + + + + + + + + + +
+ + + + + +
+ + + + +
+
+
+
+ + + + + + + +
diff --git a/modules/zone/front/events/index.js b/modules/zone/front/events/index.js index 0df16a42a..b86330126 100644 --- a/modules/zone/front/events/index.js +++ b/modules/zone/front/events/index.js @@ -1,5 +1,6 @@ import ngModule from '../module'; import Section from 'salix/components/section'; +import './style.scss'; class Controller extends Section { constructor($element, $, vnWeekDays) { @@ -20,6 +21,16 @@ class Controller extends Section { return `Zones/${this.$params.id}/exclusions`; } + get checked() { + const geos = this.$.model.data || []; + const checkedLines = []; + for (let geo of geos) { + if (geo.checked) + checkedLines.push(geo); + } + return checkedLines; + } + refresh() { this.$.data = null; this.$.$applyAsync(() => { @@ -48,33 +59,56 @@ class Controller extends Section { : this.$t('Everyday'); } - onSelection(days, type, weekday, events, exclusions) { + onSelection(days, type, weekday, events, exclusions, exclusionGeos) { if (this.editMode == 'include') { if (events.length) - this.edit(events[0]); - else - this.create(type, days, weekday); - } else { - if (exclusions.length) - this.exclusionDelete(exclusions); - else - this.exclusionCreate(days); + return this.editInclusion(events[0]); + return this.createInclusion(type, days, weekday); + } else if (this.editMode == 'exclude') { + if (exclusions.length || exclusionGeos.length) + return this.editExclusion(exclusions[0] || {}, exclusionGeos); + return this.createExclusion(days); } } + editExclusion(exclusion, exclusionGeos) { + this.isNew = false; + this.excludeSelected = angular.copy(exclusion); + this.excludeSelected.type = exclusionGeos.length ? + 'specificLocations' : 'all'; + + this.exclusionGeos = new Set(); + if (exclusionGeos.length) { + this.excludeSelected.id = exclusionGeos[0].zoneExclusionFk; + exclusionGeos.forEach(x => this.exclusionGeos.add(x.geoFk)); + } + + this.$.excludeDialog.show(); + } + + createExclusion(days) { + this.isNew = true; + this.excludeSelected = { + type: 'all', + dated: days[0] + }; + this.exclusionGeos = new Set(); + this.$.excludeDialog.show(); + } + onEditClick(row, event) { if (event.defaultPrevented) return; - this.edit(row); + this.editInclusion(row); } - edit(row) { + editInclusion(row) { this.isNew = false; this.selected = angular.copy(row); this.selected.wdays = this.vnWeekDays.fromSet(row.weekDays); - this.$.dialog.show(); + this.$.includeDialog.show(); } - create(type, days, weekday) { + createInclusion(type, days, weekday) { this.isNew = true; if (type == 'weekday') { @@ -92,7 +126,7 @@ class Controller extends Section { }; } - this.$.dialog.show(); + this.$.includeDialog.show(); } onIncludeResponse(response) { @@ -132,6 +166,19 @@ class Controller extends Section { } } + onExcludeResponse(response) { + const type = this.excludeSelected.type; + switch (response) { + case 'accept': { + if (type == 'all') + return this.exclusionCreate(); + return this.exclusionGeoCreate(); + } + case 'delete': + return this.exclusionDelete(this.excludeSelected); + } + } + onDeleteClick(id, event) { if (event.defaultPrevented) return; event.preventDefault(); @@ -149,31 +196,121 @@ class Controller extends Section { .then(() => this.refresh()); } - exclusionCreate(days) { - let exclusions = days.map(dated => { - return {dated}; - }); + exclusionCreate() { + const excludeSelected = this.excludeSelected; + const dated = excludeSelected.dated; + let req; - this.$http.post(this.exclusionsPath, exclusions) + if (this.isNew) + req = this.$http.post(this.exclusionsPath, [{dated}]); + if (!this.isNew) + req = this.$http.put(`${this.exclusionsPath}/${excludeSelected.id}`, {dated}); + + return req.then(() => { + this.refresh(); + }); + } + + exclusionGeoCreate() { + const excludeSelected = this.excludeSelected; + let req; + const geoIds = []; + this.exclusionGeos.forEach(id => geoIds.push(id)); + + if (this.isNew) { + const params = { + zoneFk: parseInt(this.$params.id), + date: excludeSelected.dated, + geoIds + }; + req = this.$http.post(`Zones/exclusionGeo`, params); + } else { + const params = { + zoneExclusionFk: this.excludeSelected.id, + geoIds + }; + req = this.$http.post(`Zones/updateExclusionGeo`, params); + } + return req.then(() => this.refresh()); + } + + exclusionDelete(exclusion) { + const path = `${this.exclusionsPath}/${exclusion.id}`; + return this.$http.delete(path) .then(() => this.refresh()); } - exclusionDelete(exclusions) { - let reqs = []; + set excludeSearch(value) { + this._excludeSearch = value; + if (!value) this.onSearch(); + } - for (let exclusion of exclusions) { - if (!exclusion.id) continue; - let path = `${this.exclusionsPath}/${exclusion.id}`; - reqs.push(this.$http.delete(path)); + get excludeSearch() { + return this._excludeSearch; + } + + onKeyDown(event) { + if (event.key == 'Enter') { + event.preventDefault(); + this.onSearch(); + } + } + + onSearch() { + const params = {search: this._excludeSearch}; + if (this.excludeSelected.type == 'specificLocations') { + this.$.model.applyFilter({}, params).then(() => { + const data = this.$.model.data; + this.getChecked(data); + this.$.treeview.data = data; + }); + } + } + + onFetch(item) { + const params = item ? {parentId: item.id} : null; + return this.$.model.applyFilter({}, params).then(() => { + const data = this.$.model.data; + this.getChecked(data); + return data; + }); + } + + onSort(a, b) { + if (b.selected !== a.selected) { + if (a.selected == null) + return 1; + if (b.selected == null) + return -1; + return b.selected - a.selected; } - this.$q.all(reqs) - .then(() => this.refresh()); + return a.name.localeCompare(b.name); + } + + getChecked(data) { + for (let geo of data) { + geo.checked = this.exclusionGeos.has(geo.id); + if (geo.childs) this.getChecked(geo.childs); + } + } + + onItemCheck(geoId, checked) { + if (checked) + this.exclusionGeos.add(geoId); + else + this.exclusionGeos.delete(geoId); } } Controller.$inject = ['$element', '$scope', 'vnWeekDays']; ngModule.vnComponent('vnZoneEvents', { template: require('./index.html'), - controller: Controller + controller: Controller, + bindings: { + zone: '<' + }, + require: { + card: '^vnZoneCard' + } }); diff --git a/modules/zone/front/events/index.spec.js b/modules/zone/front/events/index.spec.js index ed2c91c31..b4ff800d6 100644 --- a/modules/zone/front/events/index.spec.js +++ b/modules/zone/front/events/index.spec.js @@ -1,4 +1,5 @@ import './index'; +import crudModel from 'core/mocks/crud-model'; describe('component vnZoneEvents', () => { let $scope; @@ -34,7 +35,8 @@ describe('component vnZoneEvents', () => { const query = `Zones/getEventsFiltered?ended=${date}&started=${date}&zoneFk=${params.zoneFk}`; const response = { events: 'myEvents', - exclusions: 'myExclusions' + exclusions: 'myExclusions', + geoExclusions: 'myGeoExclusions', }; $httpBackend.whenGET(query).respond(response); controller.refresh(); @@ -48,71 +50,129 @@ describe('component vnZoneEvents', () => { }); describe('onSelection()', () => { - it('should call the edit() method', () => { - jest.spyOn(controller, 'edit').mockReturnThis(); + it('should call the editInclusion() method', () => { + jest.spyOn(controller, 'editInclusion').mockReturnThis(); const weekday = {}; const days = []; const type = 'EventType'; const events = [{name: 'Event'}]; const exclusions = []; + const exclusionsGeo = []; controller.editMode = 'include'; - controller.onSelection(days, type, weekday, events, exclusions); + controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo); - expect(controller.edit).toHaveBeenCalledWith({name: 'Event'}); + expect(controller.editInclusion).toHaveBeenCalledWith({name: 'Event'}); }); - it('should call the create() method', () => { - jest.spyOn(controller, 'create').mockReturnThis(); + it('should call the createInclusion() method', () => { + jest.spyOn(controller, 'createInclusion').mockReturnThis(); const weekday = {dated: new Date()}; const days = [weekday]; const type = 'EventType'; const events = []; const exclusions = []; + const exclusionsGeo = []; controller.editMode = 'include'; - controller.onSelection(days, type, weekday, events, exclusions); + controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo); - expect(controller.create).toHaveBeenCalledWith(type, days, weekday); + expect(controller.createInclusion).toHaveBeenCalledWith(type, days, weekday); }); - it('should call the exclusionDelete() method', () => { - jest.spyOn(controller, 'exclusionDelete').mockReturnThis(); + it('should call the editExclusion() method with exclusions', () => { + jest.spyOn(controller, 'editExclusion').mockReturnThis(); const weekday = {}; const days = []; const type = 'EventType'; const events = []; - const exclusions = [{id: 1}]; - controller.editMode = 'delete'; - controller.onSelection(days, type, weekday, events, exclusions); + const exclusions = [{name: 'Exclusion'}]; + const exclusionsGeo = []; + controller.editMode = 'exclude'; + controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo); - expect(controller.exclusionDelete).toHaveBeenCalledWith(exclusions); + expect(controller.editExclusion).toHaveBeenCalled(); }); - it('should call the exclusionCreate() method', () => { - jest.spyOn(controller, 'exclusionCreate').mockReturnThis(); + it('should call the editExclusion() method with exclusionsGeo', () => { + jest.spyOn(controller, 'editExclusion').mockReturnThis(); + + const weekday = {}; + const days = []; + const type = 'EventType'; + const events = []; + const exclusions = []; + const exclusionsGeo = [{name: 'GeoExclusion'}]; + controller.editMode = 'exclude'; + controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo); + + expect(controller.editExclusion).toHaveBeenCalled(); + }); + + it('should call the createExclusion() method', () => { + jest.spyOn(controller, 'createExclusion').mockReturnThis(); const weekday = {}; const days = [{dated: new Date()}]; const type = 'EventType'; const events = []; const exclusions = []; - controller.editMode = 'delete'; - controller.onSelection(days, type, weekday, events, exclusions); + const exclusionsGeo = []; + controller.editMode = 'exclude'; + controller.onSelection(days, type, weekday, events, exclusions, exclusionsGeo); - expect(controller.exclusionCreate).toHaveBeenCalledWith(days); + expect(controller.createExclusion).toHaveBeenCalledWith(days); }); }); - describe('create()', () => { - it('shoud set the selected property and then call the dialog show() method', () => { - controller.$.dialog = {show: jest.fn()}; + describe('editExclusion()', () => { + it('shoud set the excludeSelected.type = "specificLocations" and then call the excludeDialog show() method', () => { + controller.$.excludeDialog = {show: jest.fn()}; + + const exclusionGeos = [{id: 1}]; + const exclusions = []; + + controller.editExclusion(exclusions, exclusionGeos); + + expect(controller.excludeSelected.type).toEqual('specificLocations'); + expect(controller.$.excludeDialog.show).toHaveBeenCalledWith(); + }); + + it('shoud set the excludeSelected.type = "all" and then call the excludeDialog show() method', () => { + controller.$.excludeDialog = {show: jest.fn()}; + + const exclusionGeos = []; + const exclusions = [{id: 1}]; + + controller.editExclusion(exclusions, exclusionGeos); + + expect(controller.excludeSelected.type).toEqual('all'); + expect(controller.$.excludeDialog.show).toHaveBeenCalledWith(); + }); + }); + + describe('createExclusion()', () => { + it('shoud set the excludeSelected property and then call the excludeDialog show() method', () => { + controller.$.excludeDialog = {show: jest.fn()}; + + const days = [new Date()]; + controller.createExclusion(days); + + expect(controller.excludeSelected).toBeDefined(); + expect(controller.isNew).toBeTruthy(); + expect(controller.$.excludeDialog.show).toHaveBeenCalledWith(); + }); + }); + + describe('createInclusion()', () => { + it('shoud set the selected property and then call the includeDialog show() method', () => { + controller.$.includeDialog = {show: jest.fn()}; const type = 'weekday'; const days = [new Date()]; const weekday = 1; - controller.create(type, days, weekday); + controller.createInclusion(type, days, weekday); const selection = controller.selected; const firstWeekday = selection.wdays[weekday]; @@ -120,23 +180,23 @@ describe('component vnZoneEvents', () => { expect(selection.type).toEqual('indefinitely'); expect(firstWeekday).toBeTruthy(); expect(controller.isNew).toBeTruthy(); - expect(controller.$.dialog.show).toHaveBeenCalledWith(); + expect(controller.$.includeDialog.show).toHaveBeenCalledWith(); }); - it('shoud set the selected property with the first day and then call the dialog show() method', () => { - controller.$.dialog = {show: jest.fn()}; + it('shoud set the selected property with the first day and then call the includeDialog show() method', () => { + controller.$.includeDialog = {show: jest.fn()}; const type = 'nonListedType'; const days = [new Date()]; const weekday = 1; - controller.create(type, days, weekday); + controller.createInclusion(type, days, weekday); const selection = controller.selected; expect(selection.type).toEqual('day'); expect(selection.dated).toEqual(days[0]); expect(controller.isNew).toBeTruthy(); - expect(controller.$.dialog.show).toHaveBeenCalledWith(); + expect(controller.$.includeDialog.show).toHaveBeenCalledWith(); }); }); @@ -180,6 +240,35 @@ describe('component vnZoneEvents', () => { }); }); + describe('onExcludeResponse()', () => { + it('should call the exclusionCreate() method', () => { + jest.spyOn(controller, 'exclusionCreate').mockReturnThis(); + + controller.excludeSelected = {type: 'all'}; + controller.onExcludeResponse('accept'); + + expect(controller.exclusionCreate).toHaveBeenCalledWith(); + }); + + it('should call the exclusionGeoCreate() method', () => { + jest.spyOn(controller, 'exclusionGeoCreate').mockReturnThis(); + + controller.excludeSelected = {type: 'specificLocations'}; + controller.onExcludeResponse('accept'); + + expect(controller.exclusionGeoCreate).toHaveBeenCalledWith(); + }); + + it('should call the exclusionDelete() method', () => { + jest.spyOn(controller, 'exclusionDelete').mockReturnThis(); + + controller.excludeSelected = {id: 1, type: 'all'}; + controller.onExcludeResponse('delete'); + + expect(controller.exclusionDelete).toHaveBeenCalledWith(controller.excludeSelected); + }); + }); + describe('onDeleteResponse()', () => { it('shoud make an HTTP DELETE query and then call the refresh() method', () => { jest.spyOn(controller, 'refresh').mockReturnThis(); @@ -197,9 +286,10 @@ describe('component vnZoneEvents', () => { it('shoud make an HTTP POST query and then call the refresh() method', () => { jest.spyOn(controller, 'refresh').mockReturnThis(); - const dates = [new Date()]; + controller.excludeSelected = {}; + controller.isNew = true; $httpBackend.expect('POST', `Zones/1/exclusions`).respond({id: 1}); - controller.exclusionCreate(dates); + controller.exclusionCreate(); $httpBackend.flush(); expect(controller.refresh).toHaveBeenCalledWith(); @@ -210,25 +300,41 @@ describe('component vnZoneEvents', () => { it('shoud make an HTTP DELETE query once and then call the refresh() method', () => { jest.spyOn(controller, 'refresh').mockReturnThis(); - const exclusions = [{id: 1}]; + const exclusions = {id: 1}; const firstExclusionId = 1; - $httpBackend.when('DELETE', `Zones/1/exclusions/${firstExclusionId}`).respond(200); + $httpBackend.expectDELETE(`Zones/1/exclusions/${firstExclusionId}`).respond(200); controller.exclusionDelete(exclusions); $httpBackend.flush(); expect(controller.refresh).toHaveBeenCalledWith(); }); + }); - it('shoud make an HTTP DELETE query for every event and then call the refresh() method', () => { - jest.spyOn(controller, 'refresh').mockReturnThis(); - jest.spyOn(controller.$http, 'delete').mockReturnValue(200); + describe('onSearch()', () => { + it('should call the applyFilter() method and then set the data', () => { + jest.spyOn(controller, 'getChecked').mockReturnValue([1, 2, 3]); - const exclusions = [{id: 1}, {id: 2}, {id: 3}, {id: 4}]; - controller.exclusionDelete(exclusions); - $scope.$apply(); + controller.$.treeview = {}; + controller.$.model = crudModel; + controller.excludeSelected = {type: 'specificLocations'}; + controller._excludeSearch = 'es'; - expect(controller.$http.delete).toHaveBeenCalledTimes(4); - expect(controller.refresh).toHaveBeenCalledWith(); + controller.onSearch(); + const treeviewData = controller.$.treeview.data; + + expect(treeviewData).toBeDefined(); + expect(treeviewData.length).toEqual(3); + }); + }); + + describe('onFetch()', () => { + it('should call the applyFilter() method and then return the model data', () => { + jest.spyOn(controller, 'getChecked').mockReturnValue([1, 2, 3]); + + controller.$.model = crudModel; + const result = controller.onFetch(); + + expect(result.length).toEqual(3); }); }); }); diff --git a/modules/zone/front/events/locale/es.yml b/modules/zone/front/events/locale/es.yml index eb581a719..1fb114720 100644 --- a/modules/zone/front/events/locale/es.yml +++ b/modules/zone/front/events/locale/es.yml @@ -4,3 +4,7 @@ Exclude: Excluir Events: Eventos Add event: AƱadir evento Edit event: Editar evento +All: Todo +Specific locations: Localizaciones concretas +Locations where it is not distributed: Localizaciones en las que no se reparte +You must select a location: Debes seleccionar una localizaciĆ³n diff --git a/modules/zone/front/events/style.scss b/modules/zone/front/events/style.scss new file mode 100644 index 000000000..49a6e87a6 --- /dev/null +++ b/modules/zone/front/events/style.scss @@ -0,0 +1,11 @@ +@import "variables"; + + .width{ + width: 600px + } + + .treeview{ + max-height: 300px; + overflow: auto; + } + diff --git a/modules/zone/front/location/style.scss b/modules/zone/front/location/style.scss index 2316a2622..24d685a51 100644 --- a/modules/zone/front/location/style.scss +++ b/modules/zone/front/location/style.scss @@ -1,19 +1,21 @@ @import "variables"; -vn-treeview-child { - .content > .vn-check:not(.indeterminate):not(.checked) { - color: $color-alert; +vn-zone-location { + vn-treeview-child { + .content > .vn-check:not(.indeterminate):not(.checked) { + color: $color-alert; - & > .btn { - border-color: $color-alert; + & > .btn { + border-color: $color-alert; + } + } + .content > .vn-check.checked { + color: $color-notice; + + & > .btn { + background-color: $color-notice; + border-color: $color-notice + } } } - .content > .vn-check.checked { - color: $color-notice; - - & > .btn { - background-color: $color-notice; - border-color: $color-notice - } - } -} \ No newline at end of file +} diff --git a/modules/zone/front/routes.json b/modules/zone/front/routes.json index e08f97314..7f67260da 100644 --- a/modules/zone/front/routes.json +++ b/modules/zone/front/routes.json @@ -85,10 +85,13 @@ "description": "Warehouses" }, { - "url": "/events", + "url": "/events?q", "state": "zone.card.events", "component": "vn-zone-events", - "description": "Calendar" + "description": "Calendar", + "params": { + "zone": "$ctrl.zone" + } }, { "url": "/location?q",