Skip to content

Commit

Permalink
Merge pull request #1575 from BLSQ/POLIO-1636-incident-reports
Browse files Browse the repository at this point in the history
POLIO-1645 POLIO-1646  POLIO-1647  incident reports improvements
  • Loading branch information
beygorghor authored Aug 26, 2024
2 parents 6f94ac7 + 55ca837 commit 3b447ad
Show file tree
Hide file tree
Showing 16 changed files with 577 additions and 110 deletions.
132 changes: 79 additions & 53 deletions plugins/polio/api/vaccines/stock_management.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import enum
from typing import Union
from drf_yasg import openapi

from django.db.models import QuerySet
from drf_yasg.utils import swagger_auto_schema, no_body
from drf_yasg import openapi
from drf_yasg.utils import no_body, swagger_auto_schema
from rest_framework import filters, serializers, status, viewsets
from rest_framework.decorators import action
from rest_framework.filters import SearchFilter
Expand All @@ -12,14 +13,14 @@
from iaso.api.common import GenericReadWritePerm, ModelViewSet, Paginator
from iaso.models import OrgUnit
from plugins.polio.models import (
DOSES_PER_VIAL,
Campaign,
DestructionReport,
IncidentReport,
OutgoingStockMovement,
VaccineArrivalReport,
VaccineRequestForm,
VaccineStock,
DOSES_PER_VIAL,
)

vaccine_stock_id_param = openapi.Parameter(
Expand Down Expand Up @@ -59,10 +60,12 @@ def get_doses_per_vial(self):
return DOSES_PER_VIAL[self.vaccine_stock.vaccine]

def get_vials_used(self):
received = self.get_vials_received()
stock = self.get_total_of_usable_vials()[0]
results = self.get_list_of_used_vials()
total = 0
for result in results:
total += result["vials_in"]

return received - stock
return total

def get_vials_destroyed(self):
if not self.destruction_reports.exists():
Expand All @@ -71,7 +74,6 @@ def get_vials_destroyed(self):

def get_total_of_usable_vials(self):
results = self.get_list_of_usable_vials()

total_vials_in = 0
total_doses_in = 0

Expand All @@ -88,7 +90,7 @@ def get_total_of_usable_vials(self):
return total_vials_in, total_doses_in

def get_vials_received(self):
results = self.get_list_of_usable_vials()
results = self.get_list_of_vaccines_received()

total_vials_in = 0

Expand Down Expand Up @@ -116,7 +118,10 @@ def get_total_of_unusable_vials(self):

return total_vials_in, total_doses_in

def get_list_of_usable_vials(self):
def get_list_of_vaccines_received(self):
"""
Vaccines received are only those linked to an arrival report. We exclude those found e.g. during physical inventory
"""
# First find the corresponding VaccineRequestForms
vrfs = VaccineRequestForm.objects.filter(
campaign__country=self.vaccine_stock.country, vaccine_type=self.vaccine_stock.vaccine
Expand All @@ -128,12 +133,6 @@ def get_list_of_usable_vials(self):
arrival_reports = VaccineArrivalReport.objects.filter(request_form__in=vrfs)
if not arrival_reports.exists():
arrival_reports = []

incident_reports = IncidentReport.objects.filter(vaccine_stock=self.vaccine_stock).order_by(
"date_of_incident_report"
)
stock_movements = OutgoingStockMovement.objects.filter(vaccine_stock=self.vaccine_stock).order_by("report_date")

results = []

for report in arrival_reports:
Expand All @@ -148,7 +147,45 @@ def get_list_of_usable_vials(self):
"type": MovementTypeEnum.VACCINE_ARRIVAL_REPORT.value,
}
)
return results

def get_list_of_usable_vials(self):
# First get vaccines received from arrival reports
results = self.get_list_of_vaccines_received()

