Skip to content

Commit

Permalink
fix(i18n): better cash and credit note description
Browse files Browse the repository at this point in the history
This commit refactors the cash payments and credit note descriptions.
It makes sure that we have human readable text for our reports to aid
our administrators to better track references by their human readable
equivalents.  This is based on feedback from the administrative team at
Vanga as well as a need to have translated description.

It also slightly improves the cash thermal receipts by ordering the paid
invoices in the order in which they are paid.  The invoices not fully
paid appear at the bottom of the list as one might expect.

Finally, the cash description is now on the receipt.  Icons have been
removed in favor of text.

Partially addresses #1330.
  • Loading branch information
Jonathan Niles authored and sfount committed Mar 17, 2017
1 parent 15fc2f4 commit 901abeb
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 87 deletions.
12 changes: 7 additions & 5 deletions client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@
"CURRENT_CASHBOX" : "Current Cashbox",
"DEBTOR_INVOICES" : "Debtor Invoices",
"PAYMENT" : "Payment",
"PAYMENT_DESCRIPTION" : "Cash payment by {{patientName}} ({{patientReference}}) against invoice(s) {{invoiceReferences}}.",
"PREPAYMENT_DESCRIPTION" : "Prepayment by {{patientName}} ({{patientReference}}).",
"REGISTRY" : {
"INCLUDE_ONLY_REVERSED_RECORDS" : "Include only reversed cash payments",
"EXCLUDE_REVERSED_RECORDS" : "Exclude all reversed cash payments",
Expand All @@ -115,10 +117,10 @@
"MISSING_CASH" : "Missing Cash",
"TITLE" : "Select a Cashbox"
},
"TITLE" : "Cash Window",
"TOOLS" : "Tools",
"RECEIPT": {
"TITLE" : "Cash Receipt",
"TITLE" : "Cash Window",
"TOOLS" : "Tools",
"RECEIPT" : {
"TITLE" : "Cash Receipt",
"SUCCESS" : "Successfully created a Cash Payment"
},
"TRANSFER": {
Expand Down Expand Up @@ -417,7 +419,7 @@
"CURRENT" : "Current",
"CLEAR_FILTERS" : "Clear Filters",
"CREDIT_NOTE" : "Credit Note",
"CREDIT_NOTE_INVOICE" : "Credit Note reversing invoice {{ invoiceReference }} ({{ invoiceAmount }}) of debtor {{ debtorName }} ({{ debtorIdentifier }}). {{ description }}",
"CREDIT_NOTE_INVOICE" : "Credit Note reversing invoice {{ invoiceReference }} of debtor {{ debtorName }} ({{ debtorIdentifier }}). {{ description }}",
"DANGER_ZONE" : "Danger Zone",
"DELETE_SUCCESS" : "Successfully deleted a record",
"DISABLED_CURRENCY" : "This currency is unusable since there is no account set for it. Use the Cashbox Management module to set an account for this currency and cashbox.",
Expand Down
6 changes: 4 additions & 2 deletions client/src/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@
"CAUTION_REMAINING" : "Caution Restante",
"CONFIRM_PAYMENT_WHEN_CAUTION" : "Procéder au paiement alors que le patient dispose d'une caution ?",
"CURRENT_CASHBOX" : "Caisse courante",
"PAYMENT_DESCRIPTION" : "Paiement par {{patientName}} ({{patientReference}}) contre les facture(s) {{invoiceReferences}}.",
"PREPAYMENT_DESCRIPTION" : "Paiement caution par {{patientName}} ({{patientReference}}).",
"DEBTOR_INVOICES": {
"TITLE" : "Facture(s) debiteur"
},
Expand All @@ -116,7 +118,7 @@
"SUCCESS" : "Créé avec succès un paiement en espèces"
},
"SELECTION": {
"GO_TO_CASHBOX_PAGE" : "Aller a la page de gestion de caisse",
"GO_TO_CASHBOX_PAGE" : "Allez a la page de gestion de caisse",
"MISSING_CASH" : "Caisse manquante",
"TITLE" : "Selection caisse"
},
Expand Down Expand Up @@ -418,7 +420,7 @@
"CREATED" : "Crée",
"CREATE_SUCCESS" : "Crée avec succès",
"CREDIT_NOTE" : "Note de crédit",
"CREDIT_NOTE_INVOICE" : "Note de credit concernant la facture {{ invoiceReference }} ({{ invoiceAmount }}) de debiteur {{ debtorName }} ({{ debtorIdentifier }}). {{ description }}",
"CREDIT_NOTE_INVOICE" : "Note de credit concernant la facture {{ invoiceReference }} de debiteur {{ debtorName }} ({{ debtorIdentifier }}). {{ description }}",
"CURRENT" : "Actuel",
"DANGER_ZONE" : "Zone Dangereuse",
"DELETE_SUCCESS" : "Suppression avec succès",
Expand Down
57 changes: 29 additions & 28 deletions client/src/js/services/CashService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ angular.module('bhima.services')
.service('CashService', CashService);

CashService.$inject = [
'$uibModal' ,'PrototypeApiService', 'ExchangeRateService', 'SessionService', 'moment'
'$uibModal', 'PrototypeApiService', 'ExchangeRateService', 'SessionService', 'moment', '$translate',
];

/**
Expand All @@ -12,13 +12,9 @@ CashService.$inject = [
* @description
* A service to interact with the server-side /cash API.
*/
function CashService(Modal, Api, Exchange, Session, moment) {
var service = new Api('/cash/');
var urlCheckin = '/cash/checkin/';

// templates for descriptions
var PAYMENT_DESCRIPTION = 'Cash Payment / :date / :name \n ';
var CAUTION_DESCRIPTION = 'Caution Payment / :date / :name';
function CashService(Modal, Api, Exchange, Session, moment, $translate) {
var service = new Api('/cash/');
var urlCheckin = '/cash/checkin/';

// custom methods
service.create = create;
Expand All @@ -34,17 +30,13 @@ function CashService(Modal, Api, Exchange, Session, moment) {
* invoice until it is decimated.
*/
function allocatePaymentAmounts(data) {

// the global amount paid
var totalAmount = data.amount;

// default to an empty array if necessary -- the server will throw an error
var items = (data.invoices || [])

// loop through the invoices, allocating a sum to the invoice until there
// is no more left to allocate.
.map(function (invoice) {
return { invoice_uuid : invoice.uuid };
return { invoice_uuid: invoice.uuid };
});

return items;
Expand Down Expand Up @@ -76,7 +68,7 @@ function CashService(Modal, Api, Exchange, Session, moment) {
delete data.invoices;

// call the prototype create method with the formatted data
return Api.create.call(service, { payment : data });
return Api.create.call(service, { payment: data });
}

/*
Expand All @@ -86,12 +78,21 @@ function CashService(Modal, Api, Exchange, Session, moment) {
var isCaution = payment.is_caution;
var date = payment.date;

var tmpl = isCaution ? CAUTION_DESCRIPTION : PAYMENT_DESCRIPTION;
// invoice references
var invoicesReferences = payment.invoices.map(function (invoice) {
return invoice.reference;
});

return tmpl
.replace(':date', moment(date).format('YYYY-MM-DD'))
.replace(':name', patient.display_name)
.trim();
var referencesString = invoicesReferences.join(', ');

var tmpl = isCaution ? 'CASH.PREPAYMENT_DESCRIPTION' : 'CASH.PAYMENT_DESCRIPTION';

return $translate.instant(tmpl, {
patientName : patient.display_name,
invoiceReferences : referencesString,
patientReference : patient.reference,
amount : payment.amount,
});
}

/**
Expand Down Expand Up @@ -132,11 +133,11 @@ function CashService(Modal, Api, Exchange, Session, moment) {
{ field: 'debtor_uuid', displayName: 'FORM.LABELS.CLIENT' },
{ field: 'user_id', displayName: 'FORM.LABELS.USER' },
{ field: 'reference', displayName: 'FORM.LABELS.REFERENCE' },
{ field: 'dateFrom', displayName: 'FORM.LABELS.DATE_FROM', comparitor: '>', ngFilter:'date' },
{ field: 'dateTo', displayName: 'FORM.LABELS.DATE_TO', comparitor: '<', ngFilter:'date' },
{ field: 'dateFrom', displayName: 'FORM.LABELS.DATE_FROM', comparitor: '>', ngFilter: 'date' },
{ field: 'dateTo', displayName: 'FORM.LABELS.DATE_TO', comparitor: '<', ngFilter: 'date' },
{ field: 'currency_id', displayName: 'FORM.LABELS.CURRENCY' },
{ field: 'reversed', displayName: 'CASH.REGISTRY.REVERSED_RECORDS' },
{ field : 'patientReference', displayName: 'FORM.LABELS.REFERENCE_PATIENT'}
{ field : 'patientReference', displayName: 'FORM.LABELS.REFERENCE_PATIENT' },
];

// returns columns from filters
Expand All @@ -156,12 +157,12 @@ function CashService(Modal, Api, Exchange, Session, moment) {
function openCancelCashModal(invoice) {
return Modal.open({
templateUrl : 'partials/cash/modals/modalCancelCash.html',
resolve : { data : { invoice : invoice } },
size : 'md',
animation : false,
keyboard : false,
backdrop : 'static',
controller : 'ModalCancelCashController as ModalCtrl',
resolve : { data: { invoice: invoice } },
size : 'md',
animation : false,
keyboard : false,
backdrop : 'static',
controller : 'ModalCancelCashController as ModalCtrl',
}).result;
}

Expand Down
3 changes: 1 addition & 2 deletions client/src/partials/cash/cash.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ function CashController(Cash, Cashboxes, AppCache, Currencies, Session, Modals,

// submit payment
function submitPayment(form) {

// make a copy of the data before submitting
var copy = angular.copy(vm.Payment.details);

Expand All @@ -158,7 +157,7 @@ function CashController(Cash, Cashboxes, AppCache, Currencies, Session, Modals,
clear(form);

if (vm.openBarcodeModalOnSuccess) {
$state.go('^.scan', { id : vm.cashbox.id });
$state.go('^.scan', { id: vm.cashbox.id });
}
})
.catch(Notify.handleError);
Expand Down
10 changes: 3 additions & 7 deletions client/src/partials/patient_invoice/registry/modalCreditNote.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@ angular.module('bhima.controllers')

ModalCreditNoteController.$inject = [
'$uibModalInstance', 'PatientInvoiceService', 'data', 'VoucherService', 'NotifyService',
'$translate', '$filter',
'$translate',
];

function ModalCreditNoteController(Instance, Invoices, data, Vouchers, Notify, $translate, $filter) {
function ModalCreditNoteController(Instance, Invoices, data, Vouchers, Notify, $translate) {
var vm = this;

var $currency = $filter('currency');

vm.creditNote = {};
vm.submit = submit;
vm.cancel = function () { Instance.close(false); };
vm.cancel = function cancel() { Instance.close(false); };

vm.creditNote.uuid = data.invoice.uuid;
vm.patientInvoice = data.invoice;
Expand All @@ -25,15 +23,13 @@ function ModalCreditNoteController(Instance, Invoices, data, Vouchers, Notify, $
.catch(Notify.handleError);

function submit(form) {

// stop submission if the form is invalid
if (form.$invalid) { return; }

var note = angular.copy(vm.creditNote);

var creditNoteMessage = $translate.instant('FORM.INFO.CREDIT_NOTE_INVOICE', {
invoiceReference : vm.patientInvoice.reference,
invoiceAmount : $currency(vm.patientInvoice.cost, data.invoice.currency_id),
description : vm.creditNote.description,
debtorName : data.invoice.patientName,
debtorIdentifier : data.invoice.patientReference,
Expand Down
15 changes: 7 additions & 8 deletions server/controllers/finance/cash.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ function lookup(id) {
JOIN invoice AS i ON ci.invoice_uuid = i.uuid
JOIN project AS p ON i.project_id = p.id
LEFT JOIN service AS s ON i.service_id = s.id
WHERE ci.cash_uuid = ?;
WHERE ci.cash_uuid = ?
ORDER BY i.date ASC;
`;

return db.exec(cashRecordSql, [ bid ])
.then(function (rows) {

return db.exec(cashRecordSql, [bid])
.then((rows) => {
if (!rows.length) {
throw new NotFound(`No cash record by uuid: ${id}`);
}
Expand All @@ -104,8 +104,7 @@ function lookup(id) {

return db.exec(cashItemsRecordSql, bid);
})
.then(function (rows) {

.then((rows) => {
// bind the cash items to the "items" property and return
record.items = rows;

Expand All @@ -127,7 +126,7 @@ function lookup(id) {
*/
function list(req, res, next) {
listPayment()
.then(function (rows) {
.then((rows) => {
res.status(200).json(rows);
})
.catch(next)
Expand All @@ -140,7 +139,7 @@ function list(req, res, next) {
*/
function search(req, res, next) {
listPayment(req.query)
.then(rows => {
.then((rows) => {
res.status(200).json(rows);
})
.catch(next)
Expand Down
26 changes: 11 additions & 15 deletions server/controllers/finance/reports/cash/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@
* @module finance/reports/cash
*/

const _ = require('lodash');
const q = require('q');
const _ = require('lodash');
const q = require('q');
const moment = require('moment');

const BadRequest = require('../../../../lib/errors/BadRequest');
const ReportManager = require('../../../../lib/ReportManager');

const pdf = require('../../../../lib/renderers/pdf');
Expand Down Expand Up @@ -45,10 +44,9 @@ function receipt(req, res, next) {
const options = req.query;

let report;

let template = RECEIPT_TEMPLATE;

if (Boolean(Number(options.posReceipt))) {
if (Number(options.posReceipt)) {
template = POS_RECEIPT_TEMPLATE;
_.extend(options, pdf.posReceiptOptions);
}
Expand All @@ -63,41 +61,39 @@ function receipt(req, res, next) {
const data = {};

CashPayments.lookup(req.params.uuid)
.then(payment => {
.then((payment) => {
data.payment = payment;

// create a description for the cash payment's receipt
let descriptionParts = payment.description.split(' -- ');

const descriptionParts = payment.description.split(' -- ');
let renderedDescription;

if (descriptionParts.length > 1) {

// render everything after the descriptor
renderedDescription = _.drop(descriptionParts, 1).join('');
} else {

// unable to parse ... use the whole description.
renderedDescription = payment.description;
}

payment.renderedDescription = renderedDescription;

// lookup balances on all invoices
let invoices = payment.items.map(invoices => invoices.invoice_uuid);
const invoices = payment.items.map(invoices => invoices.invoice_uuid);

return q.all([
Users.lookup(payment.user_id),
Patients.lookupByDebtorUuid(payment.debtor_uuid),
Enterprises.lookupByProjectId(payment.project_id),
Debtors.invoiceBalances(payment.debtor_uuid, invoices),
Debtors.balance(payment.debtor_uuid)
Debtors.balance(payment.debtor_uuid),
]);
})
.spread((user, patient, enterprise, invoices, totalInvoices) => {
_.assign(data, { user, patient, enterprise, invoices, totalInvoices });
return Exchange.getExchangeRate(enterprise.id, data.payment.currency_id, data.payment.date);
})
.then(exchange => {
.then((exchange) => {
data.rate = exchange.rate;
data.currentDateFormatted = (new moment()).format('L');
data.hasRate = (data.rate && !data.payment.is_caution);
Expand All @@ -109,7 +105,7 @@ function receipt(req, res, next) {

data.debtorTotalBalance = data.totalInvoices.balance;

data.payment.items.forEach(invoiceItem => {
data.payment.items.forEach((invoiceItem) => {
invoiceItem.balance = data.balances[invoiceItem.invoice_uuid];

// if the payment is anything other than the enterprise rate, exchange it
Expand All @@ -120,7 +116,7 @@ function receipt(req, res, next) {

return report.render(data);
})
.then(result => {
.then((result) => {
res.set(result.headers).send(result.report);
})
.catch(next)
Expand Down
Loading

0 comments on commit 901abeb

Please sign in to comment.