diff --git a/client/src/js/services/util.js b/client/src/js/services/util.js index a7e212d288..c8c2dfc32d 100644 --- a/client/src/js/services/util.js +++ b/client/src/js/services/util.js @@ -27,8 +27,6 @@ function UtilService(moment) { } } - - /** @todo comments showing usage */ service.filterFormElements = function filterFormElements(formDefinition, requireDirty) { var response = {}; @@ -270,4 +268,4 @@ function UtilService(moment) { return array.indexOf(value) !== -1; }); }; -} \ No newline at end of file +} diff --git a/client/src/modules/cash/payments/templates/search.modal.js b/client/src/modules/cash/payments/templates/search.modal.js index b0dbf8ff73..45b7e9fe2c 100644 --- a/client/src/modules/cash/payments/templates/search.modal.js +++ b/client/src/modules/cash/payments/templates/search.modal.js @@ -2,38 +2,41 @@ angular.module('bhima.controllers') .controller('SearchCashPaymentModalController', SearchCashPaymentModalController); SearchCashPaymentModalController.$inject = [ - 'NotifyService', '$uibModalInstance', 'filters', 'Store', 'PeriodService', 'util', 'CashService', 'CurrencyService', + 'NotifyService', '$uibModalInstance', 'filters', 'Store', 'PeriodService', + 'util', 'CashService', 'CurrencyService', ]; /** * Search Cash Payment controller * * @description - * This controller powers the Invoice Search modal. Invoices are passed in from the registry as + * This controller powers the Cash Search modal. Cash filters are passed in from the registry as * POJO and are attached to the view. They are modified here and returned to the parent controller * as a POJO. */ function SearchCashPaymentModalController(Notify, Instance, filters, Store, Periods, util, Cash, Currencies) { - var vm = this; - var changes = new Store({ identifier : 'key' }); - // @TODO ideally these should be passed in when the modal is initialised - // these are known when the filter service is defined - var searchQueryOptions = [ - 'is_caution', 'reference', 'cashbox_id', 'user_id', 'reference_patient', 'currency_id', 'reversed', 'debtor_group_uuid', - ]; + const vm = this; + const changes = new Store({ identifier : 'key' }); - vm.filters = filters; + const searchQueryOptions = [ + 'is_caution', 'reference', 'cashbox_id', 'user_id', 'reference_patient', + 'currency_id', 'reversed', 'debtor_group_uuid', + ]; vm.searchQueries = {}; vm.defaultQueries = {}; // displayValues will be an id:displayValue pair - var displayValues = {}; - var lastDisplayValues = Cash.filters.getDisplayValueMap(); + const displayValues = {}; + const lastDisplayValues = Cash.filters.getDisplayValueMap(); // assign already defined custom filters to searchQueries object vm.searchQueries = util.maskObjectFromKeys(filters, searchQueryOptions); + // keep track of the initial search queries to make sure we properly restore + // default display values + const initialSearchQueries = angular.copy(vm.searchQueries); + // assign default filters if (filters.limit) { vm.defaultQueries.limit = filters.limit; @@ -51,9 +54,9 @@ function SearchCashPaymentModalController(Notify, Instance, filters, Store, Peri // load all the available currencies Currencies.read() - .then(function (currencies) { + .then(currencies => { // cache a label for faster view rendering - currencies.forEach(function (currency) { + currencies.forEach(currency => { currency.label = Currencies.format(currency.id); }); @@ -75,7 +78,7 @@ function SearchCashPaymentModalController(Notify, Instance, filters, Store, Peri }; vm.setCurrency = function setCurrency(currencyId) { - vm.currencies.forEach(function (currency) { + vm.currencies.forEach(currency => { if (currency.id === currencyId) { displayValues.currency_id = currency.label; } @@ -84,9 +87,9 @@ function SearchCashPaymentModalController(Notify, Instance, filters, Store, Peri // default filter period - directly write to changes list vm.onSelectPeriod = function onSelectPeriod(period) { - var periodFilters = Periods.processFilterChanges(period); + const periodFilters = Periods.processFilterChanges(period); - periodFilters.forEach(function (filterChange) { + periodFilters.forEach(filterChange => { changes.post(filterChange); }); }; @@ -95,11 +98,10 @@ function SearchCashPaymentModalController(Notify, Instance, filters, Store, Peri vm.onSelectLimit = function onSelectLimit(value) { // input is type value, this will only be defined for a valid number if (angular.isDefined(value)) { - changes.post({ key : 'limit', value : value }); + changes.post({ key : 'limit', value }); } }; - // deletes a filter from the custom filter object, this key will no longer be written to changes on exit vm.clear = function clear(key) { delete vm.searchQueries[key]; @@ -108,15 +110,24 @@ function SearchCashPaymentModalController(Notify, Instance, filters, Store, Peri // returns the filters to the journal to be used to refresh the page vm.submit = function submit() { // push all searchQuery values into the changes array to be applied - angular.forEach(vm.searchQueries, function (value, key) { + angular.forEach(vm.searchQueries, (value, key) => { if (angular.isDefined(value)) { - // default to the original value if no display value is defined - var displayValue = displayValues[key] || lastDisplayValues[key] || value; - changes.post({ key: key, value: value, displayValue: displayValue }); - } + + // To avoid overwriting a real display value, we first determine if the value changed in the current view. + // If so, we do not use the previous display value. If the values are identical, we can restore the + // previous display value without fear of data being out of date. + const usePreviousDisplayValue = + angular.equals(initialSearchQueries[key], value) && + angular.isDefined(lastDisplayValues[key]); + + // default to the raw value if no display value is defined + const displayValue = usePreviousDisplayValue ? lastDisplayValues[key] : displayValues[key] || value; + + changes.post({ key, value, displayValue }); + } }); - var loggedChanges = changes.getAll(); + const loggedChanges = changes.getAll(); // return values to the CashController return Instance.close(loggedChanges); diff --git a/client/src/modules/invoices/registry/search.modal.js b/client/src/modules/invoices/registry/search.modal.js index 4afc75fe7a..ecaf95cbe8 100644 --- a/client/src/modules/invoices/registry/search.modal.js +++ b/client/src/modules/invoices/registry/search.modal.js @@ -13,15 +13,15 @@ InvoiceRegistrySearchModalController.$inject = [ * the underlying filters before passing them back to the parent controller. */ function InvoiceRegistrySearchModalController(ModalInstance, filters, Notify, Store, Periods, util, Invoices) { - var vm = this; - var changes = new Store({ identifier : 'key' }); + const vm = this; + const changes = new Store({ identifier : 'key' }); vm.filters = filters; vm.defaultQueries = {}; // displayValues will be an id:displayValue pair - var displayValues = {}; - var lastDisplayValues = Invoices.filters.getDisplayValueMap(); + const displayValues = {}; + const lastDisplayValues = Invoices.filters.getDisplayValueMap(); // assign default limit filter if (filters.limit) { @@ -30,13 +30,18 @@ function InvoiceRegistrySearchModalController(ModalInstance, filters, Notify, St // @TODO ideally these should be passed in when the modal is initialised // these are known when the filter service is defined - var searchQueryOptions = [ - 'is_caution', 'reference', 'cashbox_id', 'user_id', 'reference_patient', 'currency_id', 'reversed', 'service_id', 'debtor_group_uuid', + const searchQueryOptions = [ + 'is_caution', 'reference', 'cashbox_id', 'user_id', 'reference_patient', + 'currency_id', 'reversed', 'service_id', 'debtor_group_uuid', ]; // assign already defined custom filters to searchQueries object vm.searchQueries = util.maskObjectFromKeys(filters, searchQueryOptions); + // keep track of the initial search queries to make sure we properly restore + // default display values + const initialSearchQueries = angular.copy(vm.searchQueries); + // set controller data vm.cancel = ModalInstance.close; @@ -62,15 +67,15 @@ function InvoiceRegistrySearchModalController(ModalInstance, filters, Notify, St vm.onSelectLimit = function onSelectLimit(value) { // input is type value, this will only be defined for a valid number if (angular.isDefined(value)) { - changes.post({ key : 'limit', value : value }); + changes.post({ key : 'limit', value }); } }; // default filter period - directly write to changes list vm.onSelectPeriod = function onSelectPeriod(period) { - var periodFilters = Periods.processFilterChanges(period); + const periodFilters = Periods.processFilterChanges(period); - periodFilters.forEach(function (filterChange) { + periodFilters.forEach(filterChange => { changes.post(filterChange); }); }; @@ -83,15 +88,23 @@ function InvoiceRegistrySearchModalController(ModalInstance, filters, Notify, St // returns the filters to the journal to be used to refresh the page vm.submit = function submit() { // push all searchQuery values into the changes array to be applied - angular.forEach(vm.searchQueries, function (value, key) { + angular.forEach(vm.searchQueries, (value, key) => { if (angular.isDefined(value)) { - // default to the original value if no display value is defined - var displayValue = displayValues[key] || lastDisplayValues[key] || value; - changes.post({ key: key, value: value, displayValue: displayValue }); + // To avoid overwriting a real display value, we first determine if the value changed in the current view. + // If so, we do not use the previous display value. If the values are identical, we can restore the + // previous display value without fear of data being out of date. + const usePreviousDisplayValue = + angular.equals(initialSearchQueries[key], value) && + angular.isDefined(lastDisplayValues[key]); + + // default to the raw value if no display value is defined + const displayValue = usePreviousDisplayValue ? lastDisplayValues[key] : displayValues[key] || value; + + changes.post({ key, value, displayValue }); } }); - var loggedChanges = changes.getAll(); + const loggedChanges = changes.getAll(); // return values to the Invoice Registry Controller return ModalInstance.close(loggedChanges); diff --git a/client/src/modules/patients/registry/search.modal.js b/client/src/modules/patients/registry/search.modal.js index 3e93ca278e..8be4c57ea1 100644 --- a/client/src/modules/patients/registry/search.modal.js +++ b/client/src/modules/patients/registry/search.modal.js @@ -2,8 +2,7 @@ angular.module('bhima.controllers') .controller('PatientRegistryModalController', PatientRegistryModalController); PatientRegistryModalController.$inject = [ - '$uibModalInstance', 'filters', - 'bhConstants', 'moment', 'Store', 'util', 'PeriodService', + '$uibModalInstance', 'filters', 'Store', 'util', 'PeriodService', 'PatientService', ]; /** @@ -14,15 +13,17 @@ PatientRegistryModalController.$inject = [ * search functionality on the patient registry page. Filters that are already * applied to the grid can be passed in via the filters inject. */ -function PatientRegistryModalController(ModalInstance, filters, bhConstants, moment, Store, util, Periods) { +function PatientRegistryModalController(ModalInstance, filters, Store, util, Periods, Patients) { const vm = this; const changes = new Store({ identifier : 'key' }); + // displayValues will be an id:displayValue pair const displayValues = {}; - // @TODO ideally these should be passed in when the modal is initialised these are known when the filter service is defined + const searchQueryOptions = [ - 'display_name', 'sex', 'hospital_no', 'reference', 'dateBirthFrom', 'dateBirthTo', 'dateRegistrationFrom', 'dateRegistrationTo', - 'debtor_group_uuid', 'patient_group_uuid', 'user_id', 'defaultPeriod', + 'display_name', 'sex', 'hospital_no', 'reference', 'dateBirthFrom', 'dateBirthTo', + 'dateRegistrationFrom', 'dateRegistrationTo', 'debtor_group_uuid', + 'patient_group_uuid', 'user_id', 'defaultPeriod', ]; vm.filters = filters; @@ -36,9 +37,15 @@ function PatientRegistryModalController(ModalInstance, filters, bhConstants, mom vm.defaultQueries.limit = filters.limit; } + const lastDisplayValues = Patients.filters.getDisplayValueMap(); + // assign already defined custom filters to searchQueries object vm.searchQueries = util.maskObjectFromKeys(filters, searchQueryOptions); + // keep track of the initial search queries to make sure we properly restore + // default display values + const initialSearchQueries = angular.copy(vm.searchQueries); + // bind methods vm.submit = submit; vm.cancel = cancel; @@ -81,11 +88,20 @@ function PatientRegistryModalController(ModalInstance, filters, bhConstants, mom // returns the parameters to the parent controller function submit() { // push all searchQuery values into the changes array to be applied - angular.forEach(vm.searchQueries, (val, _key) => { - if (angular.isDefined(val)) { - // default to the original value if no display value is defined - const displayVal = displayValues[_key] || val; - changes.post({ key : _key, value: val, displayValue : displayVal }); + angular.forEach(vm.searchQueries, (value, key) => { + if (angular.isDefined(value)) { + + // To avoid overwriting a real display value, we first determine if the value changed in the current view. + // If so, we do not use the previous display value. If the values are identical, we can restore the + // previous display value without fear of data being out of date. + const usePreviousDisplayValue = + angular.equals(initialSearchQueries[key], value) && + angular.isDefined(lastDisplayValues[key]); + + // default to the raw value if no display value is defined + const displayValue = usePreviousDisplayValue ? lastDisplayValues[key] : displayValues[key] || value; + + changes.post({ key, value, displayValue }); } }); diff --git a/client/src/modules/vouchers/modals/search.modal.js b/client/src/modules/vouchers/modals/search.modal.js index 9d5d7fb4d2..f802c709e0 100644 --- a/client/src/modules/vouchers/modals/search.modal.js +++ b/client/src/modules/vouchers/modals/search.modal.js @@ -15,8 +15,8 @@ VoucherRegistrySearchModalController.$inject = [ * preset by passing in a filters object using filtersProvider(). */ function VoucherRegistrySearchModalController( - ModalInstance, filters, Notify, Periods, Store, util, - Vouchers, TransactionTypeService, $translate, + ModalInstance, filters, Notify, Periods, Store, util, Vouchers, + TransactionTypeService, $translate, ) { const vm = this; const changes = new Store({ identifier : 'key' }); @@ -28,14 +28,18 @@ function VoucherRegistrySearchModalController( const displayValues = {}; const lastDisplayValues = Vouchers.filters.getDisplayValueMap(); - vm.filters = filters; // searchQueries is the same id:value pair vm.searchQueries = {}; vm.defaultQueries = {}; + // assign already defined custom filters to searchQueries object vm.searchQueries = util.maskObjectFromKeys(filters, searchQueryOptions); + // keep track of the initial search queries to make sure we properly restore + // default display values + const initialSearchQueries = angular.copy(vm.searchQueries); + if (filters.limit) { vm.defaultQueries.limit = filters.limit; } @@ -101,9 +105,6 @@ function VoucherRegistrySearchModalController( // submit the filter object to the parent controller. vm.submit = function submit(form) { - let _displayValue; - - if (form.$invalid) { return; } // delete type_ids if there is no transaction type sent @@ -111,11 +112,20 @@ function VoucherRegistrySearchModalController( vm.clear('type_ids'); } + // push all searchQuery values into the changes array to be applied angular.forEach(vm.searchQueries, (_value, _key) => { if (angular.isDefined(_value)) { - // default to the original value if no display value is defined - _displayValue = displayValues[_key] || lastDisplayValues[_key] || _value; + + // To avoid overwriting a real display value, we first determine if the value changed in the current view. + // If so, we do not use the previous display value. If the values are identical, we can restore the + // previous display value without fear of data being out of date. + const usePreviousDisplayValue = + angular.equals(initialSearchQueries[_key], _value) && + angular.isDefined(lastDisplayValues[_key]); + + // default to the raw value if no display value is defined + const _displayValue = usePreviousDisplayValue ? lastDisplayValues[_key] : displayValues[_key] || _value; changes.post({ key : _key, value : _value, displayValue : _displayValue }); } });