Skip to content

Commit

Permalink
fix(invoice): block no sales account
Browse files Browse the repository at this point in the history
This commit ensures that the patient invoice page blocks invoices that
contain items missing sales account.  In production, this should only
occur if your database was improperly initialized.

Each item now is able to alert its own message as a tooltip when the
error state is hovered over.  Additionally, the grid itself displays a
large error when it detects items without a sales account.

Partially addresses #980.
  • Loading branch information
Jonathan Niles authored and sfount committed Nov 30, 2016
1 parent 379be48 commit 569185e
Show file tree
Hide file tree
Showing 8 changed files with 59 additions and 12 deletions.
7 changes: 6 additions & 1 deletion client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,12 @@
"STAT_TOTAL_INVOICE" : "Total Invoices",
"STAT_PAID" : "Paid",
"STAT_NOT_PAID" : "Waiting Payment",
"SUCCESS" : "Patient invoice successfully recorded."
"SUCCESS" : "Patient invoice successfully recorded.",
"ERRORS" : {
"INVALID_NUMBERS" : "The record contains negative, zero, or invalid numbers",
"MISSING_SALES_ACCOUNT" : "The record contains an inventory item missing a sales account. You cannot make an invoice without a sales account.",
"NOT_CONFIGURED" : "The record is missing an inventory item. Please put in an inventory item or remove the row."
}
},
"PATIENT_RECORDS": {
"TITLE" : "Patients",
Expand Down
16 changes: 13 additions & 3 deletions client/src/js/services/PatientInvoiceForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ function PatientInvoiceFormService(Patients, PriceLists, Inventory, AppCache, St
this.inventory = new Pool({ identifier: 'uuid', data : [] });

// set up the inventory
// @todo - if necessary, we could call this in a reload() or setup() method
Inventory.getInventoryItems()
Inventory.read(null, { detailed : 1 })
.then(function (data) {
this.inventory.initialize('uuid', data);
}.bind(this));
Expand Down Expand Up @@ -152,15 +151,23 @@ function PatientInvoiceFormService(Patients, PriceLists, Inventory, AppCache, St
PatientInvoiceForm.prototype.validate = function validate(highlight) {
this.digest();

var globalConfigurationError = null;

// filters out valid items
var invalidItems = this.store.data.filter(function (row) {
row[ROW_ERROR_FLAG] = highlight ? row._invalid : false;

// this sets the global configuration error if a
if (!row._hasSalesAccount) {
globalConfigurationError = row._message;
}

return row._invalid;
});


this._invalid = invalidItems.length > 0;
this._valid = !this._invalid;
this._error = globalConfigurationError;

return invalidItems;
};
Expand Down Expand Up @@ -360,6 +367,9 @@ function PatientInvoiceFormService(Patients, PriceLists, Inventory, AppCache, St

// make sure to validate and calculate new totals
this.digest();

// check for global configuration errors
this.validate();
};


Expand Down
26 changes: 25 additions & 1 deletion client/src/js/services/PatientInvoiceItemService.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,33 @@ function PatientInvoiceItemService(uuid) {
item.quantity > 0 &&
item.transaction_price >= 0;

// ensure the item has a sales account
var hasSalesAccount = angular.isDefined(item._salesAccount) &&
item._salesAccount !== null;

item._hasSalesAccount = hasSalesAccount;

// the item is only initialised if it has an inventory item
item._initialised = angular.isDefined(item.inventory_uuid);

// alias both valid and invalid for easy reading
item._valid = item._initialised && hasValidNumbers;
item._valid = item._initialised && hasValidNumbers && hasSalesAccount;
item._invalid = !item._valid;

item._message = '';

// if the item is invalid, bind the error reason to it.
if (item._invalid) {

// possible validation messages
if (!item._initialised) {
item._message = 'PATIENT_INVOICE.ERRORS.MISSING_SALES_ACCOUNT';
} else if (!hasSalesAccount) {
item._message = 'PATIENT_INVOICE.ERRORS.NOT_CONFIGURED';
} else {
item._message = 'PATIENT_INVOICE.ERRORS.INVALID_NUMBERS';
}
}
};

/**
Expand All @@ -91,6 +112,9 @@ function PatientInvoiceItemService(uuid) {
this.transaction_price = inventoryItem.price;
this.inventory_price = inventoryItem.price;
this.inventory_uuid = inventoryItem.uuid;

// special binding to make sure inventory items have a sales_account
this._salesAccount = inventoryItem.sales_account;
} catch (e) {}

// reset the validation flags.
Expand Down
8 changes: 8 additions & 0 deletions client/src/partials/patient_invoice/patientInvoice.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@
</div>
</div>

<div ng-show="PatientInvoiceCtrl.Invoice._error" class="row">
<div class="col-xs-12">
<div class="alert alert-danger">
<i class="fa fa-warning"></i> {{ PatientInvoiceCtrl.Invoice._error | translate }}
</div>
</div>
</div>

<!-- TODO Move padding to generic CSS class -->
<div class="row" style="padding-bottom : 5px;">
<div class="col-xs-12">
Expand Down
3 changes: 1 addition & 2 deletions client/src/partials/patient_invoice/patientInvoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ function PatientInvoiceController(Patients, PatientInvoices, PatientInvoiceForm,
Patients.read(uuid)
.then(function (patient) {
vm.Invoice.setPatient(patient);

return Patients.balance(patient.debtor_uuid);
})
.then(function (balance) {
Expand All @@ -92,9 +91,9 @@ function PatientInvoiceController(Patients, PatientInvoices, PatientInvoiceForm,
var invalidItems = vm.Invoice.validate(true);

if (invalidItems.length) {
Notify.danger('PATIENT_INVOICE.INVALID_ITEMS');

var firstInvalidItem = invalidItems[0];
Notify.danger(firstInvalidItem._message);

// show the user where the error is
vm.gridApi.core.scrollTo(firstInvalidItem);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
popover-placement="right"
popover-append-to-body="true"
popover-trigger="mouseenter">

<span class="fa fa-info-circle"></span>
</span>
<span>{{ row.entity.code }}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
class="ui-grid-cell-contents">

<!-- warning state: invalid, but not initialised yet -->
<span ng-hide="row.entity._initialised" class="fa fa-circle-o"></span>
<span ng-hide="row.entity._initialised" class="fa fa-circle-o" uib-tooltip="{{ row.entity._message | translate }}" tooltip-placement="right" tooltip-append-to-body="true"></span>

<!-- error state: invalid and initialised -->
<span ng-show="row.entity._initialised" class="fa fa-times-circle"></span>
<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>

<div ng-show="row.entity._valid" class="bg-success ui-grid-cell-contents">
Expand Down
6 changes: 4 additions & 2 deletions server/controllers/inventory/inventory/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ function getItemsMetadata() {
var sql =
`SELECT BUID(i.uuid) as uuid, i.code, i.text AS label, i.price, iu.text AS unit,
it.text AS type, ig.name AS groupName, BUID(ig.uuid) AS group_uuid, i.consumable, i.stock_min,
i.stock_max, i.origin_stamp AS timestamp, i.type_id, i.unit_id, i.unit_weight, i.unit_volume
i.stock_max, i.origin_stamp AS timestamp, i.type_id, i.unit_id, i.unit_weight, i.unit_volume,
ig.sales_account
FROM inventory AS i JOIN inventory_type AS it
JOIN inventory_unit AS iu JOIN inventory_group AS ig ON
i.type_id = it.id AND i.group_uuid = ig.uuid AND
Expand All @@ -137,7 +138,8 @@ function getItemsMetadataById(uuid) {
var sql =
`SELECT BUID(i.uuid) as uuid, i.code, i.text AS label, i.price, iu.text AS unit,
it.text AS type, ig.name AS groupName, BUID(ig.uuid) AS group_uuid, i.consumable, i.stock_min,
i.stock_max, i.origin_stamp AS timestamp, i.type_id, i.unit_id, i.unit_weight, i.unit_volume
i.stock_max, i.origin_stamp AS timestamp, i.type_id, i.unit_id, i.unit_weight, i.unit_volume,
ig.sales_account
FROM inventory AS i JOIN inventory_type AS it
JOIN inventory_unit AS iu JOIN inventory_group AS ig ON
i.type_id = it.id AND i.group_uuid = ig.uuid AND
Expand Down

0 comments on commit 569185e

Please sign in to comment.