diff --git a/client/src/i18n/en/report.json b/client/src/i18n/en/report.json
index 09bd2a5bc0..6a03e8f645 100644
--- a/client/src/i18n/en/report.json
+++ b/client/src/i18n/en/report.json
@@ -143,6 +143,14 @@
"DESCRIPTION": "This report shows the difference between what we purchase with what we have entered in the stock.",
"TITLE": "Purchase Order Analysis"
},
+ "RUMER": {
+ "DESCRIPTION": "Drug Usage and Recipe Register",
+ "INCLUDE_OUT_STOCK_ITEMS": "Include out-of-stock items",
+ "STOCK_BEGINNING": "Stock at the beginning of the month",
+ "STOCK_END": "Stock at the end of the month",
+ "TITLE": "RUMER",
+ "TOTAL_ENTRIES": "Total entries for the month"
+ },
"STOCK_VALUE": {
"TITLE": "Stock value report",
"DESCRIPTION": "This report shows the current stock value for each inventory in a depot",
diff --git a/client/src/i18n/en/tree.json b/client/src/i18n/en/tree.json
index f37e50bff1..a523fd8c3c 100644
--- a/client/src/i18n/en/tree.json
+++ b/client/src/i18n/en/tree.json
@@ -139,6 +139,7 @@
"ROLE_MANAGEMENT" : "Role Management",
"ROOT" : "Root",
"RUBRIC_MANAGEMENT" : "Rubrics Management",
+ "RUMER_REPORT" : "Rumer",
"SERVICE" : "Services",
"SIMPLE_VOUCHER" : "Simple Voucher",
"STAFFING_INDICES_MANAGEMENT" : "Staffing indices management",
diff --git a/client/src/i18n/fr/report.json b/client/src/i18n/fr/report.json
index faa45147da..916c3d7248 100644
--- a/client/src/i18n/fr/report.json
+++ b/client/src/i18n/fr/report.json
@@ -132,6 +132,14 @@
"TITLE": "Situation financière d'un Patient",
"TOTAL_ALL_STOCK_MOV": "Total de tous les mouvements de stocks"
},
+ "RUMER": {
+ "DESCRIPTION": "Registre d’utilisation des médicaments et recettes",
+ "INCLUDE_OUT_STOCK_ITEMS": "Inclure les articles en rupture de stock",
+ "STOCK_BEGINNING": "Stock au début du mois",
+ "STOCK_END": "Stock à la fin du mois",
+ "TITLE": "RUMER",
+ "TOTAL_ENTRIES": "Total des entrées du mois"
+ },
"PURCHASE_ORDER_ANALYSIS": {
"DESCRIPTION": "Ce rapport permet de connaitre la différence entre ce la quantité commandée et la quantité entrée en stock",
"TITLE": "Analyse de commande d'achat"
diff --git a/client/src/i18n/fr/tree.json b/client/src/i18n/fr/tree.json
index 69c08a9cc5..ab10f8df95 100644
--- a/client/src/i18n/fr/tree.json
+++ b/client/src/i18n/fr/tree.json
@@ -139,6 +139,7 @@
"ROLE_MANAGEMENT":"Gestion des rôles",
"ROOT":"Racine",
"RUBRIC_MANAGEMENT":"Gestion Rubriques",
+ "RUMER_REPORT" : "Rumer",
"SERVICE":"Services",
"SIMPLE_VOUCHER":"Bordereau de transfert",
"STAFFING_INDICES_MANAGEMENT" : "Gestion des indices de paie",
diff --git a/client/src/modules/reports/generate/rumer_report/rumer_report.config.js b/client/src/modules/reports/generate/rumer_report/rumer_report.config.js
new file mode 100644
index 0000000000..aca0c80328
--- /dev/null
+++ b/client/src/modules/reports/generate/rumer_report/rumer_report.config.js
@@ -0,0 +1,83 @@
+angular.module('bhima.controllers')
+ .controller('rumer_reportController', rumerReportController);
+
+rumerReportController.$inject = [
+ '$sce', 'NotifyService', 'BaseReportService', 'AppCache', 'reportData', '$state',
+ 'LanguageService',
+];
+
+function rumerReportController($sce, Notify, SavedReports, AppCache, reportData, $state, Languages) {
+ const vm = this;
+ const cache = new AppCache('rumer_report');
+ const reportUrl = 'reports/stock/rumer_report';
+
+ // default values
+ vm.reportDetails = {
+ includePurchaseEntry : 1,
+ };
+ vm.previewGenerated = false;
+
+ // check cached configuration
+ checkCachedConfiguration();
+
+ vm.onSelectDepot = depot => {
+ vm.reportDetails.depotUuid = depot.uuid;
+ vm.reportDetails.depot_text = depot.text;
+ };
+
+ vm.onSelectFiscalYear = (fiscalYear) => {
+ vm.reportDetails.fiscal_id = fiscalYear.id;
+ vm.reportDetails.fiscalYearStart = fiscalYear.start_date;
+ };
+
+ vm.onSelectPeriod = (period) => {
+ vm.reportDetails.period_id = period.id;
+ vm.reportDetails.end_date = period.end_date;
+ vm.reportDetails.start_date = period.start_date;
+ };
+
+ vm.clear = key => {
+ delete vm[key];
+ };
+
+ vm.clearPreview = () => {
+ vm.previewGenerated = false;
+ vm.previewResult = null;
+ };
+
+ vm.preview = form => {
+ if (form.$invalid) {
+ return 0;
+ }
+
+ // update cached configuration
+ cache.reportDetails = angular.copy(vm.reportDetails);
+ angular.extend(vm.reportDetails, { lang : Languages.key });
+
+ 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);
+ };
+
+ function checkCachedConfiguration() {
+ vm.reportDetails = angular.copy(cache.reportDetails || {});
+ }
+
+}
diff --git a/client/src/modules/reports/generate/rumer_report/rumer_report.html b/client/src/modules/reports/generate/rumer_report/rumer_report.html
new file mode 100644
index 0000000000..aa8afe178f
--- /dev/null
+++ b/client/src/modules/reports/generate/rumer_report/rumer_report.html
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
REPORT.RUMER.TITLE
+
REPORT.RUMER.DESCRIPTION
+
+
+
+
+
+
+
+ REPORT.UTIL.OPTIONS
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/modules/reports/reports.routes.js b/client/src/modules/reports/reports.routes.js
index 0774dc9620..f67e30d808 100644
--- a/client/src/modules/reports/reports.routes.js
+++ b/client/src/modules/reports/reports.routes.js
@@ -52,6 +52,7 @@ angular.module('bhima.routes')
'stock_movement_report',
'stock_expiration_report',
'stock_changes',
+ 'rumer_report',
];
function resolveReportData($stateParams, SavedReports) {
diff --git a/server/config/routes.js b/server/config/routes.js
index 57db6be706..1872b2da25 100644
--- a/server/config/routes.js
+++ b/server/config/routes.js
@@ -846,6 +846,7 @@ exports.configure = function configure(app) {
app.get('/reports/stock/sheet', stockReports.stockSheetReport);
app.get('/reports/stock/value', stockReports.stockValue);
app.get('/reports/stock/monthly_consumption', stockReports.monthlyConsumption.report);
+ app.get('/reports/stock/rumer_report', stockReports.rumer.report);
// stock receipts API
app.get('/receipts/stock/:uuid', stockReports.renderStockReceipt);
diff --git a/server/controllers/stock/index.js b/server/controllers/stock/index.js
index 144d08a048..908c40b8eb 100644
--- a/server/controllers/stock/index.js
+++ b/server/controllers/stock/index.js
@@ -193,7 +193,6 @@ function updateQuantityInStockAfterMovement(inventoryUuids, mvmtDate, depotUuid)
*/
async function insertNewStock(session, params) {
const transaction = db.transaction();
- const identifier = uuid();
const documentUuid = uuid();
const period = await Fiscal.lookupFiscalYearByDate(params.movement.date);
diff --git a/server/controllers/stock/reports/index.js b/server/controllers/stock/reports/index.js
index a3052e0161..ae076352d9 100644
--- a/server/controllers/stock/reports/index.js
+++ b/server/controllers/stock/reports/index.js
@@ -154,3 +154,4 @@ exports.stockChangesReport = stockChangesReport;
exports.stockAdjustmentReceipt = stockAdjustmentReceipt;
exports.stockExitAggregateConsumptionReceipt = stockExitAggregateConsumptionReceipt;
exports.monthlyConsumption = require('./stock/monthly_consumption');
+exports.rumer = require('./stock/rumer');
diff --git a/server/controllers/stock/reports/rumer.report.handlebars b/server/controllers/stock/reports/rumer.report.handlebars
new file mode 100644
index 0000000000..84a722ee54
--- /dev/null
+++ b/server/controllers/stock/reports/rumer.report.handlebars
@@ -0,0 +1,54 @@
+{{> head title="REPORT.MONTHLY_CONSUMPTION.TITLE" }}
+
+
+ {{> header}}
+
+
+
+
+
+
+ {{translate 'REPORT.RUMER.TITLE'}}
+
+
+
{{translate 'REPORT.RUMER.DESCRIPTION'}}
+
+
{{params.depot_text}}
+
{{date params.start_date}} - {{date params.end_date}}
+
+
+
+
+ {{translate 'FORM.LABELS.INVENTORY'}} |
+ {{translate 'REPORT.RUMER.STOCK_BEGINNING'}} |
+ {{translate 'REPORT.RUMER.TOTAL_ENTRIES'}} |
+ {{#each header as | key |}}
+ {{key}} |
+ {{/each}}
+ {{translate 'FORM.LABELS.TOTAL'}} |
+ {{translate 'REPORT.RUMER.STOCK_END'}} |
+
+
+
+
+ {{#each configurationData}}
+
+ {{ inventoryText }} |
+ {{ quantityOpening }} |
+ {{ quantityTotalEntry }} |
+ {{#each dailyConsumption }}
+
+ {{#if value}}
+ {{ value }}
+ {{/if}}
+ |
+ {{/each}}
+ {{ quantityTotalExit }} |
+ {{ quantityEnding }} |
+
+ {{/each}}
+
+
+
+
+
diff --git a/server/controllers/stock/reports/stock/rumer.js b/server/controllers/stock/reports/stock/rumer.js
new file mode 100644
index 0000000000..b60cbfd303
--- /dev/null
+++ b/server/controllers/stock/reports/stock/rumer.js
@@ -0,0 +1,176 @@
+const _ = require('lodash');
+const moment = require('moment');
+
+const db = require('../../../../lib/db');
+const core = require('../../core');
+const ReportManager = require('../../../../lib/ReportManager');
+
+const TEMPLATE = './server/controllers/stock/reports/rumer.report.handlebars';
+
+exports.report = report;
+
+// default report parameters
+const DEFAULT_PARAMS = {
+ csvKey : 'rumer',
+ filename : 'TREE.RUMER',
+ orientation : 'landscape',
+};
+
+/**
+ * @method report
+ *
+ * @description
+ * This method builds the RUMER (Drug Usage and Recipe Register) report by month JSON, PDF, or HTML
+ * file to be sent to the client.
+ *
+ * GET /reports/stock/rumer_report
+ */
+async function report(req, res, next) {
+ const params = req.query;
+
+ const data = {};
+ const headerReport = [];
+ const configurationData = [];
+
+ params.start_date = moment(new Date(params.start_date)).format('YYYY-MM-DD');
+ params.end_date = moment(new Date(params.end_date)).format('YYYY-MM-DD');
+
+ const startDate = parseInt(moment(params.start_date).format('DD'), 10);
+ const endDate = parseInt(moment(params.end_date).format('DD'), 10);
+
+ const lastDayPreviousMonth = moment(params.start_date).subtract(1, 'day').format('YYYY-MM-DD');
+
+ for (let i = startDate; i <= endDate; i++) {
+ headerReport.push(i);
+ }
+
+ data.params = params;
+ data.header = headerReport;
+
+ const parameterOpeningStock = {
+ depot_uuid : params.depotUuid,
+ dateTo : lastDayPreviousMonth,
+ includeEmptyLot : 1,
+ };
+
+ const parameterEndingStock = {
+ depot_uuid : params.depotUuid,
+ dateTo : params.end_date,
+ includeEmptyLot : 1,
+ };
+
+ _.defaults(params, DEFAULT_PARAMS);
+
+ try {
+ const reporting = new ReportManager(TEMPLATE, req.session, params);
+
+ const sqlDailyConsumption = `
+ SELECT BUID(inv.uuid) AS uuid, inv.code, inv.text AS inventory_text,
+ SUM(sm.quantity) AS quantity, DATE(sm.date) AS dateMovement
+ FROM stock_movement AS sm
+ JOIN lot AS l ON l.uuid = sm.lot_uuid
+ JOIN inventory AS inv ON inv.uuid = l.inventory_uuid
+ WHERE sm.depot_uuid = ? AND DATE(sm.date) >= DATE(?) AND DATE(sm.date) <= DATE(?)
+ AND sm.is_exit = 1
+ GROUP BY inv.uuid, dateMovement;
+ `;
+
+ const sqlMonthlyConsumption = `
+ SELECT BUID(inv.uuid) AS uuid, inv.code, inv.text AS inventory_text, SUM(sm.quantity) AS quantity, sm.date
+ FROM stock_movement AS sm
+ JOIN lot AS l ON l.uuid = sm.lot_uuid
+ JOIN inventory AS inv ON inv.uuid = l.inventory_uuid
+ WHERE sm.depot_uuid = ? AND DATE(sm.date) >= DATE(?) AND DATE(sm.date) <= DATE(?)
+ AND sm.is_exit = 1
+ GROUP BY inv.uuid;
+ `;
+
+ const sqlStockEntryMonth = `
+ SELECT BUID(inv.uuid) AS uuid, inv.code, inv.text AS inventory_text, SUM(sm.quantity) AS quantity,
+ sm.date, sm.is_exit
+ FROM stock_movement AS sm
+ JOIN lot AS l ON l.uuid = sm.lot_uuid
+ JOIN inventory AS inv ON inv.uuid = l.inventory_uuid
+ WHERE sm.depot_uuid = ? AND DATE(sm.date) >= DATE(?) AND DATE(sm.date) <= DATE(?)
+ AND sm.is_exit = 0
+ GROUP BY inv.uuid;
+ `;
+
+ const [inventoriesOpening, inventoriesConsumed,
+ inventoriesEntry, monthlyConsumption, inventoriesEnding] = await Promise.all([
+ core.getInventoryQuantityAndConsumption(parameterOpeningStock),
+ db.exec(sqlDailyConsumption, [db.bid(params.depotUuid), params.start_date, params.end_date]),
+ db.exec(sqlStockEntryMonth, [db.bid(params.depotUuid), params.start_date, params.end_date]),
+ db.exec(sqlMonthlyConsumption, [db.bid(params.depotUuid), params.start_date, params.end_date]),
+ core.getInventoryQuantityAndConsumption(parameterEndingStock),
+ ]);
+
+ inventoriesEnding.forEach(inventory => {
+ configurationData.push({
+ inventoryUuid : inventory.inventory_uuid,
+ inventoryText : inventory.text,
+ quantityOpening : 0,
+ quantityTotalEntry : 0,
+ quantityTotalExit : 0,
+ quantityEnding : inventory.quantity,
+ });
+ });
+
+ configurationData.forEach(inventory => {
+ const dailyConsumption = [];
+ for (let i = startDate; i <= endDate; i++) {
+ dailyConsumption.push({ value : 0, index : i });
+ }
+
+ inventoriesConsumed.forEach(consumed => {
+ if (inventory.inventoryUuid === consumed.uuid) {
+ const dateConsumption = parseInt(moment(consumed.dateMovement).format('DD'), 10);
+ dailyConsumption.forEach(d => {
+ if (d.index === dateConsumption) {
+ d.value = consumed.quantity;
+ }
+ });
+ }
+ });
+
+ inventory.dailyConsumption = dailyConsumption;
+ });
+
+ if (inventoriesOpening.length) {
+ configurationData.forEach(inventory => {
+ inventoriesOpening.forEach(opening => {
+ if (inventory.inventoryUuid === opening.inventory_uuid) {
+ inventory.quantityOpening = opening.quantity;
+ }
+ });
+ });
+ }
+
+ if (inventoriesEntry.length) {
+ configurationData.forEach(inventory => {
+ inventoriesEntry.forEach(entry => {
+ if (inventory.inventoryUuid === entry.uuid) {
+ inventory.quantityTotalEntry = entry.quantity;
+ }
+ });
+ });
+ }
+
+ if (monthlyConsumption.length) {
+ configurationData.forEach(inventory => {
+ monthlyConsumption.forEach(exit => {
+ if (inventory.inventoryUuid === exit.uuid) {
+ inventory.quantityTotalExit = exit.quantity;
+ }
+ });
+ });
+ }
+
+ data.configurationData = configurationData;
+
+ const result = await reporting.render(data);
+ res.set(result.headers).send(result.report);
+ } catch (e) {
+ next(e);
+ }
+}
diff --git a/server/models/bhima.sql b/server/models/bhima.sql
index 30ffd8decb..58cb225e19 100644
--- a/server/models/bhima.sql
+++ b/server/models/bhima.sql
@@ -166,8 +166,10 @@ INSERT INTO unit VALUES
(290, '[SETTINGS] Settings', 'TREE.STOCK_SETTINGS', 'Stock Settings', 160, '/stock/setting'),
(291, '[Stock] Dashboard', 'TREE.STOCK_DASHBOARD','Stock Dashboard', 160,'/stock/dashboard'),
(292, '[Stock] Changes Report', 'REPORT.STOCK_CHANGES.TITLE', 'Stock Changes Report', 282, '/reports/stock_changes'),
- (293, 'Aggregated consumption','TREE.AGGREGATED_STOCK_CONSUMPTION','Aggregated consumption',160,'/stock/aggregated_consumption'),
- (294, 'Duplicate Lots','TREE.DUPLICATE_LOTS','The stock lots duplicates list',160,'/stock/lots/duplicates');
+ (293, 'Aggregated consumption','TREE.AGGREGATED_STOCK_CONSUMPTION','Aggregated consumption', 160,'/stock/aggregated_consumption'),
+ (294, 'Duplicate Lots','TREE.DUPLICATE_LOTS','The stock lots duplicates list', 160,'/stock/lots/duplicates'),
+ (295, 'Rumer report','TREE.RUMER_REPORT','The rumer reports', 282,'/reports/rumer_report');
+
-- Reserved system account type
INSERT INTO `account_category` VALUES
@@ -237,7 +239,8 @@ INSERT INTO `report` (`report_key`, `title_key`) VALUES
('invoiceRegistryReport', 'Invoice Registry as report'),
('stock_movement_report', 'REPORT.STOCK_MOVEMENT_REPORT.TITLE'),
('stock_expiration_report', 'REPORT.STOCK_EXPIRATION_REPORT.TITLE'),
- ('stock_changes', 'REPORT.STOCK_CHANGES.TITLE');
+ ('stock_changes', 'REPORT.STOCK_CHANGES.TITLE'),
+ ('rumer_report', 'REPORT.RUMER.TITLE');
-- Supported Languages
INSERT INTO `language` VALUES
diff --git a/server/models/migrations/next/migrate.sql b/server/models/migrations/next/migrate.sql
index d0d3cbc913..64f34adc85 100644
--- a/server/models/migrations/next/migrate.sql
+++ b/server/models/migrations/next/migrate.sql
@@ -90,3 +90,14 @@ INSERT IGNORE INTO `exchange_rate` VALUES (3, 1, @EUR, 0.84, NOW());
@description: remove the origin_uuid column from the lot table.
*/
ALTER TABLE `lot` DROP COLUMN `origin_uuid`;
+
+/*
+ * @author: lomamech
+ * @date: 2021-04-26
+ * @subject : Implement the RUMER report
+ */
+INSERT INTO unit VALUES
+ (295, 'Rumer report','TREE.RUMER_REPORT','The rumer reports', 282,'/reports/rumer_report');
+
+INSERT INTO `report` (`report_key`, `title_key`) VALUES
+ ('rumer_report', 'REPORT.RUMER.TITLE');