Skip to content

Commit

Permalink
fix(vouchers): implement value precision checking
Browse files Browse the repository at this point in the history
This commit implements a precision check as part of the input checks in
the complex voucher grid.  The grid will now block if someone enters an
amount that cannot be stored properly in the database.

It also hides the column drop down menus on the ui-grid.

Closes #885.
  • Loading branch information
Jonathan Niles authored and sfount committed Nov 18, 2016
1 parent cabc1fa commit ae1fa6f
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 40 deletions.
1 change: 1 addition & 0 deletions client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,7 @@
"ERROR_ACCOUNT" : "Selected accounts are invalid",
"ERROR_AMOUNT" : "Amount's values are invalid",
"ERROR_TOTALS" : "The totals are not balanced",
"ERROR_PRECISION" : "The amounts entered cannot have more than 4 decimal places in precision",
"INVALID_VALUES" : "There are invalid values detected",
"PATIENT_INVOICE" : "Patient Invoice",
"REFERENCE" : "Reference",
Expand Down
5 changes: 3 additions & 2 deletions client/src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,13 +348,14 @@ function constantConfig() {
GRID_HEIGHT: 200
},
settings: {
CONTACT_EMAIL : 'developers@imaworldhealth.org'
CONTACT_EMAIL : 'developers@imaworldhealth.org',
},
dates : {
minDOB : new Date('1900-01-01'),
},
lengths : {
maxTextLength : 1000
maxTextLength : 1000,
minDecimalValue: 0.0001
},
grid : {
ROW_HIGHLIGHT_FLAG : '_highlight',
Expand Down
97 changes: 59 additions & 38 deletions client/src/partials/vouchers/complex.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ angular.module('bhima.controllers')
ComplexJournalVoucherController.$inject = [
'VoucherService', '$translate', 'AccountService',
'CurrencyService', 'SessionService', 'FindEntityService',
'FindReferenceService', 'NotifyService', 'CashboxService', 'ReceiptModal'
'FindReferenceService', 'NotifyService', 'CashboxService', 'ReceiptModal',
'bhConstants'
];

/**
Expand All @@ -18,8 +19,9 @@ ComplexJournalVoucherController.$inject = [
* @todo - Implement caching mechanism for incomplete forms (via AppCache)
* @todo - Implement Patient Invoices data and Cash Payment data for modal
* @todo - Implement a mean to categorise transactions for cashflow reports
* @todo/@fixme - this error notification system needs serious refactor.
*/
function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currencies, Session, FindEntity, FindReference, Notify, Cashbox, Receipts) {
function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currencies, Session, FindEntity, FindReference, Notify, Cashbox, Receipts, bhConstants) {
var vm = this;

// bread crumb paths
Expand All @@ -28,6 +30,10 @@ function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currenc
current: true
}];


var MIN_DECIMAL_VALUE= bhConstants.lengths.minDecimalValue;
var MIN_PRECISION_VALUE = getDecimalPrecision(MIN_DECIMAL_VALUE);

// global variables
vm.gridOptions = {};
vm.gridApi = {};
Expand Down Expand Up @@ -172,9 +178,14 @@ function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currenc
var allValidAccount = vm.rows.every(function (row) {

// must have an account defined
var validAccount = (row.account && row.account.id);
return row.account && row.account.id;
});

return validAccount;
var allValidPrecision = vm.rows.every(function (row) {
return (
getDecimalPrecision(row.debit) <= MIN_PRECISION_VALUE &&
getDecimalPrecision(row.credit) <= MIN_PRECISION_VALUE
);
});

// validate that the transaction balances
Expand All @@ -190,7 +201,8 @@ function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currenc
return {
validAmount : allValidAmount,
validAccount : allValidAccount,
validTotals : validTotals
validTotals : validTotals,
validPrecision : allValidPrecision
};
}

Expand All @@ -199,37 +211,59 @@ function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currenc
vm.posted = false;
vm.financialTransaction = false;
vm.rowsInput = validRowsInput();
vm.validInput = vm.rowsInput.validAmount && vm.rowsInput.validAccount && vm.rowsInput.validTotals && vm.rows.length > 1 ? true : false;

vm.validInput = (
vm.rowsInput.validAmount && vm.rowsInput.validAccount &&
vm.rowsInput.validPrecision && vm.rowsInput.validTotals &&
vm.rows.length > 1
);

vm.notifyMessage =
!vm.rowsInput.validAmount ? { icon : 'glyphicon glyphicon-alert', label : 'VOUCHERS.COMPLEX.ERROR_AMOUNT' } :
!vm.rowsInput.validAccount ? { icon : 'glyphicon glyphicon-alert', label : 'VOUCHERS.COMPLEX.ERROR_ACCOUNT' } :
!vm.rowsInput.validTotals ? { icon : 'glyphicon glyphicon-alert', label : 'VOUCHERS.COMPLEX.ERROR_TOTALS' } :
!vm.rowsInput.validPrecision ? { icon : 'glyphicon glyphicon-alert', label : 'VOUCHERS.COMPLEX.ERROR_PRECISION' } :
vm.rowsInput.validTotals && vm.validInput ? { icon : 'glyphicon glyphicon-ok-sign', label : 'VOUCHERS.COMPLEX.VALID_TOTALS' } :
{ icon : '', label : '' };

