From ece2fd384db19b74e34fde70c4c6a94d40cade15 Mon Sep 17 00:00:00 2001 From: Carlos Jimenez <=> Date: Fri, 16 Mar 2018 15:06:42 +0100 Subject: [PATCH] #179 listar notas de ticket --- client/ticket/routes.json | 12 ++ .../ticket/src/notes/ticket-observations.html | 60 ++++++++ .../ticket/src/notes/ticket-observations.js | 140 +++++++++++++++++ .../src/notes/ticket-observations.spec.js | 143 ++++++++++++++++++ client/ticket/src/ticket.js | 1 + .../methods/ticket/crudTicketObservations.js | 3 + .../ticket/common/models/ticketObservation.js | 3 + .../common/models/ticketObservation.json | 50 +++--- 8 files changed, 387 insertions(+), 25 deletions(-) create mode 100644 client/ticket/src/notes/ticket-observations.html create mode 100644 client/ticket/src/notes/ticket-observations.js create mode 100644 client/ticket/src/notes/ticket-observations.spec.js create mode 100644 services/ticket/common/methods/ticket/crudTicketObservations.js create mode 100644 services/ticket/common/models/ticketObservation.js diff --git a/client/ticket/routes.json b/client/ticket/routes.json index 060d0c772..e0c476028 100644 --- a/client/ticket/routes.json +++ b/client/ticket/routes.json @@ -46,6 +46,18 @@ "icon": "settings" } }, + { + "url": "/observations", + "state": "ticket.card.observations", + "component": "vn-ticket-observations", + "params": { + "ticket": "$ctrl.ticket" + }, + "menu": { + "description": "Notes", + "icon": "insert_drive_file" + } + }, { "url" : "/package", "abstract": true, diff --git a/client/ticket/src/notes/ticket-observations.html b/client/ticket/src/notes/ticket-observations.html new file mode 100644 index 000000000..b48ec5a72 --- /dev/null +++ b/client/ticket/src/notes/ticket-observations.html @@ -0,0 +1,60 @@ + + +
+ + + Notes + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/client/ticket/src/notes/ticket-observations.js b/client/ticket/src/notes/ticket-observations.js new file mode 100644 index 000000000..793865fde --- /dev/null +++ b/client/ticket/src/notes/ticket-observations.js @@ -0,0 +1,140 @@ +import ngModule from '../module'; + +class TicketObservations { + constructor($stateParams, $scope, $http, $translate, vnApp) { + this.params = $stateParams; + this.$scope = $scope; + this.$http = $http; + this.$translate = $translate; + this.vnApp = vnApp; + + this.ticketObservations = []; + this.oldObservations = {}; + this.removedObservations = []; + } + + _setIconAdd() { + if (this.ticketObservations.length) { + this.ticketObservations.map(element => { + element.showAddIcon = false; + return true; + }); + this.ticketObservations[this.ticketObservations.length - 1].showAddIcon = true; + } + } + + _setDirtyForm() { + if (this.$scope.form) { + this.$scope.form.$setDirty(); + } + } + + _unsetDirtyForm() { + if (this.$scope.form) { + this.$scope.form.$setPristine(); + } + } + + addObservation() { + this.ticketObservations.push({description: null, ticketFk: this.params.id, showAddIcon: true}); + this._setIconAdd(); + } + + removeObservation(index) { + let item = this.ticketObservations[index]; + if (item) { + this.ticketObservations.splice(index, 1); + this._setIconAdd(); + if (item.id) { + this.removedObservations.push(item.id); + this._setDirtyForm(); + } + } + } + + _equalObservations(oldObservation, newObservation) { + return oldObservation.id === newObservation.id && oldObservation.observationTypeFk === newObservation.observationTypeFk && oldObservation.description === newObservation.description; + } + + setOldObservations(response) { + this._setIconAdd(); + response.data.forEach(observation => { + this.oldObservations[observation.id] = Object.assign({}, observation); + }); + } + + getObservations() { + let filter = { + where: {ticketFk: this.params.id}, + include: ['observationType'] + }; + + this.$http.get(`/ticket/api/TicketObservations?filter=${JSON.stringify(filter)}`).then(response => { + this.ticketObservations = response.data; + this.setOldObservations(response); + }); + } + + submit() { + let typesDefined = []; + let repeatedType = false; + let canSubmit; + let observationsObj = { + delete: this.removedObservations, + create: [], + update: [] + }; + + this.ticketObservations.forEach(observation => { + let isNewObservation = !observation.id; + + delete observation.showAddIcon; + + if (typesDefined.indexOf(observation.observationTypeFk) !== -1) { + repeatedType = true; + return; + } + typesDefined.push(observation.observationTypeFk); + + if (isNewObservation && observation.description && observation.observationTypeFk) { + observationsObj.create.push(observation); + } + + if (!isNewObservation && !this._equalObservations(this.oldObservations[observation.id], observation)) { + observationsObj.update.push(observation); + } + }); + + if (this.$scope.form.$invalid) { + return this.vnApp.showMessage(this.$translate.instant('Some fields are invalid')); + } + + if (repeatedType) { + return this.vnApp.showMessage(this.$translate.instant('The observation type must be unique')); + } + + canSubmit = observationsObj.update.length > 0 || observationsObj.create.length > 0 || observationsObj.delete.length > 0; + + if (canSubmit) { + return this.$http.post(`/ticket/api/TicketObservations/crudTicketObservations`, observationsObj).then(() => { + this.getObservations(); + this._unsetDirtyForm(); + }); + } + this.vnApp.showMessage(this.$translate.instant('No changes to save')); + } + + $onInit() { + this.getObservations(); + } +} + +TicketObservations.$inject = ['$stateParams', '$scope', '$http', '$translate', 'vnApp']; + +ngModule.component('vnTicketObservations', { + template: require('./ticket-observations.html'), + controller: TicketObservations, + bindings: { + ticket: '<' + } +}); diff --git a/client/ticket/src/notes/ticket-observations.spec.js b/client/ticket/src/notes/ticket-observations.spec.js new file mode 100644 index 000000000..982cfb61b --- /dev/null +++ b/client/ticket/src/notes/ticket-observations.spec.js @@ -0,0 +1,143 @@ +import './ticket-observations.js'; + +describe('ticket', () => { + describe('Component vnTicketObservations', () => { + let $componentController; + let $state; + let controller; + let $httpBackend; + + beforeEach(() => { + angular.mock.module('ticket'); + }); + + beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => { + $componentController = _$componentController_; + $state = _$state_; + $httpBackend = _$httpBackend_; + controller = $componentController('vnTicketObservations', {$state: $state}); + })); + + describe('add / remove observation', () => { + it('should add one empty observation into controller observations collection and call _setIconAdd()', () => { + controller.ticketObservations = []; + spyOn(controller, '_setIconAdd').and.callThrough(); + controller.addObservation(); + + expect(controller._setIconAdd).toHaveBeenCalledWith(); + expect(controller.ticketObservations.length).toEqual(1); + expect(controller.ticketObservations[0].id).toBe(undefined); + expect(controller.ticketObservations[0].showAddIcon).toBeTruthy(); + }); + + it('should remove an observation that occupies the position in the index given and call _setIconAdd()', () => { + let index = 2; + controller.ticketObservations = [ + {id: 1, observationTypeFk: 1, description: 'one', showAddIcon: false}, + {id: 2, observationTypeFk: 2, description: 'two', showAddIcon: false}, + {id: 3, observationTypeFk: 3, description: 'three', showAddIcon: true} + ]; + + spyOn(controller, '_setIconAdd').and.callThrough(); + + controller.removeObservation(index); + + expect(controller._setIconAdd).toHaveBeenCalledWith(); + expect(controller.ticketObservations.length).toEqual(2); + expect(controller.ticketObservations[0].showAddIcon).toBeFalsy(); + expect(controller.ticketObservations[1].showAddIcon).toBeTruthy(); + expect(controller.ticketObservations[index]).toBe(undefined); + }); + }); + + describe('_equalObservations()', () => { + it('should return true if two observations are equals independent of control attributes', () => { + let observationOne = {id: 1, observationTypeFk: 1, description: 'one', showAddIcon: true}; + let observationTwo = {id: 1, observationTypeFk: 1, description: 'one', showAddIcon: false}; + let equals = controller._equalObservations(observationOne, observationTwo); + + expect(equals).toBeTruthy(); + }); + + it('should return false if two observations aint equals independent of control attributes', () => { + let observationOne = {id: 1, observationTypeFk: 1, description: 'one', showAddIcon: true}; + let observationTwo = {id: 1, observationTypeFk: 1, description: 'two', showAddIcon: true}; + let equals = controller._equalObservations(observationOne, observationTwo); + + expect(equals).toBeFalsy(); + }); + }); + + describe('get Observations()', () => { + it('should perform a GET query to receive the ticket observations', () => { + let res = [{id: 1, observationTypeFk: 1, description: 'one'}]; + + $httpBackend.whenGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`).respond(res); + $httpBackend.expectGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`); + controller.getObservations(); + $httpBackend.flush(); + }); + }); + + describe('submit()', () => { + it("should return an error message 'The observation type must be unique'", () => { + controller.$scope.form = {}; + spyOn(controller.vnApp, 'showMessage').and.callThrough(); + controller.ticketObservations = [ + {id: 1, observationTypeFk: 1, description: 'one', itemFk: 1}, + {observationTypeFk: 1, description: 'one', itemFk: 1} + ]; + controller.oldObservations = {1: {id: 1, observationTypeFk: 1, description: 'one', itemFk: 1}}; + controller.submit(); + + expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The observation type must be unique'); + }); + + it("should perfom a query to delete observations", () => { + controller.$scope.form = {$setPristine: () => {}}; + controller.oldObservations = {1: {id: 1, observationTypeFk: 1, description: 'one'}}; + controller.ticketObservations = []; + controller.removedObservations = [1]; + + $httpBackend.whenGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`).respond([]); + $httpBackend.expectPOST(`/ticket/api/TicketObservations/crudTicketObservations`).respond('ok!'); + controller.submit(); + $httpBackend.flush(); + }); + + it("should perfom a query to update observations", () => { + controller.$scope.form = {$setPristine: () => {}}; + controller.ticketObservations = [{id: 1, observationTypeFk: 1, description: 'number one!'}]; + controller.oldObservations = {1: {id: 1, observationTypeFk: 1, description: 'one'}}; + + $httpBackend.whenGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`).respond([]); + $httpBackend.expectPOST(`/ticket/api/TicketObservations/crudTicketObservations`).respond('ok!'); + controller.submit(); + $httpBackend.flush(); + }); + + it("should perfom a query to create new observation", () => { + controller.$scope.form = {$setPristine: () => {}}; + controller.ticketObservations = [{observationTypeFk: 2, description: 'two'}]; + + $httpBackend.whenGET(`/ticket/api/TicketObservations?filter={"where":{},"include":["observationType"]}`).respond([]); + $httpBackend.expectPOST(`/ticket/api/TicketObservations/crudTicketObservations`).respond('ok!'); + controller.submit(); + $httpBackend.flush(); + }); + + it("should return a message 'No changes to save' when there are no changes to apply", () => { + controller.$scope.form = {$setPristine: () => {}}; + spyOn(controller.vnApp, 'showMessage').and.callThrough(); + controller.oldObservations = [ + {id: 1, observationTypeFk: 1, description: 'one', showAddIcon: false}, + {id: 2, observationTypeFk: 2, description: 'two', showAddIcon: true} + ]; + controller.ticketObservations = []; + controller.submit(); + + expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save'); + }); + }); + }); +}); diff --git a/client/ticket/src/ticket.js b/client/ticket/src/ticket.js index d269a5fbd..4a7af6e58 100644 --- a/client/ticket/src/ticket.js +++ b/client/ticket/src/ticket.js @@ -5,5 +5,6 @@ import './create/ticket-create'; import './card/ticket-card'; import './summary/ticket-summary'; import './data/ticket-data'; +import './notes/ticket-observations'; import './package/list/package-list'; import './review/review'; diff --git a/services/ticket/common/methods/ticket/crudTicketObservations.js b/services/ticket/common/methods/ticket/crudTicketObservations.js new file mode 100644 index 000000000..33949e1f4 --- /dev/null +++ b/services/ticket/common/methods/ticket/crudTicketObservations.js @@ -0,0 +1,3 @@ +module.exports = Self => { + Self.installCrudModel('crudTicketObservations'); +}; diff --git a/services/ticket/common/models/ticketObservation.js b/services/ticket/common/models/ticketObservation.js new file mode 100644 index 000000000..bed837bd3 --- /dev/null +++ b/services/ticket/common/models/ticketObservation.js @@ -0,0 +1,3 @@ +module.exports = function(Self) { + require('../methods/ticket/crudTicketObservations.js')(Self); +}; diff --git a/services/ticket/common/models/ticketObservation.json b/services/ticket/common/models/ticketObservation.json index a6e5e3029..e5b0169c1 100644 --- a/services/ticket/common/models/ticketObservation.json +++ b/services/ticket/common/models/ticketObservation.json @@ -2,33 +2,33 @@ "name": "TicketObservation", "base": "VnModel", "options": { - "mysql": { - "table": "ticketObservation" - } + "mysql": { + "table": "ticketObservation" + } }, "properties": { - "id": { - "id": true, - "type": "Number", - "description": "Identifier" - }, - "description": { - "type": "String", - "required": true - } + "id": { + "id": true, + "type": "Number", + "description": "Identifier" + }, + "description": { + "type": "String", + "required": true + } }, "relations": { - "ticket": { - "type": "belongsTo", - "model": "Ticket", - "foreignKey": "ticketFk", - "required": true - }, - "observationType": { - "type": "belongsTo", - "model": "ObservationType", - "foreignKey": "observationTypeFk", - "required": true - } + "ticket": { + "type": "belongsTo", + "model": "Ticket", + "foreignKey": "ticketFk", + "required": true + }, + "observationType": { + "type": "belongsTo", + "model": "ObservationType", + "foreignKey": "observationTypeFk", + "required": true + } } - } \ No newline at end of file +}