Skip to content

Commit

Permalink
Merge #5103
Browse files Browse the repository at this point in the history
5103: refactor(stock): improve find modals r=jniles a=jniles

Consolidates code related to finding depots and services for the stock exit modal.  The logic is all contained in a single file now so that we have uniform error handling between the two different modals.

Co-authored-by: Jonathan Niles <jonathanwniles@gmail.com>
  • Loading branch information
bors[bot] and jniles authored Nov 22, 2020
2 parents 3e8ae9a + a02f94b commit 37ba489
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 101 deletions.
4 changes: 2 additions & 2 deletions client/src/i18n/en/stock.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@
"DOES_REQUISITION_EXIST_HELP":"Click on this option if you want to exit stock based on a requisition slip",
"EDIT_REQUISITION": "Edit",
"NEW_REQUISITION": "New requisition",
"NOT_FOR_SERVICE":"The requisition slip is not for services",
"NOT_FOR_DEPOT":"The requisition slip is not for depots",
"NOT_FOR_SERVICE":"The requisition slip is not for the selected service",
"NOT_FOR_DEPOT":"The requisition slip is not for the selected depot",
"NOT_FOR_THE_SAME_DEPOT":"Exit on a requisition slip can not be made on the same depot",
"OTHER_REQUISITION": "Other requisition",
"RECEIVER": "Beneficiary",
Expand Down
4 changes: 2 additions & 2 deletions client/src/i18n/fr/stock.json
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@
"DOES_REQUISITION_EXIST_HELP": "Cliquez sur cette option si vous voulez effectuer une sortie de stock à partir d'un bon de requisition",
"EDIT_REQUISITION" : "Editer",
"NEW_REQUISITION" : "Nouvelle réquisition",
"NOT_FOR_SERVICE":"Le bon de requisition ne concerne pas les services",
"NOT_FOR_DEPOT":"Le bon de requisition ne concerne pas les depots",
"NOT_FOR_SERVICE":"Le bon de requisition ne concerne pas le service en cours",
"NOT_FOR_DEPOT":"Le bon de requisition ne concerne pas le depot en cours",
"NOT_FOR_THE_SAME_DEPOT":"La sortie sur bon de requisition ne peut être effectué sur un même dépôt",
"OTHER_REQUISITION" : "Autre réquisition",
"RECEIVER" : "Bénéficiaire",
Expand Down
97 changes: 97 additions & 0 deletions client/src/modules/stock/exit/modals/RequisitionHelper.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
angular.module('bhima.services')
.service('RequisitionHelperService', RequisitionHelperService);

RequisitionHelperService.$inject = ['StockService'];

/**
* @function RequisitionHelperService
*
* @description
* This function combines shared options from the FindDepot and FindService modals
* into a single file to not repeat code.
*
*/
function RequisitionHelperService(Stock) {

const service = this;

/**
* @function lookupRequisitionByReference
*
* @description
* This function looks up the requisition by the reference and asserts that
* the requisition is available for use. If it passes checks, the requisition
* API is queried again with the balance check.
*/
function lookupRequisitionByReference(reference) {
return Stock.stockRequisition.read(null, { reference })
.then(([requisition]) => {

// check that the requisition actually exists
if (!requisition || !requisition.uuid) {
const err = new Error('REQUISITION.VOUCHER_NOT_FOUND');
err.label = 'label label-warning';
throw err;
}

// check that the requisition is available to use
const statuses = ['done', 'completed', 'excessive'];
if (statuses.includes(requisition.status_key)) {
const err = new Error('REQUISITION.ALREADY_USED');
err.label = 'label label-success';
throw err;
}

// check that the requisition hasn't been canceled
if (requisition.status_key === 'cancelled') {
const err = new Error('REQUISITION.CANCELLED');
err.label = 'label label-danger';
throw err;
}

return Stock.stockRequisition.read(requisition.uuid, { balance : true });
});
}

/**
* @function isRequisitionForDepot
*
* @description
* Checks if the requisition is for the correct depot and sets the appropriate
* error flags if not.
*/
function isRequisitionForDepot(requisition, depot) {
if (depot.uuid !== requisition.depot_uuid) {
const err = new Error('REQUISITION.NOT_FOR_DEPOT');
err.label = 'label label-warning';
throw err;
}
return requisition;
}

/**
* @function isRequisitionForService
*
* @description
* Checks if the requisition is for the correct service and sets the appropriate
* error flags if not.
*/
function isRequisitionForService(requisition, reqService) {
if (!reqService || !reqService.uuid) {
const err = new Error('REQUISITION.NOT_FOR_SERVICE');
err.label = 'label label-warning';
throw err;
}
return requisition;
}

// bind functions/properties to service
Object.assign(service, {
lookupRequisitionByReference,
isRequisitionForDepot,
isRequisitionForService,
});

return service;

}
10 changes: 7 additions & 3 deletions client/src/modules/stock/exit/modals/findDepot.modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
novalidate>