# Add stock movements (used and missing vials)
stock_movements = OutgoingStockMovement.objects.filter(vaccine_stock=self.vaccine_stock).order_by("report_date")
for movement in stock_movements:
if movement.usable_vials_used > 0:
results.append(
{
"date": movement.report_date,
"action": "Form A - Vials Used",
"vials_in": None,
"doses_in": None,
"vials_out": movement.usable_vials_used or 0,
"doses_out": (movement.usable_vials_used or 0) * self.get_doses_per_vial(),
"type": MovementTypeEnum.OUTGOING_STOCK_MOVEMENT.value,
}
)

if movement.missing_vials > 0:
results.append(
{
"date": movement.report_date,
"action": "Form A - Missing Vials",
"vials_in": None,
"doses_in": None,
"vials_out": movement.missing_vials or 0,
"doses_out": (movement.missing_vials or 0) * self.get_doses_per_vial(),
"type": MovementTypeEnum.OUTGOING_STOCK_MOVEMENT.value,
}
)

# Add incident reports (IN movements then OUT movements)
incident_reports = IncidentReport.objects.filter(vaccine_stock=self.vaccine_stock).order_by(
"date_of_incident_report"
)
for report in incident_reports:
if (
report.usable_vials > 0
Expand All @@ -165,14 +202,10 @@ def get_list_of_usable_vials(self):
"type": MovementTypeEnum.INCIDENT_REPORT.value,
}
)
if report.unusable_vials > 0 and (
report.stock_correction == IncidentReport.StockCorrectionChoices.PHYSICAL_INVENTORY
or report.stock_correction == IncidentReport.StockCorrectionChoices.VACCINE_EXPIRED
or report.stock_correction == IncidentReport.StockCorrectionChoices.LOSSES
if report.usable_vials > 0 and (
report.stock_correction == IncidentReport.StockCorrectionChoices.LOSSES
or report.stock_correction == IncidentReport.StockCorrectionChoices.RETURN
or report.stock_correction == IncidentReport.StockCorrectionChoices.STEALING
or report.stock_correction == IncidentReport.StockCorrectionChoices.VVM_REACHED_DISCARD_POINT
or report.stock_correction == IncidentReport.StockCorrectionChoices.UNREADABLE_LABEL
or report.stock_correction == IncidentReport.StockCorrectionChoices.BROKEN
):
results.append(
Expand All @@ -181,52 +214,34 @@ def get_list_of_usable_vials(self):
"action": report.stock_correction,
"vials_in": None,
"doses_in": None,
"vials_out": report.unusable_vials or 0,
"doses_out": (report.unusable_vials or 0) * self.get_doses_per_vial(),
"vials_out": report.usable_vials or 0,
"doses_out": (report.usable_vials or 0) * self.get_doses_per_vial(),
"type": MovementTypeEnum.INCIDENT_REPORT.value,
}
)

for movement in stock_movements:
if movement.usable_vials_used > 0:
if report.unusable_vials > 0 and (
report.stock_correction == IncidentReport.StockCorrectionChoices.VACCINE_EXPIRED
or report.stock_correction == IncidentReport.StockCorrectionChoices.VVM_REACHED_DISCARD_POINT
or report.stock_correction == IncidentReport.StockCorrectionChoices.UNREADABLE_LABEL
):
results.append(
{
"date": movement.report_date,
"action": "Form A - Vials Used",
"date": report.date_of_incident_report,
"action": report.stock_correction,
"vials_in": None,
"doses_in": None,
"vials_out": movement.usable_vials_used or 0,
"doses_out": (movement.usable_vials_used or 0) * self.get_doses_per_vial(),
"type": MovementTypeEnum.OUTGOING_STOCK_MOVEMENT.value,
"vials_out": report.unusable_vials or 0,
"doses_out": (report.unusable_vials or 0) * self.get_doses_per_vial(),
"type": MovementTypeEnum.INCIDENT_REPORT.value,
}
)

if movement.missing_vials > 0:
results.append(
{
"date": movement.report_date,
"action": "Form A - Missing Vials",
"vials_in": None,
"doses_in": None,
"vials_out": movement.missing_vials or 0,
"doses_out": (movement.missing_vials or 0) * self.get_doses_per_vial(),
"type": MovementTypeEnum.OUTGOING_STOCK_MOVEMENT.value,
}
)
return results

