Skip to content

Commit

Permalink
feat(voucher): Patient Invoice Debt Transfer (#186)
Browse files Browse the repository at this point in the history
* feat(voucher): Patient Invoice Debt Transfer

This commit implements a payment of a patient's debts via the Simple
Voucher module.  The debts are written off towards an account - in the
present use case, against an account belonging to a debtor group.

* fix(vouchers): implement clear() on form

This commit cleans up the form simple voucher form's worst hacks and
replaces them with _slightly_ cleaner code.  It also implements a
clear() button.

Closes #191.

* fix(currency): improve translation/rendering

This commit improves the bhCurrencyInput's translation and rendering by
making sure the currency is always rendered in uppercase and using the
more efficient translate directive instead of the translate filter.

* refactor(vouchers): SimpleVoucher use VoucherForm

This commit refactors the Simple Voucher module to use the VoucherForm.
The VoucherForm brings with it numerous advantages:
 1. Better error detection and handling
 2. Built-in caching.
 3. Cleaner separation of view/model

This makes the SimpleVoucher module much easier to extend and operate
in the future.

* fix(vouchers): fix e2e test

This commit fixes a case-sensitive issue in the simple vouchers form.

* fix(voucher): fix general bugs

This commit fixes the simple vouchers to have more maintainable code,
closes some tags, and makes sure that the appropriate values are
triggered when the scan barcode modal closes.
  • Loading branch information
jniles authored and sfount committed Jan 25, 2017
1 parent 02d3705 commit 7d08f3c
Show file tree
Hide file tree
Showing 16 changed files with 452 additions and 269 deletions.
26 changes: 17 additions & 9 deletions client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@
"TOO_MANY_TRYS" : "Forgot your username or password? Please ask the system administrator to reset it.",
"UNAUTHENTICATED" : "You have been logged off. Please sign back in to resume using the application."
},
"BARCODE" : {
"SCAN" : "Scan Invoice Barcode",
"AWAITING_INPUT" : "Waiting for Input",
"AWAITING_HTTP" : "Barcode Read! Looking up Invoice...",
"READ_SUCCESS" : "Found Invoice",
"READ_ERROR" : "Unreadable Barcode! Please enter the barcode manually."
},
"BALANCE" : {
"ACCOUNT_CHOICE" : "Accounts choice",
"ALL_ACCOUNT" : "All accounts",
Expand Down Expand Up @@ -126,13 +133,6 @@
"NO_CASHBOX_SELECTED" : "You have no selected a cashbox. Please select a cashbox to continue.",
"SELECTED" : "Selected cash"
},
"BARCODE" : {
"SCAN" : "Scan Invoice Barcode",
"AWAITING_INPUT" : "Waiting for Input",
"AWAITING_HTTP" : "Barcode Read! Looking up Invoice...",
"READ_SUCCESS" : "Found Invoice",
"READ_ERROR" : "Unreadable Barcode! Please enter the barcode manually."
},
"SCAN_BARCODE" : "Scan Invoice Barcode",
"OPEN_BARCODE_MODAL" : "Scan another barcode on successful payment.",
"DEBTOR_INVOICES" : "Debtor Invoices",
Expand Down Expand Up @@ -669,6 +669,7 @@
"TITLE" : "Title",
"TITLE_ACCOUNT" : "Title Account",
"TODAY" : "Today",
"TOOLS" : "Tools",
"TOTAL" : "Total",
"TOTAL_BILLED" : "Total Billed",
"TOTAL_DEBT" : "Total of Debts",
Expand Down Expand Up @@ -964,7 +965,7 @@
"REPORT_TITLE" : "Patient Checkin Report",
"SUCCESS" : "Patient has been checked in"
},
"VISITS" : {
"VISITS" : {
"TITLE" : "Patient Visits",
"ADMIT" : "Admit Patient",
"ADMISSION_DIAGNOSIS" : "Admission Diagnosis",
Expand Down Expand Up @@ -1547,10 +1548,17 @@
"PURCHASES" : "Purchases",
"SUPPORT_INCOME" : "Support Incomes",
"TITLE" : "Simple Voucher",
"TRANSFER" : "Money Transfer"
"TRANSFER" : "Money Transfer",
"TRANSFER_PATIENT_INVOICE_AMOUNT" : "Patient Invoice Debt Transfer"
},
"REPORT": "Report"
},
"TRANSACTIONS" : {
"SINGLE_ACCOUNT_TRANSACTION" : "This transaction only concerns a single account. Please select multiple accounts to complete the transaction.",
"MISSING_TRANSACTION_TYPE" : "Transactions involving cash accounts (banks or cashboxes) must have a transaction type specified to differentiate them in the cashflow report. Please select a transaction type.",
"IMBALANCED_TRANSACTION" : "This transaction's debits and credits must balance. Please correct the imbalances before submitting.",
"SINGLE_ROW_TRANSACTION" : "This transaction only has a single row, but two rows are required. Please add one or more lines."
},
"GRAPHS" : {
"PATIENT_REGISTRATIONS" : "Patient Registrations",
"PATIENT_INVOICES" : "Patient Invoices",
Expand Down
34 changes: 21 additions & 13 deletions client/src/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@
"TOO_MANY_TRYS" : "Avez-vous oublié votre login ou votre mot de passe? S'il vous plaît demander à l'administrateur du système pour le réinitialiser.",
"UNAUTHENTICATED" : "Vous avez été déconnecté. Veuillez vous reconnecté avec d'utiliser l'application."
},
"BARCODE" : {
"SCAN" : "Scanner le Code à Barres",
"AWAITING_INPUT" : "En Attente d'Entrée",
"AWAITING_HTTP" : "Code à barres lu! Recherche de factures ....",
"READ_SUCCESS" : "Facture Trouvée",
"READ_ERROR" : "Code à barres illisible! Entrez le code à barres manuellement."
},
"BALANCE" : {
"ACCOUNT_CHOICE" : "Choix des comptes",
"ALL_ACCOUNT" : "Tous les comptes",
Expand Down Expand Up @@ -130,13 +137,6 @@
"SELECTED" : "Selectionnée",
"TRANSFER" : "Transférer Fonds"
},
"BARCODE" : {
"SCAN" : "Scanner le Code à Barres",
"AWAITING_INPUT" : "En Attente d'Entrée",
"AWAITING_HTTP" : "Code à barres lu! Recherche de factures ....",
"READ_SUCCESS" : "Facture Trouvée",
"READ_ERROR" : "Code à barres illisible! Entrez le code à barres manuellement."
},
"OPEN_BARCODE_MODAL" : "Numériser un autre code à barres lors d'un paiement réussi.",
"DEBTOR_INVOICES" : "Factures des débiteurs",
"REPORT" : "Rapport",
Expand Down Expand Up @@ -200,7 +200,7 @@
"INFO" : "Avec cette configuration, aucun membre de ce groupe débiteur ne sera subventionné",
"LABEL" : "Ce groupe est qualifié pour les subventions",
"UPDATE" : "Mettre à jour les subventions",
"EMPTY" : "Ce groupe de débiteurs n'est pas souscrit à des subventions"
"EMPTY" : "Ce groupe de débiteurs n'est pas souscrit à des subventions"
},
"DISCOUNTS" : {
"TITLE" : "Réductions",
Expand All @@ -220,7 +220,7 @@
"SUBSCRIPTIONS" : "Abonnements",
"UPDATED" : "Enregistrement du groupe débiteur mis à jour"
},
"DOWNLOADS" : {
"DOWNLOADS" : {
"JSON" : "Télécharger en JSON",
"PDF" : "Télécharger en PDF",
"CSV" : "Télécharger en CSV"
Expand Down Expand Up @@ -521,7 +521,7 @@
"END" : "Fin",
"END_DATE" : "Date de fin",
"ENTER_BIRTH_YEAR" : "Entrez seulement l'année de naissance",
"ENTER_BIRTH_DAY" : "Entrez la date de naissance complète",
"ENTER_BIRTH_DAY" : "Entrez la date de naissance complète",
"ENTERPRISE" : "Entreprise",
"ENTERPRISE_ID" : "ID entreprise",
"ENTITY" : "Entité",
Expand Down Expand Up @@ -674,6 +674,7 @@
"TITLE" : "Titre",
"TITLE_ACCOUNT" : "Comptes Titre",
"TO" : "à",
"TOOLS" : "Utils",
"TODAY" : "Aujourd'hui",
"TOTAL" : "Total",
"TOTAL_BILLED" : "Total Facture",
Expand Down Expand Up @@ -851,7 +852,7 @@
"SYSTEM" : "Hospital Management System",
"TITLE" : "Bhima",
"TODAY" : "Date du jour",
"DEBTORS" : {
"DEBTORS" : {
"TITLE" : "Debiteurs de l'entreprise",
"BALANCED" : "Toutes les dettes sont reglées",
"CACHED" : "Données à jour"
Expand Down Expand Up @@ -973,7 +974,7 @@
"BY" : "par",
"SUCCESS" : "Confirmation de la visite du patient avec succes"
},
"VISITS" : {
"VISITS" : {
"TITLE" : "Visites des patients",
"ADMIT" : "Admission à l'hôpital",
"ADMISSION_DIAGNOSIS" : "Diagnostic Admission",
Expand Down Expand Up @@ -1571,10 +1572,17 @@
"PURCHASES" : "Achats",
"SUPPORT_INCOME" : "Paiement Prise en charge",
"TITLE" : "Bordereau de transfert",
"TRANSFER" : "Transfert d'argent"
"TRANSFER" : "Transfert d'argent",
"TRANSFER_PATIENT_INVOICE_AMOUNT" : "Prise en Charge de Facture de Patient"
},
"REPORT" : "Rapport"
},
"TRANSACTIONS" : {
"SINGLE_ACCOUNT_TRANSACTION" : "Cette transaction contient un seul compte. Veuillez sélectionner plusieurs comptes pour compléter la transaction.",
"MISSING_TRANSACTION_TYPE" : "Les transactions impliquant des comptes de trésorerie (banques ou caisses) doivent avoir un type de transaction spécifié pour les différencier dans le rapport de trésorerie. Veuillez sélectionner un type de transaction.",
"IMBALANCED_TRANSACTION" : "Les débits et les crédits de cette transaction doivent être équilibrés. Veuillez corriger les déséquilibres avant de les soumettre.",
"SINGLE_ROW_TRANSACTION" : "Cette transaction ne comporte qu'une seule ligne, mais deux lignes sont nécessaires. Veuillez ajouter une ou plusieurs lignes."
},
"GRAPHS" : {
"PATIENT_REGISTRATIONS" : "Enregistrement des patients",
"PATIENT_INVOICES" : "Factation des patients",
Expand Down
15 changes: 0 additions & 15 deletions client/src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,21 +129,6 @@ function bhimaConfig($stateProvider, $urlMatcherFactoryProvider) {
controller : 'CountryController as CountryCtrl',
templateUrl: 'partials/locations/country/country.html'
})
.state('simpleVouchers', {
url : '/vouchers/simple',
controller: 'SimpleJournalVoucherController as SimpleVoucherCtrl',
templateUrl: 'partials/vouchers/simple.html'
})
.state('vouchersComplex', {
url : '/vouchers/complex',
controller: 'ComplexJournalVoucherController as ComplexVoucherCtrl',
templateUrl: 'partials/vouchers/complex.html'
})
.state('vouchers', {
url : '/vouchers',
controller: 'VoucherController as VoucherCtrl',
templateUrl: 'partials/vouchers/index.html'
})

/** General ledger routes**/
.state('generalLedger', {
Expand Down
46 changes: 8 additions & 38 deletions client/src/js/services/VoucherService.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ VoucherService.$inject = [
* @extends PrototypeApiService
*
* @description
* This service manages posting data to the database via the /vouchers/ URL.
* This service manages posting data to the database via the /vouchers/ URL. It also
* includes some utilities that are useful for voucher pages.
*/
function VoucherService(Api, $http, util, TransactionTypeStore) {
var service = new Api('/vouchers/');

// @tdoo - remove this reference to baseUrl
var baseUrl = '/journal/';

service.createSimple = createSimple;
service.create = create;
service.reverse = reverse;
service.transactionType = transactionType;
Expand Down Expand Up @@ -64,43 +64,13 @@ function VoucherService(Api, $http, util, TransactionTypeStore) {
return escapedItem;
});

return Api.create.call(service, { voucher : voucher });
}
// we pick either the debit or the credit side to assign as the total amount
// of the voucher
v.amount = v.items.reduce(function (sum, row) {
return sum + row.debit;
}, 0);

/**
* @method createSimple
*
* @description
* Creates a simple journal voucher, transforming the object into double-entry
* accounting.
*
* @param {object} voucher - the raw journal voucher
* @returns {Promise} - the $http promise object
*/
function createSimple(voucher) {

// create a local working copy for manipulation
var clean = angular.copy(voucher);

// in a simple journal voucher, there are two items
var items = [{
debit : clean.amount,
credit: 0,
account_id : clean.fromAccount.id
}, {
debit : 0,
credit : clean.amount,
account_id : clean.toAccount.id
}];

// clean up the voucher by removing view properties
delete clean.toAccount;
delete clean.fromAccount;

// bind the voucher items
clean.items = items;

return Api.create.call(service, { voucher : clean });
return Api.create.call(service, { voucher : v });
}

/**
Expand Down
3 changes: 2 additions & 1 deletion client/src/partials/cash/cash.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
</li>
<li role="separator" class="divider"></li>
<li role="menuitem">
<!-- TODO(@jniles) remove CashFormInstance -->
<a href ui-sref="cash.scan({ id: CashCtrl.cashbox.id, CashFormInstance: CashCtrl.Payment })" data-action="scan-barcode">
<i class="fa fa-barcode"></i> <span translate>CASH.VOUCHER.BARCODE.SCAN</span>
<i class="fa fa-barcode"></i> <span translate>BARCODE.SCAN</span>
</a>
</li>
</ul>
Expand Down
6 changes: 3 additions & 3 deletions client/src/partials/cash/cash.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ angular.module('bhima.routes')
.state('cash.scan', {
url : '/:id/scan',
params : { id : { squash: true, value: null } },
onEnter :['$state', '$uibModal', scanBarcodeModal],
onEnter :['$state', '$uibModal', scanCashBarcodeModal],
onExit : ['$uibModalStack', closeModal]
});
}]);
Expand All @@ -74,9 +74,9 @@ function transferModal($state, Modal) {
});
}

function scanBarcodeModal($state, Modal) {
function scanCashBarcodeModal($state, Modal) {
Modal.open({
controller: 'CashBarcodeScannerModalController as CashBarCtrl',
controller: 'CashBarcodeScannerModalController as BarcodeModalCtrl',
templateUrl: 'partials/cash/modals/scanBarcode.modal.html',
size : 'lg',
backdrop: 'static',
Expand Down
29 changes: 14 additions & 15 deletions client/src/partials/cash/modals/scanBarcode.modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,48 @@

<div class="modal-header">
<ol class="headercrumb">
<li class="static">{{ ::CashBarCtrl.cashbox.label }}</li>
<li class="title" translate>CASH.VOUCHER.BARCODE.SCAN</li>
<li class="title" translate>BARCODE.SCAN</li>
</ol>
</div>

<div class="modal-body text-center" style="min-height:300px;">

<!-- state: awaiting barcode input -->
<h2 translate>CASH.VOUCHER.BARCODE.SCAN</h2>
<h2 translate>BARCODE.SCAN</h2>

<h1>
<i class="fa fa-barcode fa-3x"></i>
</h1>

<p ng-class="{ 'animate-flicker' : CashBarCtrl.flicker }">
<span class="text-muted" ng-if="CashBarCtrl.step === CashBarCtrl.INITIALIZED">
<span translate>CASH.VOUCHER.BARCODE.AWAITING_INPUT</span>
<p ng-class="{ 'animate-flicker' : BarcodeModalCtrl.flicker }">
<span class="text-muted" ng-if="BarcodeModalCtrl.step === BarcodeModalCtrl.INITIALIZED">
<span translate>BARCODE.AWAITING_INPUT</span>
</span>

<span class="text-primary" ng-if="CashBarCtrl.step === CashBarCtrl.LOADING">
<span class="text-primary" ng-if="BarcodeModalCtrl.step === BarcodeModalCtrl.LOADING">
<i class="fa fa-info-circle"></i>
<span translate>CASH.VOUCHER.BARCODE.AWAITING_HTTP</span>
<span translate>BARCODE.AWAITING_HTTP</span>
</span>

<span class="text-success" ng-if="CashBarCtrl.step === CashBarCtrl.READ_SUCCESS">
<i class="fa fa-check-circle-o"></i> <span translate>CASH.VOUCHER.BARCODE.READ_SUCCESS</span> {{ ::CashBarCtrl.invoice.reference }}
<span class="text-success" ng-if="BarcodeModalCtrl.step === BarcodeModalCtrl.READ_SUCCESS">
<i class="fa fa-check-circle-o"></i> <span translate>BARCODE.READ_SUCCESS</span> {{ ::BarcodeModalCtrl.invoice.reference }}
</span>

<span class="text-danger" ng-if="CashBarCtrl.step === CashBarCtrl.READ_ERROR">
<i class="fa fa-danger-sign"></i> <span translate>CASH.VOUCHER.BARCODE.READ_ERROR</span>
<span class="text-danger" ng-if="BarcodeModalCtrl.step === BarcodeModalCtrl.READ_ERROR">
<i class="fa fa-danger-sign"></i> <span translate>BARCODE.READ_ERROR</span>
</span>
</p>

<!-- hidden barcode input -->
<!-- TODO(@jniles): use ng-model-options to delay the $digest() loop and validate that a full barcode was inputted -->
<div style="width:0; overflow: hidden;">
<input ng-model="CashBarCtrl.barcode" ng-change="CashBarCtrl.triggerBarcodeRead()" bh-focus-on="true">
<input ng-model="BarcodeModalCtrl.barcode" ng-change="BarcodeModalCtrl.triggerBarcodeRead()" bh-focus-on="true">
</div>
</div>

<div class="modal-footer text-right">
<button type="button" class="btn btn-default" ng-click="CashBarCtrl.dismiss()">
<button type="button" class="btn btn-default" ng-click="BarcodeModalCtrl.dismiss()">
<span translate>FORM.BUTTONS.CANCEL</span>
</button>
</div>

</form>
12 changes: 5 additions & 7 deletions client/src/partials/templates/bhCurrencyInput.tmpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@
<div class="form-group"
ng-class="{ 'has-error' : $ctrl.validationTrigger && bhCurrencyInputForm.currencyInputValue.$invalid }"
data-bh-currency-input>
<label class="control-label">
{{ "FORM.LABELS.PAYMENT" | translate }}
</label>
<label class="control-label" translate>FORM.LABELS.PAYMENT</label>

<div class="input-group">
<span class="input-group-addon">{{ $ctrl.currency.symbol || '...' }}</span>
<span class="input-group-addon text-uppercase">{{ $ctrl.currency.symbol || '...' }}</span>
<input
class="form-control"
type="number"
Expand All @@ -23,17 +21,17 @@

<!-- displays: "This value is required" -->
<span ng-message="required">
<i class="fa fa-exclamation-circle"></i> {{ "FORM.ERRORS.REQUIRED" | translate }}
<i class="fa fa-exclamation-circle"></i> <span translate>FORM.ERRORS.REQUIRED</span>
</span>

<!-- displays: "This value is too small for the selected currency" -->
<span ng-message="min">
<i class="fa fa-exclamation-circle"></i> {{ "FORM.ERRORS.MIN_CURRENCY_UNIT" | translate }}
<i class="fa fa-exclamation-circle"></i> <span translate>FORM.ERRORS.MIN_CURRENCY_UNIT</span>
</span>

<!-- displays: "You must enter a valid number." -->
<span ng-message="number">
<i class="fa fa-exclamation-circle"></i> {{ "FORM.ERRORS.NUMBER" | translate }}
<i class="fa fa-exclamation-circle"></i> <span translate>FORM.ERRORS.NUMBER</span>
</span>
</p>
</div>
Expand Down
Loading

0 comments on commit 7d08f3c

Please sign in to comment.