<div class="modal-header">
<i class="fa fa-search"></i>
<span translate>FORM.LABELS.SEARCH</span>
<span translate>STOCK.DEPOT</span>
<ol class="headercrumb">
<li class="static"> <i class="fa fa-search"></i> </li>
<li class="title">
<span translate>FORM.LABELS.SEARCH</span>
<span translate>STOCK.DEPOT</span>
</li>
</ol>
</div>

<div class="modal-body">
Expand Down
58 changes: 16 additions & 42 deletions client/src/modules/stock/exit/modals/findDepot.modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ angular.module('bhima.controllers')

StockFindDepotModalController.$inject = [
'$uibModalInstance', 'DepotService', 'NotifyService', 'data',
'StockService', 'SessionService',
'StockService', 'SessionService', 'RequisitionHelperService',
];

function StockFindDepotModalController(Instance, Depot, Notify, Data, Stock, Session) {
function StockFindDepotModalController(Instance, Depot, Notify, Data, Stock, Session, RequisitionHelpers) {
const vm = this;
const enableStrictDepotDistribution = Session.stock_settings.enable_strict_depot_distribution;

Expand Down Expand Up @@ -43,9 +43,7 @@ function StockFindDepotModalController(Instance, Depot, Notify, Data, Stock, Ses
}

function extractDepotFromCollection(depotUuid, collection) {
const idx = collection.findIndex(i => (i.uuid === depotUuid));
if (idx > -1) { collection.splice(idx, 1); }
return collection;
return collection.filter(depot => depot.uuid !== depotUuid);
}

vm.onChangeReference = reference => {
Expand All @@ -55,67 +53,43 @@ function StockFindDepotModalController(Instance, Depot, Notify, Data, Stock, Ses
// submit
function submit(form) {
if (vm.reference) {
return Stock.stockRequisition.read(null, { reference : vm.reference })
.then(requisitionDetails)
return RequisitionHelpers.lookupRequisitionByReference(vm.reference)
.then(requisition => RequisitionHelpers.isRequisitionForDepot(requisition, Data.depot))
.then(depotDetails)
.then(assignDepotRequisition)
.catch(Notify.handleError);
.catch(err => {

// bind the error flags as needed
vm.requisitionMessage = err.message;
vm.requisitionLabel = err.label;
Notify.handleError(err);
});
}

if (form.$invalid && !vm.requisition.uuid) { return null; }
return Instance.close(vm.selected);
}

function requisitionDetails([requisition]) {
if (!requisition || !requisition.uuid) {
vm.requisitionMessage = 'REQUISITION.VOUCHER_NOT_FOUND';
vm.requisitionLabel = 'label label-primary';
throw new Error('Requisition Not Found');
}

if (requisition.status_key === 'done' || requisition.status_key === 'completed'
|| requisition.status_key === 'excessive') {
vm.requisitionMessage = 'REQUISITION.ALREADY_USED';
vm.requisitionLabel = 'label label-success';
throw new Error('Requisition Already Used');
}

if (requisition.status_key === 'cancelled') {
vm.requisitionMessage = 'REQUISITION.CANCELLED';
vm.requisitionLabel = 'label label-danger';
throw new Error('Requisition Cancelled');
}

return Stock.stockRequisition.read(requisition.uuid, { balance : true });
}

function depotDetails(requisition) {
vm.requisition = requisition;

if (vm.requisition.depot_uuid !== Data.depot.uuid) {
vm.requisitionMessage = 'REQUISITION.NOT_FOR_DEPOT';
vm.requisitionLabel = 'label label-warning';
throw new Error('This requisition is not for this depot');
}

return Depot.read(null, { uuid : vm.requisition.requestor_uuid });
}

function assignDepotRequisition([depot]) {
if (Data.depot.uuid === vm.selected.uuid) {
vm.requisitionMessage = 'REQUISITION.NOT_FOR_THE_SAME_DEPOT';
vm.requisitionLabel = 'label label-danger';
throw new Error('The requisition cannot be for the same depot');
const err = new Error('REQUISITION.NOT_FOR_THE_SAME_DEPOT');
err.label = 'label label-danger';
throw err;
}

vm.selected = depot;
vm.selected.requisition = vm.requisition;

Instance.close(vm.selected);
}

// cancel
function cancel() {
Instance.close(vm.selected);
}

}
11 changes: 6 additions & 5 deletions client/src/modules/stock/exit/modals/findPatient.modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

<div class="modal-header">
<ol class="headercrumb">
<li class="static"> <i class="fa fa-search"></i> </li>
<li class="title">
<span translate>FORM.LABELS.SEARCH</span>
<span translate>FORM.LABELS.PATIENT</span>
</li>
<li class="static"> <i class="fa fa-search"></i> </li>
<li class="title">
<span translate>FORM.LABELS.SEARCH</span>
<span translate>FORM.LABELS.PATIENT</span>
</li>
</ol>
</div>

<div class="modal-body">
Expand Down
10 changes: 7 additions & 3 deletions client/src/modules/stock/exit/modals/findService.modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
novalidate>

<div class="modal-header">
<i class="fa fa-search"></i>
<span translate>FORM.LABELS.SEARCH</span>
<span translate>FORM.LABELS.SERVICE</span>
<ol class="headercrumb">
<li class="static"> <i class="fa fa-search"></i> </li>
<li class="title">
<span translate>FORM.LABELS.SEARCH</span>
<span translate>FORM.LABELS.SERVICE</span>
</li>
</ol>
</div>

<div class="modal-body">
Expand Down
58 changes: 17 additions & 41 deletions client/src/modules/stock/exit/modals/findService.modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,22 @@ angular.module('bhima.controllers')

StockFindServiceModalController.$inject = [
'$uibModalInstance', 'ServiceService', 'NotifyService', 'data',
'StockService',
'StockService', 'RequisitionHelperService',
];

function StockFindServiceModalController(Instance, Service, Notify, Data, Stock) {
function StockFindServiceModalController(Instance, Service, Notify, Data, Stock, RequisitionHelpers) {
const vm = this;

vm.selected = {};

// bind methods
vm.submit = submit;
vm.cancel = cancel;
vm.clear = clear;

Service.read()
Service.read(null, { hidden : 0 })
.then(services => {
vm.services = services.filter(s => !s.hidden);
vm.services = services;
[vm.selected] = services.filter(s => s.uuid === Data.entity_uuid);
})
.catch(Notify.handleError);
Expand All @@ -32,60 +34,34 @@ function StockFindServiceModalController(Instance, Service, Notify, Data, Stock)
// submit
function submit(form) {
if (vm.reference) {
return Stock.stockRequisition.read(null, { reference : vm.reference })
.then(requisitionDetails)
return RequisitionHelpers.lookupRequisitionByReference(vm.reference)
.then(requisition => RequisitionHelpers.isRequisitionForDepot(requisition, Data.depot))
.then(serviceDetails)
.then(assignServiceRequisition)
.catch(Notify.handleError);
.catch(err => {

// bind the error flags as needed
vm.requisitionMessage = err.message;
vm.requisitionLabel = err.label;
Notify.handleError(err);
});
}

if (form.$invalid && !vm.requisition.uuid) { return null; }
return Instance.close(vm.selected);
}

function requisitionDetails([requisition]) {
if (Data.depot.uuid !== requisition.depot_uuid) {
vm.requisitionMessage = 'REQUISITION.NOT_FOR_DEPOT';
vm.requisitionLabel = 'label label-warning';
throw new Error('This requisition is not for this depot');
}

if (!requisition || !requisition.uuid) {
vm.requisitionMessage = 'REQUISITION.VOUCHER_NOT_FOUND';
vm.requisitionLabel = 'label label-warning';
throw new Error('Requisition Not Found');
}

if (requisition.status_key === 'done' || requisition.status_key === 'completed'
|| requisition.status_key === 'excessive') {
vm.requisitionMessage = 'REQUISITION.ALREADY_USED';
vm.requisitionLabel = 'label label-success';
throw new Error('Requisition Already Used');
}

if (requisition.status_key === 'cancelled') {
vm.requisitionMessage = 'REQUISITION.CANCELLED';
vm.requisitionLabel = 'label label-danger';
throw new Error('Requisition Cancelled');
}

return Stock.stockRequisition.read(requisition.uuid, { balance : true });
}

function serviceDetails(requisition) {
vm.requisition = requisition;
return Service.read(null, { uuid : vm.requisition.requestor_uuid });
}

function assignServiceRequisition([service]) {
if (!service || !service.uuid) {
vm.requisitionMessage = 'REQUISITION.NOT_FOR_SERVICE';
vm.requisitionLabel = 'label label-warning';
throw new Error('The requisition is not for services');
}
RequisitionHelpers.isRequisitionForService(vm.requisition, service);

vm.selected = service;
vm.selected.requisition = vm.requisition;

Instance.close(vm.selected);
}

Expand Down
7 changes: 4 additions & 3 deletions server/controllers/admin/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ function list(req, res, next) {
}

const params = db.convert(req.query, ['uuid']);
const filters = new FilterParser(params);
const filters = new FilterParser({ ...params, tableAlias : 's' });

filters.equals('uuid', 'uuid', 's');
filters.equals('name', 'name', 's');
filters.equals('uuid');
filters.equals('name');
filters.equals('hidden');
filters.setOrder('ORDER BY s.name');

const query = filters.applyQuery(sql);
Expand Down

0 comments on commit 37ba489

Please sign in to comment.