Skip to content

Commit

Permalink
feat(stock): add option to view AMC calcs
Browse files Browse the repository at this point in the history
Adds the ability to view the AMC/CMM calculations in the Articles in
Stock Registry.  A summary of the information is presented for the user
to examine and the explicit formula used to calculate the AMC is shown
for the user to validate.

Closes #5291.
  • Loading branch information
jniles committed Jan 29, 2021
1 parent 79b08d8 commit 9d943a5
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 3 deletions.
1 change: 1 addition & 0 deletions client/src/i18n/en/inventory.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"INVENTORY": {
"ADD_GROUP": "Add Inventory Group",
"OPEN_AMC_CALCULATION" : "View AMC Calculations",
"ADD_METADATA": "Add Inventory Item",
"ADD_TYPE": "Add Inventory Type",
"ADD_UNIT": "Add Inventory unit form",
Expand Down
15 changes: 14 additions & 1 deletion client/src/i18n/en/stock.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,27 @@
"MONTH_OF_STOCK" : "Stock Months - Months until Stock Out",
"MONTHLY_CONSUM" : "Average Monthly Consumption",
"MONTHLY_CONSUMPTION" : {
"AVERAGE_MONTHLY_CONSUMPTION" : "Average Monthly Consumption",
"ALGO_1" : "Algorithm 1",
"ALGO_1_COMMENT" : "The average monthly consumption is obtained by dividing the quantity consumed during the period by the number of days with stock for the period, and by multiplying the result by 30.5.",
"ALGO_2" : "Algorithm 2",
"ALGO_2_COMMENT" : "The average consumption is obtained by dividing the quantity consumed during the period by the number of days of consumption for the period, and by multiplying the result by 30.5.",
"ALGO_3" : "Algorithm 3",
"ALGO_3_COMMENT" : "The average consumption is obtained by dividing the quantity consumed during the period by the number of days in the period, and by multiplying the result obtained by 30.5.",
"ALGO_4" : "Algorithm 4 (MSH)",
"ALGO_4_COMMENT" : "The average consumption is obtained by dividing the quantity consumed during the period by the difference of the number of months in the period minus the total number of days of stock out in the period. The MSH algorithm is recommended by the Management Sciences for Health organization (https://www.msh.org)."
"ALGO_4_COMMENT" : "The average consumption is obtained by dividing the quantity consumed during the period by the difference of the number of months in the period minus the total number of days of stock out in the period. The MSH algorithm is recommended by the Management Sciences for Health organization (https://www.msh.org).",
"FIRST_INVENTORY_MOVEMENT_DATE" : "First Recorded Movement",
"LAST_INVENTORY_MOVEMENT_DATE" : "Last Recorded Movement",
"DAYS_BEFORE_CONSUMPTION" : "Days before First Consumption",
"NUMBER_OF_MONTHS" : "Number of Months",
"SUM_CONSUMPTION_DAY" : "Stock Consumption",
"SUM_CONSUMED_QUANTITY" :"Quantity Consumed",
"SUM_DAYS" : "Total Days",
"SUM_STOCK_DAY" : "Stock Available in Depot",
"SUM_STOCK_OUT_DAYS" : "Stock Outs",
"TITLE" : "Average Monthly Consumption",
"DAYS" : "Days",
"MONTHS" : "Months"
},
"MOTIF" : "Motive",
"MOVEMENT" : "Movement",
Expand Down
1 change: 1 addition & 0 deletions client/src/i18n/fr/inventory.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"INVENTORY": {
"ADD_GROUP": "Ajouter un groupe d'inventaire",
"OPEN_AMC_CALCULATION" : "Voire CMM calcules",
"ADD_METADATA": "Ajouter un inventaire",
"ADD_TYPE": "Ajouter un type d'inventaire",
"ADD_UNIT": "Ajouter une forme d'Inventaire",
Expand Down
5 changes: 5 additions & 0 deletions client/src/js/services/StockService.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ function StockService(Api, StockFilterer, HttpCache, util, Periods) {
});
}

inventories.loadAMCForInventory = function loadAMCForInventory(inventoryUuid, depotUuid) {
return inventories.$http.get(`/depots/${depotUuid}/inventories/${inventoryUuid}/cmm`)
.then(util.unwrapHttpResponse);
};

