Skip to content

Commit

Permalink
feat(journal): implement advanced filtering
Browse files Browse the repository at this point in the history
This commit implements advanced filtering on the Posting Journal through
a modal form.  The module uses bhFiltersApplied to display the filters
currently applied to the posting journal at the top of the dash.

Filters are cached so that they survive page reloads.  All filtering is
done using the journal API from the server.

See #840.  Closes #793.
  • Loading branch information
Jonathan Niles authored and sfount committed Nov 15, 2016
1 parent 78e3ed3 commit 3372432
Show file tree
Hide file tree
Showing 12 changed files with 345 additions and 53 deletions.
4 changes: 4 additions & 0 deletions client/src/css/structure.css
Original file line number Diff line number Diff line change
Expand Up @@ -798,3 +798,7 @@ growl-notification.fading.ng-leave.ng-leave-active {
display: flex;
justify-content: space-between;
}

.modal-tall {
max-height: 80vh;
}
8 changes: 8 additions & 0 deletions client/src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -432,11 +432,19 @@ function compileConfig($compileProvider) {
}
}

/**
* Configure global properties about ui-select
*/
function uiSelectConfig(uiSelectConfig) {
uiSelectConfig.theme = 'bootstrap';
}

bhima.constant('bhConstants', constantConfig());

