Skip to content

Commit

Permalink
feat(cash): read barcodes into CashForm
Browse files Browse the repository at this point in the history
This commit completes the feature of reading a barcode into the
CashForm.  The data is passed around via event emitters after
unsuccessfully trying to use the data in the `$state` object to
configure the form.
  • Loading branch information
Jonathan Niles authored and sfount committed Jan 14, 2017
1 parent 31d14e3 commit 7a23ac8
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 21 deletions.
2 changes: 1 addition & 1 deletion client/src/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ function constantConfig() {
ROW_INVALID_FLAG : '_invalid'
},
barcodes : {
LENGTH : 0
LENGTH : 10
},
transactionType : {
GENERIC_INCOME : 1,
Expand Down
2 changes: 1 addition & 1 deletion client/src/js/services/BarcodeService.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function BarcodeService($http, util) {

service.search = function search(code) {
return $http.get('/barcode/'.concat(code))
.then(util.unwrapHTTPResponse);
.then(util.unwrapHttpResponse);
};

return service;
Expand Down
16 changes: 16 additions & 0 deletions client/src/js/services/PatientInvoiceService.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function PatientInvoiceService(Modal, util, Session, Api) {
service.openSearchModal = openSearchModal;
service.formatFilterParameters = formatFilterParameters;
service.openCreditNoteModal = openCreditNoteModal;
service.balance = balance;

/**
* @method create
Expand Down Expand Up @@ -52,6 +53,21 @@ function PatientInvoiceService(Modal, util, Session, Api) {
return Api.create.call(this, { invoice: invoice });
}

/**
* @method balance
*
* @description
* This method returns the balance on an invoice due to a debtor.
*
* @param {String} uuid - the invoice uuid
* @param {String} debtorUuid - the amount due to the debtor
*/
function balance(uuid) {
var url = '/invoices/'.concat(uuid).concat('/balance');
return this.$http.get(url)
.then(this.util.unwrapHttpResponse);
}

// utility methods

// remove the source items from invoice items - if they exist
Expand Down
17 changes: 16 additions & 1 deletion client/src/partials/cash/CashForm.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,16 @@ function CashFormService(AppCache, Session, Patients, Exchange) {
* This method takes in a patient and sets the form's debtor_uuid as needed.
* It also looks up to confirm if the patient has a caution to alert the user.
*/
CashForm.prototype.setPatient = function setPatient(patient) {
CashForm.prototype.setPatient = function setPatient(patient, searchByUuid) {
var self = this;

this.patient = patient;
this.details.debtor_uuid = patient.debtor_uuid;

if (this.patientApi && searchByUuid) {
this.patientApi.searchByUuid(patient.uuid);
}

return Patients.balance(patient.debtor_uuid)
.then(function (balance) {

Expand All @@ -109,6 +113,17 @@ function CashFormService(AppCache, Session, Patients, Exchange) {
});
};

/**
* @method bindPatientApi
*
* @description
* This binds the bhFindPatient's API to re-search for a patient.
* @todo -- this feels really strictly tied ... can we do better?
*/
CashForm.prototype.bindPatientApi = function bindPatientApi(api) {
this.patientApi = api;
};

/**
* @method setCashbox
*
Expand Down
5 changes: 2 additions & 3 deletions client/src/partials/cash/cash.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
</li>
<li role="separator" class="divider"></li>
<li role="menuitem">
<a href ui-sref="cash.scan({ id: CashCtrl.cashbox.id })" data-action="scan-barcode">
<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>
</a>
</li>
Expand Down Expand Up @@ -110,7 +110,7 @@
<tbody>

<!-- show a list of previous invoices to pay again -->
<tr ng-repeat="invoice in CashCtrl.Payment.details.invoices | orderBy:'date' track by invoice.uuid">
<tr ng-repeat="invoice in CashCtrl.Payment.details.invoices | orderBy:'date'">
<td>{{ invoice.reference }}</td>
<td>{{ invoice.date | date }}</td>
<td class="text-right">{{ invoice.balance | currency:CashCtrl.enterprise.currency_id }}</td>
Expand Down Expand Up @@ -174,7 +174,6 @@
</div>

<div class="form-group" ng-if="CashCtrl.Payment.messages.hasPositiveAccountBalance">

<div class="alert alert-info">
<i class="fa fa-info-circle"></i> <strong class="text-capitalize" translate>CASH.CAUTION_REMAINING</strong>:
{{ CashCtrl.Payment.messages.patientAccountBalance | currency:CashCtrl.enterprise.currency_id }}
Expand Down
13 changes: 11 additions & 2 deletions client/src/partials/cash/cash.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ angular.module('bhima.controllers')
CashController.$inject = [
'CashService', 'CashboxService', 'AppCache', 'CurrencyService',
'SessionService', 'ModalService', 'NotifyService', '$state',
'ReceiptModal', 'CashFormService', '$q'
'ReceiptModal', 'CashFormService', '$q', '$rootScope'
];

/**
Expand All @@ -16,7 +16,7 @@ CashController.$inject = [
* against previous invoices. The cash payments module provides
* functionality to pay both in multiple currencies.
*/
function CashController(Cash, Cashboxes, AppCache, Currencies, Session, Modals, Notify, $state, Receipts, CashForm, $q) {
function CashController(Cash, Cashboxes, AppCache, Currencies, Session, Modals, Notify, $state, Receipts, CashForm, $q, RS) {
var vm = this;

var cacheKey = 'CashPayments';
Expand Down Expand Up @@ -53,6 +53,7 @@ function CashController(Cash, Cashboxes, AppCache, Currencies, Session, Modals,
// fired when the bhFindPatient API becomes available
function onRegisterApiCallback(api) {
vm.bhFindPatient = api;
vm.Payment.bindPatientApi(api);
}

// fired on controller start or form refresh
Expand Down Expand Up @@ -171,6 +172,14 @@ function CashController(Cash, Cashboxes, AppCache, Currencies, Session, Modals,
form.$setPristine();
}

function configureCashPaymentsForm(event, data) {
vm.Payment.setInvoices(data.invoices);
vm.Payment.details.description = data.description;
vm.Payment.setPatient(data.patient, true);
}

RS.$on('cash:configure', configureCashPaymentsForm);

// start up the module
startup();
}
2 changes: 1 addition & 1 deletion client/src/partials/cash/modals/scanBarcode.modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ <h1>

<!-- hidden barcode input -->
<div style="width:0; overflow: hidden;">
<input ng-model="CashBarCtrl.barcode" ng-change="CashBarCtrl.triggerBarcodeRead()" bh-focus-on="CashBarCtrl.step == CashBarCtrl.INITIALIZED">
<input ng-model="CashBarCtrl.barcode" ng-change="CashBarCtrl.triggerBarcodeRead()" bh-focus-on="true">
</div>
</div>

Expand Down
43 changes: 32 additions & 11 deletions client/src/partials/cash/modals/scanBarcode.modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@ angular.module('bhima.controllers')
.controller('CashBarcodeScannerModalController', CashBarController);

CashBarController.$inject = [
'$state', 'CashboxService', 'NotifyService', 'BarcodeService', 'PatientService', 'bhConstants', '$uibModalInstance'
'$state', 'CashboxService', 'NotifyService', 'BarcodeService', 'PatientService',
'bhConstants', '$uibModalInstance', '$timeout', 'PatientInvoiceService', '$rootScope'
];

/**
* @module cash/modals/CashBarController
*
* @description
* This controller is responsible for scanning barcodes and then configuring the CashForm with the barcode
* This controller is responsible for scanning barcodes and the
* configuring the CashForm with the barcode
*/
function CashBarController($state, Cashboxes, Notify, Barcodes, Patients, bhConstants, Instance) {
function CashBarController($state, Cashboxes, Notify, Barcodes, Patients, bhConstants, Instance, $timeout, Invoices, RS) {
var vm = this;
var id = $state.params.id;

var MODAL_CLOSE_TIMEOUT = 500;

vm.triggerBarcodeRead = triggerBarcodeRead;
vm.dismiss = dismiss;

Expand All @@ -37,14 +41,14 @@ function CashBarController($state, Cashboxes, Notify, Barcodes, Patients, bhCons
Instance.dismiss();
}

// TODO(@jniles) potentially this should tell you if you are trying to read a
// cash payment instead of an invoice
// TODO(@jniles) potentially this should clear the input when the barcode is greater in length than 10.
function triggerBarcodeRead() {

console.log('TriggerBarcodeRead with:', vm.barcode);

if (!isValidBarcode(vm.barcode)) {
vm.step = vm.READ_ERROR;
} else {
if (isValidBarcode(vm.barcode)) {
searchForBarcode(vm.barcode);
} else {
vm.step = vm.READ_ERROR;
}
}

Expand All @@ -58,15 +62,32 @@ function CashBarController($state, Cashboxes, Notify, Barcodes, Patients, bhCons
Barcodes.search(barcode)
.then(function (invoice) {
vm.invoice = invoice;
return Patients.search({ debtor_uuid : invoice.debtor_uuid });
}).then(function (patients) {
return Invoices.balance(invoice.uuid);
})
.then(function (balance) {
vm.balance = balance;
return Patients.search({ debtor_uuid : vm.invoice.debtor_uuid });
})
.then(function (patients) {

// de-structure search array
var patient = patients[0];

vm.patient = patient;

// emit the
RS.$broadcast('cash:configure', {
invoices: [vm.balance],
patient: vm.patient,
description : vm.invoice.serviceName
});

vm.step = vm.READ_SUCCESS;

// close the modal after a timeout
$timeout(function () {
Instance.close();
}, MODAL_CLOSE_TIMEOUT, false);
})
.catch(function (error) {
vm.step = vm.READ_ERROR;
Expand Down
1 change: 1 addition & 0 deletions server/config/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ exports.configure = function configure(app) {
app.post('/invoices', patientInvoice.create);
app.get('/invoices/search', patientInvoice.search);
app.get('/invoices/:uuid', patientInvoice.detail);
app.get('/invoices/:uuid/balance', patientInvoice.balance);

// route for invoice Report

Expand Down
28 changes: 27 additions & 1 deletion server/controllers/finance/patientInvoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const NotFound = require('../../lib/errors/NotFound');
const BadRequest = require('../../lib/errors/BadRequest');

const createInvoice = require('./invoice/patientInvoice.create');
const Debtors = require('./debtors');

/** Retrieves a list of all patient invoices (accepts ?q delimiter). */
exports.list = list;
Expand All @@ -45,6 +46,9 @@ exports.lookupInvoice = lookupInvoice;

exports.find = find;

/** find the balance on an invoice due the particular debtor */
exports.balance = balance;

/**
* list
*
Expand All @@ -59,6 +63,27 @@ function list(req, res, next) {
.done();
}

/**
* @method balance
*
* @description
* This uses the lookupInvoice() and the invoiceBalances methods to find the
* balance on a single invoice due to a debtor.
*
* @todo(jniles) write tests!
*/
function balance(req, res, next) {
lookupInvoice(req.params.uuid)
.then(invoice => {
return Debtors.invoiceBalances(invoice.debtor_uuid, [req.params.uuid]);
})
.then(rows => {
res.status(200).json(rows[0]);
})
.catch(next)
.done();
}


/**
* @method lookupInvoice
Expand All @@ -76,10 +101,11 @@ function lookupInvoice(invoiceUuid) {
`SELECT BUID(invoice.uuid) as uuid, CONCAT_WS('.', '${identifiers.INVOICE.key}', project.abbr, invoice.reference) AS reference,
invoice.cost, invoice.description, BUID(invoice.debtor_uuid) AS debtor_uuid,
patient.display_name AS debtor_name, BUID(patient.uuid) as patient_uuid,
invoice.user_id, invoice.date, user.display_name,
invoice.user_id, invoice.date, user.display_name, invoice.service_id, service.name AS serviceName,
enterprise.currency_id
FROM invoice
LEFT JOIN patient ON patient.debtor_uuid = invoice.debtor_uuid
JOIN service ON invoice.service_id = service.id
JOIN project ON project.id = invoice.project_id
JOIN enterprise ON enterprise.id = project.enterprise_id
JOIN user ON user.id = invoice.user_id
Expand Down

0 comments on commit 7a23ac8

Please sign in to comment.