diff --git a/client/item/src/niche/item-niche.html b/client/item/src/niche/item-niche.html index 9d2297ca2..7a7ea9f1d 100644 --- a/client/item/src/niche/item-niche.html +++ b/client/item/src/niche/item-niche.html @@ -1,5 +1,48 @@ - - - Niche - - \ No newline at end of file +
+ + + + Item Niches + + + + + + + + + + + + + + + + + + +
diff --git a/client/item/src/niche/item-niche.js b/client/item/src/niche/item-niche.js index caa84c9ea..f12160483 100644 --- a/client/item/src/niche/item-niche.js +++ b/client/item/src/niche/item-niche.js @@ -1,5 +1,142 @@ import ngModule from '../module'; +export default class Controller { + constructor($state, $scope, $http, $q, $translate, vnApp) { + this.$state = $state; + this.$scope = $scope; + this.$http = $http; + this.$q = $q; + this.$translate = $translate; + this.vnApp = vnApp; + + this.warehouses = []; + this.niches = []; + this.removedNiches = []; + this.oldNiches = {}; + } + + _setIconAdd() { + if (this.niches.length) { + this.niches.map(element => { + element.showAddIcon = false; + return true; + }); + this.niches[this.niches.length - 1].showAddIcon = true; + } else { + this.addNiche(); + } + } + + _setDirtyForm() { + if (this.$scope.form) { + this.$scope.form.$setDirty(); + } + } + _unsetDirtyForm() { + if (this.$scope.form) { + this.$scope.form.$setPristine(); + } + } + + addNiche() { + this.niches.push({code: null, itemFk: this.$state.params.id, showAddIcon: true}); + this._setIconAdd(); + } + + removeNiche(index) { + let item = this.niches[index]; + if (item) { + this.niches.splice(index, 1); + this._setIconAdd(); + if (item.id) { + this.removedNiches.push(item.id); + this._setDirtyForm(); + } + } + } + + _equalNiches(oldNiche, newNiche) { + return oldNiche.id === newNiche.id && oldNiche.code === newNiche.code && oldNiche.warehouseFk === newNiche.warehouseFk; + } + + submit() { + let warehousesDefined = []; + let repeatedWarehouse = false; + let canSubmit; + let nichesObj = { + delete: this.removedNiches, + create: [], + update: [] + }; + this.niches.forEach(niche => { + let isNewNiche = !niche.id; + + if (warehousesDefined.indexOf(niche.warehouseFk) !== -1) { + repeatedWarehouse = true; + return; + } + warehousesDefined.push(niche.warehouseFk); + + if (isNewNiche) { + nichesObj.create.push(niche); + } + + if (!isNewNiche && !this._equalNiches(this.oldNiches[niche.id], niche)) { + nichesObj.update.push(niche); + } + }); + + if (repeatedWarehouse) { + return this.vnApp.showMessage(this.$translate.instant('The niche must be unique')); + } + canSubmit = nichesObj.update.length > 0 || nichesObj.create.length > 0 || nichesObj.delete.length > 0; + + if (canSubmit) { + return this.$http.post(`/item/api/ItemNiches/crudItemNiches`, nichesObj).then(() => { + this.getNiches(); + this._unsetDirtyForm(); + }); + } + this.vnApp.showMessage(this.$translate.instant('No changes to save')); + } + + setOldNiches(response) { + this._setIconAdd(); + response.data.forEach(niche => { + this.oldNiches[niche.id] = Object.assign({}, niche); + }); + } + + getWarehouse(id, warehouses) { + return warehouses.find(warehouse => warehouse.id === id); + } + + getNiches() { + let filter = { + where: {itemFk: this.$state.params.id}, + include: {relation: 'warehouse'} + }; + this.$http.get(`/item/api/ItemNiches?filter=${JSON.stringify(filter)}`).then(response => { + this.niches = response.data; + this.setOldNiches(response); + }); + } + + getWarehouses() { + this.$http.get(`/item/api/Warehouses`).then(response => { + this.warehouses = response.data; + }); + } + + $onInit() { + this.getNiches(); + this.getWarehouses(); + } +} + +Controller.$inject = ['$state', '$scope', '$http', '$q', '$translate', 'vnApp']; + ngModule.component('vnItemNiche', { - template: require('./item-niche.html') + template: require('./item-niche.html'), + controller: Controller }); diff --git a/client/item/src/niche/item-niche.spec.js b/client/item/src/niche/item-niche.spec.js new file mode 100644 index 000000000..b86fa9061 --- /dev/null +++ b/client/item/src/niche/item-niche.spec.js @@ -0,0 +1,150 @@ +import './item-niche.js'; + +describe('Item', () => { + describe('Component vnItemNiche', () => { + let $componentController; + let $state; + let controller; + let $httpBackend; + + beforeEach(() => { + angular.mock.module('item'); + }); + + beforeEach(angular.mock.inject((_$componentController_, _$state_, _$httpBackend_) => { + $componentController = _$componentController_; + $state = _$state_; + $httpBackend = _$httpBackend_; + controller = $componentController('vnItemNiche', {$state: $state}); + })); + + describe('add / remove niche()', () => { + it('should add one empty niche into controller niches collection and call _setIconAdd()', () => { + controller.niches = []; + spyOn(controller, '_setIconAdd').and.callThrough(); + controller.addNiche(); + + expect(controller._setIconAdd).toHaveBeenCalledWith(); + expect(controller.niches.length).toEqual(1); + expect(controller.niches[0].id).toBe(undefined); + expect(controller.niches[0].showAddIcon).toBeTruthy(); + }); + + it('should remove a niche that occupies the position in the index given and call _setIconAdd()', () => { + let index = 2; + controller.niches = [ + {id: 1, warehouseFk: 1, code: '1111', showAddIcon: false}, + {id: 2, warehouseFk: 2, code: '2222', showAddIcon: false}, + {id: 3, warehouseFk: 3, code: '3333', showAddIcon: true} + ]; + + spyOn(controller, '_setIconAdd').and.callThrough(); + + controller.removeNiche(index); + + expect(controller._setIconAdd).toHaveBeenCalledWith(); + expect(controller.niches.length).toEqual(2); + expect(controller.niches[0].showAddIcon).toBeFalsy(); + expect(controller.niches[1].showAddIcon).toBeTruthy(); + expect(controller.niches[index]).toBe(undefined); + }); + }); + + describe('_equalNiches()', () => { + it('should return true if two niches are equals independent of control attributes', () => { + let niche1 = {id: 1, warehouseFk: 1, code: '1111', showAddIcon: true}; + let niche2 = {id: 1, warehouseFk: 1, code: '1111', showAddIcon: false}; + let equals = controller._equalNiches(niche2, niche1); + + expect(equals).toBeTruthy(); + }); + + it('should return false if two niches aint equals independent of control attributes', () => { + let niche1 = {id: 1, warehouseFk: 1, code: '1111', showAddIcon: true}; + let niche2 = {id: 1, warehouseFk: 1, code: '2222', showAddIcon: true}; + let equals = controller._equalNiches(niche2, niche1); + + expect(equals).toBeFalsy(); + }); + }); + + describe('get Niches / Warehouses', () => { + it('should perform a GET query to receive the item niches', () => { + let res = [{id: 1, warehouseFk: 1, code: '1111'}]; + + $httpBackend.when('GET', `/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`).respond(res); + $httpBackend.expectGET(`/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`); + controller.getNiches(); + $httpBackend.flush(); + }); + + it('should perform a GET query to receive the all warehouses', () => { + let res = [ + {id: 1, warehouseFk: 1, name: 'warehouse one'}, + {id: 2, warehouseFk: 2, name: 'warehouse two'} + ]; + + $httpBackend.when('GET', `/item/api/Warehouses`).respond(res); + $httpBackend.expectGET(`/item/api/Warehouses`); + controller.getWarehouses(); + $httpBackend.flush(); + }); + }); + + describe('submit()', () => { + it("should return an error message 'The niche must be unique' when the niche code isnt unique", () => { + spyOn(controller.vnApp, 'showMessage').and.callThrough(); + controller.niches = [ + {warehouseFk: 1, code: 123454, itemFk: 1, id: 1}, + {warehouseFk: 1, code: 123454, itemFk: 1} + ]; + controller.oldNiches = {1: {warehouseFk: 1, id: 1, code: 123454, itemFk: 1}}; + controller.submit(); + + expect(controller.vnApp.showMessage).toHaveBeenCalledWith('The niche must be unique'); + }); + + it("should perfom a query to delete niches", () => { + controller.oldNiches = {1: {id: 1, warehouseFk: 1, code: '1111'}}; + controller.niches = []; + controller.removedNiches = [1]; + + $httpBackend.when('GET', `/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`).respond([]); + $httpBackend.expectPOST(`/item/api/ItemNiches/crudItemNiches`).respond('ok!'); + controller.submit(); + $httpBackend.flush(); + }); + + it("should perfom a query to update niches", () => { + controller.niches = [{id: 1, warehouseFk: 1, code: '2222'}]; + controller.oldNiches = {1: {id: 1, warehouseFk: 1, code: '1111'}}; + + $httpBackend.whenGET(`/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`).respond([]); + $httpBackend.expectPOST(`/item/api/ItemNiches/crudItemNiches`).respond('ok!'); + controller.submit(); + $httpBackend.flush(); + }); + + it("should perfom a query to create new niche", () => { + controller.niches = [{warehouseFk: 1, code: 1111, itemFk: 1}]; + + $httpBackend.whenGET(`/item/api/ItemNiches?filter={"where":{},"include":{"relation":"warehouse"}}`).respond([]); + $httpBackend.expectPOST(`/item/api/ItemNiches/crudItemNiches`).respond('ok!'); + controller.submit(); + $httpBackend.flush(); + }); + + it("should return a message 'No changes to save' when there are no changes to apply", () => { + spyOn(controller.vnApp, 'showMessage').and.callThrough(); + controller.oldNiches = [ + {warehouseFk: 1, code: 1, itemFk: 1, id: 1}, + {warehouseFk: 2, code: 2, itemFk: 1, id: 2} + ]; + controller.niches = []; + controller.submit(); + + expect(controller.vnApp.showMessage).toHaveBeenCalledWith('No changes to save'); + }); + }); + }); +}); diff --git a/client/production/src/index/index.html b/client/production/src/index/index.html index 63e899206..be21fd321 100644 --- a/client/production/src/index/index.html +++ b/client/production/src/index/index.html @@ -4,28 +4,27 @@ Finder - - + data ="$ctrl.sharedData"> - - + label="Store"> + diff --git a/client/route/src/create/create.html b/client/route/src/create/create.html index 90dddbf71..8b7b7c6ba 100644 --- a/client/route/src/create/create.html +++ b/client/route/src/create/create.html @@ -12,15 +12,14 @@ Create Route - + field="$ctrl.delivery.agency"> - + filter="{where: {isActive:1, warehouseFk:1}}"> + - diff --git a/client/route/src/search-panel/search-panel.html b/client/route/src/search-panel/search-panel.html index 126b7ca9d..3661a2c5f 100644 --- a/client/route/src/search-panel/search-panel.html +++ b/client/route/src/search-panel/search-panel.html @@ -7,8 +7,8 @@ label="Zone" field="$ctrl.filter.zone" url="/route/api/Zones" - order="printingOrder ASC" - > + order="printingOrder ASC"> + diff --git a/services/client/common/models/observation-type.json b/services/client/common/models/observation-type.json index ac3b84092..b1a45e883 100644 --- a/services/client/common/models/observation-type.json +++ b/services/client/common/models/observation-type.json @@ -17,14 +17,6 @@ "type": "string", "required": true } - }, - "acls": [ - { - "accessType": "READ", - "principalType": "ROLE", - "principalId": "$everyone", - "permission": "ALLOW" - } - ] + } } \ No newline at end of file diff --git a/services/item/common/methods/item/crudItemNiches.js b/services/item/common/methods/item/crudItemNiches.js new file mode 100644 index 000000000..ed3440494 --- /dev/null +++ b/services/item/common/methods/item/crudItemNiches.js @@ -0,0 +1,36 @@ +module.exports = Self => { + Self.remoteMethod('crudItemNiches', { + description: 'create, update or delete niches', + accessType: 'WRITE', + accepts: [ + { + arg: 'niches', + type: 'Object', + require: true, + description: 'object with niches to create, update or delete, Example: {create: [], update: [], delete: []}', + http: {source: 'body'} + } + ], + http: { + path: `/crudItemNiches`, + verb: 'post' + } + }); + + Self.crudItemNiches = niches => { + let promises = []; + + if (niches.delete && niches.delete.length) { + promises.push(Self.destroyAll({id: {inq: niches.delete}})); + } + if (niches.create.length) { + promises.push(Self.create(niches.create)); + } + if (niches.update.length) { + niches.update.forEach(niche => { + promises.push(Self.upsert(niche)); + }); + } + return Promise.all(promises); + }; +}; diff --git a/services/item/common/methods/item/specs/crudItemNiches.spec.js b/services/item/common/methods/item/specs/crudItemNiches.spec.js new file mode 100644 index 000000000..d6b4bf175 --- /dev/null +++ b/services/item/common/methods/item/specs/crudItemNiches.spec.js @@ -0,0 +1,51 @@ +const crudItemNiches = require('../crudItemNiches'); +const catchErrors = require('../../../../../../services/utils/jasmineHelpers').catchErrors; + +describe('Item crudItemNiches()', () => { + it('should call the destroyAll method if there are ids in delete Array', done => { + let self = jasmine.createSpyObj('self', ['remoteMethod', 'crudItemNiches', 'destroyAll', 'create', 'upsert']); + + crudItemNiches(self); + self.crudItemNiches({ + delete: [1], + create: [], + update: [] + }).then(result => { + expect(self.destroyAll).toHaveBeenCalledWith({id: {inq: [1]}}); + done(); + }) + .catch(catchErrors(done)); + }); + + it('should call the create method if there are ids in create Array', done => { + let self = jasmine.createSpyObj('self', ['remoteMethod', 'crudItemNiches', 'destroyAll', 'create', 'upsert']); + + crudItemNiches(self); + self.crudItemNiches({ + delete: [], + create: [1], + update: [] + }).then(result => { + expect(self.create).toHaveBeenCalledWith([1]); + done(); + }) + .catch(catchErrors(done)); + }); + + it('should call the upsert method as many times as ids in update Array', done => { + let self = jasmine.createSpyObj('self', ['remoteMethod', 'crudItemNiches', 'destroyAll', 'create', 'upsert']); + + crudItemNiches(self); + self.crudItemNiches({ + delete: [], + create: [], + update: [1, 2] + }).then(result => { + expect(self.upsert).toHaveBeenCalledWith(1); + expect(self.upsert).toHaveBeenCalledWith(2); + expect(self.upsert.calls.count()).toEqual(2); + done(); + }) + .catch(catchErrors(done)); + }); +}); diff --git a/services/item/common/models/item.json b/services/item/common/models/item.json index a4121e8f5..daff68084 100644 --- a/services/item/common/models/item.json +++ b/services/item/common/models/item.json @@ -84,6 +84,11 @@ "type": "hasMany", "model": "ItemBarcode", "foreignKey": "itemFk" + }, + "itemNiche": { + "type": "hasMany", + "model": "ItemNiche", + "foreignKey": "itemFk" } } } \ No newline at end of file diff --git a/services/item/common/models/itemNiche.js b/services/item/common/models/itemNiche.js new file mode 100644 index 000000000..8f9d85da7 --- /dev/null +++ b/services/item/common/models/itemNiche.js @@ -0,0 +1,3 @@ +module.exports = function(Self) { + require('../methods/item/crudItemNiches.js')(Self); +}; diff --git a/services/item/common/models/itemNiche.json b/services/item/common/models/itemNiche.json new file mode 100644 index 000000000..035c07e6b --- /dev/null +++ b/services/item/common/models/itemNiche.json @@ -0,0 +1,28 @@ +{ + "name": "ItemNiche", + "base": "VnModel", + "options": { + "mysql": { + "table": "itemPlacement", + "database": "vn" + } + }, + "properties": { + "code": { + "type": "String", + "required": true + } + }, + "relations": { + "item": { + "type": "belongsTo", + "model": "Item", + "foreignKey": "itemFk" + }, + "warehouse": { + "type": "belongsTo", + "model": "Warehouse", + "foreignKey": "warehouseFk" + } + } +} diff --git a/services/item/common/models/warehouse.json b/services/item/common/models/warehouse.json new file mode 100644 index 000000000..d52991ed6 --- /dev/null +++ b/services/item/common/models/warehouse.json @@ -0,0 +1,29 @@ +{ + "name": "Warehouse", + "base": "VnModel", + "options": { + "mysql": { + "table": "warehouse", + "database": "vn" + } + }, + "properties": { + "id": { + "type": "Number", + "id": true, + "description": "Identifier" + }, + "name": { + "type": "String", + "required": true + } + }, + "acls": [ + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ] + } diff --git a/services/item/server/model-config.json b/services/item/server/model-config.json index c021a3fab..83b473a5a 100644 --- a/services/item/server/model-config.json +++ b/services/item/server/model-config.json @@ -35,6 +35,9 @@ "Tag": { "dataSource": "vn" }, + "ItemNiche": { + "dataSource": "vn" + }, "ItemLog": { "dataSource": "vn" }, @@ -47,6 +50,9 @@ "ItemPlacement": { "dataSource": "vn" }, + "Warehouse": { + "dataSource": "vn" + }, "Specie": { "dataSource": "edi" },