From 9b7f65c3fc91a23eb98477d1a6ed4f6ebbcdc896 Mon Sep 17 00:00:00 2001 From: mbayopanda Date: Mon, 13 May 2019 14:19:16 +0100 Subject: [PATCH] stock entry report --- client/src/i18n/en/report.json | 8 +- client/src/i18n/en/tree.json | 1 + client/src/i18n/fr/report.json | 8 +- client/src/i18n/fr/stock.json | 2 +- client/src/i18n/fr/tree.json | 3 +- .../stock_entry/stock_entry.config.js | 97 ++++++++ .../generate/stock_entry/stock_entry.html | 92 ++++++++ client/src/modules/reports/reports.routes.js | 1 + server/config/routes.js | 1 + server/controllers/stock/reports/common.js | 2 + server/controllers/stock/reports/index.js | 2 + .../reports/stock/entry/entryFromDonation.js | 38 +++ .../stock/entry/entryFromIntegration.js | 38 +++ .../reports/stock/entry/entryFromPurchase.js | 43 ++++ .../reports/stock/entry/entryFromTransfer.js | 39 ++++ .../stock/reports/stock/entry_report.js | 147 ++++++++++++ .../reports/stock_entry.report.handlebars | 216 ++++++++++++++++++ server/models/bhima.sql | 6 +- .../migrations/v1.1.0-v1.1.1/migrate.sql | 13 +- 19 files changed, 748 insertions(+), 9 deletions(-) create mode 100644 client/src/modules/reports/generate/stock_entry/stock_entry.config.js create mode 100644 client/src/modules/reports/generate/stock_entry/stock_entry.html create mode 100644 server/controllers/stock/reports/stock/entry/entryFromDonation.js create mode 100644 server/controllers/stock/reports/stock/entry/entryFromIntegration.js create mode 100644 server/controllers/stock/reports/stock/entry/entryFromPurchase.js create mode 100644 server/controllers/stock/reports/stock/entry/entryFromTransfer.js create mode 100644 server/controllers/stock/reports/stock/entry_report.js create mode 100644 server/controllers/stock/reports/stock_entry.report.handlebars diff --git a/client/src/i18n/en/report.json b/client/src/i18n/en/report.json index a00e38b396..3712f7c9e2 100644 --- a/client/src/i18n/en/report.json +++ b/client/src/i18n/en/report.json @@ -177,7 +177,13 @@ "INCLUDE_SERVICE_EXIT": "Include stock exit to service", "INCLUDE_DEPOT_EXIT": "Include stock exit to other depots", "INCLUDE_LOSS_EXIT": "Include stock loss", - "SHOW_DETAILS": "Show Details" + "SHOW_DETAILS": "Show Details", + "ENTRY_REPORT": "Stock Entry Report", + "ENTRY_REPORT_DESCRIPTION": "this report displays the different stock entries that took place in depots", + "INCLUDE_PURCHASE_ENTRY": "Include entries from purchases", + "INCLUDE_INTEGRATION_ENTRY": "Include entries from integrations", + "INCLUDE_DONATION_ENTRY": "Include entries from donation", + "INCLUDE_TRANSFER_ENTRY": "Include entries from transfers" }, "UTIL": { "CANCEL_PREVIEW": "Cancel Preview", diff --git a/client/src/i18n/en/tree.json b/client/src/i18n/en/tree.json index 529729e698..693943b850 100644 --- a/client/src/i18n/en/tree.json +++ b/client/src/i18n/en/tree.json @@ -116,6 +116,7 @@ "STOCK_REPORT" : "[Stock] Inventories", "STOCK_INVENTORY_REPORT" : "[Stock] Inventory Report", "STOCK_EXIT_REPORT" : "[Stock] Exit Report", + "STOCK_ENTRY_REPORT":"[Stock] Entries Report", "STOCK_LOTS" : "Stock Lots", "STOCK_VALUE" : "[Stock] Stock value Report", "STOCK_MOVEMENTS" : "Stock Movements", diff --git a/client/src/i18n/fr/report.json b/client/src/i18n/fr/report.json index 3923af732c..246d5b2db6 100644 --- a/client/src/i18n/fr/report.json +++ b/client/src/i18n/fr/report.json @@ -177,7 +177,13 @@ "INCLUDE_SERVICE_EXIT" : "Inclure les distributions vers les services", "INCLUDE_DEPOT_EXIT" : "Inclure les distributions vers d'autres dépôts", "INCLUDE_LOSS_EXIT" : "Inclure les pertes de stocks", - "SHOW_DETAILS" : "Afficher les details" + "SHOW_DETAILS" : "Afficher les details", + "ENTRY_REPORT": "Rapports des entrées de stocks", + "ENTRY_REPORT_DESCRIPTION": "Ce rapport affiche les différentes entrées de stock qui ont eu lieu dans les dépots", + "INCLUDE_PURCHASE_ENTRY": "Inclure les entrées en provenance des achats", + "INCLUDE_INTEGRATION_ENTRY": "Inclure les entrées en provenance des intégrations", + "INCLUDE_DONATION_ENTRY": "Inclure les entrées en provenance des donations", + "INCLUDE_TRANSFER_ENTRY": "Inclure les entrées en provenance des transferts" }, "UTIL":{ "CANCEL_PREVIEW" : "Fermer l'Aperçu", diff --git a/client/src/i18n/fr/stock.json b/client/src/i18n/fr/stock.json index 71aa8b1029..d2515464a2 100644 --- a/client/src/i18n/fr/stock.json +++ b/client/src/i18n/fr/stock.json @@ -109,7 +109,7 @@ "TRANSFER_AVAILABLE" : "Disponible", "RECEIPT" : { "ADJUSTMENT" : "Ajustement", - "ENTRY_DEPOT" : "Entrée de Stock", + "ENTRY_DEPOT" : "Entrée en provenance d'autres dépots", "ENTRY_DONATION" : "Entree de Stock - Donation", "ENTRY_PURCHASE" : "Entree de Stock - Achat", "ENTRY_INTEGRATION" : "Entree de Stock - Intégration", diff --git a/client/src/i18n/fr/tree.json b/client/src/i18n/fr/tree.json index 1f1eb9a7ec..80da5d88bc 100644 --- a/client/src/i18n/fr/tree.json +++ b/client/src/i18n/fr/tree.json @@ -115,7 +115,8 @@ "STOCK_INVENTORY":"Articles en stock", "STOCK_REPORT": "[Stock] Articles en stock", "STOCK_INVENTORY_REPORT":"[Stock] Fiche de stock", - "STOCK_EXIT_REPORT":"[Stock] Sortie des stocks", + "STOCK_EXIT_REPORT":"[Stock] Sorties des stocks", + "STOCK_ENTRY_REPORT":"[Stock] Entrées des stocks", "STOCK_LOTS":"Lots en stock", "STOCK_VALUE" : "[Stock] Valeur de stock", "STOCK_MOVEMENTS":"Movements de stock", diff --git a/client/src/modules/reports/generate/stock_entry/stock_entry.config.js b/client/src/modules/reports/generate/stock_entry/stock_entry.config.js new file mode 100644 index 0000000000..fa7d6a4ed4 --- /dev/null +++ b/client/src/modules/reports/generate/stock_entry/stock_entry.config.js @@ -0,0 +1,97 @@ +angular.module('bhima.controllers') + .controller('stock_entryController', StockEntryConfigController); + +StockEntryConfigController.$inject = [ + '$sce', 'NotifyService', 'BaseReportService', 'AppCache', 'reportData', '$state', + 'LanguageService', +]; + +function StockEntryConfigController($sce, Notify, SavedReports, AppCache, reportData, $state, Languages) { + const vm = this; + const cache = new AppCache('configure_stock_entry_report'); + const reportUrl = 'reports/stock/entry'; + + // default values + vm.includePurchaseEntry = 1; + vm.includeIntegrationEntry = 0; + vm.includeDonationEntry = 0; + vm.includeTransferEntry = 0; + vm.previewGenerated = false; + vm.onEntryTypeChange = onEntryTypeChange; + + // check cached configuration + checkCachedConfiguration(); + + // check checked entry type + onEntryTypeChange(); + + vm.onSelectDepot = depot => { + vm.depot = depot; + }; + + vm.clear = key => { + delete vm[key]; + }; + + vm.clearPreview = () => { + vm.previewGenerated = false; + vm.previewResult = null; + }; + + vm.preview = form => { + if (form.$invalid) { + return 0; + } + + if (!vm.hasOneChecked) { + return 0; + } + + const params = { + depotUuid : vm.depot.uuid, + dateFrom : vm.dateFrom, + dateTo : vm.dateTo, + includePurchaseEntry : vm.includePurchaseEntry, + includeIntegrationEntry : vm.includeIntegrationEntry, + includeDonationEntry : vm.includeDonationEntry, + includeTransferEntry : vm.includeTransferEntry, + showDetails : vm.showDetails, + }; + + // update cached configuration + cache.reportDetails = angular.copy(params); + angular.extend(vm.reportDetails, params, { 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 || {}); + } + + function onEntryTypeChange() { + // be sure at least one checkbox is checked + const sum = vm.includePurchaseEntry + + vm.includeIntegrationEntry + vm.includeDonationEntry + vm.includeTransferEntry; + vm.hasOneChecked = sum > 0; + } +} diff --git a/client/src/modules/reports/generate/stock_entry/stock_entry.html b/client/src/modules/reports/generate/stock_entry/stock_entry.html new file mode 100644 index 0000000000..a143dd2bfd --- /dev/null +++ b/client/src/modules/reports/generate/stock_entry/stock_entry.html @@ -0,0 +1,92 @@ + + + +
+
+
+

REPORT.STOCK.ENTRY_REPORT

+

REPORT.STOCK.ENTRY_REPORT_DESCRIPTION

+
+
+ +
+
+
+
+ REPORT.UTIL.OPTIONS +
+ +
+ +
+ + + + + + + + + + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ STOCK.AT_LEAST_ONE_CHECKED +
+ +
+ +
+ +
+ + + + REPORT.UTIL.PREVIEW + +
+
+
+
+
+
diff --git a/client/src/modules/reports/reports.routes.js b/client/src/modules/reports/reports.routes.js index f9f2661b86..6d95f7c7b1 100644 --- a/client/src/modules/reports/reports.routes.js +++ b/client/src/modules/reports/reports.routes.js @@ -27,6 +27,7 @@ angular.module('bhima.routes') 'operating', 'patientStanding', 'stock_exit', + 'stock_entry', 'stock_value', 'unpaid-invoice-payments', 'breakEven', diff --git a/server/config/routes.js b/server/config/routes.js index a073c396b2..c8f6ea0d86 100644 --- a/server/config/routes.js +++ b/server/config/routes.js @@ -757,6 +757,7 @@ exports.configure = function configure(app) { // stock reports API app.get('/reports/stock/exit', stockReports.stockExitReport); + app.get('/reports/stock/entry', stockReports.stockEntryReport); app.get('/reports/stock/lots', stockReports.stockLotsReport); app.get('/reports/stock/movements', stockReports.stockMovementsReport); app.get('/reports/stock/inventories', stockReports.stockInventoriesReport); diff --git a/server/controllers/stock/reports/common.js b/server/controllers/stock/reports/common.js index 08a2db2262..2540c922b2 100644 --- a/server/controllers/stock/reports/common.js +++ b/server/controllers/stock/reports/common.js @@ -16,6 +16,7 @@ const STOCK_ADJUSTMENT_TEMPLATE = `${BASE_PATH}/stock_adjustment.receipt.handleb // reports const STOCK_EXIT_REPORT_TEMPLATE = `${BASE_PATH}/stock_exit.report.handlebars`; +const STOCK_ENTRY_REPORT_TEMPLATE = `${BASE_PATH}/stock_entry.report.handlebars`; const STOCK_LOTS_REPORT_TEMPLATE = `${BASE_PATH}/stock_lots.report.handlebars`; const STOCK_MOVEMENTS_REPORT_TEMPLATE = `${BASE_PATH}/stock_movements.report.handlebars`; const STOCK_INVENTORIES_REPORT_TEMPLATE = `${BASE_PATH}/stock_inventories.report.handlebars`; @@ -198,6 +199,7 @@ exports.STOCK_ENTRY_DONATION_TEMPLATE = STOCK_ENTRY_DONATION_TEMPLATE; exports.STOCK_ADJUSTMENT_TEMPLATE = STOCK_ADJUSTMENT_TEMPLATE; exports.STOCK_EXIT_REPORT_TEMPLATE = STOCK_EXIT_REPORT_TEMPLATE; +exports.STOCK_ENTRY_REPORT_TEMPLATE = STOCK_ENTRY_REPORT_TEMPLATE; exports.STOCK_LOTS_REPORT_TEMPLATE = STOCK_LOTS_REPORT_TEMPLATE; exports.STOCK_MOVEMENTS_REPORT_TEMPLATE = STOCK_MOVEMENTS_REPORT_TEMPLATE; exports.STOCK_INVENTORY_REPORT_TEMPLATE = STOCK_INVENTORY_REPORT_TEMPLATE; diff --git a/server/controllers/stock/reports/index.js b/server/controllers/stock/reports/index.js index 9522a084c8..60a7b7002a 100644 --- a/server/controllers/stock/reports/index.js +++ b/server/controllers/stock/reports/index.js @@ -8,6 +8,7 @@ */ const stockExitReport = require('./stock/exit_report'); +const stockEntryReport = require('./stock/entry_report'); const stockLotsReport = require('./stock/lots_report'); const stockMovementsReport = require('./stock/movements_report'); const stockInventoriesReport = require('./stock/inventories_report'); @@ -26,6 +27,7 @@ const stockAssignReceipt = require('./stock/assign_receipt'); // expose to the api exports.stockExitReport = stockExitReport; +exports.stockEntryReport = stockEntryReport; exports.stockLotsReport = stockLotsReport; exports.stockMovementsReport = stockMovementsReport; exports.stockInventoriesReport = stockInventoriesReport; diff --git a/server/controllers/stock/reports/stock/entry/entryFromDonation.js b/server/controllers/stock/reports/stock/entry/entryFromDonation.js new file mode 100644 index 0000000000..bd67eb4f58 --- /dev/null +++ b/server/controllers/stock/reports/stock/entry/entryFromDonation.js @@ -0,0 +1,38 @@ +const db = require('../../../../../lib/db'); + +const IS_EXIT = 0; +const ENTRY_FROM_DONATION_ID = 6; + +/** + * @function fetch + * @description fetch stock entry from donation + */ +function fetch(depotUuid, dateFrom, dateTo, showDetails) { + const sql = ` + SELECT + i.code, i.text, iu.text AS unit_text, BUID(m.document_uuid) AS document_uuid, + SUM(m.quantity) as quantity, m.date, m.description, + u.display_name AS user_display_name, + dm.text AS document_reference, d.text AS depot_name + FROM stock_movement m + JOIN lot l ON l.uuid = m.lot_uuid + JOIN inventory i ON i.uuid = l.inventory_uuid + JOIN inventory_unit iu ON iu.id = i.unit_id + JOIN depot d ON d.uuid = m.depot_uuid + JOIN user u ON u.id = m.user_id + LEFT JOIN document_map dm ON dm.uuid = m.document_uuid + WHERE m.is_exit = ${IS_EXIT} AND m.flux_id = ${ENTRY_FROM_DONATION_ID} AND d.uuid = ? + AND (DATE(m.date) BETWEEN DATE(?) AND DATE(?)) + GROUP BY i.uuid`; + + const groupBy = ', m.uuid'; + const orderBy = ' ORDER BY i.text, m.date ASC'; + const query = showDetails ? sql.concat(groupBy, orderBy) : sql.concat(orderBy); + + const _depotUuid = db.bid(depotUuid); + const _dateFrom = new Date(dateFrom); + const _dateTo = new Date(dateTo); + return db.exec(query, [_depotUuid, _dateFrom, _dateTo]); +} + +module.exports.fetch = fetch; diff --git a/server/controllers/stock/reports/stock/entry/entryFromIntegration.js b/server/controllers/stock/reports/stock/entry/entryFromIntegration.js new file mode 100644 index 0000000000..af49b86786 --- /dev/null +++ b/server/controllers/stock/reports/stock/entry/entryFromIntegration.js @@ -0,0 +1,38 @@ +const db = require('../../../../../lib/db'); + +const IS_EXIT = 0; +const ENTRY_FROM_INTEGRATION_ID = 13; + +/** + * @function fetch + * @description fetch stock entry from integration + */ +function fetch(depotUuid, dateFrom, dateTo, showDetails) { + const sql = ` + SELECT + i.code, i.text, iu.text AS unit_text, BUID(m.document_uuid) AS document_uuid, + SUM(m.quantity) as quantity, m.date, m.description, + u.display_name AS user_display_name, + dm.text AS document_reference, d.text AS depot_name + FROM stock_movement m + JOIN lot l ON l.uuid = m.lot_uuid + JOIN inventory i ON i.uuid = l.inventory_uuid + JOIN inventory_unit iu ON iu.id = i.unit_id + JOIN depot d ON d.uuid = m.depot_uuid + JOIN user u ON u.id = m.user_id + LEFT JOIN document_map dm ON dm.uuid = m.document_uuid + WHERE m.is_exit = ${IS_EXIT} AND m.flux_id = ${ENTRY_FROM_INTEGRATION_ID} AND d.uuid = ? + AND (DATE(m.date) BETWEEN DATE(?) AND DATE(?)) + GROUP BY i.uuid`; + + const groupBy = ', m.uuid'; + const orderBy = ' ORDER BY i.text, m.date ASC'; + const query = showDetails ? sql.concat(groupBy, orderBy) : sql.concat(orderBy); + + const _depotUuid = db.bid(depotUuid); + const _dateFrom = new Date(dateFrom); + const _dateTo = new Date(dateTo); + return db.exec(query, [_depotUuid, _dateFrom, _dateTo]); +} + +module.exports.fetch = fetch; diff --git a/server/controllers/stock/reports/stock/entry/entryFromPurchase.js b/server/controllers/stock/reports/stock/entry/entryFromPurchase.js new file mode 100644 index 0000000000..cba0eac294 --- /dev/null +++ b/server/controllers/stock/reports/stock/entry/entryFromPurchase.js @@ -0,0 +1,43 @@ +const db = require('../../../../../lib/db'); +const identifiers = require('../../../../../config/identifiers'); + +const IS_EXIT = 0; +const ENTRY_FROM_PURCHASE_ID = 1; + +/** + * @function fetch + * @description fetch stock entry from purchase + */ +function fetch(depotUuid, dateFrom, dateTo, showDetails) { + const sql = ` + SELECT + i.code, i.text, iu.text AS unit_text, BUID(m.document_uuid) AS document_uuid, + SUM(m.quantity) as quantity, m.date, m.description, + u.display_name AS user_display_name, sup.display_name AS supplier_display_name, + dm.text AS document_reference, d.text AS depot_name, + CONCAT_WS('.', '${identifiers.PURCHASE_ORDER.key}', proj.abbr, p.reference) AS purchase_reference + FROM stock_movement m + JOIN lot l ON l.uuid = m.lot_uuid + JOIN inventory i ON i.uuid = l.inventory_uuid + JOIN inventory_unit iu ON iu.id = i.unit_id + JOIN depot d ON d.uuid = m.depot_uuid + JOIN purchase p ON p.uuid = l.origin_uuid + JOIN supplier sup ON sup.uuid = p.supplier_uuid + JOIN project proj ON proj.id = p.project_id + JOIN user u ON u.id = m.user_id + LEFT JOIN document_map dm ON dm.uuid = m.document_uuid + WHERE m.is_exit = ${IS_EXIT} AND m.flux_id = ${ENTRY_FROM_PURCHASE_ID} AND d.uuid = ? + AND (DATE(m.date) BETWEEN DATE(?) AND DATE(?)) + GROUP BY i.uuid`; + + const groupBy = ', m.uuid, p.uuid'; + const orderBy = ' ORDER BY i.text, m.date ASC'; + const query = showDetails ? sql.concat(groupBy, orderBy) : sql.concat(orderBy); + + const _depotUuid = db.bid(depotUuid); + const _dateFrom = new Date(dateFrom); + const _dateTo = new Date(dateTo); + return db.exec(query, [_depotUuid, _dateFrom, _dateTo]); +} + +module.exports.fetch = fetch; diff --git a/server/controllers/stock/reports/stock/entry/entryFromTransfer.js b/server/controllers/stock/reports/stock/entry/entryFromTransfer.js new file mode 100644 index 0000000000..420e8c5ba7 --- /dev/null +++ b/server/controllers/stock/reports/stock/entry/entryFromTransfer.js @@ -0,0 +1,39 @@ +const db = require('../../../../../lib/db'); + +const IS_EXIT = 0; +const ENTRY_FROM_DEPOT_ID = 2; + +/** + * @function fetch + * @description fetch stock entry from donation + */ +function fetch(depotUuid, dateFrom, dateTo, showDetails) { + const sql = ` + SELECT + i.code, i.text, iu.text AS unit_text, BUID(m.document_uuid) AS document_uuid, + SUM(m.quantity) as quantity, m.date, m.description, + u.display_name AS user_display_name, + dm.text AS document_reference, dd.text AS depot_name + FROM stock_movement m + JOIN lot l ON l.uuid = m.lot_uuid + JOIN inventory i ON i.uuid = l.inventory_uuid + JOIN inventory_unit iu ON iu.id = i.unit_id + JOIN depot d ON d.uuid = m.depot_uuid + JOIN depot dd ON dd.uuid = m.entity_uuid + JOIN user u ON u.id = m.user_id + LEFT JOIN document_map dm ON dm.uuid = m.document_uuid + WHERE m.is_exit = ${IS_EXIT} AND m.flux_id = ${ENTRY_FROM_DEPOT_ID} AND d.uuid = ? + AND (DATE(m.date) BETWEEN DATE(?) AND DATE(?)) + GROUP BY i.uuid`; + + const groupBy = ', m.uuid, dd.uuid'; + const orderBy = ' ORDER BY i.text, m.date ASC'; + const query = showDetails ? sql.concat(groupBy, orderBy) : sql.concat(orderBy); + + const _depotUuid = db.bid(depotUuid); + const _dateFrom = new Date(dateFrom); + const _dateTo = new Date(dateTo); + return db.exec(query, [_depotUuid, _dateFrom, _dateTo]); +} + +module.exports.fetch = fetch; diff --git a/server/controllers/stock/reports/stock/entry_report.js b/server/controllers/stock/reports/stock/entry_report.js new file mode 100644 index 0000000000..50da07e77e --- /dev/null +++ b/server/controllers/stock/reports/stock/entry_report.js @@ -0,0 +1,147 @@ +const { + _, db, util, ReportManager, pdfOptions, STOCK_ENTRY_REPORT_TEMPLATE, +} = require('../common'); + +const StockEntryFromPurchase = require('./entry/entryFromPurchase'); +const StockEntryFromIntegration = require('./entry/entryFromIntegration'); +const StockEntryFromDonation = require('./entry/entryFromDonation'); +const StockEntryFromTransfer = require('./entry/entryFromTransfer'); + +/** + * @method stockEntryReport + * + * @description + * This method builds the stock entry report as either a JSON, PDF, or HTML + * file to be sent to the client. + * + * GET /reports/stock/entry + */ +function stockEntryReport(req, res, next) { + let report; + + const params = util.convertStringToNumber(req.query); + + const optionReport = _.extend(params, pdfOptions, { + filename : 'REPORT.STOCK.ENTRY_REPORT', + }); + + // set up the report with report manager + try { + report = new ReportManager(STOCK_ENTRY_REPORT_TEMPLATE, req.session, optionReport); + } catch (e) { + return next(e); + } + + return fetchDepotDetails(params.depotUuid) + .then(depot => { + params.depotName = depot.text; + return collect(params); + }) + .then(groupCollection) + .then((bundle) => { + _.extend(bundle, params); + + return report.render(bundle); + }) + .then((result) => { + res.set(result.headers).send(result.report); + }) + .catch(next) + .done(); +} + +/** + * fetchDepotDetails + * @param {number} depotUuid depot uuid + */ +function fetchDepotDetails(depotUuid) { + const query = 'SELECT text FROM depot WHERE uuid = ?'; + return db.one(query, [db.bid(depotUuid)]); +} + +/** + * @function groupCollection + * @description group collected data by inventory + */ +function groupCollection(entryCollection) { + const collection = {}; + + // entry to purchase + collection.entryFromPurchase = formatAndCombine(entryCollection.entryFromPurchase); + + // entry to integration + collection.entryFromIntegration = formatAndCombine(entryCollection.entryFromIntegration); + + // entry to donation + collection.entryFromDonation = formatAndCombine(entryCollection.entryFromDonation); + + // entry to transfer + collection.entryFromTransfer = formatAndCombine(entryCollection.entryFromTransfer); + + return collection; +} + +function formatAndCombine(data) { + const aggregate = _.chain(data) + .groupBy('text') + .map(formatEntry) + .value(); + + return { data : aggregate, isEmpty : _.size(aggregate) === 0 }; +} + +/** + * @function formatEntry + */ +function formatEntry(value, key) { + return { + inventory_name : key, + inventory_unit : value && value[0] ? value[0].unit_text : '', + inventory_stock_entry_data : value, + inventory_stock_entry_quantity : _.sumBy(value, 'quantity'), + }; +} + +/** + * @function collect + * @param {object} params query parameters + * @return {promise} { entryFromPurchase: [], entryFromIntegration: [], entryFromDonation: [], entryFromTransfer: [] } + */ +async function collect(params) { + const { + depotUuid, + dateFrom, + dateTo, + showDetails, + includePurchaseEntry, + includeIntegrationEntry, + includeDonationEntry, + includeTransferEntry, + } = params; + + const data = {}; + + // get stock entry from purchase + if (includePurchaseEntry) { + data.entryFromPurchase = await StockEntryFromPurchase.fetch(depotUuid, dateFrom, dateTo, showDetails); + } + + // get stock entry from integration + if (includeIntegrationEntry) { + data.entryFromIntegration = await StockEntryFromIntegration.fetch(depotUuid, dateFrom, dateTo, showDetails); + } + + // get stock entry from donation + if (includeDonationEntry) { + data.entryFromDonation = await StockEntryFromDonation.fetch(depotUuid, dateFrom, dateTo, showDetails); + } + + // get stock entry from transfer + if (includeTransferEntry) { + data.entryFromTransfer = await StockEntryFromTransfer.fetch(depotUuid, dateFrom, dateTo, showDetails); + } + + return data; +} + +module.exports = stockEntryReport; diff --git a/server/controllers/stock/reports/stock_entry.report.handlebars b/server/controllers/stock/reports/stock_entry.report.handlebars new file mode 100644 index 0000000000..762a2d9f4f --- /dev/null +++ b/server/controllers/stock/reports/stock_entry.report.handlebars @@ -0,0 +1,216 @@ +{{> head title="REPORT.STOCK.ENTRY_REPORT" }} + + + +
+ {{> header}} + + +
+
+ + +

+ {{translate 'REPORT.STOCK.ENTRY_REPORT'}} +

+ +

+ {{ depotName }} +

+ +

+ {{date dateFrom}} - {{date dateTo}} +

+ +
+ + + {{#if includePurchaseEntry}} +

{{translate 'STOCK.RECEIPT.ENTRY_PURCHASE'}}

+ + + {{#each entryFromPurchase.data as | data |}} + + + + + + + + {{#if ../showDetails}} + + {{#each data.inventory_stock_entry_data as | item |}} + + + + + + + + {{/each}} + + {{/if}} + + {{/each}} + + + {{#if entryFromPurchase.isEmpty}} + + {{/if}} +
{{ data.inventory_name }}{{ data.inventory_stock_entry_quantity }} ({{ data.inventory_unit }})
{{ document_reference }}({{ purchase_reference }}) {{ supplier_display_name }} {{date date }}{{ quantity }} ({{ unit_text }})
{{translate 'STOCK.NO_DATA'}}
+ {{/if}} + +
+ + + {{#if includeIntegrationEntry}} +

{{translate 'STOCK.RECEIPT.ENTRY_INTEGRATION'}}

+ + + {{#each entryFromIntegration.data as | data |}} + + + + + + + + {{#if ../showDetails}} + + {{#each data.inventory_stock_entry_data as | item |}} + + + + + + + + {{/each}} + + {{/if}} + + {{/each}} + + + {{#if entryFromIntegration.isEmpty}} + + {{/if}} +
{{ data.inventory_name }}{{ data.inventory_stock_entry_quantity }} ({{ data.inventory_unit }})
{{ document_reference }}{{ description }} {{date date }}{{ quantity }} ({{ unit_text }})
{{translate 'STOCK.NO_DATA'}}
+ {{/if}} + +
+ + + {{#if includeDonationEntry}} +

{{translate 'STOCK.RECEIPT.ENTRY_DONATION'}}

+ + + {{#each entryFromDonation.data as | data |}} + + + + + + + + {{#if ../showDetails}} + + {{#each data.inventory_stock_entry_data as | item |}} + + + + + + + + {{/each}} + + {{/if}} + + {{/each}} + + + {{#if entryFromDonation.isEmpty}} + + {{/if}} +
{{ data.inventory_name }}{{ data.inventory_stock_entry_quantity }} ({{ data.inventory_unit }})
{{ document_reference }}{{ description }} {{date date }}{{ quantity }} ({{ unit_text }})
{{translate 'STOCK.NO_DATA'}}
+ {{/if}} + + + {{#if includeTransferEntry}} +

{{translate 'STOCK.RECEIPT.ENTRY_DEPOT'}}

+ + + {{#each entryFromTransfer.data as | data |}} + + + + + + + + {{#if ../showDetails}} + + {{#each data.inventory_stock_entry_data as | item |}} + + + + + + + + {{/each}} + + {{/if}} + + {{/each}} + + + {{#if entryFromTransfer.isEmpty}} + + {{/if}} +
{{ data.inventory_name }}{{ data.inventory_stock_entry_quantity }} ({{ data.inventory_unit }})
{{ document_reference }}{{ depot_name }} {{date date }}{{ quantity }} ({{ unit_text }})
{{translate 'STOCK.NO_DATA'}}
+ {{/if}} + +
+ + + {{#if includeLossEntry}} +

{{translate 'STOCK.RECEIPT.ENTRY_LOSS'}}

+ + + {{#each entryToLoss.data as | data |}} + + + + + + + + {{#if ../showDetails}} + + {{#each data.inventory_stock_entry_data as | item |}} + + + + + + + + {{/each}} + + {{/if}} + + {{/each}} + + + {{#if entryToLoss.isEmpty}} + + {{/if}} +
{{ data.inventory_name }}{{ data.inventory_stock_entry_quantity }} ({{ data.inventory_unit }})
{{ document_reference }}{{ description }} {{date date }}{{ quantity }} ({{ unit_text }})
{{translate 'STOCK.NO_DATA'}}
+ {{/if}} +
+
+ +
+ diff --git a/server/models/bhima.sql b/server/models/bhima.sql index f8031ccfd1..f900cbb7d6 100644 --- a/server/models/bhima.sql +++ b/server/models/bhima.sql @@ -124,7 +124,8 @@ INSERT INTO unit VALUES (236, 'Human Resources dashboard', 'TREE.DASHBOARDS.HUMAN_RESOURCES', 'Tableau de bord du Personnel', 233, '/modules/dashboards/staff/', '/dashboards/staff'), (237, 'Finances dashboard', 'TREE.DASHBOARDS.FINANCES', 'Tableau de bord des finances', 233, '/modules/dashboards/finances/', '/dashboards/finances'), (238, 'Indicators report', 'TREE.INDICATORS_REPORT', 'Rapport sur les indicateurs', 144,'/modules/reports/indicatorsReport', '/reports/indicatorsReport'), - (239, 'Visits Report', 'TREE.VISITS_REPORT', 'Visits registry', 144, '/modules/reports/visit_report', '/reports/visit_report'); + (239, 'Visits Report', 'TREE.VISITS_REPORT', 'Visits registry', 144, '/modules/reports/visit_report', '/reports/visit_report'), + (240, '[Stock] Stock Entry Report','TREE.STOCK_ENTRY_REPORT','Stock Entry Report', 144,'/modules/reports/generated/stock_entry','/reports/stock_entry'); -- Reserved system account type INSERT INTO `account_category` VALUES @@ -174,7 +175,8 @@ INSERT INTO `report` (`id`, `report_key`, `title_key`) VALUES (29, 'breakEven', 'TREE.BREAK_EVEN_REPORT'), (30, 'breakEvenFeeCenter', 'TREE.BREAK_EVEN_FEE_CENTER_REPORT'), (31, 'indicatorsReport', 'TREE.INDICATORS_REPORT'), - (32, 'visit_report', 'PATIENT_RECORDS.REPORT.VISITS'); + (32, 'visit_report', 'PATIENT_RECORDS.REPORT.VISITS'), + (33, 'stock_entry', 'REPORT.STOCK.ENTRY_REPORT'); -- Supported Languages INSERT INTO `language` VALUES diff --git a/server/models/migrations/v1.1.0-v1.1.1/migrate.sql b/server/models/migrations/v1.1.0-v1.1.1/migrate.sql index 7b2c637fab..a1ff80202f 100644 --- a/server/models/migrations/v1.1.0-v1.1.1/migrate.sql +++ b/server/models/migrations/v1.1.0-v1.1.1/migrate.sql @@ -134,9 +134,6 @@ BEGIN END $$ DELIMITER ; -DELETE FROM role_unit WHERE unit_id = 171; -DELETE FROM unit WHERE id=171; - /* @author: mbayopanda @description: patient visits report @@ -146,3 +143,13 @@ INSERT INTO unit VALUES INSERT INTO `report` (`id`, `report_key`, `title_key`) VALUES (32, 'visit_report', 'PATIENT_RECORDS.REPORT.VISITS'); +/* + @author: mbayopanda + @date: 2019-05-13 + @description: stock entries report +*/ +INSERT INTO unit VALUES + (240, '[Stock] Stock Entry Report','TREE.STOCK_ENTRY_REPORT','Stock Entry Report', 144,'/modules/reports/generated/stock_entry','/reports/stock_entry'); + +INSERT INTO `report` (`id`, `report_key`, `title_key`) VALUES + (33, 'stock_entry', 'REPORT.STOCK.ENTRY_REPORT');