return {
stocks,
stockAssign,
Expand Down
115 changes: 115 additions & 0 deletions client/src/modules/stock/inventories/modals/amc.modal.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<div class="modal-header">
<ol class="headercrumb">
<li class="static">{{ModalCtrl.depot.text}}</li>
<li class="static">{{ModalCtrl.inventory.groupName}}</li>
<li class="title">{{ModalCtrl.inventory.code}} - {{ModalCtrl.inventory.label}}</li>
</ol>
</div>

<div class="modal-body">

<p class="alert alert-info">
<i class="fa fa-info-circle"></i>
<span ng-show="ModalCtrl.isAlgo1">
<strong translate>STOCK.MONTHLY_CONSUMPTION.ALGO_1</strong>
<br />
<span translate>STOCK.MONTHLY_CONSUMPTION.ALGO_1_COMMENT</span>
</span>

<span ng-show="ModalCtrl.isAlgo2">
<strong translate>STOCK.MONTHLY_CONSUMPTION.ALGO_2</strong>
<br />
<span translate>STOCK.MONTHLY_CONSUMPTION.ALGO_2_COMMENT</span>
</span>

<span ng-show="ModalCtrl.isAlgo3">
<strong translate>STOCK.MONTHLY_CONSUMPTION.ALGO_3</strong>
<br />
<span translate>STOCK.MONTHLY_CONSUMPTION.ALGO_3_COMMENT</span>
</span>

<span ng-show="ModalCtrl.isAlgo4">
<strong translate>STOCK.MONTHLY_CONSUMPTION.ALGO_4</strong>
<br />
<span translate>STOCK.MONTHLY_CONSUMPTION.ALGO_4_COMMENT</span>
</span>
</p>

<div class="row">
<!-- left hand side -->
<div class="col-sm-6 col-xs-12">
<dl>
<dt translate>STOCK.INVENTORY</dt>
<dd>{{ModalCtrl.inventory.code}} - {{ModalCtrl.inventory.label}}</dd>

<dt translate>STOCK.MONTHLY_CONSUMPTION.NUMBER_OF_MONTHS</dt>
<dd>
{{ModalCtrl.data.start_date | date }} &minus; {{ModalCtrl.data.end_date | date}}
(<span class="text-warning">{{ModalCtrl.data.number_of_month}} <span translate>STOCK.MONTHLY_CONSUMPTION.MONTHS</span></span>)
</dd>

<dt translate>STOCK.MONTHLY_CONSUMPTION.FIRST_INVENTORY_MOVEMENT_DATE</dt>
<dd>{{ModalCtrl.data.first_inventory_movement_date | date }}</dd>

<dt translate>STOCK.MONTHLY_CONSUMPTION.LAST_INVENTORY_MOVEMENT_DATE</dt>
<dd>{{ModalCtrl.data.last_inventory_movement_date | date }}</dd>

<dt translate>STOCK.MONTHLY_CONSUMPTION.SUM_CONSUMED_QUANTITY</dt>
<dd class="text-primary">{{ModalCtrl.data.sum_consumed_quantity }} {{ModalCtrl.inventory.unit}}</dd>

<dt translate>STOCK.MONTHLY_CONSUMPTION.SUM_CONSUMPTION_DAY</dt>
<dd class="text-info">{{ModalCtrl.data.sum_consumption_day}} <span translate>STOCK.MONTHLY_CONSUMPTION.DAYS</span></dd>

<dt translate>STOCK.MONTHLY_CONSUMPTION.SUM_STOCK_DAY</dt>
<dd class="text-success">{{ModalCtrl.data.sum_stock_day}} <span translate>STOCK.MONTHLY_CONSUMPTION.DAYS</span></dd>

<dt translate>STOCK.MONTHLY_CONSUMPTION.SUM_STOCK_OUT_DAYS</dt>
<dd class="text-danger">{{ModalCtrl.data.sum_stock_out_days}} <span translate>STOCK.MONTHLY_CONSUMPTION.DAYS</span></dd>
</dl>
</div>

<!-- right hand side -->
<div class="col-sm-6 col-xs-12">
<table class="table table-condensed table-bordered">
<thead>
<tr>
<th translate translate-attr="{ 'title' : 'STOCK.MONTHLY_CONSUMPTION.ALGO_1_COMMENT' }">STOCK.MONTHLY_CONSUMPTION.ALGO_1</th>
<th translate translate-attr="{ 'title' : 'STOCK.MONTHLY_CONSUMPTION.ALGO_1_COMMENT' }">STOCK.MONTHLY_CONSUMPTION.ALGO_2</th>
<th translate translate-attr="{ 'title' : 'STOCK.MONTHLY_CONSUMPTION.ALGO_1_COMMENT' }">STOCK.MONTHLY_CONSUMPTION.ALGO_3</th>
<th translate translate-attr="{ 'title' : 'STOCK.MONTHLY_CONSUMPTION.ALGO_1_COMMENT' }">STOCK.MONTHLY_CONSUMPTION.ALGO_4</th>
</tr>
</thead>
<tbody>
<tr class="text-right">
<td ng-class="{ 'bg-success text-success' : ModalCtrl.isAlgo1 }">{{ModalCtrl.data.algo1}}</td>
<td ng-class="{ 'bg-success text-success' : ModalCtrl.isAlgo2 }">{{ModalCtrl.data.algo2}}</td>
<td ng-class="{ 'bg-success text-success' : ModalCtrl.isAlgo3 }">{{ModalCtrl.data.algo3}}</td>
<td ng-class="{ 'bg-success text-success' : ModalCtrl.isAlgo4 }">{{ModalCtrl.data.algo_msh}}</td>
</tr>
</tbody>
</table>

<hr />

<dl>
<dt ng-show="ModalCtrl.isAlgo1" translate>STOCK.MONTHLY_CONSUMPTION.ALGO_1</dt>
<dd ng-show="ModalCtrl.isAlgo1">30.5 &times; (<span class="text-primary">{{ModalCtrl.data.sum_consumed_quantity}}</span> / <span class="text-success">{{ModalCtrl.data.sum_stock_day}}</span>) &equals; <u>{{ModalCtrl.data.avg_consumption}}</u> </dd>

<dt ng-show="ModalCtrl.isAlgo2" translate>STOCK.MONTHLY_CONSUMPTION.ALGO_2</dt>
<dd ng-show="ModalCtrl.isAlgo2">30.5 &times; (<span class="text-primary">{{ModalCtrl.data.sum_consumed_quantity}}</span> / <span class="text-info">{{ModalCtrl.data.sum_consumption_day}}</span>) &equals; <u>{{ModalCtrl.data.avg_consumption}}</u></dd>

<dt ng-show="ModalCtrl.isAlgo3" translate>STOCK.MONTHLY_CONSUMPTION.ALGO_3</dt>
<dd ng-show="ModalCtrl.isAlgo3">30.5 &times; (<span class="text-primary">{{ModalCtrl.data.sum_consumed_quantity}}</span> / {{ModalCtrl.data.sum_days}}) &equals; <u>{{ModalCtrl.data.avg_consumption}}</u> </dd>

<dt ng-show="ModalCtrl.isAlgo4" translate>STOCK.MONTHLY_CONSUMPTION.ALGO_4</dt>
<dd ng-show="ModalCtrl.isAlgo4"><span class="text-primary">{{ModalCtrl.data.sum_consumed_quantity}}</span> / (<span class="text-warning">{{ModalCtrl.data.number_of_month}}</span> &minus; (<span class="text-danger">{{ModalCtrl.data.sum_stock_out_days}}</span> / 30.5)) &equals; <u>{{ModalCtrl.data.avg_consumption}}</u></dd>
</dl>
</div>
</div>
</div>

<div class="modal-footer">
<button type="button" class="btn btn-default" ng-click="ModalCtrl.close()">
<span translate>FORM.BUTTONS.CLOSE</span>
</button>
</div>
31 changes: 31 additions & 0 deletions client/src/modules/stock/inventories/modals/amc.modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
angular.module('bhima.controllers')
.controller('StockAMCModalController', StockAMCModalController);

StockAMCModalController.$inject = [
'StockService', 'NotifyService', '$uibModalInstance', 'data',
];

function StockAMCModalController(Stock, Notify, Instance, data) {
const vm = this;

vm.close = () => Instance.dismiss();

Stock.inventories.loadAMCForInventory(data.inventory_uuid, data.depot_uuid)
.then(items => {
vm.data = items;

vm.settings = vm.data.settings;
vm.inventory = items.inventory;
vm.depot = items.depot;

vm.data.avg_consumption = vm.data[vm.settings.average_consumption_algo];

// nicer aliases to use in the HTML
vm.isAlgo1 = vm.settings.average_consumption_algo === 'algo1';
vm.isAlgo2 = vm.settings.average_consumption_algo === 'algo2';
vm.isAlgo3 = vm.settings.average_consumption_algo === 'algo3';
vm.isAlgo4 = vm.settings.average_consumption_algo === 'algo_msh';
})
.catch(Notify.handleError);

}
7 changes: 7 additions & 0 deletions client/src/modules/stock/inventories/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,13 @@ function StockInventoriesController(
vm.gridApi.core.notifyDataChange(uiGridConstants.dataChange.COLUMN);
};

vm.viewAMCCalculations = viewAMCCalculations;

function viewAMCCalculations(item) {
return Modal.openAMCCalculationModal(item)
.catch(angular.noop);
}

/**
* @function openBarcodeScanner
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,13 @@
<i class="fa fa-cubes"></i> <span translate>INVENTORY.VIEW_LOTS_IN_STOCK</span>
</a>
</li>

<li class="divider"></li>

<li>
<a data-method="view-amc-calculations" href ng-click="grid.appScope.viewAMCCalculations(row.entity)">
<i class="fa fa-newspaper-o"></i> <span translate>INVENTORY.OPEN_AMC_CALCULATION</span>
</a>
</li>
</ul>
</div>
5 changes: 4 additions & 1 deletion client/src/modules/stock/lots/modals/edit.modal.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<form name="ActionForm" bh-submit="$ctrl.submit(ActionForm)" novalidate>
<div class="modal-header">
<strong translate>LOTS.DETAILS</strong>
<ol class="headercrumb">
<li class="static" translate>LOTS.DETAILS</li>
<li class="title" translate>{{$ctrl.model.text}}</li>
</ol>
</div>

<div class="modal-body">
Expand Down
15 changes: 15 additions & 0 deletions client/src/modules/stock/stock.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function StockModalService(Modal) {
service.openSearchStockRequisition = openSearchStockRequisition;
service.openAssignmentHistoric = openAssignmentHistoric;
service.openDuplicateLotsModal = openDuplicateLotsModal;
service.openAMCCalculationModal = openAMCCalculationModal;

/** create stock assign */
function openActionStockAssign(request) {
Expand Down Expand Up @@ -241,6 +242,20 @@ function StockModalService(Modal) {
return instance.result;
}

function openAMCCalculationModal(request) {
const templateUrl = 'modules/stock/inventories/modals/amc.modal.html';
const controller = 'StockAMCModalController as ModalCtrl';
const params = angular.extend(modalParameters, {
templateUrl,
controller,
size : 'lg',
resolve : { data : () => request },
});

const instance = Modal.open(params);
return instance.result;
}

function openDefineLots(request) {
const params = angular.extend(modalParameters, {
templateUrl : 'modules/stock/entry/modals/lots.modal.html',
Expand Down
21 changes: 20 additions & 1 deletion server/controllers/inventory/depots/extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
const router = require('express').Router({ mergeParams : true });
const db = require('../../../lib/db');
const core = require('../../stock/core');
const inv = require('../inventory/core');

exports.router = router;

Expand Down Expand Up @@ -91,7 +92,25 @@ async function getInventoryAverageMonthlyConsumption(req, res, next) {
[monthAvgConsumption, db.bid(inventoryUuid), db.bid(uuid)],
);

res.status(200).json(averageMonthlyConsumption);
const sql = `SELECT
BUID(d.uuid) as uuid, d.text, d.description, d.is_warehouse,
allow_entry_purchase, allow_entry_donation, allow_entry_integration, allow_entry_transfer,
allow_exit_debtor, allow_exit_service, allow_exit_transfer, allow_exit_loss,
BUID(parent_uuid) parent_uuid, dhis2_uid,
min_months_security_stock
FROM depot AS d
WHERE d.enterprise_id = ? AND d.uuid = ?;`;

const [[inventory], [depot]] = await Promise.all([
inv.getItemsMetadata({ uuid : inventoryUuid }),
db.exec(sql, [req.session.enterprise.id, db.bid(uuid)]),
]);

const settings = req.session.stock_settings;

res.status(200).json({
...averageMonthlyConsumption, inventory, depot, settings,
});
} catch (err) {
next(err);
}
Expand Down

0 comments on commit 9d943a5

Please sign in to comment.