Skip to content

Commit

Permalink
Merge #3827
Browse files Browse the repository at this point in the history
3827: feat(report) debtor summary report r=lomamech a=jeremielodi

closes #3826

Co-authored-by: jeremielodi <jeremielodi@gmail.com>
  • Loading branch information
bors[bot] and jeremielodi committed Aug 6, 2019
2 parents 19f0753 + a835fb3 commit f1a7e6f
Show file tree
Hide file tree
Showing 10 changed files with 321 additions and 3 deletions.
5 changes: 5 additions & 0 deletions client/src/i18n/en/report.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@
"SHOW_UNVERIFIED_TRANSACTIONS": "Include Unverified Transactions",
"SHOW_UNVERIFIED_TRANSACTIONS_HELP": "Enable to include records from the Journal which have not been audited by the accountant as well as records from the General Ledger the accountant has verified. This view must be treated as a projection until the accountant has approved the Journal transactions and moved them to the General Ledger."
},

"DEBTOR_SUMMARY" : {
"TITLE": "Debtors summary report",
"DESCRIPTION": "This report details the amount to paid in every inventory group for an invoice during a period, for a specific debor group"
},
"OPENING_BALANCE": "Opening Balance",
"ORDER": {
"LAST_TRANSACTION": "Date of Last Transaction",
Expand Down
4 changes: 4 additions & 0 deletions client/src/i18n/fr/report.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@
"SHOW_UNVERIFIED_TRANSACTIONS" : "Inclure les transactions non vérifiées",
"SHOW_UNVERIFIED_TRANSACTIONS_HELP" : "L'activation de cette option inclut les données du Journal (données non verifiées) et celles du Grand Livre (données vérifiées). Ce rapport doit être consideré comme une simple projection jusqu'à ce que les données du journal soient postées."
},
"DEBTOR_SUMMARY" : {
"TITLE": "Rapport sommaire des débiteurs",
"DESCRIPTION": "Ce rapport détaille le montant à payer dans chaque groupe d'inventaire pour une facture pendant une période, pour un groupe debor spécifique"
},
"OPENING_BALANCE": "Balance d'ouverture",
"ORDER": {
"LAST_TRANSACTION": "Date de la dernière transaction",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
angular.module('bhima.controllers')
.controller('debtorSummaryController', DebtorSummaryController);

DebtorSummaryController.$inject = [
'$sce', 'NotifyService', 'BaseReportService',
'AppCache', 'reportData', '$state',
];

function DebtorSummaryController($sce, Notify, SavedReports, AppCache, reportData, $state) {
const vm = this;
const cache = new AppCache('debtorSummary');
const reportUrl = 'reports/finance/debtorSummary';

vm.previewGenerated = false;
vm.reportDetails = {};

vm.clearPreview = function clearPreview() {
vm.previewGenerated = false;
vm.previewResult = null;
};

vm.onSelectDebtor = (debtorGroup) => {
vm.reportDetails.group_uuid = debtorGroup.uuid;
};

vm.preview = function preview(form) {
if (form.$invalid) {
Notify.danger('FORM.ERRORS.RECORD_ERROR');
return 0;
}


// update cached configuration
cache.reportDetails = angular.copy(vm.reportDetails);

return SavedReports.requestPreview(reportUrl, reportData.id, angular.copy(vm.reportDetails))
.then(result => {
vm.previewGenerated = true;
vm.previewResult = $sce.trustAsHtml(result);
})
.catch(Notify.handleError);
};

vm.requestSaveAs = function requestSaveAs() {
const options = {
url : reportUrl,
report : reportData,
reportOptions : angular.copy(vm.reportDetails),
};

return SavedReports.saveAsModal(options)
.then(() => {
$state.go('reportsBase.reportsArchive', { key : options.report.report_key });
})
.catch(Notify.handleError);
};

checkCachedConfiguration();

function checkCachedConfiguration() {
if (cache.reportDetails) {
vm.reportDetails = angular.copy(cache.reportDetails);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<bh-report-preview
ng-if="ReportConfigCtrl.previewGenerated"
source-document="ReportConfigCtrl.previewResult"
on-clear-callback="ReportConfigCtrl.clearPreview()"
on-save-callback="ReportConfigCtrl.requestSaveAs()">
</bh-report-preview>

<div ng-show="!ReportConfigCtrl.previewGenerated">
<div class="row">
<div class="col-md-12">
<h3 class="text-capitalize" translate>REPORT.DEBTOR_SUMMARY.TITLE</h3>
<p class="text-info" translate>REPORT.DEBTOR_SUMMARY.DESCRIPTION</p>
</div>
</div>

<div class="row" style="margin-top : 10px">
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<span translate>REPORT.UTIL.OPTIONS</span>
</div>
<div class="panel-body">
<form name="ConfigForm" bh-submit="ReportConfigCtrl.preview(ConfigForm)" novalidate>

<!-- debtor group -->
<bh-debtor-group-select
required="true"
debtor-group-uuid = "ReportConfigCtrl.reportDetails.group_uuid"
on-select-callback = "ReportConfigCtrl.onSelectDebtor(debtorGroup)">
</bh-debtor-group-select>


<!-- Date interval -->
<bh-date-interval
date-from="ReportConfigCtrl.reportDetails.dateFrom"
date-to="ReportConfigCtrl.reportDetails.dateTo"
required="true"
limit-min-fiscal>
</bh-date-interval>

<bh-loading-button loading-state="ConfigForm.$loading">
<span translate>REPORT.UTIL.PREVIEW</span>
</bh-loading-button>
</form>
</div>
</div>
</div>
</div>
</div>
1 change: 1 addition & 0 deletions client/src/modules/reports/reports.routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ angular.module('bhima.routes')
'indicatorsReport',
'visit_report',
'monthlyBalance',
'debtorSummary',
];

$stateProvider
Expand Down
3 changes: 2 additions & 1 deletion server/config/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ const accountReferenceType = require('../controllers/finance/accounts/accountRef
const indicators = require('../controllers/finance/indicator');
const breakEvenReference = require('../controllers/finance/breakEvenReference');

const debotrSummaryReport = require('../controllers/finance/reports/debtors/summaryReport');
// periods
const period = require('../controllers/finance/period');

Expand Down Expand Up @@ -416,7 +417,7 @@ exports.configure = function configure(app) {
app.get('/reports/finance/break_even', financeReports.breakEven.report);
app.get('/reports/finance/break_even_fee_center', financeReports.breakEvenFeeCenter.report);
app.get('/reports/finance/operating', financeReports.operating.document);

app.get('/reports/finance/debtorSummary', debotrSummaryReport.summaryReport);
// visits reports
app.get('/reports/visits', medicalReports.visitsReports.document);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{{> head title="REPORT.CLIENTS.TITLE"}}

<body>
<main class="container">
{{> header }}

<h3 class="text-center text-capitalize">
<strong>{{translate "REPORT.DEBTOR_SUMMARY.TITLE"}}</strong>
</h3>

<h4 class="text-center text-capitalize">
{{ debtorGroup.name }}
</h4>
<h5 class="text-center">
<strong class="text-capitalize">{{date dateFrom "MMMM YYYY"}}</strong>
-
<strong class="text-capitalize">{{date dateTo "MMMM YYYY"}}</strong>
</h4>

<section>
<table style="font-size:11px !important" class="table table-condensed table-report table-header-rotated table-bordered">
<thead>
<tr class="text-capitalize text-center" style="background: #ddd">
<th>{{translate "FORM.LABELS.NO"}}</th>
<th>{{translate "FORM.LABELS.DATE"}}</th>
<th>{{translate "TABLE.COLUMNS.REFERENCE"}}</th>
<th>{{translate "TABLE.COLUMNS.NAME"}}</th>
<th>{{translate "FORM.LABELS.SERVICE"}}</th>
{{#each ./inventoryGroups as | s |}}
<th class="rotate-45" >
<div><span style="padding-left:2px;">{{ s.name }}</span></div></th>

{{/each}}
<th class="rotate-45"><div><span style="padding-left:2px;">{{translate "FORM.LABELS.TOTAL"}}</span></div></th>

</tr>
</thead>
<tbody>
{{#each ./data as |invoice| }}
<tr>
<td>{{ add @index 1}}</td>
<td>{{ date invoice.date "DD/MM/YYYY"}}</td>
<td>{{ invoice.invRef}}</td>
<td>{{ invoice.debtorName}}</td>
<td>{{ invoice.serviceName}}</td>

{{#each invoice.inventoryGroups as | amount |}}
<th class="text-right">{{amount}}</th>
{{/each}}
<th class="text-right">{{currency invoice.total ../metadata.enterprise.currency_id}}</th>
</tr>

{{/each}}

</tbody>

<tfoot>
<td colspan="5" class="text-right">{{ translate 'FORM.LABELS.TOTAL'}}</td>
{{#each inventoryGroups as | s |}}
<td class="text-right">{{ currency s.total ../metadata.enterprise.currency_id}}</td>
{{/each}}
<td>{{currency gobalSum ../metadata.enterprise.currency_id}}</td>
</tfoot>
</table>
</section>
</main>
</body>
112 changes: 112 additions & 0 deletions server/controllers/finance/reports/debtors/summaryReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* @overview ./finance/reports/debtors/summaryReport.js
*
*/

const _ = require('lodash');
const ReportManager = require('../../../../lib/ReportManager');
const db = require('../../../../lib/db');
const util = require('../../../../lib/util');

module.exports.summaryReport = summaryReport;

// path to the template to render
const TEMPLATE = './server/controllers/finance/reports/debtors/summaryReport.handlebars';

const DEFAULT_OPTIONS = {
csvKey : 'debtors',
orientation : 'landscape',
footerRight : '[page] / [toPage]',
footerFontSize : '7',
};

/**
* @method summaryReport
*
* @description
* The HTTP interface which actually creates the report.
*/
async function summaryReport(req, res, next) {
try {
const qs = _.extend(req.query, DEFAULT_OPTIONS);
const { dateFrom, dateTo } = req.query;
const groupUuid = req.query.group_uuid;
const metadata = _.clone(req.session);

const report = new ReportManager(TEMPLATE, metadata, qs);

const inventoryGroupMap = {};
const emptyArray = [];
const inventoryGroupIndexMap = {};
let gobalSum = 0;

const invoiceSql = `
SELECT BUID(i.uuid) AS invoice_uuid, i.date, dm.text AS invRef, ent.text AS debtorRef,
d.text, SUM(it.transaction_price) AS amount, i.cost as total, BUID(i.debtor_uuid) AS debtor_uuid,
invg.name as inventoryGroupName, BUID(invg.uuid) as inventoryGroupUuid, inv.text as inventoryName, i.service_id,
s.name as serviceName
FROM invoice i
JOIN invoice_item it ON it.invoice_uuid = i.uuid
JOIN inventory inv ON inv.uuid = it.inventory_uuid
JOIN inventory_group invg ON invg.uuid = inv.group_uuid
JOIN debtor d ON d.uuid = i.debtor_uuid
JOIN debtor_group dg ON dg.uuid = d.group_uuid
JOIN entity_map ent ON ent.uuid = d.uuid
JOIN document_map dm ON dm.uuid = i.uuid
JOIN service s ON s.id = i.service_id
WHERE dg.uuid = ? AND (i.date BETWEEN ? AND ?) AND i.reversed = 0
GROUP BY invg.uuid, i.uuid
ORDER BY i.date
`;


const inventoryGroupsSql = `
SELECT DISTINCT x.inventoryGroupUuid as id, x.inventoryGroupName as name
FROM (${invoiceSql}) as x
`;

const debtorGroup = await db.one('SELECT name FROM debtor_group WHERE uuid=?', db.bid(groupUuid));
const inventoryGroups = await db.exec(inventoryGroupsSql, [db.bid(groupUuid), dateFrom, dateTo]);

// initilisation
inventoryGroups.forEach((s, index) => {
inventoryGroupMap[s.id] = s;
inventoryGroupMap[s.id].total = 0;
emptyArray.push(null);
inventoryGroupIndexMap[s.id] = index;
});

// let get the list of invoices for this group
const invoices = await db.exec(invoiceSql, [db.bid(groupUuid), dateFrom, dateTo]);

const invoicesList = _.groupBy(invoices, 'invoice_uuid');
const data = [];
// let loop each invoice attribute each invoice item to it inventoryGroup
Object.keys(invoicesList).forEach(invKey => {
const invItems = invoicesList[invKey];
const record = { inventoryGroups : _.clone(emptyArray) };
invItems.forEach(item => {
record.date = item.date;
record.invRef = item.invRef;
record.debtorRef = item.debtorRef;
record.total = item.total;
record.debtorName = item.text;
record.serviceName = item.serviceName;
record.inventoryGroups[inventoryGroupIndexMap[item.inventoryGroupUuid]] = item.amount;
inventoryGroupMap[item.inventoryGroupUuid].total += item.amount;
});
data.push(record);
});

data.forEach(record => {
gobalSum += record.total;
});
// then let render the report
const result = await report.render({
debtorGroup, inventoryGroups, data, dateFrom, dateTo, gobalSum : util.roundDecimal(gobalSum, 2),
});
res.set(result.headers).send(result.report);
} catch (ex) {
next(ex);
}
}
6 changes: 4 additions & 2 deletions server/models/bhima.sql
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ INSERT INTO unit VALUES
(241, 'Entity Folder', 'ENTITY.MANAGEMENT', 'Entity Folder', 0, '/modules/entities', '/ENTITY_FOLDER'),
(242, 'Entity Management','ENTITY.MANAGEMENT','',241,'/modules/entities','/entities'),
(243, 'Entity Group', 'ENTITY.GROUP.TITLE', 'Entity Group', 241, '/modules/entity_group', '/entity_group'),
(244, 'Monthly Balance', 'TREE.MONTHLY_BALANCE', 'Monthly Balance', 144, '/modules/reports/monthlyBalance', '/reports/monthlyBalance');
(244, 'Monthly Balance', 'TREE.MONTHLY_BALANCE', 'Monthly Balance', 144, '/modules/reports/monthlyBalance', '/reports/monthlyBalance'),
(245, 'Debtor summary report', 'REPORT.DEBTOR_SUMMARY.TITLE', 'Debtor summary report', 144, '/modules/reports/debtorSummary', '/reports/debtorSummary');

-- Reserved system account type
INSERT INTO `account_category` VALUES
Expand Down Expand Up @@ -180,7 +181,8 @@ INSERT INTO `report` (`id`, `report_key`, `title_key`) VALUES
(31, 'indicatorsReport', 'TREE.INDICATORS_REPORT'),
(32, 'visit_report', 'PATIENT_RECORDS.REPORT.VISITS'),
(33, 'stock_entry', 'REPORT.STOCK.ENTRY_REPORT'),
(34, 'monthlyBalance', 'REPORT.MONTHLY_BALANCE.TITLE');
(34, 'monthlyBalance', 'REPORT.MONTHLY_BALANCE.TITLE'),
(35, 'debtorSummary', 'REPORT.DEBTOR_SUMMARY.TITLE');

-- Supported Languages
INSERT INTO `language` VALUES
Expand Down
12 changes: 12 additions & 0 deletions server/models/migrations/next/migrations.sql
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,15 @@ INSERT INTO unit VALUES
INSERT INTO `report` (`id`, `report_key`, `title_key`) VALUES
(34, 'monthlyBalance', 'REPORT.MONTHLY_BALANCE.TITLE');

/*
@author:jeremielodi
@date: 2019-08-04
@title : Debtors summary report,
@description: TThis report shows the amount to paid for each debtor of a given debtor group in each service during a period
*/

INSERT INTO `report` (`id`, `report_key`, `title_key`) VALUES
(35, 'debtorSummary', 'REPORT.DEBTOR_SUMMARY.TITLE');

INSERT INTO unit VALUES
(245, 'Debtor summary report', 'REPORT.DEBTOR_SUMMARY.TITLE', 'Debtor summary report', 144, '/modules/reports/debtorSummary', '/reports/debtorSummary');

0 comments on commit f1a7e6f

Please sign in to comment.