Skip to content

Commit

Permalink
feat(invoice): lock price changes
Browse files Browse the repository at this point in the history
Implements the business logic for the enterprise setting "lock
price changes".  This enterprise setting allows the user to lock the
price changes on the Patient Invoice module.  If locked, no user can
change the price.

Closes #2174.
  • Loading branch information
jniles committed Mar 27, 2018
1 parent 1581916 commit 69510d0
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 44 deletions.
2 changes: 1 addition & 1 deletion client/src/js/components/bhYesNoRadios.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ angular.module('bhima.components')
bindings : {
value : '<',
label : '@',
name : '@',
helpText : '@?',
onChangeCallback : '&',
},
Expand All @@ -21,7 +22,6 @@ function YesNoRadioController() {

$ctrl.$onInit = () => {
$ctrl.value = Number.parseInt($ctrl.value, 10);
$ctrl.onChangeCallback = $ctrl.onChangeCallback || angular.noop;
};

$ctrl.onChange = (value) => {
Expand Down
5 changes: 3 additions & 2 deletions client/src/modules/enterprises/enterprises.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ angular.module('bhima.controllers')
.controller('EnterpriseController', EnterpriseController);

EnterpriseController.$inject = [
'EnterpriseService', 'util', 'NotifyService', 'ProjectService', 'ModalService', 'ScrollService',
'EnterpriseService', 'util', 'NotifyService', 'ProjectService', 'ModalService', 'ScrollService', 'SessionService',
];

/**
Expand All @@ -11,7 +11,7 @@ EnterpriseController.$inject = [
* @description
* This controller binds the basic CRUD operations on the enterprise.
*/
function EnterpriseController(Enterprises, util, Notify, Projects, Modal, ScrollTo) {
function EnterpriseController(Enterprises, util, Notify, Projects, Modal, ScrollTo, Session) {
const vm = this;

vm.enterprise = {};
Expand Down Expand Up @@ -85,6 +85,7 @@ function EnterpriseController(Enterprises, util, Notify, Projects, Modal, Scroll
.then(() => {
Notify.success(creation ? 'FORM.INFO.SAVE_SUCCESS' : 'FORM.INFO.UPDATE_SUCCESS');
})
.then(() => Session.reload())
.catch(Notify.handleError);
}

Expand Down
96 changes: 57 additions & 39 deletions client/src/modules/invoices/patientInvoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,46 +14,65 @@ PatientInvoiceController.$inject = [
*
* @todo (required) Tabbing through UI grid. Code -> Quantity -> Price
* @todo (required) Invoice made outside of fiscal year error should be handled and shown to user
* @todo (requires) use a loading button for the form loading state.
*/
function PatientInvoiceController(Patients, PatientInvoices, PatientInvoiceForm, util, Session, Receipts, Notify, Constants, Exchange, moment) {
var vm = this;
function PatientInvoiceController(
Patients, PatientInvoices, PatientInvoiceForm, util, Session, Receipts, Notify,
Constants, Exchange, moment
) {
const vm = this;

// bind the enterprise to get the enterprise currency id
vm.enterprise = Session.enterprise;
vm.Invoice = new PatientInvoiceForm('PatientInvoiceForm');
vm.ROW_ERROR_FLAG = Constants.grid.ROW_ERROR_FLAG;
vm.FORBID_PRICE_CHANGES = (Session.enterprise.settings.enable_price_lock);

// application constants
vm.maxLength = util.maxTextLength;
vm.minimumDate = util.minimumDate;
vm.itemIncrement = 1;
vm.onPatientSearchApiCallback = onPatientSearchApiCallback;

var gridOptions = {

const gridOptions = {
appScopeProvider : vm,
enableSorting : false,
enableColumnMenus : false,
rowTemplate : 'modules/templates/grid/error.row.html',
columnDefs : [
{
field : 'status', width : 25, displayName : '', cellTemplate : 'modules/invoices/templates/grid/status.tmpl.html'
},
{
field : 'code', displayName : 'TABLE.COLUMNS.CODE', headerCellFilter : 'translate', cellTemplate : 'modules/invoices/templates/grid/code.tmpl.html'
},
{ field : 'description', displayName : 'TABLE.COLUMNS.DESCRIPTION', headerCellFilter : 'translate' },
{
field : 'quantity', displayName : 'TABLE.COLUMNS.QUANTITY', headerCellFilter : 'translate', cellTemplate : 'modules/invoices/templates/grid/quantity.tmpl.html'
},
{
field : 'transaction_price', displayName : 'FORM.LABELS.UNIT_PRICE', headerCellFilter : 'translate', cellTemplate : 'modules/invoices/templates/grid/unit.tmpl.html'
},
{
field : 'amount', displayName : 'TABLE.COLUMNS.AMOUNT', headerCellFilter : 'translate', cellTemplate : 'modules/invoices/templates/grid/amount.tmpl.html'
},
{ field : 'actions', width : 25, cellTemplate : 'modules/invoices/templates/grid/actions.tmpl.html' },
],
columnDefs : [{
field : 'status',
width : 25,
displayName : '',
cellTemplate : 'modules/invoices/templates/grid/status.tmpl.html',
}, {
field : 'code',
displayName : 'TABLE.COLUMNS.CODE',
headerCellFilter : 'translate',
cellTemplate : 'modules/invoices/templates/grid/code.tmpl.html',
}, {
field : 'description',
displayName : 'TABLE.COLUMNS.DESCRIPTION',
headerCellFilter : 'translate',
}, {
field : 'quantity',
displayName : 'TABLE.COLUMNS.QUANTITY',
headerCellFilter : 'translate',
cellTemplate : 'modules/invoices/templates/grid/quantity.tmpl.html',
}, {
field : 'transaction_price',
displayName : 'FORM.LABELS.UNIT_PRICE',
headerCellFilter : 'translate',
cellTemplate : 'modules/invoices/templates/grid/unit.tmpl.html',
}, {
field : 'amount',
displayName : 'TABLE.COLUMNS.AMOUNT',
headerCellFilter : 'translate',
cellTemplate : 'modules/invoices/templates/grid/amount.tmpl.html',
}, {
field : 'actions',
width : 25,
cellTemplate : 'modules/invoices/templates/grid/actions.tmpl.html',
}],
onRegisterApi : exposeGridScroll,
data : vm.Invoice.store.data,
};
Expand All @@ -65,22 +84,20 @@ function PatientInvoiceController(Patients, PatientInvoices, PatientInvoiceForm,
}

function setPatient(p) {
var uuid = p.uuid;
const { uuid } = p;
Patients.read(uuid)
.then(function (patient) {
.then(patient => {
vm.Invoice.setPatient(patient);
return Patients.balance(patient.uuid);
})
.then(function (balance) {
.then(balance => {
vm.patientBalance = balance;
});
}

// invoice total and items are successfully sent and written to the server
function submit(detailsForm) {
var items;
var invalidItems;
var firstInvalidItem;
let firstInvalidItem;

vm.Invoice.writeCache();

Expand All @@ -90,39 +107,39 @@ function PatientInvoiceController(Patients, PatientInvoices, PatientInvoiceForm,
// make sure there are actually items to validate
if (vm.Invoice.store.data.length === 0) {
Notify.danger('PATIENT_INVOICE.INVALID_ITEMS');
return;
return 0;
}

// ask service items to validate themselves - if anything is returned it is invalid
invalidItems = vm.Invoice.validate(true);
const invalidItems = vm.Invoice.validate(true);

if (invalidItems.length) {
firstInvalidItem = invalidItems[0];
[firstInvalidItem] = invalidItems;
Notify.danger(firstInvalidItem._message);

// show the user where the error is
vm.gridApi.core.scrollTo(firstInvalidItem);
return;
return 0;
}

// if the form is invalid, return right away
if (detailsForm.$invalid) {
Notify.danger('FORM.ERRORS.RECORD_ERROR');
return;
return 0;
}

// copy the rows for insertion
items = angular.copy(vm.Invoice.store.data);
const items = angular.copy(vm.Invoice.store.data);

var description = vm.Invoice.getTemplatedDescription();
const description = vm.Invoice.getTemplatedDescription();

// invoice consists of
// 1. Invoice details
// 2. Invoice items
// 3. Charged invoicing fees - each of these have the global charge calculated by the client
// 4. Charged subsidies - each of these have the global charge calculated by the client
return PatientInvoices.create(vm.Invoice.details, items, vm.Invoice.invoicingFees, vm.Invoice.subsidies, description)
.then(function (result) {
.then(result => {
detailsForm.$setPristine();
detailsForm.$setUntouched();
return result;
Expand All @@ -138,7 +155,8 @@ function PatientInvoiceController(Patients, PatientInvoices, PatientInvoiceForm,

// adds n items to the grid (unless the inventory is used up)
function addItems(n) {
while (n--) {
let i = n;
while (i--) {
vm.Invoice.addItem();
}
}
Expand All @@ -147,7 +165,7 @@ function PatientInvoiceController(Patients, PatientInvoices, PatientInvoiceForm,
vm.Invoice.clearCache();

Receipts.invoice(invoice.uuid, true)
.then(function () { clear(); });
.then(() => { clear(); });
}

// register the patient search api
Expand Down
8 changes: 8 additions & 0 deletions client/src/modules/invoices/templates/grid/unit.tmpl.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
<div style="padding : 5px;">
<div ng-if="grid.appScope.FORBID_PRICE_CHANGES" class="text-right">
<span ng-hide="row.entity._initialised">-</span>
<span ng-show="row.entity._initialised">
{{ row.entity.transaction_price | currency:grid.appScope.enterprise.currency_id }}
</span>
</div>

<input
min="0.0001"
type="number"
ng-if="!grid.appScope.FORBID_PRICE_CHANGES"
ng-disabled="!row.entity._initialised"
class="form-control"
style="padding-left : 5px !important"
Expand Down
4 changes: 2 additions & 2 deletions client/src/modules/templates/bhYesNoRadios.tmpl.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<label class="radio-inline">
<input
type="radio"
name="choice"
name="$ctrl.name"
data-choice="yes"
ng-value="1"
ng-model="$ctrl.value"
Expand All @@ -19,7 +19,7 @@
<label class="radio-inline">
<input
type="radio"
name="choice"
name="$ctrl.name"
data-choice="no"
ng-value="0"
ng-model="$ctrl.value"
Expand Down

0 comments on commit 69510d0

Please sign in to comment.