Skip to content

Commit

Permalink
feat(VoucherForm): Service-powered voucher grid
Browse files Browse the repository at this point in the history
This commit implements modular validation for the complex journal
vouchers page by creating a VoucherFormService and a
VoucherFormItemService similar to the PatientInvoice services.  These
services are charged with validating the VoucherForm and items,
respectively.

The following errors are caught by the item-level validation:
 1. Negative Values
 2. Values of too small precision
 3. Missing account_id

The following errors are caught by the form-level validation:
 1. Imbalanced totals
 2. Missing (and required) transaction type:
 3. Missing metadata

These allows fine-grained control over the error states of the module.
Errors for particular lines are displayed in the status column next to
the row on a mouse hover.

There are still some TODOs:
 1. The import tool hasn't been updated to work with the vouchers
 properly.
 2. The create() method now needs to clean up all unused properties
 before submitting to the server.
 3. Error messages should be centralized and translated for better error
 handling.
  • Loading branch information
Jonathan Niles committed Dec 22, 2016
1 parent 7c7720a commit 00cae5b
Show file tree
Hide file tree
Showing 20 changed files with 683 additions and 509 deletions.
5 changes: 5 additions & 0 deletions client/src/css/structure.css
Original file line number Diff line number Diff line change
Expand Up @@ -842,3 +842,8 @@ growl-notification.fading.ng-leave.ng-leave-active {
.ui-select-bootstrap > .ui-select-choices.ui-grid-ui-select {
width: initial;
}

/* fix weird whitespace at the top of grids. */
.ui-grid-canvas {
padding-top : 0;
}
1 change: 1 addition & 0 deletions client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,7 @@
"TRANSACTION" : "Transaction",
"TRANSFER_ACCOUNT" : "Transfer Account",
"TRANSFER_TYPE" : "Transfer Type",
"TRANSACTION_TYPE" : "Transaction Type",
"TYPE" : "Type",
"UNCONFIGURED" : "Unconfigured",
"UNDEFINED" : "Undefined",
Expand Down
2 changes: 0 additions & 2 deletions client/src/js/services/PatientInvoiceForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ PatientInvoiceFormService.$inject = [
* with the Patient PatientInvoiceForm module. You must specify a cacheKey to enable the
* class to be instantiated correctly.
*
* @todo (required) discuss if all subsidies/billings services are all
* percentages. If so, the logic can be simplified.
* @todo (required) only the maximum of the bill should be subsidised
* @todo (required) billing services and subsidies should be ignored for
* specific debtors.
Expand Down
3 changes: 1 addition & 2 deletions client/src/js/services/VoucherService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ angular.module('bhima.services')
.service('VoucherService', VoucherService);

VoucherService.$inject = [
'PrototypeApiService', '$http', 'util',
'TransactionTypeStoreService'
'PrototypeApiService', '$http', 'util', 'TransactionTypeStoreService'
];

/**
Expand Down
19 changes: 18 additions & 1 deletion client/src/js/services/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,24 @@ function UtilService(moment) {
// if we haven't seen the value yet, add it to the array (otherwise, ignore it).
if (uniq.indexOf(value) === -1) { uniq.push(value); }
return uniq;

}, []); // initialize with an empty array
};


/**
* @function xor
*
* @description
* Returns the logical XOR of two booleans.
*
* @param {Boolean} a - a boolean value to XOR with b
* @param {Boolean} b - a boolean value to XOR with a
*
* @returns {Boolean} - the result
*/
service.xor = function xor(a, b) {
/*jshint -W018 */
return !a !== !b;
/*jshint +W018 */
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<span ng-show="row.entity._initialised" class="fa fa-times-circle" uib-tooltip="{{ row.entity._message | translate }}" tooltip-placement="right" tooltip-append-to-body="true"></span>
</div>

<!-- success state: valid and initialised -->
<div ng-show="row.entity._valid" class="bg-success ui-grid-cell-contents">
<!-- success state: valid and initialised -->
<span class="text-success fa fa-check-circle"></span>
</div>
38 changes: 16 additions & 22 deletions client/src/partials/reports/modals/cashflow.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,21 @@ function CashflowConfigController($state, ModalInstance, Cashbox, Notify, Langua
vm.cancel = ModalInstance.dismiss;
vm.report = report;

vm.$loading = false;

Cashbox.read(null, { detailed: 1, is_auxiliary: 0})
.then(function (list) {
list.forEach(function (cashbox) {
cashbox.hrlabel = cashbox.label + ' ' + cashbox.symbol;
});
vm.cashboxes = list;
})
.catch(Notify.handleError);

function generate() {
var url = 'reports/finance/cashflow';
if (!vm.cashbox || !vm.dateFrom || !vm.label || !vm.dateTo) { return ; }
Cashbox.read(null, { detailed: 1 })
.then(function (cashboxes) {

cashboxes.forEach(function (cashbox) {
cashbox.hrlabel = cashbox.label + ' ' + cashbox.symbol;
});

vm.cashboxes = cashboxes;
})
.catch(Notify.handleError);

vm.$loading = true;
function generate(form) {
if (form.$invalid) { return; }

var url = 'reports/finance/cashflow';

var options = {
account_id: vm.cashbox.account_id,
Expand All @@ -45,16 +44,11 @@ function CashflowConfigController($state, ModalInstance, Cashbox, Notify, Langua
weekly : vm.weekly
};

SavedReports.requestPDF(url, report, options)
return SavedReports.requestPDF(url, report, options)
.then(function (result) {
ModalInstance.dismiss();
$state.reload();
})
.catch(function (error) {
throw error;
})
.finally(function () {
vm.$loading = false;
});
.catch(Notify.handleError);
}
}
108 changes: 54 additions & 54 deletions client/src/partials/reports/modals/cashflow.modal.html
Original file line number Diff line number Diff line change
@@ -1,71 +1,71 @@
<div class="modal-header">
<ol class="headercrumb">
<li class="static">{{ ReportConfigCtrl.report.title_key | translate }}</li>
<li class="title">{{ "FORM.LABELS.CREATE" | translate }}</li>
<li class="title">
{{ ReportConfigCtrl.report.title_key | translate }}
<span class="badge badge-success text-uppercase">{{ "FORM.LABELS.CREATE" | translate }}</span>
</li>
</ol>
</div>

<form name="ConfigForm" bh-submit="ReportConfigCtrl.generate()" bh-form-defaults novalidate>
<div class="modal-body">
<div class="form-group"
ng-class="{ 'has-error' : ConfigForm.$submitted && ConfigForm.label.$invalid }">
<label class="control-label">{{ "FORM.LABELS.LABEL" | translate }}</label>
<input class="form-control" id="label" name="label" ng-model="ReportConfigCtrl.label" autocomplete="off" required />
<form name="ConfigForm" bh-submit="ReportConfigCtrl.generate(ConfigForm)" bh-form-defaults novalidate>
<div class="modal-body">

<div class="help-block" ng-messages="ConfigForm.label.$error" ng-show="ConfigForm.$submitted">
<div ng-messages-include="partials/templates/messages.tmpl.html"></div>
</div>
<div class="form-group"
ng-class="{ 'has-error' : ConfigForm.$submitted && ConfigForm.label.$invalid }">
<label class="control-label">{{ "FORM.LABELS.LABEL" | translate }}</label>
<input class="form-control" id="label" name="label" ng-model="ReportConfigCtrl.label" autocomplete="off" required />

</div>
<!-- Date interval -->
<bh-date-interval
date-from="ReportConfigCtrl.dateFrom"
date-to="ReportConfigCtrl.dateTo">
</bh-date-interval>
<div class="help-block" ng-messages="ConfigForm.label.$error" ng-show="ConfigForm.$submitted">
<div ng-messages-include="partials/templates/messages.tmpl.html"></div>
</div>
</div>

<!-- by week -->
<div class="form-group">
<label class="text-action">
<input type="checkbox" name="weekly" ng-model="ReportConfigCtrl.weekly" ng-true-value="1" ng-false-value="0">
{{ 'FORM.LABELS.WEEKLY_REPORT' | translate }}
</label>
</div>
<!-- date interval -->
<bh-date-interval
date-from="ReportConfigCtrl.dateFrom"
date-to="ReportConfigCtrl.dateTo"
required="true">
</bh-date-interval>

<!-- by week -->
<div class="checkbox">
<label>
<input type="checkbox" name="weekly" ng-model="ReportConfigCtrl.weekly" ng-true-value="1" ng-false-value="0">
{{ 'FORM.LABELS.WEEKLY_REPORT' | translate }}
</label>
</div>

<!-- cashbox selection -->
<div class="form-group"
ng-class="{ 'has-error' : ConfigForm.$submitted && ConfigForm.cashbox.$invalid }">
<label class="control-label">
<span class="fa fa-briefcase"></span> {{ 'FORM.SELECT.CASHBOX' | translate }}
</label>
<select
class="form-control"
name="cashbox"
ng-model="ReportConfigCtrl.cashbox"
ng-options="cash as cash.hrlabel for cash in ReportConfigCtrl.cashboxes"
required>
<option value="" disabled>-- {{ 'FORM.SELECT.CASHBOX' | translate }} --</option>
</select>
<div class="help-block" ng-messages="ConfigForm.cashbox.$error" ng-show="ConfigForm.$submitted">
<div ng-messages-include="partials/templates/messages.tmpl.html"></div>
<!-- cashbox selection -->
<div class="form-group"
ng-class="{ 'has-error' : ConfigForm.$submitted && ConfigForm.cashbox.$invalid }">
<label class="control-label">
<span class="fa fa-briefcase"></span> {{ 'FORM.SELECT.CASHBOX' | translate }}
</label>
<select
class="form-control"
name="cashbox"
ng-model="ReportConfigCtrl.cashbox"
ng-options="cash as cash.hrlabel for cash in ReportConfigCtrl.cashboxes track by cash.id"
required>
<option value="" disabled>{{ 'FORM.SELECT.CASHBOX' | translate }}</option>
</select>
<div class="help-block" ng-messages="ConfigForm.cashbox.$error" ng-show="ConfigForm.$submitted">
<div ng-messages-include="partials/templates/messages.tmpl.html"></div>
</div>
</div>
</div>
</div>

<div class="modal-footer">
<button
type="button"
class="btn btn-default"
ng-click="ReportConfigCtrl.cancel()"
data-method="cancel">
{{ "FORM.BUTTONS.CANCEL" | translate }}
</button>
<div class="modal-footer">
<button
type="button"
class="btn btn-default"
ng-click="ReportConfigCtrl.cancel()"
data-method="cancel">
{{ "FORM.BUTTONS.CANCEL" | translate }}
</button>

<!-- <div data-submit-area> -->
<bh-loading-button loading-state="ReportConfigCtrl.$loading"
ng-disabled="ConfigForm.$invalid">
<bh-loading-button loading-state="ConfigForm.$loading">
{{ "FORM.BUTTONS.GENERATE" | translate }}
</bh-loading-button>
<!-- </div> -->
</div>
</div>
</form>
Loading

0 comments on commit 00cae5b

Please sign in to comment.