Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Upcoming Version (WIP)
==================

Improvements:
* [OLMIS-8238](https://openlmis.atlassian.net/browse/OLMIS-8238): Add facility-level packs/doses display configuration.
* [SELV3-748](https://openlmis.atlassian.net/browse/SELV3-748) Resolved cookie issue causing untranslated warning messages

7.2.15 / 2026-02-05
Expand Down
137 changes: 137 additions & 0 deletions src/openlmis-quantity-unit-toggle/quantity-unit-config.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* 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 openlmis-quantity-unit-toggle.quantityUnitConfigService
*
* @description
* Holds the current quantity unit display mode (PACKS, DOSES or BOTH). The mode defaults to the
* build-time ${QUANTITY_UNIT_OPTION} placeholder, but may be overridden at runtime (e.g. from the
* current user's home facility configuration) via setMode.
*/
angular
.module('openlmis-quantity-unit-toggle')
.service('quantityUnitConfigService', service);

service.$inject = ['QUANTITY_UNIT', 'localStorageService'];

function service(QUANTITY_UNIT, localStorageService) {

var QUANTITY_UNIT_KEY = 'quantityUnit';
var VALID_MODES = [QUANTITY_UNIT.PACKS, QUANTITY_UNIT.DOSES, QUANTITY_UNIT.BOTH];
var VALID_UNITS = [QUANTITY_UNIT.PACKS, QUANTITY_UNIT.DOSES];

var defaultMode = toValid(VALID_MODES, '${QUANTITY_UNIT_OPTION}', QUANTITY_UNIT.BOTH);
var defaultUnit = toValid(VALID_UNITS, '${DEFAULT_QUANTITY_UNIT}', QUANTITY_UNIT.DOSES);
var mode = defaultMode;

this.getMode = getMode;
this.getEffectiveUnit = getEffectiveUnit;
this.setSelectedUnit = setSelectedUnit;
this.setMode = setMode;
this.resetMode = resetMode;

/**
* @ngdoc method
* @methodOf openlmis-quantity-unit-toggle.quantityUnitConfigService
* @name getMode
*
* @description
* Returns the current display mode (PACKS, DOSES or BOTH).
*/
function getMode() {
return mode;
}

/**
* @ngdoc method
* @methodOf openlmis-quantity-unit-toggle.quantityUnitConfigService
* @name getEffectiveUnit
*
* @description
* Resolves the unit (PACKS or DOSES) that should currently be displayed. When the facility
* mode forces a single unit it is returned and any cached user choice is ignored; when the
* mode is BOTH the user's last choice from local storage is used, falling back to the
* default unit. This is the single source of truth for all consumers.
*
* @return {String} PACKS or DOSES
*/
function getEffectiveUnit() {
if (mode !== QUANTITY_UNIT.BOTH) {
return mode;
}

var cachedQuantityUnit = localStorageService.get(QUANTITY_UNIT_KEY);
return cachedQuantityUnit === null ? defaultUnit : cachedQuantityUnit;
}

/**
* @ngdoc method
* @methodOf openlmis-quantity-unit-toggle.quantityUnitConfigService
* @name setSelectedUnit
*
* @description
* Persists the unit the user selected on the toggle so it becomes their default on the next
* page. Only meaningful while the mode is BOTH.
*
* @param {String} unit PACKS or DOSES
*/
function setSelectedUnit(unit) {
if (VALID_UNITS.indexOf(unit) !== -1) {
localStorageService.add(QUANTITY_UNIT_KEY, unit);
}
}

/**
* @ngdoc method
* @methodOf openlmis-quantity-unit-toggle.quantityUnitConfigService
* @name setMode
*
* @description
* Overrides the display mode. Invalid values are ignored so a misconfigured facility cannot
* break the toggle.
*
* @param {String} newMode one of PACKS, DOSES, BOTH
*/
function setMode(newMode) {
if (VALID_MODES.indexOf(newMode) !== -1) {
mode = newMode;
}
}

/**
* @ngdoc method
* @methodOf openlmis-quantity-unit-toggle.quantityUnitConfigService
* @name resetMode
*
* @description
* Resets the mode back to the build-time default. Called at the start of each post-login
* flow (and on logout) so a user without facility-level configuration does not inherit the
* previous user's facility mode.
*/
function resetMode() {
mode = defaultMode;
}

function toValid(allowed, value, fallback) {
return allowed.indexOf(value) === -1 ? fallback : value;
}
}
})();
122 changes: 122 additions & 0 deletions src/openlmis-quantity-unit-toggle/quantity-unit-config.service.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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('quantityUnitConfigService', function() {

beforeEach(function() {
module('openlmis-quantity-unit-toggle', function($provide) {
$provide.value('featureFlagService', {
set: function() {},
get: function() {}
});
});

inject(function($injector) {
this.quantityUnitConfigService = $injector.get('quantityUnitConfigService');
this.QUANTITY_UNIT = $injector.get('QUANTITY_UNIT');
this.localStorageService = $injector.get('localStorageService');
});
});

describe('getMode', function() {

it('should default to BOTH when no facility mode is set', function() {
expect(this.quantityUnitConfigService.getMode()).toEqual(this.QUANTITY_UNIT.BOTH);
});

it('should return mode set via setMode', function() {
this.quantityUnitConfigService.setMode(this.QUANTITY_UNIT.PACKS);

expect(this.quantityUnitConfigService.getMode()).toEqual(this.QUANTITY_UNIT.PACKS);
});

it('should ignore an invalid mode and keep the previous one', function() {
this.quantityUnitConfigService.setMode('NONSENSE');

expect(this.quantityUnitConfigService.getMode()).toEqual(this.QUANTITY_UNIT.BOTH);
});

it('should ignore an undefined mode and keep the previous one', function() {
this.quantityUnitConfigService.setMode(this.QUANTITY_UNIT.DOSES);
this.quantityUnitConfigService.setMode(undefined);

expect(this.quantityUnitConfigService.getMode()).toEqual(this.QUANTITY_UNIT.DOSES);
});
});

describe('resetMode', function() {

it('should reset the mode back to the build-time default', function() {
this.quantityUnitConfigService.setMode(this.QUANTITY_UNIT.PACKS);

this.quantityUnitConfigService.resetMode();

expect(this.quantityUnitConfigService.getMode()).toEqual(this.QUANTITY_UNIT.BOTH);
});
});

describe('getEffectiveUnit', function() {

it('should force PACKS and ignore storage when mode is PACKS', function() {
spyOn(this.localStorageService, 'get').andReturn(this.QUANTITY_UNIT.DOSES);
this.quantityUnitConfigService.setMode(this.QUANTITY_UNIT.PACKS);

expect(this.quantityUnitConfigService.getEffectiveUnit()).toEqual(this.QUANTITY_UNIT.PACKS);
expect(this.localStorageService.get).not.toHaveBeenCalled();
});

it('should force DOSES and ignore storage when mode is DOSES', function() {
spyOn(this.localStorageService, 'get').andReturn(this.QUANTITY_UNIT.PACKS);
this.quantityUnitConfigService.setMode(this.QUANTITY_UNIT.DOSES);

expect(this.quantityUnitConfigService.getEffectiveUnit()).toEqual(this.QUANTITY_UNIT.DOSES);
expect(this.localStorageService.get).not.toHaveBeenCalled();
});

it('should use the stored unit when mode is BOTH', function() {
spyOn(this.localStorageService, 'get').andReturn(this.QUANTITY_UNIT.PACKS);
this.quantityUnitConfigService.setMode(this.QUANTITY_UNIT.BOTH);

expect(this.quantityUnitConfigService.getEffectiveUnit()).toEqual(this.QUANTITY_UNIT.PACKS);
expect(this.localStorageService.get).toHaveBeenCalledWith('quantityUnit');
});

it('should fall back to the default unit when mode is BOTH and storage is empty', function() {
spyOn(this.localStorageService, 'get').andReturn(null);
this.quantityUnitConfigService.setMode(this.QUANTITY_UNIT.BOTH);

expect(this.quantityUnitConfigService.getEffectiveUnit()).toEqual(this.QUANTITY_UNIT.DOSES);
});
});

describe('setSelectedUnit', function() {

it('should store the chosen unit under the quantityUnit key', function() {
spyOn(this.localStorageService, 'add');

this.quantityUnitConfigService.setSelectedUnit(this.QUANTITY_UNIT.PACKS);

expect(this.localStorageService.add).toHaveBeenCalledWith('quantityUnit', this.QUANTITY_UNIT.PACKS);
});

it('should ignore an invalid unit and not store it', function() {
spyOn(this.localStorageService, 'add');

this.quantityUnitConfigService.setSelectedUnit('NONSENSE');

expect(this.localStorageService.add).not.toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@
.controller('QuantityUnitToggleController', QuantityUnitToggleController);

QuantityUnitToggleController.$inject = [
'messageService', 'QUANTITY_UNIT', 'localStorageService'
'messageService', 'QUANTITY_UNIT', 'quantityUnitConfigService', '$rootScope'
];

var QUANTITY_UNIT_KEY = 'quantityUnit';

function QuantityUnitToggleController(messageService, QUANTITY_UNIT, localStorageService) {
function QuantityUnitToggleController(messageService, QUANTITY_UNIT, quantityUnitConfigService,
$rootScope) {

var vm = this;

Expand Down Expand Up @@ -79,12 +78,8 @@
QUANTITY_UNIT.PACKS,
QUANTITY_UNIT.DOSES
];
var cachedQuantityUnit = localStorageService.get(QUANTITY_UNIT_KEY);
if (cachedQuantityUnit === null) {
vm.quantityUnit = QUANTITY_UNIT.$getDefaultQuantityUnit();
} else {
vm.quantityUnit = cachedQuantityUnit;
}

vm.quantityUnit = quantityUnitConfigService.getEffectiveUnit();
}

/**
Expand All @@ -108,7 +103,8 @@
* Handles change in toggle.
*/
function onChange() {
localStorageService.add(QUANTITY_UNIT_KEY, vm.quantityUnit);
quantityUnitConfigService.setSelectedUnit(vm.quantityUnit);
$rootScope.$emit('openlmis.quantityUnit.selected', vm.quantityUnit);
}

/**
Expand All @@ -120,7 +116,7 @@
* Returns information about quantity unit toggle visibility.
*/
function isQuantityUnitToggleVisible() {
return '${QUANTITY_UNIT_OPTION}' === QUANTITY_UNIT.BOTH;
return quantityUnitConfigService.getMode() === QUANTITY_UNIT.BOTH;
}

}
Expand Down
Loading
Loading