// configure services, providers, factories
bhima.config(['$stateProvider', '$urlRouterProvider', '$urlMatcherFactoryProvider', bhimaConfig]);
bhima.config(['$translateProvider', translateConfig]);
bhima.config(['uiSelectConfig', uiSelectConfig]);
bhima.config(['tmhDynamicLocaleProvider', localeConfig]);
bhima.config(['$localStorageProvider', localStorageConfig]);
bhima.config(['$httpProvider', httpConfig]);
Expand Down
39 changes: 19 additions & 20 deletions client/src/js/components/bhDateInterval.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,25 @@
* </bh-date-interval>
* ```
*/
angular.module('bhima.components').component('bhDateInterval', {
templateUrl : '/partials/templates/bhDateInterval.tmpl.html',
controller : bhDateInterval,
bindings : {
validationTrigger : '<', // validation trigger action
dateFrom : '=', // date from
dateTo : '=', // date to
required : '<', // true or false
onChange : '<', // on change action
mode : '@' // the date mode (day|month|year)
}
});
angular.module('bhima.components')
.component('bhDateInterval', {
templateUrl : '/partials/templates/bhDateInterval.tmpl.html',
controller : bhDateInterval,
bindings : {
validationTrigger : '<', // validation trigger action
dateFrom : '=', // date from
dateTo : '=', // date to
required : '<', // true or false
onChange : '<', // on change action
mode : '@' // the date mode (day|month|year)
}
});

// dependencies injection
bhDateInterval.$inject = ['DateService'];
bhDateInterval.$inject = ['DateService', 'moment'];

// controller definition
function bhDateInterval(Dates) {
/* global moment */
function bhDateInterval(Dates, moment) {
var vm = this;

vm.options = [
Expand All @@ -48,7 +48,7 @@ function bhDateInterval(Dates) {
// start up the modal
startup();

function search (selection) {
function search(selection) {
vm.selected = selection.translateKey;
selection.fn();
}
Expand All @@ -74,11 +74,12 @@ function bhDateInterval(Dates) {
}

function clear() {
vm.dateFrom = null;
vm.dateTo = null;
delete vm.dateFrom;
delete vm.dateTo;
}

function startup() {

// set today as default date plage value
if (!vm.dateFrom && !vm.dateTo) {
search(vm.options[0]);
Expand All @@ -96,7 +97,5 @@ function bhDateInterval(Dates) {
if (vm.mode === 'clean') {
clear();
}

}

}
11 changes: 11 additions & 0 deletions client/src/js/services/JournalConfig.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ function JournalConfigService(Modal) {
var service = this;

service.openColumnConfigModal = openColumnConfigModal;
service.openSearchModal = openSearchModal;

function openColumnConfigModal(Columns) {
return Modal.open({
Expand All @@ -20,6 +21,16 @@ function JournalConfigService(Modal) {
});
}

function openSearchModal(options) {
return Modal.open({
templateUrl: 'partials/journal/modals/search.modal.html',
controller: 'JournalSearchModalController as ModalCtrl',
resolve : {
options : function () { return options; }
}
}).result;
}

return service;
}

44 changes: 44 additions & 0 deletions client/src/js/services/JournalService.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,49 @@ JournalService.$inject = ['PrototypeApiService'];
*/
function JournalService(Api) {
var service = new Api('/journal/');

service.formatFilterParameters = formatFilterParameters;

/**
* This function prepares the filters for the journal for display to the
* client via the bhFiltersApplied directive.
* @todo - this might be better in it's own service
*/
function formatFilterParameters(params) {
var columns = [
{ field: 'debit', displayName: 'FORM.LABELS.DEBIT' },
{ field: 'credit', displayName: 'FORM.LABELS.CREDIT' },
{ field: 'credit_equiv', displayName: 'FORM.LABELS.CREDIT' },
{ field: 'debit_equiv', displayName: 'FORM.LABELS.DEBIT' },
{ field: 'trans_id', displayName: 'FORM.LABELS.TRANS_ID' },
{ field: 'reference', displayName: 'FORM.LABELS.REFERENCE' },
{ field: 'user_id', displayName: 'FORM.LABELS.USER' },
{ field: 'account_id', displayName: 'FORM.LABELS.ACCOUNT' },
{ field: 'description', displayName: 'FORM.LABELS.DESCRIPTION', truncate: 8 },
{ field: 'dateFrom', displayName: 'FORM.LABELS.DATE', comparitor: '>', ngFilter:'date' },
{ field: 'dateTo', displayName: 'FORM.LABELS.DATE', comparitor: '<', ngFilter:'date' },
];

// returns columns from filters
return columns.filter(function (column) {
var value = params[column.field];

if (angular.isDefined(value)) {

// this is to temporarily reduce the size of the description field
// @TODO - find a better way of doing this
if (column.truncate) {
column.value = String(value).substring(0, column.truncate) + '... ';
} else {
column.value = value;
}

return true;
} else {
return false;
}
});
}

return service;
}
2 changes: 1 addition & 1 deletion client/src/js/services/PatientService.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function PatientService($http, util, Session, $uibModal, Documents, Visits) {
}

/**
* This method accepts infromation recorded by a controllers form, formats it
* This method accepts information recorded by a controllers form, formats it
* for submission and forwards it to the server /patients/create API. It can
* be used for creating new patient records in the database.
*
Expand Down
21 changes: 20 additions & 1 deletion client/src/partials/journal/journal.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@
</div>
</div>

<!-- @todo remove hardcoded styles -->
<div class="flex-util" style="min-height : 35px; padding-top : 7px; max-height: initial" ng-if="JournalCtrl.filtersFmt.length > 0">
<bh-filters-applied
style="max-width:90%"
filters="JournalCtrl.filtersFmt"
on-remove-filter="JournalCtrl.onRemoveFilter(filter)">
</bh-filters-applied>

<a
href
ng-click="JournalCtrl.clearFilters()"
class="text-danger"
data-method="clear">
<i class="fa fa-ban text-danger"></i>
{{ "FORM.INFO.CLEAR_FILTERS" | translate }}
</a>
</div>

<div class="flex-content">
<div class="container-fluid">

Expand All @@ -47,6 +65,7 @@
<div
id="journal-grid"
class="grid-full-height"
ng-style="JournalCtrl.filterBarHeight"
ui-grid="JournalCtrl.gridOptions"
ui-grid-auto-resize
ui-grid-resize-columns
Expand All @@ -61,7 +80,7 @@
empty-state="JournalCtrl.gridOptions.data.length === 0"
error-state="JournalCtrl.hasError"
>
</bh-grid-loading-indicator>
</bh-grid-loading-indicator>

</div>
</div>
Expand Down
107 changes: 80 additions & 27 deletions client/src/partials/journal/journal.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ JournalController.$inject = [
'JournalService', 'GridSortingService', 'GridGroupingService',
'GridFilteringService', 'GridColumnService', 'JournalConfigService',
'SessionService', 'NotifyService', 'TransactionService', 'GridEditorService',
'bhConstants', '$state', 'uiGridConstants', 'ModalService', 'LanguageService'
'bhConstants', '$state', 'uiGridConstants', 'ModalService', 'LanguageService',
'AppCache'
];

/**
Expand All @@ -30,7 +31,7 @@ JournalController.$inject = [
*
* @module bhima/controllers/JournalController
*/
function JournalController(Journal, Sorting, Grouping, Filtering, Columns, Config, Session, Notify, Transactions, Editors, bhConstants, $state, uiGridConstants, Modal, Languages) {
function JournalController(Journal, Sorting, Grouping, Filtering, Columns, Config, Session, Notify, Transactions, Editors, bhConstants, $state, uiGridConstants, Modal, Languages, AppCache) {
var vm = this;

/** @constants */
Expand All @@ -44,6 +45,9 @@ function JournalController(Journal, Sorting, Grouping, Filtering, Columns, Confi
/** @const the cache alias for this controller */
var cacheKey = 'Journal';

// filter cache
var cache = AppCache(cacheKey + '-filters');

vm.enterprise = Session.enterprise;

// gridOptions is bound to the UI Grid and used to configure many of the
Expand All @@ -64,26 +68,17 @@ function JournalController(Journal, Sorting, Grouping, Filtering, Columns, Confi
transactions = new Transactions(vm.gridOptions);
editors = new Editors(vm.gridOptions);

//attaching the filtering object to the view
// attaching the filtering object to the view
vm.filtering = filtering;

//attaching the grouping object to the view
// attaching the grouping object to the view
vm.grouping = grouping;

//Attaching the transaction to the view
// Attaching the transaction to the view
vm.transactions = transactions;

vm.loading = true;
Journal.read()
.then(function (records) {
vm.gridOptions.data = records;
})
.catch(function (error) {
vm.hasError = true;
Notify.handleError(error);
})
.finally(toggleLoadingIndicator);

vm.onRemoveFilter = onRemoveFilter;
vm.clearFilters = clearFilters;

/**
* @function toggleLoadingIndicator
Expand Down Expand Up @@ -169,10 +164,10 @@ function JournalController(Journal, Sorting, Grouping, Filtering, Columns, Confi

//This function opens a modal, to let the user posting transaction to the general ledger
vm.openTrialBalanceModal = function openTrialBalanceModal () {
$state.go('trialBalanceMain', {records : vm.grouping.getSelectedGroups()}, {reload : false});
$state.go('trialBalanceMain', { records : vm.grouping.getSelectedGroups() });
};

// display the journal printalble report of selected transactions
// display the journal printable report of selected transactions
vm.openJournalReport = function openJournalReport() {
var uuids = vm.grouping.getSelectedGroups().map(function (trans) {
return trans.uuid;
Expand All @@ -183,16 +178,74 @@ function JournalController(Journal, Sorting, Grouping, Filtering, Columns, Confi
Modal.openReports({ url: url, params: params });
};

// search modal
function errorHandler(error) {
vm.hasError = true;
Notify.handleError(error);
}

// loads data for the journal
function load(options) {
vm.loading = true;
vm.hasError = false;

Journal.read(null, options)
.then(function (records) {
vm.gridOptions.data = records;

// try to unfold groups
try { grouping.unfoldAllGroups(); } catch (e) {}
})
.catch(errorHandler)
.finally(toggleLoadingIndicator);
}

// open search modal
vm.openSearchModal = function openSearchModal() {
Modal.openDateInterval()
.then(function (dateInterval) {
return Journal.read(null, dateInterval);
})
.then(function (list) {
vm.gridOptions.data = list;
})
.catch(Notify.errorHandler);
var parameters = angular.copy(vm.filters);
Config.openSearchModal(parameters)
.then(function (options) {

// if the options are not returned or have not changed, do not refresh
// the data source
if (angular.equals(options, vm.filters)) { return; }

// bind filters to the view and format appropriate
cacheFilters(options);

// turn loading on
toggleLoadingIndicator();

return load(options);
});
};

// save the parameters to use later. Formats the parameters in filtersFmt for the filter toolbar.
function cacheFilters(filters) {
vm.filters = cache.filters = filters;
vm.filtersFmt = Journal.formatFilterParameters(filters);
vm.filterBarHeight = (vm.filtersFmt.length > 0) ?
{ 'height' : 'calc(100vh - 102px)' } : {};
}

// remove a filter with from the filter object, save the filters and reload
function onRemoveFilter(key) {
delete vm.filters[key];
cacheFilters(vm.filters);
load(vm.filters);
}

// clears the filters by forcing a cache of an empty array
function clearFilters() {
cacheFilters({});
load({});
}

// runs on startup
function startup() {
vm.filters = cache.filters;
vm.filtersFmt = Journal.formatFilterParameters(cache.filters || {});
load(vm.filters);
}

startup();
}
Loading

0 comments on commit 3372432

Please sign in to comment.