Skip to content

Commit

Permalink
feat(invoices): summary report invoice aggregates
Browse files Browse the repository at this point in the history
This commit implements a summary report on the invoice HTML/PDF
registries with statistics about the queried fields.  These fields show
aggregate information about the contents of the registry for validation
and auditing.
  • Loading branch information
Jonathan Niles authored and sfount committed Jan 14, 2017
1 parent ca1d522 commit 6b968c3
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 6 deletions.
13 changes: 12 additions & 1 deletion client/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,6 @@
"FOUND" : "Found",
"GROUPS_DEBTOR" : "Assign a new debtor group from the options below. Press 'confirm' to permanently update the patient's debtor group",
"GROUPS_PATIENT" : "Assign the patient to patient groups from the options below. Press 'confirm' to permanently update the patient's subscribed groups.",
"INVOICES" : "Invoices",
"ITEMS_FULL" : "There are no additional inventory items that be assigned to this invoice.",
"IMPORT_SUCCESS" : "Successfully Imported",
"LOADING" : "Loading",
Expand Down Expand Up @@ -551,6 +550,7 @@
"INTL" : "International",
"INVENTORY" : "Inventory",
"INVOICE" : "Invoice",
"INVOICES" : "Invoices",
"INVOICES_DETAILS" : "Invoices Details",
"INVOICE_PAYMENT" : "Invoice Payment",
"IS_ASSET" : "Asset ?",
Expand Down Expand Up @@ -1276,6 +1276,7 @@
"COST_CENTER" : "Cost Center",
"COUNTRY" : "Country",
"CREDIT" : "Credit",
"CREATED_BY" : "Created by",
"CURRENCY" : "Currency",
"DATA_SOURCE" : "Data Source",
"DATE" : "Date",
Expand Down Expand Up @@ -1387,6 +1388,16 @@
"VALUE" : "Value",
"VILLAGE" : "Village/Township",
"QUANTITY" : "Quantity"
},
"AGGREGATES" : {
"NUMBER_OF_DAYS" : "Number of Days",
"TOTAL_COST" : "Total Cost",
"TOTAL_AMOUNT" : "Total Amount",
"NUM_USERS" : "Number of Users",
"NUM_SERVICES" : "Number of Services",
"NUM_PROJECTS" : "Number of Projects",
"NUM_PAYMENTS" : "Number of Payments",
"NUM_INVOICES" : "Number of Invoices"
}
},
"TRANSACTION_TYPE" : {
Expand Down
33 changes: 31 additions & 2 deletions server/controllers/finance/reports/invoices/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const util = require('../../../../lib/util');

const moment = require('moment');

const db = require('../../../../lib/db');
const ReportManager = require('../../../../lib/ReportManager');
const Invoices = require('../../patientInvoice');
const Patients = require('../../../medical/patients');
Expand Down Expand Up @@ -53,9 +54,37 @@ function report(req, res, next) {
return next(e);
}

// @todo - this should use a .find() method like patient registratons
// This is an easy way to make sure that all the data is captured from any
// given search without having to parse the parameters. Just map the UUIDs
// and then we will use only the values previously used.
const sql = `
SELECT MIN(invoice.date) AS minDate, MAX(invoice.date) AS maxDate,
SUM(invoice.cost) AS amount, COUNT(DISTINCT(user_id)) AS numUsers,
COUNT(invoice.uuid) AS numInvoices,
COUNT(DISTINCT(project_id)) AS numProjects,
COUNT(DISTINCT(DATE(invoice.date))) AS numDays,
COUNT(DISTINCT(invoice.service_id)) AS numServices
FROM invoice
WHERE invoice.uuid IN (?);
`;

const data = {};

Invoices.find(query)
.then(rows => report.render({ rows }))
.then(rows => {
data.rows = rows;
let uuids = rows.map(row => db.bid(row.uuid));

// if no uuids, return false as the aggregates
if (!uuids.length) { return false; }

return db.one(sql, [uuids]);
})
.then(aggregates => {
data.aggregates = aggregates;
data.hasMultipleProjects = aggregates.numProjects > 1;
return report.render(data);
})
.then(result => {
res.set(result.headers).send(result.report);
})
Expand Down
55 changes: 52 additions & 3 deletions server/controllers/finance/reports/invoices/report.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
{{translate 'INVOICE_REGISTRY.TITLE'}}
</h2>

<!-- list of data -->
<!-- invoices listed -->
<table class="table table-condensed table-bordered table-striped">
<thead>
<tr>
<th>{{translate 'TABLE.COLUMNS.REFERENCE'}}</th>
<th>{{translate 'TABLE.COLUMNS.BILLING_DATE'}}</th>
<th>{{translate 'TABLE.COLUMNS.PATIENT'}}</th>
<th>{{translate 'TABLE.COLUMNS.COST'}}</th>
<th>{{translate 'TABLE.COLUMNS.AMOUNT'}}</th>
<th>{{translate 'TABLE.COLUMNS.SERVICE'}}</th>
<th>{{translate 'TABLE.COLUMNS.USER'}}</th>
<th>{{translate 'TABLE.COLUMNS.CREATED_BY'}}</th>
</tr>
</thead>
<tbody>
Expand All @@ -35,9 +35,58 @@
<td>{{serviceName}}</td>
<td>{{display_name}}</td>
</tr>
{{else}}
{{> emptyTable columns=6}}
{{/each}}
</tbody>
<tfoot>
<tr>
<th colspan="3">{{ translate 'TABLE.AGGREGATES.NUM_INVOICES' }}: {{ aggregates.numInvoices }} </th>
<th class="text-right">{{currency aggregates.amount metadata.enterprise.currency_id }}</th>
<th colspan="2"></th>
</tr>
</tfoot>
</table>
</div>
</div>

{{#if aggregates}}
<div class="row">
<div class="col-xs-6">

<!-- summary table -->
<table class="table table-condensed table-bordered table-striped">
<tbody>
<tr>
<th colspan="2" class="text-center">
{{ translate 'FORM.LABELS.SUMMARY' }} ({{date aggregates.minDate }} - {{date aggregates.maxDate}})
</th>
</tr>
<tr>
<th>{{ translate 'TABLE.AGGREGATES.NUM_INVOICES' }}</th>
<td class="text-right">{{ aggregates.numInvoices }}</td>
</tr>
<tr>
<th>{{ translate 'TABLE.AGGREGATES.TOTAL_AMOUNT' }}</th>
<td class="text-right">{{currency aggregates.amount metadata.enterprise.currency_id}}</td>
</tr>
<tr>
<th>{{ translate 'TABLE.AGGREGATES.NUMBER_OF_DAYS' }}</th>
<td class="text-right">{{ aggregates.numDays }}</td>
</tr>
<tr>
<th>{{ translate 'TABLE.AGGREGATES.NUM_SERVICES' }}</th>
<td class="text-right">{{ aggregates.numServices }}</td>
</tr>
{{#if hasMultipleProjects }}
<tr>
<th>{{ translate 'TABLE.AGGREGATES.NUM_PROJECTS' }}</th>
<td class="text-right">{{ aggregates.numProjects }}</td>
</tr>
{{/if}}
</tbody>
</table>
</div>
</div>
{{/if}}
</body>

0 comments on commit 6b968c3

Please sign in to comment.