From 901abeb5e489609f0a99bc283dbc64ce8893e995 Mon Sep 17 00:00:00 2001 From: Jonathan Niles Date: Tue, 14 Mar 2017 09:47:17 +0100 Subject: [PATCH] fix(i18n): better cash and credit note description 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. --- client/src/i18n/en.json | 12 ++-- client/src/i18n/fr.json | 6 +- client/src/js/services/CashService.js | 57 ++++++++++--------- client/src/partials/cash/cash.js | 3 +- .../registry/modalCreditNote.js | 10 +--- server/controllers/finance/cash.js | 15 +++-- .../controllers/finance/reports/cash/index.js | 26 ++++----- .../finance/reports/cash/receipt.handlebars | 39 +++++++------ .../reports/cash/receipt.pos.handlebars | 5 +- 9 files changed, 86 insertions(+), 87 deletions(-) diff --git a/client/src/i18n/en.json b/client/src/i18n/en.json index 0daded23f5..85692b8561 100644 --- a/client/src/i18n/en.json +++ b/client/src/i18n/en.json @@ -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", @@ -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": { @@ -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.", diff --git a/client/src/i18n/fr.json b/client/src/i18n/fr.json index 5bd62f7fa1..2773ab2a8f 100644 --- a/client/src/i18n/fr.json +++ b/client/src/i18n/fr.json @@ -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" }, @@ -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" }, @@ -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", diff --git a/client/src/js/services/CashService.js b/client/src/js/services/CashService.js index 45b23cd604..831299f664 100644 --- a/client/src/js/services/CashService.js +++ b/client/src/js/services/CashService.js @@ -2,7 +2,7 @@ angular.module('bhima.services') .service('CashService', CashService); CashService.$inject = [ - '$uibModal' ,'PrototypeApiService', 'ExchangeRateService', 'SessionService', 'moment' + '$uibModal', 'PrototypeApiService', 'ExchangeRateService', 'SessionService', 'moment', '$translate', ]; /** @@ -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; @@ -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; @@ -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 }); } /* @@ -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, + }); } /** @@ -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 @@ -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; } diff --git a/client/src/partials/cash/cash.js b/client/src/partials/cash/cash.js index 56901a4d4c..d01154cafd 100644 --- a/client/src/partials/cash/cash.js +++ b/client/src/partials/cash/cash.js @@ -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); @@ -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); diff --git a/client/src/partials/patient_invoice/registry/modalCreditNote.js b/client/src/partials/patient_invoice/registry/modalCreditNote.js index ace02f807a..296face797 100644 --- a/client/src/partials/patient_invoice/registry/modalCreditNote.js +++ b/client/src/partials/patient_invoice/registry/modalCreditNote.js @@ -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; @@ -25,7 +23,6 @@ function ModalCreditNoteController(Instance, Invoices, data, Vouchers, Notify, $ .catch(Notify.handleError); function submit(form) { - // stop submission if the form is invalid if (form.$invalid) { return; } @@ -33,7 +30,6 @@ function ModalCreditNoteController(Instance, Invoices, data, Vouchers, Notify, $ 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, diff --git a/server/controllers/finance/cash.js b/server/controllers/finance/cash.js index e77f64bdb6..d399e1700d 100644 --- a/server/controllers/finance/cash.js +++ b/server/controllers/finance/cash.js @@ -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}`); } @@ -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; @@ -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) @@ -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) diff --git a/server/controllers/finance/reports/cash/index.js b/server/controllers/finance/reports/cash/index.js index 6d3f1e49d2..9a8d3cce41 100644 --- a/server/controllers/finance/reports/cash/index.js +++ b/server/controllers/finance/reports/cash/index.js @@ -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'); @@ -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); } @@ -63,20 +61,17 @@ 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; } @@ -84,20 +79,21 @@ function receipt(req, res, next) { 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); @@ -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 @@ -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) diff --git a/server/controllers/finance/reports/cash/receipt.handlebars b/server/controllers/finance/reports/cash/receipt.handlebars index c4c337de70..a4f830c46b 100644 --- a/server/controllers/finance/reports/cash/receipt.handlebars +++ b/server/controllers/finance/reports/cash/receipt.handlebars @@ -13,6 +13,7 @@ {{translate 'FORM.LABELS.EMAIL'}}: {{enterprise.email}}

+

@@ -44,26 +45,29 @@ {{translate 'FORM.LABELS.PAYMENT'}}: {{payment.reference}}
{{translate 'FORM.LABELS.AMOUNT'}}: {{currency payment.amount payment.currency_id}}
{{translate 'FORM.LABELS.DATE'}}: {{date payment.date}}
- {{#if hasRate}} {{currency rate payment.currency_id}}
{{/if}} + {{#if hasRate}} + {{translate 'FORM.LABELS.EXCHANGE_RATE'}} {{currency rate payment.currency_id}}
+ {{/if}} {{translate "TABLE.COLUMNS.CREATED_BY"}}: {{user.display_name}}

+ +

{{payment.description}}

+ - - {{#with payment}} - {{#if is_caution}} - - {{else}} - - - {{/if}} - {{/with}} - + {{#with payment}} + {{#if is_caution}} + + {{else}} + + + {{/if}} + {{/with}} @@ -88,14 +92,13 @@ - - {{#if payment.is_caution}} - - {{else}} + + {{#unless payment.is_caution}} + - {{/if }} - - + + + {{/unless }}
{{translate "CASH.CAUTION"}}{{translate "FORM.LABELS.INVOICE"}}{{translate "FORM.LABELS.SERVICE"}}{{translate "CASH.CAUTION"}}{{translate "FORM.LABELS.INVOICE"}}{{translate "FORM.LABELS.SERVICE"}}{{translate "FORM.LABELS.AMOUNT"}}
{{currency payment.amount payment.currency_id}}
{{translate "FORM.LABELS.TOTAL_BALANCE_REMAINING"}}
{{translate "FORM.LABELS.TOTAL_BALANCE_REMAINING"}}{{currency debtorTotalBalance enterprise.currency_id}}
{{currency debtorTotalBalance enterprise.currency_id}}
diff --git a/server/controllers/finance/reports/cash/receipt.pos.handlebars b/server/controllers/finance/reports/cash/receipt.pos.handlebars index 40e669ea74..59f482d1db 100644 --- a/server/controllers/finance/reports/cash/receipt.pos.handlebars +++ b/server/controllers/finance/reports/cash/receipt.pos.handlebars @@ -34,8 +34,9 @@ {{#each payment.items as |item| }} - -

{{translate "FORM.LABELS.INVOICE"}} {{item.reference}} / {{translate "FORM.LABELS.SERVICE"}} {{item.serviceName}}

+

+ {{translate "FORM.LABELS.INVOICE"}} {{item.reference}} -- {{item.serviceName}} +