From 7e9f09a361152773d32eb26e48e6a92eded43c95 Mon Sep 17 00:00:00 2001 From: olewandowski1 Date: Fri, 19 Apr 2024 13:47:10 +0200 Subject: [PATCH 1/2] OAM-45: wards management ui implementation --- src/admin-facility-view/_facility-view.scss | 24 ++ .../admin-facility-view.routes.js | 74 ++++ .../facility-view.controller.js | 351 ++++++++++++++++ .../facility-view.controller.spec.js | 382 ++++++++++++++++++ src/admin-facility-view/facility-view.html | 28 ++ src/admin-facility-view/messages_en.json | 39 ++ .../services-and-wards.html | 83 ++++ .../ward-data-builder.spec.js | 51 +++ src/admin-facility-view/ward.service.js | 102 +++++ src/admin-facility-view/ward.service.spec.js | 91 +++++ src/referencedata-period/tz-period.service.js | 145 +++++++ 11 files changed, 1370 insertions(+) create mode 100644 src/admin-facility-view/_facility-view.scss create mode 100644 src/admin-facility-view/admin-facility-view.routes.js create mode 100644 src/admin-facility-view/facility-view.controller.js create mode 100644 src/admin-facility-view/facility-view.controller.spec.js create mode 100644 src/admin-facility-view/facility-view.html create mode 100644 src/admin-facility-view/messages_en.json create mode 100644 src/admin-facility-view/services-and-wards.html create mode 100644 src/admin-facility-view/ward-data-builder.spec.js create mode 100644 src/admin-facility-view/ward.service.js create mode 100644 src/admin-facility-view/ward.service.spec.js create mode 100644 src/referencedata-period/tz-period.service.js diff --git a/src/admin-facility-view/_facility-view.scss b/src/admin-facility-view/_facility-view.scss new file mode 100644 index 0000000..afd1d90 --- /dev/null +++ b/src/admin-facility-view/_facility-view.scss @@ -0,0 +1,24 @@ +nav.facility-edit { + + > ul { + + padding: 0; + + li.tab-pane { + padding: 1em; + } + + li a { + text-decoration: none; + } + } + + div.button-group { + margin-top: 8px; + max-width: 440px; + } +} + +.note-warning { + font-style: italic; +} diff --git a/src/admin-facility-view/admin-facility-view.routes.js b/src/admin-facility-view/admin-facility-view.routes.js new file mode 100644 index 0000000..fc5d902 --- /dev/null +++ b/src/admin-facility-view/admin-facility-view.routes.js @@ -0,0 +1,74 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + *   + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +(function() { + + 'use strict'; + + angular.module('admin-facility-view').config(routes); + + routes.$inject = ['$stateProvider', 'ADMINISTRATION_RIGHTS']; + + function routes($stateProvider, ADMINISTRATION_RIGHTS) { + + $stateProvider.state('openlmis.administration.facilities.edit', { + label: 'adminFacilityView.editFacility', + url: '/:id', + accessRights: [ADMINISTRATION_RIGHTS.FACILITIES_MANAGE], + views: { + '@openlmis': { + controller: 'FacilityViewController', + templateUrl: 'admin-facility-view/facility-view.html', + controllerAs: 'vm' + } + }, + resolve: { + facility: function(FacilityRepository, $stateParams) { + return new FacilityRepository().get($stateParams.id); + }, + facilityTypes: function(facilityTypeService) { + return facilityTypeService.query({ + active: true + }) + .then(function(response) { + return response.content; + }); + }, + geographicZones: function($q, geographicZoneService) { + var deferred = $q.defer(); + + geographicZoneService.getAll().then(function(response) { + deferred.resolve(response.content); + }, deferred.reject); + + return deferred.promise; + }, + facilityOperators: function(facilityOperatorService) { + return facilityOperatorService.getAll(); + }, + programs: function(programService) { + return programService.getAll(); + }, + wards: function(wardService, facility) { + return wardService.getWardsByFacility({ + facilityId: facility.id + }).then(function(response) { + return response.content; + }); + } + } + }); + } +})(); diff --git a/src/admin-facility-view/facility-view.controller.js b/src/admin-facility-view/facility-view.controller.js new file mode 100644 index 0000000..d8cb33f --- /dev/null +++ b/src/admin-facility-view/facility-view.controller.js @@ -0,0 +1,351 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + *   + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +(function() { + + 'use strict'; + + /** + * @ngdoc controller + * @name admin-facility-view.controller:FacilityViewController + * + * @description + * Controller for managing facility view screen. + */ + angular + .module('admin-facility-view') + .controller('FacilityViewController', controller); + + controller.$inject = [ + '$q', '$state', 'facility', 'facilityTypes', 'geographicZones', 'facilityOperators', + 'programs', 'FacilityRepository', 'loadingModalService', 'notificationService', + 'tzPeriodService', 'messageService', 'confirmService', 'wards', 'wardService' + ]; + + function controller($q, $state, facility, facilityTypes, geographicZones, facilityOperators, + programs, FacilityRepository, loadingModalService, notificationService, + tzPeriodService, messageService, confirmService, wards, wardService) { + + var vm = this; + + vm.$onInit = onInit; + vm.goToFacilityList = goToFacilityList; + vm.saveFacilityDetails = saveFacilityDetails; + vm.saveFacilityWithPrograms = saveFacilityWithPrograms; + vm.saveFacilityWards = saveFacilityWards; + vm.addProgram = addProgram; + vm.addWard = addWard; + vm.deleteProgramAssociate = deleteProgramAssociate; + + /** + * @ngdoc property + * @propertyOf admin-facility-view.controller:FacilityViewController + * @name facility + * @type {Object} + * + * @description + * Contains facility object. + */ + vm.facility = undefined; + + /** + * @ngdoc property + * @propertyOf admin-facility-view.controller:FacilityViewController + * @name facilityTypes + * @type {Array} + * + * @description + * Contains all facility types. + */ + vm.facilityTypes = undefined; + + /** + * @ngdoc property + * @propertyOf admin-facility-view.controller:FacilityViewController + * @name geographicZones + * @type {Array} + * + * @description + * Contains all geographic zones. + */ + vm.geographicZones = undefined; + + /** + * @ngdoc property + * @propertyOf admin-facility-view.controller:FacilityViewController + * @name facilityOperators + * @type {Array} + * + * @description + * Contains all facility operators. + */ + vm.facilityOperators = undefined; + + /** + * @ngdoc property + * @propertyOf admin-facility-view.controller:FacilityViewController + * @name programs + * @type {Array} + * + * @description + * Contains all programs. + */ + vm.programs = undefined; + + /** + * @ngdoc property + * @propertyOf admin-facility-view.controller:FacilityViewController + * @name wards + * @type {Array} + * + * @description + * Contains all wards. + */ + vm.wards = undefined; + + /** + * @ngdoc property + * @propertyOf admin-facility-view.controller:FacilityViewController + * @name selectedTab + * @type {String} + * + * @description + * Contains currently selected tab. + */ + vm.selectedTab = undefined; + + /** + * @ngdoc property + * @propertyOf admin-facility-view.controller:FacilityViewController + * @name newWard + * @type {Object} + * + * @description + * Contains new ward object. By default, active true. + */ + vm.newWard = { + disabled: false + }; + + /** + * @ngdoc method + * @propertyOf admin-facility-view.controller:FacilityViewController + * @name $onInit + * + * @description + * Method that is executed on initiating FacilityListController. + */ + function onInit() { + vm.originalFacilityName = facility.name; + vm.facility = angular.copy(facility); + vm.facilityWithPrograms = angular.copy(facility); + vm.facilityTypes = facilityTypes; + vm.geographicZones = geographicZones; + vm.facilityOperators = facilityOperators; + vm.programs = programs; + vm.wards = wards; + vm.selectedTab = 0; + vm.managedExternally = facility.isManagedExternally(); + + if (!vm.facilityWithPrograms.supportedPrograms) { + vm.facilityWithPrograms.supportedPrograms = []; + } + + vm.facilityWithPrograms.supportedPrograms.filter(function(supportedProgram) { + vm.programs = vm.programs.filter(function(program) { + return supportedProgram.id !== program.id; + }); + }); + } + + /** + * @ngdoc method + * @methodOf admin-facility-view.controller:FacilityViewController + * @name goToFacilityList + * + * @description + * Redirects to facility list screen. + */ + function goToFacilityList() { + $state.go('openlmis.administration.facilities', {}, { + reload: true + }); + } + + /** + * @ngdoc method + * @methodOf admin-facility-view.controller:FacilityViewController + * @name saveFacilityDetails + * + * @description + * Saves facility details and redirects to facility list screen. + */ + function saveFacilityDetails() { + doSave(vm.facility, + 'adminFacilityView.saveFacility.success', + 'adminFacilityView.saveFacility.fail'); + } + + /** + * @ngdoc method + * @methodOf admin-facility-view.controller:FacilityViewController + * @name saveFacilityWithPrograms + * + * @description + * Saves facility with supported programs and redirects to facility list screen. + */ + function saveFacilityWithPrograms() { + doSave(vm.facilityWithPrograms, + 'adminFacilityView.saveFacility.success', + 'adminFacilityView.saveFacility.fail'); + } + + /** + * @ngdoc method + * @methodOf admin-facility-view.controller:FacilityViewController + * @name addProgram + * + * @description + * Adds program to associated program list. + */ + function addProgram() { + var supportedProgram = angular.copy(vm.selectedProgram); + + vm.programs = vm.programs.filter(function(program) { + return supportedProgram.id !== program.id; + }); + + supportedProgram.supportStartDate = vm.selectedStartDate; + supportedProgram.supportActive = true; + + vm.selectedStartDate = null; + vm.selectedProgram = null; + + vm.facilityWithPrograms.supportedPrograms.push(supportedProgram); + + return $q.when(); + } + + function doSave(facility, successMessage, errorMessage) { + loadingModalService.open(); + return new FacilityRepository().update(facility) + .then(function(facility) { + notificationService.success(successMessage); + goToFacilityList(); + return $q.resolve(facility); + }) + .catch(function() { + notificationService.error(errorMessage); + loadingModalService.close(); + return $q.reject(); + }); + } + + function deleteProgramAssociate(program) { + var confirmMessage = messageService.get('adminFacilityView.question', { + period: program.name + }); + + confirmService.confirm(confirmMessage, + 'adminFacilityView.deleteAssociatedProgram').then(function() { + var loadingPromise = loadingModalService.open(); + tzPeriodService + .deleteProgramAssociate(program.id, vm.facility.id) + .then(function() { + loadingPromise.then(function() { + notificationService.success('adminFacilityView.deleteAssociatedPrograms.success'); + }); + $state.reload('openlmis.administration.facility.view'); + }) + .catch(function() { + loadingModalService.close(); + notificationService.error('adminFacilityView.deleteAssociatedPrograms.fail'); + }); + }); + } + + /** + * @ngdoc method + * @methodOf admin-facility-view.controller:FacilityViewController + * @name addWard + * + * @description + * Adds ward to associated wards list. + */ + function addWard() { + var newWard = angular.copy(vm.newWard); + newWard.facility = { + id: vm.facility.id + }; + + return wardService.getAllWards().then(function(dbWards) { + var wardExistsInDb = wardExists(dbWards.content, newWard); + var wardExistsInLocalState = wardExists(vm.wards, newWard); + + if (wardExistsInDb || wardExistsInLocalState) { + return notifyAndReturn('adminFacilityView.wardExists'); + } + + vm.wards.push(newWard); + vm.newWard = { + disabled: false + }; + + return $q.when(); + }); + + function wardExists(wards, wardToCheck) { + return wards.some(function(ward) { + return wardToCheck.code === ward.code; + }); + } + + function notifyAndReturn(message) { + notificationService.error(message); + return $q.when(); + } + } + + /** + * @ngdoc method + * @methodOf admin-facility-view.controller:FacilityViewController + * @name saveFacilityWards + * + * @description + * Saves facility wards and redirects to facility list screen. + */ + function saveFacilityWards() { + var facilityWards = angular.copy(vm.wards); + + confirmService.confirm( + 'adminFacilityView.savingConfirmation', + 'adminFacilityView.save' + ).then(function() { + loadingModalService.open(); + return new wardService.saveFacilityWards(facilityWards) + .then(function(facilityWards) { + notificationService.success('adminFacilityView.saveWards.success'); + goToFacilityList(); + return $q.resolve(facilityWards); + }) + .catch(function() { + notificationService.error('adminFacilityView.saveWards.fail'); + loadingModalService.close(); + return $q.reject(); + }); + }); + } + } +})(); diff --git a/src/admin-facility-view/facility-view.controller.spec.js b/src/admin-facility-view/facility-view.controller.spec.js new file mode 100644 index 0000000..3acf5bb --- /dev/null +++ b/src/admin-facility-view/facility-view.controller.spec.js @@ -0,0 +1,382 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + *   + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +describe('FacilityViewController', function() { + + beforeEach(function() { + module('admin-facility-view'); + module('referencedata-period'); + + inject(function($injector) { + this.$q = $injector.get('$q'); + this.$rootScope = $injector.get('$rootScope'); + this.$controller = $injector.get('$controller'); + this.$state = $injector.get('$state'); + this.tzPeriodService = $injector.get('tzPeriodService'); + this.wardService = $injector.get('wardService'); + this.notificationService = $injector.get('notificationService'); + this.loadingModalService = $injector.get('loadingModalService'); + this.FacilityRepository = $injector.get('FacilityRepository'); + this.FacilityTypeDataBuilder = $injector.get('FacilityTypeDataBuilder'); + this.GeographicZoneDataBuilder = $injector.get('GeographicZoneDataBuilder'); + this.FacilityOperatorDataBuilder = $injector.get('FacilityOperatorDataBuilder'); + this.ProgramDataBuilder = $injector.get('ProgramDataBuilder'); + this.FacilityDataBuilder = $injector.get('FacilityDataBuilder'); + this.WardDataBuilder = $injector.get('WardDataBuilder'); + this.confirmService = $injector.get('confirmService'); + }); + + spyOn(this.FacilityRepository.prototype, 'update').andReturn(this.$q.when()); + spyOn(this.tzPeriodService, 'tzCreate').andReturn(this.$q.when()); + + this.confirmDeferred = this.$q.defer(); + spyOn(this.confirmService, 'confirm').andReturn(this.confirmDeferred.promise); + + this.saveFacilityDetailsDeferred = this.$q.defer(); + spyOn(this.wardService, 'saveFacilityWards').andReturn(this.saveFacilityDetailsDeferred.promise); + + var loadingModalPromise = this.$q.defer(); + spyOn(this.loadingModalService, 'close').andCallFake(loadingModalPromise.resolve); + spyOn(this.loadingModalService, 'open').andReturn(loadingModalPromise.promise); + + spyOn(this.notificationService, 'success').andReturn(true); + spyOn(this.notificationService, 'error').andReturn(true); + spyOn(this.$state, 'go').andCallFake(loadingModalPromise.resolve); + + this.facilityTypes = [ + new this.FacilityTypeDataBuilder().build(), + new this.FacilityTypeDataBuilder().build() + ]; + + this.geographicZones = [ + new this.GeographicZoneDataBuilder().build(), + new this.GeographicZoneDataBuilder().build() + ]; + + this.facilityOperators = [ + new this.FacilityOperatorDataBuilder().build(), + new this.FacilityOperatorDataBuilder().build() + ]; + + this.programs = [ + new this.ProgramDataBuilder().build(), + new this.ProgramDataBuilder().build() + ]; + + this.wards = [ + new this.WardDataBuilder().build(), + new this.WardDataBuilder().build() + ]; + + this.facility = new this.FacilityDataBuilder().withFacilityType(this.facilityTypes[0]) + .build(); + + this.vm = this.$controller('FacilityViewController', { + facility: this.facility, + facilityTypes: this.facilityTypes, + geographicZones: this.geographicZones, + facilityOperators: this.facilityOperators, + programs: this.programs, + wards: this.wards + }); + this.vm.$onInit(); + }); + + describe('onInit', function() { + + it('should expose goToFacilityList method', function() { + expect(angular.isFunction(this.vm.goToFacilityList)).toBe(true); + }); + + it('should expose saveFacility method', function() { + expect(angular.isFunction(this.vm.saveFacilityDetails)).toBe(true); + }); + + it('should expose addWard method', function() { + expect(angular.isFunction(this.vm.addWard)).toBe(true); + }); + + it('should expose saveFacilityWards method', function() { + expect(angular.isFunction(this.vm.saveFacilityWards)).toBe(true); + }); + + it('should expose facility', function() { + expect(this.vm.facility).toEqual(this.facility); + }); + + it('should expose facilityTypes list', function() { + expect(this.vm.facilityTypes).toEqual(this.facilityTypes); + }); + + it('should expose geographicZones list', function() { + expect(this.vm.geographicZones).toEqual(this.geographicZones); + }); + + it('should expose facilityOperators list', function() { + expect(this.vm.facilityOperators).toEqual(this.facilityOperators); + }); + + it('should expose program list', function() { + expect(this.vm.programs).toEqual(this.programs); + }); + + it('should expose ward list', function() { + expect(this.vm.wards).toEqual(this.wards); + }); + + it('should expose supported programs list', function() { + expect(this.vm.facilityWithPrograms.supportedPrograms).toEqual([]); + }); + + it('should expose supported programs list as empty list if undefined', function() { + this.vm.facility.supportedPrograms = undefined; + this.vm.$onInit(); + + expect(this.vm.facilityWithPrograms.supportedPrograms).toEqual([]); + }); + + it('should expose managedExternally flag', function() { + spyOn(this.facility, 'isManagedExternally').andReturn(true); + + this.vm.$onInit(); + + expect(this.vm.managedExternally).toBe(true); + }); + + it('should expose original facility name', function() { + this.vm.$onInit(); + + expect(this.vm.originalFacilityName).toEqual(this.facility.name); + + this.vm.facility.name += ' (Edited)'; + + expect(this.vm.originalFacilityName).not.toBe(this.vm.facility.name); + }); + }); + + describe('goToFacilityList', function() { + + it('should call state go with correct params', function() { + this.vm.goToFacilityList(); + + expect(this.$state.go).toHaveBeenCalledWith('openlmis.administration.facilities', {}, { + reload: true + }); + }); + }); + + describe('saveFacility', function() { + + it('should open loading modal', function() { + this.vm.saveFacilityDetails(); + this.$rootScope.$apply(); + + expect(this.loadingModalService.open).toHaveBeenCalled(); + }); + + it('should call this.FacilityRepository save method', function() { + this.vm.saveFacilityDetails(); + this.$rootScope.$apply(); + + expect(this.FacilityRepository.prototype.update).toHaveBeenCalledWith(this.vm.facility); + }); + + it('should close loading modal and show error notification after save fails', function() { + this.FacilityRepository.prototype.update.andReturn(this.$q.reject()); + this.vm.saveFacilityDetails(); + this.$rootScope.$apply(); + + expect(this.loadingModalService.close).toHaveBeenCalled(); + expect(this.notificationService.error).toHaveBeenCalledWith('adminFacilityView.saveFacility.fail'); + }); + + it('should go to facility list after successful save', function() { + this.vm.saveFacilityDetails(); + this.$rootScope.$apply(); + + expect(this.$state.go).toHaveBeenCalledWith('openlmis.administration.facilities', {}, { + reload: true + }); + }); + + it('should show success notification after successful save', function() { + this.vm.saveFacilityDetails(); + this.$rootScope.$apply(); + + expect(this.notificationService.success).toHaveBeenCalledWith('adminFacilityView.saveFacility.success'); + }); + }); + + describe('addProgram', function() { + + beforeEach(function() { + this.vm.facilityWithPrograms = {}; + this.vm.facilityWithPrograms.supportedPrograms = []; + }); + + it('should add supported program to the list', function() { + this.vm.selectedProgram = this.vm.programs[0]; + this.vm.selectedStartDate = new Date('08/10/2017'); + + var program = this.vm.selectedProgram; + + this.vm.addProgram(); + + expect(this.vm.facilityWithPrograms.supportedPrograms[0]) + .toEqual(angular.extend({}, program, { + supportStartDate: new Date('08/10/2017'), + supportActive: true + })); + }); + + it('should clear selections', function() { + this.vm.selectedProgram = this.vm.programs[0]; + this.vm.selectedStartDate = new Date('08/10/2017'); + + this.vm.addProgram(); + + expect(this.vm.selectedProgram).toBe(null); + expect(this.vm.selectedStartDate).toBe(null); + }); + + }); + + describe('addWard', function() { + + beforeEach(function() { + this.vm.wards = []; + this.vm.facility = { + id: 'facility-id' + }; + this.vm.newWard = { + code: 'ward-code', + disabled: false + }; + }); + + it('should add new ward to the list', function() { + var newWard = angular.copy(this.vm.newWard); + newWard.facility = { + id: this.vm.facility.id + }; + + spyOn(this.wardService, 'getAllWards').andReturn(this.$q.when({ + content: [] + })); + + this.vm.addWard(); + this.$rootScope.$apply(); + + expect(this.vm.wards[0]).toEqual(newWard); + }); + + it('should clear newWard', function() { + spyOn(this.wardService, 'getAllWards').andReturn(this.$q.when({ + content: [] + })); + + this.vm.addWard(); + this.$rootScope.$apply(); + + expect(this.vm.newWard).toEqual({ + disabled: false + }); + }); + + it('should not add new ward if it exists in db', function() { + var existingWard = angular.copy(this.vm.newWard); + existingWard.facility = { + id: this.vm.facility.id + }; + + spyOn(this.wardService, 'getAllWards').andReturn(this.$q.when({ + content: [existingWard] + })); + + this.vm.addWard(); + this.$rootScope.$apply(); + + expect(this.vm.wards.length).toBe(0); + }); + + it('should not add new ward if it exists in local state', function() { + var existingWard = angular.copy(this.vm.newWard); + existingWard.facility = { + id: this.vm.facility.id + }; + + this.vm.wards.push(existingWard); + + spyOn(this.wardService, 'getAllWards').andReturn(this.$q.when({ + content: [] + })); + + this.vm.addWard(); + this.$rootScope.$apply(); + + expect(this.vm.wards.length).toBe(1); + }); + }); + + describe('saveFacilityWards', function() { + + it('should prompt user for confirmation before saving', function() { + this.vm.saveFacilityWards(); + + expect(this.confirmService.confirm).toHaveBeenCalledWith( + 'adminFacilityView.savingConfirmation', + 'adminFacilityView.save' + ); + }); + + it('should open loading modal after user confirms', function() { + this.confirmDeferred.resolve(); + this.vm.saveFacilityWards(); + this.$rootScope.$apply(); + + expect(this.loadingModalService.open).toHaveBeenCalled(); + }); + + it('should call wardService.saveFacilityWards with correct parameters', function() { + this.confirmDeferred.resolve(); + this.vm.saveFacilityWards(); + this.$rootScope.$apply(); + + expect(this.wardService.saveFacilityWards).toHaveBeenCalledWith(this.vm.wards); + }); + + it('should show success notification and navigate to facility list if saved successfully', function() { + this.confirmDeferred.resolve(); + this.saveFacilityDetailsDeferred.resolve(this.wards); + this.vm.saveFacilityWards(); + this.$rootScope.$apply(); + + expect(this.notificationService.success).toHaveBeenCalledWith('adminFacilityView.saveWards.success'); + expect(this.$state.go).toHaveBeenCalledWith('openlmis.administration.facilities', {}, { + reload: true + }); + }); + + it('should show error notification and close loading modal if wards save has failed', function() { + this.confirmDeferred.resolve(); + this.saveFacilityDetailsDeferred.reject(); + this.vm.saveFacilityWards(); + this.$rootScope.$apply(); + + expect(this.notificationService.error).toHaveBeenCalledWith('adminFacilityView.saveWards.fail'); + expect(this.loadingModalService.close).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/admin-facility-view/facility-view.html b/src/admin-facility-view/facility-view.html new file mode 100644 index 0000000..bea60bb --- /dev/null +++ b/src/admin-facility-view/facility-view.html @@ -0,0 +1,28 @@ +

{{'adminFacilityView.edit' | message: {facility: vm.originalFacilityName} }}

+ \ No newline at end of file diff --git a/src/admin-facility-view/messages_en.json b/src/admin-facility-view/messages_en.json new file mode 100644 index 0000000..4ab751a --- /dev/null +++ b/src/admin-facility-view/messages_en.json @@ -0,0 +1,39 @@ +{ + "adminFacilityView.noAssociatedPrograms": "No programs are associated with ${facility}", + "adminFacilityView.noAssociatedWards": "No services/wards are associated with ${facility}.", + "adminFacilityView.program": "Program", + "adminFacilityView.startDate": "Start Date", + "adminFacilityView.active": "Active", + "adminFacilityView.disabled": "Disabled", + "adminFacilityView.locallyFulfill": "Locally Fulfilled", + "adminFacilityView.cancel": "Cancel", + "adminFacilityView.editFacility": "Edit Facility", + "adminFacilityView.edit": "Edit ${facility}", + "adminFacilityView.save": "Save", + "adminFacilityView.saveFacility.success": "Facility saved successfully!", + "adminFacilityView.saveFacility.fail": "Failed to save facility!", + "adminFacilityView.facilityInformation": "Facility Information", + "adminFacilityView.associatedPrograms": "Associated Programs", + "adminFacilityView.servicesAndWards": "Services & Wards", + "adminFacilityView.add": "Add", + "adminFacilityView.savePrograms": "Save Programs", + "adminFacilityView.saveWards": "Save Services & Wards", + "adminFacilityView.enabled": "Enabled", + "adminFacilityView.startDate.description": "The program start date determines the first date available for users to enter requisitions related to the program.", + "adminFacilityView.deleteProgramAssociate": "Remove", + "adminFacilityView.action": "Action", + "adminFacilityView.question" : "Are you sure you want to remove?", + "adminFacilityView.savingConfirmation" : "Are you sure you want to save Services & Wards?", + "adminFacilityView.deleteAssociatedPrograms.success": "Program Associated deleted successfully", + "adminFacilityView.deleteAssociatedPrograms.fail": "Failed to delete Program Associated", + "adminFacilityView.deleteAssociatedProgram": "Delete", + "adminFacilityView.name": "Name", + "adminFacilityView.description": "Description", + "adminFacilityView.code": "Code", + "adminFacilityView.wardExists": "Ward with the same code already exists and might be attached to different facility. Ensure code uniqueness.", + "adminFacilityView.wardIsDisabled.note": "NOTE: Once a Service/Ward is disabled, it is not available for issuing stock.", + "adminFacilityView.uniqueCodePopover": "Must be unique and is not editable once saved.", + "adminFacilityView.statusPopover": "If checked, the Service/Ward won't be available for issuing stock.", + "adminFacilityView.saveWards.success": "Services & Wards saved successfully!", + "adminFacilityView.saveWards.fail": "Failed to save Services & Wards." +} diff --git a/src/admin-facility-view/services-and-wards.html b/src/admin-facility-view/services-and-wards.html new file mode 100644 index 0000000..4d107c1 --- /dev/null +++ b/src/admin-facility-view/services-and-wards.html @@ -0,0 +1,83 @@ +
+
+ + +
+
+ + +
+
+ + +
+
+

+ {{'adminFacilityView.wardIsDisabled.note' | message}} +

+ +
+ +
+
+ + + + + + + + + + + + + + + + + + +
+ {{'adminFacilityView.noAssociatedWards' | message: {facility: + vm.facility.name} }} +
+ {{'adminFacilityView.code' | message}} + {{'adminFacilityView.name' | message}}{{'adminFacilityView.description' | message}} + {{'adminFacilityView.disabled' | message}} +
+
diff --git a/src/admin-facility-view/ward-data-builder.spec.js b/src/admin-facility-view/ward-data-builder.spec.js new file mode 100644 index 0000000..7ba1c36 --- /dev/null +++ b/src/admin-facility-view/ward-data-builder.spec.js @@ -0,0 +1,51 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + *   + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +(function() { + + 'use strict'; + + angular + .module('admin-facility-view') + .factory('WardDataBuilder', WardDataBuilder); + + function WardDataBuilder() { + + WardDataBuilder.prototype.build = build; + + return WardDataBuilder; + + function WardDataBuilder() { + WardDataBuilder.instanceNumber = (WardDataBuilder.instanceNumber || 0) + 1; + this.code = 'ward-code' + WardDataBuilder.instanceNumber; + this.name = 'ward-name' + WardDataBuilder.instanceNumber; + this.description = 'description' + WardDataBuilder.instanceNumber; + this.disabled = false; + this.facility = { + id: 'facility-id' + WardDataBuilder.instanceNumber + }; + } + + function build() { + return { + code: this.code, + name: this.name, + description: this.description, + disabled: this.disabled, + facility: this.facility + }; + } + } +})(); \ No newline at end of file diff --git a/src/admin-facility-view/ward.service.js b/src/admin-facility-view/ward.service.js new file mode 100644 index 0000000..78007d3 --- /dev/null +++ b/src/admin-facility-view/ward.service.js @@ -0,0 +1,102 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + *   + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +(function() { + + 'use strict'; + + /** + * @ngdoc service + * @name admin-facility-view.wardService + * + * @description + * Responsible for retrieving wards from the server. + */ + angular + .module('admin-facility-view') + .service('wardService', service); + + service.$inject = ['$resource', 'referencedataUrlFactory']; + + function service($resource, referencedataUrlFactory) { + + var resource = $resource(referencedataUrlFactory('/api/wards/:id'), {}, { + getWardsByFacility: { + url: referencedataUrlFactory('/api/wards'), + method: 'GET', + isArray: false + }, + getAllWards: { + url: referencedataUrlFactory('/api/wards'), + method: 'GET', + isArray: false + }, + saveFacilityWards: { + url: referencedataUrlFactory('/api/wards/saveAll'), + method: 'PUT', + isArray: true + } + }); + + return { + getWardsByFacility: getWardsByFacility, + saveFacilityWards: saveFacilityWards, + getAllWards: getAllWards + }; + + /** + * @ngdoc method + * @methodOf admin-facility-view.wardService + * @name getAllWards + * + * @description + * Retrieves all available wards. + * + * @return {Promise} Array of all wards + */ + function getAllWards() { + return resource.getAllWards().$promise; + } + + /** + * @ngdoc method + * @methodOf admin-facility-view.wardService + * @name getWardsByFacility + * + * @description + * Retrieves wards for the facility. + * + * @param {String} facilityId Facility UUID + * @return {Promise} Array of wards + */ + function getWardsByFacility(queryParams) { + return resource.getWardsByFacility(queryParams).$promise; + } + + /** + * @ngdoc method + * @methodOf admin-facility-view.wardService + * @name saveFacilityWards + * + * @description + * Saves facility wards. + * + * @return {Promise} Saved wards promise + */ + function saveFacilityWards(wards) { + return resource.saveFacilityWards(wards).$promise; + } + } +})(); \ No newline at end of file diff --git a/src/admin-facility-view/ward.service.spec.js b/src/admin-facility-view/ward.service.spec.js new file mode 100644 index 0000000..38c9adc --- /dev/null +++ b/src/admin-facility-view/ward.service.spec.js @@ -0,0 +1,91 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + *   + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +describe('wardService', function() { + beforeEach(function() { + module('admin-facility-view'); + + inject(function($injector) { + this.$rootScope = $injector.get('$rootScope'); + this.wardService = $injector.get('wardService'); + this.$httpBackend = $injector.get('$httpBackend'); + this.referencedataUrlFactory = $injector.get('referencedataUrlFactory'); + this.WardDataBuilder = $injector.get('WardDataBuilder'); + }); + + this.ward1 = new this.WardDataBuilder().build(); + this.ward2 = new this.WardDataBuilder().build(); + + this.wards = [ + this.ward1, + this.ward2 + ]; + }); + + it('should get all wards', function() { + this.$httpBackend + .expectGET(this.referencedataUrlFactory('/api/wards')) + .respond(200, { + content: this.wards + }); + + var result; + this.wardService.getAllWards().then(function(response) { + result = response.content; + }); + + this.$httpBackend.flush(); + this.$rootScope.$apply(); + + expect(angular.toJson(result)).toEqual(angular.toJson(this.wards)); + }); + + it('should get wards by facility', function() { + var facilityId = '123'; + this.$httpBackend + .expectGET(this.referencedataUrlFactory('/api/wards?facilityId=' + facilityId)) + .respond(200, { + content: this.wards + }); + + var result; + this.wardService.getWardsByFacility({ + facilityId: facilityId + }).then(function(response) { + result = response.content; + }); + + this.$httpBackend.flush(); + this.$rootScope.$apply(); + + expect(result).toEqual(this.wards); + }); + + it('should save facility wards', function() { + this.$httpBackend + .expectPUT(this.referencedataUrlFactory('/api/wards/saveAll'), this.wards) + .respond(200, this.wards); + + var result; + this.wardService.saveFacilityWards(this.wards).then(function(response) { + result = response; + }); + + this.$httpBackend.flush(); + this.$rootScope.$apply(); + + expect(angular.toJson(result)).toEqual(angular.toJson(this.wards)); + }); +}); \ No newline at end of file diff --git a/src/referencedata-period/tz-period.service.js b/src/referencedata-period/tz-period.service.js new file mode 100644 index 0000000..799e02a --- /dev/null +++ b/src/referencedata-period/tz-period.service.js @@ -0,0 +1,145 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + *   + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.  + */ + +(function() { + + 'use strict'; + + /** + * @ngdoc service + * @name referencedata-period.periodService + * + * @description + * Responsible for retrieving all processing period information from the server. + */ + angular + .module('referencedata-period') + .service('tzPeriodService', service); + + service.$inject = ['$q', '$resource', 'referencedataUrlFactory', 'openlmisDateFilter', + 'dateUtils']; + + function service($q, $resource, referencedataUrlFactory, openlmisDateFilter, dateUtils) { + + var resource = $resource(referencedataUrlFactory('/api/tzProcessingPeriods/:id'), {}, { + + deleteProgramAssociate: { + url: referencedataUrlFactory('/api/tzProcessingPeriods/:programId/:facilityId/supportedPrograms'), + method: 'DELETE', + isArray: true + }, + + tzCreate: { + url: referencedataUrlFactory('/api/tzProcessingPeriods/create'), + method: 'POST', + isArray: false + } + }); + this.deleteProcessingPeriod = deleteProcessingPeriod; + this.deleteProgramAssociate = deleteProgramAssociate; + this.tzCreate = tzCreate; + + /** + * @ngdoc method + * @name delete + * @methodOf referencedata-period.periodService + * + * @description + * Delete specified processing period. + * + * @param {Object} periodId period to delete. + * @return {Promise} deleted Period + */ + function deleteProcessingPeriod(periodId) { + return resource.delete({ + id: periodId + }).$promise; + } + + /** + * @ngdoc method + * @name delete + * @methodOf referencedata-period.periodService + * + * @description + * Delete specified processing period. + * + * @param {Object} programId program to delete. + * @param {Object} facilityId facility to delete. + * @return {Promise} deleted Period + */ + function deleteProgramAssociate(programId, facilityId) { + return resource.deleteProgramAssociate({ + programId: programId, + facilityId: facilityId + }).$promise; + } + + /** + * @ngdoc method + * @methodOf referencedata-period.periodService + * @name toStringDateUTC + * + * @description + * Transforms dates from Date to string at UTC 00:00. + * + * @param {Date} date date to be parsed + * @return {String} parsed date string in format yyyy-MM-dd. + */ + function toStringDateUTC(date) { + return openlmisDateFilter(date, 'yyyy-MM-dd'); + } + + /** + * @ngdoc method + * @name create + * @methodOf referencedata-period.periodService + * + * @description + * Creates processing periods. + * + * @param {Object} period new Period + * @return {Promise} created Period + */ + function tzCreate(period) { + period.startDate = toBackendFormat(period.startDate); + period.endDate = toBackendFormat(period.endDate); + return resource.tzCreate(period).$promise; + } + + /** + * @ngdoc method + * @methodOf referencedata-period.periodService + * @name toBackendFormat + * + * @description + * Transforms dates from Date, String or Array of numbers + * to string at UTC 00:00. + * + * @param {Date | String | Array} date to be parsed + * @return {String} parsed date string in format yyyy-MM-dd. + */ + function toBackendFormat(date) { + var parsed; + if (date instanceof Date) { + parsed = date; + } else { + parsed = dateUtils.toDate(date); + } + return (parsed) ? toStringDateUTC(parsed) : undefined; + } + + } +})(); From c35674ada3ced29ed48ddf18eeb701ce578ddcdc Mon Sep 17 00:00:00 2001 From: olewandowski1 Date: Fri, 19 Apr 2024 13:51:31 +0200 Subject: [PATCH 2/2] OAM-45: add missing empty lines --- src/admin-facility-view/facility-view.html | 2 +- src/admin-facility-view/ward-data-builder.spec.js | 2 +- src/admin-facility-view/ward.service.js | 2 +- src/admin-facility-view/ward.service.spec.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/admin-facility-view/facility-view.html b/src/admin-facility-view/facility-view.html index bea60bb..3c0195b 100644 --- a/src/admin-facility-view/facility-view.html +++ b/src/admin-facility-view/facility-view.html @@ -25,4 +25,4 @@

{{'adminFacilityView.edit' | message: {facility: vm.originalFacilityName} }} - \ No newline at end of file + diff --git a/src/admin-facility-view/ward-data-builder.spec.js b/src/admin-facility-view/ward-data-builder.spec.js index 7ba1c36..c92cf6d 100644 --- a/src/admin-facility-view/ward-data-builder.spec.js +++ b/src/admin-facility-view/ward-data-builder.spec.js @@ -48,4 +48,4 @@ }; } } -})(); \ No newline at end of file +})(); diff --git a/src/admin-facility-view/ward.service.js b/src/admin-facility-view/ward.service.js index 78007d3..ecf7613 100644 --- a/src/admin-facility-view/ward.service.js +++ b/src/admin-facility-view/ward.service.js @@ -99,4 +99,4 @@ return resource.saveFacilityWards(wards).$promise; } } -})(); \ No newline at end of file +})(); diff --git a/src/admin-facility-view/ward.service.spec.js b/src/admin-facility-view/ward.service.spec.js index 38c9adc..6b347ad 100644 --- a/src/admin-facility-view/ward.service.spec.js +++ b/src/admin-facility-view/ward.service.spec.js @@ -88,4 +88,4 @@ describe('wardService', function() { expect(angular.toJson(result)).toEqual(angular.toJson(this.wards)); }); -}); \ No newline at end of file +});