summation();
}

// get the length of the portion after the decimal point.
function getDecimalPrecision(value) {
var valueString = String(value);
var decimalPart = valueString.split('.')[1] || [];
return decimalPart.length;
}

/** checking validity of a row */
function checkRowValidity(index) {
if (angular.isUndefined(index)) { return; }

var row = vm.rows[index];

/** validity of the amount */
var validAmount =
((row.debit > 0 && !row.credit) || (!row.debit && row.credit > 0)) &&
var hasValidAmount =
((!row.credit && row.debit >= MIN_DECIMAL_VALUE) || (!row.debit && row.credit >= MIN_DECIMAL_VALUE)) &&
(angular.isDefined(row.debit) && angular.isDefined(row.credit));

/** must have an account defined */
var validAccount = (row.account && row.account.id) ? true : false;
var hasValidAccount = (row.account && row.account.id);

// the amounts recorded in each line should have the correct number of
// digits after the decimal point
var hasValidPrecision = (
getDecimalPrecision(row.debit) <= MIN_PRECISION_VALUE &&
getDecimalPrecision(row.credit) <= MIN_PRECISION_VALUE
);

/** validity of the row */
vm.rows[index].isValid = validAccount && validAmount;
vm.rows[index].hasAccount = validAccount;
row.isValid = hasValidAccount && hasValidAmount && hasValidPrecision;
row.hasAccount = hasValidAccount;

/**
* refresh the ui to the real state
* This function does a lot of process but it usefull for informing the user
* This function does a lot of process but it useful for informing the user
* it notify about :
* -- the validity of all amount given
* -- the validity of totals (balanced or not)
Expand All @@ -242,7 +276,6 @@ function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currenc
* Check financial account
*/
isFinancial();

}

/** summation */
Expand Down Expand Up @@ -365,6 +398,7 @@ function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currenc

// grid default options
vm.gridOptions.appScopeProvider = vm;
vm.gridOptions.enableColumnMenus = false;
vm.gridOptions.columnDefs =
[
{ field : 'isValid', displayName : '...',
Expand All @@ -373,42 +407,30 @@ function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currenc
enableFiltering: false,
enableColumnMenu: false,
width: 40
},

{ field : 'account', displayName : 'FORM.LABELS.ACCOUNT',
}, {
field : 'account', displayName : 'FORM.LABELS.ACCOUNT',
headerCellFilter: 'translate',
cellTemplate: 'partials/vouchers/templates/account.grid.tmpl.html'
},

{ field : 'debit', displayName : 'FORM.LABELS.DEBIT',
}, {
field : 'debit', displayName : 'FORM.LABELS.DEBIT',
headerCellFilter: 'translate',
cellTemplate: 'partials/vouchers/templates/debit.grid.tmpl.html'
},

{ field : 'credit', displayName : 'FORM.LABELS.CREDIT',
}, {
field : 'credit', displayName : 'FORM.LABELS.CREDIT',
headerCellFilter: 'translate',
cellTemplate: 'partials/vouchers/templates/credit.grid.tmpl.html'
},

{ field : 'entity', displayName : 'FORM.LABELS.DEBTOR_CREDITOR',
}, {
field : 'entity', displayName : 'FORM.LABELS.DEBTOR_CREDITOR',
headerCellFilter: 'translate',
cellTemplate: 'partials/vouchers/templates/entity.grid.tmpl.html',
enableFiltering: false,
enableColumnMenu: false
},

{ field : 'reference', displayName : 'FORM.LABELS.REFERENCE',
}, {
field : 'reference', displayName : 'FORM.LABELS.REFERENCE',
headerCellFilter: 'translate',
cellTemplate: 'partials/vouchers/templates/reference.grid.tmpl.html',
enableFiltering: false,
enableColumnMenu: false
},

{ field : 'action', displayName : '...',
}, {
field : 'action', displayName : '...',
width: 25,
cellTemplate: 'partials/vouchers/templates/remove.grid.tmpl.html',
enableFiltering: false,
enableColumnMenu: false
}
];

Expand All @@ -434,7 +456,6 @@ function ComplexJournalVoucherController(Vouchers, $translate, Accounts, Currenc
refreshState();

var voucherItems = handleVoucherItems();

var voucher = handleVoucher();

if (voucherItems.length > 0) {
Expand Down

0 comments on commit ae1fa6f

Please sign in to comment.