def get_list_of_unusable_vials(self):
# Get all OutgoingStockMovements and IncidentReports for the VaccineStock
def get_list_of_used_vials(self):
# Used vials are those related to formA outgoing movements. Vials with e.g expired date become unusable, but have not been used
outgoing_movements = OutgoingStockMovement.objects.filter(vaccine_stock=self.vaccine_stock)
incident_reports = IncidentReport.objects.filter(vaccine_stock=self.vaccine_stock)
destruction_reports = DestructionReport.objects.filter(vaccine_stock=self.vaccine_stock).order_by(
"destruction_report_date"
)

# Prepare the results list
results = []

# Add unusable vials from OutgoingStockMovements/FORMA
for movement in outgoing_movements:
if movement.usable_vials_used > 0:
results.append(
Expand All @@ -240,6 +255,17 @@ def get_list_of_unusable_vials(self):
"type": MovementTypeEnum.OUTGOING_STOCK_MOVEMENT.value,
}
)
return results

def get_list_of_unusable_vials(self):
# First get the used vials
results = self.get_list_of_used_vials()

# Get all IncidentReports and Destruction reports for the VaccineStock
incident_reports = IncidentReport.objects.filter(vaccine_stock=self.vaccine_stock)
destruction_reports = DestructionReport.objects.filter(vaccine_stock=self.vaccine_stock).order_by(
"destruction_report_date"
)

for report in destruction_reports:
results.append(
Expand Down
12 changes: 12 additions & 0 deletions plugins/polio/js/src/constants/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"iaso.forms.options.emailFormat": "Please use correctly formatted email addresses",
"iaso.forms.options.urlFormat": "Please use correctly formatted URL",
"iaso.forms.options.validation": "Validation",
"iaso.incident.inventoryUnusableMovement": "Unusable vials in: {movement}",
"iaso.incident.inventoryUsableMovement": "Usable vials in: {movement}",
"iaso.incident.missingMovement": "Usable vials out: {movement}",
"iaso.incident.plainMovement": "Usable vials out: {movement}, Unusable vials in: {movement}",
"iaso.instance.last_modified_by": "Modified by",
"iaso.label.account": "Account ID",
"iaso.label.action": "Action",
Expand Down Expand Up @@ -339,6 +343,7 @@
"iaso.polio.label.countryNameInSheet": "Country Name in sheet",
"iaso.polio.label.countryPassing": "80% or more districts passed",
"iaso.polio.label.create": "Create",
"iaso.polio.label.createIncident": "Create incident report",
"iaso.polio.label.createReasonForDelay": "Create reason for delay",
"iaso.polio.label.createSubActivity": "Create sub-activity",
"iaso.polio.label.createVrf": "Create VRF",
Expand All @@ -348,6 +353,7 @@
"iaso.polio.label.currentExpirationDate": "Current expiration date",
"iaso.polio.label.cvdpv_notified_at": "CVDPV Notification date",
"iaso.polio.label.date_dg_approval": "Date of DG approval",
"iaso.polio.label.date_of_incident_report": "Date of incident report",
"iaso.polio.label.date_pre_alert_reception": "Pre-alert reception",
"iaso.polio.label.date_rrt_orpg_approval": "Date of RRT/ORPG approval",
"iaso.polio.label.date_vrf_reception": "Date of VRF reception",
Expand Down Expand Up @@ -399,6 +405,7 @@
"iaso.polio.label.dosesShipped": "Doses shipped",
"iaso.polio.label.downloadScopesToCsv": "Download scopes",
"iaso.polio.label.editGroupedCampaign": "Edit grouped campaign",
"iaso.polio.label.editIncident": "Edit incident report",
"iaso.polio.label.editReasonForDelay": "Edit reason for delay",
"iaso.polio.label.editRoundDates": "Edit round dates",
"iaso.polio.label.editSubActivity": "Edit sub-activity",
Expand Down Expand Up @@ -499,6 +506,7 @@
"iaso.polio.label.MOH_DECISION": "Decision from MOH",
"iaso.polio.label.months": "Months",
"iaso.polio.label.MONTHS": "Months",
"iaso.polio.label.movement": "Movement",
"iaso.polio.label.name": "Name",
"iaso.polio.label.name_en": "English text",
"iaso.polio.label.name_fr": "French text",
Expand Down Expand Up @@ -640,6 +648,7 @@
"iaso.polio.label.Teachers_Student": "Teachers",
"iaso.polio.label.testCampaign": "Test campaign/On hold",
"iaso.polio.label.timesSelected": "Times selected",
"iaso.polio.label.title": "Title",
"iaso.polio.label.toSubmit": "To submit",
"iaso.polio.label.Tot_child_Absent_HH": "Child absent",
"iaso.polio.label.Tot_child_Asleep_HH": "Child asleep",
Expand All @@ -662,8 +671,10 @@
"iaso.polio.label.unknown": "Unknown",
"iaso.polio.label.unreadable_label": "Unreadable label",
"iaso.polio.label.unusable_vials": "Unusable vials",
"iaso.polio.label.unusableVialsIn": "Unusable Vials In",
"iaso.polio.label.url": "Url",
"iaso.polio.label.usable_vials": "Usable vials",
"iaso.polio.label.usableVialsIn": "Usable Vials In",
"iaso.polio.label.vaccinated": "Vaccinated",
"iaso.polio.label.vaccinated_but_not_fm": "Vaccinated but not marked",
"iaso.polio.label.vaccine_expired": "Vaccine expired",
Expand All @@ -676,6 +687,7 @@
"iaso.polio.label.verypoor": "Very Poor",
"iaso.polio.label.vials_destroyed": "Vials destroyed",
"iaso.polio.label.vialsDestroyed": "Unusable vials destroyed",
"iaso.polio.label.vialsOut": "Vials Out",
"iaso.polio.label.vialsReceived": "Vials received",
"iaso.polio.label.vialsShipped": "Vials shipped",
"iaso.polio.label.viewFiles": "View files",
Expand Down
12 changes: 12 additions & 0 deletions plugins/polio/js/src/constants/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
"iaso.forms.options.emailFormat": "Veuillez encoder des adresses email valides",
"iaso.forms.options.urlFormat": "Veuillez utiliser une URL valide ",
"iaso.forms.options.validation": "Validation",
"iaso.incident.inventoryUnusableMovement": "Flacons inutilisables entrés : {movement}",
"iaso.incident.inventoryUsableMovement": "Flacons utilisables entrés : {movement}",
"iaso.incident.missingMovement": "Flacons utilisables sortis : {movement}",
"iaso.incident.plainMovement": "Flacons utilisables sortis : {movement}, Flacons inutilisables entrés : {movement}",
"iaso.instance.last_modified_by": "Modifié par",
"iaso.label.account": "ID Compte",
"iaso.label.action": "Action",
Expand Down Expand Up @@ -338,6 +342,7 @@
"iaso.polio.label.countryNameInSheet": "Nom du pays dans la feuille",
"iaso.polio.label.countryPassing": "80% ou plus de districts ont réussi",
"iaso.polio.label.create": "Créer",
"iaso.polio.label.createIncident": "Créer un rapport d'incident",
"iaso.polio.label.createReasonForDelay": "Ajouter un raison de retard",
"iaso.polio.label.createSubActivity": "Créer une sous-activité",
"iaso.polio.label.createVrf": "Créer VRF",
Expand All @@ -347,6 +352,7 @@
"iaso.polio.label.currentExpirationDate": "Date d'expiration en cours",
"iaso.polio.label.cvdpv_notified_at": "Date de notification CVDPV",
"iaso.polio.label.date_dg_approval": "Date d'approbation par DG",
"iaso.polio.label.date_of_incident_report": "Date du rapport d'incident",
"iaso.polio.label.date_pre_alert_reception": "Réception de la pré-alerte",
"iaso.polio.label.date_rrt_orpg_approval": "Date d'approbation par RRT/ORPG",
"iaso.polio.label.date_vrf_reception": "Date de réception VRF",
Expand Down Expand Up @@ -398,6 +404,7 @@
"iaso.polio.label.dosesShipped": "Doses expédiées",
"iaso.polio.label.downloadScopesToCsv": "Télécharger les scopes",
"iaso.polio.label.editGroupedCampaign": "Editer campagne groupée",
"iaso.polio.label.editIncident": "Modifier le rapport d'incident",
"iaso.polio.label.editReasonForDelay": "Modifier une raisond de retard",
"iaso.polio.label.editRoundDates": "Modifier les dates de round",
"iaso.polio.label.editSubActivity": "Editer sous-activité",
Expand Down Expand Up @@ -498,6 +505,7 @@
"iaso.polio.label.MOH_DECISION": "Décision du Ministère de la Santé",
"iaso.polio.label.months": "Mois",
"iaso.polio.label.MONTHS": "Mois",
"iaso.polio.label.movement": "Mouvement",
"iaso.polio.label.name": "Nom",
"iaso.polio.label.name_en": "Texte anglais",
"iaso.polio.label.name_fr": "Texte français",
Expand Down Expand Up @@ -639,6 +647,7 @@
"iaso.polio.label.Teachers_Student": "Professeurs",
"iaso.polio.label.testCampaign": "Campagne de test/en pause",
"iaso.polio.label.timesSelected": "Nombre de sélections",
"iaso.polio.label.title": "Titre",
"iaso.polio.label.toSubmit": "À soumettre",
"iaso.polio.label.Tot_child_Absent_HH": "Enfant absent",
"iaso.polio.label.Tot_child_Asleep_HH": "Enfant endormi",
Expand All @@ -661,8 +670,10 @@
"iaso.polio.label.unknown": "Inconnu",
"iaso.polio.label.unreadable_label": "Etiquette illisible",
"iaso.polio.label.unusable_vials": "Flacons inutilisables",
"iaso.polio.label.unusableVialsIn": "Flacons inutilisables entrés",
"iaso.polio.label.url": "Url",
"iaso.polio.label.usable_vials": "Flacons utilisables",
"iaso.polio.label.usableVialsIn": "Flacons utilisables entrés",
"iaso.polio.label.vaccinated": "Vaccinés",
"iaso.polio.label.vaccinated_but_not_fm": "Vacciné mais non-marqué",
"iaso.polio.label.vaccine_expired": "Vaccin expiré",
Expand All @@ -675,6 +686,7 @@
"iaso.polio.label.verypoor": "Très insatisfaisant",
"iaso.polio.label.vials_destroyed": "Flacons détruits",
"iaso.polio.label.vialsDestroyed": "Flacons inutilisables détruits",
"iaso.polio.label.vialsOut": "Flacons sortis",
"iaso.polio.label.vialsReceived": "Flacons reçus",
"iaso.polio.label.vialsShipped": "Flacons livrés",
"iaso.polio.label.viewFiles": "Voir les fichiers",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
/* eslint-disable camelcase */
import React, { useMemo } from 'react';
import { Column, textPlaceholder, useSafeIntl } from 'bluesquare-components';
import React, { useMemo } from 'react';
import { DateCell } from '../../../../../../../../../hat/assets/js/apps/Iaso/components/Cells/DateTimeCell';
import MESSAGES from '../../messages';
import { NumberCell } from '../../../../../../../../../hat/assets/js/apps/Iaso/components/Cells/NumberCell';
import MESSAGES from '../../messages';

export const useVaccineStockManagementDetailsColumnsUsable = (): Column[] => {
const { formatMessage } = useSafeIntl();
Expand All @@ -19,6 +19,12 @@ export const useVaccineStockManagementDetailsColumnsUsable = (): Column[] => {
Header: formatMessage(MESSAGES.action),
accessor: 'action',
id: 'action',
Cell: settings => {
const { action } = settings.row.original;
return MESSAGES[action]
? formatMessage(MESSAGES[action])
: action;
},
},
{
Header: formatMessage(MESSAGES.vials_in),
Expand Down
Loading

0 comments on commit 3b447ad

Please sign in to comment.