From 1d0be0647a981894d2e5a786246e9432a9afe9d8 Mon Sep 17 00:00:00 2001 From: Pavlo Mokiichuk Date: Wed, 4 Dec 2024 14:22:07 +0100 Subject: [PATCH 01/11] Grievance People Update (#4483) * upd fields * people changes * update IndividualDataUpdateService * fix update country * add edit people gf * size smol * add test * imports * fix text * build fix * fix country display --------- Co-authored-by: Maciej Szewczyk Co-authored-by: Maciej Szewczyk <34482854+mmaciekk@users.noreply.github.com> --- .../EditHouseholdDataChange.tsx | 19 +-- .../EditPeopleDataChange/CurrentValue.tsx | 69 ++++++++++ .../EditPeopleDataChange.tsx | 127 ++++++++++++++++++ .../EditPeopleDataChangeField.tsx | 110 +++++++++++++++ .../EditPeopleDataChangeFieldRow.tsx | 93 +++++++++++++ .../grievances/FormikBoolFieldGrievances.tsx | 1 + .../LookUpIndividualTable.tsx | 2 +- .../householdDataRow.tsx | 4 +- .../EntriesTable.tsx | 4 + .../RequestedIndividualDataChangeTable.tsx | 1 + .../individualDataRow.tsx | 33 ++++- .../grievances/utils/validateGrievance.ts | 10 +- .../pages/grievances/CreateGrievancePage.tsx | 27 ++-- .../pages/grievances/EditGrievancePage.tsx | 21 ++- .../core_fields_attributes.py | 2 - src/hct_mis_api/apps/grievance/inputs.py | 13 ++ src/hct_mis_api/apps/grievance/schema.py | 1 - .../individual_data_update_service.py | 29 +++- .../test_update_individual_data_service.py | 76 +++++++++-- .../test_template_file_generator.py | 18 +-- 20 files changed, 597 insertions(+), 63 deletions(-) create mode 100644 src/frontend/src/components/grievances/EditPeopleDataChange/CurrentValue.tsx create mode 100644 src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChange.tsx create mode 100644 src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChangeField.tsx create mode 100644 src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChangeFieldRow.tsx diff --git a/src/frontend/src/components/grievances/EditHouseholdDataChange/EditHouseholdDataChange.tsx b/src/frontend/src/components/grievances/EditHouseholdDataChange/EditHouseholdDataChange.tsx index 65361277cc..3ab5fc1636 100644 --- a/src/frontend/src/components/grievances/EditHouseholdDataChange/EditHouseholdDataChange.tsx +++ b/src/frontend/src/components/grievances/EditHouseholdDataChange/EditHouseholdDataChange.tsx @@ -7,13 +7,11 @@ import { useTranslation } from 'react-i18next'; import { AllHouseholdsQuery, useAllEditHouseholdFieldsQuery, - useAllEditPeopleFieldsQuery, useHouseholdLazyQuery, } from '@generated/graphql'; import { LoadingComponent } from '@core/LoadingComponent'; import { Title } from '@core/Title'; import { EditHouseholdDataChangeFieldRow } from './EditHouseholdDataChangeFieldRow'; -import { useProgramContext } from 'src/programContext'; export interface EditHouseholdDataChangeProps { values; @@ -25,7 +23,6 @@ export function EditHouseholdDataChange({ }: EditHouseholdDataChangeProps): ReactElement { const { t } = useTranslation(); const location = useLocation(); - const { isSocialDctType } = useProgramContext(); const isEditTicket = location.pathname.includes('edit-ticket'); const household: AllHouseholdsQuery['allHouseholds']['edges'][number]['node'] = values.selectedHousehold; @@ -50,22 +47,14 @@ export function EditHouseholdDataChange({ }, []); const { data: householdFieldsData, loading: householdFieldsLoading } = useAllEditHouseholdFieldsQuery(); - const { data: allEditPeopleFieldsData, loading: allEditPeopleFieldsLoading } = - useAllEditPeopleFieldsQuery(); - const fieldsByDctType = isSocialDctType - ? allEditPeopleFieldsData?.allEditPeopleFieldsAttributes - : householdFieldsData?.allEditHouseholdFieldsAttributes; + const householdFieldsDict = + householdFieldsData?.allEditHouseholdFieldsAttributes; if (!household) { return
{t('You have to select a household earlier')}
; } - if ( - fullHouseholdLoading || - householdFieldsLoading || - allEditPeopleFieldsLoading || - !fullHousehold - ) { + if (fullHouseholdLoading || householdFieldsLoading || !fullHousehold) { return ; } const notAvailableItems = (values.householdDataUpdateFields || []).map( @@ -89,7 +78,7 @@ export function EditHouseholdDataChange({ itemValue={item} index={index} household={fullHousehold.household} - fields={fieldsByDctType} + fields={householdFieldsDict} notAvailableFields={notAvailableItems} onDelete={() => arrayHelpers.remove(index)} values={values} diff --git a/src/frontend/src/components/grievances/EditPeopleDataChange/CurrentValue.tsx b/src/frontend/src/components/grievances/EditPeopleDataChange/CurrentValue.tsx new file mode 100644 index 0000000000..4dffaa88aa --- /dev/null +++ b/src/frontend/src/components/grievances/EditPeopleDataChange/CurrentValue.tsx @@ -0,0 +1,69 @@ +import { Grid } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useLocation } from 'react-router-dom'; +import { AllAddIndividualFieldsQuery } from '@generated/graphql'; +import { LabelizedField } from '@core/LabelizedField'; +import { GrievanceFlexFieldPhotoModal } from '../GrievancesPhotoModals/GrievanceFlexFieldPhotoModal'; +import { GrievanceFlexFieldPhotoModalNewIndividual } from '../GrievancesPhotoModals/GrievanceFlexFieldPhotoModalNewIndividual'; +import { ReactElement } from 'react'; + +export interface CurrentValueProps { + field: AllAddIndividualFieldsQuery['allAddIndividualsFieldsAttributes'][number]; + value; + values; +} + +export function CurrentValue({ + field, + value, + values, +}: CurrentValueProps): ReactElement { + const location = useLocation(); + const isNewTicket = location.pathname.indexOf('new-ticket') !== -1; + + const { t } = useTranslation(); + let displayValue = value; + switch (field?.type) { + case 'SELECT_ONE': + displayValue = + field.choices.find((item) => item.value === value)?.labelEn || '-'; + break; + case 'SELECT_MANY': + displayValue = + field.choices.find((item) => item.value === value)?.labelEn || '-'; + if (value instanceof Array) { + displayValue = value + .map( + (choice) => + field.choices.find((item) => item.value === choice)?.labelEn || + '-', + ) + .join(', '); + } + break; + case 'BOOL': + /* eslint-disable-next-line no-nested-ternary */ + displayValue = value === null ? '-' : value ? t('Yes') : t('No'); + break; + case 'IMAGE': + return isNewTicket ? ( + + + + ) : ( + + + + ); + default: + displayValue = value; + } + return ( + + + + ); +} diff --git a/src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChange.tsx b/src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChange.tsx new file mode 100644 index 0000000000..f5544f05e9 --- /dev/null +++ b/src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChange.tsx @@ -0,0 +1,127 @@ +import { Button, Grid, Typography } from '@mui/material'; +import { AddCircleOutline } from '@mui/icons-material'; +import { useLocation } from 'react-router-dom'; +import { FieldArray } from 'formik'; +import { ReactElement, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; +import { + AllIndividualsQuery, + useAllEditPeopleFieldsQuery, + useIndividualLazyQuery, +} from '@generated/graphql'; +import { LoadingComponent } from '@core/LoadingComponent'; +import { Title } from '@core/Title'; +import { EditPeopleDataChangeFieldRow } from './EditPeopleDataChangeFieldRow'; + +const BoxWithBorders = styled.div` + border-bottom: 1px solid ${({ theme }) => theme.hctPalette.lighterGray}; + padding: 15px 0; +`; + +export interface EditPeopleDataChangeProps { + values; + setFieldValue; + form; + field; +} + +export function EditPeopleDataChange({ + values, + setFieldValue, +}: EditPeopleDataChangeProps): ReactElement { + const { t } = useTranslation(); + const location = useLocation(); + const isEditTicket = location.pathname.indexOf('edit-ticket') !== -1; + const individual: AllIndividualsQuery['allIndividuals']['edges'][number]['node'] = + values.selectedIndividual; + const { data: editPeopleFieldsData, loading: editPeopleFieldsLoading } = + useAllEditPeopleFieldsQuery(); + + const [ + getIndividual, + { data: fullIndividual, loading: fullIndividualLoading }, + ] = useIndividualLazyQuery({ variables: { id: individual?.id } }); + + useEffect(() => { + if (individual) { + getIndividual(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [values.selectedIndividual]); + + useEffect(() => { + if ( + !values.individualDataUpdateFields || + values.individualDataUpdateFields.length === 0 + ) { + setFieldValue('individualDataUpdateFields', [ + { fieldName: null, fieldValue: null }, + ]); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + if (!individual) { + return
{t('You have to select an individual earlier')}
; + } + if ( + editPeopleFieldsLoading || + fullIndividualLoading || + editPeopleFieldsLoading || + !fullIndividual + ) { + return ; + } + const notAvailableItems = (values.individualDataUpdateFields || []).map( + (fieldItem) => fieldItem.fieldName, + ); + + return ( + <> + {!isEditTicket && ( + + + <Typography variant="h6">{t('Bio Data')}</Typography> + + + ( + <> + {values.individualDataUpdateFields.map((item, index) => ( + arrayHelpers.remove(index)} + values={values} + /> + ))} + + + + + )} + /> + + + )} + + ); +} diff --git a/src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChangeField.tsx b/src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChangeField.tsx new file mode 100644 index 0000000000..08f89f2191 --- /dev/null +++ b/src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChangeField.tsx @@ -0,0 +1,110 @@ +import { Grid } from '@mui/material'; +import { Field } from 'formik'; +import { useLocation } from 'react-router-dom'; +import CalendarTodayRoundedIcon from '@mui/icons-material/CalendarTodayRounded'; +import { FormikDateField } from '@shared/Formik/FormikDateField'; +import { FormikDecimalField } from '@shared/Formik/FormikDecimalField'; +import { FormikFileField } from '@shared/Formik/FormikFileField'; +import { FormikSelectField } from '@shared/Formik/FormikSelectField'; +import { FormikTextField } from '@shared/Formik/FormikTextField'; +import { AllAddIndividualFieldsQuery } from '@generated/graphql'; +import { FormikBoolFieldGrievances } from '../FormikBoolFieldGrievances'; +import { GrievanceFlexFieldPhotoModalEditable } from '../GrievancesPhotoModals/GrievanceFlexFieldPhotoModalEditable'; +import { ReactElement } from 'react'; + +export interface EditPeopleDataChangeFieldProps { + field: AllAddIndividualFieldsQuery['allAddIndividualsFieldsAttributes'][number]; + name: string; +} +export const EditPeopleDataChangeField = ({ + name, + field, +}: EditPeopleDataChangeFieldProps): ReactElement => { + const location = useLocation(); + const isNewTicket = location.pathname.indexOf('new-ticket') !== -1; + const isEditTicket = location.pathname.indexOf('edit-ticket') !== -1; + + let fieldProps; + if (!field) return null; + + switch (field.type) { + case 'DECIMAL': + fieldProps = { + fullWidth: true, + component: FormikDecimalField, + }; + break; + case 'INTEGER': + fieldProps = { + component: FormikTextField, + type: 'number', + }; + break; + case 'STRING': + fieldProps = { + fullWidth: true, + component: FormikTextField, + }; + break; + case 'SELECT_ONE': + fieldProps = { + choices: field.choices, + fullWidth: true, + component: FormikSelectField, + }; + break; + case 'SELECT_MANY': + fieldProps = { + choices: field.choices, + fullWidth: true, + component: FormikSelectField, + multiple: true, + }; + break; + case 'SELECT_MULTIPLE': + fieldProps = { + choices: field.choices, + fullWidth: true, + component: FormikSelectField, + }; + break; + case 'DATE': + fieldProps = { + component: FormikDateField, + fullWidth: true, + decoratorEnd: , + }; + break; + + case 'BOOL': + fieldProps = { + component: FormikBoolFieldGrievances, + required: field.required, + }; + break; + case 'IMAGE': + fieldProps = { + component: isNewTicket + ? FormikFileField + : GrievanceFlexFieldPhotoModalEditable, + flexField: field, + isIndividual: true, + }; + break; + default: + fieldProps = {}; + } + return ( + + + + ); +}; diff --git a/src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChangeFieldRow.tsx b/src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChangeFieldRow.tsx new file mode 100644 index 0000000000..d8c2465899 --- /dev/null +++ b/src/frontend/src/components/grievances/EditPeopleDataChange/EditPeopleDataChangeFieldRow.tsx @@ -0,0 +1,93 @@ +import { Grid, IconButton } from '@mui/material'; +import camelCase from 'lodash/camelCase'; +import { Delete } from '@mui/icons-material'; +import { useLocation } from 'react-router-dom'; +import { useField, Field } from 'formik'; +import { ReactElement, useEffect } from 'react'; +import { FormikSelectField } from '@shared/Formik/FormikSelectField'; +import { + AllAddIndividualFieldsQuery, + IndividualQuery, +} from '@generated/graphql'; +import { EditPeopleDataChangeField } from './EditPeopleDataChangeField'; +import { CurrentValue } from './CurrentValue'; + +export interface EditPeopleDataChangeFieldRowProps { + fields: AllAddIndividualFieldsQuery['allAddIndividualsFieldsAttributes']; + individual: IndividualQuery['individual']; + itemValue: { fieldName: string; fieldValue: string | number | Date }; + index: number; + notAvailableFields: string[]; + onDelete: () => void; + values; +} +export const EditPeopleDataChangeFieldRow = ({ + fields, + individual, + index, + itemValue, + notAvailableFields, + onDelete, + values, +}: EditPeopleDataChangeFieldRowProps): ReactElement => { + const location = useLocation(); + const isEditTicket = location.pathname.indexOf('edit-ticket') !== -1; + const field = fields.find((item) => item.name === itemValue.fieldName); + const [, , helpers] = useField( + `individualDataUpdateFields[${index}].isFlexField`, + ); + useEffect(() => { + helpers.setValue(field?.isFlexField); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [itemValue.fieldName]); + return ( + + + + !notAvailableFields.includes(item.name) || + item.name === itemValue?.fieldName, + ) + .map((item) => ({ + value: item.name, + name: item.labelEn, + }))} + /> + + + + {itemValue.fieldName ? ( + + ) : ( + + )} + {itemValue.fieldName && ( + + + + + + )} + + ); +}; diff --git a/src/frontend/src/components/grievances/FormikBoolFieldGrievances.tsx b/src/frontend/src/components/grievances/FormikBoolFieldGrievances.tsx index 2d04a4ecb6..0dded62585 100644 --- a/src/frontend/src/components/grievances/FormikBoolFieldGrievances.tsx +++ b/src/frontend/src/components/grievances/FormikBoolFieldGrievances.tsx @@ -64,6 +64,7 @@ export function FormikBoolFieldGrievances({ required={required} variant="outlined" fullWidth + size="small" {...otherProps} > diff --git a/src/frontend/src/components/grievances/LookUps/LookUpIndividualTable/LookUpIndividualTable.tsx b/src/frontend/src/components/grievances/LookUps/LookUpIndividualTable/LookUpIndividualTable.tsx index e20bf7a230..da6c4b269a 100644 --- a/src/frontend/src/components/grievances/LookUps/LookUpIndividualTable/LookUpIndividualTable.tsx +++ b/src/frontend/src/components/grievances/LookUps/LookUpIndividualTable/LookUpIndividualTable.tsx @@ -55,7 +55,7 @@ export function LookUpIndividualTable({ const handleRadioChange = (individual): void => { setSelectedIndividual(individual); - if (individual.household && !isSocialDctType) { + if (individual.household) { setSelectedHousehold(individual.household); setFieldValue('selectedHousehold', individual.household); } diff --git a/src/frontend/src/components/grievances/RequestedHouseholdDataChangeTable/householdDataRow.tsx b/src/frontend/src/components/grievances/RequestedHouseholdDataChangeTable/householdDataRow.tsx index 36382dc14e..789dba285b 100644 --- a/src/frontend/src/components/grievances/RequestedHouseholdDataChangeTable/householdDataRow.tsx +++ b/src/frontend/src/components/grievances/RequestedHouseholdDataChangeTable/householdDataRow.tsx @@ -36,7 +36,9 @@ export const householdDataRow = ( }; const previousValue = - fieldName === 'country' || fieldName === 'country_origin' + fieldName === 'country' || + fieldName === 'country_origin' || + fieldName === 'countryOrigin' ? countriesDict[valueDetails.previousValue] : valueDetails.previousValue; diff --git a/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/EntriesTable.tsx b/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/EntriesTable.tsx index 77c04efc52..4f5252b732 100644 --- a/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/EntriesTable.tsx +++ b/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/EntriesTable.tsx @@ -22,6 +22,7 @@ export interface EntriesTableProps { values; isEdit; fieldsDict; + countriesDict; ticket: GrievanceTicketQuery['grievanceTicket']; entries; entriesFlexFields; @@ -32,6 +33,7 @@ export function EntriesTable({ values, isEdit, fieldsDict, + countriesDict, ticket, entries, entriesFlexFields, @@ -83,6 +85,7 @@ export function EntriesTable({ index, ticket, fieldsDict, + countriesDict, isEdit, handleSelectBioData, ), @@ -94,6 +97,7 @@ export function EntriesTable({ index, ticket, fieldsDict, + countriesDict, isEdit, handleFlexFields, ), diff --git a/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/RequestedIndividualDataChangeTable.tsx b/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/RequestedIndividualDataChangeTable.tsx index 3b4a198f86..fdf1ea499d 100644 --- a/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/RequestedIndividualDataChangeTable.tsx +++ b/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/RequestedIndividualDataChangeTable.tsx @@ -91,6 +91,7 @@ export function RequestedIndividualDataChangeTable({ values={values} isEdit={isEdit} fieldsDict={fieldsDict} + countriesDict={countriesDict} ticket={ticket} entries={entries} entriesFlexFields={entriesFlexFields} diff --git a/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/individualDataRow.tsx b/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/individualDataRow.tsx index d1d74669a1..acb21199b9 100644 --- a/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/individualDataRow.tsx +++ b/src/frontend/src/components/grievances/RequestedIndividualDataChangeTable/individualDataRow.tsx @@ -22,6 +22,7 @@ export const individualDataRow = ( index, ticket, fieldsDict, + countriesDict, isEdit, handleSelectBioData, ): ReactElement => { @@ -34,15 +35,37 @@ export const individualDataRow = ( approveStatus: boolean; }; const field = fieldsDict[row[0]]; + + const isCountryFieldName = + fieldName === 'country' || + fieldName === 'country_origin' || + fieldName === 'countryOrigin'; + + const previousValue = isCountryFieldName + ? countriesDict[valueDetails.previousValue] + : valueDetails.previousValue; + const individualValue = field?.isFlexField ? ticket.individualDataUpdateTicketDetails?.individual?.flexFields[row[0]] - : ticket.individualDataUpdateTicketDetails?.individual[ - camelCase(fieldName) - ]; + : isCountryFieldName + ? countriesDict[ + ticket.individualDataUpdateTicketDetails?.individual[ + camelCase(fieldName) + ] + ] + : ticket.individualDataUpdateTicketDetails?.individual[ + camelCase(fieldName) + ]; + const currentValue = ticket.status === GRIEVANCE_TICKET_STATES.CLOSED - ? valueDetails.previousValue + ? previousValue : individualValue; + + const newValue = isCountryFieldName + ? countriesDict[valueDetails.value] + : valueDetails.value; + return ( @@ -76,7 +99,7 @@ export const individualDataRow = ( - + ); diff --git a/src/frontend/src/components/grievances/utils/validateGrievance.ts b/src/frontend/src/components/grievances/utils/validateGrievance.ts index 6e14b01694..eb3063cc1c 100644 --- a/src/frontend/src/components/grievances/utils/validateGrievance.ts +++ b/src/frontend/src/components/grievances/utils/validateGrievance.ts @@ -240,7 +240,6 @@ export function validateUsingSteps( householdFieldsDict, activeStep, setValidateData, - isSocialDctType, ) { const category = values.category?.toString(); const issueType = values.issueType?.toString(); @@ -469,8 +468,7 @@ export function validateUsingSteps( if ( activeStep === GrievanceSteps.Lookup && !values.selectedHousehold && - householdRequiredGrievanceTypes.includes(values.issueType) && - !isSocialDctType + householdRequiredGrievanceTypes.includes(values.issueType) ) { errors.selectedHousehold = 'Household is Required'; } @@ -483,9 +481,9 @@ export function validateUsingSteps( GRIEVANCE_ISSUE_TYPES.EDIT_HOUSEHOLD, GRIEVANCE_ISSUE_TYPES.DELETE_HOUSEHOLD, ]; - const isHouseholdRequired = - householdRequiredIssueTypes.includes(values.issueType) && - !isSocialDctType; + const isHouseholdRequired = householdRequiredIssueTypes.includes( + values.issueType, + ); const isIndividualRequired = individualRequiredIssueTypes.includes( values.issueType, ); diff --git a/src/frontend/src/containers/pages/grievances/CreateGrievancePage.tsx b/src/frontend/src/containers/pages/grievances/CreateGrievancePage.tsx index 6a28e6c442..b42c8df4c3 100644 --- a/src/frontend/src/containers/pages/grievances/CreateGrievancePage.tsx +++ b/src/frontend/src/containers/pages/grievances/CreateGrievancePage.tsx @@ -53,6 +53,7 @@ import { } from '../../../config/permissions'; import { useProgramContext } from 'src/programContext'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; +import { EditPeopleDataChange } from '@components/grievances/EditPeopleDataChange/EditPeopleDataChange'; const InnerBoxPadding = styled.div` .MuiPaper-root { @@ -72,13 +73,6 @@ const BoxWithBorders = styled.div` function EmptyComponent(): ReactElement { return null; } -export const dataChangeComponentDict = { - [GRIEVANCE_CATEGORIES.DATA_CHANGE]: { - [GRIEVANCE_ISSUE_TYPES.ADD_INDIVIDUAL]: AddIndividualDataChange, - [GRIEVANCE_ISSUE_TYPES.EDIT_INDIVIDUAL]: EditIndividualDataChange, - [GRIEVANCE_ISSUE_TYPES.EDIT_HOUSEHOLD]: EditHouseholdDataChange, - }, -}; export const CreateGrievancePage = (): ReactElement => { const navigate = useNavigate(); @@ -92,6 +86,16 @@ export const CreateGrievancePage = (): ReactElement => { const [activeStep, setActiveStep] = useState(GrievanceSteps.Selection); const [validateData, setValidateData] = useState(false); + const dataChangeComponentDict = { + [GRIEVANCE_CATEGORIES.DATA_CHANGE]: { + [GRIEVANCE_ISSUE_TYPES.ADD_INDIVIDUAL]: AddIndividualDataChange, + [GRIEVANCE_ISSUE_TYPES.EDIT_INDIVIDUAL]: isSocialDctType + ? EditPeopleDataChange + : EditIndividualDataChange, + [GRIEVANCE_ISSUE_TYPES.EDIT_HOUSEHOLD]: EditHouseholdDataChange, + }, + }; + const linkedTicketId = location.state?.linkedTicketId; const selectedHousehold = location.state?.selectedHousehold; const selectedIndividual = location.state?.selectedIndividual; @@ -167,9 +171,9 @@ export const CreateGrievancePage = (): ReactElement => { '*', ); - const householdFieldsDictByDctType = isSocialDctType + const individualFieldsDictForValidation = isSocialDctType ? peopleFieldsDict - : householdFieldsDict; + : individualFieldsDict; const showIssueType = (values): boolean => values.category === GRIEVANCE_CATEGORIES.SENSITIVE_GRIEVANCE || @@ -309,11 +313,10 @@ export const CreateGrievancePage = (): ReactElement => { validateUsingSteps( values, allAddIndividualFieldsData, - individualFieldsDict, - householdFieldsDictByDctType, + individualFieldsDictForValidation, + householdFieldsDict, activeStep, setValidateData, - isSocialDctType, ) } validationSchema={validationSchemaWithSteps(activeStep)} diff --git a/src/frontend/src/containers/pages/grievances/EditGrievancePage.tsx b/src/frontend/src/containers/pages/grievances/EditGrievancePage.tsx index 8e8690a8e5..ecb4b4d389 100644 --- a/src/frontend/src/containers/pages/grievances/EditGrievancePage.tsx +++ b/src/frontend/src/containers/pages/grievances/EditGrievancePage.tsx @@ -7,6 +7,7 @@ import { GrievanceTicketDocument, useAllAddIndividualFieldsQuery, useAllEditHouseholdFieldsQuery, + useAllEditPeopleFieldsQuery, useAllProgramsForChoicesQuery, useGrievanceTicketQuery, useGrievanceTicketStatusChangeMutation, @@ -72,6 +73,7 @@ import { import { grievancePermissions } from './GrievancesDetailsPage/grievancePermissions'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; import { ReactElement } from 'react'; +import { useProgramContext } from 'src/programContext'; const BoxPadding = styled.div` padding: 15px 0; @@ -90,6 +92,7 @@ export const EditGrievancePage = (): ReactElement => { const location = useLocation(); const { t } = useTranslation(); const { baseUrl, businessArea, isAllPrograms } = useBaseUrl(); + const { isSocialDctType } = useProgramContext(); const permissions = usePermissions(); const { showMessage } = useSnackbar(); const { id } = useParams(); @@ -119,6 +122,9 @@ export const EditGrievancePage = (): ReactElement => { } = useAllAddIndividualFieldsQuery(); const { data: householdFieldsData, loading: householdFieldsLoading } = useAllEditHouseholdFieldsQuery(); + const { data: allEditPeopleFieldsData, loading: allEditPeopleFieldsLoading } = + useAllEditPeopleFieldsQuery(); + const { data: programsData, loading: programsDataLoading } = useAllProgramsForChoicesQuery({ variables: { @@ -137,10 +143,17 @@ export const EditGrievancePage = (): ReactElement => { '*', ); + const peopleFieldsDict = useArrayToDict( + allEditPeopleFieldsData?.allEditPeopleFieldsAttributes, + 'name', + '*', + ); + if ( choicesLoading || ticketLoading || allAddIndividualFieldsDataLoading || + allEditPeopleFieldsLoading || householdFieldsLoading || currentUserDataLoading || programsDataLoading @@ -155,6 +168,7 @@ export const EditGrievancePage = (): ReactElement => { !householdFieldsData || !householdFieldsDict || !individualFieldsDict || + !peopleFieldsDict || !programsData ) return null; @@ -216,6 +230,7 @@ export const EditGrievancePage = (): ReactElement => { 'individualDataUpdateIdentitiesToEdit', 'individualDataUpdateFieldsPaymentChannels', 'individualDataUpdatePaymentChannelsToEdit', + 'peopleDataUpdateFields', ].map( (fieldname) => isInvalid(fieldname, errors, touched) && ( @@ -253,6 +268,10 @@ export const EditGrievancePage = (): ReactElement => { ticket?.individualDataUpdateTicketDetails?.individualData ?.delivery_mechanism_data_to_edit; + const individualFieldsDictForValidation = isSocialDctType + ? peopleFieldsDict + : individualFieldsDict; + return ( { validate( values, allAddIndividualFieldsData, - individualFieldsDict, + individualFieldsDictForValidation, householdFieldsDict, ) } diff --git a/src/hct_mis_api/apps/core/field_attributes/core_fields_attributes.py b/src/hct_mis_api/apps/core/field_attributes/core_fields_attributes.py index 75b37f5d0e..53ff45df98 100644 --- a/src/hct_mis_api/apps/core/field_attributes/core_fields_attributes.py +++ b/src/hct_mis_api/apps/core/field_attributes/core_fields_attributes.py @@ -366,8 +366,6 @@ Scope.TARGETING, Scope.KOBO_IMPORT, Scope.HOUSEHOLD_UPDATE, - Scope.XLSX_PEOPLE, - Scope.PEOPLE_UPDATE, ], }, { diff --git a/src/hct_mis_api/apps/grievance/inputs.py b/src/hct_mis_api/apps/grievance/inputs.py index d2bfb9b463..0289b98a15 100644 --- a/src/hct_mis_api/apps/grievance/inputs.py +++ b/src/hct_mis_api/apps/grievance/inputs.py @@ -211,6 +211,19 @@ class IndividualUpdateDataObjectType(graphene.InputObjectType): delivery_mechanism_data = graphene.List(DeliveryMechanismDataObjectType) delivery_mechanism_data_to_edit = graphene.List(EditDeliveryMechanismDataObjectType) delivery_mechanism_data_to_remove = graphene.List(graphene.ID) + # people fields + consent = graphene.Boolean(description="People update") + residence_status = graphene.String(description="People update") + country_origin = graphene.String(description="People update") + country = graphene.String(description="People update") + address = graphene.String(description="People update") + village = graphene.String(description="People update") + currency = graphene.String(description="People update") + unhcr_id = graphene.String(description="People update") + name_enumerator = graphene.String(description="People update") + org_enumerator = graphene.String(description="People update") + org_name_enumerator = graphene.String(description="People update") + registration_method = graphene.String(description="People update") class AddIndividualDataObjectType(graphene.InputObjectType): diff --git a/src/hct_mis_api/apps/grievance/schema.py b/src/hct_mis_api/apps/grievance/schema.py index 55cff156d4..6856073b12 100644 --- a/src/hct_mis_api/apps/grievance/schema.py +++ b/src/hct_mis_api/apps/grievance/schema.py @@ -680,7 +680,6 @@ def resolve_all_edit_people_fields_attributes(self, info: Any, **kwargs: Any) -> all_options = list(fields) + list( FlexibleAttribute.objects.filter( associated_with__in=[ - FlexibleAttribute.ASSOCIATED_WITH_HOUSEHOLD, FlexibleAttribute.ASSOCIATED_WITH_INDIVIDUAL, ] ).prefetch_related("choices") diff --git a/src/hct_mis_api/apps/grievance/services/data_change/individual_data_update_service.py b/src/hct_mis_api/apps/grievance/services/data_change/individual_data_update_service.py index eb7d415989..395eac7681 100644 --- a/src/hct_mis_api/apps/grievance/services/data_change/individual_data_update_service.py +++ b/src/hct_mis_api/apps/grievance/services/data_change/individual_data_update_service.py @@ -12,6 +12,7 @@ from hct_mis_api.apps.activity_log.models import log_create from hct_mis_api.apps.activity_log.utils import copy_model_object from hct_mis_api.apps.core.utils import decode_id_string, to_snake_case +from hct_mis_api.apps.geo.models import Country from hct_mis_api.apps.grievance.celery_tasks import ( deduplicate_and_check_against_sanctions_list_task, ) @@ -358,7 +359,33 @@ def close(self, user: AbstractUser) -> None: only_approved_data["phone_no_alternative_valid"] = is_valid_phone_number( only_approved_data["phone_no_alternative"] ) - + # people update + hh_fields = [ + "consent", + "residence_status", + "country_origin", + "country", + "address", + "village", + "currency", + "unhcr_id", + "name_enumerator", + "org_enumerator", + "org_name_enumerator", + "registration_method", + ] + # move HH fields from only_approved_data into hh_approved_data + hh_approved_data = {hh_f: only_approved_data.pop(hh_f) for hh_f in hh_fields if hh_f in only_approved_data} + if hh_approved_data: + hh_country_origin = hh_approved_data.get("country_origin") + hh_country = hh_approved_data.get("country") + if hh_country_origin is not None: + hh_approved_data["country_origin"] = Country.objects.filter(iso_code3=hh_country_origin).first() + if hh_country is not None: + hh_approved_data["country"] = Country.objects.filter(iso_code3=hh_country).first() + # people update HH + Household.objects.filter(id=household.id).update(**hh_approved_data, updated_at=timezone.now()) + # upd Individual Individual.objects.filter(id=new_individual.id).update( flex_fields=merged_flex_fields, **only_approved_data, updated_at=timezone.now() ) diff --git a/tests/unit/apps/grievance/services/data_change/test_update_individual_data_service.py b/tests/unit/apps/grievance/services/data_change/test_update_individual_data_service.py index 0b4cb20a6b..3e65e62fdb 100644 --- a/tests/unit/apps/grievance/services/data_change/test_update_individual_data_service.py +++ b/tests/unit/apps/grievance/services/data_change/test_update_individual_data_service.py @@ -6,6 +6,7 @@ from hct_mis_api.apps.account.fixtures import BusinessAreaFactory, UserFactory from hct_mis_api.apps.core.utils import encode_id_base64 from hct_mis_api.apps.geo.fixtures import CountryFactory +from hct_mis_api.apps.geo.models import Country from hct_mis_api.apps.grievance.fixtures import TicketIndividualDataUpdateDetailsFactory from hct_mis_api.apps.grievance.services.data_change.individual_data_update_service import ( IndividualDataUpdateService, @@ -30,10 +31,13 @@ def setUpTestData(cls) -> None: cls.business_area = BusinessAreaFactory() cls.program = ProgramFactory() cls.country_afg = CountryFactory(iso_code3="AFG") + cls.user = UserFactory() - household, _ = create_household({"program": cls.program}) + cls.household, _ = create_household({"program": cls.program}) - cls.individual = IndividualFactory(household=household, business_area=cls.business_area, program=cls.program) + cls.individual = IndividualFactory( + household=cls.household, business_area=cls.business_area, program=cls.program + ) cls.document_type_unique_for_individual = DocumentTypeFactory( unique_for_individual=True, key="unique", label="Unique" @@ -79,7 +83,7 @@ def test_add_document_of_same_type_not_unique_per_individual_valid(self) -> None service = IndividualDataUpdateService(self.ticket, self.ticket.individual_data_update_ticket_details) try: - service.close(UserFactory()) + service.close(self.user) except ValidationError: self.fail("ValidationError should not be raised") @@ -109,7 +113,7 @@ def test_add_document_of_same_type_not_unique_per_individual_pending(self) -> No service = IndividualDataUpdateService(self.ticket, self.ticket.individual_data_update_ticket_details) try: - service.close(UserFactory()) + service.close(self.user) except ValidationError: self.fail("ValidationError should not be raised") @@ -139,7 +143,7 @@ def test_add_document_of_same_type_unique_per_individual_valid(self) -> None: service = IndividualDataUpdateService(self.ticket, self.ticket.individual_data_update_ticket_details) with self.assertRaises(ValidationError) as e: - service.close(UserFactory()) + service.close(self.user) self.assertEqual( f"Document of type {self.document_type_unique_for_individual} already exists for this individual", e.exception.message, @@ -171,7 +175,7 @@ def test_add_document_of_same_type_unique_per_individual_pending(self) -> None: service = IndividualDataUpdateService(self.ticket, self.ticket.individual_data_update_ticket_details) try: - service.close(UserFactory()) + service.close(self.user) except ValidationError: self.fail("ValidationError should not be raised") @@ -217,7 +221,7 @@ def test_edit_document_of_same_type_unique_per_individual(self) -> None: service = IndividualDataUpdateService(self.ticket, self.ticket.individual_data_update_ticket_details) with self.assertRaises(ValidationError) as e: - service.close(UserFactory()) + service.close(self.user) self.assertEqual( f"Document of type {self.document_type_unique_for_individual} already exists for this individual", e.exception.message, @@ -258,7 +262,7 @@ def test_edit_document_unique_per_individual(self) -> None: service = IndividualDataUpdateService(self.ticket, self.ticket.individual_data_update_ticket_details) try: - service.close(UserFactory()) + service.close(self.user) except ValidationError: self.fail("ValidationError should not be raised") @@ -307,7 +311,7 @@ def test_edit_document_with_data_already_existing_in_same_program(self) -> None: self.ticket.individual_data_update_ticket_details.save() service = IndividualDataUpdateService(self.ticket, self.ticket.individual_data_update_ticket_details) with self.assertRaises(ValidationError) as e: - service.close(UserFactory()) + service.close(self.user) self.assertEqual( f"Document with number {existing_document.document_number} of type {self.document_type_unique_for_individual} already exists", e.exception.message, @@ -316,3 +320,57 @@ def test_edit_document_with_data_already_existing_in_same_program(self) -> None: document_to_edit.refresh_from_db() # document was not updated self.assertEqual(document_to_edit.document_number, "111111") + + def test_update_people_individual_hh_fields(self) -> None: + CountryFactory(name="Poland", iso_code3="POL", iso_code2="PL", iso_num="620") + CountryFactory(name="Other Country", short_name="Oth", iso_code2="O", iso_code3="OTH", iso_num="111") + hh_fields = [ + "consent", + "residence_status", + "country_origin", + "country", + "address", + "village", + "currency", + "unhcr_id", + "name_enumerator", + "org_enumerator", + "org_name_enumerator", + "registration_method", + ] + hh = self.household + ind_data = {} + new_data = { + "address": "Test Address ABC", + "country_origin": "POL", + "country": "OTH", + "residence_status": "HOST", + "village": "El Paso", + "consent": None, + "currency": "PLN", + "unhcr_id": "random_unhcr_id_123", + "name_enumerator": "test_name", + "org_enumerator": "test_org", + "org_name_enumerator": "test_org_name", + "registration_method": "COMMUNITY", + } + for hh_field in hh_fields: + ind_data[hh_field] = { + "value": new_data.get(hh_field), + "approve_status": True, + "previous_value": getattr(hh, hh_field).iso_code3 + if isinstance(getattr(hh, hh_field), Country) + else getattr(hh, hh_field), + } + self.ticket.individual_data_update_ticket_details.individual_data = ind_data + self.ticket.individual_data_update_ticket_details.save() + + service = IndividualDataUpdateService(self.ticket, self.ticket.individual_data_update_ticket_details) + service.close(self.user) + + hh.refresh_from_db() + for hh_field in hh_fields: + hh_value = ( + getattr(hh, hh_field).iso_code3 if isinstance(getattr(hh, hh_field), Country) else getattr(hh, hh_field) + ) + self.assertEqual(hh_value, new_data.get(hh_field)) diff --git a/tests/unit/apps/registration_data/test_template_file_generator.py b/tests/unit/apps/registration_data/test_template_file_generator.py index 65d07fd0f8..74286ac3f6 100644 --- a/tests/unit/apps/registration_data/test_template_file_generator.py +++ b/tests/unit/apps/registration_data/test_template_file_generator.py @@ -83,19 +83,19 @@ def test_add_template_columns(self) -> None: self.assertEqual("pp_admin3_i_c", people_rows[0][10]) self.assertEqual("Social Worker resides in which admin3? - SELECT_ONE", people_rows[1][10]) - self.assertEqual("pp_middle_name_i_c", people_rows[0][20]) - self.assertEqual("Middle name(s) - STRING", people_rows[1][20]) + self.assertEqual("pp_middle_name_i_c", people_rows[0][19]) + self.assertEqual("Middle name(s) - STRING", people_rows[1][19]) - self.assertEqual("pp_drivers_license_issuer_i_c", people_rows[0][40]) - self.assertEqual("Issuing country of driver's license - SELECT_ONE", people_rows[1][40]) + self.assertEqual("pp_drivers_license_issuer_i_c", people_rows[0][39]) + self.assertEqual("Issuing country of driver's license - SELECT_ONE", people_rows[1][39]) - self.assertEqual("pp_village_i_c", people_rows[0][70]) - self.assertEqual("Village - STRING", people_rows[1][70]) + self.assertEqual("pp_village_i_c", people_rows[0][69]) + self.assertEqual("Village - STRING", people_rows[1][69]) - self.assertEqual("pp_bank_branch_name_i_c", people_rows[0][86]) + self.assertEqual("pp_bank_branch_name_i_c", people_rows[0][85]) - self.assertEqual("pp_index_id", people_rows[0][84]) - self.assertEqual("Index ID - INTEGER - required", people_rows[1][84]) + self.assertEqual("pp_index_id", people_rows[0][83]) + self.assertEqual("Index ID - INTEGER - required", people_rows[1][83]) self.assertIn("pp_wallet_name__transfer_to_digital_wallet_i_c", people_rows[0]) self.assertIn( From 186062072a922be9ba69ba39f9d8b457313c5133 Mon Sep 17 00:00:00 2001 From: Pavlo Mokiichuk Date: Fri, 6 Dec 2024 13:54:18 +0100 Subject: [PATCH 02/11] STG // Fix RDI import dmd json field (#4504) (#4505) * fix rdi import dmd data json * add migration * fix test * imports * more fixes Co-authored-by: Domenico --- .../core/management/commands/generateroles.py | 19 +++++++++++++++ .../apps/core/management/commands/upgrade.py | 1 - src/hct_mis_api/apps/household/schema.py | 2 +- .../apps/payment/migrations/0006_migration.py | 24 +++++++++++++++++++ .../apps/payment/models/payment.py | 10 +++----- .../tasks/rdi_base_create.py | 5 +--- src/hct_mis_api/migrations_script/main.py | 1 + .../test_rdi_kobo_create.py | 2 +- .../test_rdi_people_create.py | 7 +++--- .../test_rdi_xlsx_create.py | 7 +++--- 10 files changed, 56 insertions(+), 22 deletions(-) create mode 100644 src/hct_mis_api/apps/payment/migrations/0006_migration.py diff --git a/src/hct_mis_api/apps/core/management/commands/generateroles.py b/src/hct_mis_api/apps/core/management/commands/generateroles.py index df7cd861e3..053cd66a33 100644 --- a/src/hct_mis_api/apps/core/management/commands/generateroles.py +++ b/src/hct_mis_api/apps/core/management/commands/generateroles.py @@ -74,11 +74,22 @@ def handle(self, *args: Any, **options: Any) -> None: Permissions.PAYMENT_VERIFICATION_INVALID, Permissions.PAYMENT_VERIFICATION_DELETE, Permissions.PAYMENT_VERIFICATION_VERIFY, + Permissions.PAYMENT_VERIFICATION_EXPORT, + Permissions.PAYMENT_VERIFICATION_IMPORT, Permissions.PAYMENT_VERIFICATION_VIEW_PAYMENT_RECORD_DETAILS, Permissions.REPORTING_EXPORT, Permissions.USER_MANAGEMENT_VIEW_LIST, Permissions.ACTIVITY_LOG_VIEW, Permissions.ACTIVITY_LOG_DOWNLOAD, + Permissions.PDU_VIEW_LIST_AND_DETAILS, + Permissions.PDU_TEMPLATE_CREATE, + Permissions.PDU_TEMPLATE_DOWNLOAD, + Permissions.PDU_UPLOAD, + Permissions.PM_PROGRAMME_CYCLE_CREATE, + Permissions.PM_PROGRAMME_CYCLE_UPDATE, + Permissions.PM_PROGRAMME_CYCLE_DELETE, + Permissions.PM_PROGRAMME_CYCLE_VIEW_DETAILS, + Permissions.PM_PROGRAMME_CYCLE_VIEW_LIST, ], }, { @@ -121,6 +132,8 @@ def handle(self, *args: Any, **options: Any) -> None: Permissions.USER_MANAGEMENT_VIEW_LIST, Permissions.ACTIVITY_LOG_VIEW, Permissions.ACTIVITY_LOG_DOWNLOAD, + Permissions.PM_PROGRAMME_CYCLE_VIEW_DETAILS, + Permissions.PM_PROGRAMME_CYCLE_VIEW_LIST, ], }, { @@ -184,6 +197,12 @@ def handle(self, *args: Any, **options: Any) -> None: Permissions.GRIEVANCES_ADD_NOTE, Permissions.GRIEVANCES_UPDATE_AS_CREATOR, Permissions.GRIEVANCES_UPDATE_AS_OWNER, + Permissions.GRIEVANCE_DOCUMENTS_UPLOAD, + Permissions.GRIEVANCES_FEEDBACK_MESSAGE_VIEW_CREATE, + Permissions.GRIEVANCES_FEEDBACK_VIEW_CREATE, + Permissions.GRIEVANCES_FEEDBACK_VIEW_DETAILS, + Permissions.GRIEVANCES_FEEDBACK_VIEW_LIST, + Permissions.GRIEVANCES_FEEDBACK_VIEW_UPDATE, ], }, { diff --git a/src/hct_mis_api/apps/core/management/commands/upgrade.py b/src/hct_mis_api/apps/core/management/commands/upgrade.py index 29bd9c9010..43f2cc670f 100644 --- a/src/hct_mis_api/apps/core/management/commands/upgrade.py +++ b/src/hct_mis_api/apps/core/management/commands/upgrade.py @@ -7,7 +7,6 @@ class Command(BaseCommand): def handle(self, *args: Any, **options: Any) -> None: call_command("migratealldb") call_command("collectstatic", "--no-default-ignore", interactive=False) - call_command("generateroles") from adminactions.perms import create_extra_permissions create_extra_permissions() diff --git a/src/hct_mis_api/apps/household/schema.py b/src/hct_mis_api/apps/household/schema.py index fe8980214d..95652bc139 100644 --- a/src/hct_mis_api/apps/household/schema.py +++ b/src/hct_mis_api/apps/household/schema.py @@ -217,7 +217,7 @@ def resolve_name(self, info: Any) -> str: return self.delivery_mechanism.name def resolve_individual_tab_data(self, info: Any) -> dict: - return {key: self._data.get(key, None) for key in self.all_dm_fields} + return {key: self.data.get(key, None) for key in self.all_dm_fields} class Meta: model = DeliveryMechanismData diff --git a/src/hct_mis_api/apps/payment/migrations/0006_migration.py b/src/hct_mis_api/apps/payment/migrations/0006_migration.py new file mode 100644 index 0000000000..2dd4bb97f1 --- /dev/null +++ b/src/hct_mis_api/apps/payment/migrations/0006_migration.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.25 on 2024-12-04 11:03 + +import django.core.serializers.json +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0005_migration'), + ] + + operations = [ + migrations.AlterField( + model_name='deliverymechanismdata', + name='data', + field=models.JSONField(blank=True, default=dict, encoder=django.core.serializers.json.DjangoJSONEncoder), + ), + migrations.AlterField( + model_name='paymentverificationplan', + name='sex_filter', + field=models.CharField(blank=True, max_length=10, null=True), + ), + ] diff --git a/src/hct_mis_api/apps/payment/models/payment.py b/src/hct_mis_api/apps/payment/models/payment.py index 6e9cfd303f..fee7acd602 100644 --- a/src/hct_mis_api/apps/payment/models/payment.py +++ b/src/hct_mis_api/apps/payment/models/payment.py @@ -1,5 +1,4 @@ import hashlib -import json import logging import uuid from collections import defaultdict @@ -14,6 +13,7 @@ from django.contrib.admin.widgets import FilteredSelectMultiple from django.contrib.postgres.fields import ArrayField from django.core.exceptions import ValidationError +from django.core.serializers.json import DjangoJSONEncoder from django.core.validators import ( MaxLengthValidator, MaxValueValidator, @@ -1608,7 +1608,7 @@ class DeliveryMechanismData(MergeStatusModel, TimeStampedUUIDModel, SignatureMix "household.Individual", on_delete=models.CASCADE, related_name="delivery_mechanisms_data" ) delivery_mechanism = models.ForeignKey("payment.DeliveryMechanism", on_delete=models.PROTECT) - data = JSONField(default=dict, blank=True) + data = JSONField(default=dict, blank=True, encoder=DjangoJSONEncoder) is_valid: bool = models.BooleanField(default=False) # type: ignore validation_errors: dict = JSONField(default=dict) # type: ignore @@ -1650,14 +1650,10 @@ def get_associated_object(self, associated_with: str) -> Any: associated_objects = { _INDIVIDUAL: self.individual, _HOUSEHOLD: self.individual.household, - _DELIVERY_MECHANISM_DATA: self._data, + _DELIVERY_MECHANISM_DATA: self.data, } return associated_objects.get(associated_with) - @property - def _data(self) -> Dict: - return json.loads(self.data) if not isinstance(self.data, dict) else self.data - @cached_property def delivery_data(self) -> Dict: delivery_data = {} diff --git a/src/hct_mis_api/apps/registration_datahub/tasks/rdi_base_create.py b/src/hct_mis_api/apps/registration_datahub/tasks/rdi_base_create.py index 9418ee6f23..e8b13edc01 100644 --- a/src/hct_mis_api/apps/registration_datahub/tasks/rdi_base_create.py +++ b/src/hct_mis_api/apps/registration_datahub/tasks/rdi_base_create.py @@ -1,11 +1,8 @@ -import json import logging from collections import defaultdict from functools import reduce from typing import Any, List -from django.core.serializers.json import DjangoJSONEncoder - from hct_mis_api.apps.core.utils import ( get_combined_attributes, serialize_flex_attributes, @@ -95,7 +92,7 @@ def _create_delivery_mechanisms_data(self) -> None: PendingDeliveryMechanismData( individual=individual, delivery_mechanism=self.available_delivery_mechanisms[delivery_type], - data=json.dumps(values, cls=DjangoJSONEncoder), + data=values, rdi_merge_status=MergeStatusModel.PENDING, ) ) diff --git a/src/hct_mis_api/migrations_script/main.py b/src/hct_mis_api/migrations_script/main.py index 06f0572595..31b6ff89b1 100644 --- a/src/hct_mis_api/migrations_script/main.py +++ b/src/hct_mis_api/migrations_script/main.py @@ -54,6 +54,7 @@ def apply_migrations(): ("payment", "0003_migration"), ("payment", "0004_migration"), ("payment", "0005_migration"), + ("payment", "0006_migration"), ("aurora", "0003_migration"), ] fake_migrations(excluded_migrations) diff --git a/tests/unit/apps/registration_datahub/test_rdi_kobo_create.py b/tests/unit/apps/registration_datahub/test_rdi_kobo_create.py index 5e87ad5a5f..d806708648 100644 --- a/tests/unit/apps/registration_datahub/test_rdi_kobo_create.py +++ b/tests/unit/apps/registration_datahub/test_rdi_kobo_create.py @@ -168,7 +168,7 @@ def test_execute(self) -> None: dmd = PendingDeliveryMechanismData.objects.get(individual__full_name="Tesa Testowski") self.assertEqual(dmd.delivery_mechanism.code, "mobile_money") self.assertEqual( - json.loads(dmd.data), + dmd.data, { "service_provider_code__mobile_money": "ABC", "delivery_phone_number__mobile_money": "+48880110456", diff --git a/tests/unit/apps/registration_datahub/test_rdi_people_create.py b/tests/unit/apps/registration_datahub/test_rdi_people_create.py index a35e00684d..d17390e5e2 100644 --- a/tests/unit/apps/registration_datahub/test_rdi_people_create.py +++ b/tests/unit/apps/registration_datahub/test_rdi_people_create.py @@ -1,4 +1,3 @@ -import json from datetime import date from io import BytesIO from pathlib import Path @@ -130,11 +129,11 @@ def test_execute(self) -> None: self.assertEqual(dmd2.rdi_merge_status, MergeStatusModel.PENDING) self.assertEqual(dmd3.rdi_merge_status, MergeStatusModel.PENDING) self.assertEqual( - json.loads(dmd1.data), + dmd1.data, {"card_number__atm_card": "164260858", "card_expiry_date__atm_card": "1995-06-03T00:00:00"}, ) self.assertEqual( - json.loads(dmd2.data), + dmd2.data, { "card_number__atm_card": "1975549730", "card_expiry_date__atm_card": "2022-02-17T00:00:00", @@ -142,7 +141,7 @@ def test_execute(self) -> None: }, ) self.assertEqual( - json.loads(dmd3.data), + dmd3.data, { "card_number__atm_card": "870567340", "card_expiry_date__atm_card": "2016-06-27T00:00:00", diff --git a/tests/unit/apps/registration_datahub/test_rdi_xlsx_create.py b/tests/unit/apps/registration_datahub/test_rdi_xlsx_create.py index 55541e5b23..b2eb4b394f 100644 --- a/tests/unit/apps/registration_datahub/test_rdi_xlsx_create.py +++ b/tests/unit/apps/registration_datahub/test_rdi_xlsx_create.py @@ -1,5 +1,4 @@ import datetime -import json from datetime import date from io import BytesIO from pathlib import Path @@ -583,11 +582,11 @@ def test_create_delivery_mechanism_data(self) -> None: self.assertEqual(dmd2.rdi_merge_status, MergeStatusModel.PENDING) self.assertEqual(dmd3.rdi_merge_status, MergeStatusModel.PENDING) self.assertEqual( - json.loads(dmd1.data), + dmd1.data, {"card_number__atm_card": "164260858", "card_expiry_date__atm_card": "1995-06-03T00:00:00"}, ) self.assertEqual( - json.loads(dmd2.data), + dmd2.data, { "card_number__atm_card": "1975549730", "card_expiry_date__atm_card": "2022-02-17T00:00:00", @@ -595,7 +594,7 @@ def test_create_delivery_mechanism_data(self) -> None: }, ) self.assertEqual( - json.loads(dmd3.data), + dmd3.data, { "card_number__atm_card": "870567340", "card_expiry_date__atm_card": "2016-06-27T00:00:00", From a82aa7be30fcdcbdffc82e5bce4276580eb0ac84 Mon Sep 17 00:00:00 2001 From: Pavlo Mokiichuk Date: Mon, 9 Dec 2024 09:52:22 +0100 Subject: [PATCH 03/11] Back merge STG > DEV (#4511) * STG // Fix RDI import dmd json field (#4504) * fix rdi import dmd data json * add migration * fix test * imports * more fixes * remove generateroles from upgrade, update generateroles based on admin history for these roles * [STG] Payment Plan export xlsx: added FSP template doc types list (#4508) * upd get_column_value_from_payment & fsp template doc types * document_types :star: * fix :star2: --------- Co-authored-by: Domenico Co-authored-by: Paulina Kujawa Co-authored-by: Paulina Kujawa <42150286+pkujawa@users.noreply.github.com> --- src/hct_mis_api/apps/household/models.py | 10 +++++++ src/hct_mis_api/apps/payment/admin.py | 2 +- .../apps/payment/migrations/0007_migration.py | 19 +++++++++++++ .../apps/payment/models/payment.py | 28 ++++++++----------- ...lsx_payment_plan_export_per_fsp_service.py | 10 +++++++ src/hct_mis_api/migrations_script/main.py | 1 + ...test_fsp_xlsx_template_get_column_value.py | 8 ++++-- 7 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 src/hct_mis_api/apps/payment/migrations/0007_migration.py diff --git a/src/hct_mis_api/apps/household/models.py b/src/hct_mis_api/apps/household/models.py index 1802857333..ca48ab5733 100644 --- a/src/hct_mis_api/apps/household/models.py +++ b/src/hct_mis_api/apps/household/models.py @@ -699,6 +699,16 @@ class Meta: def __str__(self) -> str: return f"{self.label}" + @classmethod + def get_all_doc_types_choices(cls) -> List[Tuple[str, str]]: + """return list of Document Types choices""" + return [(obj.key, obj.label) for obj in cls.objects.all()] + + @classmethod + def get_all_doc_types(cls) -> List[str]: + """return list of Document Types keys""" + return list(cls.objects.all().only("key").values_list("key", flat=True)) + class Document(AbstractSyncable, SoftDeletableRepresentationMergeStatusModel, TimeStampedUUIDModel): STATUS_PENDING = "PENDING" diff --git a/src/hct_mis_api/apps/payment/admin.py b/src/hct_mis_api/apps/payment/admin.py index efd4bfc814..ab82df38ba 100644 --- a/src/hct_mis_api/apps/payment/admin.py +++ b/src/hct_mis_api/apps/payment/admin.py @@ -279,7 +279,7 @@ class FinancialServiceProviderXlsxTemplateAdmin(HOPEModelAdminBase): ) list_filter = (("created_by", AutoCompleteFilter),) search_fields = ("name",) - fields = ("name", "columns", "core_fields", "flex_fields") + fields = ("name", "columns", "core_fields", "flex_fields", "document_types") def total_selected_columns(self, obj: Any) -> str: return f"{len(obj.columns)} of {len(FinancialServiceProviderXlsxTemplate.COLUMNS_CHOICES)}" diff --git a/src/hct_mis_api/apps/payment/migrations/0007_migration.py b/src/hct_mis_api/apps/payment/migrations/0007_migration.py new file mode 100644 index 0000000000..4bd14071ad --- /dev/null +++ b/src/hct_mis_api/apps/payment/migrations/0007_migration.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.25 on 2024-12-06 13:32 + +from django.db import migrations, models +import hct_mis_api.apps.payment.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0006_migration'), + ] + + operations = [ + migrations.AddField( + model_name='financialserviceproviderxlsxtemplate', + name='document_types', + field=hct_mis_api.apps.payment.fields.DynamicChoiceArrayField(base_field=models.CharField(blank=True, max_length=255), blank=True, default=list, size=None), + ), + ] diff --git a/src/hct_mis_api/apps/payment/models/payment.py b/src/hct_mis_api/apps/payment/models/payment.py index fee7acd602..1b6e4874ae 100644 --- a/src/hct_mis_api/apps/payment/models/payment.py +++ b/src/hct_mis_api/apps/payment/models/payment.py @@ -51,7 +51,7 @@ from hct_mis_api.apps.core.mixins import LimitBusinessAreaModelMixin from hct_mis_api.apps.core.models import FileTemp, FlexibleAttribute from hct_mis_api.apps.geo.models import Area, Country -from hct_mis_api.apps.household.models import FEMALE, MALE, Individual +from hct_mis_api.apps.household.models import FEMALE, MALE, DocumentType, Individual from hct_mis_api.apps.payment.fields import DynamicChoiceArrayField from hct_mis_api.apps.payment.managers import PaymentManager from hct_mis_api.apps.payment.validators import payment_token_and_order_number_validator @@ -974,19 +974,23 @@ class FinancialServiceProviderXlsxTemplate(TimeStampedUUIDModel): verbose_name=_("Columns"), help_text=_("Select the columns to include in the report"), ) - core_fields = DynamicChoiceArrayField( models.CharField(max_length=255, blank=True), choices_callable=FieldFactory.get_all_core_fields_choices, default=list, blank=True, ) - flex_fields = FlexFieldArrayField( models.CharField(max_length=255, blank=True), default=list, blank=True, ) + document_types = DynamicChoiceArrayField( + models.CharField(max_length=255, blank=True), + choices_callable=DocumentType.get_all_doc_types_choices, + default=list, + blank=True, + ) @staticmethod def get_data_from_payment_snapshot( @@ -1124,11 +1128,12 @@ def get_column_value_from_payment(cls, payment: "Payment", column_name: str) -> ), } additional_columns = { - "registration_token": cls.get_registration_token_doc_number, - "national_id": cls.get_national_id_doc_number, "admin_level_2": cls.get_admin_level_2, "alternate_collector_document_numbers": cls.get_alternate_collector_doc_numbers, } + if column_name in DocumentType.get_all_doc_types(): + return cls.get_document_number_by_doc_type_key(snapshot_data, column_name) + if column_name in additional_columns: method = additional_columns[column_name] return method(snapshot_data) @@ -1148,22 +1153,13 @@ def get_column_value_from_payment(cls, payment: "Payment", column_name: str) -> return getattr(obj, nested_field, None) or "" @staticmethod - def get_registration_token_doc_number(snapshot_data: Dict[str, Any]) -> str: - collector_data = ( - snapshot_data.get("primary_collector", {}) or snapshot_data.get("alternate_collector", {}) or dict() - ) - documents_list = collector_data.get("documents", []) - documents_dict = {doc.get("type"): doc for doc in documents_list} - return documents_dict.get("registration_token", {}).get("document_number", "") - - @staticmethod - def get_national_id_doc_number(snapshot_data: Dict[str, Any]) -> str: + def get_document_number_by_doc_type_key(snapshot_data: Dict[str, Any], document_type_key: str) -> str: collector_data = ( snapshot_data.get("primary_collector", {}) or snapshot_data.get("alternate_collector", {}) or dict() ) documents_list = collector_data.get("documents", []) documents_dict = {doc.get("type"): doc for doc in documents_list} - return documents_dict.get("national_id", {}).get("document_number", "") + return documents_dict.get(document_type_key, {}).get("document_number", "") @staticmethod def get_alternate_collector_doc_numbers(snapshot_data: Dict[str, Any]) -> str: diff --git a/src/hct_mis_api/apps/payment/xlsx/xlsx_payment_plan_export_per_fsp_service.py b/src/hct_mis_api/apps/payment/xlsx/xlsx_payment_plan_export_per_fsp_service.py index be749f0086..bba83cb8f5 100644 --- a/src/hct_mis_api/apps/payment/xlsx/xlsx_payment_plan_export_per_fsp_service.py +++ b/src/hct_mis_api/apps/payment/xlsx/xlsx_payment_plan_export_per_fsp_service.py @@ -123,6 +123,8 @@ def prepare_headers(self, fsp_xlsx_template: "FinancialServiceProviderXlsxTempla column_list.append(core_field) for flex_field in fsp_xlsx_template.flex_fields: column_list.append(flex_field) + for document_field in fsp_xlsx_template.document_types: + column_list.append(document_field) column_list = self._remove_column_for_people(column_list) column_list = self._remove_core_fields_for_people(column_list) @@ -144,6 +146,7 @@ def add_rows( def get_payment_row(self, payment: Payment, fsp_xlsx_template: "FinancialServiceProviderXlsxTemplate") -> List[str]: fsp_template_columns = self._remove_column_for_people(fsp_xlsx_template.columns) fsp_template_core_fields = self._remove_core_fields_for_people(fsp_xlsx_template.core_fields) + fsp_template_document_fields = fsp_xlsx_template.document_types if self.payment_generate_token_and_order_numbers: payment = generate_token_and_order_numbers(payment) @@ -165,6 +168,13 @@ def get_payment_row(self, payment: Payment, fsp_xlsx_template: "FinancialService self._get_flex_field_by_name(column_name, payment) for column_name in fsp_xlsx_template.flex_fields ] payment_row.extend(flex_field_row) + # get document number by document type key + documents_row = [ + FinancialServiceProviderXlsxTemplate.get_column_value_from_payment(payment, doc_type_key) + for doc_type_key in fsp_template_document_fields + ] + payment_row.extend(documents_row) + return list(map(self.right_format_for_xlsx, payment_row)) def _get_flex_field_by_name(self, name: str, payment: Payment) -> str: diff --git a/src/hct_mis_api/migrations_script/main.py b/src/hct_mis_api/migrations_script/main.py index 31b6ff89b1..f6968d622e 100644 --- a/src/hct_mis_api/migrations_script/main.py +++ b/src/hct_mis_api/migrations_script/main.py @@ -55,6 +55,7 @@ def apply_migrations(): ("payment", "0004_migration"), ("payment", "0005_migration"), ("payment", "0006_migration"), + ("payment", "0007_migration"), ("aurora", "0003_migration"), ] fake_migrations(excluded_migrations) diff --git a/tests/unit/apps/payment/test_fsp_xlsx_template_get_column_value.py b/tests/unit/apps/payment/test_fsp_xlsx_template_get_column_value.py index c5ad868861..81ee3acc62 100644 --- a/tests/unit/apps/payment/test_fsp_xlsx_template_get_column_value.py +++ b/tests/unit/apps/payment/test_fsp_xlsx_template_get_column_value.py @@ -2,9 +2,9 @@ from parameterized import parameterized +from hct_mis_api.apps.account.fixtures import UserFactory from hct_mis_api.apps.core.base_test_case import APITestCase from hct_mis_api.apps.core.fixtures import create_afghanistan -from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.household.fixtures import ( DocumentFactory, DocumentTypeFactory, @@ -26,9 +26,9 @@ class FinancialServiceProviderXlsxTemplateTest(APITestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() - create_afghanistan() - cls.business_area = BusinessArea.objects.get(slug="afghanistan") + cls.business_area = create_afghanistan() cls.program = ProgramFactory(business_area=cls.business_area) + cls.user = UserFactory() def test_get_column_value_registration_token_empty(self) -> None: household, individuals = create_household(household_args={"size": 1, "business_area": self.business_area}) @@ -37,7 +37,9 @@ def test_get_column_value_registration_token_empty(self) -> None: program_cycle=self.program.cycles.first(), status=PaymentPlan.Status.ACCEPTED, business_area=self.business_area, + created_by=self.user, ) + DocumentTypeFactory(key="registration_token") payment = PaymentFactory(parent=payment_plan, household=household, collector=individual, currency="PLN") create_payment_plan_snapshot_data(payment_plan) From 06b8c7f53fad9de8e4ac8b3d1ecd4d0068b082e7 Mon Sep 17 00:00:00 2001 From: Jan Romaniak Date: Mon, 9 Dec 2024 16:13:47 +0100 Subject: [PATCH 04/11] switched to use redis in e2e --- src/hct_mis_api/migrations_script/__init__.py | 0 src/hct_mis_api/migrations_script/main.py | 60 ------------------- tests/selenium/conftest.py | 45 +++++++++++--- 3 files changed, 37 insertions(+), 68 deletions(-) delete mode 100644 src/hct_mis_api/migrations_script/__init__.py delete mode 100644 src/hct_mis_api/migrations_script/main.py diff --git a/src/hct_mis_api/migrations_script/__init__.py b/src/hct_mis_api/migrations_script/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/migrations_script/main.py b/src/hct_mis_api/migrations_script/main.py deleted file mode 100644 index 06f0572595..0000000000 --- a/src/hct_mis_api/migrations_script/main.py +++ /dev/null @@ -1,60 +0,0 @@ -import csv -import os -from datetime import datetime - -import django -from django.core.management import call_command -from django.db import connection - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "your_project_name.settings") -django.setup() - - -def export_migration_info_to_csv(filename="migrations_info.csv"): - timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - filename = f"migrations_info_{timestamp}.csv" - with connection.cursor() as cursor: - cursor.execute("SELECT * FROM django_migrations") - rows = cursor.fetchall() - column_names = [desc[0] for desc in cursor.description] - - with open(filename, mode="w", newline="") as file: - writer = csv.writer(file) - writer.writerow(column_names) - writer.writerows(rows) - print(f"Migration info exported to {filename}") - - -def clear_migration_table(): - with connection.cursor() as cursor: - cursor.execute("DELETE FROM django_migrations") - - -def fake_migrations(excluded_migrations): - call_command("migrate", "--fake") - with connection.cursor() as cursor: - for app, name in excluded_migrations: - cursor.execute("DELETE FROM django_migrations WHERE app = %s AND name = %s", [app, name]) - - -def apply_migrations(): - call_command("migrate") - print("Migrations applied.") - - -if __name__ == "__main__": - export_migration_info_to_csv() - clear_migration_table() - excluded_migrations = [ - ("targeting", "0002_migration"), - ("household", "0003_migration"), - ("household", "0004_migration"), - ("grievance", "0004_migration"), - ("payment", "0002_migration"), - ("payment", "0003_migration"), - ("payment", "0004_migration"), - ("payment", "0005_migration"), - ("aurora", "0003_migration"), - ] - fake_migrations(excluded_migrations) - apply_migrations() diff --git a/tests/selenium/conftest.py b/tests/selenium/conftest.py index e7da8c809e..9bbeff429e 100644 --- a/tests/selenium/conftest.py +++ b/tests/selenium/conftest.py @@ -1,11 +1,14 @@ import logging import os +import re from datetime import datetime +from typing import Any from django.conf import settings from django.core.management import call_command import pytest +import redis from _pytest.fixtures import FixtureRequest from _pytest.nodes import Item from _pytest.runner import CallInfo @@ -28,6 +31,7 @@ from hct_mis_api.apps.geo.models import Country from hct_mis_api.apps.household.fixtures import DocumentTypeFactory from hct_mis_api.apps.household.models import DocumentType +from hct_mis_api.config.env import env from tests.selenium.page_object.accountability.communication import ( AccountabilityCommunication, ) @@ -113,11 +117,42 @@ def pytest_addoption(parser) -> None: # type: ignore parser.addoption("--mapping", action="store_true", default=False, help="Enable mapping mode") +def get_redis_host() -> str: + regex = "\\/\\/(.*):" + redis_host = re.search(regex, env("CACHE_LOCATION")).group(1) + return redis_host + + +@pytest.fixture(autouse=True) +def configure_cache_for_tests(worker_id: str, settings: Any) -> None: + """ + Dynamically configure Django's cache backend for each pytest-xdist worker. + """ + redis_db = 0 if worker_id == "master" else int(worker_id.replace("gw", "")) + settings.CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": f"redis://{get_redis_host()}:6379/{redis_db}", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + }, + } + } + + +@pytest.fixture(autouse=True) +def flush_redis(worker_id: str) -> None: + redis_db = 0 if worker_id == "master" else int(worker_id.replace("gw", "")) + redis_client = redis.StrictRedis(host=get_redis_host(), port=6379, db=redis_db) + redis_client.flushdb() + + def pytest_configure(config) -> None: # type: ignore - env = Env() - settings.OUTPUT_DATA_ROOT = env("OUTPUT_DATA_ROOT", default="/tests/selenium/output_data") config.addinivalue_line("markers", "night: This marker is intended for e2e tests conducted during the night on CI") # delete all old screenshots + + env = Env() + settings.OUTPUT_DATA_ROOT = env("OUTPUT_DATA_ROOT", default="/tests/selenium/output_data") settings.REPORT_DIRECTORY = f"{settings.OUTPUT_DATA_ROOT}/report" settings.DOWNLOAD_DIRECTORY = f"{settings.OUTPUT_DATA_ROOT}/report/downloads" settings.SCREENSHOT_DIRECTORY = f"{settings.REPORT_DIRECTORY}/screenshot" @@ -146,12 +181,6 @@ def pytest_configure(config) -> None: # type: ignore settings.SECURE_CONTENT_TYPE_NOSNIFF = True settings.SECURE_REFERRER_POLICY = "same-origin" settings.CACHE_ENABLED = False - settings.CACHES = { - "default": { - "BACKEND": "hct_mis_api.apps.core.memcache.LocMemCache", - "TIMEOUT": 1800, - } - } settings.LOGGING["loggers"].update( { From 2a9e429282f640e6f1c64d21803a43a5b6552880 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <42150286+pkujawa@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:57:57 +0100 Subject: [PATCH 05/11] [217496] Beneficiary Groups (#4366) --- src/frontend/data/schema.graphql | 28 +- src/frontend/generated/core/ApiError.ts | 25 + .../generated/core/ApiRequestOptions.ts | 17 + src/frontend/generated/core/ApiResult.ts | 11 + .../generated/core/CancelablePromise.ts | 131 ++ src/frontend/generated/core/OpenAPI.ts | 32 + src/frontend/generated/core/request.ts | 322 ++++ src/frontend/generated/index.ts | 99 ++ src/frontend/generated/models/ActionEnum.ts | 30 + src/frontend/generated/models/Admin1Enum.ts | 146 ++ src/frontend/generated/models/Admin2Enum.ts | 1260 +++++++++++++++ src/frontend/generated/models/Admin3Enum.ts | 486 ++++++ src/frontend/generated/models/Admin4Enum.ts | 5 + src/frontend/generated/models/Area.ts | 24 + src/frontend/generated/models/AreaList.ts | 10 + src/frontend/generated/models/AreaType.ts | 22 + .../generated/models/BeneficiaryGroup.ts | 14 + src/frontend/generated/models/BlankEnum.ts | 7 + src/frontend/generated/models/BusinessArea.ts | 15 + .../models/CollectIndividualDataEnum.ts | 17 + .../generated/models/CollectTypeEnum.ts | 12 + .../generated/models/CommsDisabilityEnum.ts | 15 + .../generated/models/ConsentSharingEnum.ts | 17 + src/frontend/generated/models/Country.ts | 16 + src/frontend/generated/models/CountryEnum.ts | 508 +++++++ .../generated/models/CountryOriginEnum.ts | 508 +++++++ src/frontend/generated/models/CurrencyEnum.ts | 333 ++++ .../DeduplicationGoldenRecordStatusEnum.ts | 18 + src/frontend/generated/models/Delegate.ts | 9 + .../generated/models/DelegatePeople.ts | 9 + .../generated/models/DisabilityEnum.ts | 12 + src/frontend/generated/models/Document.ts | 33 + .../generated/models/DocumentStatusEnum.ts | 16 + .../generated/models/DocumentTypeEnum.ts | 30 + .../generated/models/FollowUpPaymentPlan.ts | 9 + .../models/FrequencyOfPaymentsEnum.ts | 12 + .../generated/models/HearingDisabilityEnum.ts | 15 + src/frontend/generated/models/Household.ts | 113 ++ src/frontend/generated/models/Individual.ts | 124 ++ .../generated/models/MemoryDisabilityEnum.ts | 15 + src/frontend/generated/models/NullEnum.ts | 7 + .../generated/models/OrgEnumeratorEnum.ts | 13 + .../generated/models/PaginatedAreaList.ts | 12 + .../generated/models/PaginatedAreaListList.ts | 12 + .../generated/models/PaginatedAreaTypeList.ts | 12 + .../models/PaginatedBeneficiaryGroupList.ts | 12 + .../models/PaginatedBusinessAreaList.ts | 12 + .../generated/models/PaginatedCountryList.ts | 12 + .../models/PaginatedPaymentPlanList.ts | 12 + ...natedPeriodicDataUpdateTemplateListList.ts | 12 + ...ginatedPeriodicDataUpdateUploadListList.ts | 12 + .../models/PaginatedPeriodicFieldList.ts | 12 + .../models/PaginatedProgramCycleListList.ts | 12 + .../models/PaginatedProgramGlobalList.ts | 12 + ...PaginatedRegistrationDataImportListList.ts | 12 + .../PaginatedTargetPopulationListList.ts | 12 + .../models/PatchedProgramCycleUpdate.ts | 10 + src/frontend/generated/models/PatchedRDI.ts | 9 + src/frontend/generated/models/PaymentPlan.ts | 26 + .../generated/models/PaymentPlanBulkAction.ts | 11 + .../models/PaymentPlanSupportingDocument.ts | 12 + .../PeriodicDataUpdateTemplateCreate.ts | 10 + .../PeriodicDataUpdateTemplateDetail.ts | 9 + .../models/PeriodicDataUpdateTemplateList.ts | 14 + .../models/PeriodicDataUpdateUpload.ts | 8 + .../models/PeriodicDataUpdateUploadDetail.ts | 14 + .../models/PeriodicDataUpdateUploadList.ts | 13 + .../generated/models/PeriodicField.ts | 12 + .../generated/models/PeriodicFieldData.ts | 11 + .../models/PhysicalDisabilityEnum.ts | 15 + .../generated/models/PreferredLanguageEnum.ts | 40 + src/frontend/generated/models/Program.ts | 20 + .../generated/models/ProgramCycleCreate.ts | 10 + .../generated/models/ProgramCycleList.ts | 22 + .../generated/models/ProgramCycleUpdate.ts | 10 + .../generated/models/ProgramGlobal.ts | 26 + .../models/ProgramGlobalStatusEnum.ts | 14 + src/frontend/generated/models/PushPeople.ts | 140 ++ .../generated/models/PushPeopleTypeEnum.ts | 11 + src/frontend/generated/models/RDI.ts | 9 + src/frontend/generated/models/RDINested.ts | 11 + .../generated/models/RdiMergeStatusEnum.ts | 12 + .../models/RegistrationDataImportList.ts | 13 + .../models/RegistrationMethodEnum.ts | 13 + .../generated/models/RelationshipEnum.ts | 44 + .../generated/models/ResidenceStatusEnum.ts | 21 + src/frontend/generated/models/ScopeEnum.ts | 12 + src/frontend/generated/models/SectorEnum.ts | 22 + .../generated/models/SeeingDisabilityEnum.ts | 15 + .../models/SelfcareDisabilityEnum.ts | 15 + src/frontend/generated/models/SexEnum.ts | 12 + src/frontend/generated/models/SubtypeEnum.ts | 16 + .../generated/models/TargetPopulationList.ts | 12 + .../generated/models/WorkStatusEnum.ts | 14 + .../services/FieldsAttributesService.ts | 19 + .../generated/services/HhStatusService.ts | 19 + .../generated/services/RestService.ts | 1350 +++++++++++++++++ src/frontend/src/__generated__/graphql.tsx | 86 +- .../src/__generated__/introspection-result.ts | 1 + src/frontend/src/api/programsApi.ts | 24 + .../fragments/ProgramDetailsFragment.ts | 9 + .../queries/program/AllProgramsForChoices.ts | 13 + .../LookUpHouseholdFiltersCommunication.tsx | 10 +- .../LookUpSelectionCommunication.tsx | 10 +- .../FeedbackDetails/FeedbackDetails.tsx | 7 +- .../HouseholdQuestionnaire.tsx | 8 +- .../IndividualQuestionnaire.tsx | 6 +- .../LookUpProgrammesFiltersSurveys.tsx | 5 +- .../LookUpTargetPopulationFiltersSurveys.tsx | 5 +- .../core/ActivityLogPageFilters.tsx | 7 +- .../components/core/Drawer/DrawerItems.tsx | 38 +- .../PaymentVerificationSection.tsx | 8 +- .../grievances/AddIndividualDataChange.tsx | 7 +- .../AddIndividualGrievanceDetails.tsx | 12 +- ...ApproveDeleteHouseholdGrievanceDetails.tsx | 13 +- .../Description/Description.tsx | 13 +- .../CreateGrievance/Selection/Selection.tsx | 50 +- .../Verification/Verification.tsx | 8 +- .../DeleteHouseholdGrievanceDetails.tsx | 14 +- .../DeleteIndividualGrievanceDetails.tsx | 9 +- .../EditHouseholdDataChange.tsx | 10 +- .../EditIndividualDataChange.tsx | 10 +- .../grievances/GrievanceDetailsToolbar.tsx | 11 +- .../GrievancesDetails/GrievancesDetails.tsx | 14 +- .../GrievancesTable/GrievancesTable.tsx | 18 +- .../HouseholdQuestionnaire.tsx | 7 +- .../IndividualQuestionnaire.tsx | 9 +- ...okUpHouseholdIndividualSelectionDetail.tsx | 30 +- ...kUpHouseholdIndividualSelectionDisplay.tsx | 20 +- .../LookUpHouseholdTable.tsx | 22 +- .../LookUpIndividualTable.tsx | 19 +- .../LookUpReassignRoleDisplay.tsx | 14 +- .../LookUpReassignRole/ReassignRoleUnique.tsx | 5 +- .../NeedsAdjudication/BiometricsResults.tsx | 9 +- .../NeedsAdjudicationDetailsOld.tsx | 6 +- .../NeedsAdjudicationTable.tsx | 18 +- .../grievances/OtherRelatedTickets.tsx | 9 +- .../grievances/OtherRelatedTicketsCreate.tsx | 11 +- .../grievances/ReassignMultipleRoleBox.tsx | 49 +- .../components/grievances/ReassignRoleBox.tsx | 19 +- .../grievances/utils/createGrievanceUtils.ts | 6 + .../grievances/utils/validateGrievance.ts | 36 +- .../CreateFollowUpPaymentPlan.tsx | 10 +- .../PaymentDetails/PaymentDetails.tsx | 35 +- .../ExcludeSection/ExcludeSection.tsx | 16 +- .../LockPaymentPlan.tsx | 7 +- .../ResultsForHouseholds.tsx | 13 +- .../CreateFollowUpPaymentPlan.tsx | 12 +- .../PaymentDetails/PaymentDetails.tsx | 7 +- .../components/payments/HouseholdDetails.tsx | 10 +- .../components/payments/IndividualDetails.tsx | 15 +- .../src/components/people/PeopleFilter.tsx | 28 +- .../HouseholdDetails/HouseholdDetails.tsx | 30 +- .../HouseholdDetails.test.tsx.snap | 36 +- .../components/population/HouseholdFilter.tsx | 14 +- .../IndividualBioData/IndividualBioData.tsx | 12 +- .../IndividualBioData.test.tsx.snap | 4 +- .../IndividualDeliveryMechanisms.tsx | 6 +- .../population/IndividualsFilter.tsx | 8 +- .../LinkedGrievancesModal.tsx | 6 +- .../programs/CreateProgram/DetailsStep.tsx | 4 +- .../editProgramValidationSchema.ts | 3 +- .../CreateProgram/programValidationSchema.ts | 1 + .../programs/EditProgram/DetailsStep.tsx | 4 +- .../ProgramDetails/ProgramDetails.tsx | 6 + .../ProgramDetails.test.tsx.snap | 17 + .../components/rdi/RegistrationFilters.tsx | 5 +- .../components/rdi/create/ImportCounters.tsx | 8 +- .../CreateImportFromProgramPopulation.tsx | 1 + .../components/rdi/details/DedupeResults.tsx | 5 +- .../RegistrationDetails.tsx | 8 +- .../RegistrationDetails.test.tsx.snap | 4 +- .../HouseholdDetails/HouseholdDetails.tsx | 6 +- .../RegistrationIndividualBioData.tsx | 7 +- ...egistrationIndividualBioData.test.tsx.snap | 1 + .../EmptyTargetingCriteria.tsx | 5 +- .../CreateTargetPopulation/Exclusions.tsx | 13 +- .../EditTargetPopulation.tsx | 14 +- .../targeting/ResultsForHouseholds.tsx | 12 +- .../targeting/TargetPopulationCore.tsx | 10 +- .../TargetPopulationTableFilters.tsx | 5 +- .../TargetingCriteriaDisplay/Criteria.tsx | 11 +- .../ExcludeCheckboxes.tsx | 19 +- .../src/containers/GlobalProgramSelect.tsx | 72 +- .../src/containers/forms/ProgramForm.tsx | 58 +- .../forms/TargetingCriteriaForm.tsx | 23 +- ...argetingCriteriaIndividualFilterBlocks.tsx | 6 +- .../feedback/CreateFeedbackPage.tsx | 37 +- .../feedback/EditFeedbackPage.tsx | 14 +- .../pages/grievances/CreateGrievancePage.tsx | 7 +- .../pages/grievances/EditGrievancePage.tsx | 35 +- .../pages/grievances/GrievancesTablePage.tsx | 1 - .../householdMembers/NewTemplatePage.tsx | 7 +- .../ManagerialConsolePage.tsx | 3 +- .../ProgramCycleDetails/PaymentPlansTable.tsx | 18 +- .../ProgramCycle/ProgramCyclePage.tsx | 16 +- .../pages/population/HouseholdMembersPage.tsx | 11 +- .../PopulationHouseholdDetailsPage.tsx | 10 +- .../population/PopulationHouseholdPage.tsx | 19 +- .../PopulationIndividualsDetailsPage.tsx | 7 +- .../pages/program/CreateProgramPage.tsx | 6 +- .../pages/program/DuplicateProgramPage.tsx | 12 +- .../pages/program/EditProgramPage.tsx | 6 + .../pages/program/ProgramDetailsPage.tsx | 3 +- .../rdi/RegistrationDataImportDetailsPage.tsx | 13 +- .../pages/targeting/TargetPopulationsPage.tsx | 2 + .../LookUpHouseholdTableCommunication.tsx | 22 +- .../RecipientsTable/RecipientsTable.tsx | 19 +- .../tables/Feedback/FeedbackTable.tsx | 29 +- .../tables/ProgramCycle/ProgramCycleTable.tsx | 2 +- .../ProgramCyclesTablePaymentModule.tsx | 5 +- .../LookUpProgrammesTableSurveys.tsx | 21 +- .../RecipientsTable/RecipientsTable.tsx | 19 +- .../PaymentPlansTable/PaymentPlansTable.tsx | 17 +- .../PaymentsTable/PaymentsTable.tsx | 20 +- .../WarningTooltipTable.tsx | 7 +- .../WarningTooltipTable.tsx | 7 +- .../PaymentRecordAndPaymentHouseholdTable.tsx | 21 +- .../VerificationRecordsTable.tsx | 21 +- .../VerificationsTable.tsx | 20 +- .../CollectorsTable/CollectorsTable.tsx | 19 +- .../HouseholdCompositionTable.tsx | 5 +- .../HouseholdMembersTable.tsx | 23 +- .../HouseholdTable/HouseholdTable.tsx | 23 +- .../IndividualsListTable.tsx | 32 +- .../IndividualsListTable.test.tsx.snap | 2 +- .../HouseholdImportedIndividualsTable.tsx | 26 +- .../ImportedHouseholdTable.tsx | 34 +- .../ImportedIndividualsTable.tsx | 25 +- .../ImportedIndividualsTable.test.tsx.snap | 2 +- .../RegistrationDataImportTable.tsx | 23 +- .../TargetPopulationHouseholdTable.tsx | 24 +- .../TargetPopulationTable.tsx | 20 +- src/frontend/src/programContext.tsx | 9 + .../BeneficiaryGroupsAutocompleteRest.tsx | 65 + src/frontend/src/testUtils/testUtils.tsx | 11 + src/frontend/src/utils/constants.ts | 115 +- src/frontend/src/utils/en.json | 3 +- src/frontend/src/utils/targetingUtils.ts | 1 - src/frontend/src/utils/utils.ts | 17 + src/hct_mis_api/api/endpoints/rdi/program.py | 1 + src/hct_mis_api/api/urls.py | 6 +- src/hct_mis_api/apps/household/fixtures.py | 68 + src/hct_mis_api/apps/household/forms.py | 7 +- src/hct_mis_api/apps/payment/fixtures.py | 21 +- src/hct_mis_api/apps/program/admin.py | 15 +- src/hct_mis_api/apps/program/api/caches.py | 24 +- .../apps/program/api/serializers.py | 16 +- .../apps/program/api/urls/__init__.py | 0 .../program/api/urls/beneficiary_group.py | 13 + .../api/{urls.py => urls/program_related.py} | 0 src/hct_mis_api/apps/program/api/views.py | 30 +- src/hct_mis_api/apps/program/filters.py | 9 + src/hct_mis_api/apps/program/fixtures.py | 25 +- .../apps/program/fixtures/data-cypress.json | 20 +- .../apps/program/fixtures/data.json | 33 +- src/hct_mis_api/apps/program/inputs.py | 2 + .../apps/program/migrations/0002_migration.py | 77 + src/hct_mis_api/apps/program/models.py | 35 + src/hct_mis_api/apps/program/mutations.py | 17 +- src/hct_mis_api/apps/program/schema.py | 10 +- src/hct_mis_api/apps/program/signals.py | 17 +- .../apps/registration_datahub/mutations.py | 15 +- src/hct_mis_api/apps/targeting/fixtures.py | 1 + src/hct_mis_api/config/fragments/constance.py | 5 + src/hct_mis_api/migrations_script/main.py | 1 + .../accountability/test_communication.py | 6 +- tests/selenium/accountability/test_surveys.py | 6 +- tests/selenium/conftest.py | 31 +- .../test_country_dashboard.py | 4 +- tests/selenium/drawer/test_drawer.py | 32 +- tests/selenium/filters/test_filters.py | 38 +- .../grievance/feedback/test_feedback.py | 52 +- .../test_grievance_tickets.py | 102 +- tests/selenium/helpers/fixtures.py | 4 +- .../test_managerial_console.py | 6 +- tests/selenium/page_object/base_components.py | 22 +- .../grievance/details_feedback_page.py | 4 +- .../grievance/details_grievance_page.py | 4 + .../page_object/grievance/feedback.py | 2 +- .../page_object/grievance/new_feedback.py | 14 +- .../page_object/grievance/new_ticket.py | 8 +- .../payment_module/payment_module.py | 3 - .../payment_module/payment_module_details.py | 4 +- .../payment_verification/payment_record.py | 6 +- .../programme_management.py | 4 + .../households_details.py | 10 +- .../individuals_details.py | 6 +- .../periodic_data_update_templates.py | 8 - .../periodic_data_update_uploads.py | 4 - .../rdi_details_page.py | 4 +- .../registration_data_import.py | 2 +- .../page_object/targeting/targeting_create.py | 6 +- .../targeting/targeting_details.py | 14 +- .../payment_module/test_payment_plans.py | 18 +- .../payment_module/test_program_cycles.py | 4 +- .../test_payment_verification.py | 14 +- tests/selenium/people/test_people.py | 33 +- .../test_people_periodic_data_update.py | 4 +- .../program_details/test_program_details.py | 33 +- .../test_programme_management.py | 77 +- .../programme_population/test_households.py | 96 +- .../programme_population/test_individuals.py | 89 +- .../test_periodic_data_templates.py | 14 +- .../test_periodic_data_update_upload.py | 4 +- .../test_registration_data_import.py | 85 +- tests/selenium/targeting/test_targeting.py | 326 ++-- tests/unit/api/base.py | 8 + tests/unit/api/test_program.py | 7 +- .../snap_test_approve_automatic_tickets.py | 14 +- ...ap_test_grievance_data_change_mutations.py | 48 +- .../test_approve_automatic_tickets.py | 43 +- .../grievance/test_approve_data_change.py | 1 - .../test_close_data_change_tickets.py | 16 +- ...evance_ticket_and_disable_decuplication.py | 4 +- .../test_create_needs_adjudication_tickets.py | 3 - .../test_grievance_data_change_mutations.py | 10 +- .../grievance/test_reassign_role_mutation.py | 20 +- ...st_reassign_roles_on_disable_individual.py | 2 +- .../test_reassign_roles_on_update.py | 2 +- .../grievance/test_reassign_roles_services.py | 2 +- .../test_update_grievance_tickets.py | 4 +- .../apps/grievance/test_withdraw_household.py | 4 +- tests/unit/apps/household/test_forms.py | 7 +- .../apps/payment/test_exclude_households.py | 13 +- tests/unit/apps/payment/test_models.py | 5 +- .../test_payment_plan_reconciliation.py | 3 + ...erification_plan_status_change_services.py | 17 +- .../snapshots/snap_test_all_programs_query.py | 61 +- .../snapshots/snap_test_create_program.py | 64 + .../snapshots/snap_test_update_program.py | 138 +- .../apps/program/test_all_programs_query.py | 58 +- .../program/test_beneficiary_group_views.py | 94 ++ .../unit/apps/program/test_create_program.py | 47 +- .../unit/apps/program/test_update_program.py | 62 +- ...data_import_program_population_mutation.py | 54 +- .../registration_datahub/test_rdi_merge.py | 5 +- ...data_import_program_population_mutation.py | 98 +- tests/unit/apps/steficon/test_rules.py | 3 +- 339 files changed, 10625 insertions(+), 1020 deletions(-) create mode 100644 src/frontend/generated/core/ApiError.ts create mode 100644 src/frontend/generated/core/ApiRequestOptions.ts create mode 100644 src/frontend/generated/core/ApiResult.ts create mode 100644 src/frontend/generated/core/CancelablePromise.ts create mode 100644 src/frontend/generated/core/OpenAPI.ts create mode 100644 src/frontend/generated/core/request.ts create mode 100644 src/frontend/generated/index.ts create mode 100644 src/frontend/generated/models/ActionEnum.ts create mode 100644 src/frontend/generated/models/Admin1Enum.ts create mode 100644 src/frontend/generated/models/Admin2Enum.ts create mode 100644 src/frontend/generated/models/Admin3Enum.ts create mode 100644 src/frontend/generated/models/Admin4Enum.ts create mode 100644 src/frontend/generated/models/Area.ts create mode 100644 src/frontend/generated/models/AreaList.ts create mode 100644 src/frontend/generated/models/AreaType.ts create mode 100644 src/frontend/generated/models/BeneficiaryGroup.ts create mode 100644 src/frontend/generated/models/BlankEnum.ts create mode 100644 src/frontend/generated/models/BusinessArea.ts create mode 100644 src/frontend/generated/models/CollectIndividualDataEnum.ts create mode 100644 src/frontend/generated/models/CollectTypeEnum.ts create mode 100644 src/frontend/generated/models/CommsDisabilityEnum.ts create mode 100644 src/frontend/generated/models/ConsentSharingEnum.ts create mode 100644 src/frontend/generated/models/Country.ts create mode 100644 src/frontend/generated/models/CountryEnum.ts create mode 100644 src/frontend/generated/models/CountryOriginEnum.ts create mode 100644 src/frontend/generated/models/CurrencyEnum.ts create mode 100644 src/frontend/generated/models/DeduplicationGoldenRecordStatusEnum.ts create mode 100644 src/frontend/generated/models/Delegate.ts create mode 100644 src/frontend/generated/models/DelegatePeople.ts create mode 100644 src/frontend/generated/models/DisabilityEnum.ts create mode 100644 src/frontend/generated/models/Document.ts create mode 100644 src/frontend/generated/models/DocumentStatusEnum.ts create mode 100644 src/frontend/generated/models/DocumentTypeEnum.ts create mode 100644 src/frontend/generated/models/FollowUpPaymentPlan.ts create mode 100644 src/frontend/generated/models/FrequencyOfPaymentsEnum.ts create mode 100644 src/frontend/generated/models/HearingDisabilityEnum.ts create mode 100644 src/frontend/generated/models/Household.ts create mode 100644 src/frontend/generated/models/Individual.ts create mode 100644 src/frontend/generated/models/MemoryDisabilityEnum.ts create mode 100644 src/frontend/generated/models/NullEnum.ts create mode 100644 src/frontend/generated/models/OrgEnumeratorEnum.ts create mode 100644 src/frontend/generated/models/PaginatedAreaList.ts create mode 100644 src/frontend/generated/models/PaginatedAreaListList.ts create mode 100644 src/frontend/generated/models/PaginatedAreaTypeList.ts create mode 100644 src/frontend/generated/models/PaginatedBeneficiaryGroupList.ts create mode 100644 src/frontend/generated/models/PaginatedBusinessAreaList.ts create mode 100644 src/frontend/generated/models/PaginatedCountryList.ts create mode 100644 src/frontend/generated/models/PaginatedPaymentPlanList.ts create mode 100644 src/frontend/generated/models/PaginatedPeriodicDataUpdateTemplateListList.ts create mode 100644 src/frontend/generated/models/PaginatedPeriodicDataUpdateUploadListList.ts create mode 100644 src/frontend/generated/models/PaginatedPeriodicFieldList.ts create mode 100644 src/frontend/generated/models/PaginatedProgramCycleListList.ts create mode 100644 src/frontend/generated/models/PaginatedProgramGlobalList.ts create mode 100644 src/frontend/generated/models/PaginatedRegistrationDataImportListList.ts create mode 100644 src/frontend/generated/models/PaginatedTargetPopulationListList.ts create mode 100644 src/frontend/generated/models/PatchedProgramCycleUpdate.ts create mode 100644 src/frontend/generated/models/PatchedRDI.ts create mode 100644 src/frontend/generated/models/PaymentPlan.ts create mode 100644 src/frontend/generated/models/PaymentPlanBulkAction.ts create mode 100644 src/frontend/generated/models/PaymentPlanSupportingDocument.ts create mode 100644 src/frontend/generated/models/PeriodicDataUpdateTemplateCreate.ts create mode 100644 src/frontend/generated/models/PeriodicDataUpdateTemplateDetail.ts create mode 100644 src/frontend/generated/models/PeriodicDataUpdateTemplateList.ts create mode 100644 src/frontend/generated/models/PeriodicDataUpdateUpload.ts create mode 100644 src/frontend/generated/models/PeriodicDataUpdateUploadDetail.ts create mode 100644 src/frontend/generated/models/PeriodicDataUpdateUploadList.ts create mode 100644 src/frontend/generated/models/PeriodicField.ts create mode 100644 src/frontend/generated/models/PeriodicFieldData.ts create mode 100644 src/frontend/generated/models/PhysicalDisabilityEnum.ts create mode 100644 src/frontend/generated/models/PreferredLanguageEnum.ts create mode 100644 src/frontend/generated/models/Program.ts create mode 100644 src/frontend/generated/models/ProgramCycleCreate.ts create mode 100644 src/frontend/generated/models/ProgramCycleList.ts create mode 100644 src/frontend/generated/models/ProgramCycleUpdate.ts create mode 100644 src/frontend/generated/models/ProgramGlobal.ts create mode 100644 src/frontend/generated/models/ProgramGlobalStatusEnum.ts create mode 100644 src/frontend/generated/models/PushPeople.ts create mode 100644 src/frontend/generated/models/PushPeopleTypeEnum.ts create mode 100644 src/frontend/generated/models/RDI.ts create mode 100644 src/frontend/generated/models/RDINested.ts create mode 100644 src/frontend/generated/models/RdiMergeStatusEnum.ts create mode 100644 src/frontend/generated/models/RegistrationDataImportList.ts create mode 100644 src/frontend/generated/models/RegistrationMethodEnum.ts create mode 100644 src/frontend/generated/models/RelationshipEnum.ts create mode 100644 src/frontend/generated/models/ResidenceStatusEnum.ts create mode 100644 src/frontend/generated/models/ScopeEnum.ts create mode 100644 src/frontend/generated/models/SectorEnum.ts create mode 100644 src/frontend/generated/models/SeeingDisabilityEnum.ts create mode 100644 src/frontend/generated/models/SelfcareDisabilityEnum.ts create mode 100644 src/frontend/generated/models/SexEnum.ts create mode 100644 src/frontend/generated/models/SubtypeEnum.ts create mode 100644 src/frontend/generated/models/TargetPopulationList.ts create mode 100644 src/frontend/generated/models/WorkStatusEnum.ts create mode 100644 src/frontend/generated/services/FieldsAttributesService.ts create mode 100644 src/frontend/generated/services/HhStatusService.ts create mode 100644 src/frontend/generated/services/RestService.ts create mode 100644 src/frontend/src/api/programsApi.ts create mode 100644 src/frontend/src/shared/autocompletes/rest/BeneficiaryGroupsAutocompleteRest.tsx create mode 100644 src/hct_mis_api/apps/program/api/urls/__init__.py create mode 100644 src/hct_mis_api/apps/program/api/urls/beneficiary_group.py rename src/hct_mis_api/apps/program/api/{urls.py => urls/program_related.py} (100%) create mode 100644 src/hct_mis_api/apps/program/migrations/0002_migration.py create mode 100644 tests/unit/apps/program/test_beneficiary_group_views.py diff --git a/src/frontend/data/schema.graphql b/src/frontend/data/schema.graphql index 03ec69bba9..9cf6a765c4 100644 --- a/src/frontend/data/schema.graphql +++ b/src/frontend/data/schema.graphql @@ -288,6 +288,19 @@ input BankTransferObjectType { accountHolderName: String! } +type BeneficiaryGroupNode implements Node { + id: ID! + createdAt: DateTime! + updatedAt: DateTime! + name: String! + groupLabel: String! + groupLabelPlural: String! + memberLabel: String! + memberLabelPlural: String! + masterDetail: Boolean! + programs(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! +} + scalar BigInt type BulkGrievanceAddNoteMutation { @@ -699,6 +712,7 @@ input CreateProgramInput { administrativeAreasOfImplementation: String businessAreaSlug: String dataCollectingTypeCode: String + beneficiaryGroup: String partners: [ProgramPartnerThroughInput] partnerAccess: String programmeCode: String @@ -2513,8 +2527,8 @@ type PaymentNode implements Node { fspAuthCode: String isCashAssist: Boolean! followUps(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! - paymentVerification: PaymentVerificationNode householdSnapshot: PaymentHouseholdSnapshotNode + paymentVerification: PaymentVerificationNode ticketComplaintDetails: TicketComplaintDetailsNode ticketSensitiveDetails: TicketSensitiveDetailsNode adminUrl: String @@ -2607,13 +2621,13 @@ type PaymentPlanNode implements Node { excludeHouseholdError: String! name: String isCashAssist: Boolean! + approvalProcess(offset: Int, before: String, after: String, first: Int, last: Int): ApprovalProcessNodeConnection! followUps(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanNodeConnection! deliveryMechanisms: [DeliveryMechanismPerPaymentPlanNode] paymentItems(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! + documents(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanSupportingDocumentNodeConnection! paymentVerificationPlans(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationPlanNodeConnection! paymentVerificationSummary: PaymentVerificationSummaryNode - approvalProcess(offset: Int, before: String, after: String, first: Int, last: Int): ApprovalProcessNodeConnection! - documents(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanSupportingDocumentNodeConnection! adminUrl: String currencyName: String hasPaymentListExportFile: Boolean @@ -2988,6 +3002,7 @@ type ProgramNode implements Node { partners: [PartnerNode] biometricDeduplicationEnabled: Boolean! deduplicationSetId: UUID + beneficiaryGroup: BeneficiaryGroupNode pduFields: [PeriodicFieldNode] households(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! householdSet(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! @@ -3157,7 +3172,7 @@ type Query { dataCollectionTypeChoices: [DataCollectingTypeChoiceObject] pduSubtypeChoices: [PDUSubtypeChoiceObject] program(id: ID!): ProgramNode - allPrograms(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, search: String, status: [String], sector: [String], numberOfHouseholds: String, budget: String, startDate: Date, endDate: Date, name: String, numberOfHouseholdsWithTpInProgram: String, dataCollectingType: String, compatibleDct: Boolean, orderBy: String): ProgramNodeConnection + allPrograms(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, search: String, status: [String], sector: [String], numberOfHouseholds: String, budget: String, startDate: Date, endDate: Date, name: String, beneficiaryGroupMatch: Boolean, numberOfHouseholdsWithTpInProgram: String, dataCollectingType: String, compatibleDct: Boolean, orderBy: String): ProgramNodeConnection chartProgrammesBySector(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDetailedDatasetsNode chartTotalTransferredByMonth(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDetailedDatasetsNode programStatusChoices: [ChoiceObject] @@ -3167,7 +3182,7 @@ type Query { programScopeChoices: [ChoiceObject] cashPlanStatusChoices: [ChoiceObject] dataCollectingTypeChoices: [ChoiceObject] - allActivePrograms(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, search: String, status: [String], sector: [String], numberOfHouseholds: String, budget: String, startDate: Date, endDate: Date, name: String, numberOfHouseholdsWithTpInProgram: String, dataCollectingType: String, compatibleDct: Boolean, orderBy: String): ProgramNodeConnection + allActivePrograms(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, search: String, status: [String], sector: [String], numberOfHouseholds: String, budget: String, startDate: Date, endDate: Date, name: String, beneficiaryGroupMatch: Boolean, numberOfHouseholdsWithTpInProgram: String, dataCollectingType: String, compatibleDct: Boolean, orderBy: String): ProgramNodeConnection programCycle(id: ID!): ProgramCycleNode canRunDeduplication: Boolean isDeduplicationDisabled: Boolean @@ -4550,6 +4565,7 @@ input UpdateProgramInput { populationGoal: Int administrativeAreasOfImplementation: String dataCollectingTypeCode: String + beneficiaryGroup: String programmeCode: String pduFields: [PDUFieldInput] } @@ -4681,13 +4697,13 @@ type UserNode implements Node { doapHash: String! userRoles: [UserRoleNode!]! documentSet(offset: Int, before: String, after: String, first: Int, last: Int): DocumentNodeConnection! + approvalSet: [ApprovalNode!]! registrationDataImports(offset: Int, before: String, after: String, first: Int, last: Int): RegistrationDataImportNodeConnection! createdPaymentPlans(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanNodeConnection! createdFinancialServiceProviderXlsxTemplates(offset: Int, before: String, after: String, first: Int, last: Int): FinancialServiceProviderXlsxTemplateNodeConnection! createdFinancialServiceProviders(offset: Int, before: String, after: String, first: Int, last: Int): FinancialServiceProviderNodeConnection! createdDeliveryMechanisms(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismPerPaymentPlanNodeConnection! sentDeliveryMechanisms(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismPerPaymentPlanNodeConnection! - approvalSet: [ApprovalNode!]! createdTickets(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! assignedTickets(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! ticketNotes(offset: Int, before: String, after: String, first: Int, last: Int): TicketNoteNodeConnection! diff --git a/src/frontend/generated/core/ApiError.ts b/src/frontend/generated/core/ApiError.ts new file mode 100644 index 0000000000..ec7b16af6f --- /dev/null +++ b/src/frontend/generated/core/ApiError.ts @@ -0,0 +1,25 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ApiRequestOptions } from './ApiRequestOptions'; +import type { ApiResult } from './ApiResult'; + +export class ApiError extends Error { + public readonly url: string; + public readonly status: number; + public readonly statusText: string; + public readonly body: any; + public readonly request: ApiRequestOptions; + + constructor(request: ApiRequestOptions, response: ApiResult, message: string) { + super(message); + + this.name = 'ApiError'; + this.url = response.url; + this.status = response.status; + this.statusText = response.statusText; + this.body = response.body; + this.request = request; + } +} diff --git a/src/frontend/generated/core/ApiRequestOptions.ts b/src/frontend/generated/core/ApiRequestOptions.ts new file mode 100644 index 0000000000..93143c3ce1 --- /dev/null +++ b/src/frontend/generated/core/ApiRequestOptions.ts @@ -0,0 +1,17 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ApiRequestOptions = { + readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH'; + readonly url: string; + readonly path?: Record; + readonly cookies?: Record; + readonly headers?: Record; + readonly query?: Record; + readonly formData?: Record; + readonly body?: any; + readonly mediaType?: string; + readonly responseHeader?: string; + readonly errors?: Record; +}; diff --git a/src/frontend/generated/core/ApiResult.ts b/src/frontend/generated/core/ApiResult.ts new file mode 100644 index 0000000000..ee1126e2cc --- /dev/null +++ b/src/frontend/generated/core/ApiResult.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ApiResult = { + readonly url: string; + readonly ok: boolean; + readonly status: number; + readonly statusText: string; + readonly body: any; +}; diff --git a/src/frontend/generated/core/CancelablePromise.ts b/src/frontend/generated/core/CancelablePromise.ts new file mode 100644 index 0000000000..d70de92946 --- /dev/null +++ b/src/frontend/generated/core/CancelablePromise.ts @@ -0,0 +1,131 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export class CancelError extends Error { + + constructor(message: string) { + super(message); + this.name = 'CancelError'; + } + + public get isCancelled(): boolean { + return true; + } +} + +export interface OnCancel { + readonly isResolved: boolean; + readonly isRejected: boolean; + readonly isCancelled: boolean; + + (cancelHandler: () => void): void; +} + +export class CancelablePromise implements Promise { + #isResolved: boolean; + #isRejected: boolean; + #isCancelled: boolean; + readonly #cancelHandlers: (() => void)[]; + readonly #promise: Promise; + #resolve?: (value: T | PromiseLike) => void; + #reject?: (reason?: any) => void; + + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason?: any) => void, + onCancel: OnCancel + ) => void + ) { + this.#isResolved = false; + this.#isRejected = false; + this.#isCancelled = false; + this.#cancelHandlers = []; + this.#promise = new Promise((resolve, reject) => { + this.#resolve = resolve; + this.#reject = reject; + + const onResolve = (value: T | PromiseLike): void => { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { + return; + } + this.#isResolved = true; + if (this.#resolve) this.#resolve(value); + }; + + const onReject = (reason?: any): void => { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { + return; + } + this.#isRejected = true; + if (this.#reject) this.#reject(reason); + }; + + const onCancel = (cancelHandler: () => void): void => { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { + return; + } + this.#cancelHandlers.push(cancelHandler); + }; + + Object.defineProperty(onCancel, 'isResolved', { + get: (): boolean => this.#isResolved, + }); + + Object.defineProperty(onCancel, 'isRejected', { + get: (): boolean => this.#isRejected, + }); + + Object.defineProperty(onCancel, 'isCancelled', { + get: (): boolean => this.#isCancelled, + }); + + return executor(onResolve, onReject, onCancel as OnCancel); + }); + } + + get [Symbol.toStringTag]() { + return "Cancellable Promise"; + } + + public then( + onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onRejected?: ((reason: any) => TResult2 | PromiseLike) | null + ): Promise { + return this.#promise.then(onFulfilled, onRejected); + } + + public catch( + onRejected?: ((reason: any) => TResult | PromiseLike) | null + ): Promise { + return this.#promise.catch(onRejected); + } + + public finally(onFinally?: (() => void) | null): Promise { + return this.#promise.finally(onFinally); + } + + public cancel(): void { + if (this.#isResolved || this.#isRejected || this.#isCancelled) { + return; + } + this.#isCancelled = true; + if (this.#cancelHandlers.length) { + try { + for (const cancelHandler of this.#cancelHandlers) { + cancelHandler(); + } + } catch (error) { + console.warn('Cancellation threw an error', error); + return; + } + } + this.#cancelHandlers.length = 0; + if (this.#reject) this.#reject(new CancelError('Request aborted')); + } + + public get isCancelled(): boolean { + return this.#isCancelled; + } +} diff --git a/src/frontend/generated/core/OpenAPI.ts b/src/frontend/generated/core/OpenAPI.ts new file mode 100644 index 0000000000..a0a9ed4a99 --- /dev/null +++ b/src/frontend/generated/core/OpenAPI.ts @@ -0,0 +1,32 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ApiRequestOptions } from './ApiRequestOptions'; + +type Resolver = (options: ApiRequestOptions) => Promise; +type Headers = Record; + +export type OpenAPIConfig = { + BASE: string; + VERSION: string; + WITH_CREDENTIALS: boolean; + CREDENTIALS: 'include' | 'omit' | 'same-origin'; + TOKEN?: string | Resolver | undefined; + USERNAME?: string | Resolver | undefined; + PASSWORD?: string | Resolver | undefined; + HEADERS?: Headers | Resolver | undefined; + ENCODE_PATH?: ((path: string) => string) | undefined; +}; + +export const OpenAPI: OpenAPIConfig = { + BASE: '', + VERSION: '1.0.0', + WITH_CREDENTIALS: false, + CREDENTIALS: 'include', + TOKEN: undefined, + USERNAME: undefined, + PASSWORD: undefined, + HEADERS: undefined, + ENCODE_PATH: undefined, +}; diff --git a/src/frontend/generated/core/request.ts b/src/frontend/generated/core/request.ts new file mode 100644 index 0000000000..f83d711995 --- /dev/null +++ b/src/frontend/generated/core/request.ts @@ -0,0 +1,322 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import { ApiError } from './ApiError'; +import type { ApiRequestOptions } from './ApiRequestOptions'; +import type { ApiResult } from './ApiResult'; +import { CancelablePromise } from './CancelablePromise'; +import type { OnCancel } from './CancelablePromise'; +import type { OpenAPIConfig } from './OpenAPI'; + +export const isDefined = (value: T | null | undefined): value is Exclude => { + return value !== undefined && value !== null; +}; + +export const isString = (value: any): value is string => { + return typeof value === 'string'; +}; + +export const isStringWithValue = (value: any): value is string => { + return isString(value) && value !== ''; +}; + +export const isBlob = (value: any): value is Blob => { + return ( + typeof value === 'object' && + typeof value.type === 'string' && + typeof value.stream === 'function' && + typeof value.arrayBuffer === 'function' && + typeof value.constructor === 'function' && + typeof value.constructor.name === 'string' && + /^(Blob|File)$/.test(value.constructor.name) && + /^(Blob|File)$/.test(value[Symbol.toStringTag]) + ); +}; + +export const isFormData = (value: any): value is FormData => { + return value instanceof FormData; +}; + +export const base64 = (str: string): string => { + try { + return btoa(str); + } catch (err) { + // @ts-ignore + return Buffer.from(str).toString('base64'); + } +}; + +export const getQueryString = (params: Record): string => { + const qs: string[] = []; + + const append = (key: string, value: any) => { + qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); + }; + + const process = (key: string, value: any) => { + if (isDefined(value)) { + if (Array.isArray(value)) { + value.forEach(v => { + process(key, v); + }); + } else if (typeof value === 'object') { + Object.entries(value).forEach(([k, v]) => { + process(`${key}[${k}]`, v); + }); + } else { + append(key, value); + } + } + }; + + Object.entries(params).forEach(([key, value]) => { + process(key, value); + }); + + if (qs.length > 0) { + return `?${qs.join('&')}`; + } + + return ''; +}; + +const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { + const encoder = config.ENCODE_PATH || encodeURI; + + const path = options.url + .replace('{api-version}', config.VERSION) + .replace(/{(.*?)}/g, (substring: string, group: string) => { + if (options.path?.hasOwnProperty(group)) { + return encoder(String(options.path[group])); + } + return substring; + }); + + const url = `${config.BASE}${path}`; + if (options.query) { + return `${url}${getQueryString(options.query)}`; + } + return url; +}; + +export const getFormData = (options: ApiRequestOptions): FormData | undefined => { + if (options.formData) { + const formData = new FormData(); + + const process = (key: string, value: any) => { + if (isString(value) || isBlob(value)) { + formData.append(key, value); + } else { + formData.append(key, JSON.stringify(value)); + } + }; + + Object.entries(options.formData) + .filter(([_, value]) => isDefined(value)) + .forEach(([key, value]) => { + if (Array.isArray(value)) { + value.forEach(v => process(key, v)); + } else { + process(key, value); + } + }); + + return formData; + } + return undefined; +}; + +type Resolver = (options: ApiRequestOptions) => Promise; + +export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => { + if (typeof resolver === 'function') { + return (resolver as Resolver)(options); + } + return resolver; +}; + +export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise => { + const [token, username, password, additionalHeaders] = await Promise.all([ + resolve(options, config.TOKEN), + resolve(options, config.USERNAME), + resolve(options, config.PASSWORD), + resolve(options, config.HEADERS), + ]); + + const headers = Object.entries({ + Accept: 'application/json', + ...additionalHeaders, + ...options.headers, + }) + .filter(([_, value]) => isDefined(value)) + .reduce((headers, [key, value]) => ({ + ...headers, + [key]: String(value), + }), {} as Record); + + if (isStringWithValue(token)) { + headers['Authorization'] = `Bearer ${token}`; + } + + if (isStringWithValue(username) && isStringWithValue(password)) { + const credentials = base64(`${username}:${password}`); + headers['Authorization'] = `Basic ${credentials}`; + } + + if (options.body !== undefined) { + if (options.mediaType) { + headers['Content-Type'] = options.mediaType; + } else if (isBlob(options.body)) { + headers['Content-Type'] = options.body.type || 'application/octet-stream'; + } else if (isString(options.body)) { + headers['Content-Type'] = 'text/plain'; + } else if (!isFormData(options.body)) { + headers['Content-Type'] = 'application/json'; + } + } + + return new Headers(headers); +}; + +export const getRequestBody = (options: ApiRequestOptions): any => { + if (options.body !== undefined) { + if (options.mediaType?.includes('/json')) { + return JSON.stringify(options.body) + } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) { + return options.body; + } else { + return JSON.stringify(options.body); + } + } + return undefined; +}; + +export const sendRequest = async ( + config: OpenAPIConfig, + options: ApiRequestOptions, + url: string, + body: any, + formData: FormData | undefined, + headers: Headers, + onCancel: OnCancel +): Promise => { + const controller = new AbortController(); + + const request: RequestInit = { + headers, + body: body ?? formData, + method: options.method, + signal: controller.signal, + }; + + if (config.WITH_CREDENTIALS) { + request.credentials = config.CREDENTIALS; + } + + onCancel(() => controller.abort()); + + return await fetch(url, request); +}; + +export const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => { + if (responseHeader) { + const content = response.headers.get(responseHeader); + if (isString(content)) { + return content; + } + } + return undefined; +}; + +export const getResponseBody = async (response: Response): Promise => { + if (response.status !== 204) { + try { + const contentType = response.headers.get('Content-Type'); + if (contentType) { + const jsonTypes = ['application/json', 'application/problem+json'] + const isJSON = jsonTypes.some(type => contentType.toLowerCase().startsWith(type)); + if (isJSON) { + return await response.json(); + } else { + return await response.text(); + } + } + } catch (error) { + console.error(error); + } + } + return undefined; +}; + +export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { + const errors: Record = { + 400: 'Bad Request', + 401: 'Unauthorized', + 403: 'Forbidden', + 404: 'Not Found', + 500: 'Internal Server Error', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + ...options.errors, + } + + const error = errors[result.status]; + if (error) { + throw new ApiError(options, result, error); + } + + if (!result.ok) { + const errorStatus = result.status ?? 'unknown'; + const errorStatusText = result.statusText ?? 'unknown'; + const errorBody = (() => { + try { + return JSON.stringify(result.body, null, 2); + } catch (e) { + return undefined; + } + })(); + + throw new ApiError(options, result, + `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` + ); + } +}; + +/** + * Request method + * @param config The OpenAPI configuration object + * @param options The request options from the service + * @returns CancelablePromise + * @throws ApiError + */ +export const request = (config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise => { + return new CancelablePromise(async (resolve, reject, onCancel) => { + try { + const url = getUrl(config, options); + const formData = getFormData(options); + const body = getRequestBody(options); + const headers = await getHeaders(config, options); + + if (!onCancel.isCancelled) { + const response = await sendRequest(config, options, url, body, formData, headers, onCancel); + const responseBody = await getResponseBody(response); + const responseHeader = getResponseHeader(response, options.responseHeader); + + const result: ApiResult = { + url, + ok: response.ok, + status: response.status, + statusText: response.statusText, + body: responseHeader ?? responseBody, + }; + + catchErrorCodes(options, result); + + resolve(result.body); + } + } catch (error) { + reject(error); + } + }); +}; diff --git a/src/frontend/generated/index.ts b/src/frontend/generated/index.ts new file mode 100644 index 0000000000..359c31e8d8 --- /dev/null +++ b/src/frontend/generated/index.ts @@ -0,0 +1,99 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export { ApiError } from './core/ApiError'; +export { CancelablePromise, CancelError } from './core/CancelablePromise'; +export { OpenAPI } from './core/OpenAPI'; +export type { OpenAPIConfig } from './core/OpenAPI'; + +export { ActionEnum } from './models/ActionEnum'; +export { Admin1Enum } from './models/Admin1Enum'; +export { Admin2Enum } from './models/Admin2Enum'; +export { Admin3Enum } from './models/Admin3Enum'; +export type { Admin4Enum } from './models/Admin4Enum'; +export type { Area } from './models/Area'; +export type { AreaList } from './models/AreaList'; +export type { AreaType } from './models/AreaType'; +export type { BeneficiaryGroup } from './models/BeneficiaryGroup'; +export { BlankEnum } from './models/BlankEnum'; +export type { BusinessArea } from './models/BusinessArea'; +export { CollectIndividualDataEnum } from './models/CollectIndividualDataEnum'; +export { CollectTypeEnum } from './models/CollectTypeEnum'; +export { CommsDisabilityEnum } from './models/CommsDisabilityEnum'; +export { ConsentSharingEnum } from './models/ConsentSharingEnum'; +export type { Country } from './models/Country'; +export { CountryEnum } from './models/CountryEnum'; +export { CountryOriginEnum } from './models/CountryOriginEnum'; +export { CurrencyEnum } from './models/CurrencyEnum'; +export { DeduplicationGoldenRecordStatusEnum } from './models/DeduplicationGoldenRecordStatusEnum'; +export type { Delegate } from './models/Delegate'; +export type { DelegatePeople } from './models/DelegatePeople'; +export { DisabilityEnum } from './models/DisabilityEnum'; +export type { Document } from './models/Document'; +export { DocumentStatusEnum } from './models/DocumentStatusEnum'; +export { DocumentTypeEnum } from './models/DocumentTypeEnum'; +export type { FollowUpPaymentPlan } from './models/FollowUpPaymentPlan'; +export { FrequencyOfPaymentsEnum } from './models/FrequencyOfPaymentsEnum'; +export { HearingDisabilityEnum } from './models/HearingDisabilityEnum'; +export type { Household } from './models/Household'; +export type { Individual } from './models/Individual'; +export { MemoryDisabilityEnum } from './models/MemoryDisabilityEnum'; +export type { NullEnum } from './models/NullEnum'; +export { OrgEnumeratorEnum } from './models/OrgEnumeratorEnum'; +export type { PaginatedAreaList } from './models/PaginatedAreaList'; +export type { PaginatedAreaListList } from './models/PaginatedAreaListList'; +export type { PaginatedAreaTypeList } from './models/PaginatedAreaTypeList'; +export type { PaginatedBeneficiaryGroupList } from './models/PaginatedBeneficiaryGroupList'; +export type { PaginatedBusinessAreaList } from './models/PaginatedBusinessAreaList'; +export type { PaginatedCountryList } from './models/PaginatedCountryList'; +export type { PaginatedPaymentPlanList } from './models/PaginatedPaymentPlanList'; +export type { PaginatedPeriodicDataUpdateTemplateListList } from './models/PaginatedPeriodicDataUpdateTemplateListList'; +export type { PaginatedPeriodicDataUpdateUploadListList } from './models/PaginatedPeriodicDataUpdateUploadListList'; +export type { PaginatedPeriodicFieldList } from './models/PaginatedPeriodicFieldList'; +export type { PaginatedProgramCycleListList } from './models/PaginatedProgramCycleListList'; +export type { PaginatedProgramGlobalList } from './models/PaginatedProgramGlobalList'; +export type { PaginatedRegistrationDataImportListList } from './models/PaginatedRegistrationDataImportListList'; +export type { PaginatedTargetPopulationListList } from './models/PaginatedTargetPopulationListList'; +export type { PatchedProgramCycleUpdate } from './models/PatchedProgramCycleUpdate'; +export type { PatchedRDI } from './models/PatchedRDI'; +export type { PaymentPlan } from './models/PaymentPlan'; +export type { PaymentPlanBulkAction } from './models/PaymentPlanBulkAction'; +export type { PaymentPlanSupportingDocument } from './models/PaymentPlanSupportingDocument'; +export type { PeriodicDataUpdateTemplateCreate } from './models/PeriodicDataUpdateTemplateCreate'; +export type { PeriodicDataUpdateTemplateDetail } from './models/PeriodicDataUpdateTemplateDetail'; +export type { PeriodicDataUpdateTemplateList } from './models/PeriodicDataUpdateTemplateList'; +export type { PeriodicDataUpdateUpload } from './models/PeriodicDataUpdateUpload'; +export type { PeriodicDataUpdateUploadDetail } from './models/PeriodicDataUpdateUploadDetail'; +export type { PeriodicDataUpdateUploadList } from './models/PeriodicDataUpdateUploadList'; +export type { PeriodicField } from './models/PeriodicField'; +export type { PeriodicFieldData } from './models/PeriodicFieldData'; +export { PhysicalDisabilityEnum } from './models/PhysicalDisabilityEnum'; +export { PreferredLanguageEnum } from './models/PreferredLanguageEnum'; +export type { Program } from './models/Program'; +export type { ProgramCycleCreate } from './models/ProgramCycleCreate'; +export type { ProgramCycleList } from './models/ProgramCycleList'; +export type { ProgramCycleUpdate } from './models/ProgramCycleUpdate'; +export type { ProgramGlobal } from './models/ProgramGlobal'; +export { ProgramGlobalStatusEnum } from './models/ProgramGlobalStatusEnum'; +export type { PushPeople } from './models/PushPeople'; +export { PushPeopleTypeEnum } from './models/PushPeopleTypeEnum'; +export type { RDI } from './models/RDI'; +export { RdiMergeStatusEnum } from './models/RdiMergeStatusEnum'; +export type { RDINested } from './models/RDINested'; +export type { RegistrationDataImportList } from './models/RegistrationDataImportList'; +export { RegistrationMethodEnum } from './models/RegistrationMethodEnum'; +export { RelationshipEnum } from './models/RelationshipEnum'; +export { ResidenceStatusEnum } from './models/ResidenceStatusEnum'; +export { ScopeEnum } from './models/ScopeEnum'; +export { SectorEnum } from './models/SectorEnum'; +export { SeeingDisabilityEnum } from './models/SeeingDisabilityEnum'; +export { SelfcareDisabilityEnum } from './models/SelfcareDisabilityEnum'; +export { SexEnum } from './models/SexEnum'; +export { SubtypeEnum } from './models/SubtypeEnum'; +export type { TargetPopulationList } from './models/TargetPopulationList'; +export { WorkStatusEnum } from './models/WorkStatusEnum'; + +export { FieldsAttributesService } from './services/FieldsAttributesService'; +export { HhStatusService } from './services/HhStatusService'; +export { RestService } from './services/RestService'; diff --git a/src/frontend/generated/models/ActionEnum.ts b/src/frontend/generated/models/ActionEnum.ts new file mode 100644 index 0000000000..1ac5a16bb9 --- /dev/null +++ b/src/frontend/generated/models/ActionEnum.ts @@ -0,0 +1,30 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `LOCK` - Lock + * * `LOCK_FSP` - Lock FSP + * * `UNLOCK` - Unlock + * * `UNLOCK_FSP` - Unlock FSP + * * `SEND_FOR_APPROVAL` - Send For Approval + * * `APPROVE` - Approve + * * `AUTHORIZE` - Authorize + * * `REVIEW` - Review + * * `REJECT` - Reject + * * `FINISH` - Finish + * * `SEND_TO_PAYMENT_GATEWAY` - Send to Payment Gateway + */ +export enum ActionEnum { + LOCK = 'LOCK', + LOCK_FSP = 'LOCK_FSP', + UNLOCK = 'UNLOCK', + UNLOCK_FSP = 'UNLOCK_FSP', + SEND_FOR_APPROVAL = 'SEND_FOR_APPROVAL', + APPROVE = 'APPROVE', + AUTHORIZE = 'AUTHORIZE', + REVIEW = 'REVIEW', + REJECT = 'REJECT', + FINISH = 'FINISH', + SEND_TO_PAYMENT_GATEWAY = 'SEND_TO_PAYMENT_GATEWAY', +} diff --git a/src/frontend/generated/models/Admin1Enum.ts b/src/frontend/generated/models/Admin1Enum.ts new file mode 100644 index 0000000000..64f5a6e773 --- /dev/null +++ b/src/frontend/generated/models/Admin1Enum.ts @@ -0,0 +1,146 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `AF09` - Baghlan + * * `AF29` - Badghis + * * `AF18` - Balkh + * * `AF28` - Faryab + * * `AF21` - Ghor + * * `AF10` - Bamyan + * * `AF22` - Daykundi + * * `AF31` - Farah + * * `AF27` - Jawzjan + * * `AF01` - Kabul + * * `AF33` - Kandahar + * * `AF32` - Hilmand + * * `AF11` - Ghazni + * * `AF02` - Kapisa + * * `AF26` - Khost + * * `AF30` - Hirat + * * `AF15` - Badakhshan + * * `AF25` - Paktika + * * `AF06` - Nangarhar + * * `AF14` - Nuristan + * * `AF34` - Nimroz + * * `AF12` - Paktya + * * `AF13` - Kunar + * * `AF23` - Uruzgan + * * `AF20` - Sar-e-Pul + * * `AF16` - Takhar + * * `AG06` - Saint Philip - AG05 + * * `AF04` - Wardak + * * `AG08` - Redonda - AG08 + * * `AF24` - Zabul + * * `AF05` - Logar + * * `AG05` - Saint Peter - AG05 + * * `AF03` - Parwan + * * `AG03` - Saint Mary - AG03 + * * `AG04` - Saint Paul - AG04 + * * `AG01` - Saint George - AG01 + * * `AF19` - Samangan + * * `AG07` - Barbuda - AG07 + * * `AF17` - Kunduz + * * `AG02` - Saint John - AG02 + * * `AF08` - Panjsher + * * `AF07` - Laghman + * * `UA07` - Volynska + * * `UA68` - Khmelnytska + * * `UA21` - Zakarpatska + * * `UA46` - Lvivska + * * `UA12` - Dnipropetrovska + * * `UA80` - Kyivska + * * `UA53` - Poltavska + * * `UA73` - Chernivetska + * * `UA01` - Avtonomna Respublika Krym + * * `UA18` - Zhytomyrska + * * `UA48` - Mykolaivska + * * `UA71` - Cherkaska + * * `UA65` - Khersonska + * * `UA85` - Sevastopilska + * * `UA63` - Kharkivska + * * `UA32` - Kyivska + * * `UA05` - Vinnytska + * * `UA51` - Odeska + * * `UA23` - Zaporizka + * * `UA14` - Donetska + * * `UA56` - Rivnenska + * * `UA44` - Luhanska + * * `UA59` - Sumska + * * `UA61` - Ternopilska + * * `UA74` - Chernihivska + * * `UA35` - Kirovohradska + * * `UA26` - Ivano-Frankivska + */ +export enum Admin1Enum { + AF09 = 'AF09', + AF29 = 'AF29', + AF18 = 'AF18', + AF28 = 'AF28', + AF21 = 'AF21', + AF10 = 'AF10', + AF22 = 'AF22', + AF31 = 'AF31', + AF27 = 'AF27', + AF01 = 'AF01', + AF33 = 'AF33', + AF32 = 'AF32', + AF11 = 'AF11', + AF02 = 'AF02', + AF26 = 'AF26', + AF30 = 'AF30', + AF15 = 'AF15', + AF25 = 'AF25', + AF06 = 'AF06', + AF14 = 'AF14', + AF34 = 'AF34', + AF12 = 'AF12', + AF13 = 'AF13', + AF23 = 'AF23', + AF20 = 'AF20', + AF16 = 'AF16', + AG06 = 'AG06', + AF04 = 'AF04', + AG08 = 'AG08', + AF24 = 'AF24', + AF05 = 'AF05', + AG05 = 'AG05', + AF03 = 'AF03', + AG03 = 'AG03', + AG04 = 'AG04', + AG01 = 'AG01', + AF19 = 'AF19', + AG07 = 'AG07', + AF17 = 'AF17', + AG02 = 'AG02', + AF08 = 'AF08', + AF07 = 'AF07', + UA07 = 'UA07', + UA68 = 'UA68', + UA21 = 'UA21', + UA46 = 'UA46', + UA12 = 'UA12', + UA80 = 'UA80', + UA53 = 'UA53', + UA73 = 'UA73', + UA01 = 'UA01', + UA18 = 'UA18', + UA48 = 'UA48', + UA71 = 'UA71', + UA65 = 'UA65', + UA85 = 'UA85', + UA63 = 'UA63', + UA32 = 'UA32', + UA05 = 'UA05', + UA51 = 'UA51', + UA23 = 'UA23', + UA14 = 'UA14', + UA56 = 'UA56', + UA44 = 'UA44', + UA59 = 'UA59', + UA61 = 'UA61', + UA74 = 'UA74', + UA35 = 'UA35', + UA26 = 'UA26', +} diff --git a/src/frontend/generated/models/Admin2Enum.ts b/src/frontend/generated/models/Admin2Enum.ts new file mode 100644 index 0000000000..3633db419c --- /dev/null +++ b/src/frontend/generated/models/Admin2Enum.ts @@ -0,0 +1,1260 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `AF0904` - Doshi + * * `AF0903` - Dahana-e-Ghori + * * `AF0906` - Tala Wa barfak + * * `AF0912` - Dehsalah + * * `AF0908` - Andarab + * * `AF0902` - Baghlan-e-Jadid + * * `AF0910` - Burka + * * `AF0915` - Fereng Wa Gharu + * * `AF0914` - Guzargah-e-Nur + * * `AF0907` - Khenjan + * * `AF0913` - Khost Wa Fereng + * * `AF0909` - Khwajahejran + * * `AF0905` - Nahrin + * * `AF0911` - Pul-e-Hesar + * * `AF0901` - Pul-e-Khumri + * * `AF2905` - Jawand + * * `AF2904` - Qadis + * * `AF2903` - Abkamari + * * `AF2906` - Balamurghab + * * `AF2907` - Ghormach + * * `AF2901` - Qala-e-Naw + * * `AF2902` - Muqur + * * `AF1811` - Sharak-e-Hayratan + * * `AF1805` - Balkh + * * `AF1807` - Charkent + * * `AF1813` - Chemtal + * * `AF1815` - Keshendeh + * * `AF1809` - Khulm + * * `AF1814` - Sholgareh + * * `AF1803` - Shortepa + * * `AF1801` - Mazar-e-Sharif + * * `AF1812` - Charbulak + * * `AF1816` - Zari + * * `AF1806` - Dehdadi + * * `AF1810` - Kaldar + * * `AF1808` - Marmul + * * `AF1802` - Nahr-e-Shahi + * * `AF1804` - Dawlatabad + * * `AF2814` - Qorghan + * * `AF2812` - Qaramqol + * * `AF2809` - Bilcheragh + * * `AF2803` - Pashtunkot + * * `AF2808` - Garziwan + * * `AF2806` - Qaysar + * * `AF2804` - Shirintagab + * * `AF2802` - Khwajasabzposh + * * `AF2801` - Maymana + * * `AF2811` - Andkhoy + * * `AF2805` - Almar + * * `AF2813` - Khan-e-Char Bagh + * * `AF2807` - Kohestan + * * `AF2810` - Dawlatabad + * * `AF2101` - Chaghcharan + * * `AF2102` - Charsadra + * * `AF2104` - Dawlatyar + * * `AF2103` - DoLayna + * * `AF2108` - Lal Wa Sarjangal + * * `AF2107` - Pasaband + * * `AF2110` - Saghar + * * `AF2105` - Shahrak + * * `AF2106` - Taywarah + * * `AF2109` - Tolak + * * `AF1001` - Bamyan + * * `AF1006` - Kahmard + * * `AF1002` - Sayghan + * * `AF1005` - Shibar + * * `AF1007` - Waras + * * `AF1004` - Panjab + * * `AF1003` - Yakawlang + * * `AF2202` - Ashtarlay + * * `AF2205` - Gizab / Patoo + * * `AF2203` - Khadir + * * `AF2208` - Kajran + * * `AF2204` - Kiti + * * `AF2209` - Miramor + * * `AF2207` - Sang-e-Takht + * * `AF2201` - Nili + * * `AF2206` - Shahrestan + * * `AF3109` - Gulestan + * * `AF3104` - Khak-e-Safed + * * `AF3108` - Lash-e-Juwayn + * * `AF3110` - Purchaman + * * `AF3105` - Pushtrod + * * `AF3107` - Shibkoh + * * `AF3102` - Bakwa + * * `AF3101` - Farah + * * `AF3106` - Qala-e-Kah + * * `AF3111` - Anardara + * * `AF3103` - Balabuluk + * * `AF2704` - Qushtepa + * * `AF2708` - Mardyan + * * `AF2710` - Khamyab + * * `AF2707` - Aqcha + * * `AF2711` - Darzab + * * `AF2706` - Khanaqa + * * `AF2703` - Khwajadukoh + * * `AF2709` - Qarqin + * * `AF2701` - Shiberghan + * * `AF2702` - Mingajik + * * `AF2705` - Fayzabad + * * `AF0109` - Kalakan + * * `AF0115` - Farza + * * `AF0114` - Estalef + * * `AF0101` - Kabul + * * `AF0112` - Khak-e-Jabbar + * * `AF0113` - Surobi + * * `AF0105` - Chaharasyab + * * `AF0102` - Dehsabz + * * `AF0107` - Bagrami + * * `AF0111` - Guldara + * * `AF0110` - Mirbachakot + * * `AF0106` - Musayi + * * `AF0104` - Paghman + * * `AF0103` - Shakardara + * * `AF0108` - Qarabagh + * * `AF3310` - Shorabak + * * `AF3307` - Khakrez + * * `AF3316` - Maruf + * * `AF3308` - Maywand + * * `AF3314` - Nesh + * * `AF3304` - Panjwayi + * * `AF3301` - Kandahar / Dand + * * `AF3311` - Spinboldak + * * `AF3303` - Zheray + * * `AF3312` - Arghestan + * * `AF3305` - Daman + * * `AF3315` - Ghorak + * * `AF3313` - Miyanshin + * * `AF3306` - Shahwalikot + * * `AF3309` - Reg + * * `AF3302` - Arghandab + * * `AF3211` - Baghran + * * `AF3210` - Kajaki + * * `AF3213` - Deh-e-Shu + * * `AF3209` - Garmser + * * `AF3201` - Lashkargah + * * `AF3206` - Musaqalah + * * `AF3203` - Nad-e-Ali / Marja + * * `AF3202` - Nahr-e-Saraj + * * `AF3207` - Nawzad + * * `AF3205` - Sangin + * * `AF3204` - Nawa-e-Barakzaiy + * * `AF3208` - Washer + * * `AF3212` - Reg + * * `AF1119` - Nawa + * * `AF1112` - Nawur + * * `AF1117` - Malestan + * * `AF1115` - Abband + * * `AF1116` - Ajrestan + * * `AF1105` - Andar + * * `AF1106` - Dehyak + * * `AF1111` - Giro + * * `AF1108` - Rashidan + * * `AF1104` - Waghaz + * * `AF1103` - Walimuhammad-e-Shahid + * * `AF1107` - Zanakhan + * * `AF1118` - Gelan + * * `AF1113` - Jaghuri + * * `AF1102` - Khwajaumari + * * `AF1101` - Ghazni + * * `AF1114` - Muqur + * * `AF1109` - Jaghatu + * * `AF1110` - Qarabagh + * * `AF0204` - Hisa-e-Duwum-e-Kohestan + * * `AF0202` - Nejrab + * * `AF0206` - Alasay + * * `AF0207` - Hisa-e-Awal-e-Kohestan + * * `AF0203` - Kohband + * * `AF0201` - Mahmud-e-Raqi + * * `AF0205` - Tagab + * * `AF2609` - Qalandar + * * `AF2611` - Spera + * * `AF2605` - Mandozayi + * * `AF2612` - Bak + * * `AF2607` - Gurbuz + * * `AF2613` - Jajimaydan + * * `AF2601` - Khost (Matun) + * * `AF2603` - Musakhel + * * `AF2604` - Nadirshahkot + * * `AF2602` - Sabari + * * `AF2610` - Shamal + * * `AF2606` - Tani + * * `AF2608` - Terezayi + * * `AF3011` - Farsi + * * `AF3009` - Ghoryan + * * `AF3008` - Gulran + * * `AF3002` - Injil + * * `AF3003` - Kushk + * * `AF3015` - Shindand + * * `AF3010` - Adraskan + * * `AF3016` - Chisht-e-Sharif + * * `AF3005` - Guzara + * * `AF3007` - Karukh + * * `AF3014` - Kohsan + * * `AF3013` - Kushk-e-Kohna + * * `AF3012` - Obe + * * `AF3006` - Pashtunzarghun + * * `AF3004` - Zindajan + * * `AF3001` - Hirat + * * `AF1521` - Jorm + * * `AF1518` - Keshem + * * `AF1517` - Khwahan + * * `AF1516` - Kofab + * * `AF1511` - Khash + * * `AF1526` - Koran Wa Monjan + * * `AF1506` - Raghestan + * * `AF1515` - Darwaz-e-Balla + * * `AF1504` - Arghanjkhwa + * * `AF1508` - Shahr-e-Buzorg + * * `AF1503` - Argo + * * `AF1510` - Darayem + * * `AF1525` - Shaki + * * `AF1502` - Yaftal-e-Sufla + * * `AF1507` - Yawan + * * `AF1523` - Eshkashem + * * `AF1514` - Shighnan + * * `AF1513` - Shuhada + * * `AF1524` - Darwaz + * * `AF1509` - Teshkan + * * `AF1528` - Wakhan + * * `AF1522` - Warduj + * * `AF1520` - Yamgan + * * `AF1527` - Zebak + * * `AF1505` - Kohestan + * * `AF1519` - Tagab + * * `AF1512` - Baharak + * * `AF1501` - Fayzabad + * * `AF2503` - Yosufkhel + * * `AF2515` - Bermel + * * `AF2518` - Dila + * * `AF2508` - Gomal + * * `AF2516` - Gyan + * * `AF2502` - Matakhan + * * `AF2511` - Naka + * * `AF2507` - Omna + * * `AF2509` - Sarobi + * * `AF2504` - Sarrawzah + * * `AF2519` - Turwo + * * `AF2510` - Urgun + * * `AF2513` - Wazakhah + * * `AF2514` - Wormamay + * * `AF2506` - Yahyakhel + * * `AF2517` - Ziruk + * * `AF2501` - Sharan + * * `AF2505` - Zarghunshahr + * * `AF2512` - Janikhel + * * `AF0604` - Khogyani + * * `AF0616` - Goshta + * * `AF0610` - Hesarak + * * `AF0614` - Kot + * * `AF0601` - Jalalabad + * * `AF0617` - Achin + * * `AF0615` - Batikot + * * `AF0602` - Behsud + * * `AF0609` - Dara-e-Nur + * * `AF0613` - Dehbala + * * `AF0622` - Durbaba + * * `AF0607` - Kama + * * `AF0608` - Kuzkunar + * * `AF0620` - Lalpur + * * `AF0619` - Muhmand Dara + * * `AF0621` - Nazyan + * * `AF0612` - Pachieragam + * * `AF0606` - Rodat + * * `AF0611` - Sherzad + * * `AF0618` - Shinwar + * * `AF0603` - Surkhrod + * * `AF0605` - Chaparhar + * * `AF1408` - Barg-e-Matal + * * `AF1403` - Duab + * * `AF1407` - Kamdesh + * * `AF1402` - Mandol + * * `AF1404` - Nurgeram + * * `AF1401` - Poruns + * * `AF1405` - Wama + * * `AF1406` - Waygal + * * `AF3403` - Charburjak + * * `AF3402` - Kang + * * `AF3405` - Khashrod / Dularam + * * `AF3404` - Chakhansur + * * `AF3401` - Zaranj + * * `AF1203` - Ahmadaba + * * `AF1208` - Alikhel (Jaji) + * * `AF1210` - Chamkani + * * `AF1211` - Dand Wa Patan + * * `AF1201` - Gardez + * * `AF1205` - Shawak + * * `AF1207` - Lija Ahmad Khel / Laja Mangel + * * `AF1202` - Sayedkaram / Mirzaka + * * `AF1206` - Zadran + * * `AF1204` - Zurmat + * * `AF1209` - Janikhel + * * `AF1306` - Shigal Wa Sheltan + * * `AF1304` - Sarkani + * * `AF1315` - Nari + * * `AF1303` - Narang + * * `AF1301` - Asadabad + * * `AF1311` - Barkunar + * * `AF1313` - Chapadara + * * `AF1307` - Dara-e-Pech + * * `AF1305` - Marawara + * * `AF1314` - Nurgal + * * `AF1302` - Watapur + * * `AF1310` - Dangam + * * `AF1308` - Chawkay + * * `AF1312` - Ghaziabad + * * `AF1309` - Khaskunar + * * `AF2302` - Chora / Chinarto + * * `AF2304` - Dehrawud + * * `AF2305` - Khasuruzgan + * * `AF2303` - Shahid-e-Hassas + * * `AF2301` - Tirinkot + * * `AF2006` - Balkhab + * * `AF2003` - Kohestanat + * * `AF2005` - Gosfandi + * * `AF2007` - Sancharak + * * `AF2001` - Sar-e-Pul + * * `AF2002` - Sayad + * * `AF2004` - Sozmaqala + * * `AF1604` - Bangi + * * `AF1610` - Chahab + * * `AF1605` - Chal + * * `AF1617` - Darqad + * * `AF1613` - Dasht-e-Qala + * * `AF1615` - Eshkmesh + * * `AF1607` - Farkhar + * * `AF1602` - Hazarsumuch + * * `AF1608` - Kalafgan + * * `AF1612` - Khwajabahawuddin + * * `AF1606` - Namakab + * * `AF1609` - Rostaq + * * `AF1601` - Taloqan + * * `AF1616` - Warsaj + * * `AF1614` - Khwajaghar + * * `AF1611` - Yang-e-Qala + * * `AF1603` - Baharak + * * `AG0601` - Collins - AG0601 + * * `AG0602` - Ffryes - AG0602 + * * `AG0603` - Freetown - AG0603 + * * `AG0604` - Glanvilles - AG0604 + * * `AG0605` - Lavingtons - AG0605 + * * `AG0606` - Lyons - AG0606 + * * `AG0607` - Montpelier - AG0607 + * * `AG0608` - Newfield - AG0608 + * * `AG0612` - Saint Philips - AG0612 + * * `AG0609` - Seatons - AG0609 + * * `AG0610` - Sign - AG0610 + * * `AG0611` - Simpson - AG0611 + * * `AG0613` - Willikies - AG0613 + * * `AG0614` - Willoughby - AG0614 + * * `AF0403` - Nerkh + * * `AF0404` - Hesa-e-Awal-e-Behsud + * * `AF0406` - Chak + * * `AF0402` - Jalrez + * * `AF0408` - Markaz-e-Behsud + * * `AF0401` - Maydanshahr + * * `AF0407` - Saydabad + * * `AF0405` - Daymirdad + * * `AF0409` - Jaghatu + * * `AG0801` - Redonda - AG0801 + * * `AF2409` - Atghar + * * `AF2408` - Daychopan + * * `AF2407` - Kakar + * * `AF2403` - Mizan + * * `AF2411` - Nawbahar + * * `AF2401` - Qalat + * * `AF2406` - Shahjoy + * * `AF2405` - Shinkay + * * `AF2410` - Shomulzay + * * `AF2404` - Tarnak Wa Jaldak + * * `AF2402` - Arghandab + * * `AF0503` - Mohammadagha + * * `AF0501` - Pul-e-Alam + * * `AF0507` - Azra + * * `AF0504` - Barakibarak + * * `AF0506` - Kharwar + * * `AF0502` - Khoshi + * * `AF0505` - Charkh + * * `AG0501` - Big Duers - AG0501 + * * `AG0502` - Cocoa Hall - AG0502 + * * `AG0503` - Freemans - AG0503 + * * `AG0504` - Gilberts - AG0504 + * * `AG0505` - Mercers Creek - AG0505 + * * `AG0506` - Parham - AG0506 + * * `AG0507` - Parrys - AG0507 + * * `AG0508` - Vernons - AG0508 + * * `AF0309` - Shekhali + * * `AF0304` - Bagram + * * `AF0301` - Charikar + * * `AF0307` - Ghorband + * * `AF0302` - Jabalussaraj + * * `AF0308` - Koh-e-Safi + * * `AF0306` - Salang + * * `AF0305` - Saydkhel + * * `AF0303` - Shinwari + * * `AF0310` - Surkh-e-Parsa + * * `AG0301` - Bishops - AG0301 + * * `AG0302` - Blubber Valley - AG0302 + * * `AG0303` - Bolans - AG0303 + * * `AG0304` - Cades Bay - AG0304 + * * `AG0305` - Cedar Hall - AG0305 + * * `AG0306` - Claremont - AG0306 + * * `AG0307` - Crabs Hill - AG0307 + * * `AG0308` - Ebenezer - AG0308 + * * `AG0309` - Glebe - AG0309 + * * `AG0310` - Jennings - AG0310 + * * `AG0311` - John Hughes - AG0311 + * * `AG0312` - Johnsons Point - AG0312 + * * `AG0313` - New Division - AG0313 + * * `AG0314` - Old Road - AG0314 + * * `AG0315` - Orange Valley Mill - AG0315 + * * `AG0316` - Sawcolts - AG0316 + * * `AG0317` - Seaforths - AG0317 + * * `AG0318` - Urlings - AG0318 + * * `AG0319` - Yorks - AG0319 + * * `AG0401` - Bethesda - AG0401 + * * `AG0402` - Burkes - AG0402 + * * `AG0403` - Christian Hill - AG0403 + * * `AG0404` - Delaps - AG0404 + * * `AG0405` - English Harbour - AG0405 + * * `AG0406` - Falmouth - AG0406 + * * `AG0407` - Liberta - AG0407 + * * `AG0408` - Mathews - AG0408 + * * `AG0409` - Pattersons - AG0409 + * * `AG0410` - Swetes - AG0410 + * * `AG0101` - Barnes Hill - AG0101 + * * `AG0102` - Carlisle - AG0102 + * * `AG0103` - Coolidge - AG0103 + * * `AG0104` - Crosbies - AG0104 + * * `AG0105` - Fitches Creek - AG0105 + * * `AG0106` - Gunthorpes - AG0106 + * * `AG0107` - Hodges Bay - AG0107 + * * `AG0108` - Marble Hill - AG0108 + * * `AG0109` - New Winthorpes - AG0109 + * * `AG0110` - Osbourn - AG0110 + * * `AG0111` - Paradise View - AG0111 + * * `AG0112` - Paynters - AG0112 + * * `AG0113` - Piggotts - AG0113 + * * `AG0114` - Sea View Farm - AG0114 + * * `AF1901` - Aybak + * * `AF1904` - Dara-e-Suf-e-Payin + * * `AF1905` - Dara-e Suf-e-Bala + * * `AF1903` - Feroznakhchir + * * `AF1902` - Hazrat-e-Sultan + * * `AF1906` - Khuram Wa Sarbagh + * * `AF1907` - Ruy-e-Duab + * * `AG0701` - Codrington - AG0701 + * * `AF1701` - Kunduz + * * `AF1702` - Emamsaheb + * * `AF1704` - Chardarah + * * `AF1706` - Khanabad + * * `AF1705` - Aliabad + * * `AF1707` - Dasht-e-Archi + * * `AF1703` - Qala-e-Zal + * * `AG0201` - Aberdeen - AG0201 + * * `AG0202` - Bendals - AG0202 + * * `AG0203` - Branns Hamlet - AG0203 + * * `AG0204` - Buckleys - AG0204 + * * `AG0205` - Cedar Grove - AG0205 + * * `AG0206` - Cooks Hill - AG0206 + * * `AG0207` - Cooks New Extension - AG0207 + * * `AG0208` - Emanuel - AG0208 + * * `AG0209` - Five Islands - AG0209 + * * `AG0210` - Gamble's Terrace - AG0210 + * * `AG0211` - Golden Grove - AG0211 + * * `AG0212` - Gray Hill - AG0212 + * * `AG0213` - Grays Farm - AG0213 + * * `AG0214` - Green Bay - AG0214 + * * `AG0215` - Nut Grove - AG0215 + * * `AG0216` - Renfrew - AG0216 + * * `AG0217` - Saint John's - AG0217 + * * `AG0218` - Tomlinson - AG0218 + * * `AG0219` - Upper Gamble's - AG0219 + * * `AG0220` - Villa - AG0220 + * * `AG0221` - Weatherhills - AG0221 + * * `AF0801` - Bazarak + * * `AF0804` - Dara / Ab Shar + * * `AF0805` - Khenj (Hes-e-Awal) + * * `AF0806` - Onaba (Anawa) + * * `AF0807` - Paryan + * * `AF0803` - Rukha + * * `AF0802` - Shutul + * * `AF0704` - Alingar + * * `AF0702` - Alishang + * * `AF0705` - Dawlatshah + * * `AF0701` - Mehtarlam / Bad Pash + * * `AF0703` - Qarghayi + * * `UA0708` - Lutskyi + * * `UA0702` - Volodymyr-Volynskyi + * * `UA0706` - Kovelskyi + * * `UA0704` - Kamin-Kashyrskyi + * * `UA6804` - Khmelnytskyi + * * `UA6806` - Shepetivskyi + * * `UA6802` - Kamianets-Podilskyi + * * `UA2102` - Berehivskyi + * * `UA2108` - Tiachivskyi + * * `UA2112` - Khustskyi + * * `UA2110` - Uzhhorodskyi + * * `UA2106` - Rakhivskyi + * * `UA2104` - Mukachivskyi + * * `UA4604` - Zolochivskyi + * * `UA4606` - Lvivskyi + * * `UA4614` - Yavorivskyi + * * `UA4612` - Chervonohradskyi + * * `UA4610` - Stryiskyi + * * `UA4602` - Drohobytskyi + * * `UA4608` - Sambirskyi + * * `UA1206` - Kryvorizkyi + * * `UA1202` - Dniprovskyi + * * `UA1204` - Kamianskyi + * * `UA1212` - Pavlohradskyi + * * `UA1210` - Novomoskovskyi + * * `UA1208` - Nikopolskyi + * * `UA1214` - Synelnykivskyi + * * `UA8000` - Kyivska + * * `UA5304` - Lubenskyi + * * `UA5302` - Kremenchutskyi + * * `UA5308` - Poltavskyi + * * `UA5306` - Myrhorodskyi + * * `UA7306` - Cnernivetskyi + * * `UA7304` - Dnistrovskyi + * * `UA7302` - Vyzhnytskyi + * * `UA0112` - Kurmanskyi + * * `UA0108` - Yevpatoriiskyi + * * `UA0104` - Bilohirskyi + * * `UA0106` - Dzhankoiskyi + * * `UA0102` - Bakhchysaraiskyi + * * `UA0116` - Simferopolskyi + * * `UA0114` - Perekopskyi + * * `UA0118` - Feodosiiskyi + * * `UA0110` - Kerchynskyi + * * `UA0120` - Yaltynskyi + * * `UA1804` - Zhytomyrskyi + * * `UA1808` - Novohrad-Volynskyi + * * `UA1802` - Berdychivskyi + * * `UA1806` - Korostenskyi + * * `UA4804` - Voznesenskyi + * * `UA4808` - Pervomaiskyi + * * `UA4802` - Bashtanskyi + * * `UA4806` - Mykolaivskyi + * * `UA7102` - Zvenyhorodskyi + * * `UA7108` - Cherkaskyi + * * `UA7104` - Zolotoniskyi + * * `UA7106` - Umanskyi + * * `UA6504` - Henicheskyi + * * `UA6508` - Skadovskyi + * * `UA6502` - Beryslavskyi + * * `UA6510` - Khersonskyi + * * `UA6506` - Kakhovskyi + * * `UA8500` - Sevastopilska + * * `UA6306` - Krasnohradskyi + * * `UA6314` - Chuhuivskyi + * * `UA6310` - Lozivskyi + * * `UA6302` - Bohodukhivskyi + * * `UA6304` - Iziumskyi + * * `UA6308` - Kupianskyi + * * `UA6312` - Kharkivskyi + * * `UA3208` - Buchanskyi + * * `UA3202` - Bilotserkivskyi + * * `UA3210` - Vyshhorodskyi + * * `UA3200` - Chornobylska zona vidchuzhennia + * * `UA3214` - Fastivskyi + * * `UA3212` - Obukhivskyi + * * `UA3204` - Boryspilskyi + * * `UA3206` - Brovarskyi + * * `UA0510` - Tulchynskyi + * * `UA0504` - Haisynskyi + * * `UA0506` - Zhmerynskyi + * * `UA0508` - Mohyliv-Podilskyi + * * `UA0502` - Vinnytskyi + * * `UA0512` - Khmilnytskyi + * * `UA5106` - Bolhradskyi + * * `UA5108` - Izmailskyi + * * `UA5110` - Odeskyi + * * `UA5114` - Rozdilnianskyi + * * `UA5102` - Berezivskyi + * * `UA5112` - Podilskyi + * * `UA5104` - Bilhorod-Dnistrovskyi + * * `UA2302` - Berdianskyi + * * `UA2308` - Melitopolskyi + * * `UA2306` - Zaporizkyi + * * `UA2304` - Vasylivskyi + * * `UA2310` - Polohivskyi + * * `UA1410` - Kalmiuskyi + * * `UA1412` - Kramatorskyi + * * `UA1408` - Donetskyi + * * `UA1404` - Volnovaskyi + * * `UA1414` - Mariupolskyi + * * `UA1402` - Bakhmutskyi + * * `UA1406` - Horlivskyi + * * `UA1416` - Pokrovskyi + * * `UA5604` - Dubenskyi + * * `UA5606` - Rivnenskyi + * * `UA5608` - Sarnenskyi + * * `UA5602` - Varaskyi + * * `UA4402` - Alchevskyi + * * `UA4410` - Svativskyi + * * `UA4412` - Sievierodonetskyi + * * `UA4408` - Rovenkivskyi + * * `UA4404` - Dovzhanskyi + * * `UA4414` - Starobilskyi + * * `UA4406` - Luhanskyi + * * `UA4416` - Shchastynskyi + * * `UA5902` - Konotopskyi + * * `UA5904` - Okhtyrskyi + * * `UA5910` - Shostkynskyi + * * `UA5908` - Sumskyi + * * `UA5906` - Romenskyi + * * `UA6102` - Kremenetskyi + * * `UA6104` - Ternopilskyi + * * `UA6106` - Chortkivskyi + * * `UA7402` - Koriukivskyi + * * `UA7410` - Chernihivskyi + * * `UA7408` - Prylutskyi + * * `UA7406` - Novhorod-Siverskyi + * * `UA7404` - Nizhynskyi + * * `UA3502` - Holovanivskyi + * * `UA3506` - Novoukrainskyi + * * `UA3508` - Oleksandriiskyi + * * `UA3504` - Kropyvnytskyi + * * `UA2610` - Kosivskyi + * * `UA2608` - Kolomyiskyi + * * `UA2612` - Nadvirnianskyi + * * `UA2604` - Ivano-Frankivskyi + * * `UA2602` - Verkhovynskyi + * * `UA2606` - Kaluskyi + */ +export enum Admin2Enum { + AF0904 = 'AF0904', + AF0903 = 'AF0903', + AF0906 = 'AF0906', + AF0912 = 'AF0912', + AF0908 = 'AF0908', + AF0902 = 'AF0902', + AF0910 = 'AF0910', + AF0915 = 'AF0915', + AF0914 = 'AF0914', + AF0907 = 'AF0907', + AF0913 = 'AF0913', + AF0909 = 'AF0909', + AF0905 = 'AF0905', + AF0911 = 'AF0911', + AF0901 = 'AF0901', + AF2905 = 'AF2905', + AF2904 = 'AF2904', + AF2903 = 'AF2903', + AF2906 = 'AF2906', + AF2907 = 'AF2907', + AF2901 = 'AF2901', + AF2902 = 'AF2902', + AF1811 = 'AF1811', + AF1805 = 'AF1805', + AF1807 = 'AF1807', + AF1813 = 'AF1813', + AF1815 = 'AF1815', + AF1809 = 'AF1809', + AF1814 = 'AF1814', + AF1803 = 'AF1803', + AF1801 = 'AF1801', + AF1812 = 'AF1812', + AF1816 = 'AF1816', + AF1806 = 'AF1806', + AF1810 = 'AF1810', + AF1808 = 'AF1808', + AF1802 = 'AF1802', + AF1804 = 'AF1804', + AF2814 = 'AF2814', + AF2812 = 'AF2812', + AF2809 = 'AF2809', + AF2803 = 'AF2803', + AF2808 = 'AF2808', + AF2806 = 'AF2806', + AF2804 = 'AF2804', + AF2802 = 'AF2802', + AF2801 = 'AF2801', + AF2811 = 'AF2811', + AF2805 = 'AF2805', + AF2813 = 'AF2813', + AF2807 = 'AF2807', + AF2810 = 'AF2810', + AF2101 = 'AF2101', + AF2102 = 'AF2102', + AF2104 = 'AF2104', + AF2103 = 'AF2103', + AF2108 = 'AF2108', + AF2107 = 'AF2107', + AF2110 = 'AF2110', + AF2105 = 'AF2105', + AF2106 = 'AF2106', + AF2109 = 'AF2109', + AF1001 = 'AF1001', + AF1006 = 'AF1006', + AF1002 = 'AF1002', + AF1005 = 'AF1005', + AF1007 = 'AF1007', + AF1004 = 'AF1004', + AF1003 = 'AF1003', + AF2202 = 'AF2202', + AF2205 = 'AF2205', + AF2203 = 'AF2203', + AF2208 = 'AF2208', + AF2204 = 'AF2204', + AF2209 = 'AF2209', + AF2207 = 'AF2207', + AF2201 = 'AF2201', + AF2206 = 'AF2206', + AF3109 = 'AF3109', + AF3104 = 'AF3104', + AF3108 = 'AF3108', + AF3110 = 'AF3110', + AF3105 = 'AF3105', + AF3107 = 'AF3107', + AF3102 = 'AF3102', + AF3101 = 'AF3101', + AF3106 = 'AF3106', + AF3111 = 'AF3111', + AF3103 = 'AF3103', + AF2704 = 'AF2704', + AF2708 = 'AF2708', + AF2710 = 'AF2710', + AF2707 = 'AF2707', + AF2711 = 'AF2711', + AF2706 = 'AF2706', + AF2703 = 'AF2703', + AF2709 = 'AF2709', + AF2701 = 'AF2701', + AF2702 = 'AF2702', + AF2705 = 'AF2705', + AF0109 = 'AF0109', + AF0115 = 'AF0115', + AF0114 = 'AF0114', + AF0101 = 'AF0101', + AF0112 = 'AF0112', + AF0113 = 'AF0113', + AF0105 = 'AF0105', + AF0102 = 'AF0102', + AF0107 = 'AF0107', + AF0111 = 'AF0111', + AF0110 = 'AF0110', + AF0106 = 'AF0106', + AF0104 = 'AF0104', + AF0103 = 'AF0103', + AF0108 = 'AF0108', + AF3310 = 'AF3310', + AF3307 = 'AF3307', + AF3316 = 'AF3316', + AF3308 = 'AF3308', + AF3314 = 'AF3314', + AF3304 = 'AF3304', + AF3301 = 'AF3301', + AF3311 = 'AF3311', + AF3303 = 'AF3303', + AF3312 = 'AF3312', + AF3305 = 'AF3305', + AF3315 = 'AF3315', + AF3313 = 'AF3313', + AF3306 = 'AF3306', + AF3309 = 'AF3309', + AF3302 = 'AF3302', + AF3211 = 'AF3211', + AF3210 = 'AF3210', + AF3213 = 'AF3213', + AF3209 = 'AF3209', + AF3201 = 'AF3201', + AF3206 = 'AF3206', + AF3203 = 'AF3203', + AF3202 = 'AF3202', + AF3207 = 'AF3207', + AF3205 = 'AF3205', + AF3204 = 'AF3204', + AF3208 = 'AF3208', + AF3212 = 'AF3212', + AF1119 = 'AF1119', + AF1112 = 'AF1112', + AF1117 = 'AF1117', + AF1115 = 'AF1115', + AF1116 = 'AF1116', + AF1105 = 'AF1105', + AF1106 = 'AF1106', + AF1111 = 'AF1111', + AF1108 = 'AF1108', + AF1104 = 'AF1104', + AF1103 = 'AF1103', + AF1107 = 'AF1107', + AF1118 = 'AF1118', + AF1113 = 'AF1113', + AF1102 = 'AF1102', + AF1101 = 'AF1101', + AF1114 = 'AF1114', + AF1109 = 'AF1109', + AF1110 = 'AF1110', + AF0204 = 'AF0204', + AF0202 = 'AF0202', + AF0206 = 'AF0206', + AF0207 = 'AF0207', + AF0203 = 'AF0203', + AF0201 = 'AF0201', + AF0205 = 'AF0205', + AF2609 = 'AF2609', + AF2611 = 'AF2611', + AF2605 = 'AF2605', + AF2612 = 'AF2612', + AF2607 = 'AF2607', + AF2613 = 'AF2613', + AF2601 = 'AF2601', + AF2603 = 'AF2603', + AF2604 = 'AF2604', + AF2602 = 'AF2602', + AF2610 = 'AF2610', + AF2606 = 'AF2606', + AF2608 = 'AF2608', + AF3011 = 'AF3011', + AF3009 = 'AF3009', + AF3008 = 'AF3008', + AF3002 = 'AF3002', + AF3003 = 'AF3003', + AF3015 = 'AF3015', + AF3010 = 'AF3010', + AF3016 = 'AF3016', + AF3005 = 'AF3005', + AF3007 = 'AF3007', + AF3014 = 'AF3014', + AF3013 = 'AF3013', + AF3012 = 'AF3012', + AF3006 = 'AF3006', + AF3004 = 'AF3004', + AF3001 = 'AF3001', + AF1521 = 'AF1521', + AF1518 = 'AF1518', + AF1517 = 'AF1517', + AF1516 = 'AF1516', + AF1511 = 'AF1511', + AF1526 = 'AF1526', + AF1506 = 'AF1506', + AF1515 = 'AF1515', + AF1504 = 'AF1504', + AF1508 = 'AF1508', + AF1503 = 'AF1503', + AF1510 = 'AF1510', + AF1525 = 'AF1525', + AF1502 = 'AF1502', + AF1507 = 'AF1507', + AF1523 = 'AF1523', + AF1514 = 'AF1514', + AF1513 = 'AF1513', + AF1524 = 'AF1524', + AF1509 = 'AF1509', + AF1528 = 'AF1528', + AF1522 = 'AF1522', + AF1520 = 'AF1520', + AF1527 = 'AF1527', + AF1505 = 'AF1505', + AF1519 = 'AF1519', + AF1512 = 'AF1512', + AF1501 = 'AF1501', + AF2503 = 'AF2503', + AF2515 = 'AF2515', + AF2518 = 'AF2518', + AF2508 = 'AF2508', + AF2516 = 'AF2516', + AF2502 = 'AF2502', + AF2511 = 'AF2511', + AF2507 = 'AF2507', + AF2509 = 'AF2509', + AF2504 = 'AF2504', + AF2519 = 'AF2519', + AF2510 = 'AF2510', + AF2513 = 'AF2513', + AF2514 = 'AF2514', + AF2506 = 'AF2506', + AF2517 = 'AF2517', + AF2501 = 'AF2501', + AF2505 = 'AF2505', + AF2512 = 'AF2512', + AF0604 = 'AF0604', + AF0616 = 'AF0616', + AF0610 = 'AF0610', + AF0614 = 'AF0614', + AF0601 = 'AF0601', + AF0617 = 'AF0617', + AF0615 = 'AF0615', + AF0602 = 'AF0602', + AF0609 = 'AF0609', + AF0613 = 'AF0613', + AF0622 = 'AF0622', + AF0607 = 'AF0607', + AF0608 = 'AF0608', + AF0620 = 'AF0620', + AF0619 = 'AF0619', + AF0621 = 'AF0621', + AF0612 = 'AF0612', + AF0606 = 'AF0606', + AF0611 = 'AF0611', + AF0618 = 'AF0618', + AF0603 = 'AF0603', + AF0605 = 'AF0605', + AF1408 = 'AF1408', + AF1403 = 'AF1403', + AF1407 = 'AF1407', + AF1402 = 'AF1402', + AF1404 = 'AF1404', + AF1401 = 'AF1401', + AF1405 = 'AF1405', + AF1406 = 'AF1406', + AF3403 = 'AF3403', + AF3402 = 'AF3402', + AF3405 = 'AF3405', + AF3404 = 'AF3404', + AF3401 = 'AF3401', + AF1203 = 'AF1203', + AF1208 = 'AF1208', + AF1210 = 'AF1210', + AF1211 = 'AF1211', + AF1201 = 'AF1201', + AF1205 = 'AF1205', + AF1207 = 'AF1207', + AF1202 = 'AF1202', + AF1206 = 'AF1206', + AF1204 = 'AF1204', + AF1209 = 'AF1209', + AF1306 = 'AF1306', + AF1304 = 'AF1304', + AF1315 = 'AF1315', + AF1303 = 'AF1303', + AF1301 = 'AF1301', + AF1311 = 'AF1311', + AF1313 = 'AF1313', + AF1307 = 'AF1307', + AF1305 = 'AF1305', + AF1314 = 'AF1314', + AF1302 = 'AF1302', + AF1310 = 'AF1310', + AF1308 = 'AF1308', + AF1312 = 'AF1312', + AF1309 = 'AF1309', + AF2302 = 'AF2302', + AF2304 = 'AF2304', + AF2305 = 'AF2305', + AF2303 = 'AF2303', + AF2301 = 'AF2301', + AF2006 = 'AF2006', + AF2003 = 'AF2003', + AF2005 = 'AF2005', + AF2007 = 'AF2007', + AF2001 = 'AF2001', + AF2002 = 'AF2002', + AF2004 = 'AF2004', + AF1604 = 'AF1604', + AF1610 = 'AF1610', + AF1605 = 'AF1605', + AF1617 = 'AF1617', + AF1613 = 'AF1613', + AF1615 = 'AF1615', + AF1607 = 'AF1607', + AF1602 = 'AF1602', + AF1608 = 'AF1608', + AF1612 = 'AF1612', + AF1606 = 'AF1606', + AF1609 = 'AF1609', + AF1601 = 'AF1601', + AF1616 = 'AF1616', + AF1614 = 'AF1614', + AF1611 = 'AF1611', + AF1603 = 'AF1603', + AG0601 = 'AG0601', + AG0602 = 'AG0602', + AG0603 = 'AG0603', + AG0604 = 'AG0604', + AG0605 = 'AG0605', + AG0606 = 'AG0606', + AG0607 = 'AG0607', + AG0608 = 'AG0608', + AG0612 = 'AG0612', + AG0609 = 'AG0609', + AG0610 = 'AG0610', + AG0611 = 'AG0611', + AG0613 = 'AG0613', + AG0614 = 'AG0614', + AF0403 = 'AF0403', + AF0404 = 'AF0404', + AF0406 = 'AF0406', + AF0402 = 'AF0402', + AF0408 = 'AF0408', + AF0401 = 'AF0401', + AF0407 = 'AF0407', + AF0405 = 'AF0405', + AF0409 = 'AF0409', + AG0801 = 'AG0801', + AF2409 = 'AF2409', + AF2408 = 'AF2408', + AF2407 = 'AF2407', + AF2403 = 'AF2403', + AF2411 = 'AF2411', + AF2401 = 'AF2401', + AF2406 = 'AF2406', + AF2405 = 'AF2405', + AF2410 = 'AF2410', + AF2404 = 'AF2404', + AF2402 = 'AF2402', + AF0503 = 'AF0503', + AF0501 = 'AF0501', + AF0507 = 'AF0507', + AF0504 = 'AF0504', + AF0506 = 'AF0506', + AF0502 = 'AF0502', + AF0505 = 'AF0505', + AG0501 = 'AG0501', + AG0502 = 'AG0502', + AG0503 = 'AG0503', + AG0504 = 'AG0504', + AG0505 = 'AG0505', + AG0506 = 'AG0506', + AG0507 = 'AG0507', + AG0508 = 'AG0508', + AF0309 = 'AF0309', + AF0304 = 'AF0304', + AF0301 = 'AF0301', + AF0307 = 'AF0307', + AF0302 = 'AF0302', + AF0308 = 'AF0308', + AF0306 = 'AF0306', + AF0305 = 'AF0305', + AF0303 = 'AF0303', + AF0310 = 'AF0310', + AG0301 = 'AG0301', + AG0302 = 'AG0302', + AG0303 = 'AG0303', + AG0304 = 'AG0304', + AG0305 = 'AG0305', + AG0306 = 'AG0306', + AG0307 = 'AG0307', + AG0308 = 'AG0308', + AG0309 = 'AG0309', + AG0310 = 'AG0310', + AG0311 = 'AG0311', + AG0312 = 'AG0312', + AG0313 = 'AG0313', + AG0314 = 'AG0314', + AG0315 = 'AG0315', + AG0316 = 'AG0316', + AG0317 = 'AG0317', + AG0318 = 'AG0318', + AG0319 = 'AG0319', + AG0401 = 'AG0401', + AG0402 = 'AG0402', + AG0403 = 'AG0403', + AG0404 = 'AG0404', + AG0405 = 'AG0405', + AG0406 = 'AG0406', + AG0407 = 'AG0407', + AG0408 = 'AG0408', + AG0409 = 'AG0409', + AG0410 = 'AG0410', + AG0101 = 'AG0101', + AG0102 = 'AG0102', + AG0103 = 'AG0103', + AG0104 = 'AG0104', + AG0105 = 'AG0105', + AG0106 = 'AG0106', + AG0107 = 'AG0107', + AG0108 = 'AG0108', + AG0109 = 'AG0109', + AG0110 = 'AG0110', + AG0111 = 'AG0111', + AG0112 = 'AG0112', + AG0113 = 'AG0113', + AG0114 = 'AG0114', + AF1901 = 'AF1901', + AF1904 = 'AF1904', + AF1905 = 'AF1905', + AF1903 = 'AF1903', + AF1902 = 'AF1902', + AF1906 = 'AF1906', + AF1907 = 'AF1907', + AG0701 = 'AG0701', + AF1701 = 'AF1701', + AF1702 = 'AF1702', + AF1704 = 'AF1704', + AF1706 = 'AF1706', + AF1705 = 'AF1705', + AF1707 = 'AF1707', + AF1703 = 'AF1703', + AG0201 = 'AG0201', + AG0202 = 'AG0202', + AG0203 = 'AG0203', + AG0204 = 'AG0204', + AG0205 = 'AG0205', + AG0206 = 'AG0206', + AG0207 = 'AG0207', + AG0208 = 'AG0208', + AG0209 = 'AG0209', + AG0210 = 'AG0210', + AG0211 = 'AG0211', + AG0212 = 'AG0212', + AG0213 = 'AG0213', + AG0214 = 'AG0214', + AG0215 = 'AG0215', + AG0216 = 'AG0216', + AG0217 = 'AG0217', + AG0218 = 'AG0218', + AG0219 = 'AG0219', + AG0220 = 'AG0220', + AG0221 = 'AG0221', + AF0801 = 'AF0801', + AF0804 = 'AF0804', + AF0805 = 'AF0805', + AF0806 = 'AF0806', + AF0807 = 'AF0807', + AF0803 = 'AF0803', + AF0802 = 'AF0802', + AF0704 = 'AF0704', + AF0702 = 'AF0702', + AF0705 = 'AF0705', + AF0701 = 'AF0701', + AF0703 = 'AF0703', + UA0708 = 'UA0708', + UA0702 = 'UA0702', + UA0706 = 'UA0706', + UA0704 = 'UA0704', + UA6804 = 'UA6804', + UA6806 = 'UA6806', + UA6802 = 'UA6802', + UA2102 = 'UA2102', + UA2108 = 'UA2108', + UA2112 = 'UA2112', + UA2110 = 'UA2110', + UA2106 = 'UA2106', + UA2104 = 'UA2104', + UA4604 = 'UA4604', + UA4606 = 'UA4606', + UA4614 = 'UA4614', + UA4612 = 'UA4612', + UA4610 = 'UA4610', + UA4602 = 'UA4602', + UA4608 = 'UA4608', + UA1206 = 'UA1206', + UA1202 = 'UA1202', + UA1204 = 'UA1204', + UA1212 = 'UA1212', + UA1210 = 'UA1210', + UA1208 = 'UA1208', + UA1214 = 'UA1214', + UA8000 = 'UA8000', + UA5304 = 'UA5304', + UA5302 = 'UA5302', + UA5308 = 'UA5308', + UA5306 = 'UA5306', + UA7306 = 'UA7306', + UA7304 = 'UA7304', + UA7302 = 'UA7302', + UA0112 = 'UA0112', + UA0108 = 'UA0108', + UA0104 = 'UA0104', + UA0106 = 'UA0106', + UA0102 = 'UA0102', + UA0116 = 'UA0116', + UA0114 = 'UA0114', + UA0118 = 'UA0118', + UA0110 = 'UA0110', + UA0120 = 'UA0120', + UA1804 = 'UA1804', + UA1808 = 'UA1808', + UA1802 = 'UA1802', + UA1806 = 'UA1806', + UA4804 = 'UA4804', + UA4808 = 'UA4808', + UA4802 = 'UA4802', + UA4806 = 'UA4806', + UA7102 = 'UA7102', + UA7108 = 'UA7108', + UA7104 = 'UA7104', + UA7106 = 'UA7106', + UA6504 = 'UA6504', + UA6508 = 'UA6508', + UA6502 = 'UA6502', + UA6510 = 'UA6510', + UA6506 = 'UA6506', + UA8500 = 'UA8500', + UA6306 = 'UA6306', + UA6314 = 'UA6314', + UA6310 = 'UA6310', + UA6302 = 'UA6302', + UA6304 = 'UA6304', + UA6308 = 'UA6308', + UA6312 = 'UA6312', + UA3208 = 'UA3208', + UA3202 = 'UA3202', + UA3210 = 'UA3210', + UA3200 = 'UA3200', + UA3214 = 'UA3214', + UA3212 = 'UA3212', + UA3204 = 'UA3204', + UA3206 = 'UA3206', + UA0510 = 'UA0510', + UA0504 = 'UA0504', + UA0506 = 'UA0506', + UA0508 = 'UA0508', + UA0502 = 'UA0502', + UA0512 = 'UA0512', + UA5106 = 'UA5106', + UA5108 = 'UA5108', + UA5110 = 'UA5110', + UA5114 = 'UA5114', + UA5102 = 'UA5102', + UA5112 = 'UA5112', + UA5104 = 'UA5104', + UA2302 = 'UA2302', + UA2308 = 'UA2308', + UA2306 = 'UA2306', + UA2304 = 'UA2304', + UA2310 = 'UA2310', + UA1410 = 'UA1410', + UA1412 = 'UA1412', + UA1408 = 'UA1408', + UA1404 = 'UA1404', + UA1414 = 'UA1414', + UA1402 = 'UA1402', + UA1406 = 'UA1406', + UA1416 = 'UA1416', + UA5604 = 'UA5604', + UA5606 = 'UA5606', + UA5608 = 'UA5608', + UA5602 = 'UA5602', + UA4402 = 'UA4402', + UA4410 = 'UA4410', + UA4412 = 'UA4412', + UA4408 = 'UA4408', + UA4404 = 'UA4404', + UA4414 = 'UA4414', + UA4406 = 'UA4406', + UA4416 = 'UA4416', + UA5902 = 'UA5902', + UA5904 = 'UA5904', + UA5910 = 'UA5910', + UA5908 = 'UA5908', + UA5906 = 'UA5906', + UA6102 = 'UA6102', + UA6104 = 'UA6104', + UA6106 = 'UA6106', + UA7402 = 'UA7402', + UA7410 = 'UA7410', + UA7408 = 'UA7408', + UA7406 = 'UA7406', + UA7404 = 'UA7404', + UA3502 = 'UA3502', + UA3506 = 'UA3506', + UA3508 = 'UA3508', + UA3504 = 'UA3504', + UA2610 = 'UA2610', + UA2608 = 'UA2608', + UA2612 = 'UA2612', + UA2604 = 'UA2604', + UA2602 = 'UA2602', + UA2606 = 'UA2606', +} diff --git a/src/frontend/generated/models/Admin3Enum.ts b/src/frontend/generated/models/Admin3Enum.ts new file mode 100644 index 0000000000..5964fe239f --- /dev/null +++ b/src/frontend/generated/models/Admin3Enum.ts @@ -0,0 +1,486 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `UA0112065` - Stakhanovska + * * `UA0112001` - Abrykosivska + * * `UA0112067` - Stepnivska + * * `UA0112015` - Kalininska + * * `UA0112069` - Susaninska + * * `UA0112003` - Amurska + * * `UA0112071` - Chernovska + * * `UA0112005` - Voikovska + * * `UA0112073` - Yantarnenska + * * `UA0112017` - Kalininska + * * `UA0112007` - Voskhodnenska + * * `UA0112025` - Kotelnykivska + * * `UA0112009` - Hvardiiska + * * `UA0112011` - Hryshynska + * * `UA0112013` - Zernivska + * * `UA0112021` - Kolodiazianska + * * `UA0112019` - Klepyninska + * * `UA0112023` - Kormivska + * * `UA0112027` - Krasnohvardiiska + * * `UA0112029` - Krasnoznamianska + * * `UA0112031` - Krestianivska + * * `UA0112033` - Leninska + * * `UA0112035` - Marianivska + * * `UA0112037` - Naidonivska + * * `UA0112039` - Novopokrovska + * * `UA0112041` - Oktiabrska + * * `UA0112043` - Oktiabrska + * * `UA0112045` - Oleksandrivska + * * `UA0112047` - Oleksiivska + * * `UA0112049` - Ostrovska + * * `UA0112051` - Piatykhatska + * * `UA0112053` - Pervomaiska + * * `UA0112055` - Petrivska + * * `UA0112057` - Poltavska + * * `UA0112059` - Pravdivska + * * `UA0112061` - Rivnivska + * * `UA0112063` - Sarybashivska + * * `UA0108079` - Shtormivska + * * `UA0108053` - Novofedorivska + * * `UA0108021` - Ivanivska + * * `UA0108003` - Veselivska + * * `UA0108001` - Veresaievska + * * `UA0108019` - Zernivska + * * `UA0108009` - Heroiska + * * `UA0108005` - Vynohradivska + * * `UA0108007` - Vorobiovska + * * `UA0108017` - Zaozernenska + * * `UA0108013` - Dobrushynska + * * `UA0108015` - Yevpatoriiska + * * `UA0108051` - Novosilska + * * `UA0108033` - Krymska + * * `UA0108025` - Koltsovska + * * `UA0108023` - Kirovska + * * `UA0108031` - Krasnoiarska + * * `UA0108027` - Krainenska + * * `UA0108029` - Krasnopolianska + * * `UA0108049` - Novoozernivska + * * `UA0108039` - Myrnivska + * * `UA0108035` - Lisnivska + * * `UA0108037` - Medvedivska + * * `UA0108047` - Novoivanivska + * * `UA0108041` - Mytiaivska + * * `UA0108045` - Molochnenska + * * `UA0108069` - Stovpivska + * * `UA0108059` - Orikhivska + * * `UA0108055` - Okunivska + * * `UA0108057` - Olenivska + * * `UA0108067` - Syzivska + * * `UA0108061` - Okhotnykivska + * * `UA0108065` - Sakska + * * `UA0108075` - Frunzenska + * * `UA0108071` - Suvorovska + * * `UA0108073` - Uiutnenska + * * `UA0108077` - Chornomorska + * * `UA0108063` - Romashkinska + * * `UA0108043` - Mizhvodnenska + * * `UA0108011` - Dalekivska + * * `UA0104043` - Mytrofanivska + * * `UA0104001` - Aromatnivska + * * `UA0104003` - Bahativska + * * `UA0104007` - Vasylivska + * * `UA0104005` - Bilohirska + * * `UA0104011` - Drofynska + * * `UA0104013` - Zheliabovska + * * `UA0104009` - Vyshenska + * * `UA0104015` - Zhemchuzhynska + * * `UA0104017` - Zelenohirska + * * `UA0104019` - Zemlianychnenska + * * `UA0104021` - Zybynska + * * `UA0104035` - Krynychnenska + * * `UA0104029` - Izobilnenska + * * `UA0104031` - Kistochkivska + * * `UA0104033` - Krymskorozivska + * * `UA0104027` - Ivanivska + * * `UA0104037` - Kurska + * * `UA0104025` - Zuiska + * * `UA0104023` - Zorkinska + * * `UA0104041` - Melnychna + * * `UA0104063` - Rusakivska + * * `UA0104061` - Pshenychnenska + * * `UA0104045` - Mykhailivska + * * `UA0104059` - Okhotska + * * `UA0104051` - Nyzhnohirska + * * `UA0104049` - Muromska + * * `UA0104057` - Omelianivska + * * `UA0104055` - Novozhylivska + * * `UA0104069` - Tsvitochnenska + * * `UA0104067` - Uvarivska + * * `UA0104065` - Sadova + * * `UA0104071` - Chkalovska + * * `UA0104075` - Yakymivska + * * `UA0104039` - Lystvynska + * * `UA0104073` - Chornopilska + * * `UA0104053` - Novohryhorivska + * * `UA0104047` - Michurinska + * * `UA0106049` - Tsilynna + * * `UA0106047` - Tabachnenska + * * `UA0106045` - Stalnenska + * * `UA0106011` - Zarichnenska + * * `UA0106009` - Zavito-Leninska + * * `UA0106001` - Azovska + * * `UA0106007` - Yermakivska + * * `UA0106005` - Dzhankoiska + * * `UA0106043` - Svitlivska + * * `UA0106029` - Myrnivska + * * `UA0106019` - Lobanivska + * * `UA0106015` - Kindrativska + * * `UA0106017` - Krymkivska + * * `UA0106027` - Medvedivska + * * `UA0106021` - Luhanska + * * `UA0106025` - Maslivska + * * `UA0106041` - Roshchynska + * * `UA0106035` - Pobiednenska + * * `UA0106033` - Pakharivska + * * `UA0106039` - Rozkishnenska + * * `UA0106037` - Prostornenska + * * `UA0106013` - Izumrudnivska + * * `UA0106053` - Yarkivska + * * `UA0106051` - Chaikynska + * * `UA0106057` - Yasnopolianska + * * `UA0106055` - Yarkopolenska + * * `UA0106031` - Novokrymska + * * `UA0106023` - Maiska + * * `UA0106003` - Vilnenska + * * `UA0102003` - Aromatnenska + * * `UA0102000` - Sevastopilska + * * `UA0102009` - Verkhorichenska + * * `UA0102005` - Bakhchysaraiska + * * `UA0102011` - Vilinska + * * `UA0102017` - Zaliznychnenska + * * `UA0102013` - Holubynska + * * `UA0102015` - Dolynnenska + * * `UA0102019` - Zelenivska + * * `UA0102033` - Pishchanivska + * * `UA0102025` - Kashtanivska + * * `UA0102029` - Kuibyshevska + * * `UA0102027` - Krasnomatska + * * `UA0102039` - Skalystivska + * * `UA0102035` - Plodivska + * * `UA0102037` - Poshtivska + * * `UA0102041` - Tabachnenska + * * `UA0102045` - Tinystivska + * * `UA0102047` - Uhlivska + * * `UA0116041` - Urozhainivska + * * `UA0116001` - Hvardiiska + * * `UA0116003` - Hresivska + * * `UA0116005` - Dobrivska + * * `UA0116007` - Donska + * * `UA0116009` - Zhuravlivska + * * `UA0116011` - Kolchuhynska + * * `UA0116013` - Mazanska + * * `UA0116015` - Mykolaivska + * * `UA0116017` - Myrnivska + * * `UA0116019` - Molodizhnenska + * * `UA0116021` - Novoandriivska + * * `UA0116023` - Novoselivska + * * `UA0116025` - Pervomaiska + * * `UA0116027` - Perovska + * * `UA0116029` - Pozharska + * * `UA0116031` - Rodnykivska + * * `UA0116033` - Simferopolska + * * `UA0116035` - Skvortsivska + * * `UA0116037` - Trudivska + * * `UA0116039` - Ukromnivska + * * `UA0114017` - Ishunska + * * `UA0114015` - Illinska + * * `UA0114037` - Rozdolnenska + * * `UA0114001` - Armianska + * * `UA0114021` - Krasnoarmiiska + * * `UA0114033` - Orlivska + * * `UA0114003` - Berezivska + * * `UA0114005` - Botanichna + * * `UA0114007` - Bratska + * * `UA0114011` - Voinska + * * `UA0114031` - Novoselivska + * * `UA0114013` - Zymynska + * * `UA0114029` - Novopavlivska + * * `UA0114035` - Pochetnenska + * * `UA0114019` - Kovylnivska + * * `UA0114023` - Krasnoperekopska + * * `UA0114025` - Kukushkinska + * * `UA0114027` - Mahazynska + * * `UA0114039` - Ruchivska + * * `UA0114041` - Serebrianska + * * `UA0114043` - Slavnivska + * * `UA0114045` - Slovianska + * * `UA0114047` - Sovkhoznenska + * * `UA0114049` - Suvorovska + * * `UA0114051` - Filativska + * * `UA0114053` - Chernyshivska + * * `UA0114009` - Vyshnivska + * * `UA0110027` - Leninska + * * `UA0110003` - Batalnenska + * * `UA0110011` - Hlazivska + * * `UA0110029` - Leninska + * * `UA0110039` - Novomykolaivska + * * `UA0110031` - Luhivska + * * `UA0110013` - Hornostaivska + * * `UA0110033` - Marivska + * * `UA0110035` - Marfivska + * * `UA0110015` - Zavitnenska + * * `UA0110037` - Mysivska + * * `UA0110017` - Illichivska + * * `UA0110041` - Oktiabrska + * * `UA0110005` - Bielinska + * * `UA0110019` - Kalynivska + * * `UA0110043` - Ostaninska + * * `UA0110007` - Vynohradnenska + * * `UA0110045` - Pryozernivska + * * `UA0110047` - Semysotska + * * `UA0110021` - Kerchenska + * * `UA0110049` - Uvarivska + * * `UA0110051` - Cheliadinivska + * * `UA0110001` - Baherivska + * * `UA0110023` - Kirovska + * * `UA0110053` - Chystopilska + * * `UA0110009` - Voikovska + * * `UA0110025` - Krasnohirska + * * `UA0110055` - Shcholkinska + */ +export enum Admin3Enum { + UA0112065 = 'UA0112065', + UA0112001 = 'UA0112001', + UA0112067 = 'UA0112067', + UA0112015 = 'UA0112015', + UA0112069 = 'UA0112069', + UA0112003 = 'UA0112003', + UA0112071 = 'UA0112071', + UA0112005 = 'UA0112005', + UA0112073 = 'UA0112073', + UA0112017 = 'UA0112017', + UA0112007 = 'UA0112007', + UA0112025 = 'UA0112025', + UA0112009 = 'UA0112009', + UA0112011 = 'UA0112011', + UA0112013 = 'UA0112013', + UA0112021 = 'UA0112021', + UA0112019 = 'UA0112019', + UA0112023 = 'UA0112023', + UA0112027 = 'UA0112027', + UA0112029 = 'UA0112029', + UA0112031 = 'UA0112031', + UA0112033 = 'UA0112033', + UA0112035 = 'UA0112035', + UA0112037 = 'UA0112037', + UA0112039 = 'UA0112039', + UA0112041 = 'UA0112041', + UA0112043 = 'UA0112043', + UA0112045 = 'UA0112045', + UA0112047 = 'UA0112047', + UA0112049 = 'UA0112049', + UA0112051 = 'UA0112051', + UA0112053 = 'UA0112053', + UA0112055 = 'UA0112055', + UA0112057 = 'UA0112057', + UA0112059 = 'UA0112059', + UA0112061 = 'UA0112061', + UA0112063 = 'UA0112063', + UA0108079 = 'UA0108079', + UA0108053 = 'UA0108053', + UA0108021 = 'UA0108021', + UA0108003 = 'UA0108003', + UA0108001 = 'UA0108001', + UA0108019 = 'UA0108019', + UA0108009 = 'UA0108009', + UA0108005 = 'UA0108005', + UA0108007 = 'UA0108007', + UA0108017 = 'UA0108017', + UA0108013 = 'UA0108013', + UA0108015 = 'UA0108015', + UA0108051 = 'UA0108051', + UA0108033 = 'UA0108033', + UA0108025 = 'UA0108025', + UA0108023 = 'UA0108023', + UA0108031 = 'UA0108031', + UA0108027 = 'UA0108027', + UA0108029 = 'UA0108029', + UA0108049 = 'UA0108049', + UA0108039 = 'UA0108039', + UA0108035 = 'UA0108035', + UA0108037 = 'UA0108037', + UA0108047 = 'UA0108047', + UA0108041 = 'UA0108041', + UA0108045 = 'UA0108045', + UA0108069 = 'UA0108069', + UA0108059 = 'UA0108059', + UA0108055 = 'UA0108055', + UA0108057 = 'UA0108057', + UA0108067 = 'UA0108067', + UA0108061 = 'UA0108061', + UA0108065 = 'UA0108065', + UA0108075 = 'UA0108075', + UA0108071 = 'UA0108071', + UA0108073 = 'UA0108073', + UA0108077 = 'UA0108077', + UA0108063 = 'UA0108063', + UA0108043 = 'UA0108043', + UA0108011 = 'UA0108011', + UA0104043 = 'UA0104043', + UA0104001 = 'UA0104001', + UA0104003 = 'UA0104003', + UA0104007 = 'UA0104007', + UA0104005 = 'UA0104005', + UA0104011 = 'UA0104011', + UA0104013 = 'UA0104013', + UA0104009 = 'UA0104009', + UA0104015 = 'UA0104015', + UA0104017 = 'UA0104017', + UA0104019 = 'UA0104019', + UA0104021 = 'UA0104021', + UA0104035 = 'UA0104035', + UA0104029 = 'UA0104029', + UA0104031 = 'UA0104031', + UA0104033 = 'UA0104033', + UA0104027 = 'UA0104027', + UA0104037 = 'UA0104037', + UA0104025 = 'UA0104025', + UA0104023 = 'UA0104023', + UA0104041 = 'UA0104041', + UA0104063 = 'UA0104063', + UA0104061 = 'UA0104061', + UA0104045 = 'UA0104045', + UA0104059 = 'UA0104059', + UA0104051 = 'UA0104051', + UA0104049 = 'UA0104049', + UA0104057 = 'UA0104057', + UA0104055 = 'UA0104055', + UA0104069 = 'UA0104069', + UA0104067 = 'UA0104067', + UA0104065 = 'UA0104065', + UA0104071 = 'UA0104071', + UA0104075 = 'UA0104075', + UA0104039 = 'UA0104039', + UA0104073 = 'UA0104073', + UA0104053 = 'UA0104053', + UA0104047 = 'UA0104047', + UA0106049 = 'UA0106049', + UA0106047 = 'UA0106047', + UA0106045 = 'UA0106045', + UA0106011 = 'UA0106011', + UA0106009 = 'UA0106009', + UA0106001 = 'UA0106001', + UA0106007 = 'UA0106007', + UA0106005 = 'UA0106005', + UA0106043 = 'UA0106043', + UA0106029 = 'UA0106029', + UA0106019 = 'UA0106019', + UA0106015 = 'UA0106015', + UA0106017 = 'UA0106017', + UA0106027 = 'UA0106027', + UA0106021 = 'UA0106021', + UA0106025 = 'UA0106025', + UA0106041 = 'UA0106041', + UA0106035 = 'UA0106035', + UA0106033 = 'UA0106033', + UA0106039 = 'UA0106039', + UA0106037 = 'UA0106037', + UA0106013 = 'UA0106013', + UA0106053 = 'UA0106053', + UA0106051 = 'UA0106051', + UA0106057 = 'UA0106057', + UA0106055 = 'UA0106055', + UA0106031 = 'UA0106031', + UA0106023 = 'UA0106023', + UA0106003 = 'UA0106003', + UA0102003 = 'UA0102003', + UA0102000 = 'UA0102000', + UA0102009 = 'UA0102009', + UA0102005 = 'UA0102005', + UA0102011 = 'UA0102011', + UA0102017 = 'UA0102017', + UA0102013 = 'UA0102013', + UA0102015 = 'UA0102015', + UA0102019 = 'UA0102019', + UA0102033 = 'UA0102033', + UA0102025 = 'UA0102025', + UA0102029 = 'UA0102029', + UA0102027 = 'UA0102027', + UA0102039 = 'UA0102039', + UA0102035 = 'UA0102035', + UA0102037 = 'UA0102037', + UA0102041 = 'UA0102041', + UA0102045 = 'UA0102045', + UA0102047 = 'UA0102047', + UA0116041 = 'UA0116041', + UA0116001 = 'UA0116001', + UA0116003 = 'UA0116003', + UA0116005 = 'UA0116005', + UA0116007 = 'UA0116007', + UA0116009 = 'UA0116009', + UA0116011 = 'UA0116011', + UA0116013 = 'UA0116013', + UA0116015 = 'UA0116015', + UA0116017 = 'UA0116017', + UA0116019 = 'UA0116019', + UA0116021 = 'UA0116021', + UA0116023 = 'UA0116023', + UA0116025 = 'UA0116025', + UA0116027 = 'UA0116027', + UA0116029 = 'UA0116029', + UA0116031 = 'UA0116031', + UA0116033 = 'UA0116033', + UA0116035 = 'UA0116035', + UA0116037 = 'UA0116037', + UA0116039 = 'UA0116039', + UA0114017 = 'UA0114017', + UA0114015 = 'UA0114015', + UA0114037 = 'UA0114037', + UA0114001 = 'UA0114001', + UA0114021 = 'UA0114021', + UA0114033 = 'UA0114033', + UA0114003 = 'UA0114003', + UA0114005 = 'UA0114005', + UA0114007 = 'UA0114007', + UA0114011 = 'UA0114011', + UA0114031 = 'UA0114031', + UA0114013 = 'UA0114013', + UA0114029 = 'UA0114029', + UA0114035 = 'UA0114035', + UA0114019 = 'UA0114019', + UA0114023 = 'UA0114023', + UA0114025 = 'UA0114025', + UA0114027 = 'UA0114027', + UA0114039 = 'UA0114039', + UA0114041 = 'UA0114041', + UA0114043 = 'UA0114043', + UA0114045 = 'UA0114045', + UA0114047 = 'UA0114047', + UA0114049 = 'UA0114049', + UA0114051 = 'UA0114051', + UA0114053 = 'UA0114053', + UA0114009 = 'UA0114009', + UA0110027 = 'UA0110027', + UA0110003 = 'UA0110003', + UA0110011 = 'UA0110011', + UA0110029 = 'UA0110029', + UA0110039 = 'UA0110039', + UA0110031 = 'UA0110031', + UA0110013 = 'UA0110013', + UA0110033 = 'UA0110033', + UA0110035 = 'UA0110035', + UA0110015 = 'UA0110015', + UA0110037 = 'UA0110037', + UA0110017 = 'UA0110017', + UA0110041 = 'UA0110041', + UA0110005 = 'UA0110005', + UA0110019 = 'UA0110019', + UA0110043 = 'UA0110043', + UA0110007 = 'UA0110007', + UA0110045 = 'UA0110045', + UA0110047 = 'UA0110047', + UA0110021 = 'UA0110021', + UA0110049 = 'UA0110049', + UA0110051 = 'UA0110051', + UA0110001 = 'UA0110001', + UA0110023 = 'UA0110023', + UA0110053 = 'UA0110053', + UA0110009 = 'UA0110009', + UA0110025 = 'UA0110025', + UA0110055 = 'UA0110055', +} diff --git a/src/frontend/generated/models/Admin4Enum.ts b/src/frontend/generated/models/Admin4Enum.ts new file mode 100644 index 0000000000..d88bf70c1a --- /dev/null +++ b/src/frontend/generated/models/Admin4Enum.ts @@ -0,0 +1,5 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type Admin4Enum = boolean; diff --git a/src/frontend/generated/models/Area.ts b/src/frontend/generated/models/Area.ts new file mode 100644 index 0000000000..134759e183 --- /dev/null +++ b/src/frontend/generated/models/Area.ts @@ -0,0 +1,24 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type Area = { + readonly id: string; + readonly created_at: string; + readonly updated_at: string; + original_id?: string | null; + name: string; + p_code?: string | null; + geom?: string | null; + point?: string | null; + readonly valid_from: string | null; + valid_until?: string | null; + extras?: any; + readonly lft: number; + readonly rght: number; + readonly tree_id: number; + readonly level: number; + parent?: string | null; + area_type: string; +}; + diff --git a/src/frontend/generated/models/AreaList.ts b/src/frontend/generated/models/AreaList.ts new file mode 100644 index 0000000000..04e29322db --- /dev/null +++ b/src/frontend/generated/models/AreaList.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type AreaList = { + readonly id: string; + name: string; + p_code?: string | null; +}; + diff --git a/src/frontend/generated/models/AreaType.ts b/src/frontend/generated/models/AreaType.ts new file mode 100644 index 0000000000..3e7579f7cc --- /dev/null +++ b/src/frontend/generated/models/AreaType.ts @@ -0,0 +1,22 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type AreaType = { + readonly id: string; + readonly created_at: string; + readonly updated_at: string; + original_id?: string | null; + name: string; + area_level?: number; + readonly valid_from: string | null; + valid_until?: string | null; + extras?: any; + readonly lft: number; + readonly rght: number; + readonly tree_id: number; + readonly level: number; + country: string; + parent?: string | null; +}; + diff --git a/src/frontend/generated/models/BeneficiaryGroup.ts b/src/frontend/generated/models/BeneficiaryGroup.ts new file mode 100644 index 0000000000..fe1eaf3111 --- /dev/null +++ b/src/frontend/generated/models/BeneficiaryGroup.ts @@ -0,0 +1,14 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type BeneficiaryGroup = { + readonly id: string; + name: string; + group_label: string; + group_label_plural: string; + member_label: string; + member_label_plural: string; + master_detail?: boolean; +}; + diff --git a/src/frontend/generated/models/BlankEnum.ts b/src/frontend/generated/models/BlankEnum.ts new file mode 100644 index 0000000000..80941d70af --- /dev/null +++ b/src/frontend/generated/models/BlankEnum.ts @@ -0,0 +1,7 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export enum BlankEnum { + = '', +} diff --git a/src/frontend/generated/models/BusinessArea.ts b/src/frontend/generated/models/BusinessArea.ts new file mode 100644 index 0000000000..5ac16312d2 --- /dev/null +++ b/src/frontend/generated/models/BusinessArea.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type BusinessArea = { + readonly id: string; + name: string; + code: string; + long_name: string; + slug: string; + parent?: string | null; + is_split?: boolean; + active?: boolean; +}; + diff --git a/src/frontend/generated/models/CollectIndividualDataEnum.ts b/src/frontend/generated/models/CollectIndividualDataEnum.ts new file mode 100644 index 0000000000..68890bffa5 --- /dev/null +++ b/src/frontend/generated/models/CollectIndividualDataEnum.ts @@ -0,0 +1,17 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - Unknown + * * `2` - Partial individuals collected + * * `1` - Full individual collected + * * `3` - Size only collected + * * `0` - No individual data + */ +export enum CollectIndividualDataEnum { + _2 = '2', + _1 = '1', + _3 = '3', + _0 = '0', +} diff --git a/src/frontend/generated/models/CollectTypeEnum.ts b/src/frontend/generated/models/CollectTypeEnum.ts new file mode 100644 index 0000000000..46bcbcf5f4 --- /dev/null +++ b/src/frontend/generated/models/CollectTypeEnum.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `STANDARD` - Standard + * * `SINGLE` - Single + */ +export enum CollectTypeEnum { + STANDARD = 'STANDARD', + SINGLE = 'SINGLE', +} diff --git a/src/frontend/generated/models/CommsDisabilityEnum.ts b/src/frontend/generated/models/CommsDisabilityEnum.ts new file mode 100644 index 0000000000..9263cc7947 --- /dev/null +++ b/src/frontend/generated/models/CommsDisabilityEnum.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `LOT_DIFFICULTY` - A lot of difficulty + * * `CANNOT_DO` - Cannot do at all + * * `SOME_DIFFICULTY` - Some difficulty + */ +export enum CommsDisabilityEnum { + LOT_DIFFICULTY = 'LOT_DIFFICULTY', + CANNOT_DO = 'CANNOT_DO', + SOME_DIFFICULTY = 'SOME_DIFFICULTY', +} diff --git a/src/frontend/generated/models/ConsentSharingEnum.ts b/src/frontend/generated/models/ConsentSharingEnum.ts new file mode 100644 index 0000000000..1ee67f80c3 --- /dev/null +++ b/src/frontend/generated/models/ConsentSharingEnum.ts @@ -0,0 +1,17 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `GOVERNMENT_PARTNER` - Government partners + * * `HUMANITARIAN_PARTNER` - Humanitarian partners + * * `PRIVATE_PARTNER` - Private partners + * * `UNICEF` - UNICEF + */ +export enum ConsentSharingEnum { + GOVERNMENT_PARTNER = 'GOVERNMENT_PARTNER', + HUMANITARIAN_PARTNER = 'HUMANITARIAN_PARTNER', + PRIVATE_PARTNER = 'PRIVATE_PARTNER', + UNICEF = 'UNICEF', +} diff --git a/src/frontend/generated/models/Country.ts b/src/frontend/generated/models/Country.ts new file mode 100644 index 0000000000..7e9c34356a --- /dev/null +++ b/src/frontend/generated/models/Country.ts @@ -0,0 +1,16 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type Country = { + readonly id: string; + name: string; + short_name: string; + iso_code2: string; + iso_code3: string; + iso_num: string; + readonly valid_from: string | null; + valid_until?: string | null; + readonly updated_at: string; +}; + diff --git a/src/frontend/generated/models/CountryEnum.ts b/src/frontend/generated/models/CountryEnum.ts new file mode 100644 index 0000000000..acc5d5a11d --- /dev/null +++ b/src/frontend/generated/models/CountryEnum.ts @@ -0,0 +1,508 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `AF` - Afghanistan + * * `AX` - Åland Islands + * * `AL` - Albania + * * `DZ` - Algeria + * * `AS` - American Samoa + * * `AD` - Andorra + * * `AO` - Angola + * * `AI` - Anguilla + * * `AQ` - Antarctica + * * `AG` - Antigua and Barbuda + * * `AR` - Argentina + * * `AM` - Armenia + * * `AW` - Aruba + * * `AU` - Australia + * * `AT` - Austria + * * `AZ` - Azerbaijan + * * `BS` - Bahamas + * * `BH` - Bahrain + * * `BD` - Bangladesh + * * `BB` - Barbados + * * `BY` - Belarus + * * `BE` - Belgium + * * `BZ` - Belize + * * `BJ` - Benin + * * `BM` - Bermuda + * * `BT` - Bhutan + * * `BO` - Bolivia + * * `BQ` - Bonaire, Sint Eustatius and Saba + * * `BA` - Bosnia and Herzegovina + * * `BW` - Botswana + * * `BV` - Bouvet Island + * * `BR` - Brazil + * * `IO` - British Indian Ocean Territory + * * `BN` - Brunei + * * `BG` - Bulgaria + * * `BF` - Burkina Faso + * * `BI` - Burundi + * * `CV` - Cabo Verde + * * `KH` - Cambodia + * * `CM` - Cameroon + * * `CA` - Canada + * * `KY` - Cayman Islands + * * `CF` - Central African Republic + * * `TD` - Chad + * * `CL` - Chile + * * `CN` - China + * * `CX` - Christmas Island + * * `CC` - Cocos (Keeling) Islands + * * `CO` - Colombia + * * `KM` - Comoros + * * `CG` - Congo + * * `CD` - Congo (the Democratic Republic of the) + * * `CK` - Cook Islands + * * `CR` - Costa Rica + * * `CI` - Côte d'Ivoire + * * `HR` - Croatia + * * `CU` - Cuba + * * `CW` - Curaçao + * * `CY` - Cyprus + * * `CZ` - Czechia + * * `DK` - Denmark + * * `DJ` - Djibouti + * * `DM` - Dominica + * * `DO` - Dominican Republic + * * `EC` - Ecuador + * * `EG` - Egypt + * * `SV` - El Salvador + * * `GQ` - Equatorial Guinea + * * `ER` - Eritrea + * * `EE` - Estonia + * * `SZ` - Eswatini + * * `ET` - Ethiopia + * * `FK` - Falkland Islands (Malvinas) + * * `FO` - Faroe Islands + * * `FJ` - Fiji + * * `FI` - Finland + * * `FR` - France + * * `GF` - French Guiana + * * `PF` - French Polynesia + * * `TF` - French Southern Territories + * * `GA` - Gabon + * * `GM` - Gambia + * * `GE` - Georgia + * * `DE` - Germany + * * `GH` - Ghana + * * `GI` - Gibraltar + * * `GR` - Greece + * * `GL` - Greenland + * * `GD` - Grenada + * * `GP` - Guadeloupe + * * `GU` - Guam + * * `GT` - Guatemala + * * `GG` - Guernsey + * * `GN` - Guinea + * * `GW` - Guinea-Bissau + * * `GY` - Guyana + * * `HT` - Haiti + * * `HM` - Heard Island and McDonald Islands + * * `VA` - Holy See + * * `HN` - Honduras + * * `HK` - Hong Kong + * * `HU` - Hungary + * * `IS` - Iceland + * * `IN` - India + * * `ID` - Indonesia + * * `IR` - Iran + * * `IQ` - Iraq + * * `IE` - Ireland + * * `IM` - Isle of Man + * * `IL` - Israel + * * `IT` - Italy + * * `JM` - Jamaica + * * `JP` - Japan + * * `JE` - Jersey + * * `JO` - Jordan + * * `KZ` - Kazakhstan + * * `KE` - Kenya + * * `KI` - Kiribati + * * `KW` - Kuwait + * * `KG` - Kyrgyzstan + * * `LA` - Laos + * * `LV` - Latvia + * * `LB` - Lebanon + * * `LS` - Lesotho + * * `LR` - Liberia + * * `LY` - Libya + * * `LI` - Liechtenstein + * * `LT` - Lithuania + * * `LU` - Luxembourg + * * `MO` - Macao + * * `MG` - Madagascar + * * `MW` - Malawi + * * `MY` - Malaysia + * * `MV` - Maldives + * * `ML` - Mali + * * `MT` - Malta + * * `MH` - Marshall Islands + * * `MQ` - Martinique + * * `MR` - Mauritania + * * `MU` - Mauritius + * * `YT` - Mayotte + * * `MX` - Mexico + * * `FM` - Micronesia + * * `MD` - Moldova + * * `MC` - Monaco + * * `MN` - Mongolia + * * `ME` - Montenegro + * * `MS` - Montserrat + * * `MA` - Morocco + * * `MZ` - Mozambique + * * `MM` - Myanmar + * * `NA` - Namibia + * * `NR` - Nauru + * * `NP` - Nepal + * * `NL` - Netherlands + * * `NC` - New Caledonia + * * `NZ` - New Zealand + * * `NI` - Nicaragua + * * `NE` - Niger + * * `NG` - Nigeria + * * `NU` - Niue + * * `NF` - Norfolk Island + * * `KP` - North Korea + * * `MK` - North Macedonia + * * `MP` - Northern Mariana Islands + * * `NO` - Norway + * * `OM` - Oman + * * `PK` - Pakistan + * * `PW` - Palau + * * `PS` - Palestine, State of + * * `PA` - Panama + * * `PG` - Papua New Guinea + * * `PY` - Paraguay + * * `PE` - Peru + * * `PH` - Philippines + * * `PN` - Pitcairn + * * `PL` - Poland + * * `PT` - Portugal + * * `PR` - Puerto Rico + * * `QA` - Qatar + * * `RE` - Réunion + * * `RO` - Romania + * * `RU` - Russia + * * `RW` - Rwanda + * * `BL` - Saint Barthélemy + * * `SH` - Saint Helena, Ascension and Tristan da Cunha + * * `KN` - Saint Kitts and Nevis + * * `LC` - Saint Lucia + * * `MF` - Saint Martin (French part) + * * `PM` - Saint Pierre and Miquelon + * * `VC` - Saint Vincent and the Grenadines + * * `WS` - Samoa + * * `SM` - San Marino + * * `ST` - Sao Tome and Principe + * * `SA` - Saudi Arabia + * * `SN` - Senegal + * * `RS` - Serbia + * * `SC` - Seychelles + * * `SL` - Sierra Leone + * * `SG` - Singapore + * * `SX` - Sint Maarten (Dutch part) + * * `SK` - Slovakia + * * `SI` - Slovenia + * * `SB` - Solomon Islands + * * `SO` - Somalia + * * `ZA` - South Africa + * * `GS` - South Georgia and the South Sandwich Islands + * * `KR` - South Korea + * * `SS` - South Sudan + * * `ES` - Spain + * * `LK` - Sri Lanka + * * `SD` - Sudan + * * `SR` - Suriname + * * `SJ` - Svalbard and Jan Mayen + * * `SE` - Sweden + * * `CH` - Switzerland + * * `SY` - Syria + * * `TW` - Taiwan + * * `TJ` - Tajikistan + * * `TZ` - Tanzania + * * `TH` - Thailand + * * `TL` - Timor-Leste + * * `TG` - Togo + * * `TK` - Tokelau + * * `TO` - Tonga + * * `TT` - Trinidad and Tobago + * * `TN` - Tunisia + * * `TR` - Türkiye + * * `TM` - Turkmenistan + * * `TC` - Turks and Caicos Islands + * * `TV` - Tuvalu + * * `UG` - Uganda + * * `UA` - Ukraine + * * `AE` - United Arab Emirates + * * `GB` - United Kingdom + * * `UM` - United States Minor Outlying Islands + * * `US` - United States of America + * * `U` - Unknown or Not Applicable + * * `UY` - Uruguay + * * `UZ` - Uzbekistan + * * `VU` - Vanuatu + * * `VE` - Venezuela + * * `VN` - Vietnam + * * `VG` - Virgin Islands (British) + * * `VI` - Virgin Islands (U.S.) + * * `WF` - Wallis and Futuna + * * `EH` - Western Sahara + * * `YE` - Yemen + * * `ZM` - Zambia + * * `ZW` - Zimbabwe + */ +export enum CountryEnum { + AF = 'AF', + AX = 'AX', + AL = 'AL', + DZ = 'DZ', + AS = 'AS', + AD = 'AD', + AO = 'AO', + AI = 'AI', + AQ = 'AQ', + AG = 'AG', + AR = 'AR', + AM = 'AM', + AW = 'AW', + AU = 'AU', + AT = 'AT', + AZ = 'AZ', + BS = 'BS', + BH = 'BH', + BD = 'BD', + BB = 'BB', + BY = 'BY', + BE = 'BE', + BZ = 'BZ', + BJ = 'BJ', + BM = 'BM', + BT = 'BT', + BO = 'BO', + BQ = 'BQ', + BA = 'BA', + BW = 'BW', + BV = 'BV', + BR = 'BR', + IO = 'IO', + BN = 'BN', + BG = 'BG', + BF = 'BF', + BI = 'BI', + CV = 'CV', + KH = 'KH', + CM = 'CM', + CA = 'CA', + KY = 'KY', + CF = 'CF', + TD = 'TD', + CL = 'CL', + CN = 'CN', + CX = 'CX', + CC = 'CC', + CO = 'CO', + KM = 'KM', + CG = 'CG', + CD = 'CD', + CK = 'CK', + CR = 'CR', + CI = 'CI', + HR = 'HR', + CU = 'CU', + CW = 'CW', + CY = 'CY', + CZ = 'CZ', + DK = 'DK', + DJ = 'DJ', + DM = 'DM', + DO = 'DO', + EC = 'EC', + EG = 'EG', + SV = 'SV', + GQ = 'GQ', + ER = 'ER', + EE = 'EE', + SZ = 'SZ', + ET = 'ET', + FK = 'FK', + FO = 'FO', + FJ = 'FJ', + FI = 'FI', + FR = 'FR', + GF = 'GF', + PF = 'PF', + TF = 'TF', + GA = 'GA', + GM = 'GM', + GE = 'GE', + DE = 'DE', + GH = 'GH', + GI = 'GI', + GR = 'GR', + GL = 'GL', + GD = 'GD', + GP = 'GP', + GU = 'GU', + GT = 'GT', + GG = 'GG', + GN = 'GN', + GW = 'GW', + GY = 'GY', + HT = 'HT', + HM = 'HM', + VA = 'VA', + HN = 'HN', + HK = 'HK', + HU = 'HU', + IS = 'IS', + IN = 'IN', + ID = 'ID', + IR = 'IR', + IQ = 'IQ', + IE = 'IE', + IM = 'IM', + IL = 'IL', + IT = 'IT', + JM = 'JM', + JP = 'JP', + JE = 'JE', + JO = 'JO', + KZ = 'KZ', + KE = 'KE', + KI = 'KI', + KW = 'KW', + KG = 'KG', + LA = 'LA', + LV = 'LV', + LB = 'LB', + LS = 'LS', + LR = 'LR', + LY = 'LY', + LI = 'LI', + LT = 'LT', + LU = 'LU', + MO = 'MO', + MG = 'MG', + MW = 'MW', + MY = 'MY', + MV = 'MV', + ML = 'ML', + MT = 'MT', + MH = 'MH', + MQ = 'MQ', + MR = 'MR', + MU = 'MU', + YT = 'YT', + MX = 'MX', + FM = 'FM', + MD = 'MD', + MC = 'MC', + MN = 'MN', + ME = 'ME', + MS = 'MS', + MA = 'MA', + MZ = 'MZ', + MM = 'MM', + NA = 'NA', + NR = 'NR', + NP = 'NP', + NL = 'NL', + NC = 'NC', + NZ = 'NZ', + NI = 'NI', + NE = 'NE', + NG = 'NG', + NU = 'NU', + NF = 'NF', + KP = 'KP', + MK = 'MK', + MP = 'MP', + NO = 'NO', + OM = 'OM', + PK = 'PK', + PW = 'PW', + PS = 'PS', + PA = 'PA', + PG = 'PG', + PY = 'PY', + PE = 'PE', + PH = 'PH', + PN = 'PN', + PL = 'PL', + PT = 'PT', + PR = 'PR', + QA = 'QA', + RE = 'RE', + RO = 'RO', + RU = 'RU', + RW = 'RW', + BL = 'BL', + SH = 'SH', + KN = 'KN', + LC = 'LC', + MF = 'MF', + PM = 'PM', + VC = 'VC', + WS = 'WS', + SM = 'SM', + ST = 'ST', + SA = 'SA', + SN = 'SN', + RS = 'RS', + SC = 'SC', + SL = 'SL', + SG = 'SG', + SX = 'SX', + SK = 'SK', + SI = 'SI', + SB = 'SB', + SO = 'SO', + ZA = 'ZA', + GS = 'GS', + KR = 'KR', + SS = 'SS', + ES = 'ES', + LK = 'LK', + SD = 'SD', + SR = 'SR', + SJ = 'SJ', + SE = 'SE', + CH = 'CH', + SY = 'SY', + TW = 'TW', + TJ = 'TJ', + TZ = 'TZ', + TH = 'TH', + TL = 'TL', + TG = 'TG', + TK = 'TK', + TO = 'TO', + TT = 'TT', + TN = 'TN', + TR = 'TR', + TM = 'TM', + TC = 'TC', + TV = 'TV', + UG = 'UG', + UA = 'UA', + AE = 'AE', + GB = 'GB', + UM = 'UM', + US = 'US', + U = 'U', + UY = 'UY', + UZ = 'UZ', + VU = 'VU', + VE = 'VE', + VN = 'VN', + VG = 'VG', + VI = 'VI', + WF = 'WF', + EH = 'EH', + YE = 'YE', + ZM = 'ZM', + ZW = 'ZW', +} diff --git a/src/frontend/generated/models/CountryOriginEnum.ts b/src/frontend/generated/models/CountryOriginEnum.ts new file mode 100644 index 0000000000..6c50b2d9e3 --- /dev/null +++ b/src/frontend/generated/models/CountryOriginEnum.ts @@ -0,0 +1,508 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `AF` - Afghanistan + * * `AX` - Åland Islands + * * `AL` - Albania + * * `DZ` - Algeria + * * `AS` - American Samoa + * * `AD` - Andorra + * * `AO` - Angola + * * `AI` - Anguilla + * * `AQ` - Antarctica + * * `AG` - Antigua and Barbuda + * * `AR` - Argentina + * * `AM` - Armenia + * * `AW` - Aruba + * * `AU` - Australia + * * `AT` - Austria + * * `AZ` - Azerbaijan + * * `BS` - Bahamas + * * `BH` - Bahrain + * * `BD` - Bangladesh + * * `BB` - Barbados + * * `BY` - Belarus + * * `BE` - Belgium + * * `BZ` - Belize + * * `BJ` - Benin + * * `BM` - Bermuda + * * `BT` - Bhutan + * * `BO` - Bolivia + * * `BQ` - Bonaire, Sint Eustatius and Saba + * * `BA` - Bosnia and Herzegovina + * * `BW` - Botswana + * * `BV` - Bouvet Island + * * `BR` - Brazil + * * `IO` - British Indian Ocean Territory + * * `BN` - Brunei + * * `BG` - Bulgaria + * * `BF` - Burkina Faso + * * `BI` - Burundi + * * `CV` - Cabo Verde + * * `KH` - Cambodia + * * `CM` - Cameroon + * * `CA` - Canada + * * `KY` - Cayman Islands + * * `CF` - Central African Republic + * * `TD` - Chad + * * `CL` - Chile + * * `CN` - China + * * `CX` - Christmas Island + * * `CC` - Cocos (Keeling) Islands + * * `CO` - Colombia + * * `KM` - Comoros + * * `CG` - Congo + * * `CD` - Congo (the Democratic Republic of the) + * * `CK` - Cook Islands + * * `CR` - Costa Rica + * * `CI` - Côte d'Ivoire + * * `HR` - Croatia + * * `CU` - Cuba + * * `CW` - Curaçao + * * `CY` - Cyprus + * * `CZ` - Czechia + * * `DK` - Denmark + * * `DJ` - Djibouti + * * `DM` - Dominica + * * `DO` - Dominican Republic + * * `EC` - Ecuador + * * `EG` - Egypt + * * `SV` - El Salvador + * * `GQ` - Equatorial Guinea + * * `ER` - Eritrea + * * `EE` - Estonia + * * `SZ` - Eswatini + * * `ET` - Ethiopia + * * `FK` - Falkland Islands (Malvinas) + * * `FO` - Faroe Islands + * * `FJ` - Fiji + * * `FI` - Finland + * * `FR` - France + * * `GF` - French Guiana + * * `PF` - French Polynesia + * * `TF` - French Southern Territories + * * `GA` - Gabon + * * `GM` - Gambia + * * `GE` - Georgia + * * `DE` - Germany + * * `GH` - Ghana + * * `GI` - Gibraltar + * * `GR` - Greece + * * `GL` - Greenland + * * `GD` - Grenada + * * `GP` - Guadeloupe + * * `GU` - Guam + * * `GT` - Guatemala + * * `GG` - Guernsey + * * `GN` - Guinea + * * `GW` - Guinea-Bissau + * * `GY` - Guyana + * * `HT` - Haiti + * * `HM` - Heard Island and McDonald Islands + * * `VA` - Holy See + * * `HN` - Honduras + * * `HK` - Hong Kong + * * `HU` - Hungary + * * `IS` - Iceland + * * `IN` - India + * * `ID` - Indonesia + * * `IR` - Iran + * * `IQ` - Iraq + * * `IE` - Ireland + * * `IM` - Isle of Man + * * `IL` - Israel + * * `IT` - Italy + * * `JM` - Jamaica + * * `JP` - Japan + * * `JE` - Jersey + * * `JO` - Jordan + * * `KZ` - Kazakhstan + * * `KE` - Kenya + * * `KI` - Kiribati + * * `KW` - Kuwait + * * `KG` - Kyrgyzstan + * * `LA` - Laos + * * `LV` - Latvia + * * `LB` - Lebanon + * * `LS` - Lesotho + * * `LR` - Liberia + * * `LY` - Libya + * * `LI` - Liechtenstein + * * `LT` - Lithuania + * * `LU` - Luxembourg + * * `MO` - Macao + * * `MG` - Madagascar + * * `MW` - Malawi + * * `MY` - Malaysia + * * `MV` - Maldives + * * `ML` - Mali + * * `MT` - Malta + * * `MH` - Marshall Islands + * * `MQ` - Martinique + * * `MR` - Mauritania + * * `MU` - Mauritius + * * `YT` - Mayotte + * * `MX` - Mexico + * * `FM` - Micronesia + * * `MD` - Moldova + * * `MC` - Monaco + * * `MN` - Mongolia + * * `ME` - Montenegro + * * `MS` - Montserrat + * * `MA` - Morocco + * * `MZ` - Mozambique + * * `MM` - Myanmar + * * `NA` - Namibia + * * `NR` - Nauru + * * `NP` - Nepal + * * `NL` - Netherlands + * * `NC` - New Caledonia + * * `NZ` - New Zealand + * * `NI` - Nicaragua + * * `NE` - Niger + * * `NG` - Nigeria + * * `NU` - Niue + * * `NF` - Norfolk Island + * * `KP` - North Korea + * * `MK` - North Macedonia + * * `MP` - Northern Mariana Islands + * * `NO` - Norway + * * `OM` - Oman + * * `PK` - Pakistan + * * `PW` - Palau + * * `PS` - Palestine, State of + * * `PA` - Panama + * * `PG` - Papua New Guinea + * * `PY` - Paraguay + * * `PE` - Peru + * * `PH` - Philippines + * * `PN` - Pitcairn + * * `PL` - Poland + * * `PT` - Portugal + * * `PR` - Puerto Rico + * * `QA` - Qatar + * * `RE` - Réunion + * * `RO` - Romania + * * `RU` - Russia + * * `RW` - Rwanda + * * `BL` - Saint Barthélemy + * * `SH` - Saint Helena, Ascension and Tristan da Cunha + * * `KN` - Saint Kitts and Nevis + * * `LC` - Saint Lucia + * * `MF` - Saint Martin (French part) + * * `PM` - Saint Pierre and Miquelon + * * `VC` - Saint Vincent and the Grenadines + * * `WS` - Samoa + * * `SM` - San Marino + * * `ST` - Sao Tome and Principe + * * `SA` - Saudi Arabia + * * `SN` - Senegal + * * `RS` - Serbia + * * `SC` - Seychelles + * * `SL` - Sierra Leone + * * `SG` - Singapore + * * `SX` - Sint Maarten (Dutch part) + * * `SK` - Slovakia + * * `SI` - Slovenia + * * `SB` - Solomon Islands + * * `SO` - Somalia + * * `ZA` - South Africa + * * `GS` - South Georgia and the South Sandwich Islands + * * `KR` - South Korea + * * `SS` - South Sudan + * * `ES` - Spain + * * `LK` - Sri Lanka + * * `SD` - Sudan + * * `SR` - Suriname + * * `SJ` - Svalbard and Jan Mayen + * * `SE` - Sweden + * * `CH` - Switzerland + * * `SY` - Syria + * * `TW` - Taiwan + * * `TJ` - Tajikistan + * * `TZ` - Tanzania + * * `TH` - Thailand + * * `TL` - Timor-Leste + * * `TG` - Togo + * * `TK` - Tokelau + * * `TO` - Tonga + * * `TT` - Trinidad and Tobago + * * `TN` - Tunisia + * * `TR` - Türkiye + * * `TM` - Turkmenistan + * * `TC` - Turks and Caicos Islands + * * `TV` - Tuvalu + * * `UG` - Uganda + * * `UA` - Ukraine + * * `AE` - United Arab Emirates + * * `GB` - United Kingdom + * * `UM` - United States Minor Outlying Islands + * * `US` - United States of America + * * `U` - Unknown or Not Applicable + * * `UY` - Uruguay + * * `UZ` - Uzbekistan + * * `VU` - Vanuatu + * * `VE` - Venezuela + * * `VN` - Vietnam + * * `VG` - Virgin Islands (British) + * * `VI` - Virgin Islands (U.S.) + * * `WF` - Wallis and Futuna + * * `EH` - Western Sahara + * * `YE` - Yemen + * * `ZM` - Zambia + * * `ZW` - Zimbabwe + */ +export enum CountryOriginEnum { + AF = 'AF', + AX = 'AX', + AL = 'AL', + DZ = 'DZ', + AS = 'AS', + AD = 'AD', + AO = 'AO', + AI = 'AI', + AQ = 'AQ', + AG = 'AG', + AR = 'AR', + AM = 'AM', + AW = 'AW', + AU = 'AU', + AT = 'AT', + AZ = 'AZ', + BS = 'BS', + BH = 'BH', + BD = 'BD', + BB = 'BB', + BY = 'BY', + BE = 'BE', + BZ = 'BZ', + BJ = 'BJ', + BM = 'BM', + BT = 'BT', + BO = 'BO', + BQ = 'BQ', + BA = 'BA', + BW = 'BW', + BV = 'BV', + BR = 'BR', + IO = 'IO', + BN = 'BN', + BG = 'BG', + BF = 'BF', + BI = 'BI', + CV = 'CV', + KH = 'KH', + CM = 'CM', + CA = 'CA', + KY = 'KY', + CF = 'CF', + TD = 'TD', + CL = 'CL', + CN = 'CN', + CX = 'CX', + CC = 'CC', + CO = 'CO', + KM = 'KM', + CG = 'CG', + CD = 'CD', + CK = 'CK', + CR = 'CR', + CI = 'CI', + HR = 'HR', + CU = 'CU', + CW = 'CW', + CY = 'CY', + CZ = 'CZ', + DK = 'DK', + DJ = 'DJ', + DM = 'DM', + DO = 'DO', + EC = 'EC', + EG = 'EG', + SV = 'SV', + GQ = 'GQ', + ER = 'ER', + EE = 'EE', + SZ = 'SZ', + ET = 'ET', + FK = 'FK', + FO = 'FO', + FJ = 'FJ', + FI = 'FI', + FR = 'FR', + GF = 'GF', + PF = 'PF', + TF = 'TF', + GA = 'GA', + GM = 'GM', + GE = 'GE', + DE = 'DE', + GH = 'GH', + GI = 'GI', + GR = 'GR', + GL = 'GL', + GD = 'GD', + GP = 'GP', + GU = 'GU', + GT = 'GT', + GG = 'GG', + GN = 'GN', + GW = 'GW', + GY = 'GY', + HT = 'HT', + HM = 'HM', + VA = 'VA', + HN = 'HN', + HK = 'HK', + HU = 'HU', + IS = 'IS', + IN = 'IN', + ID = 'ID', + IR = 'IR', + IQ = 'IQ', + IE = 'IE', + IM = 'IM', + IL = 'IL', + IT = 'IT', + JM = 'JM', + JP = 'JP', + JE = 'JE', + JO = 'JO', + KZ = 'KZ', + KE = 'KE', + KI = 'KI', + KW = 'KW', + KG = 'KG', + LA = 'LA', + LV = 'LV', + LB = 'LB', + LS = 'LS', + LR = 'LR', + LY = 'LY', + LI = 'LI', + LT = 'LT', + LU = 'LU', + MO = 'MO', + MG = 'MG', + MW = 'MW', + MY = 'MY', + MV = 'MV', + ML = 'ML', + MT = 'MT', + MH = 'MH', + MQ = 'MQ', + MR = 'MR', + MU = 'MU', + YT = 'YT', + MX = 'MX', + FM = 'FM', + MD = 'MD', + MC = 'MC', + MN = 'MN', + ME = 'ME', + MS = 'MS', + MA = 'MA', + MZ = 'MZ', + MM = 'MM', + NA = 'NA', + NR = 'NR', + NP = 'NP', + NL = 'NL', + NC = 'NC', + NZ = 'NZ', + NI = 'NI', + NE = 'NE', + NG = 'NG', + NU = 'NU', + NF = 'NF', + KP = 'KP', + MK = 'MK', + MP = 'MP', + NO = 'NO', + OM = 'OM', + PK = 'PK', + PW = 'PW', + PS = 'PS', + PA = 'PA', + PG = 'PG', + PY = 'PY', + PE = 'PE', + PH = 'PH', + PN = 'PN', + PL = 'PL', + PT = 'PT', + PR = 'PR', + QA = 'QA', + RE = 'RE', + RO = 'RO', + RU = 'RU', + RW = 'RW', + BL = 'BL', + SH = 'SH', + KN = 'KN', + LC = 'LC', + MF = 'MF', + PM = 'PM', + VC = 'VC', + WS = 'WS', + SM = 'SM', + ST = 'ST', + SA = 'SA', + SN = 'SN', + RS = 'RS', + SC = 'SC', + SL = 'SL', + SG = 'SG', + SX = 'SX', + SK = 'SK', + SI = 'SI', + SB = 'SB', + SO = 'SO', + ZA = 'ZA', + GS = 'GS', + KR = 'KR', + SS = 'SS', + ES = 'ES', + LK = 'LK', + SD = 'SD', + SR = 'SR', + SJ = 'SJ', + SE = 'SE', + CH = 'CH', + SY = 'SY', + TW = 'TW', + TJ = 'TJ', + TZ = 'TZ', + TH = 'TH', + TL = 'TL', + TG = 'TG', + TK = 'TK', + TO = 'TO', + TT = 'TT', + TN = 'TN', + TR = 'TR', + TM = 'TM', + TC = 'TC', + TV = 'TV', + UG = 'UG', + UA = 'UA', + AE = 'AE', + GB = 'GB', + UM = 'UM', + US = 'US', + U = 'U', + UY = 'UY', + UZ = 'UZ', + VU = 'VU', + VE = 'VE', + VN = 'VN', + VG = 'VG', + VI = 'VI', + WF = 'WF', + EH = 'EH', + YE = 'YE', + ZM = 'ZM', + ZW = 'ZW', +} diff --git a/src/frontend/generated/models/CurrencyEnum.ts b/src/frontend/generated/models/CurrencyEnum.ts new file mode 100644 index 0000000000..759430eef9 --- /dev/null +++ b/src/frontend/generated/models/CurrencyEnum.ts @@ -0,0 +1,333 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `AED` - United Arab Emirates dirham + * * `AFN` - Afghan afghani + * * `ALL` - Albanian lek + * * `AMD` - Armenian dram + * * `ANG` - Netherlands Antillean guilder + * * `AOA` - Angolan kwanza + * * `ARS` - Argentine peso + * * `AUD` - Australian dollar + * * `AWG` - Aruban florin + * * `AZN` - Azerbaijani manat + * * `BAM` - Bosnia and Herzegovina convertible mark + * * `BBD` - Barbados dollar + * * `BDT` - Bangladeshi taka + * * `BGN` - Bulgarian lev + * * `BHD` - Bahraini dinar + * * `BIF` - Burundian franc + * * `BMD` - Bermudian dollar + * * `BND` - Brunei dollar + * * `BOB` - Boliviano + * * `BOV` - Bolivian Mvdol (funds code) + * * `BRL` - Brazilian real + * * `BSD` - Bahamian dollar + * * `BTN` - Bhutanese ngultrum + * * `BWP` - Botswana pula + * * `BYN` - Belarusian ruble + * * `BZD` - Belize dollar + * * `CAD` - Canadian dollar + * * `CDF` - Congolese franc + * * `CHF` - Swiss franc + * * `CLP` - Chilean peso + * * `CNY` - Chinese yuan + * * `COP` - Colombian peso + * * `CRC` - Costa Rican colon + * * `CUC` - Cuban convertible peso + * * `CUP` - Cuban peso + * * `CVE` - Cape Verdean escudo + * * `CZK` - Czech koruna + * * `DJF` - Djiboutian franc + * * `DKK` - Danish krone + * * `DOP` - Dominican peso + * * `DZD` - Algerian dinar + * * `EGP` - Egyptian pound + * * `ERN` - Eritrean nakfa + * * `ETB` - Ethiopian birr + * * `EUR` - Euro + * * `FJD` - Fiji dollar + * * `FKP` - Falkland Islands pound + * * `GBP` - Pound sterling + * * `GEL` - Georgian lari + * * `GHS` - Ghanaian cedi + * * `GIP` - Gibraltar pound + * * `GMD` - Gambian dalasi + * * `GNF` - Guinean franc + * * `GTQ` - Guatemalan quetzal + * * `GYD` - Guyanese dollar + * * `HKD` - Hong Kong dollar + * * `HNL` - Honduran lempira + * * `HRK` - Croatian kuna + * * `HTG` - Haitian gourde + * * `HUF` - Hungarian forint + * * `IDR` - Indonesian rupiah + * * `ILS` - Israeli new shekel + * * `INR` - Indian rupee + * * `IQD` - Iraqi dinar + * * `IRR` - Iranian rial + * * `ISK` - Icelandic króna + * * `JMD` - Jamaican dollar + * * `JOD` - Jordanian dinar + * * `JPY` - Japanese yen + * * `KES` - Kenyan shilling + * * `KGS` - Kyrgyzstani som + * * `KHR` - Cambodian riel + * * `KMF` - Comoro franc + * * `KPW` - North Korean won + * * `KRW` - South Korean won + * * `KWD` - Kuwaiti dinar + * * `KYD` - Cayman Islands dollar + * * `KZT` - Kazakhstani tenge + * * `LAK` - Lao kip + * * `LBP` - Lebanese pound + * * `LKR` - Sri Lankan rupee + * * `LRD` - Liberian dollar + * * `LSL` - Lesotho loti + * * `LYD` - Libyan dinar + * * `MAD` - Moroccan dirham + * * `MDL` - Moldovan leu + * * `MGA` - Malagasy ariary + * * `MKD` - Macedonian denar + * * `MMK` - Myanmar kyat + * * `MNT` - Mongolian tögrög + * * `MOP` - Macanese pataca + * * `MRU` - Mauritanian ouguiya + * * `MUR` - Mauritian rupee + * * `MVR` - Maldivian rufiyaa + * * `MWK` - Malawian kwacha + * * `MXN` - Mexican peso + * * `MYR` - Malaysian ringgit + * * `MZN` - Mozambican metical + * * `NAD` - Namibian dollar + * * `NGN` - Nigerian naira + * * `NIO` - Nicaraguan córdoba + * * `NOK` - Norwegian krone + * * `NPR` - Nepalese rupee + * * `NZD` - New Zealand dollar + * * `OMR` - Omani rial + * * `PAB` - Panamanian balboa + * * `PEN` - Peruvian sol + * * `PGK` - Papua New Guinean kina + * * `PHP` - Philippine peso + * * `PKR` - Pakistani rupee + * * `PLN` - Polish złoty + * * `PYG` - Paraguayan guaraní + * * `QAR` - Qatari riyal + * * `RON` - Romanian leu + * * `RSD` - Serbian dinar + * * `RUB` - Russian ruble + * * `RWF` - Rwandan franc + * * `SAR` - Saudi riyal + * * `SBD` - Solomon Islands dollar + * * `SCR` - Seychelles rupee + * * `SDG` - Sudanese pound + * * `SEK` - Swedish krona/kronor + * * `SGD` - Singapore dollar + * * `SHP` - Saint Helena pound + * * `SLL` - Sierra Leonean leone + * * `SOS` - Somali shilling + * * `SRD` - Surinamese dollar + * * `SSP` - South Sudanese pound + * * `STN` - São Tomé and Príncipe dobra + * * `SVC` - Salvadoran colón + * * `SYP` - Syrian pound + * * `SZL` - Swazi lilangeni + * * `THB` - Thai baht + * * `TJS` - Tajikistani somoni + * * `TMT` - Turkmenistan manat + * * `TND` - Tunisian dinar + * * `TOP` - Tongan paʻanga + * * `TRY` - Turkish lira + * * `TTD` - Trinidad and Tobago dollar + * * `TWD` - New Taiwan dollar + * * `TZS` - Tanzanian shilling + * * `UAH` - Ukrainian hryvnia + * * `UGX` - Ugandan shilling + * * `USD` - United States dollar + * * `UYU` - Uruguayan peso + * * `UYW` - Unidad previsional[14] + * * `UZS` - Uzbekistan som + * * `VES` - Venezuelan bolívar soberano + * * `VND` - Vietnamese đồng + * * `VUV` - Vanuatu vatu + * * `WST` - Samoan tala + * * `XAF` - CFA franc BEAC + * * `XAG` - Silver (one troy ounce) + * * `XAU` - Gold (one troy ounce) + * * `XCD` - East Caribbean dollar + * * `XOF` - CFA franc BCEAO + * * `XPF` - CFP franc (franc Pacifique) + * * `YER` - Yemeni rial + * * `ZAR` - South African rand + * * `ZMW` - Zambian kwacha + * * `ZWL` - Zimbabwean dollar + * * `USDC` - USD Coin + */ +export enum CurrencyEnum { + AED = 'AED', + AFN = 'AFN', + ALL = 'ALL', + AMD = 'AMD', + ANG = 'ANG', + AOA = 'AOA', + ARS = 'ARS', + AUD = 'AUD', + AWG = 'AWG', + AZN = 'AZN', + BAM = 'BAM', + BBD = 'BBD', + BDT = 'BDT', + BGN = 'BGN', + BHD = 'BHD', + BIF = 'BIF', + BMD = 'BMD', + BND = 'BND', + BOB = 'BOB', + BOV = 'BOV', + BRL = 'BRL', + BSD = 'BSD', + BTN = 'BTN', + BWP = 'BWP', + BYN = 'BYN', + BZD = 'BZD', + CAD = 'CAD', + CDF = 'CDF', + CHF = 'CHF', + CLP = 'CLP', + CNY = 'CNY', + COP = 'COP', + CRC = 'CRC', + CUC = 'CUC', + CUP = 'CUP', + CVE = 'CVE', + CZK = 'CZK', + DJF = 'DJF', + DKK = 'DKK', + DOP = 'DOP', + DZD = 'DZD', + EGP = 'EGP', + ERN = 'ERN', + ETB = 'ETB', + EUR = 'EUR', + FJD = 'FJD', + FKP = 'FKP', + GBP = 'GBP', + GEL = 'GEL', + GHS = 'GHS', + GIP = 'GIP', + GMD = 'GMD', + GNF = 'GNF', + GTQ = 'GTQ', + GYD = 'GYD', + HKD = 'HKD', + HNL = 'HNL', + HRK = 'HRK', + HTG = 'HTG', + HUF = 'HUF', + IDR = 'IDR', + ILS = 'ILS', + INR = 'INR', + IQD = 'IQD', + IRR = 'IRR', + ISK = 'ISK', + JMD = 'JMD', + JOD = 'JOD', + JPY = 'JPY', + KES = 'KES', + KGS = 'KGS', + KHR = 'KHR', + KMF = 'KMF', + KPW = 'KPW', + KRW = 'KRW', + KWD = 'KWD', + KYD = 'KYD', + KZT = 'KZT', + LAK = 'LAK', + LBP = 'LBP', + LKR = 'LKR', + LRD = 'LRD', + LSL = 'LSL', + LYD = 'LYD', + MAD = 'MAD', + MDL = 'MDL', + MGA = 'MGA', + MKD = 'MKD', + MMK = 'MMK', + MNT = 'MNT', + MOP = 'MOP', + MRU = 'MRU', + MUR = 'MUR', + MVR = 'MVR', + MWK = 'MWK', + MXN = 'MXN', + MYR = 'MYR', + MZN = 'MZN', + NAD = 'NAD', + NGN = 'NGN', + NIO = 'NIO', + NOK = 'NOK', + NPR = 'NPR', + NZD = 'NZD', + OMR = 'OMR', + PAB = 'PAB', + PEN = 'PEN', + PGK = 'PGK', + PHP = 'PHP', + PKR = 'PKR', + PLN = 'PLN', + PYG = 'PYG', + QAR = 'QAR', + RON = 'RON', + RSD = 'RSD', + RUB = 'RUB', + RWF = 'RWF', + SAR = 'SAR', + SBD = 'SBD', + SCR = 'SCR', + SDG = 'SDG', + SEK = 'SEK', + SGD = 'SGD', + SHP = 'SHP', + SLL = 'SLL', + SOS = 'SOS', + SRD = 'SRD', + SSP = 'SSP', + STN = 'STN', + SVC = 'SVC', + SYP = 'SYP', + SZL = 'SZL', + THB = 'THB', + TJS = 'TJS', + TMT = 'TMT', + TND = 'TND', + TOP = 'TOP', + TRY = 'TRY', + TTD = 'TTD', + TWD = 'TWD', + TZS = 'TZS', + UAH = 'UAH', + UGX = 'UGX', + USD = 'USD', + UYU = 'UYU', + UYW = 'UYW', + UZS = 'UZS', + VES = 'VES', + VND = 'VND', + VUV = 'VUV', + WST = 'WST', + XAF = 'XAF', + XAG = 'XAG', + XAU = 'XAU', + XCD = 'XCD', + XOF = 'XOF', + XPF = 'XPF', + YER = 'YER', + ZAR = 'ZAR', + ZMW = 'ZMW', + ZWL = 'ZWL', + USDC = 'USDC', +} diff --git a/src/frontend/generated/models/DeduplicationGoldenRecordStatusEnum.ts b/src/frontend/generated/models/DeduplicationGoldenRecordStatusEnum.ts new file mode 100644 index 0000000000..ddf5d1a50e --- /dev/null +++ b/src/frontend/generated/models/DeduplicationGoldenRecordStatusEnum.ts @@ -0,0 +1,18 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `DUPLICATE` - Duplicate + * * `NEEDS_ADJUDICATION` - Needs Adjudication + * * `NOT_PROCESSED` - Not Processed + * * `POSTPONE` - Postpone + * * `UNIQUE` - Unique + */ +export enum DeduplicationGoldenRecordStatusEnum { + DUPLICATE = 'DUPLICATE', + NEEDS_ADJUDICATION = 'NEEDS_ADJUDICATION', + NOT_PROCESSED = 'NOT_PROCESSED', + POSTPONE = 'POSTPONE', + UNIQUE = 'UNIQUE', +} diff --git a/src/frontend/generated/models/Delegate.ts b/src/frontend/generated/models/Delegate.ts new file mode 100644 index 0000000000..8bb79acef9 --- /dev/null +++ b/src/frontend/generated/models/Delegate.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type Delegate = { + delegate_id: string; + delegated_for: Array; +}; + diff --git a/src/frontend/generated/models/DelegatePeople.ts b/src/frontend/generated/models/DelegatePeople.ts new file mode 100644 index 0000000000..9bc541e2f8 --- /dev/null +++ b/src/frontend/generated/models/DelegatePeople.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Delegate } from './Delegate'; +export type DelegatePeople = { + delegates: Array; +}; + diff --git a/src/frontend/generated/models/DisabilityEnum.ts b/src/frontend/generated/models/DisabilityEnum.ts new file mode 100644 index 0000000000..c27e2106d9 --- /dev/null +++ b/src/frontend/generated/models/DisabilityEnum.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `disabled` - disabled + * * `not disabled` - not disabled + */ +export enum DisabilityEnum { + DISABLED = 'disabled', + NOT_DISABLED = 'not disabled', +} diff --git a/src/frontend/generated/models/Document.ts b/src/frontend/generated/models/Document.ts new file mode 100644 index 0000000000..9e30749fd8 --- /dev/null +++ b/src/frontend/generated/models/Document.ts @@ -0,0 +1,33 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CountryEnum } from './CountryEnum'; +import type { DocumentStatusEnum } from './DocumentStatusEnum'; +import type { DocumentTypeEnum } from './DocumentTypeEnum'; +import type { RdiMergeStatusEnum } from './RdiMergeStatusEnum'; +export type Document = { + readonly id: string; + type: DocumentTypeEnum; + country: CountryEnum; + image?: string; + document_number: string; + rdi_merge_status?: RdiMergeStatusEnum; + is_removed?: boolean; + is_original?: boolean; + readonly created_at: string; + readonly updated_at: string; + last_sync_at?: string | null; + status?: DocumentStatusEnum; + cleared?: boolean; + cleared_date?: string; + issuance_date?: string | null; + expiry_date?: string | null; + is_migration_handled?: boolean; + cleared_by?: string | null; + /** + * If this object was copied from another, this field will contain the object it was copied from. + */ + copied_from?: string | null; +}; + diff --git a/src/frontend/generated/models/DocumentStatusEnum.ts b/src/frontend/generated/models/DocumentStatusEnum.ts new file mode 100644 index 0000000000..27b528b467 --- /dev/null +++ b/src/frontend/generated/models/DocumentStatusEnum.ts @@ -0,0 +1,16 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `PENDING` - Pending + * * `VALID` - Valid + * * `NEED_INVESTIGATION` - Need Investigation + * * `INVALID` - Invalid + */ +export enum DocumentStatusEnum { + PENDING = 'PENDING', + VALID = 'VALID', + NEED_INVESTIGATION = 'NEED_INVESTIGATION', + INVALID = 'INVALID', +} diff --git a/src/frontend/generated/models/DocumentTypeEnum.ts b/src/frontend/generated/models/DocumentTypeEnum.ts new file mode 100644 index 0000000000..ba16e84653 --- /dev/null +++ b/src/frontend/generated/models/DocumentTypeEnum.ts @@ -0,0 +1,30 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `birth_certificate` - Birth Certificate + * * `drivers_license` - Driver's License + * * `electoral_card` - Electoral Card + * * `national_id` - National ID + * * `national_passport` - National Passport + * * `tax_id` - Tax Number Identification + * * `residence_permit_no` - Foreigner's Residence Permit + * * `bank_statement` - Bank Statement + * * `disability_certificate` - Disability Certificate + * * `foster_child` - Foster Child + * * `other_id` - Other + */ +export enum DocumentTypeEnum { + BIRTH_CERTIFICATE = 'birth_certificate', + DRIVERS_LICENSE = 'drivers_license', + ELECTORAL_CARD = 'electoral_card', + NATIONAL_ID = 'national_id', + NATIONAL_PASSPORT = 'national_passport', + TAX_ID = 'tax_id', + RESIDENCE_PERMIT_NO = 'residence_permit_no', + BANK_STATEMENT = 'bank_statement', + DISABILITY_CERTIFICATE = 'disability_certificate', + FOSTER_CHILD = 'foster_child', + OTHER_ID = 'other_id', +} diff --git a/src/frontend/generated/models/FollowUpPaymentPlan.ts b/src/frontend/generated/models/FollowUpPaymentPlan.ts new file mode 100644 index 0000000000..862388eb28 --- /dev/null +++ b/src/frontend/generated/models/FollowUpPaymentPlan.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type FollowUpPaymentPlan = { + readonly id: string; + unicef_id?: string | null; +}; + diff --git a/src/frontend/generated/models/FrequencyOfPaymentsEnum.ts b/src/frontend/generated/models/FrequencyOfPaymentsEnum.ts new file mode 100644 index 0000000000..ecbfa21717 --- /dev/null +++ b/src/frontend/generated/models/FrequencyOfPaymentsEnum.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `ONE_OFF` - One-off + * * `REGULAR` - Regular + */ +export enum FrequencyOfPaymentsEnum { + ONE_OFF = 'ONE_OFF', + REGULAR = 'REGULAR', +} diff --git a/src/frontend/generated/models/HearingDisabilityEnum.ts b/src/frontend/generated/models/HearingDisabilityEnum.ts new file mode 100644 index 0000000000..44ab2febb4 --- /dev/null +++ b/src/frontend/generated/models/HearingDisabilityEnum.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `LOT_DIFFICULTY` - A lot of difficulty + * * `CANNOT_DO` - Cannot do at all + * * `SOME_DIFFICULTY` - Some difficulty + */ +export enum HearingDisabilityEnum { + LOT_DIFFICULTY = 'LOT_DIFFICULTY', + CANNOT_DO = 'CANNOT_DO', + SOME_DIFFICULTY = 'SOME_DIFFICULTY', +} diff --git a/src/frontend/generated/models/Household.ts b/src/frontend/generated/models/Household.ts new file mode 100644 index 0000000000..3eca5dc0cc --- /dev/null +++ b/src/frontend/generated/models/Household.ts @@ -0,0 +1,113 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { BlankEnum } from './BlankEnum'; +import type { CollectTypeEnum } from './CollectTypeEnum'; +import type { ConsentSharingEnum } from './ConsentSharingEnum'; +import type { CountryEnum } from './CountryEnum'; +import type { CountryOriginEnum } from './CountryOriginEnum'; +import type { CurrencyEnum } from './CurrencyEnum'; +import type { Individual } from './Individual'; +import type { OrgEnumeratorEnum } from './OrgEnumeratorEnum'; +import type { RdiMergeStatusEnum } from './RdiMergeStatusEnum'; +import type { RegistrationMethodEnum } from './RegistrationMethodEnum'; +import type { ResidenceStatusEnum } from './ResidenceStatusEnum'; +export type Household = { + collect_individual_data: string; + first_registration_date?: string; + last_registration_date?: string; + members: Array; + country: CountryEnum; + country_origin?: CountryOriginEnum; + size?: number | null; + rdi_merge_status?: RdiMergeStatusEnum; + is_original?: boolean; + readonly created_at: string; + readonly updated_at: string; + is_removed?: boolean; + removed_date?: string | null; + last_sync_at?: string | null; + unicef_id?: string | null; + withdrawn?: boolean; + withdrawn_date?: string | null; + consent_sign?: string; + consent?: boolean | null; + consent_sharing?: (ConsentSharingEnum | BlankEnum); + residence_status?: (ResidenceStatusEnum | BlankEnum); + address?: string; + zip_code?: string | null; + female_age_group_0_5_count?: number | null; + female_age_group_6_11_count?: number | null; + female_age_group_12_17_count?: number | null; + female_age_group_18_59_count?: number | null; + female_age_group_60_count?: number | null; + pregnant_count?: number | null; + male_age_group_0_5_count?: number | null; + male_age_group_6_11_count?: number | null; + male_age_group_12_17_count?: number | null; + male_age_group_18_59_count?: number | null; + male_age_group_60_count?: number | null; + female_age_group_0_5_disabled_count?: number | null; + female_age_group_6_11_disabled_count?: number | null; + female_age_group_12_17_disabled_count?: number | null; + female_age_group_18_59_disabled_count?: number | null; + female_age_group_60_disabled_count?: number | null; + male_age_group_0_5_disabled_count?: number | null; + male_age_group_6_11_disabled_count?: number | null; + male_age_group_12_17_disabled_count?: number | null; + male_age_group_18_59_disabled_count?: number | null; + male_age_group_60_disabled_count?: number | null; + children_count?: number | null; + male_children_count?: number | null; + female_children_count?: number | null; + children_disabled_count?: number | null; + male_children_disabled_count?: number | null; + female_children_disabled_count?: number | null; + returnee?: boolean | null; + flex_fields?: any; + fchild_hoh?: boolean | null; + child_hoh?: boolean | null; + start?: string | null; + deviceid?: string; + name_enumerator?: string; + org_enumerator?: (OrgEnumeratorEnum | BlankEnum); + org_name_enumerator?: string; + village?: string; + registration_method?: (RegistrationMethodEnum | BlankEnum); + currency?: (CurrencyEnum | BlankEnum); + unhcr_id?: string; + user_fields?: any; + registration_id?: string | null; + program_registration_id?: string | null; + total_cash_received_usd?: string | null; + total_cash_received?: string | null; + family_id?: string | null; + origin_unicef_id?: string | null; + is_migration_handled?: boolean; + migrated_at?: string | null; + is_recalculated_group_ages?: boolean; + collect_type?: CollectTypeEnum; + enumerator_rec_id?: number | null; + mis_unicef_id?: string | null; + flex_registrations_record_id?: number | null; + household_collection?: number | null; + admin_area?: string | null; + admin1?: string | null; + admin2?: string | null; + admin3?: string | null; + admin4?: string | null; + storage_obj?: number | null; + /** + * If this household was copied from another household, this field will contain the household it was copied from. + */ + copied_from?: string | null; + /** + * This is only used to track collector (primary or secondary) of a household. + * They may still be a HOH of this household or any other household. + * Through model will contain the role (ROLE_CHOICE) they are connected with on. + */ + readonly representatives: Array; + programs?: Array; +}; + diff --git a/src/frontend/generated/models/Individual.ts b/src/frontend/generated/models/Individual.ts new file mode 100644 index 0000000000..6c0a9b4f6e --- /dev/null +++ b/src/frontend/generated/models/Individual.ts @@ -0,0 +1,124 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { BlankEnum } from './BlankEnum'; +import type { CommsDisabilityEnum } from './CommsDisabilityEnum'; +import type { DeduplicationGoldenRecordStatusEnum } from './DeduplicationGoldenRecordStatusEnum'; +import type { DisabilityEnum } from './DisabilityEnum'; +import type { Document } from './Document'; +import type { HearingDisabilityEnum } from './HearingDisabilityEnum'; +import type { MemoryDisabilityEnum } from './MemoryDisabilityEnum'; +import type { NullEnum } from './NullEnum'; +import type { PhysicalDisabilityEnum } from './PhysicalDisabilityEnum'; +import type { PreferredLanguageEnum } from './PreferredLanguageEnum'; +import type { RdiMergeStatusEnum } from './RdiMergeStatusEnum'; +import type { RelationshipEnum } from './RelationshipEnum'; +import type { SeeingDisabilityEnum } from './SeeingDisabilityEnum'; +import type { SelfcareDisabilityEnum } from './SelfcareDisabilityEnum'; +import type { SexEnum } from './SexEnum'; +import type { WorkStatusEnum } from './WorkStatusEnum'; +export type Individual = { + first_registration_date?: string; + last_registration_date?: string; + readonly household: string; + role?: string; + observed_disability?: string; + country_origin?: string; + marital_status?: string; + documents?: Array; + birth_date: string; + rdi_merge_status?: RdiMergeStatusEnum; + is_original?: boolean; + is_removed?: boolean; + removed_date?: string | null; + last_sync_at?: string | null; + unicef_id?: string | null; + duplicate?: boolean; + duplicate_date?: string | null; + withdrawn?: boolean; + withdrawn_date?: string | null; + individual_id?: string; + photo?: string; + full_name: string; + given_name?: string; + middle_name?: string; + family_name?: string; + sex: SexEnum; + estimated_birth_date?: boolean; + phone_no?: string; + phone_no_valid?: boolean | null; + phone_no_alternative?: string; + phone_no_alternative_valid?: boolean | null; + email?: string; + payment_delivery_phone_no?: string | null; + /** + * This represents the MEMBER relationship. can be blank + * as well if household is null! + * + * * `UNKNOWN` - Unknown + * * `AUNT_UNCLE` - Aunt / Uncle + * * `BROTHER_SISTER` - Brother / Sister + * * `COUSIN` - Cousin + * * `DAUGHTERINLAW_SONINLAW` - Daughter-in-law / Son-in-law + * * `GRANDDAUGHER_GRANDSON` - Granddaughter / Grandson + * * `GRANDMOTHER_GRANDFATHER` - Grandmother / Grandfather + * * `HEAD` - Head of household (self) + * * `MOTHER_FATHER` - Mother / Father + * * `MOTHERINLAW_FATHERINLAW` - Mother-in-law / Father-in-law + * * `NEPHEW_NIECE` - Nephew / Niece + * * `NON_BENEFICIARY` - Not a Family Member. Can only act as a recipient. + * * `OTHER` - Other + * * `SISTERINLAW_BROTHERINLAW` - Sister-in-law / Brother-in-law + * * `SON_DAUGHTER` - Son / Daughter + * * `WIFE_HUSBAND` - Wife / Husband + * * `FOSTER_CHILD` - Foster child + * * `FREE_UNION` - Free union + */ + relationship?: (RelationshipEnum | BlankEnum); + work_status?: (WorkStatusEnum | BlankEnum); + flex_fields?: any; + user_fields?: any; + enrolled_in_nutrition_programme?: boolean | null; + administration_of_rutf?: boolean | null; + deduplication_golden_record_status?: DeduplicationGoldenRecordStatusEnum; + imported_individual_id?: string | null; + sanction_list_possible_match?: boolean; + sanction_list_confirmed_match?: boolean; + pregnant?: boolean | null; + disability?: DisabilityEnum; + disability_certificate_picture?: string | null; + seeing_disability?: (SeeingDisabilityEnum | BlankEnum); + hearing_disability?: (HearingDisabilityEnum | BlankEnum); + physical_disability?: (PhysicalDisabilityEnum | BlankEnum); + memory_disability?: (MemoryDisabilityEnum | BlankEnum); + selfcare_disability?: (SelfcareDisabilityEnum | BlankEnum); + comms_disability?: (CommsDisabilityEnum | BlankEnum); + who_answers_phone?: string; + who_answers_alt_phone?: string; + fchild_hoh?: boolean; + child_hoh?: boolean; + /** + * Kobo asset ID, Xlsx row ID, Aurora source ID + */ + detail_id?: string | null; + registration_id?: string | null; + program_registration_id?: string | null; + preferred_language?: (PreferredLanguageEnum | BlankEnum | NullEnum) | null; + relationship_confirmed?: boolean; + age_at_registration?: number | null; + wallet_name?: string; + blockchain_name?: string; + wallet_address?: string; + origin_unicef_id?: string | null; + is_migration_handled?: boolean; + migrated_at?: string | null; + mis_unicef_id?: string | null; + individual_collection?: number | null; + program?: string | null; + /** + * If this individual was copied from another individual, this field will contain the individual it was copied from. + */ + copied_from?: string | null; +}; + diff --git a/src/frontend/generated/models/MemoryDisabilityEnum.ts b/src/frontend/generated/models/MemoryDisabilityEnum.ts new file mode 100644 index 0000000000..bb7417cc8f --- /dev/null +++ b/src/frontend/generated/models/MemoryDisabilityEnum.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `LOT_DIFFICULTY` - A lot of difficulty + * * `CANNOT_DO` - Cannot do at all + * * `SOME_DIFFICULTY` - Some difficulty + */ +export enum MemoryDisabilityEnum { + LOT_DIFFICULTY = 'LOT_DIFFICULTY', + CANNOT_DO = 'CANNOT_DO', + SOME_DIFFICULTY = 'SOME_DIFFICULTY', +} diff --git a/src/frontend/generated/models/NullEnum.ts b/src/frontend/generated/models/NullEnum.ts new file mode 100644 index 0000000000..231d6bdd61 --- /dev/null +++ b/src/frontend/generated/models/NullEnum.ts @@ -0,0 +1,7 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type NullEnum = { +}; + diff --git a/src/frontend/generated/models/OrgEnumeratorEnum.ts b/src/frontend/generated/models/OrgEnumeratorEnum.ts new file mode 100644 index 0000000000..6fb2d07e33 --- /dev/null +++ b/src/frontend/generated/models/OrgEnumeratorEnum.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `PARTNER` - Partner + * * `UNICEF` - UNICEF + */ +export enum OrgEnumeratorEnum { + PARTNER = 'PARTNER', + UNICEF = 'UNICEF', +} diff --git a/src/frontend/generated/models/PaginatedAreaList.ts b/src/frontend/generated/models/PaginatedAreaList.ts new file mode 100644 index 0000000000..33f099fb57 --- /dev/null +++ b/src/frontend/generated/models/PaginatedAreaList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Area } from './Area'; +export type PaginatedAreaList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedAreaListList.ts b/src/frontend/generated/models/PaginatedAreaListList.ts new file mode 100644 index 0000000000..a8e4733fba --- /dev/null +++ b/src/frontend/generated/models/PaginatedAreaListList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { AreaList } from './AreaList'; +export type PaginatedAreaListList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedAreaTypeList.ts b/src/frontend/generated/models/PaginatedAreaTypeList.ts new file mode 100644 index 0000000000..8d23f1dff3 --- /dev/null +++ b/src/frontend/generated/models/PaginatedAreaTypeList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { AreaType } from './AreaType'; +export type PaginatedAreaTypeList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedBeneficiaryGroupList.ts b/src/frontend/generated/models/PaginatedBeneficiaryGroupList.ts new file mode 100644 index 0000000000..b57b4dd8ce --- /dev/null +++ b/src/frontend/generated/models/PaginatedBeneficiaryGroupList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { BeneficiaryGroup } from './BeneficiaryGroup'; +export type PaginatedBeneficiaryGroupList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedBusinessAreaList.ts b/src/frontend/generated/models/PaginatedBusinessAreaList.ts new file mode 100644 index 0000000000..083d867080 --- /dev/null +++ b/src/frontend/generated/models/PaginatedBusinessAreaList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { BusinessArea } from './BusinessArea'; +export type PaginatedBusinessAreaList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedCountryList.ts b/src/frontend/generated/models/PaginatedCountryList.ts new file mode 100644 index 0000000000..31d6598e43 --- /dev/null +++ b/src/frontend/generated/models/PaginatedCountryList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Country } from './Country'; +export type PaginatedCountryList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedPaymentPlanList.ts b/src/frontend/generated/models/PaginatedPaymentPlanList.ts new file mode 100644 index 0000000000..28b171f165 --- /dev/null +++ b/src/frontend/generated/models/PaginatedPaymentPlanList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { PaymentPlan } from './PaymentPlan'; +export type PaginatedPaymentPlanList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedPeriodicDataUpdateTemplateListList.ts b/src/frontend/generated/models/PaginatedPeriodicDataUpdateTemplateListList.ts new file mode 100644 index 0000000000..3a4ea7705e --- /dev/null +++ b/src/frontend/generated/models/PaginatedPeriodicDataUpdateTemplateListList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { PeriodicDataUpdateTemplateList } from './PeriodicDataUpdateTemplateList'; +export type PaginatedPeriodicDataUpdateTemplateListList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedPeriodicDataUpdateUploadListList.ts b/src/frontend/generated/models/PaginatedPeriodicDataUpdateUploadListList.ts new file mode 100644 index 0000000000..647e2b1afd --- /dev/null +++ b/src/frontend/generated/models/PaginatedPeriodicDataUpdateUploadListList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { PeriodicDataUpdateUploadList } from './PeriodicDataUpdateUploadList'; +export type PaginatedPeriodicDataUpdateUploadListList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedPeriodicFieldList.ts b/src/frontend/generated/models/PaginatedPeriodicFieldList.ts new file mode 100644 index 0000000000..f6d048d7fb --- /dev/null +++ b/src/frontend/generated/models/PaginatedPeriodicFieldList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { PeriodicField } from './PeriodicField'; +export type PaginatedPeriodicFieldList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedProgramCycleListList.ts b/src/frontend/generated/models/PaginatedProgramCycleListList.ts new file mode 100644 index 0000000000..0a805fa263 --- /dev/null +++ b/src/frontend/generated/models/PaginatedProgramCycleListList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ProgramCycleList } from './ProgramCycleList'; +export type PaginatedProgramCycleListList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedProgramGlobalList.ts b/src/frontend/generated/models/PaginatedProgramGlobalList.ts new file mode 100644 index 0000000000..168285da8f --- /dev/null +++ b/src/frontend/generated/models/PaginatedProgramGlobalList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ProgramGlobal } from './ProgramGlobal'; +export type PaginatedProgramGlobalList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedRegistrationDataImportListList.ts b/src/frontend/generated/models/PaginatedRegistrationDataImportListList.ts new file mode 100644 index 0000000000..e6ebd10a75 --- /dev/null +++ b/src/frontend/generated/models/PaginatedRegistrationDataImportListList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { RegistrationDataImportList } from './RegistrationDataImportList'; +export type PaginatedRegistrationDataImportListList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PaginatedTargetPopulationListList.ts b/src/frontend/generated/models/PaginatedTargetPopulationListList.ts new file mode 100644 index 0000000000..0f3af7faa3 --- /dev/null +++ b/src/frontend/generated/models/PaginatedTargetPopulationListList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { TargetPopulationList } from './TargetPopulationList'; +export type PaginatedTargetPopulationListList = { + count: number; + next?: string | null; + previous?: string | null; + results: Array; +}; + diff --git a/src/frontend/generated/models/PatchedProgramCycleUpdate.ts b/src/frontend/generated/models/PatchedProgramCycleUpdate.ts new file mode 100644 index 0000000000..ed3f8058b9 --- /dev/null +++ b/src/frontend/generated/models/PatchedProgramCycleUpdate.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PatchedProgramCycleUpdate = { + title?: string; + start_date?: string; + end_date?: string; +}; + diff --git a/src/frontend/generated/models/PatchedRDI.ts b/src/frontend/generated/models/PatchedRDI.ts new file mode 100644 index 0000000000..7e12ed225d --- /dev/null +++ b/src/frontend/generated/models/PatchedRDI.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PatchedRDI = { + name?: string; + program?: string; +}; + diff --git a/src/frontend/generated/models/PaymentPlan.ts b/src/frontend/generated/models/PaymentPlan.ts new file mode 100644 index 0000000000..2f45797695 --- /dev/null +++ b/src/frontend/generated/models/PaymentPlan.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { FollowUpPaymentPlan } from './FollowUpPaymentPlan'; +export type PaymentPlan = { + id: string; + unicef_id?: string | null; + name?: string | null; + status: string; + target_population: string; + total_households_count?: number; + currency: string; + total_entitled_quantity?: string | null; + total_delivered_quantity?: string | null; + total_undelivered_quantity?: string | null; + dispersion_start_date: string; + dispersion_end_date: string; + is_follow_up?: boolean; + readonly follow_ups: Array; + program: string; + program_id: string; + readonly last_approval_process_date: string | null; + readonly last_approval_process_by: string | null; +}; + diff --git a/src/frontend/generated/models/PaymentPlanBulkAction.ts b/src/frontend/generated/models/PaymentPlanBulkAction.ts new file mode 100644 index 0000000000..d028538669 --- /dev/null +++ b/src/frontend/generated/models/PaymentPlanBulkAction.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ActionEnum } from './ActionEnum'; +export type PaymentPlanBulkAction = { + ids: Array; + action: ActionEnum; + comment?: string; +}; + diff --git a/src/frontend/generated/models/PaymentPlanSupportingDocument.ts b/src/frontend/generated/models/PaymentPlanSupportingDocument.ts new file mode 100644 index 0000000000..1079e93a32 --- /dev/null +++ b/src/frontend/generated/models/PaymentPlanSupportingDocument.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PaymentPlanSupportingDocument = { + readonly id: string; + title: string; + file: string; + readonly uploaded_at: string; + created_by?: string | null; +}; + diff --git a/src/frontend/generated/models/PeriodicDataUpdateTemplateCreate.ts b/src/frontend/generated/models/PeriodicDataUpdateTemplateCreate.ts new file mode 100644 index 0000000000..6f03e6ecc6 --- /dev/null +++ b/src/frontend/generated/models/PeriodicDataUpdateTemplateCreate.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PeriodicDataUpdateTemplateCreate = { + readonly id: number; + rounds_data: any; + filters?: any; +}; + diff --git a/src/frontend/generated/models/PeriodicDataUpdateTemplateDetail.ts b/src/frontend/generated/models/PeriodicDataUpdateTemplateDetail.ts new file mode 100644 index 0000000000..a80bca3307 --- /dev/null +++ b/src/frontend/generated/models/PeriodicDataUpdateTemplateDetail.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PeriodicDataUpdateTemplateDetail = { + readonly id: number; + rounds_data: any; +}; + diff --git a/src/frontend/generated/models/PeriodicDataUpdateTemplateList.ts b/src/frontend/generated/models/PeriodicDataUpdateTemplateList.ts new file mode 100644 index 0000000000..99ba796684 --- /dev/null +++ b/src/frontend/generated/models/PeriodicDataUpdateTemplateList.ts @@ -0,0 +1,14 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PeriodicDataUpdateTemplateList = { + readonly id: number; + number_of_records?: number | null; + readonly created_at: string; + created_by?: string; + status: string; + status_display: string; + can_export: boolean; +}; + diff --git a/src/frontend/generated/models/PeriodicDataUpdateUpload.ts b/src/frontend/generated/models/PeriodicDataUpdateUpload.ts new file mode 100644 index 0000000000..8eb83e4e3f --- /dev/null +++ b/src/frontend/generated/models/PeriodicDataUpdateUpload.ts @@ -0,0 +1,8 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PeriodicDataUpdateUpload = { + file: string; +}; + diff --git a/src/frontend/generated/models/PeriodicDataUpdateUploadDetail.ts b/src/frontend/generated/models/PeriodicDataUpdateUploadDetail.ts new file mode 100644 index 0000000000..5cbc3269be --- /dev/null +++ b/src/frontend/generated/models/PeriodicDataUpdateUploadDetail.ts @@ -0,0 +1,14 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PeriodicDataUpdateUploadDetail = { + readonly id: number; + template: number; + readonly created_at: string; + created_by?: string; + status: string; + status_display: string; + errors_info: any; +}; + diff --git a/src/frontend/generated/models/PeriodicDataUpdateUploadList.ts b/src/frontend/generated/models/PeriodicDataUpdateUploadList.ts new file mode 100644 index 0000000000..c4dc6819a0 --- /dev/null +++ b/src/frontend/generated/models/PeriodicDataUpdateUploadList.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type PeriodicDataUpdateUploadList = { + readonly id: number; + template: number; + readonly created_at: string; + created_by?: string; + status: string; + status_display: string; +}; + diff --git a/src/frontend/generated/models/PeriodicField.ts b/src/frontend/generated/models/PeriodicField.ts new file mode 100644 index 0000000000..f68c196de3 --- /dev/null +++ b/src/frontend/generated/models/PeriodicField.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { PeriodicFieldData } from './PeriodicFieldData'; +export type PeriodicField = { + readonly id: string; + name: string; + readonly label: string; + pdu_data: PeriodicFieldData; +}; + diff --git a/src/frontend/generated/models/PeriodicFieldData.ts b/src/frontend/generated/models/PeriodicFieldData.ts new file mode 100644 index 0000000000..e1846a7474 --- /dev/null +++ b/src/frontend/generated/models/PeriodicFieldData.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { SubtypeEnum } from './SubtypeEnum'; +export type PeriodicFieldData = { + subtype: SubtypeEnum; + number_of_rounds: number; + rounds_names?: Array; +}; + diff --git a/src/frontend/generated/models/PhysicalDisabilityEnum.ts b/src/frontend/generated/models/PhysicalDisabilityEnum.ts new file mode 100644 index 0000000000..0635321703 --- /dev/null +++ b/src/frontend/generated/models/PhysicalDisabilityEnum.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `LOT_DIFFICULTY` - A lot of difficulty + * * `CANNOT_DO` - Cannot do at all + * * `SOME_DIFFICULTY` - Some difficulty + */ +export enum PhysicalDisabilityEnum { + LOT_DIFFICULTY = 'LOT_DIFFICULTY', + CANNOT_DO = 'CANNOT_DO', + SOME_DIFFICULTY = 'SOME_DIFFICULTY', +} diff --git a/src/frontend/generated/models/PreferredLanguageEnum.ts b/src/frontend/generated/models/PreferredLanguageEnum.ts new file mode 100644 index 0000000000..e4adacf605 --- /dev/null +++ b/src/frontend/generated/models/PreferredLanguageEnum.ts @@ -0,0 +1,40 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `en-us` - English | English + * * `ar-ae` - | عربيArabic + * * `cs-cz` - čeština | Czech + * * `de-de` - Deutsch + * * `es-es` - Español | Spanish + * * `fr-fr` - Français | French + * * `hu-hu` - Magyar | Hungarian + * * `it-it` - Italiano + * * `pl-pl` - Polskie | Polish + * * `pt-pt` - Português + * * `ro-ro` - Română + * * `ru-ru` - Русский | Russian + * * `si-si` - සිංහල | Sinhala + * * `ta-ta` - தமிழ் | Tamil + * * `uk-ua` - український | Ukrainian + * * `hi-hi` - हिंदी + */ +export enum PreferredLanguageEnum { + EN_US = 'en-us', + AR_AE = 'ar-ae', + CS_CZ = 'cs-cz', + DE_DE = 'de-de', + ES_ES = 'es-es', + FR_FR = 'fr-fr', + HU_HU = 'hu-hu', + IT_IT = 'it-it', + PL_PL = 'pl-pl', + PT_PT = 'pt-pt', + RO_RO = 'ro-ro', + RU_RU = 'ru-ru', + SI_SI = 'si-si', + TA_TA = 'ta-ta', + UK_UA = 'uk-ua', + HI_HI = 'hi-hi', +} diff --git a/src/frontend/generated/models/Program.ts b/src/frontend/generated/models/Program.ts new file mode 100644 index 0000000000..0146bd58a1 --- /dev/null +++ b/src/frontend/generated/models/Program.ts @@ -0,0 +1,20 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { FrequencyOfPaymentsEnum } from './FrequencyOfPaymentsEnum'; +import type { SectorEnum } from './SectorEnum'; +export type Program = { + readonly id: string; + name: string; + start_date: string; + end_date?: string | null; + budget: string; + frequency_of_payments: FrequencyOfPaymentsEnum; + sector: SectorEnum; + cash_plus: boolean; + population_goal: number; + data_collecting_type: number; + beneficiary_group: string; +}; + diff --git a/src/frontend/generated/models/ProgramCycleCreate.ts b/src/frontend/generated/models/ProgramCycleCreate.ts new file mode 100644 index 0000000000..9cfc2e8e70 --- /dev/null +++ b/src/frontend/generated/models/ProgramCycleCreate.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ProgramCycleCreate = { + title: string; + start_date: string; + end_date?: string; +}; + diff --git a/src/frontend/generated/models/ProgramCycleList.ts b/src/frontend/generated/models/ProgramCycleList.ts new file mode 100644 index 0000000000..655216dc88 --- /dev/null +++ b/src/frontend/generated/models/ProgramCycleList.ts @@ -0,0 +1,22 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ProgramCycleList = { + readonly id: string; + title?: string | null; + status: string; + start_date: string; + end_date: string; + program_start_date: string; + program_end_date: string; + readonly created_at: string; + readonly total_entitled_quantity_usd: number; + readonly total_undelivered_quantity_usd: number; + readonly total_delivered_quantity_usd: number; + readonly frequency_of_payments: string; + readonly created_by: string; + readonly admin_url: string | null; + readonly can_remove_cycle: boolean; +}; + diff --git a/src/frontend/generated/models/ProgramCycleUpdate.ts b/src/frontend/generated/models/ProgramCycleUpdate.ts new file mode 100644 index 0000000000..7194f2af67 --- /dev/null +++ b/src/frontend/generated/models/ProgramCycleUpdate.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ProgramCycleUpdate = { + title?: string; + start_date?: string; + end_date?: string; +}; + diff --git a/src/frontend/generated/models/ProgramGlobal.ts b/src/frontend/generated/models/ProgramGlobal.ts new file mode 100644 index 0000000000..75ea27cbfc --- /dev/null +++ b/src/frontend/generated/models/ProgramGlobal.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { BlankEnum } from './BlankEnum'; +import type { FrequencyOfPaymentsEnum } from './FrequencyOfPaymentsEnum'; +import type { NullEnum } from './NullEnum'; +import type { ProgramGlobalStatusEnum } from './ProgramGlobalStatusEnum'; +import type { ScopeEnum } from './ScopeEnum'; +import type { SectorEnum } from './SectorEnum'; +export type ProgramGlobal = { + readonly id: string; + name: string; + programme_code?: string | null; + status: ProgramGlobalStatusEnum; + start_date: string; + end_date?: string | null; + budget: string; + frequency_of_payments: FrequencyOfPaymentsEnum; + sector: SectorEnum; + scope?: (ScopeEnum | BlankEnum | NullEnum) | null; + cash_plus: boolean; + population_goal: number; + readonly business_area_code: string; +}; + diff --git a/src/frontend/generated/models/ProgramGlobalStatusEnum.ts b/src/frontend/generated/models/ProgramGlobalStatusEnum.ts new file mode 100644 index 0000000000..ff22365e2a --- /dev/null +++ b/src/frontend/generated/models/ProgramGlobalStatusEnum.ts @@ -0,0 +1,14 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `ACTIVE` - Active + * * `DRAFT` - Draft + * * `FINISHED` - Finished + */ +export enum ProgramGlobalStatusEnum { + ACTIVE = 'ACTIVE', + DRAFT = 'DRAFT', + FINISHED = 'FINISHED', +} diff --git a/src/frontend/generated/models/PushPeople.ts b/src/frontend/generated/models/PushPeople.ts new file mode 100644 index 0000000000..f49fd92589 --- /dev/null +++ b/src/frontend/generated/models/PushPeople.ts @@ -0,0 +1,140 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Admin1Enum } from './Admin1Enum'; +import type { Admin2Enum } from './Admin2Enum'; +import type { Admin3Enum } from './Admin3Enum'; +import type { Admin4Enum } from './Admin4Enum'; +import type { BlankEnum } from './BlankEnum'; +import type { CollectIndividualDataEnum } from './CollectIndividualDataEnum'; +import type { CommsDisabilityEnum } from './CommsDisabilityEnum'; +import type { CountryEnum } from './CountryEnum'; +import type { CountryOriginEnum } from './CountryOriginEnum'; +import type { DeduplicationGoldenRecordStatusEnum } from './DeduplicationGoldenRecordStatusEnum'; +import type { DisabilityEnum } from './DisabilityEnum'; +import type { Document } from './Document'; +import type { HearingDisabilityEnum } from './HearingDisabilityEnum'; +import type { MemoryDisabilityEnum } from './MemoryDisabilityEnum'; +import type { NullEnum } from './NullEnum'; +import type { PhysicalDisabilityEnum } from './PhysicalDisabilityEnum'; +import type { PreferredLanguageEnum } from './PreferredLanguageEnum'; +import type { PushPeopleTypeEnum } from './PushPeopleTypeEnum'; +import type { RdiMergeStatusEnum } from './RdiMergeStatusEnum'; +import type { RelationshipEnum } from './RelationshipEnum'; +import type { ResidenceStatusEnum } from './ResidenceStatusEnum'; +import type { SeeingDisabilityEnum } from './SeeingDisabilityEnum'; +import type { SelfcareDisabilityEnum } from './SelfcareDisabilityEnum'; +import type { SexEnum } from './SexEnum'; +import type { WorkStatusEnum } from './WorkStatusEnum'; +export type PushPeople = { + first_registration_date?: string; + last_registration_date?: string; + observed_disability?: string; + marital_status?: string; + documents?: Array; + birth_date: string; + type: (PushPeopleTypeEnum | BlankEnum); + country_origin?: CountryOriginEnum; + country: CountryEnum; + collect_individual_data: (CollectIndividualDataEnum | BlankEnum); + residence_status: (ResidenceStatusEnum | BlankEnum); + village?: string | null; + phone_no?: string | null; + phone_no_alternative?: string | null; + admin1?: (Admin1Enum | BlankEnum | NullEnum) | null; + admin2?: (Admin2Enum | BlankEnum | NullEnum) | null; + admin3?: (Admin3Enum | BlankEnum | NullEnum) | null; + admin4?: (Admin4Enum | BlankEnum | NullEnum) | null; + rdi_merge_status?: RdiMergeStatusEnum; + is_original?: boolean; + is_removed?: boolean; + removed_date?: string | null; + last_sync_at?: string | null; + /** + * record revision number + */ + version?: number; + duplicate?: boolean; + duplicate_date?: string | null; + withdrawn?: boolean; + withdrawn_date?: string | null; + individual_id?: string; + photo?: string; + full_name: string; + given_name?: string; + middle_name?: string; + family_name?: string; + sex: SexEnum; + estimated_birth_date?: boolean; + phone_no_valid?: boolean | null; + phone_no_alternative_valid?: boolean | null; + email?: string; + payment_delivery_phone_no?: string | null; + /** + * This represents the MEMBER relationship. can be blank + * as well if household is null! + * + * * `UNKNOWN` - Unknown + * * `AUNT_UNCLE` - Aunt / Uncle + * * `BROTHER_SISTER` - Brother / Sister + * * `COUSIN` - Cousin + * * `DAUGHTERINLAW_SONINLAW` - Daughter-in-law / Son-in-law + * * `GRANDDAUGHER_GRANDSON` - Granddaughter / Grandson + * * `GRANDMOTHER_GRANDFATHER` - Grandmother / Grandfather + * * `HEAD` - Head of household (self) + * * `MOTHER_FATHER` - Mother / Father + * * `MOTHERINLAW_FATHERINLAW` - Mother-in-law / Father-in-law + * * `NEPHEW_NIECE` - Nephew / Niece + * * `NON_BENEFICIARY` - Not a Family Member. Can only act as a recipient. + * * `OTHER` - Other + * * `SISTERINLAW_BROTHERINLAW` - Sister-in-law / Brother-in-law + * * `SON_DAUGHTER` - Son / Daughter + * * `WIFE_HUSBAND` - Wife / Husband + * * `FOSTER_CHILD` - Foster child + * * `FREE_UNION` - Free union + */ + relationship?: (RelationshipEnum | BlankEnum); + work_status?: (WorkStatusEnum | BlankEnum); + flex_fields?: any; + user_fields?: any; + enrolled_in_nutrition_programme?: boolean | null; + administration_of_rutf?: boolean | null; + deduplication_golden_record_status?: DeduplicationGoldenRecordStatusEnum; + imported_individual_id?: string | null; + sanction_list_possible_match?: boolean; + sanction_list_confirmed_match?: boolean; + pregnant?: boolean | null; + disability?: DisabilityEnum; + disability_certificate_picture?: string | null; + seeing_disability?: (SeeingDisabilityEnum | BlankEnum); + hearing_disability?: (HearingDisabilityEnum | BlankEnum); + physical_disability?: (PhysicalDisabilityEnum | BlankEnum); + memory_disability?: (MemoryDisabilityEnum | BlankEnum); + selfcare_disability?: (SelfcareDisabilityEnum | BlankEnum); + comms_disability?: (CommsDisabilityEnum | BlankEnum); + who_answers_phone?: string; + who_answers_alt_phone?: string; + fchild_hoh?: boolean; + child_hoh?: boolean; + registration_id?: string | null; + program_registration_id?: string | null; + preferred_language?: (PreferredLanguageEnum | BlankEnum | NullEnum) | null; + relationship_confirmed?: boolean; + age_at_registration?: number | null; + wallet_name?: string; + blockchain_name?: string; + wallet_address?: string; + origin_unicef_id?: string | null; + is_migration_handled?: boolean; + migrated_at?: string | null; + mis_unicef_id?: string | null; + vector_column?: string | null; + individual_collection?: number | null; + program?: string | null; + /** + * If this individual was copied from another individual, this field will contain the individual it was copied from. + */ + copied_from?: string | null; +}; + diff --git a/src/frontend/generated/models/PushPeopleTypeEnum.ts b/src/frontend/generated/models/PushPeopleTypeEnum.ts new file mode 100644 index 0000000000..d09e54fef2 --- /dev/null +++ b/src/frontend/generated/models/PushPeopleTypeEnum.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `NON_BENEFICIARY` - Non Beneficiary + */ +export enum PushPeopleTypeEnum { + NON_BENEFICIARY = 'NON_BENEFICIARY', +} diff --git a/src/frontend/generated/models/RDI.ts b/src/frontend/generated/models/RDI.ts new file mode 100644 index 0000000000..062643700c --- /dev/null +++ b/src/frontend/generated/models/RDI.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type RDI = { + name: string; + program: string; +}; + diff --git a/src/frontend/generated/models/RDINested.ts b/src/frontend/generated/models/RDINested.ts new file mode 100644 index 0000000000..c3c4f4117f --- /dev/null +++ b/src/frontend/generated/models/RDINested.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Household } from './Household'; +export type RDINested = { + name: string; + households: Array; + program: string; +}; + diff --git a/src/frontend/generated/models/RdiMergeStatusEnum.ts b/src/frontend/generated/models/RdiMergeStatusEnum.ts new file mode 100644 index 0000000000..ec5feb792d --- /dev/null +++ b/src/frontend/generated/models/RdiMergeStatusEnum.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `PENDING` - Pending + * * `MERGED` - Merged + */ +export enum RdiMergeStatusEnum { + PENDING = 'PENDING', + MERGED = 'MERGED', +} diff --git a/src/frontend/generated/models/RegistrationDataImportList.ts b/src/frontend/generated/models/RegistrationDataImportList.ts new file mode 100644 index 0000000000..255af0d15e --- /dev/null +++ b/src/frontend/generated/models/RegistrationDataImportList.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type RegistrationDataImportList = { + readonly id: string; + name: string; + status: string; + data_source: string; + imported_by?: string; + readonly created_at: string; +}; + diff --git a/src/frontend/generated/models/RegistrationMethodEnum.ts b/src/frontend/generated/models/RegistrationMethodEnum.ts new file mode 100644 index 0000000000..2fa9eb3197 --- /dev/null +++ b/src/frontend/generated/models/RegistrationMethodEnum.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `COMMUNITY` - Community-level Registration + * * `HH_REGISTRATION` - Household Registration + */ +export enum RegistrationMethodEnum { + COMMUNITY = 'COMMUNITY', + HH_REGISTRATION = 'HH_REGISTRATION', +} diff --git a/src/frontend/generated/models/RelationshipEnum.ts b/src/frontend/generated/models/RelationshipEnum.ts new file mode 100644 index 0000000000..8863da6c94 --- /dev/null +++ b/src/frontend/generated/models/RelationshipEnum.ts @@ -0,0 +1,44 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `UNKNOWN` - Unknown + * * `AUNT_UNCLE` - Aunt / Uncle + * * `BROTHER_SISTER` - Brother / Sister + * * `COUSIN` - Cousin + * * `DAUGHTERINLAW_SONINLAW` - Daughter-in-law / Son-in-law + * * `GRANDDAUGHER_GRANDSON` - Granddaughter / Grandson + * * `GRANDMOTHER_GRANDFATHER` - Grandmother / Grandfather + * * `HEAD` - Head of household (self) + * * `MOTHER_FATHER` - Mother / Father + * * `MOTHERINLAW_FATHERINLAW` - Mother-in-law / Father-in-law + * * `NEPHEW_NIECE` - Nephew / Niece + * * `NON_BENEFICIARY` - Not a Family Member. Can only act as a recipient. + * * `OTHER` - Other + * * `SISTERINLAW_BROTHERINLAW` - Sister-in-law / Brother-in-law + * * `SON_DAUGHTER` - Son / Daughter + * * `WIFE_HUSBAND` - Wife / Husband + * * `FOSTER_CHILD` - Foster child + * * `FREE_UNION` - Free union + */ +export enum RelationshipEnum { + UNKNOWN = 'UNKNOWN', + AUNT_UNCLE = 'AUNT_UNCLE', + BROTHER_SISTER = 'BROTHER_SISTER', + COUSIN = 'COUSIN', + DAUGHTERINLAW_SONINLAW = 'DAUGHTERINLAW_SONINLAW', + GRANDDAUGHER_GRANDSON = 'GRANDDAUGHER_GRANDSON', + GRANDMOTHER_GRANDFATHER = 'GRANDMOTHER_GRANDFATHER', + HEAD = 'HEAD', + MOTHER_FATHER = 'MOTHER_FATHER', + MOTHERINLAW_FATHERINLAW = 'MOTHERINLAW_FATHERINLAW', + NEPHEW_NIECE = 'NEPHEW_NIECE', + NON_BENEFICIARY = 'NON_BENEFICIARY', + OTHER = 'OTHER', + SISTERINLAW_BROTHERINLAW = 'SISTERINLAW_BROTHERINLAW', + SON_DAUGHTER = 'SON_DAUGHTER', + WIFE_HUSBAND = 'WIFE_HUSBAND', + FOSTER_CHILD = 'FOSTER_CHILD', + FREE_UNION = 'FREE_UNION', +} diff --git a/src/frontend/generated/models/ResidenceStatusEnum.ts b/src/frontend/generated/models/ResidenceStatusEnum.ts new file mode 100644 index 0000000000..9d2c6168d2 --- /dev/null +++ b/src/frontend/generated/models/ResidenceStatusEnum.ts @@ -0,0 +1,21 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `IDP` - Displaced | Internally Displaced People + * * `REFUGEE` - Displaced | Refugee / Asylum Seeker + * * `OTHERS_OF_CONCERN` - Displaced | Others of Concern + * * `HOST` - Non-displaced | Host + * * `NON_HOST` - Non-displaced | Non-host + * * `RETURNEE` - Displaced | Returnee + */ +export enum ResidenceStatusEnum { + IDP = 'IDP', + REFUGEE = 'REFUGEE', + OTHERS_OF_CONCERN = 'OTHERS_OF_CONCERN', + HOST = 'HOST', + NON_HOST = 'NON_HOST', + RETURNEE = 'RETURNEE', +} diff --git a/src/frontend/generated/models/ScopeEnum.ts b/src/frontend/generated/models/ScopeEnum.ts new file mode 100644 index 0000000000..82936aed86 --- /dev/null +++ b/src/frontend/generated/models/ScopeEnum.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `FOR_PARTNERS` - For partners + * * `UNICEF` - Unicef + */ +export enum ScopeEnum { + FOR_PARTNERS = 'FOR_PARTNERS', + UNICEF = 'UNICEF', +} diff --git a/src/frontend/generated/models/SectorEnum.ts b/src/frontend/generated/models/SectorEnum.ts new file mode 100644 index 0000000000..d3d3d90ee8 --- /dev/null +++ b/src/frontend/generated/models/SectorEnum.ts @@ -0,0 +1,22 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `CHILD_PROTECTION` - Child Protection + * * `EDUCATION` - Education + * * `HEALTH` - Health + * * `MULTI_PURPOSE` - Multi Purpose + * * `NUTRITION` - Nutrition + * * `SOCIAL_POLICY` - Social Policy + * * `WASH` - WASH + */ +export enum SectorEnum { + CHILD_PROTECTION = 'CHILD_PROTECTION', + EDUCATION = 'EDUCATION', + HEALTH = 'HEALTH', + MULTI_PURPOSE = 'MULTI_PURPOSE', + NUTRITION = 'NUTRITION', + SOCIAL_POLICY = 'SOCIAL_POLICY', + WASH = 'WASH', +} diff --git a/src/frontend/generated/models/SeeingDisabilityEnum.ts b/src/frontend/generated/models/SeeingDisabilityEnum.ts new file mode 100644 index 0000000000..f3e84a286f --- /dev/null +++ b/src/frontend/generated/models/SeeingDisabilityEnum.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `LOT_DIFFICULTY` - A lot of difficulty + * * `CANNOT_DO` - Cannot do at all + * * `SOME_DIFFICULTY` - Some difficulty + */ +export enum SeeingDisabilityEnum { + LOT_DIFFICULTY = 'LOT_DIFFICULTY', + CANNOT_DO = 'CANNOT_DO', + SOME_DIFFICULTY = 'SOME_DIFFICULTY', +} diff --git a/src/frontend/generated/models/SelfcareDisabilityEnum.ts b/src/frontend/generated/models/SelfcareDisabilityEnum.ts new file mode 100644 index 0000000000..a50b79643f --- /dev/null +++ b/src/frontend/generated/models/SelfcareDisabilityEnum.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `` - None + * * `LOT_DIFFICULTY` - A lot of difficulty + * * `CANNOT_DO` - Cannot do at all + * * `SOME_DIFFICULTY` - Some difficulty + */ +export enum SelfcareDisabilityEnum { + LOT_DIFFICULTY = 'LOT_DIFFICULTY', + CANNOT_DO = 'CANNOT_DO', + SOME_DIFFICULTY = 'SOME_DIFFICULTY', +} diff --git a/src/frontend/generated/models/SexEnum.ts b/src/frontend/generated/models/SexEnum.ts new file mode 100644 index 0000000000..bad33e7690 --- /dev/null +++ b/src/frontend/generated/models/SexEnum.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `MALE` - Male + * * `FEMALE` - Female + */ +export enum SexEnum { + MALE = 'MALE', + FEMALE = 'FEMALE', +} diff --git a/src/frontend/generated/models/SubtypeEnum.ts b/src/frontend/generated/models/SubtypeEnum.ts new file mode 100644 index 0000000000..d4f65d58fa --- /dev/null +++ b/src/frontend/generated/models/SubtypeEnum.ts @@ -0,0 +1,16 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `DATE` - Date + * * `DECIMAL` - Number + * * `STRING` - Text + * * `BOOL` - Boolean (true/false) + */ +export enum SubtypeEnum { + DATE = 'DATE', + DECIMAL = 'DECIMAL', + STRING = 'STRING', + BOOL = 'BOOL', +} diff --git a/src/frontend/generated/models/TargetPopulationList.ts b/src/frontend/generated/models/TargetPopulationList.ts new file mode 100644 index 0000000000..15adb8d0a6 --- /dev/null +++ b/src/frontend/generated/models/TargetPopulationList.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type TargetPopulationList = { + readonly id: string; + name: string; + status: string; + created_by?: string; + readonly created_at: string; +}; + diff --git a/src/frontend/generated/models/WorkStatusEnum.ts b/src/frontend/generated/models/WorkStatusEnum.ts new file mode 100644 index 0000000000..8797602a52 --- /dev/null +++ b/src/frontend/generated/models/WorkStatusEnum.ts @@ -0,0 +1,14 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * * `1` - Yes + * * `0` - No + * * `NOT_PROVIDED` - Not provided + */ +export enum WorkStatusEnum { + _1 = '1', + _0 = '0', + NOT_PROVIDED = 'NOT_PROVIDED', +} diff --git a/src/frontend/generated/services/FieldsAttributesService.ts b/src/frontend/generated/services/FieldsAttributesService.ts new file mode 100644 index 0000000000..6028765090 --- /dev/null +++ b/src/frontend/generated/services/FieldsAttributesService.ts @@ -0,0 +1,19 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CancelablePromise } from '../core/CancelablePromise'; +import { OpenAPI } from '../core/OpenAPI'; +import { request as __request } from '../core/request'; +export class FieldsAttributesService { + /** + * @returns any No response body + * @throws ApiError + */ + public static fieldsAttributesRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/fields_attributes/', + }); + } +} diff --git a/src/frontend/generated/services/HhStatusService.ts b/src/frontend/generated/services/HhStatusService.ts new file mode 100644 index 0000000000..738fe417cc --- /dev/null +++ b/src/frontend/generated/services/HhStatusService.ts @@ -0,0 +1,19 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CancelablePromise } from '../core/CancelablePromise'; +import { OpenAPI } from '../core/OpenAPI'; +import { request as __request } from '../core/request'; +export class HhStatusService { + /** + * @returns any No response body + * @throws ApiError + */ + public static hhStatusRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/hh-status', + }); + } +} diff --git a/src/frontend/generated/services/RestService.ts b/src/frontend/generated/services/RestService.ts new file mode 100644 index 0000000000..6bd325d91b --- /dev/null +++ b/src/frontend/generated/services/RestService.ts @@ -0,0 +1,1350 @@ +/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { DelegatePeople } from '../models/DelegatePeople'; +import type { PaginatedAreaList } from '../models/PaginatedAreaList'; +import type { PaginatedAreaListList } from '../models/PaginatedAreaListList'; +import type { PaginatedAreaTypeList } from '../models/PaginatedAreaTypeList'; +import type { PaginatedBeneficiaryGroupList } from '../models/PaginatedBeneficiaryGroupList'; +import type { PaginatedBusinessAreaList } from '../models/PaginatedBusinessAreaList'; +import type { PaginatedCountryList } from '../models/PaginatedCountryList'; +import type { PaginatedPaymentPlanList } from '../models/PaginatedPaymentPlanList'; +import type { PaginatedPeriodicDataUpdateTemplateListList } from '../models/PaginatedPeriodicDataUpdateTemplateListList'; +import type { PaginatedPeriodicDataUpdateUploadListList } from '../models/PaginatedPeriodicDataUpdateUploadListList'; +import type { PaginatedPeriodicFieldList } from '../models/PaginatedPeriodicFieldList'; +import type { PaginatedProgramCycleListList } from '../models/PaginatedProgramCycleListList'; +import type { PaginatedProgramGlobalList } from '../models/PaginatedProgramGlobalList'; +import type { PaginatedRegistrationDataImportListList } from '../models/PaginatedRegistrationDataImportListList'; +import type { PaginatedTargetPopulationListList } from '../models/PaginatedTargetPopulationListList'; +import type { PatchedProgramCycleUpdate } from '../models/PatchedProgramCycleUpdate'; +import type { PatchedRDI } from '../models/PatchedRDI'; +import type { PaymentPlanBulkAction } from '../models/PaymentPlanBulkAction'; +import type { PaymentPlanSupportingDocument } from '../models/PaymentPlanSupportingDocument'; +import type { PeriodicDataUpdateTemplateCreate } from '../models/PeriodicDataUpdateTemplateCreate'; +import type { PeriodicDataUpdateTemplateDetail } from '../models/PeriodicDataUpdateTemplateDetail'; +import type { PeriodicDataUpdateUpload } from '../models/PeriodicDataUpdateUpload'; +import type { PeriodicDataUpdateUploadDetail } from '../models/PeriodicDataUpdateUploadDetail'; +import type { Program } from '../models/Program'; +import type { ProgramCycleCreate } from '../models/ProgramCycleCreate'; +import type { ProgramCycleList } from '../models/ProgramCycleList'; +import type { ProgramCycleUpdate } from '../models/ProgramCycleUpdate'; +import type { PushPeople } from '../models/PushPeople'; +import type { RDI } from '../models/RDI'; +import type { RDINested } from '../models/RDINested'; +import type { RegistrationDataImportList } from '../models/RegistrationDataImportList'; +import type { CancelablePromise } from '../core/CancelablePromise'; +import { OpenAPI } from '../core/OpenAPI'; +import { request as __request } from '../core/request'; +export class RestService { + /** + * OpenApi3 schema for this API. Format can be selected via content negotiation. + * + * - YAML: application/vnd.oai.openapi + * - JSON: application/vnd.oai.openapi+json + * @param format + * @param lang + * @returns any + * @throws ApiError + */ + public static restRetrieve( + format?: 'json' | 'yaml', + lang?: 'af' | 'ar' | 'ar-dz' | 'ast' | 'az' | 'be' | 'bg' | 'bn' | 'br' | 'bs' | 'ca' | 'cs' | 'cy' | 'da' | 'de' | 'dsb' | 'el' | 'en' | 'en-au' | 'en-gb' | 'eo' | 'es' | 'es-ar' | 'es-co' | 'es-mx' | 'es-ni' | 'es-ve' | 'et' | 'eu' | 'fa' | 'fi' | 'fr' | 'fy' | 'ga' | 'gd' | 'gl' | 'he' | 'hi' | 'hr' | 'hsb' | 'hu' | 'hy' | 'ia' | 'id' | 'ig' | 'io' | 'is' | 'it' | 'ja' | 'ka' | 'kab' | 'kk' | 'km' | 'kn' | 'ko' | 'ky' | 'lb' | 'lt' | 'lv' | 'mk' | 'ml' | 'mn' | 'mr' | 'my' | 'nb' | 'ne' | 'nl' | 'nn' | 'os' | 'pa' | 'pl' | 'pt' | 'pt-br' | 'ro' | 'ru' | 'sk' | 'sl' | 'sq' | 'sr' | 'sr-latn' | 'sv' | 'sw' | 'ta' | 'te' | 'tg' | 'th' | 'tk' | 'tr' | 'tt' | 'udm' | 'uk' | 'ur' | 'uz' | 'vi' | 'zh-hans' | 'zh-hant', + ): CancelablePromise> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/', + query: { + 'format': format, + 'lang': lang, + }, + }); + } + /** + * @param businessArea + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @returns PaginatedAreaListList + * @throws ApiError + */ + public static restGeoAreasList( + businessArea: string, + limit?: number, + offset?: number, + ordering?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/geo/areas/', + path: { + 'business_area': businessArea, + }, + query: { + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + }, + }); + } + /** + * @param businessArea + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @param search A search term. + * @returns PaginatedPaymentPlanList + * @throws ApiError + */ + public static restPaymentsPaymentPlansManagerialList( + businessArea: string, + limit?: number, + offset?: number, + ordering?: string, + search?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/payments/payment-plans-managerial/', + path: { + 'business_area': businessArea, + }, + query: { + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + 'search': search, + }, + }); + } + /** + * @param businessArea + * @param requestBody + * @returns PaymentPlanBulkAction + * @throws ApiError + */ + public static restPaymentsPaymentPlansManagerialBulkActionCreate( + businessArea: string, + requestBody: PaymentPlanBulkAction, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/payments/payment-plans-managerial/bulk-action/', + path: { + 'business_area': businessArea, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param ordering Which field to use when ordering the results. + * @param updatedAtAfter + * @param updatedAtBefore + * @returns Program + * @throws ApiError + */ + public static restProgramList( + businessArea: string, + ordering?: string, + updatedAtAfter?: string, + updatedAtBefore?: string, + ): CancelablePromise> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/program/', + path: { + 'business_area': businessArea, + }, + query: { + 'ordering': ordering, + 'updated_at_after': updatedAtAfter, + 'updated_at_before': updatedAtBefore, + }, + }); + } + /** + * @param businessArea + * @param requestBody + * @returns Program + * @throws ApiError + */ + public static restProgramCreateCreate( + businessArea: string, + requestBody: Program, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/program/create/', + path: { + 'business_area': businessArea, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param programId + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @returns PaginatedProgramCycleListList + * @throws ApiError + */ + public static restProgramsCyclesList( + businessArea: string, + programId: string, + limit?: number, + offset?: number, + ordering?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/cycles/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + query: { + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + }, + }); + } + /** + * @param businessArea + * @param programId + * @param requestBody + * @returns ProgramCycleCreate + * @throws ApiError + */ + public static restProgramsCyclesCreate( + businessArea: string, + programId: string, + requestBody: ProgramCycleCreate, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/programs/{program_id}/cycles/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @returns ProgramCycleList + * @throws ApiError + */ + public static restProgramsCyclesRetrieve( + businessArea: string, + id: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/cycles/{id}/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @param requestBody + * @returns ProgramCycleUpdate + * @throws ApiError + */ + public static restProgramsCyclesUpdate( + businessArea: string, + id: string, + programId: string, + requestBody?: ProgramCycleUpdate, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'PUT', + url: '/api/rest/{business_area}/programs/{program_id}/cycles/{id}/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @param requestBody + * @returns ProgramCycleUpdate + * @throws ApiError + */ + public static restProgramsCyclesPartialUpdate( + businessArea: string, + id: string, + programId: string, + requestBody?: PatchedProgramCycleUpdate, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'PATCH', + url: '/api/rest/{business_area}/programs/{program_id}/cycles/{id}/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @returns void + * @throws ApiError + */ + public static restProgramsCyclesDestroy( + businessArea: string, + id: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'DELETE', + url: '/api/rest/{business_area}/programs/{program_id}/cycles/{id}/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @returns any No response body + * @throws ApiError + */ + public static restProgramsCyclesFinishCreate( + businessArea: string, + id: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/programs/{program_id}/cycles/{id}/finish/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @returns any No response body + * @throws ApiError + */ + public static restProgramsCyclesReactivateCreate( + businessArea: string, + id: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/programs/{program_id}/cycles/{id}/reactivate/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param paymentPlanId + * @param programId + * @param requestBody + * @returns PaymentPlanSupportingDocument + * @throws ApiError + */ + public static restProgramsPaymentPlansSupportingDocumentsCreate( + businessArea: string, + paymentPlanId: string, + programId: string, + requestBody: PaymentPlanSupportingDocument, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/programs/{program_id}/payment-plans/{payment_plan_id}/supporting-documents/', + path: { + 'business_area': businessArea, + 'payment_plan_id': paymentPlanId, + 'program_id': programId, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param fileId + * @param paymentPlanId + * @param programId + * @returns void + * @throws ApiError + */ + public static restProgramsPaymentPlansSupportingDocumentsDestroy( + businessArea: string, + fileId: string, + paymentPlanId: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'DELETE', + url: '/api/rest/{business_area}/programs/{program_id}/payment-plans/{payment_plan_id}/supporting-documents/{file_id}/', + path: { + 'business_area': businessArea, + 'file_id': fileId, + 'payment_plan_id': paymentPlanId, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param fileId + * @param paymentPlanId + * @param programId + * @returns PaymentPlanSupportingDocument + * @throws ApiError + */ + public static restProgramsPaymentPlansSupportingDocumentsDownloadRetrieve( + businessArea: string, + fileId: string, + paymentPlanId: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/payment-plans/{payment_plan_id}/supporting-documents/{file_id}/download/', + path: { + 'business_area': businessArea, + 'file_id': fileId, + 'payment_plan_id': paymentPlanId, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param programId + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @returns PaginatedPeriodicDataUpdateTemplateListList + * @throws ApiError + */ + public static restProgramsPeriodicDataUpdatePeriodicDataUpdateTemplatesList( + businessArea: string, + programId: string, + limit?: number, + offset?: number, + ordering?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/periodic-data-update/periodic-data-update-templates/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + query: { + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + }, + }); + } + /** + * @param businessArea + * @param programId + * @param requestBody + * @returns PeriodicDataUpdateTemplateCreate + * @throws ApiError + */ + public static restProgramsPeriodicDataUpdatePeriodicDataUpdateTemplatesCreate( + businessArea: string, + programId: string, + requestBody: PeriodicDataUpdateTemplateCreate, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/programs/{program_id}/periodic-data-update/periodic-data-update-templates/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @returns PeriodicDataUpdateTemplateDetail + * @throws ApiError + */ + public static restProgramsPeriodicDataUpdatePeriodicDataUpdateTemplatesRetrieve( + businessArea: string, + id: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/periodic-data-update/periodic-data-update-templates/{id}/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @returns any No response body + * @throws ApiError + */ + public static restProgramsPeriodicDataUpdatePeriodicDataUpdateTemplatesDownloadRetrieve( + businessArea: string, + id: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/periodic-data-update/periodic-data-update-templates/{id}/download/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @returns any No response body + * @throws ApiError + */ + public static restProgramsPeriodicDataUpdatePeriodicDataUpdateTemplatesExportCreate( + businessArea: string, + id: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/programs/{program_id}/periodic-data-update/periodic-data-update-templates/{id}/export/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param programId + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @returns PaginatedPeriodicDataUpdateUploadListList + * @throws ApiError + */ + public static restProgramsPeriodicDataUpdatePeriodicDataUpdateUploadsList( + businessArea: string, + programId: string, + limit?: number, + offset?: number, + ordering?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/periodic-data-update/periodic-data-update-uploads/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + query: { + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + }, + }); + } + /** + * @param businessArea + * @param id + * @param programId + * @returns PeriodicDataUpdateUploadDetail + * @throws ApiError + */ + public static restProgramsPeriodicDataUpdatePeriodicDataUpdateUploadsRetrieve( + businessArea: string, + id: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/periodic-data-update/periodic-data-update-uploads/{id}/', + path: { + 'business_area': businessArea, + 'id': id, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param programId + * @param requestBody + * @returns PeriodicDataUpdateUpload + * @throws ApiError + */ + public static restProgramsPeriodicDataUpdatePeriodicDataUpdateUploadsUploadCreate( + businessArea: string, + programId: string, + requestBody: PeriodicDataUpdateUpload, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/programs/{program_id}/periodic-data-update/periodic-data-update-uploads/upload/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param programId + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @returns PaginatedPeriodicFieldList + * @throws ApiError + */ + public static restProgramsPeriodicDataUpdatePeriodicFieldsList( + businessArea: string, + programId: string, + limit?: number, + offset?: number, + ordering?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/periodic-data-update/periodic-fields/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + query: { + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + }, + }); + } + /** + * @param businessArea + * @param programId + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @returns PaginatedRegistrationDataImportListList + * @throws ApiError + */ + public static restProgramsRegistrationDataRegistrationDataImportsList( + businessArea: string, + programId: string, + limit?: number, + offset?: number, + ordering?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/registration-data/registration-data-imports/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + query: { + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + }, + }); + } + /** + * @param businessArea + * @param programId + * @param requestBody + * @returns RegistrationDataImportList + * @throws ApiError + */ + public static restProgramsRegistrationDataRegistrationDataImportsRunDeduplicationCreate( + businessArea: string, + programId: string, + requestBody: RegistrationDataImportList, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/programs/{program_id}/registration-data/registration-data-imports/run-deduplication/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param programId + * @returns any No response body + * @throws ApiError + */ + public static restProgramsRegistrationDataWebhookdeduplicationRetrieve( + businessArea: string, + programId: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/registration-data/webhookdeduplication/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param programId + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @returns PaginatedTargetPopulationListList + * @throws ApiError + */ + public static restProgramsTargetingTargetPopulationsList( + businessArea: string, + programId: string, + limit?: number, + offset?: number, + ordering?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/programs/{program_id}/targeting/target-populations/', + path: { + 'business_area': businessArea, + 'program_id': programId, + }, + query: { + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + }, + }); + } + /** + * Api to Create RDI for selected business area + * @param businessArea + * @param rdi + * @param requestBody + * @returns RDI + * @throws ApiError + */ + public static restRdiCompletedCreate( + businessArea: string, + rdi: string, + requestBody: RDI, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/rdi/{rdi}/completed/', + path: { + 'business_area': businessArea, + 'rdi': rdi, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * Api to Create RDI for selected business area + * @param businessArea + * @param rdi + * @param requestBody + * @returns RDI + * @throws ApiError + */ + public static restRdiCompletedUpdate( + businessArea: string, + rdi: string, + requestBody: RDI, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'PUT', + url: '/api/rest/{business_area}/rdi/{rdi}/completed/', + path: { + 'business_area': businessArea, + 'rdi': rdi, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * Api to Create RDI for selected business area + * @param businessArea + * @param rdi + * @param requestBody + * @returns RDI + * @throws ApiError + */ + public static restRdiCompletedPartialUpdate( + businessArea: string, + rdi: string, + requestBody?: PatchedRDI, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'PATCH', + url: '/api/rest/{business_area}/rdi/{rdi}/completed/', + path: { + 'business_area': businessArea, + 'rdi': rdi, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param rdi + * @param requestBody + * @returns any No response body + * @throws ApiError + */ + public static restRdiDelegatePeopleCreate( + businessArea: string, + rdi: string, + requestBody: DelegatePeople, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/rdi/{rdi}/delegate/people/', + path: { + 'business_area': businessArea, + 'rdi': rdi, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * Api to link Households with selected RDI + * @param businessArea + * @param rdi + * @returns any No response body + * @throws ApiError + */ + public static restRdiPushCreate( + businessArea: string, + rdi: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/rdi/{rdi}/push/', + path: { + 'business_area': businessArea, + 'rdi': rdi, + }, + }); + } + /** + * Api to link Households with selected RDI + * @param businessArea + * @param rdi + * @returns any No response body + * @throws ApiError + */ + public static restRdiPushLaxCreate( + businessArea: string, + rdi: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/rdi/{rdi}/push/lax/', + path: { + 'business_area': businessArea, + 'rdi': rdi, + }, + }); + } + /** + * @param businessArea + * @param rdi + * @param requestBody + * @returns any No response body + * @throws ApiError + */ + public static restRdiPushPeopleCreate( + businessArea: string, + rdi: string, + requestBody: PushPeople, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/rdi/{rdi}/push/people/', + path: { + 'business_area': businessArea, + 'rdi': rdi, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * Api to Create RDI for selected business area + * @param businessArea + * @param requestBody + * @returns RDI + * @throws ApiError + */ + public static restRdiCreateCreate( + businessArea: string, + requestBody: RDI, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/rdi/create/', + path: { + 'business_area': businessArea, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param businessArea + * @param requestBody + * @returns any No response body + * @throws ApiError + */ + public static restRdiUploadCreate( + businessArea: string, + requestBody: RDINested, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/rdi/upload/', + path: { + 'business_area': businessArea, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param areaTypeAreaLevel + * @param countryIsoCode2 + * @param countryIsoCode3 + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @param parentId + * @param parentPCode + * @param search A search term. + * @param updatedAtAfter + * @param updatedAtBefore + * @param validFromAfter + * @param validFromBefore + * @param validUntilAfter + * @param validUntilBefore + * @returns PaginatedAreaList + * @throws ApiError + */ + public static restAreasList( + areaTypeAreaLevel?: number, + countryIsoCode2?: string, + countryIsoCode3?: string, + limit?: number, + offset?: number, + ordering?: string, + parentId?: string, + parentPCode?: string, + search?: string, + updatedAtAfter?: string, + updatedAtBefore?: string, + validFromAfter?: string, + validFromBefore?: string, + validUntilAfter?: string, + validUntilBefore?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/areas/', + query: { + 'area_type_area_level': areaTypeAreaLevel, + 'country_iso_code2': countryIsoCode2, + 'country_iso_code3': countryIsoCode3, + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + 'parent_id': parentId, + 'parent_p_code': parentPCode, + 'search': search, + 'updated_at_after': updatedAtAfter, + 'updated_at_before': updatedAtBefore, + 'valid_from_after': validFromAfter, + 'valid_from_before': validFromBefore, + 'valid_until_after': validUntilAfter, + 'valid_until_before': validUntilBefore, + }, + }); + } + /** + * @param areaLevel + * @param countryIsoCode2 + * @param countryIsoCode3 + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @param parentAreaLevel + * @param search A search term. + * @param updatedAtAfter + * @param updatedAtBefore + * @returns PaginatedAreaTypeList + * @throws ApiError + */ + public static restAreatypesList( + areaLevel?: number, + countryIsoCode2?: string, + countryIsoCode3?: string, + limit?: number, + offset?: number, + ordering?: string, + parentAreaLevel?: number, + search?: string, + updatedAtAfter?: string, + updatedAtBefore?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/areatypes/', + query: { + 'area_level': areaLevel, + 'country_iso_code2': countryIsoCode2, + 'country_iso_code3': countryIsoCode3, + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + 'parent_area_level': parentAreaLevel, + 'search': search, + 'updated_at_after': updatedAtAfter, + 'updated_at_before': updatedAtBefore, + }, + }); + } + /** + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @returns PaginatedBeneficiaryGroupList + * @throws ApiError + */ + public static restBeneficiaryGroupsList( + limit?: number, + offset?: number, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/beneficiary-groups/', + query: { + 'limit': limit, + 'offset': offset, + }, + }); + } + /** + * @param active + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @param updatedAtAfter + * @param updatedAtBefore + * @returns PaginatedBusinessAreaList + * @throws ApiError + */ + public static restBusinessAreasList( + active?: boolean, + limit?: number, + offset?: number, + ordering?: string, + updatedAtAfter?: string, + updatedAtBefore?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/business_areas/', + query: { + 'active': active, + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + 'updated_at_after': updatedAtAfter, + 'updated_at_before': updatedAtBefore, + }, + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restConstanceRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/constance/', + }); + } + /** + * Retrieve dashboard data for a given business area from Redis cache. + * If data is not cached or needs updating, refresh it. + * @param businessAreaSlug + * @returns any No response body + * @throws ApiError + */ + public static restDashboardDataRetrieve( + businessAreaSlug: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/dashboard/{business_area_slug}/data/', + path: { + 'business_area_slug': businessAreaSlug, + }, + }); + } + /** + * API to trigger the creation or update of a DashReport for a given business area. + * Restricted to superusers and users with the required permissions. + * @param businessAreaSlug + * @returns any No response body + * @throws ApiError + */ + public static restDashboardGenerateCreate( + businessAreaSlug: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/dashboard/generate/{business_area_slug}/', + path: { + 'business_area_slug': businessAreaSlug, + }, + }); + } + /** + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @param search A search term. + * @param updatedAtAfter + * @param updatedAtBefore + * @param validFromAfter + * @param validFromBefore + * @param validUntilAfter + * @param validUntilBefore + * @returns PaginatedCountryList + * @throws ApiError + */ + public static restLookupsCountryList( + limit?: number, + offset?: number, + ordering?: string, + search?: string, + updatedAtAfter?: string, + updatedAtBefore?: string, + validFromAfter?: string, + validFromBefore?: string, + validUntilAfter?: string, + validUntilBefore?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/country/', + query: { + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + 'search': search, + 'updated_at_after': updatedAtAfter, + 'updated_at_before': updatedAtBefore, + 'valid_from_after': validFromAfter, + 'valid_from_before': validFromBefore, + 'valid_until_after': validUntilAfter, + 'valid_until_before': validUntilBefore, + }, + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsDatacollectingpolicyRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/datacollectingpolicy/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsDocumentRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/document/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsMaritalstatusRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/maritalstatus/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsObserveddisabilityRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/observeddisability/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsProgramStatusesRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/program-statuses/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsRelationshipRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/relationship/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsResidencestatusRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/residencestatus/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsRoleRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/role/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsSexRetrieve(): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/sex/', + }); + } + /** + * @param active + * @param businessArea + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @param ordering Which field to use when ordering the results. + * @param status * `ACTIVE` - Active + * * `DRAFT` - Draft + * * `FINISHED` - Finished + * @param updatedAtAfter + * @param updatedAtBefore + * @returns PaginatedProgramGlobalList + * @throws ApiError + */ + public static restProgramsList( + active?: boolean, + businessArea?: string, + limit?: number, + offset?: number, + ordering?: string, + status?: 'ACTIVE' | 'DRAFT' | 'FINISHED', + updatedAtAfter?: string, + updatedAtBefore?: string, + ): CancelablePromise { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/programs/', + query: { + 'active': active, + 'business_area': businessArea, + 'limit': limit, + 'offset': offset, + 'ordering': ordering, + 'status': status, + 'updated_at_after': updatedAtAfter, + 'updated_at_before': updatedAtBefore, + }, + }); + } +} diff --git a/src/frontend/src/__generated__/graphql.tsx b/src/frontend/src/__generated__/graphql.tsx index 4945b88c06..e29a656a6e 100644 --- a/src/frontend/src/__generated__/graphql.tsx +++ b/src/frontend/src/__generated__/graphql.tsx @@ -415,6 +415,30 @@ export type BankTransferObjectType = { type: Scalars['String']['input']; }; +export type BeneficiaryGroupNode = Node & { + __typename?: 'BeneficiaryGroupNode'; + createdAt: Scalars['DateTime']['output']; + groupLabel: Scalars['String']['output']; + groupLabelPlural: Scalars['String']['output']; + id: Scalars['ID']['output']; + masterDetail: Scalars['Boolean']['output']; + memberLabel: Scalars['String']['output']; + memberLabelPlural: Scalars['String']['output']; + name: Scalars['String']['output']; + programs: ProgramNodeConnection; + updatedAt: Scalars['DateTime']['output']; +}; + + +export type BeneficiaryGroupNodeProgramsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + name?: InputMaybe; + offset?: InputMaybe; +}; + export type BulkGrievanceAddNoteMutation = { __typename?: 'BulkGrievanceAddNoteMutation'; grievanceTickets?: Maybe>>; @@ -1098,6 +1122,7 @@ export type CreateProgram = { export type CreateProgramInput = { administrativeAreasOfImplementation?: InputMaybe; + beneficiaryGroup?: InputMaybe; budget?: InputMaybe; businessAreaSlug?: InputMaybe; cashPlus?: InputMaybe; @@ -4837,6 +4862,7 @@ export type ProgramNode = Node & { adminAreas: AreaNodeConnection; adminUrl?: Maybe; administrativeAreasOfImplementation: Scalars['String']['output']; + beneficiaryGroup?: Maybe; biometricDeduplicationEnabled: Scalars['Boolean']['output']; budget?: Maybe; businessArea: UserBusinessAreaNode; @@ -5317,6 +5343,7 @@ export type QueryAllAccountabilityCommunicationMessagesArgs = { export type QueryAllActiveProgramsArgs = { after?: InputMaybe; before?: InputMaybe; + beneficiaryGroupMatch?: InputMaybe; budget?: InputMaybe; businessArea: Scalars['String']['input']; compatibleDct?: InputMaybe; @@ -5745,6 +5772,7 @@ export type QueryAllPduFieldsArgs = { export type QueryAllProgramsArgs = { after?: InputMaybe; before?: InputMaybe; + beneficiaryGroupMatch?: InputMaybe; budget?: InputMaybe; businessArea: Scalars['String']['input']; compatibleDct?: InputMaybe; @@ -7953,6 +7981,7 @@ export type UpdateProgram = { export type UpdateProgramInput = { administrativeAreasOfImplementation?: InputMaybe; + beneficiaryGroup?: InputMaybe; budget?: InputMaybe; cashPlus?: InputMaybe; dataCollectingTypeCode?: InputMaybe; @@ -8644,7 +8673,7 @@ export type IndividualDetailedFragment = { __typename?: 'IndividualNode', givenN export type MergedIndividualMinimalFragment = { __typename?: 'IndividualNode', id: string, unicefId?: string | null, age?: number | null, fullName: string, birthDate: any, sex: IndividualSex, role?: string | null, relationship?: IndividualRelationship | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, importId?: string | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, fullName?: string | null, score?: number | null, proximityToScore?: number | null, age?: number | null, location?: string | null } | null> | null, deduplicationBatchResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, fullName?: string | null, score?: number | null, proximityToScore?: number | null, age?: number | null, location?: string | null } | null> | null, registrationDataImport: { __typename?: 'RegistrationDataImportNode', id: string, name: string } }; -export type ProgramDetailsFragment = { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate?: any | null, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, targetPopulationsCount?: number | null, canFinish?: boolean | null, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string, type?: DataCollectingTypeType | null } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null, registrationImports: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null }, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string, label: any, pduData?: { __typename?: 'PeriodicFieldDataNode', id: string, subtype: PeriodicFieldDataSubtype, numberOfRounds: number, roundsNames: Array } | null } | null> | null }; +export type ProgramDetailsFragment = { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate?: any | null, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, targetPopulationsCount?: number | null, canFinish?: boolean | null, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string, type?: DataCollectingTypeType | null } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null, registrationImports: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null }, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string, label: any, pduData?: { __typename?: 'PeriodicFieldDataNode', id: string, subtype: PeriodicFieldDataSubtype, numberOfRounds: number, roundsNames: Array } | null } | null> | null, beneficiaryGroup?: { __typename?: 'BeneficiaryGroupNode', id: string, name: string, groupLabel: string, groupLabelPlural: string, memberLabel: string, memberLabelPlural: string, masterDetail: boolean } | null }; export type RegistrationMinimalFragment = { __typename?: 'RegistrationDataImportNode', id: string, createdAt: any, name: string, status: RegistrationDataImportStatus, erased: boolean, importDate: any, dataSource: RegistrationDataImportDataSource, numberOfHouseholds: number, numberOfIndividuals: number, refuseReason?: string | null, totalHouseholdsCountWithValidPhoneNo?: number | null, adminUrl?: string | null, biometricDeduplicated?: string | null, importedBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, program?: { __typename?: 'ProgramNode', id: string, name: string, startDate: any, endDate?: any | null, status: ProgramStatus } | null }; @@ -9101,7 +9130,7 @@ export type UpdateProgramMutationVariables = Exact<{ }>; -export type UpdateProgramMutation = { __typename?: 'Mutations', updateProgram?: { __typename?: 'UpdateProgram', validationErrors?: any | null, program?: { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate?: any | null, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, targetPopulationsCount?: number | null, canFinish?: boolean | null, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string, type?: DataCollectingTypeType | null } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null, registrationImports: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null }, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string, label: any, pduData?: { __typename?: 'PeriodicFieldDataNode', id: string, subtype: PeriodicFieldDataSubtype, numberOfRounds: number, roundsNames: Array } | null } | null> | null } | null } | null }; +export type UpdateProgramMutation = { __typename?: 'Mutations', updateProgram?: { __typename?: 'UpdateProgram', validationErrors?: any | null, program?: { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate?: any | null, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, targetPopulationsCount?: number | null, canFinish?: boolean | null, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string, type?: DataCollectingTypeType | null } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null, registrationImports: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null }, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string, label: any, pduData?: { __typename?: 'PeriodicFieldDataNode', id: string, subtype: PeriodicFieldDataSubtype, numberOfRounds: number, roundsNames: Array } | null } | null> | null, beneficiaryGroup?: { __typename?: 'BeneficiaryGroupNode', id: string, name: string, groupLabel: string, groupLabelPlural: string, memberLabel: string, memberLabelPlural: string, masterDetail: boolean } | null } | null } | null }; export type UpdateProgramPartnersMutationVariables = Exact<{ programData?: InputMaybe; @@ -9109,7 +9138,7 @@ export type UpdateProgramPartnersMutationVariables = Exact<{ }>; -export type UpdateProgramPartnersMutation = { __typename?: 'Mutations', updateProgramPartners?: { __typename?: 'UpdateProgramPartners', program?: { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate?: any | null, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, targetPopulationsCount?: number | null, canFinish?: boolean | null, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string, type?: DataCollectingTypeType | null } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null, registrationImports: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null }, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string, label: any, pduData?: { __typename?: 'PeriodicFieldDataNode', id: string, subtype: PeriodicFieldDataSubtype, numberOfRounds: number, roundsNames: Array } | null } | null> | null } | null } | null }; +export type UpdateProgramPartnersMutation = { __typename?: 'Mutations', updateProgramPartners?: { __typename?: 'UpdateProgramPartners', program?: { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate?: any | null, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, targetPopulationsCount?: number | null, canFinish?: boolean | null, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string, type?: DataCollectingTypeType | null } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null, registrationImports: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null }, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string, label: any, pduData?: { __typename?: 'PeriodicFieldDataNode', id: string, subtype: PeriodicFieldDataSubtype, numberOfRounds: number, roundsNames: Array } | null } | null> | null, beneficiaryGroup?: { __typename?: 'BeneficiaryGroupNode', id: string, name: string, groupLabel: string, groupLabelPlural: string, memberLabel: string, memberLabelPlural: string, masterDetail: boolean } | null } | null } | null }; export type CreateRegistrationKoboImportMutationVariables = Exact<{ registrationDataImportData: RegistrationKoboImportMutationInput; @@ -10029,10 +10058,11 @@ export type AllProgramsForChoicesQueryVariables = Exact<{ orderBy?: InputMaybe; name?: InputMaybe; compatibleDct?: InputMaybe; + beneficiaryGroupMatch?: InputMaybe; }>; -export type AllProgramsForChoicesQuery = { __typename?: 'Query', allPrograms?: { __typename?: 'ProgramNodeConnection', totalCount?: number | null, edgeCount?: number | null, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, endCursor?: string | null, startCursor?: string | null }, edges: Array<{ __typename?: 'ProgramNodeEdge', cursor: string, node?: { __typename?: 'ProgramNode', id: string, name: string, status: ProgramStatus, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, type?: DataCollectingTypeType | null, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string } | null, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string } | null> | null } | null } | null> } | null }; +export type AllProgramsForChoicesQuery = { __typename?: 'Query', allPrograms?: { __typename?: 'ProgramNodeConnection', totalCount?: number | null, edgeCount?: number | null, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, endCursor?: string | null, startCursor?: string | null }, edges: Array<{ __typename?: 'ProgramNodeEdge', cursor: string, node?: { __typename?: 'ProgramNode', id: string, name: string, status: ProgramStatus, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, type?: DataCollectingTypeType | null, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string } | null, beneficiaryGroup?: { __typename?: 'BeneficiaryGroupNode', id: string, createdAt: any, updatedAt: any, name: string, groupLabel: string, groupLabelPlural: string, memberLabel: string, memberLabelPlural: string, masterDetail: boolean } | null, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string } | null> | null } | null } | null> } | null }; export type AllProgramsForTableQueryVariables = Exact<{ before?: InputMaybe; @@ -10064,7 +10094,7 @@ export type ProgramQueryVariables = Exact<{ }>; -export type ProgramQuery = { __typename?: 'Query', program?: { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate?: any | null, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, targetPopulationsCount?: number | null, canFinish?: boolean | null, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string, type?: DataCollectingTypeType | null } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null, registrationImports: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null }, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string, label: any, pduData?: { __typename?: 'PeriodicFieldDataNode', id: string, subtype: PeriodicFieldDataSubtype, numberOfRounds: number, roundsNames: Array } | null } | null> | null } | null }; +export type ProgramQuery = { __typename?: 'Query', program?: { __typename?: 'ProgramNode', id: string, name: string, programmeCode?: string | null, startDate: any, endDate?: any | null, status: ProgramStatus, caId?: string | null, caHashId?: string | null, description: string, budget?: any | null, frequencyOfPayments: ProgramFrequencyOfPayments, cashPlus: boolean, populationGoal: number, scope?: ProgramScope | null, sector: ProgramSector, totalNumberOfHouseholds?: number | null, totalNumberOfHouseholdsWithTpInProgram?: number | null, administrativeAreasOfImplementation: string, isSocialWorkerProgram?: boolean | null, version: any, adminUrl?: string | null, partnerAccess: ProgramPartnerAccess, targetPopulationsCount?: number | null, canFinish?: boolean | null, dataCollectingType?: { __typename?: 'DataCollectingTypeNode', id: string, code: string, label: string, active: boolean, individualFiltersAvailable: boolean, householdFiltersAvailable: boolean, description: string, type?: DataCollectingTypeType | null } | null, partners?: Array<{ __typename?: 'PartnerNode', id: string, name?: string | null, areaAccess?: string | null, areas?: Array<{ __typename?: 'AreaNode', id: string, level: number } | null> | null } | null> | null, registrationImports: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null }, pduFields?: Array<{ __typename?: 'PeriodicFieldNode', id: string, label: any, pduData?: { __typename?: 'PeriodicFieldDataNode', id: string, subtype: PeriodicFieldDataSubtype, numberOfRounds: number, roundsNames: Array } | null } | null> | null, beneficiaryGroup?: { __typename?: 'BeneficiaryGroupNode', id: string, name: string, groupLabel: string, groupLabelPlural: string, memberLabel: string, memberLabelPlural: string, masterDetail: boolean } | null } | null }; export type ProgrammeChoiceDataQueryVariables = Exact<{ [key: string]: never; }>; @@ -11293,6 +11323,15 @@ export const ProgramDetailsFragmentDoc = gql` } } canFinish + beneficiaryGroup { + id + name + groupLabel + groupLabelPlural + memberLabel + memberLabelPlural + masterDetail + } } `; export const RegistrationMinimalFragmentDoc = gql` @@ -20030,7 +20069,7 @@ export type AllProgramsLazyQueryHookResult = ReturnType; export type AllProgramsQueryResult = Apollo.QueryResult; export const AllProgramsForChoicesDocument = gql` - query AllProgramsForChoices($before: String, $after: String, $first: Int, $last: Int, $status: [String], $sector: [String], $businessArea: String!, $search: String, $numberOfHouseholds: String, $budget: String, $startDate: Date, $endDate: Date, $orderBy: String, $name: String, $compatibleDct: Boolean) { + query AllProgramsForChoices($before: String, $after: String, $first: Int, $last: Int, $status: [String], $sector: [String], $businessArea: String!, $search: String, $numberOfHouseholds: String, $budget: String, $startDate: Date, $endDate: Date, $orderBy: String, $name: String, $compatibleDct: Boolean, $beneficiaryGroupMatch: Boolean) { allPrograms( before: $before after: $after @@ -20047,6 +20086,7 @@ export const AllProgramsForChoicesDocument = gql` endDate: $endDate name: $name compatibleDct: $compatibleDct + beneficiaryGroupMatch: $beneficiaryGroupMatch ) { pageInfo { hasNextPage @@ -20072,6 +20112,17 @@ export const AllProgramsForChoicesDocument = gql` householdFiltersAvailable description } + beneficiaryGroup { + id + createdAt + updatedAt + name + groupLabel + groupLabelPlural + memberLabel + memberLabelPlural + masterDetail + } pduFields { id } @@ -20108,6 +20159,7 @@ export const AllProgramsForChoicesDocument = gql` * orderBy: // value for 'orderBy' * name: // value for 'name' * compatibleDct: // value for 'compatibleDct' + * beneficiaryGroupMatch: // value for 'beneficiaryGroupMatch' * }, * }); */ @@ -22305,7 +22357,7 @@ export type DirectiveResolverFn> = { - Node: ( ApprovalProcessNode ) | ( AreaNode ) | ( AreaTypeNode ) | ( BankAccountInfoNode ) | ( BusinessAreaNode ) | ( CommunicationMessageNode ) | ( CommunicationMessageRecipientMapNode ) | ( DataCollectingTypeNode ) | ( DeliveryMechanismDataNode ) | ( DeliveryMechanismNode ) | ( DeliveryMechanismPerPaymentPlanNode ) | ( DocumentNode ) | ( FeedbackMessageNode ) | ( FeedbackNode ) | ( FinancialServiceProviderNode ) | ( FinancialServiceProviderXlsxTemplateNode ) | ( GrievanceDocumentNode ) | ( GrievanceTicketNode ) | ( HouseholdNode ) | ( ImportDataNode ) | ( IndividualIdentityNode ) | ( IndividualNode ) | ( KoboImportDataNode ) | ( LogEntryNode ) | ( PaymentHouseholdSnapshotNode ) | ( PaymentNode ) | ( PaymentPlanNode ) | ( PaymentPlanSupportingDocumentNode ) | ( PaymentVerificationLogEntryNode ) | ( PaymentVerificationNode ) | ( PaymentVerificationPlanNode ) | ( PaymentVerificationSummaryNode ) | ( PeriodicFieldNode ) | ( ProgramCycleNode ) | ( ProgramNode ) | ( RecipientNode ) | ( RegistrationDataImportDatahubNode ) | ( RegistrationDataImportNode ) | ( ReportNode ) | ( RuleCommitNode ) | ( SanctionListIndividualAliasNameNode ) | ( SanctionListIndividualCountriesNode ) | ( SanctionListIndividualDateOfBirthNode ) | ( SanctionListIndividualDocumentNode ) | ( SanctionListIndividualNationalitiesNode ) | ( SanctionListIndividualNode ) | ( SteficonRuleNode ) | ( SurveyNode ) | ( TargetPopulationNode ) | ( TicketAddIndividualDetailsNode ) | ( TicketComplaintDetailsNode ) | ( TicketDeleteHouseholdDetailsNode ) | ( TicketDeleteIndividualDetailsNode ) | ( TicketHouseholdDataUpdateDetailsNode ) | ( TicketIndividualDataUpdateDetailsNode ) | ( TicketNeedsAdjudicationDetailsNode ) | ( TicketNegativeFeedbackDetailsNode ) | ( TicketNoteNode ) | ( TicketPaymentVerificationDetailsNode ) | ( TicketPositiveFeedbackDetailsNode ) | ( TicketReferralDetailsNode ) | ( TicketSensitiveDetailsNode ) | ( TicketSystemFlaggingDetailsNode ) | ( UserBusinessAreaNode ) | ( UserNode ) | ( VolumeByDeliveryMechanismNode ); + Node: ( ApprovalProcessNode ) | ( AreaNode ) | ( AreaTypeNode ) | ( BankAccountInfoNode ) | ( BeneficiaryGroupNode ) | ( BusinessAreaNode ) | ( CommunicationMessageNode ) | ( CommunicationMessageRecipientMapNode ) | ( DataCollectingTypeNode ) | ( DeliveryMechanismDataNode ) | ( DeliveryMechanismNode ) | ( DeliveryMechanismPerPaymentPlanNode ) | ( DocumentNode ) | ( FeedbackMessageNode ) | ( FeedbackNode ) | ( FinancialServiceProviderNode ) | ( FinancialServiceProviderXlsxTemplateNode ) | ( GrievanceDocumentNode ) | ( GrievanceTicketNode ) | ( HouseholdNode ) | ( ImportDataNode ) | ( IndividualIdentityNode ) | ( IndividualNode ) | ( KoboImportDataNode ) | ( LogEntryNode ) | ( PaymentHouseholdSnapshotNode ) | ( PaymentNode ) | ( PaymentPlanNode ) | ( PaymentPlanSupportingDocumentNode ) | ( PaymentVerificationLogEntryNode ) | ( PaymentVerificationNode ) | ( PaymentVerificationPlanNode ) | ( PaymentVerificationSummaryNode ) | ( PeriodicFieldNode ) | ( ProgramCycleNode ) | ( ProgramNode ) | ( RecipientNode ) | ( RegistrationDataImportDatahubNode ) | ( RegistrationDataImportNode ) | ( ReportNode ) | ( RuleCommitNode ) | ( SanctionListIndividualAliasNameNode ) | ( SanctionListIndividualCountriesNode ) | ( SanctionListIndividualDateOfBirthNode ) | ( SanctionListIndividualDocumentNode ) | ( SanctionListIndividualNationalitiesNode ) | ( SanctionListIndividualNode ) | ( SteficonRuleNode ) | ( SurveyNode ) | ( TargetPopulationNode ) | ( TicketAddIndividualDetailsNode ) | ( TicketComplaintDetailsNode ) | ( TicketDeleteHouseholdDetailsNode ) | ( TicketDeleteIndividualDetailsNode ) | ( TicketHouseholdDataUpdateDetailsNode ) | ( TicketIndividualDataUpdateDetailsNode ) | ( TicketNeedsAdjudicationDetailsNode ) | ( TicketNegativeFeedbackDetailsNode ) | ( TicketNoteNode ) | ( TicketPaymentVerificationDetailsNode ) | ( TicketPositiveFeedbackDetailsNode ) | ( TicketReferralDetailsNode ) | ( TicketSensitiveDetailsNode ) | ( TicketSystemFlaggingDetailsNode ) | ( UserBusinessAreaNode ) | ( UserNode ) | ( VolumeByDeliveryMechanismNode ); }; /** Mapping between all available schema types and the resolvers types */ @@ -22343,6 +22395,7 @@ export type ResolversTypes = { BankAccountInfoNodeEdge: ResolverTypeWrapper; BankAccountInfoRdiMergeStatus: BankAccountInfoRdiMergeStatus; BankTransferObjectType: BankTransferObjectType; + BeneficiaryGroupNode: ResolverTypeWrapper; BigInt: ResolverTypeWrapper; Boolean: ResolverTypeWrapper; BulkGrievanceAddNoteMutation: ResolverTypeWrapper; @@ -22856,6 +22909,7 @@ export type ResolversParentTypes = { BankAccountInfoNodeConnection: BankAccountInfoNodeConnection; BankAccountInfoNodeEdge: BankAccountInfoNodeEdge; BankTransferObjectType: BankTransferObjectType; + BeneficiaryGroupNode: BeneficiaryGroupNode; BigInt: Scalars['BigInt']['output']; Boolean: Scalars['Boolean']['output']; BulkGrievanceAddNoteMutation: BulkGrievanceAddNoteMutation; @@ -23460,6 +23514,20 @@ export type BankAccountInfoNodeEdgeResolvers; }; +export type BeneficiaryGroupNodeResolvers = { + createdAt?: Resolver; + groupLabel?: Resolver; + groupLabelPlural?: Resolver; + id?: Resolver; + masterDetail?: Resolver; + memberLabel?: Resolver; + memberLabelPlural?: Resolver; + name?: Resolver; + programs?: Resolver>; + updatedAt?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export interface BigIntScalarConfig extends GraphQLScalarTypeConfig { name: 'BigInt'; } @@ -25092,7 +25160,7 @@ export type NeedsAdjudicationApproveMutationResolvers = { - __resolveType: TypeResolveFn<'ApprovalProcessNode' | 'AreaNode' | 'AreaTypeNode' | 'BankAccountInfoNode' | 'BusinessAreaNode' | 'CommunicationMessageNode' | 'CommunicationMessageRecipientMapNode' | 'DataCollectingTypeNode' | 'DeliveryMechanismDataNode' | 'DeliveryMechanismNode' | 'DeliveryMechanismPerPaymentPlanNode' | 'DocumentNode' | 'FeedbackMessageNode' | 'FeedbackNode' | 'FinancialServiceProviderNode' | 'FinancialServiceProviderXlsxTemplateNode' | 'GrievanceDocumentNode' | 'GrievanceTicketNode' | 'HouseholdNode' | 'ImportDataNode' | 'IndividualIdentityNode' | 'IndividualNode' | 'KoboImportDataNode' | 'LogEntryNode' | 'PaymentHouseholdSnapshotNode' | 'PaymentNode' | 'PaymentPlanNode' | 'PaymentPlanSupportingDocumentNode' | 'PaymentVerificationLogEntryNode' | 'PaymentVerificationNode' | 'PaymentVerificationPlanNode' | 'PaymentVerificationSummaryNode' | 'PeriodicFieldNode' | 'ProgramCycleNode' | 'ProgramNode' | 'RecipientNode' | 'RegistrationDataImportDatahubNode' | 'RegistrationDataImportNode' | 'ReportNode' | 'RuleCommitNode' | 'SanctionListIndividualAliasNameNode' | 'SanctionListIndividualCountriesNode' | 'SanctionListIndividualDateOfBirthNode' | 'SanctionListIndividualDocumentNode' | 'SanctionListIndividualNationalitiesNode' | 'SanctionListIndividualNode' | 'SteficonRuleNode' | 'SurveyNode' | 'TargetPopulationNode' | 'TicketAddIndividualDetailsNode' | 'TicketComplaintDetailsNode' | 'TicketDeleteHouseholdDetailsNode' | 'TicketDeleteIndividualDetailsNode' | 'TicketHouseholdDataUpdateDetailsNode' | 'TicketIndividualDataUpdateDetailsNode' | 'TicketNeedsAdjudicationDetailsNode' | 'TicketNegativeFeedbackDetailsNode' | 'TicketNoteNode' | 'TicketPaymentVerificationDetailsNode' | 'TicketPositiveFeedbackDetailsNode' | 'TicketReferralDetailsNode' | 'TicketSensitiveDetailsNode' | 'TicketSystemFlaggingDetailsNode' | 'UserBusinessAreaNode' | 'UserNode' | 'VolumeByDeliveryMechanismNode', ParentType, ContextType>; + __resolveType: TypeResolveFn<'ApprovalProcessNode' | 'AreaNode' | 'AreaTypeNode' | 'BankAccountInfoNode' | 'BeneficiaryGroupNode' | 'BusinessAreaNode' | 'CommunicationMessageNode' | 'CommunicationMessageRecipientMapNode' | 'DataCollectingTypeNode' | 'DeliveryMechanismDataNode' | 'DeliveryMechanismNode' | 'DeliveryMechanismPerPaymentPlanNode' | 'DocumentNode' | 'FeedbackMessageNode' | 'FeedbackNode' | 'FinancialServiceProviderNode' | 'FinancialServiceProviderXlsxTemplateNode' | 'GrievanceDocumentNode' | 'GrievanceTicketNode' | 'HouseholdNode' | 'ImportDataNode' | 'IndividualIdentityNode' | 'IndividualNode' | 'KoboImportDataNode' | 'LogEntryNode' | 'PaymentHouseholdSnapshotNode' | 'PaymentNode' | 'PaymentPlanNode' | 'PaymentPlanSupportingDocumentNode' | 'PaymentVerificationLogEntryNode' | 'PaymentVerificationNode' | 'PaymentVerificationPlanNode' | 'PaymentVerificationSummaryNode' | 'PeriodicFieldNode' | 'ProgramCycleNode' | 'ProgramNode' | 'RecipientNode' | 'RegistrationDataImportDatahubNode' | 'RegistrationDataImportNode' | 'ReportNode' | 'RuleCommitNode' | 'SanctionListIndividualAliasNameNode' | 'SanctionListIndividualCountriesNode' | 'SanctionListIndividualDateOfBirthNode' | 'SanctionListIndividualDocumentNode' | 'SanctionListIndividualNationalitiesNode' | 'SanctionListIndividualNode' | 'SteficonRuleNode' | 'SurveyNode' | 'TargetPopulationNode' | 'TicketAddIndividualDetailsNode' | 'TicketComplaintDetailsNode' | 'TicketDeleteHouseholdDetailsNode' | 'TicketDeleteIndividualDetailsNode' | 'TicketHouseholdDataUpdateDetailsNode' | 'TicketIndividualDataUpdateDetailsNode' | 'TicketNeedsAdjudicationDetailsNode' | 'TicketNegativeFeedbackDetailsNode' | 'TicketNoteNode' | 'TicketPaymentVerificationDetailsNode' | 'TicketPositiveFeedbackDetailsNode' | 'TicketReferralDetailsNode' | 'TicketSensitiveDetailsNode' | 'TicketSystemFlaggingDetailsNode' | 'UserBusinessAreaNode' | 'UserNode' | 'VolumeByDeliveryMechanismNode', ParentType, ContextType>; id?: Resolver; }; @@ -25618,6 +25686,7 @@ export type ProgramNodeResolvers>; adminUrl?: Resolver, ParentType, ContextType>; administrativeAreasOfImplementation?: Resolver; + beneficiaryGroup?: Resolver, ParentType, ContextType>; biometricDeduplicationEnabled?: Resolver; budget?: Resolver, ParentType, ContextType>; businessArea?: Resolver; @@ -27203,6 +27272,7 @@ export type Resolvers = { BankAccountInfoNode?: BankAccountInfoNodeResolvers; BankAccountInfoNodeConnection?: BankAccountInfoNodeConnectionResolvers; BankAccountInfoNodeEdge?: BankAccountInfoNodeEdgeResolvers; + BeneficiaryGroupNode?: BeneficiaryGroupNodeResolvers; BigInt?: GraphQLScalarType; BulkGrievanceAddNoteMutation?: BulkGrievanceAddNoteMutationResolvers; BulkUpdateGrievanceTicketsAssigneesMutation?: BulkUpdateGrievanceTicketsAssigneesMutationResolvers; diff --git a/src/frontend/src/__generated__/introspection-result.ts b/src/frontend/src/__generated__/introspection-result.ts index 91a7ea27ce..65db8c37d4 100644 --- a/src/frontend/src/__generated__/introspection-result.ts +++ b/src/frontend/src/__generated__/introspection-result.ts @@ -11,6 +11,7 @@ "AreaNode", "AreaTypeNode", "BankAccountInfoNode", + "BeneficiaryGroupNode", "BusinessAreaNode", "CommunicationMessageNode", "CommunicationMessageRecipientMapNode", diff --git a/src/frontend/src/api/programsApi.ts b/src/frontend/src/api/programsApi.ts new file mode 100644 index 0000000000..9d269d4e4e --- /dev/null +++ b/src/frontend/src/api/programsApi.ts @@ -0,0 +1,24 @@ +import { api } from './api'; + +interface BeneficiaryGroup { + id: string; + name: string; + group_label: string; + group_label_plural: string; + member_label: string; + member_label_plural: string; + master_detail: boolean; +} + +export interface PaginatedListResponse { + count: number; + next?: string; + previous?: string; + results: T[]; +} + +export const fetchBeneficiaryGroups = async (): Promise< + PaginatedListResponse +> => { + return api.get('beneficiary-groups/'); +}; diff --git a/src/frontend/src/apollo/fragments/ProgramDetailsFragment.ts b/src/frontend/src/apollo/fragments/ProgramDetailsFragment.ts index 3e2022f136..d105b64d3b 100644 --- a/src/frontend/src/apollo/fragments/ProgramDetailsFragment.ts +++ b/src/frontend/src/apollo/fragments/ProgramDetailsFragment.ts @@ -58,5 +58,14 @@ export const programDetails = gql` } } canFinish + beneficiaryGroup { + id + name + groupLabel + groupLabelPlural + memberLabel + memberLabelPlural + masterDetail + } } `; diff --git a/src/frontend/src/apollo/queries/program/AllProgramsForChoices.ts b/src/frontend/src/apollo/queries/program/AllProgramsForChoices.ts index e1e2f25d3b..eed20aa26d 100644 --- a/src/frontend/src/apollo/queries/program/AllProgramsForChoices.ts +++ b/src/frontend/src/apollo/queries/program/AllProgramsForChoices.ts @@ -17,6 +17,7 @@ export const AllProgramsForChoices = gql` $orderBy: String $name: String $compatibleDct: Boolean + $beneficiaryGroupMatch: Boolean ) { allPrograms( before: $before @@ -34,6 +35,7 @@ export const AllProgramsForChoices = gql` endDate: $endDate name: $name compatibleDct: $compatibleDct + beneficiaryGroupMatch: $beneficiaryGroupMatch ) { pageInfo { hasNextPage @@ -59,6 +61,17 @@ export const AllProgramsForChoices = gql` householdFiltersAvailable description } + beneficiaryGroup { + id + createdAt + updatedAt + name + groupLabel + groupLabelPlural + memberLabel + memberLabelPlural + masterDetail + } pduFields { id } diff --git a/src/frontend/src/components/accountability/Communication/LookUpsCommunication/LookUpHouseholdFiltersCommunication.tsx b/src/frontend/src/components/accountability/Communication/LookUpsCommunication/LookUpHouseholdFiltersCommunication.tsx index a5e740b0be..3a13355704 100644 --- a/src/frontend/src/components/accountability/Communication/LookUpsCommunication/LookUpHouseholdFiltersCommunication.tsx +++ b/src/frontend/src/components/accountability/Communication/LookUpsCommunication/LookUpHouseholdFiltersCommunication.tsx @@ -5,12 +5,13 @@ import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router-dom'; import { HouseholdChoiceDataQuery } from '@generated/graphql'; import { AdminAreaAutocomplete } from '@shared/autocompletes/AdminAreaAutocomplete'; -import { householdTableOrderOptions } from '@utils/constants'; +import { generateTableOrderOptionsGroup } from '@utils/constants'; import { createHandleApplyFilterChange } from '@utils/utils'; import { FiltersSection } from '@core/FiltersSection'; import { NumberTextField } from '@core/NumberTextField'; import { SearchTextField } from '@core/SearchTextField'; import { SelectFilter } from '@core/SelectFilter'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; interface LookUpHouseholdFiltersCommunicationProps { @@ -33,6 +34,8 @@ export function LookUpHouseholdFiltersCommunication({ const { t } = useTranslation(); const navigate = useNavigate(); const location = useLocation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { handleFilterChange, applyFilterChanges, clearFilter } = createHandleApplyFilterChange( @@ -53,6 +56,9 @@ export function LookUpHouseholdFiltersCommunication({ clearFilter(); }; + const householdTableOrderOptions = + generateTableOrderOptionsGroup(beneficiaryGroup); + return ( } diff --git a/src/frontend/src/components/accountability/Communication/LookUpsCommunication/LookUpSelectionCommunication.tsx b/src/frontend/src/components/accountability/Communication/LookUpsCommunication/LookUpSelectionCommunication.tsx index 79ff8758a6..55b17770e4 100644 --- a/src/frontend/src/components/accountability/Communication/LookUpsCommunication/LookUpSelectionCommunication.tsx +++ b/src/frontend/src/components/accountability/Communication/LookUpsCommunication/LookUpSelectionCommunication.tsx @@ -13,8 +13,7 @@ import { LookUpHouseholdFiltersCommunication } from './LookUpHouseholdFiltersCom import { LookUpRegistrationFiltersCommunication } from './LookUpRegistrationFiltersCommunication'; import { LookUpSelectionTablesCommunication } from './LookUpSelectionTablesCommunication'; import { LookUpTargetPopulationFiltersCommunication } from './LookUpTargetPopulationFiltersCommunication'; - -const communicationTabs = ['Household', 'Target Population', 'RDI']; +import { useProgramContext } from 'src/programContext'; const BoxWithBorderBottom = styled(Box)` border-bottom: 1px solid ${({ theme }) => theme.hctPalette.lighterGray}; @@ -37,6 +36,13 @@ export function LookUpSelectionCommunication({ setSelectedTab; }): ReactElement { const location = useLocation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const communicationTabs = [ + `${beneficiaryGroup?.groupLabel}`, + 'Target Population', + 'RDI', + ]; const initialFilterRDI = { search: '', diff --git a/src/frontend/src/components/accountability/Feedback/FeedbackDetails/FeedbackDetails.tsx b/src/frontend/src/components/accountability/Feedback/FeedbackDetails/FeedbackDetails.tsx index 1c65518def..d4a7c27c91 100644 --- a/src/frontend/src/components/accountability/Feedback/FeedbackDetails/FeedbackDetails.tsx +++ b/src/frontend/src/components/accountability/Feedback/FeedbackDetails/FeedbackDetails.tsx @@ -9,6 +9,7 @@ import { OverviewContainer } from '@core/OverviewContainer'; import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; interface FeedbackDetailsProps { @@ -24,6 +25,8 @@ export function FeedbackDetails({ }: FeedbackDetailsProps): ReactElement { const { t } = useTranslation(); const { baseUrl, isAllPrograms } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; return ( @@ -51,7 +54,7 @@ export function FeedbackDetails({ size: 3, }, { - label: t('Household ID'), + label: `${beneficiaryGroup?.groupLabel} ID`, value: ( {feedback.householdLookup?.id && @@ -74,7 +77,7 @@ export function FeedbackDetails({ size: 3, }, { - label: t('Individual ID'), + label: `${beneficiaryGroup?.memberLabel} ID`, value: ( {feedback.individualLookup?.id && diff --git a/src/frontend/src/components/accountability/Feedback/HouseholdQuestionnaire/HouseholdQuestionnaire.tsx b/src/frontend/src/components/accountability/Feedback/HouseholdQuestionnaire/HouseholdQuestionnaire.tsx index df934eaf33..cf0d90c91c 100644 --- a/src/frontend/src/components/accountability/Feedback/HouseholdQuestionnaire/HouseholdQuestionnaire.tsx +++ b/src/frontend/src/components/accountability/Feedback/HouseholdQuestionnaire/HouseholdQuestionnaire.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'; import { FormikCheckboxField } from '@shared/Formik/FormikCheckboxField'; import { ContentLink } from '@core/ContentLink'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; interface HouseholdQuestionnaireProps { @@ -16,12 +17,15 @@ export function HouseholdQuestionnaire({ const { baseUrl } = useBaseUrl(); const { t } = useTranslation(); const selectedHouseholdData = values.selectedHousehold; + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + return ( {[ { name: 'questionnaire_size', - label: t('Household Size'), + label: `${beneficiaryGroup?.groupLabel} Size`, value: selectedHouseholdData.size, size: 3, }, @@ -45,7 +49,7 @@ export function HouseholdQuestionnaire({ }, { name: 'questionnaire_headOfHousehold', - label: t('Head of Household'), + label: `Head of ${beneficiaryGroup?.groupLabel}`, value: ( {[ { name: 'questionnaire_fullName', - label: t('Individual Full Name'), + label: t(`${beneficiaryGroup?.memberLabel} Full Name`), value: ( diff --git a/src/frontend/src/components/core/ActivityLogPageFilters.tsx b/src/frontend/src/components/core/ActivityLogPageFilters.tsx index d6dc50f95e..29c53b35aa 100644 --- a/src/frontend/src/components/core/ActivityLogPageFilters.tsx +++ b/src/frontend/src/components/core/ActivityLogPageFilters.tsx @@ -7,6 +7,7 @@ import { createHandleApplyFilterChange } from '@utils/utils'; import { FiltersSection } from './FiltersSection'; import { SearchTextField } from './SearchTextField'; import { SelectFilter } from './SelectFilter'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; interface ActivityLogPageFiltersProps { @@ -26,6 +27,8 @@ export function ActivityLogPageFilters({ const { t } = useTranslation(); const navigate = useNavigate(); const location = useLocation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { handleFilterChange, applyFilterChanges, clearFilter } = createHandleApplyFilterChange( @@ -48,8 +51,8 @@ export function ActivityLogPageFilters({ const modules = { program: 'Programme', - household: 'Household', - individual: 'Individual', + household: `${beneficiaryGroup?.groupLabel}`, + individual: `${beneficiaryGroup?.memberLabel}`, grievanceticket: 'Grievance Tickets', paymentverificationplan: 'Payment Plan Payment Verification', targetpopulation: 'Target Population', diff --git a/src/frontend/src/components/core/Drawer/DrawerItems.tsx b/src/frontend/src/components/core/Drawer/DrawerItems.tsx index c3ea5d9700..ca545cf5b6 100644 --- a/src/frontend/src/components/core/Drawer/DrawerItems.tsx +++ b/src/frontend/src/components/core/Drawer/DrawerItems.tsx @@ -21,7 +21,7 @@ import { SCOPE_ALL_PROGRAMS, SCOPE_PROGRAM, } from './menuItems'; -import { useProgramContext } from 'src/programContext'; +import { ProgramInterface, useProgramContext } from 'src/programContext'; const Text = styled(ListItemText)` .MuiTypography-body1 { @@ -61,12 +61,13 @@ export const DrawerItems = ({ open, }: DrawerItemsProps): ReactElement => { const { baseUrl, businessArea, programId, isAllPrograms } = useBaseUrl(); - const { isSocialDctType } = useProgramContext(); + const { isSocialDctType, selectedProgram } = useProgramContext(); const permissions = usePermissions(); const { data: businessAreaData } = useBusinessAreaDataQuery({ variables: { businessAreaSlug: businessArea }, fetchPolicy: 'cache-first', }); + const clearLocation = currentLocation.replace(`/${baseUrl}`, ''); const navigate = useNavigate(); const initialIndex = menuItems.findIndex((item) => { @@ -121,7 +122,38 @@ export const DrawerItems = ({ return updatedMenuItems; }; - const preparedMenuItems = prepareMenuItems(menuItems); + const beneficiaryGroupTransformator = ( + array: MenuItem[], + _beneficiaryGroup: ProgramInterface['beneficiaryGroup'], + ): MenuItem[] => { + if (!_beneficiaryGroup) { + return array; + } + + return array.map((item, index) => { + if (index === 2 && !isAllPrograms) { + item.name = _beneficiaryGroup.name; + if (item.secondaryActions) { + item.secondaryActions = item.secondaryActions.map( + (action, actionIndex) => { + if (actionIndex === 0) { + action.name = _beneficiaryGroup.groupLabelPlural; + } else if (actionIndex === 1) { + action.name = _beneficiaryGroup.memberLabelPlural; + } + return action; + }, + ); + } + } + return item; + }); + }; + + const preparedMenuItems = beneficiaryGroupTransformator( + prepareMenuItems(menuItems), + selectedProgram?.beneficiaryGroup, + ); const { isPaymentPlanApplicable, isAccountabilityApplicable } = businessAreaData.businessArea; diff --git a/src/frontend/src/components/dashboard/sections/PaymentVerificationSection/PaymentVerificationSection.tsx b/src/frontend/src/components/dashboard/sections/PaymentVerificationSection/PaymentVerificationSection.tsx index 8debe92513..06fef4d34f 100644 --- a/src/frontend/src/components/dashboard/sections/PaymentVerificationSection/PaymentVerificationSection.tsx +++ b/src/frontend/src/components/dashboard/sections/PaymentVerificationSection/PaymentVerificationSection.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'; import { AllChartsQuery } from '@generated/graphql'; import { PaymentVerificationChart } from '../../charts/PaymentVerificationChart'; import { DashboardPaper } from '../../DashboardPaper'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; interface PaymentVerificationSectionProps { @@ -12,12 +13,15 @@ export function PaymentVerificationSection({ data, }: PaymentVerificationSectionProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + if (!data) return null; const renderContacted = () => { return data.households === 1 - ? t('Household contacted') - : t('Households contacted'); + ? `${beneficiaryGroup?.groupLabel} contacted` + : `${beneficiaryGroup?.groupLabelPlural} contacted`; }; return ( diff --git a/src/frontend/src/components/grievances/AddIndividualDataChange.tsx b/src/frontend/src/components/grievances/AddIndividualDataChange.tsx index 38b5c4a0cd..16b03b2202 100644 --- a/src/frontend/src/components/grievances/AddIndividualDataChange.tsx +++ b/src/frontend/src/components/grievances/AddIndividualDataChange.tsx @@ -19,6 +19,7 @@ import { AgencyField } from './AgencyField'; import { DocumentField } from './DocumentField'; import { FormikBoolFieldGrievances } from './FormikBoolFieldGrievances'; import { removeItemById } from './utils/helpers'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; export interface AddIndividualDataChangeFieldProps { @@ -122,6 +123,8 @@ export function AddIndividualDataChange({ const { t } = useTranslation(); const location = useLocation(); const isEditTicket = location.pathname.indexOf('edit-ticket') !== -1; + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { data, loading } = useAllAddIndividualFieldsQuery(); if (loading) { @@ -138,7 +141,9 @@ export function AddIndividualDataChange({ !isEditTicket && ( <> - <Typography variant="h6">{t('Individual Data')}</Typography> + <Typography variant="h6"> + {t(`${beneficiaryGroup?.memberLabel} Data`)} + </Typography> diff --git a/src/frontend/src/components/grievances/AddIndividualGrievanceDetails.tsx b/src/frontend/src/components/grievances/AddIndividualGrievanceDetails.tsx index e556fc708e..c24c0bba9e 100644 --- a/src/frontend/src/components/grievances/AddIndividualGrievanceDetails.tsx +++ b/src/frontend/src/components/grievances/AddIndividualGrievanceDetails.tsx @@ -15,6 +15,7 @@ import { LabelizedField } from '@core/LabelizedField'; import { LoadingComponent } from '@core/LoadingComponent'; import { Title } from '@core/Title'; import { ApproveBox } from './GrievancesApproveSection/ApproveSectionStyles'; +import { useProgramContext } from 'src/programContext'; import { ReactElement, ReactNode } from 'react'; export function AddIndividualGrievanceDetails({ @@ -27,6 +28,9 @@ export function AddIndividualGrievanceDetails({ const { t } = useTranslation(); const { data, loading } = useAllAddIndividualFieldsQuery(); const [mutate] = useApproveAddIndividualDataChangeMutation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const confirm = useConfirmation(); const { showMessage } = useSnackbar(); if (loading) { @@ -117,11 +121,11 @@ export function AddIndividualGrievanceDetails({ ]; let dialogText = t( - 'You did not approve the following add individual data. Are you sure you want to continue?', + `You did not approve the following add ${beneficiaryGroup?.memberLabel} data. Are you sure you want to continue?`, ); if (!ticket.addIndividualTicketDetails.approveStatus) { dialogText = t( - 'You are approving the following Add individual data. Are you sure you want to continue?', + `You are approving the following Add ${beneficiaryGroup?.memberLabel} data. Are you sure you want to continue?`, ); } @@ -129,7 +133,9 @@ export function AddIndividualGrievanceDetails({ <Box display="flex" justifyContent="space-between"> - <Typography variant="h6">{t('Individual Data')}</Typography> + <Typography variant="h6"> + {t(`${beneficiaryGroup?.memberLabel} Data`)} + </Typography> {canApproveDataChange && ( <Button data-cy="button-approve" diff --git a/src/frontend/src/components/grievances/ApproveDeleteHouseholdGrievanceDetails.tsx b/src/frontend/src/components/grievances/ApproveDeleteHouseholdGrievanceDetails.tsx index 919991429f..00cf7ac81d 100644 --- a/src/frontend/src/components/grievances/ApproveDeleteHouseholdGrievanceDetails.tsx +++ b/src/frontend/src/components/grievances/ApproveDeleteHouseholdGrievanceDetails.tsx @@ -27,6 +27,7 @@ import { GrievanceTicketQuery, useApproveDeleteHouseholdDataChangeMutation, } from '@generated/graphql'; +import { useProgramContext } from 'src/programContext'; const EditIcon = styled(Edit)` color: ${({ theme }) => theme.hctPalette.darkerBlue}; @@ -47,6 +48,8 @@ export const ApproveDeleteHouseholdGrievanceDetails = ({ const isForApproval = ticket.status === GRIEVANCE_TICKET_STATES.FOR_APPROVAL; const { approveStatus, reasonHousehold } = ticket.deleteHouseholdTicketDetails; + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const validationSchema = Yup.object().shape({ reasonHhId: Yup.string().when( @@ -55,7 +58,7 @@ export const ApproveDeleteHouseholdGrievanceDetails = ({ const value = String(withdrawReasonValue); if (value === 'duplicate' && !approveStatus) { return Yup.string() - .required('Household Unicef Id is required') + .required(`${beneficiaryGroup?.groupLabel} Unicef Id is required`) .max(15, 'Too long'); } return Yup.string(); @@ -159,10 +162,10 @@ export const ApproveDeleteHouseholdGrievanceDetails = ({ <Typography variant="body2"> {showWithdraw() ? t( - 'Please provide the reason of withdrawal of this household.', + `Please provide the reason of withdrawal of this ${beneficiaryGroup?.groupLabel}.`, ) : t( - 'You did not approve the following household to be withdrawn. Are you sure you want to continue?', + `You did not approve the following ${beneficiaryGroup?.groupLabel} to be withdrawn. Are you sure you want to continue?`, )} </Typography> </Box> @@ -173,7 +176,7 @@ export const ApproveDeleteHouseholdGrievanceDetails = ({ choices={[ { value: 'duplicate', - name: 'This household is a duplicate of another household', + name: `This ${beneficiaryGroup?.groupLabel} is a duplicate of another ${beneficiaryGroup?.groupLabel}`, }, ]} component={FormikRadioGroup} @@ -186,7 +189,7 @@ export const ApproveDeleteHouseholdGrievanceDetails = ({ name="reasonHhId" fullWidth variant="outlined" - label={t('Household Unicef Id')} + label={`${beneficiaryGroup?.groupLabel} Unicef Id`} component={FormikTextField} required /> diff --git a/src/frontend/src/components/grievances/CreateGrievance/Description/Description.tsx b/src/frontend/src/components/grievances/CreateGrievance/Description/Description.tsx index fe73e4d6fc..587257f4b1 100644 --- a/src/frontend/src/components/grievances/CreateGrievance/Description/Description.tsx +++ b/src/frontend/src/components/grievances/CreateGrievance/Description/Description.tsx @@ -23,6 +23,7 @@ import { NewDocumentationFieldArray } from '../../Documentation/NewDocumentation import { LookUpLinkedTickets } from '../../LookUps/LookUpLinkedTickets/LookUpLinkedTickets'; import { LookUpPaymentRecord } from '../../LookUps/LookUpPaymentRecord/LookUpPaymentRecord'; import { useProgramContext } from 'src/programContext'; +import { replaceLabels } from '@components/grievances/utils/createGrievanceUtils'; const BoxPadding = styled.div` padding: 15px 0; @@ -73,6 +74,8 @@ export function Description({ }, fetchPolicy: 'network-only', }); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; // Set program value based on selected household or individual useEffect(() => { @@ -121,11 +124,15 @@ export function Description({ }, showIssueType(values) && { label: t('Issue Type'), - value: <span>{selectedIssueType(values)}</span>, + value: ( + <span> + {replaceLabels(selectedIssueType(values), beneficiaryGroup)} + </span> + ), size: 9, }, { - label: t('Household ID'), + label: `${beneficiaryGroup?.groupLabel} ID`, value: ( <span> {values.selectedHousehold?.id && @@ -144,7 +151,7 @@ export function Description({ size: 3, }, { - label: t('Individual ID'), + label: `${beneficiaryGroup?.memberLabel} ID`, value: ( <span> {values.selectedIndividual?.id && diff --git a/src/frontend/src/components/grievances/CreateGrievance/Selection/Selection.tsx b/src/frontend/src/components/grievances/CreateGrievance/Selection/Selection.tsx index 38d31e07b6..8368879a9c 100644 --- a/src/frontend/src/components/grievances/CreateGrievance/Selection/Selection.tsx +++ b/src/frontend/src/components/grievances/CreateGrievance/Selection/Selection.tsx @@ -4,15 +4,15 @@ import { useTranslation } from 'react-i18next'; import { GrievancesChoiceDataQuery } from '@generated/graphql'; import { useArrayToDict } from '@hooks/useArrayToDict'; import { FormikSelectField } from '@shared/Formik/FormikSelectField'; +import { DividerLine } from '@core/DividerLine'; +import { LabelizedField } from '@core/LabelizedField'; +import { useProgramContext } from 'src/programContext'; import { + getGrievanceCategoryDescriptions, + getGrievanceIssueTypeDescriptions, GRIEVANCE_CATEGORIES_NAMES, - GRIEVANCE_CATEGORY_DESCRIPTIONS, GRIEVANCE_ISSUE_TYPES_NAMES, - GRIEVANCE_ISSUE_TYPE_DESCRIPTIONS, } from '@utils/constants'; -import { DividerLine } from '@core/DividerLine'; -import { LabelizedField } from '@core/LabelizedField'; -import { useProgramContext } from 'src/programContext'; import { ChangeEvent, ReactElement } from 'react'; export interface SelectionProps { @@ -40,14 +40,40 @@ export function Selection({ '*', ); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const dataChangeIssueTypes = [ - { name: 'Household Data Update', value: '13' }, - { name: 'Individual Data Update', value: '14' }, + { name: `${beneficiaryGroup?.groupLabel} Data Update`, value: '13' }, + { name: `${beneficiaryGroup?.memberLabel} Data Update`, value: '14' }, ]; + function replaceLabels(choices, _beneficiaryGroup) { + if (!choices) return []; + return choices.map((choice) => { + let newName = choice.name; + if (_beneficiaryGroup?.memberLabel) { + newName = newName.replace(/Individual/g, _beneficiaryGroup.memberLabel); + } + if (_beneficiaryGroup?.groupLabel) { + newName = newName.replace(/Household/g, _beneficiaryGroup.groupLabel); + } + return { ...choice, name: newName }; + }); + } + const updatedChoices = replaceLabels( + issueTypeDict[values.category]?.subCategories, + beneficiaryGroup, + ); + + const categoryDescriptions = + getGrievanceCategoryDescriptions(beneficiaryGroup); + const issueTypeDescriptions = + getGrievanceIssueTypeDescriptions(beneficiaryGroup); + const issueTypeChoices = redirectedFromRelatedTicket ? dataChangeIssueTypes - : issueTypeDict[values.category]?.subCategories; + : updatedChoices; const addDisabledProperty = (choices) => { if (!choices) return []; @@ -69,13 +95,9 @@ export function Selection({ ); const categoryDescription = - GRIEVANCE_CATEGORY_DESCRIPTIONS[ - GRIEVANCE_CATEGORIES_NAMES[values.category] - ] || ''; + categoryDescriptions[GRIEVANCE_CATEGORIES_NAMES[values.category]] || ''; const issueTypeDescription = - GRIEVANCE_ISSUE_TYPE_DESCRIPTIONS[ - GRIEVANCE_ISSUE_TYPES_NAMES[values.issueType] - ] || ''; + issueTypeDescriptions[GRIEVANCE_ISSUE_TYPES_NAMES[values.issueType]] || ''; return ( <Grid container spacing={3}> diff --git a/src/frontend/src/components/grievances/CreateGrievance/Verification/Verification.tsx b/src/frontend/src/components/grievances/CreateGrievance/Verification/Verification.tsx index bb1e23fc40..f913feb807 100644 --- a/src/frontend/src/components/grievances/CreateGrievance/Verification/Verification.tsx +++ b/src/frontend/src/components/grievances/CreateGrievance/Verification/Verification.tsx @@ -26,7 +26,9 @@ export interface VerificationProps { export function Verification({ values }: VerificationProps): ReactElement { const { t } = useTranslation(); - const { isSocialDctType } = useProgramContext(); + const { selectedProgram, isSocialDctType } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + return ( <BoxWithBorders> <> @@ -39,7 +41,7 @@ export function Verification({ values }: VerificationProps): ReactElement { {values.selectedHousehold && !isSocialDctType && ( <Box py={4}> <Typography variant="subtitle2"> - {t('Household Questionnaire')} + {`${beneficiaryGroup?.groupLabel} Questionnaire`} </Typography> <Box py={4}> <HouseholdQuestionnaire values={values} /> @@ -49,7 +51,7 @@ export function Verification({ values }: VerificationProps): ReactElement { {values.selectedIndividual && ( <> <Typography variant="subtitle2"> - {t('Individual Questionnaire')} + {`${beneficiaryGroup?.memberLabel} Questionnaire`} </Typography> <Box py={4}> <IndividualQuestionnaire values={values} /> diff --git a/src/frontend/src/components/grievances/DeleteHouseholdGrievanceDetails.tsx b/src/frontend/src/components/grievances/DeleteHouseholdGrievanceDetails.tsx index be19c22844..228a1a32f1 100644 --- a/src/frontend/src/components/grievances/DeleteHouseholdGrievanceDetails.tsx +++ b/src/frontend/src/components/grievances/DeleteHouseholdGrievanceDetails.tsx @@ -15,6 +15,7 @@ import { LoadingComponent } from '@core/LoadingComponent'; import { Title } from '@core/Title'; import { ApproveDeleteHouseholdGrievanceDetails } from './ApproveDeleteHouseholdGrievanceDetails'; import { ApproveBox } from './GrievancesApproveSection/ApproveSectionStyles'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const Info = styled(InfoIcon)` @@ -31,6 +32,8 @@ export function DeleteHouseholdGrievanceDetails({ }): ReactElement { const { t } = useTranslation(); const { baseUrl, isAllPrograms } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { data: choicesData, loading: choicesLoading } = useHouseholdChoiceDataQuery(); @@ -48,13 +51,16 @@ export function DeleteHouseholdGrievanceDetails({ <ApproveBox> <Title> <Box display="flex" justifyContent="space-between" alignItems="center"> - <Typography variant="h6">{t('Household to be withdrawn')}</Typography> + <Typography variant="h6">{`${beneficiaryGroup?.groupLabel} to be withdrawn`}</Typography> {approveStatus && ticket.deleteHouseholdTicketDetails.reasonHousehold && ( <Box display="flex" alignItems="center"> <Info /> <Box mr={2}> - <p>This household is a duplicate of a household ID:</p> + <p> + This {beneficiaryGroup?.groupLabel} is a duplicate of a{' '} + {beneficiaryGroup?.groupLabel} ID: + </p> </Box> {!isAllPrograms ? ( <BlackLink @@ -91,7 +97,7 @@ export function DeleteHouseholdGrievanceDetails({ - + {ticket.household.size} @@ -101,7 +107,7 @@ export function DeleteHouseholdGrievanceDetails({ - + diff --git a/src/frontend/src/components/grievances/DeleteIndividualGrievanceDetails.tsx b/src/frontend/src/components/grievances/DeleteIndividualGrievanceDetails.tsx index 1ac12667e5..d5b8afb322 100644 --- a/src/frontend/src/components/grievances/DeleteIndividualGrievanceDetails.tsx +++ b/src/frontend/src/components/grievances/DeleteIndividualGrievanceDetails.tsx @@ -18,6 +18,7 @@ import { LoadingComponent } from '@core/LoadingComponent'; import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; import { ApproveBox } from './GrievancesApproveSection/ApproveSectionStyles'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; export type RoleReassignData = { @@ -36,6 +37,8 @@ export function DeleteIndividualGrievanceDetails({ const { t } = useTranslation(); const { showMessage } = useSnackbar(); const confirm = useConfirmation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const isForApproval = ticket.status === GRIEVANCE_TICKET_STATES.FOR_APPROVAL; const isHeadOfHousehold = @@ -154,11 +157,11 @@ export function DeleteIndividualGrievanceDetails({ const allLabels = [...labels, ...documentLabels]; let dialogText = t( - 'You did not approve the following individual to be withdrawn. Are you sure you want to continue?', + `You did not approve the following ${beneficiaryGroup?.memberLabel} to be withdrawn. Are you sure you want to continue?`, ); if (!ticket.deleteIndividualTicketDetails.approveStatus) { dialogText = t( - 'You are approving the following individual to be withdrawn. Are you sure you want to continue?', + `You are approving the following ${beneficiaryGroup?.memberLabel} to be withdrawn. Are you sure you want to continue?`, ); } return ( @@ -166,7 +169,7 @@ export function DeleteIndividualGrievanceDetails({ <Box display="flex" justifyContent="space-between"> <Typography variant="h6"> - {t('Individual to be withdrawn')} + {t(`${beneficiaryGroup?.memberLabel} to be withdrawn`)} </Typography> {canApproveDataChange && ( <Button diff --git a/src/frontend/src/components/grievances/EditHouseholdDataChange/EditHouseholdDataChange.tsx b/src/frontend/src/components/grievances/EditHouseholdDataChange/EditHouseholdDataChange.tsx index 3ab5fc1636..85ef346d14 100644 --- a/src/frontend/src/components/grievances/EditHouseholdDataChange/EditHouseholdDataChange.tsx +++ b/src/frontend/src/components/grievances/EditHouseholdDataChange/EditHouseholdDataChange.tsx @@ -12,6 +12,7 @@ import { import { LoadingComponent } from '@core/LoadingComponent'; import { Title } from '@core/Title'; import { EditHouseholdDataChangeFieldRow } from './EditHouseholdDataChangeFieldRow'; +import { useProgramContext } from 'src/programContext'; export interface EditHouseholdDataChangeProps { values; @@ -23,6 +24,9 @@ export function EditHouseholdDataChange({ }: EditHouseholdDataChangeProps): ReactElement { const { t } = useTranslation(); const location = useLocation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const isEditTicket = location.pathname.includes('edit-ticket'); const household: AllHouseholdsQuery['allHouseholds']['edges'][number]['node'] = values.selectedHousehold; @@ -52,7 +56,9 @@ export function EditHouseholdDataChange({ householdFieldsData?.allEditHouseholdFieldsAttributes; if (!household) { - return <div>{t('You have to select a household earlier')}</div>; + return ( + <div>{`You have to select a ${beneficiaryGroup?.groupLabel} earlier`}</div> + ); } if (fullHouseholdLoading || householdFieldsLoading || !fullHousehold) { return <LoadingComponent />; @@ -64,7 +70,7 @@ export function EditHouseholdDataChange({ !isEditTicket && ( <> <Title> - <Typography variant="h6">{t('Household Data')}</Typography> + <Typography variant="h6">{`${beneficiaryGroup?.groupLabel} Data`}</Typography> theme.hctPalette.lighterGray}; @@ -38,6 +39,9 @@ export function EditIndividualDataChange({ }: EditIndividualDataChangeProps): ReactElement { const { t } = useTranslation(); const location = useLocation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const isEditTicket = location.pathname.indexOf('edit-ticket') !== -1; const individual: AllIndividualsQuery['allIndividuals']['edges'][number]['node'] = values.selectedIndividual; @@ -68,7 +72,11 @@ export function EditIndividualDataChange({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); if (!individual) { - return
{t('You have to select an individual earlier')}
; + return ( +
+ {t(`You have to select a ${beneficiaryGroup?.memberLabel} earlier`)} +
+ ); } if ( addIndividualFieldsLoading || diff --git a/src/frontend/src/components/grievances/GrievanceDetailsToolbar.tsx b/src/frontend/src/components/grievances/GrievanceDetailsToolbar.tsx index c2a2c1e82b..d3c9a80c9b 100644 --- a/src/frontend/src/components/grievances/GrievanceDetailsToolbar.tsx +++ b/src/frontend/src/components/grievances/GrievanceDetailsToolbar.tsx @@ -74,7 +74,8 @@ export const GrievanceDetailsToolbar = ({ const { baseUrl } = useBaseUrl(); const confirm = useConfirmation(); const navigate = useNavigate(); - const { isActiveProgram } = useProgramContext(); + const { isActiveProgram, selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const breadCrumbsItems: BreadCrumbsItem[] = [ { @@ -193,7 +194,7 @@ export const GrievanceDetailsToolbar = ({ confirmationMessage = ''; } else if (isDeduplicationCategory) { confirmationMessage = t( - 'By continuing you acknowledge that individuals in this ticket were reviewed and all were deemed either distinct or duplicates in the system.', + `By continuing you acknowledge that ${beneficiaryGroup?.memberLabelPlural} in this ticket were reviewed and all were deemed either distinct or duplicates in the system.`, ); } return confirmationMessage; @@ -248,7 +249,7 @@ export const GrievanceDetailsToolbar = ({ if (notApprovedSystemFlaggingChanges) { additionalContent = t( - ' By continuing you acknowledge that individuals in this ticket was compared with sanction list. No matches were found', + `By continuing you acknowledge that ${beneficiaryGroup?.memberLabelPlural} in this ticket were compared with the sanction list. No matches were found`, ); } @@ -259,7 +260,7 @@ export const GrievanceDetailsToolbar = ({ householdHasOneIndividual ) { additionalContent += t( - ' When you close this ticket, the household that this Individual is a member of will be deactivated.', + `When you close this ticket, the ${beneficiaryGroup?.groupLabel} that this ${beneficiaryGroup?.memberLabel} is a member of will be deactivated.`, ); } @@ -292,7 +293,7 @@ export const GrievanceDetailsToolbar = ({ title={t('Duplicate Document Conflict')} buttonText={t('Close Ticket')} message={t( - 'The individuals have matching document numbers. HOPE requires that document numbers are unique. Please resolve before closing the ticket.', + `The ${beneficiaryGroup?.memberLabelPlural} have matching document numbers. HOPE requires that document numbers are unique. Please resolve before closing the ticket.`, )} /> ) : ( diff --git a/src/frontend/src/components/grievances/GrievancesDetails/GrievancesDetails.tsx b/src/frontend/src/components/grievances/GrievancesDetails/GrievancesDetails.tsx index b6c58caff9..1a8f5ee0bd 100644 --- a/src/frontend/src/components/grievances/GrievancesDetails/GrievancesDetails.tsx +++ b/src/frontend/src/components/grievances/GrievancesDetails/GrievancesDetails.tsx @@ -23,6 +23,7 @@ import { import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import { useProgramContext } from 'src/programContext'; +import { replaceLabels } from '../utils/createGrievanceUtils'; interface GrievancesDetailsProps { ticket: GrievanceTicketQuery['grievanceTicket']; @@ -41,7 +42,9 @@ export function GrievancesDetails({ }: GrievancesDetailsProps): ReactElement { const { t } = useTranslation(); const { isAllPrograms } = useBaseUrl(); - const { isSocialDctType } = useProgramContext(); + const { selectedProgram, isSocialDctType } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const statusChoices: { [id: number]: string; } = choicesToDict(choicesData.grievanceTicketStatusChoices); @@ -247,11 +250,13 @@ export function GrievancesDetails({ }, showIssueType && { label: t('Issue Type'), - value: {issueType}, + value: ( + {replaceLabels(issueType, beneficiaryGroup)} + ), size: 3, }, !isAllPrograms && { - label: t('Household ID'), + label: `${beneficiaryGroup?.groupLabel} ID`, value: ( {ticket.household?.id && @@ -275,7 +280,8 @@ export function GrievancesDetails({ label: isAllPrograms || isSocialDctType ? t('Target ID') - : t('Individual ID'), + : `${beneficiaryGroup?.memberLabel} ID`, + value: isAllPrograms || isSocialDctType ? (
{ticket?.targetId || '-'}
diff --git a/src/frontend/src/components/grievances/GrievancesTable/GrievancesTable.tsx b/src/frontend/src/components/grievances/GrievancesTable/GrievancesTable.tsx index 3f6d3d3fca..68d08c7f10 100644 --- a/src/frontend/src/components/grievances/GrievancesTable/GrievancesTable.tsx +++ b/src/frontend/src/components/grievances/GrievancesTable/GrievancesTable.tsx @@ -19,7 +19,7 @@ import { GRIEVANCE_CATEGORIES, GRIEVANCE_TICKET_STATES, } from '@utils/constants'; -import { choicesToDict, dateToIsoString } from '@utils/utils'; +import { adjustHeadCells, choicesToDict, dateToIsoString } from '@utils/utils'; import get from 'lodash/get'; import { ReactElement, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -47,9 +47,21 @@ export const GrievancesTable = ({ filter, }: GrievancesTableProps): ReactElement => { const { businessArea, programId, isAllPrograms } = useBaseUrl(); - const { isSocialDctType } = useProgramContext(); + const { isSocialDctType, selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { t } = useTranslation(); + const replacements = { + household_unicef_id: (_beneficiaryGroup) => + `${_beneficiaryGroup?.groupLabel} ID`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCellsStandardProgram, + beneficiaryGroup, + replacements, + ); + const initialVariables: AllGrievanceTicketQueryVariables = { businessArea, search: filter.search.trim(), @@ -204,7 +216,7 @@ export const GrievancesTable = ({ const baseCells = isSocialDctType || isAllPrograms ? headCellsSocialProgram - : headCellsStandardProgram; + : adjustedHeadCells; if (isAllPrograms) { return [ diff --git a/src/frontend/src/components/grievances/HouseholdQuestionnaire/HouseholdQuestionnaire.tsx b/src/frontend/src/components/grievances/HouseholdQuestionnaire/HouseholdQuestionnaire.tsx index 1f0da3e64d..10220b8156 100644 --- a/src/frontend/src/components/grievances/HouseholdQuestionnaire/HouseholdQuestionnaire.tsx +++ b/src/frontend/src/components/grievances/HouseholdQuestionnaire/HouseholdQuestionnaire.tsx @@ -7,6 +7,7 @@ import { ContentLink } from '@core/ContentLink'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { AllHouseholdsQuery, useHouseholdLazyQuery } from '@generated/graphql'; import { LoadingComponent } from '@core/LoadingComponent'; +import { useProgramContext } from 'src/programContext'; interface HouseholdQuestionnaireProps { values; @@ -17,6 +18,8 @@ export function HouseholdQuestionnaire({ }: HouseholdQuestionnaireProps): ReactElement { const { baseUrl } = useBaseUrl(); const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const household: AllHouseholdsQuery['allHouseholds']['edges'][number]['node'] = values.selectedHousehold; const [getHousehold, { data: fullHousehold, loading: fullHouseholdLoading }] = @@ -39,7 +42,7 @@ export function HouseholdQuestionnaire({ {[ { name: 'questionnaire_size', - label: t('Household Size'), + label: t(`${beneficiaryGroup?.groupLabel} Size`), value: selectedHouseholdData.size, size: 3, }, @@ -63,7 +66,7 @@ export function HouseholdQuestionnaire({ }, { name: 'questionnaire_headOfHousehold', - label: t('Head of Household'), + label: t(`Head of ${beneficiaryGroup?.groupLabel}`), value: ( { const { t } = useTranslation(); const { baseUrl } = useBaseUrl(); - const { isSocialDctType } = useProgramContext(); + const { isSocialDctType, selectedProgram } = useProgramContext(); const selectedIndividualData = values.selectedIndividual || values.selectedHousehold.headOfHousehold; + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const questionFields = isSocialDctType ? [ { name: 'questionnaire_fullName', - label: t('Individual Full Name'), + label: t(`${beneficiaryGroup?.memberLabel} Full Name`), value: ( {!isSocialDctType && ( - + )}
diff --git a/src/frontend/src/components/grievances/LookUps/LookUpHouseholdIndividual/LookUpHouseholdIndividualSelectionDisplay.tsx b/src/frontend/src/components/grievances/LookUps/LookUpHouseholdIndividual/LookUpHouseholdIndividualSelectionDisplay.tsx index f7b2b0f0f6..4d681ca54c 100644 --- a/src/frontend/src/components/grievances/LookUps/LookUpHouseholdIndividual/LookUpHouseholdIndividualSelectionDisplay.tsx +++ b/src/frontend/src/components/grievances/LookUps/LookUpHouseholdIndividual/LookUpHouseholdIndividualSelectionDisplay.tsx @@ -1,10 +1,9 @@ -import { Box, Grid } from '@mui/material'; import DeleteIcon from '@mui/icons-material/Delete'; -import styled from 'styled-components'; -import { useTranslation } from 'react-i18next'; -import { StyledBox, BlueText, DarkGrey } from '../LookUpStyles'; +import { Box, Grid } from '@mui/material'; +import * as React from 'react'; import { useProgramContext } from 'src/programContext'; -import { ReactElement } from 'react'; +import styled from 'styled-components'; +import { BlueText, DarkGrey, StyledBox } from '../LookUpStyles'; const Types = { household: 'household', individual: 'individual' }; @@ -31,9 +30,10 @@ export const LookUpHouseholdIndividualSelectionDisplay = ({ setSelectedHousehold, selectedIndividual, setSelectedIndividual, -}: LookUpHouseholdIndividualSelectionDisplayProps): ReactElement => { - const { t } = useTranslation(); - const { isSocialDctType } = useProgramContext(); +}: LookUpHouseholdIndividualSelectionDisplayProps): React.ReactElement => { + const { selectedProgram, isSocialDctType } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const handleRemove = (type): void => { if (type === Types.household) { onValueChange('selectedHousehold', null); @@ -54,7 +54,7 @@ export const LookUpHouseholdIndividualSelectionDisplay = ({ - {t('Household ID')}: + {`${beneficiaryGroup?.groupLabel} ID`}:   {selectedHousehold?.unicefId || '-'} @@ -83,7 +83,7 @@ export const LookUpHouseholdIndividualSelectionDisplay = ({ - {t('Individual ID')}: + {`${beneficiaryGroup?.memberLabel} ID`}:   {selectedIndividual?.unicefId || '-'} diff --git a/src/frontend/src/components/grievances/LookUps/LookUpHouseholdTable/LookUpHouseholdTable.tsx b/src/frontend/src/components/grievances/LookUps/LookUpHouseholdTable/LookUpHouseholdTable.tsx index f2c08c7743..aa193f9b3f 100644 --- a/src/frontend/src/components/grievances/LookUps/LookUpHouseholdTable/LookUpHouseholdTable.tsx +++ b/src/frontend/src/components/grievances/LookUps/LookUpHouseholdTable/LookUpHouseholdTable.tsx @@ -11,6 +11,8 @@ import { useBaseUrl } from '@hooks/useBaseUrl'; import { TableWrapper } from '@core/TableWrapper'; import { headCells } from './LookUpHouseholdTableHeadCells'; import { LookUpHouseholdTableRow } from './LookUpHouseholdTableRow'; +import { useProgramContext } from 'src/programContext'; +import { adjustHeadCells } from '@utils/utils'; interface LookUpHouseholdTableProps { businessArea: string; @@ -47,6 +49,9 @@ export function LookUpHouseholdTable({ isFeedbackWithHouseholdOnly, }: LookUpHouseholdTableProps): ReactElement { const { isAllPrograms, programId } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const matchWithdrawnValue = (): boolean | undefined => { if (filter.withdrawn === 'true') { return true; @@ -123,8 +128,21 @@ export function LookUpHouseholdTable({ setFieldValue('identityVerified', false); }; + const replacements = { + unicefId: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} ID`, + head_of_household__full_name: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + size: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} Size`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + const headCellsWithProgramColumn = [ - ...headCells, + ...adjustedHeadCells, { disablePadding: false, label: 'Programme', @@ -136,7 +154,7 @@ export function LookUpHouseholdTable({ const preparedHeadcells = isAllPrograms ? headCellsWithProgramColumn - : headCells; + : adjustedHeadCells; const renderTable = (): ReactElement => ( { setSelectedIndividual(individual); @@ -92,12 +94,23 @@ export function LookUpIndividualTable({ isActiveProgram: filter.programState === 'active' ? true : null, }; + const replacements = { + unicefId: (_beneficiaryGroup) => `${_beneficiaryGroup?.memberLabel} ID`, + household__id: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} ID`, + }; + const headCells = isSocialDctType ? headCellsSocialProgram : headCellsStandardProgram; + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + const headCellsWithProgramColumn = [ - ...headCells, + ...adjustedHeadCells, { disablePadding: false, label: 'Programme', @@ -109,7 +122,7 @@ export function LookUpIndividualTable({ const preparedHeadcells = isAllPrograms ? headCellsWithProgramColumn - : headCells; + : adjustedHeadCells; const renderTable = (): ReactElement => ( - {t('Household ID')}: + {`${beneficiaryGroup?.groupLabel} ID`}: {selectedHousehold?.unicefId || '-'} - {t('Individual ID')}: + {`${beneficiaryGroup?.memberLabel} ID`}: {selectedIndividual?.unicefId || '-'} diff --git a/src/frontend/src/components/grievances/LookUps/LookUpReassignRole/ReassignRoleUnique.tsx b/src/frontend/src/components/grievances/LookUps/LookUpReassignRole/ReassignRoleUnique.tsx index 801dab31c7..2bd699d496 100644 --- a/src/frontend/src/components/grievances/LookUps/LookUpReassignRole/ReassignRoleUnique.tsx +++ b/src/frontend/src/components/grievances/LookUps/LookUpReassignRole/ReassignRoleUnique.tsx @@ -8,6 +8,7 @@ import { useReassignRoleGrievanceMutation, } from '@generated/graphql'; import { Button } from '@mui/material'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const ReassignRoleButton = styled(Button)` @@ -35,6 +36,8 @@ export function ReassignRoleUnique({ const { id } = useParams(); const { showMessage } = useSnackbar(); const [mutate] = useReassignRoleGrievanceMutation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; return ( - {t('Reassign To Unique Individual')} + {t(`Reassign To Unique ${beneficiaryGroup?.memberLabel}`)} )} diff --git a/src/frontend/src/components/grievances/NeedsAdjudication/BiometricsResults.tsx b/src/frontend/src/components/grievances/NeedsAdjudication/BiometricsResults.tsx index cf14fc6610..60bfad21ac 100644 --- a/src/frontend/src/components/grievances/NeedsAdjudication/BiometricsResults.tsx +++ b/src/frontend/src/components/grievances/NeedsAdjudication/BiometricsResults.tsx @@ -16,6 +16,7 @@ import { import { FC, ReactElement, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; +import { useProgramContext } from 'src/programContext'; export interface Individual { __typename?: 'DeduplicationEngineSimilarityPairIndividualNode'; @@ -58,6 +59,8 @@ export const BiometricsResults = ({ PERMISSIONS.GRIEVANCES_VIEW_BIOMETRIC_RESULTS, permissions, ); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const [loadData] = useGrievanceTicketLazyQuery({ variables: { @@ -113,7 +116,8 @@ export const BiometricsResults = ({ )} - Individual {individual1?.unicefId}: {individual1?.fullName} + {beneficiaryGroup?.memberLabel} {individual1?.unicefId}:{' '} + {individual1?.fullName} @@ -132,7 +136,8 @@ export const BiometricsResults = ({ )} - Individual {individual2?.unicefId}: {individual2?.fullName} + {beneficiaryGroup?.memberLabel} {individual2?.unicefId}:{' '} + {individual2?.fullName} diff --git a/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationDetailsOld.tsx b/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationDetailsOld.tsx index e2efce460d..c2d98d5cfb 100644 --- a/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationDetailsOld.tsx +++ b/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationDetailsOld.tsx @@ -40,6 +40,8 @@ export const NeedsAdjudicationDetailsOld = ({ const navigate = useNavigate(); const confirm = useConfirmation(); const { isActiveProgram } = useProgramContext(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const [approve] = useApproveNeedsAdjudicationMutation({ refetchQueries: () => [ @@ -165,10 +167,10 @@ export const NeedsAdjudicationDetailsOld = ({ - {t('Individual ID')} + {`${beneficiaryGroup?.memberLabel} ID`} - {t('Household ID')} + {`${beneficiaryGroup?.groupLabel} ID`} {t('Full Name')} diff --git a/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationTable.tsx b/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationTable.tsx index 8474bbee51..cf3f2a174c 100644 --- a/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationTable.tsx +++ b/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationTable.tsx @@ -1,4 +1,4 @@ -import React, { ChangeEvent } from 'react'; +import { ChangeEvent } from 'react'; import { BlackLink } from '@core/BlackLink'; import { UniversalMoment } from '@core/UniversalMoment'; import PeopleIcon from '@mui/icons-material/People'; @@ -44,6 +44,9 @@ export const NeedsAdjudicationTable = ({ const { t } = useTranslation(); const { baseUrl, isAllPrograms } = useBaseUrl(); const { isActiveProgram, isSocialDctType } = useProgramContext(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const details = ticket?.needsAdjudicationTicketDetails; const handleSelect = (id: string) => { @@ -97,7 +100,10 @@ export const NeedsAdjudicationTable = ({ !isActiveProgram; const renderPossibleDuplicateRow = (possibleDuplicate) => ( - + - {t('Individual ID')} + {beneficiaryGroup?.memberLabel} ID {!isSocialDctType && ( - {t('Household ID')} + {beneficiaryGroup?.groupLabel} ID )} @@ -236,7 +242,9 @@ export const NeedsAdjudicationTable = ({ - + <>{renderIds(openExistingTickets)} @@ -100,9 +103,7 @@ export function OtherRelatedTickets({ {t('Closed Tickets')} <>{renderIds(closedExistingTickets)} diff --git a/src/frontend/src/components/grievances/OtherRelatedTicketsCreate.tsx b/src/frontend/src/components/grievances/OtherRelatedTicketsCreate.tsx index 7a022b61dd..7cdf481b9a 100644 --- a/src/frontend/src/components/grievances/OtherRelatedTicketsCreate.tsx +++ b/src/frontend/src/components/grievances/OtherRelatedTicketsCreate.tsx @@ -14,10 +14,13 @@ import { BlueBold, } from './GrievancesApproveSection/ApproveSectionStyles'; import { getGrievanceDetailsPath } from './utils/createGrievanceUtils'; +import { useProgramContext } from 'src/programContext'; export function OtherRelatedTicketsCreate({ values }): ReactElement { const { t } = useTranslation(); const { baseUrl, businessArea } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const [show, setShow] = useState(false); const { data, loading } = useExistingGrievanceTicketsQuery({ @@ -70,9 +73,7 @@ export function OtherRelatedTicketsCreate({ values }): ReactElement { <>{renderIds(openExistingTickets)} @@ -87,9 +88,7 @@ export function OtherRelatedTicketsCreate({ values }): ReactElement { {t('Closed Tickets')} <>{renderIds(closedExistingTickets)} diff --git a/src/frontend/src/components/grievances/ReassignMultipleRoleBox.tsx b/src/frontend/src/components/grievances/ReassignMultipleRoleBox.tsx index a6af1d500c..a33adddec3 100644 --- a/src/frontend/src/components/grievances/ReassignMultipleRoleBox.tsx +++ b/src/frontend/src/components/grievances/ReassignMultipleRoleBox.tsx @@ -11,6 +11,7 @@ import { useBaseUrl } from '@hooks/useBaseUrl'; import { ContentLink } from '@core/ContentLink'; import { LabelizedField } from '@core/LabelizedField'; import { LookUpReassignRole } from './LookUps/LookUpReassignRole/LookUpReassignRole'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const StyledBox = styled(Paper)` @@ -40,12 +41,16 @@ export function ReassignMultipleRoleBox({ }): ReactElement { const { t } = useTranslation(); const { baseUrl } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const reassignData = JSON.parse( ticket.needsAdjudicationTicketDetails.roleReassignData, ); const reassignDataDictByIndividualId = {}; for (const key of Object.keys(reassignData)) { - reassignDataDictByIndividualId[reassignData[key].individual] = reassignData[key]; + reassignDataDictByIndividualId[reassignData[key].individual] = + reassignData[key]; } const selectedIndividualsToReassign = ticket.needsAdjudicationTicketDetails.selectedDuplicates?.filter( @@ -72,7 +77,9 @@ export function ReassignMultipleRoleBox({ <>{capitalize(householdAndRole.role)} Collector - + @@ -80,7 +87,9 @@ export function ReassignMultipleRoleBox({ {' '} {householdAndRole.individual.fullName} - + @@ -97,7 +106,11 @@ export function ReassignMultipleRoleBox({ ticket={ticket} household={householdAndRole.household} individualToReassign={selectedIndividualToReassign} - initialSelectedIndividualId={reassignDataDictByIndividualId[selectedIndividualToReassign.id].new_individual} + initialSelectedIndividualId={ + reassignDataDictByIndividualId[ + selectedIndividualToReassign.id + ].new_individual + } /> )); @@ -113,9 +126,13 @@ export function ReassignMultipleRoleBox({ - <>{t('Head of Household')} + <>{t(`Head of ${beneficiaryGroup?.groupLabel}`)} - + @@ -123,7 +140,11 @@ export function ReassignMultipleRoleBox({ {' '} {ticket.individual.fullName} - + @@ -137,7 +158,11 @@ export function ReassignMultipleRoleBox({ ticket={ticket} household={household} individualToReassign={selectedIndividualToReassign} - initialSelectedIndividualId={reassignDataDictByIndividualId[selectedIndividualToReassign.id].new_individual} + initialSelectedIndividualId={ + reassignDataDictByIndividualId[ + selectedIndividualToReassign.id + ].new_individual + } /> )} @@ -153,17 +178,19 @@ export function ReassignMultipleRoleBox({ - {t('Individual is the HOH or the collector for the household')} + {t( + `${beneficiaryGroup?.memberLabel} is the Head of ${beneficiaryGroup?.groupLabel} or the collector for the ${beneficiaryGroup?.groupLabel}`, + )} {t( - 'Upon changing you will need to select new individual(s) for this role.', + `Upon changing you will need to select new ${beneficiaryGroup?.memberLabelPlural} for this role.`, )} {t( - 'Upon removing you will need to select new individual(s) for this role.', + `Upon removing you will need to select new ${beneficiaryGroup?.memberLabelPlural} for this role.`, )} diff --git a/src/frontend/src/components/grievances/ReassignRoleBox.tsx b/src/frontend/src/components/grievances/ReassignRoleBox.tsx index fa567aae9b..d9a9d6a790 100644 --- a/src/frontend/src/components/grievances/ReassignRoleBox.tsx +++ b/src/frontend/src/components/grievances/ReassignRoleBox.tsx @@ -11,6 +11,7 @@ import { ContentLink } from '@core/ContentLink'; import { LabelizedField } from '@core/LabelizedField'; import { LookUpReassignRole } from './LookUps/LookUpReassignRole/LookUpReassignRole'; import { ReassignRoleUnique } from './LookUps/LookUpReassignRole/ReassignRoleUnique'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const StyledBox = styled(Paper)` @@ -44,6 +45,9 @@ export const ReassignRoleBox = ({ }): ReactElement => { const { t } = useTranslation(); const { baseUrl } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + let { individual } = ticket; let { household } = ticket; let reassignData; @@ -88,7 +92,8 @@ export const ReassignRoleBox = ({ } const reassignDataDictByIndividualId = {}; for (const key of Object.keys(reassignData)) { - reassignDataDictByIndividualId[reassignData[key].individual] = reassignData[key]; + reassignDataDictByIndividualId[reassignData[key].individual] = + reassignData[key]; } const mappedLookUpsForExternalHouseholds = householdsAndRoles .filter((el) => el.role !== 'NO_ROLE') @@ -98,7 +103,7 @@ export const ReassignRoleBox = ({ <>{capitalize(el.role)} Collector - + @@ -149,7 +154,7 @@ export const ReassignRoleBox = ({ return ( {t( - 'Upon removing you will need to select new individual(s) for this role.', + `Upon removing you will need to select new ${beneficiaryGroup?.memberLabelPlural} for this role.`, )} ); @@ -162,7 +167,9 @@ export const ReassignRoleBox = ({ - {t('Individual is the HOH or the collector for the household')} + {t( + `${beneficiaryGroup?.memberLabel} is the Head of Household or the collector for the ${beneficiaryGroup?.groupLabel}`, + )}{' '} {showMessage()} @@ -171,9 +178,9 @@ export const ReassignRoleBox = ({ - <>{t('Head of Household')} + <>{t(`Head of ${beneficiaryGroup?.groupLabel}`)} - + diff --git a/src/frontend/src/components/grievances/utils/createGrievanceUtils.ts b/src/frontend/src/components/grievances/utils/createGrievanceUtils.ts index 6bc5aa401b..ce93ecb49e 100644 --- a/src/frontend/src/components/grievances/utils/createGrievanceUtils.ts +++ b/src/frontend/src/components/grievances/utils/createGrievanceUtils.ts @@ -3,6 +3,12 @@ import { GRIEVANCE_CATEGORIES, GRIEVANCE_ISSUE_TYPES } from '@utils/constants'; import { thingForSpecificGrievanceType } from '@utils/utils'; import { removeIdPropertyFromObjects } from './helpers'; +export const replaceLabels = (text, _beneficiaryGroup) => { + return text + .replace(/Individual/g, _beneficiaryGroup.memberLabel) + .replace(/Household/g, _beneficiaryGroup.groupLabel); +}; + export const selectedIssueType = ( formValues, grievanceTicketIssueTypeChoices, diff --git a/src/frontend/src/components/grievances/utils/validateGrievance.ts b/src/frontend/src/components/grievances/utils/validateGrievance.ts index eb3063cc1c..6b4ca38fcd 100644 --- a/src/frontend/src/components/grievances/utils/validateGrievance.ts +++ b/src/frontend/src/components/grievances/utils/validateGrievance.ts @@ -18,6 +18,7 @@ export function validate( allAddIndividualFieldsData: AllAddIndividualFieldsQuery, individualFieldsDict, householdFieldsDict, + beneficiaryGroup, ) { const category = values.category?.toString(); const issueType = values.issueType?.toString(); @@ -25,7 +26,7 @@ export function validate( if (category === GRIEVANCE_CATEGORIES.DATA_CHANGE) { if (issueType === GRIEVANCE_ISSUE_TYPES.ADD_INDIVIDUAL) { if (!values.selectedHousehold) { - errors.selectedHousehold = 'Household is Required'; + errors.selectedHousehold = `${beneficiaryGroup?.groupLabel} is Required`; } if (values.individualData?.documents?.length) { values.individualData.documents @@ -40,14 +41,14 @@ export function validate( } if (issueType === GRIEVANCE_ISSUE_TYPES.EDIT_HOUSEHOLD) { if (!values.selectedHousehold) { - errors.selectedHousehold = 'Household is Required'; + errors.selectedHousehold = `${beneficiaryGroup?.groupLabel} is Required`; } if ( // xD values.selectedHousehold && !values.householdDataUpdateFields?.[0]?.fieldName ) { - errors.householdDataUpdateFields = 'Household Data Change is Required'; + errors.householdDataUpdateFields = `${beneficiaryGroup?.groupLabel} Data Change is Required`; } if ( values.householdDataUpdateFields?.length && @@ -68,17 +69,17 @@ export function validate( } if (issueType === GRIEVANCE_ISSUE_TYPES.DELETE_INDIVIDUAL) { if (!values.selectedIndividual) { - errors.selectedIndividual = 'Individual is Required'; + errors.selectedIndividual = `${beneficiaryGroup?.memberLabel} is Required`; } } if (issueType === GRIEVANCE_ISSUE_TYPES.DELETE_HOUSEHOLD) { if (!values.selectedHousehold) { - errors.selectedHousehold = 'Household is Required'; + errors.selectedHousehold = `${beneficiaryGroup?.groupLabel} is Required`; } } if (issueType === GRIEVANCE_ISSUE_TYPES.EDIT_INDIVIDUAL) { if (!values.selectedIndividual) { - errors.selectedIndividual = 'Individual is Required'; + errors.selectedIndividual = `${beneficiaryGroup?.memberLabel} is Required`; } if ( values.selectedIndividual && @@ -94,8 +95,7 @@ export function validate( !values.individualDataUpdatePaymentChannelsToEdit?.length && !values.individualDataUpdateDeliveryMechanismDataToEdit?.length ) { - errors.individualDataUpdateFields = - 'Individual Data Change is Required'; + errors.individualDataUpdateFields = `${beneficiaryGroup?.memberLabel} Data Change is Required`; } if (values.individualDataUpdateFields?.length) { values.individualDataUpdateFields.forEach((el) => { @@ -240,6 +240,7 @@ export function validateUsingSteps( householdFieldsDict, activeStep, setValidateData, + beneficiaryGroup, ) { const category = values.category?.toString(); const issueType = values.issueType?.toString(); @@ -270,12 +271,12 @@ export function validateUsingSteps( if (category === GRIEVANCE_CATEGORIES.DATA_CHANGE) { if (issueType === GRIEVANCE_ISSUE_TYPES.ADD_INDIVIDUAL) { if (!values.selectedHousehold && activeStep === GrievanceSteps.Lookup) { - errors.selectedHousehold = 'Household is Required'; + errors.selectedHousehold = `${beneficiaryGroup?.groupLabel} is Required`; } } if (issueType === GRIEVANCE_ISSUE_TYPES.EDIT_HOUSEHOLD) { if (!values.selectedHousehold && activeStep === GrievanceSteps.Lookup) { - errors.selectedHousehold = 'Household is Required'; + errors.selectedHousehold = `${beneficiaryGroup?.groupLabel} is Required`; } if ( // xD @@ -283,7 +284,7 @@ export function validateUsingSteps( !values.householdDataUpdateFields?.[0]?.fieldName && activeStep === GrievanceSteps.Description ) { - errors.householdDataUpdateFields = 'Household Data Change is Required'; + errors.householdDataUpdateFields = `${beneficiaryGroup?.groupLabel} Data Change is Required`; } if ( values.householdDataUpdateFields?.length && @@ -304,12 +305,12 @@ export function validateUsingSteps( } if (issueType === GRIEVANCE_ISSUE_TYPES.DELETE_INDIVIDUAL) { if (!values.selectedIndividual && activeStep === GrievanceSteps.Lookup) { - errors.selectedIndividual = 'Individual is Required'; + errors.selectedIndividual = `${beneficiaryGroup?.memberLabel} is Required`; } } if (issueType === GRIEVANCE_ISSUE_TYPES.DELETE_HOUSEHOLD) { if (!values.selectedHousehold && activeStep === GrievanceSteps.Lookup) { - errors.selectedHousehold = 'Household is Required'; + errors.selectedHousehold = `${beneficiaryGroup?.groupLabel} is Required`; } } if (issueType === GRIEVANCE_ISSUE_TYPES.EDIT_INDIVIDUAL) { @@ -328,8 +329,7 @@ export function validateUsingSteps( !values.individualDataUpdatePaymentChannelsToEdit?.length && activeStep === GrievanceSteps.Description ) { - errors.individualDataUpdateFields = - 'Individual Data Change is Required'; + errors.individualDataUpdateFields = `${beneficiaryGroup?.memberLabel} Data Change is Required`; } if (values.individualDataUpdateFields?.length) { values.individualDataUpdateFields.forEach((el) => { @@ -470,7 +470,7 @@ export function validateUsingSteps( !values.selectedHousehold && householdRequiredGrievanceTypes.includes(values.issueType) ) { - errors.selectedHousehold = 'Household is Required'; + errors.selectedHousehold = `${beneficiaryGroup?.groupLabel} is Required`; } if (activeStep === GrievanceSteps.Lookup) { const individualRequiredIssueTypes = [ @@ -488,9 +488,9 @@ export function validateUsingSteps( values.issueType, ); if (isIndividualRequired && !values.selectedIndividual) { - errors.selectedIndividual = 'Individual is Required'; + errors.selectedIndividual = `${beneficiaryGroup?.memberLabel} is Required`; } else if (isHouseholdRequired && !values.selectedHousehold) { - errors.selectedHousehold = 'Household is Required'; + errors.selectedHousehold = `${beneficiaryGroup?.groupLabel} is Required`; } } if ( diff --git a/src/frontend/src/components/paymentmodule/CreateFollowUpPaymentPlan/CreateFollowUpPaymentPlan.tsx b/src/frontend/src/components/paymentmodule/CreateFollowUpPaymentPlan/CreateFollowUpPaymentPlan.tsx index 5127cd885f..a57aedca4b 100644 --- a/src/frontend/src/components/paymentmodule/CreateFollowUpPaymentPlan/CreateFollowUpPaymentPlan.tsx +++ b/src/frontend/src/components/paymentmodule/CreateFollowUpPaymentPlan/CreateFollowUpPaymentPlan.tsx @@ -50,6 +50,8 @@ export function CreateFollowUpPaymentPlan({ const [mutate, { loading }] = useCreateFollowUpPpMutation(); const { isActiveProgram } = useProgramContext(); const { showMessage } = useSnackbar(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { id, totalWithdrawnHouseholdsCount, unsuccessfulPaymentsCount } = paymentPlan; @@ -154,7 +156,7 @@ export function CreateFollowUpPaymentPlan({ {t( - 'Withdrawn Household cannot be added into follow-up payment plan', + `Withdrawn ${beneficiaryGroup?.groupLabel} cannot be added into follow-up payment plan`, )} @@ -189,7 +191,11 @@ export function CreateFollowUpPaymentPlan({ */} - + {totalWithdrawnHouseholdsCount} diff --git a/src/frontend/src/components/paymentmodule/PaymentDetails/PaymentDetails.tsx b/src/frontend/src/components/paymentmodule/PaymentDetails/PaymentDetails.tsx index fe8806b7d6..726e9f1938 100644 --- a/src/frontend/src/components/paymentmodule/PaymentDetails/PaymentDetails.tsx +++ b/src/frontend/src/components/paymentmodule/PaymentDetails/PaymentDetails.tsx @@ -1,13 +1,18 @@ -import { Grid, Paper, Typography } from '@mui/material'; -import { useTranslation } from 'react-i18next'; -import styled from 'styled-components'; +import { UniversalActivityLogTable } from '@containers/tables/UniversalActivityLogTable'; +import { BlackLink } from '@core/BlackLink'; +import { ContainerColumnWithBorder } from '@core/ContainerColumnWithBorder'; +import { DividerLine } from '@core/DividerLine'; +import { LabelizedField } from '@core/LabelizedField'; +import { StatusBox } from '@core/StatusBox'; +import { Title } from '@core/Title'; +import { UniversalMoment } from '@core/UniversalMoment'; import { PaymentQuery, PaymentStatus, PaymentVerificationStatus, } from '@generated/graphql'; -import { UniversalActivityLogTable } from '@containers/tables/UniversalActivityLogTable'; -import { useBusinessArea } from '@hooks/useBusinessArea'; +import { useBaseUrl } from '@hooks/useBaseUrl'; +import { Grid, Paper, Typography } from '@mui/material'; import { formatCurrencyWithSymbol, getPhoneNoLabel, @@ -15,15 +20,10 @@ import { paymentStatusToColor, verificationRecordsStatusToColor, } from '@utils/utils'; -import { BlackLink } from '@core/BlackLink'; -import { ContainerColumnWithBorder } from '@core/ContainerColumnWithBorder'; -import { DividerLine } from '@core/DividerLine'; -import { LabelizedField } from '@core/LabelizedField'; -import { StatusBox } from '@core/StatusBox'; -import { Title } from '@core/Title'; -import { UniversalMoment } from '@core/UniversalMoment'; -import { useBaseUrl } from '@hooks/useBaseUrl'; import { ReactElement } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useProgramContext } from 'src/programContext'; +import styled from 'styled-components'; const Overview = styled(Paper)` margin: 20px; @@ -42,9 +42,10 @@ export function PaymentDetails({ canViewActivityLog, canViewHouseholdDetails, }: PaymentDetailsProps): ReactElement { - const businessArea = useBusinessArea(); const { t } = useTranslation(); - const { programId } = useBaseUrl(); + const { businessArea, programId } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; let paymentVerification: PaymentQuery['payment']['verification'] = null; if ( @@ -149,11 +150,11 @@ export function PaymentDetails({ ) : null} - <Typography variant="h6">{t('Household')}</Typography> + <Typography variant="h6">{beneficiaryGroup?.groupLabel}</Typography> - + {payment.household?.id && canViewHouseholdDetails ? ( el.unicefId, @@ -65,9 +67,7 @@ export function ExcludeSection({ const getTooltipText = (): string => { if (!hasOpenOrLockedStatus) { - return t( - 'Households can only be excluded from a Payment Plan in status open or locked', - ); + return `${beneficiaryGroup?.groupLabelPlural} can only be excluded from a Payment Plan in status open or locked`; } if (!hasExcludePermission) { return t('Permission denied'); @@ -114,7 +114,7 @@ export function ExcludeSection({ awaitRefetchQueries: true, }); if (!error) { - showMessage(t('Households exclusion started')); + showMessage(`${beneficiaryGroup?.groupLabelPlural} exclusion started`); setExclusionsOpen(false); } } catch (e) { @@ -173,7 +173,7 @@ export function ExcludeSection({ const numberOfExcluded = excludedIds.length - deletedIds.length; - const renderButtons = (submitForm, values, resetForm): ReactElement => { + const renderButtons = (submitForm, _values, resetForm): ReactElement => { const noExclusions = numberOfExcluded === 0; const editMode = isExclusionsOpen && isEdit; const previewMode = @@ -310,7 +310,7 @@ export function ExcludeSection({ {`${numberOfExcluded} ${ - numberOfExcluded === 1 ? 'Household' : 'Households' + numberOfExcluded === 1 + ? `${beneficiaryGroup?.groupLabel}` + : `${beneficiaryGroup?.groupLabelPlural}` } excluded`} diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetailsHeader/LockPaymentPlan.tsx b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetailsHeader/LockPaymentPlan.tsx index 0d3bf49b87..ed479dc378 100644 --- a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetailsHeader/LockPaymentPlan.tsx +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetailsHeader/LockPaymentPlan.tsx @@ -16,6 +16,7 @@ import { useSnackbar } from '@hooks/useSnackBar'; import { Action, PaymentPlanQuery } from '@generated/graphql'; import { GreyText } from '@core/GreyText'; import { LoadingButton } from '@core/LoadingButton'; +import { useProgramContext } from 'src/programContext'; export interface LockPaymentPlanProps { paymentPlan: PaymentPlanQuery['paymentPlan']; @@ -26,6 +27,8 @@ export function LockPaymentPlan({ }: LockPaymentPlanProps): ReactElement { const { t } = useTranslation(); const { showMessage } = useSnackbar(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const [lockDialogOpen, setLockDialogOpen] = useState(false); const { mutatePaymentPlanAction: lock, loading: loadingLock } = usePaymentPlanAction( @@ -73,8 +76,8 @@ export function LockPaymentPlan({ : t('There are')}{' '} {paymentPlan.paymentsConflictsCount}{' '} {paymentPlan.paymentsConflictsCount === 1 - ? t('household') - : t('households')}{' '} + ? t(beneficiaryGroup?.groupLabel) + : t(beneficiaryGroup?.groupLabelPlural)}{' '} {t('that will be ignored in this Payment Plan.')} diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetailsResults/ResultsForHouseholds.tsx b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetailsResults/ResultsForHouseholds.tsx index 483afbb244..a8a6f869f4 100644 --- a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetailsResults/ResultsForHouseholds.tsx +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetailsResults/ResultsForHouseholds.tsx @@ -1,4 +1,3 @@ -import { useTranslation } from 'react-i18next'; import { Grid } from '@mui/material'; import { SummaryBorder, @@ -6,6 +5,7 @@ import { } from '@components/paymentmodule/PaymentPlanDetails/PaymentPlanDetailsResults/Styles'; import { LabelizedField } from '@core/LabelizedField'; import { PaymentPlanQuery } from '@generated/graphql'; +import { useProgramContext } from 'src/programContext'; interface ResultsForHouseholdsProps { paymentPlan: PaymentPlanQuery['paymentPlan']; @@ -14,22 +14,27 @@ interface ResultsForHouseholdsProps { export const ResultsForHouseholds = ({ paymentPlan, }: ResultsForHouseholdsProps) => { - const { t } = useTranslation(); const { totalHouseholdsCount, totalIndividualsCount } = paymentPlan; + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; return ( - + {totalHouseholdsCount || '0'} - + {totalIndividualsCount || '0'} diff --git a/src/frontend/src/components/paymentmodulepeople/CreateFollowUpPaymentPlan/CreateFollowUpPaymentPlan.tsx b/src/frontend/src/components/paymentmodulepeople/CreateFollowUpPaymentPlan/CreateFollowUpPaymentPlan.tsx index 5127cd885f..dc85c22a8a 100644 --- a/src/frontend/src/components/paymentmodulepeople/CreateFollowUpPaymentPlan/CreateFollowUpPaymentPlan.tsx +++ b/src/frontend/src/components/paymentmodulepeople/CreateFollowUpPaymentPlan/CreateFollowUpPaymentPlan.tsx @@ -48,8 +48,9 @@ export function CreateFollowUpPaymentPlan({ const { baseUrl } = useBaseUrl(); const permissions = usePermissions(); const [mutate, { loading }] = useCreateFollowUpPpMutation(); - const { isActiveProgram } = useProgramContext(); + const { isActiveProgram, selectedProgram } = useProgramContext(); const { showMessage } = useSnackbar(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { id, totalWithdrawnHouseholdsCount, unsuccessfulPaymentsCount } = paymentPlan; @@ -154,7 +155,7 @@ export function CreateFollowUpPaymentPlan({ {t( - 'Withdrawn Household cannot be added into follow-up payment plan', + `Withdrawn ${beneficiaryGroup?.groupLabel} cannot be added into follow-up payment plan`, )} @@ -189,7 +190,12 @@ export function CreateFollowUpPaymentPlan({ */} - + + {' '} {totalWithdrawnHouseholdsCount} diff --git a/src/frontend/src/components/paymentmodulepeople/PaymentDetails/PaymentDetails.tsx b/src/frontend/src/components/paymentmodulepeople/PaymentDetails/PaymentDetails.tsx index fc5624cab3..5ac343ee26 100644 --- a/src/frontend/src/components/paymentmodulepeople/PaymentDetails/PaymentDetails.tsx +++ b/src/frontend/src/components/paymentmodulepeople/PaymentDetails/PaymentDetails.tsx @@ -23,6 +23,7 @@ import { StatusBox } from '@core/StatusBox'; import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const Overview = styled(Paper)` @@ -45,6 +46,8 @@ export function PaymentDetails({ const businessArea = useBusinessArea(); const { t } = useTranslation(); const { programId } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; let paymentVerification: PaymentQuery['payment']['verification'] = null; if ( @@ -149,11 +152,11 @@ export function PaymentDetails({ ) : null} - <Typography variant="h6">{t('Household')}</Typography> + <Typography variant="h6">{beneficiaryGroup?.groupLabel}</Typography> - + {payment.household?.id && canViewHouseholdDetails ? ( { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + return ( - <Typography variant="h6">{t('Household')}</Typography> + <Typography variant="h6">{beneficiaryGroup?.groupLabel}</Typography> diff --git a/src/frontend/src/components/payments/IndividualDetails.tsx b/src/frontend/src/components/payments/IndividualDetails.tsx index 53da9bccb0..3fdb893fbb 100644 --- a/src/frontend/src/components/payments/IndividualDetails.tsx +++ b/src/frontend/src/components/payments/IndividualDetails.tsx @@ -4,24 +4,33 @@ import { Title } from '@core/Title'; import { Grid, Typography } from '@mui/material'; import { LabelizedField } from '@core/LabelizedField'; import { getPhoneNoLabel } from '@utils/utils'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; export const IndividualDetails = ({ individual }): ReactElement => { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + return ( - <Typography variant="h6">{t('Individual')}</Typography> + <Typography variant="h6"> + {t(`${beneficiaryGroup?.memberLabel}`)} + </Typography>{' '} - + @@ -74,7 +76,7 @@ export function HouseholdDetails({ - + {household?.size} @@ -84,7 +86,7 @@ export function HouseholdDetails({ - + @@ -93,12 +95,16 @@ export function HouseholdDetails({ - + {household?.fchildHoh ? t('Yes') : t('No')} - + {household?.childHoh ? t('Yes') : t('No')} @@ -171,7 +177,11 @@ export function HouseholdDetails({ - + {household?.returnee ? t('Yes') : t('No')} @@ -185,6 +195,16 @@ export function HouseholdDetails({ /> )} + + {household?.unicefId && ( + + )} + {selectedProgram?.dataCollectingType?.label} diff --git a/src/frontend/src/components/population/HouseholdDetails/__snapshots__/HouseholdDetails.test.tsx.snap b/src/frontend/src/components/population/HouseholdDetails/__snapshots__/HouseholdDetails.test.tsx.snap index 7be85f37d5..69a1810f18 100644 --- a/src/frontend/src/components/population/HouseholdDetails/__snapshots__/HouseholdDetails.test.tsx.snap +++ b/src/frontend/src/components/population/HouseholdDetails/__snapshots__/HouseholdDetails.test.tsx.snap @@ -99,10 +99,10 @@ exports[`components/population/HouseholdDetails should render 1`] = ` class="sc-gtLWhw bkWrmx" color="textSecondary" > - FEMALE CHILD HEADED HOUSEHOLD + FEMALE CHILD HEADED Household
- CHILD HEADED HOUSEHOLD + CHILD HEADED Household
- IS THIS A RETURNEE HOUSEHOLD? + IS THIS A RETURNEE Household?
+
+
+ + Linked Grievances + +
+ + + - + + +
+
+
diff --git a/src/frontend/src/components/population/HouseholdFilter.tsx b/src/frontend/src/components/population/HouseholdFilter.tsx index e55f2b11df..0fc7e70687 100644 --- a/src/frontend/src/components/population/HouseholdFilter.tsx +++ b/src/frontend/src/components/population/HouseholdFilter.tsx @@ -7,14 +7,15 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { HouseholdChoiceDataQuery, ProgramNode } from '@generated/graphql'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { AdminAreaAutocomplete } from '@shared/autocompletes/AdminAreaAutocomplete'; -import { householdTableOrderOptions } from '@utils/constants'; import { createHandleApplyFilterChange } from '@utils/utils'; import { FiltersSection } from '@core/FiltersSection'; import { NumberTextField } from '@core/NumberTextField'; import { SearchTextField } from '@core/SearchTextField'; import { SelectFilter } from '@core/SelectFilter'; import { DocumentSearchField } from '@core/DocumentSearchField'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; +import { generateTableOrderOptionsGroup } from '@utils/constants'; interface HouseholdFiltersProps { filter; @@ -38,6 +39,8 @@ export function HouseholdFilters({ isOnPaper = true, }: HouseholdFiltersProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const navigate = useNavigate(); const location = useLocation(); const { isAllPrograms } = useBaseUrl(); @@ -59,6 +62,9 @@ export function HouseholdFilters({ clearFilter(); }; + const householdTableOrderOptions = + generateTableOrderOptionsGroup(beneficiaryGroup); + return ( } data-cy="hh-filters-program" > - {programs.map((program) => ( + {programs?.map((program) => ( {program.name} @@ -118,7 +124,7 @@ export function HouseholdFilters({ } @@ -149,7 +155,7 @@ export function HouseholdFilters({ data-cy="hh-filters-order-by" disableClearable > - {householdTableOrderOptions.map((order) => ( + {householdTableOrderOptions?.map((order) => ( {order.name} diff --git a/src/frontend/src/components/population/IndividualBioData/IndividualBioData.tsx b/src/frontend/src/components/population/IndividualBioData/IndividualBioData.tsx index de74d2c410..ea53e8ad20 100644 --- a/src/frontend/src/components/population/IndividualBioData/IndividualBioData.tsx +++ b/src/frontend/src/components/population/IndividualBioData/IndividualBioData.tsx @@ -20,6 +20,7 @@ import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; import { DocumentPopulationPhotoModal } from '../DocumentPopulationPhotoModal'; import { LinkedGrievancesModal } from '../LinkedGrievancesModal/LinkedGrievancesModal'; +import { useProgramContext } from 'src/programContext'; import { ReactElement, ReactNode } from 'react'; const Overview = styled(Paper)` @@ -46,6 +47,9 @@ export const IndividualBioData = ({ grievancesChoices, }: IndividualBioDataProps): ReactElement => { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const relationshipChoicesDict = choicesToDict( choicesData.relationshipChoices, ); @@ -99,7 +103,7 @@ export const IndividualBioData = ({ const mappedRoles = ( - + {individual?.householdsAndRoles?.length ? individual?.householdsAndRoles?.map((item) => ( @@ -231,7 +235,7 @@ export const IndividualBioData = ({ - + {individual?.household?.id ? ( - + {relationshipChoicesDict[individual?.relationship]} diff --git a/src/frontend/src/components/population/IndividualBioData/__snapshots__/IndividualBioData.test.tsx.snap b/src/frontend/src/components/population/IndividualBioData/__snapshots__/IndividualBioData.test.tsx.snap index c67373dc40..6f68f01019 100644 --- a/src/frontend/src/components/population/IndividualBioData/__snapshots__/IndividualBioData.test.tsx.snap +++ b/src/frontend/src/components/population/IndividualBioData/__snapshots__/IndividualBioData.test.tsx.snap @@ -315,10 +315,10 @@ exports[`components/population/IndividualBioData should render 1`] = ` class="sc-gtLWhw bkWrmx" color="textSecondary" > - Relationship to HOH + Relationship to Head Of Household
<Typography variant="h6"> - {t('Individual Delivery Mechanisms')} + {t(`${beneficiaryGroup?.memberLabel} Delivery Mechanisms`)} </Typography> diff --git a/src/frontend/src/components/population/IndividualsFilter.tsx b/src/frontend/src/components/population/IndividualsFilter.tsx index 38acef7444..a156f6cfb1 100644 --- a/src/frontend/src/components/population/IndividualsFilter.tsx +++ b/src/frontend/src/components/population/IndividualsFilter.tsx @@ -10,7 +10,7 @@ import { } from '@generated/graphql'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { AdminAreaAutocomplete } from '@shared/autocompletes/AdminAreaAutocomplete'; -import { individualTableOrderOptions } from '@utils/constants'; +import { generateTableOrderOptionsMember } from '@utils/constants'; import { createHandleApplyFilterChange } from '@utils/utils'; import { DatePickerFilter } from '@core/DatePickerFilter'; import { FiltersSection } from '@core/FiltersSection'; @@ -47,6 +47,7 @@ export function IndividualsFilter({ const location = useLocation(); const { isAllPrograms } = useBaseUrl(); const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { handleFilterChange, applyFilterChanges, clearFilter } = createHandleApplyFilterChange( @@ -71,6 +72,9 @@ export function IndividualsFilter({ selectedProgram?.dataCollectingType?.type?.toUpperCase() === DataCollectingTypeType.Social; + const individualTableOrderOptions = + generateTableOrderOptionsMember(beneficiaryGroup); + return ( } data-cy="filters-program" > - {programs.map((program) => ( + {programs?.map((program) => ( {program.name} diff --git a/src/frontend/src/components/population/LinkedGrievancesModal/LinkedGrievancesModal.tsx b/src/frontend/src/components/population/LinkedGrievancesModal/LinkedGrievancesModal.tsx index ca06995f22..0aa8294a71 100644 --- a/src/frontend/src/components/population/LinkedGrievancesModal/LinkedGrievancesModal.tsx +++ b/src/frontend/src/components/population/LinkedGrievancesModal/LinkedGrievancesModal.tsx @@ -31,6 +31,7 @@ import { StatusBox } from '@core/StatusBox'; import { ClickableTableRow } from '@core/Table/ClickableTableRow'; import { getGrievanceDetailsPath } from '../../grievances/utils/createGrievanceUtils'; import { Bold } from '@components/core/Bold'; +import { useProgramContext } from 'src/programContext'; export const StyledLink = styled.div` color: #000; @@ -62,6 +63,8 @@ export function LinkedGrievancesModal({ const [dialogOpen, setDialogOpen] = useState(false); const navigate = useNavigate(); const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { data: grievances } = useAllGrievanceTicketQuery({ variables: { businessArea, household: household.unicefId }, @@ -173,8 +176,7 @@ export function LinkedGrievancesModal({ - Household ID - {household.unicefId} + {beneficiaryGroup?.groupLabel} ID {household.unicefId} is linked to following Grievances. diff --git a/src/frontend/src/components/programs/CreateProgram/DetailsStep.tsx b/src/frontend/src/components/programs/CreateProgram/DetailsStep.tsx index 2ea93c2aef..5c93699c6a 100644 --- a/src/frontend/src/components/programs/CreateProgram/DetailsStep.tsx +++ b/src/frontend/src/components/programs/CreateProgram/DetailsStep.tsx @@ -10,6 +10,7 @@ interface DetailsStepProps { handleNext?: () => Promise; errors: any; programId?: string; + programHasRdi?: boolean; } export const DetailsStep: FC = ({ @@ -17,6 +18,7 @@ export const DetailsStep: FC = ({ handleNext, errors, programId: formProgramId, + programHasRdi, }) => { const { t } = useTranslation(); const { businessArea, programId, baseUrl } = useBaseUrl(); @@ -29,7 +31,7 @@ export const DetailsStep: FC = ({ return ( <> - +
+
+
+ + Beneficiary Group + +
+ - +
+
+
diff --git a/src/frontend/src/components/rdi/RegistrationFilters.tsx b/src/frontend/src/components/rdi/RegistrationFilters.tsx index a9cb4fe438..1331e0ad3a 100644 --- a/src/frontend/src/components/rdi/RegistrationFilters.tsx +++ b/src/frontend/src/components/rdi/RegistrationFilters.tsx @@ -10,6 +10,7 @@ import { NumberTextField } from '@core/NumberTextField'; import { SearchTextField } from '@core/SearchTextField'; import { SelectFilter } from '@core/SelectFilter'; import { FiltersSection } from '@core/FiltersSection'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; interface RegistrationFiltersProps { @@ -29,6 +30,8 @@ export const RegistrationFilters = ({ }: RegistrationFiltersProps): ReactElement => { const navigate = useNavigate(); const location = useLocation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { handleFilterChange, applyFilterChanges, clearFilter } = createHandleApplyFilterChange( @@ -100,7 +103,7 @@ export const RegistrationFilters = ({ } diff --git a/src/frontend/src/components/rdi/create/ImportCounters.tsx b/src/frontend/src/components/rdi/create/ImportCounters.tsx index 75accf186c..46fdac4b33 100644 --- a/src/frontend/src/components/rdi/create/ImportCounters.tsx +++ b/src/frontend/src/components/rdi/create/ImportCounters.tsx @@ -1,6 +1,7 @@ import { useTranslation } from 'react-i18next'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { useProgramQuery } from '@generated/graphql'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; export interface ImportCountersPropTypes { @@ -14,6 +15,9 @@ export function ImportCounters({ }: ImportCountersPropTypes): ReactElement { const { t } = useTranslation(); const { programId } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const { data: programData } = useProgramQuery({ variables: { id: programId }, }); @@ -34,7 +38,7 @@ export function ImportCounters({
{numberOfHouseholds}{' '} {t( - 'Household' + `${beneficiaryGroup?.groupLabel}` .concat(numberOfHouseholds > 1 ? 's' : '') .concat(' available to import'), )} @@ -42,7 +46,7 @@ export function ImportCounters({
{numberOfIndividuals}{' '} {t( - 'Individual' + `${beneficiaryGroup?.memberLabel}` .concat(numberOfIndividuals > 1 ? 's' : '') .concat(' available to import'), )} diff --git a/src/frontend/src/components/rdi/create/programPopulation/CreateImportFromProgramPopulation.tsx b/src/frontend/src/components/rdi/create/programPopulation/CreateImportFromProgramPopulation.tsx index 8aea2eb8c2..3ee5d1f046 100644 --- a/src/frontend/src/components/rdi/create/programPopulation/CreateImportFromProgramPopulation.tsx +++ b/src/frontend/src/components/rdi/create/programPopulation/CreateImportFromProgramPopulation.tsx @@ -39,6 +39,7 @@ export const CreateImportFromProgramPopulationForm = ({ first: 100, businessArea, compatibleDct: true, + beneficiaryGroupMatch: true, }, fetchPolicy: 'network-only', }); diff --git a/src/frontend/src/components/rdi/details/DedupeResults.tsx b/src/frontend/src/components/rdi/details/DedupeResults.tsx index 9594e35f60..f0262f6dde 100644 --- a/src/frontend/src/components/rdi/details/DedupeResults.tsx +++ b/src/frontend/src/components/rdi/details/DedupeResults.tsx @@ -24,6 +24,7 @@ import { DialogFooter } from '@containers/dialogs/DialogFooter'; import { DialogTitleWrapper } from '@containers/dialogs/DialogTitleWrapper'; import { DialogDescription } from '@containers/dialogs/DialogDescription'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { useProgramContext } from 'src/programContext'; const Error = styled.span` color: ${({ theme }: { theme: MiśTheme }) => theme.hctPalette.red}; @@ -56,6 +57,8 @@ export function DedupeResults({ const { t } = useTranslation(); const [open, setOpen] = useState(false); const { baseUrl } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; function createData( hitId, @@ -136,7 +139,7 @@ export function DedupeResults({ - {t('Individual ID')} + {t(`${beneficiaryGroup?.memberLabel} ID`)} {t('Full Name')} {t('Age')} diff --git a/src/frontend/src/components/rdi/details/RegistrationDetails/RegistrationDetails.tsx b/src/frontend/src/components/rdi/details/RegistrationDetails/RegistrationDetails.tsx index ac11ee42bc..1c2e2cc139 100644 --- a/src/frontend/src/components/rdi/details/RegistrationDetails/RegistrationDetails.tsx +++ b/src/frontend/src/components/rdi/details/RegistrationDetails/RegistrationDetails.tsx @@ -17,6 +17,7 @@ import { } from '@generated/graphql'; import { DedupeBox, OptionType } from '../DedupeBox'; import { Title } from '@core/Title'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; export const BigValueContainer = styled.div` @@ -56,6 +57,9 @@ export function RegistrationDetails({ isSocialWorkerProgram, }: RegistrationDetailsProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const withinBatchOptions: OptionType[] = [ { name: 'Unique', @@ -112,7 +116,7 @@ export function RegistrationDetails({ {registration?.numberOfHouseholds} @@ -122,7 +126,7 @@ export function RegistrationDetails({ {registration?.numberOfIndividuals} diff --git a/src/frontend/src/components/rdi/details/RegistrationDetails/__snapshots__/RegistrationDetails.test.tsx.snap b/src/frontend/src/components/rdi/details/RegistrationDetails/__snapshots__/RegistrationDetails.test.tsx.snap index cea5d3369b..91aa7fc334 100644 --- a/src/frontend/src/components/rdi/details/RegistrationDetails/__snapshots__/RegistrationDetails.test.tsx.snap +++ b/src/frontend/src/components/rdi/details/RegistrationDetails/__snapshots__/RegistrationDetails.test.tsx.snap @@ -184,10 +184,10 @@ exports[`components/rdi/details/RegistrationDetails should render 1`] = ` class="sc-gtLWhw bkWrmx" color="textSecondary" > - Total Number of Registered Individuals + Total Number of Individuals
@@ -42,7 +44,7 @@ export function HouseholdDetails({ <Overview> <Grid container spacing={6}> <Grid item xs={3}> - <LabelizedField label={t('Household Size')}> + <LabelizedField label={`${beneficiaryGroup?.groupLabel} Size`}> {household.size} </LabelizedField> </Grid> @@ -62,7 +64,7 @@ export function HouseholdDetails({ </LabelizedField> </Grid> <Grid item xs={3}> - <LabelizedField label={t('Head of Household')}> + <LabelizedField label={`Head of ${beneficiaryGroup?.groupLabel}`}> <ContentLink href={`/${baseUrl}/registration-data-import/individual/${household.headOfHousehold.id}`} > diff --git a/src/frontend/src/components/rdi/details/individual/RegistrationIndividualBioData/RegistrationIndividualBioData.tsx b/src/frontend/src/components/rdi/details/individual/RegistrationIndividualBioData/RegistrationIndividualBioData.tsx index f4c6d7c058..5a127a05f7 100644 --- a/src/frontend/src/components/rdi/details/individual/RegistrationIndividualBioData/RegistrationIndividualBioData.tsx +++ b/src/frontend/src/components/rdi/details/individual/RegistrationIndividualBioData/RegistrationIndividualBioData.tsx @@ -18,6 +18,7 @@ import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; import { DocumentRegistrationPhotoModal } from '../DocumentRegistrationPhotoModal'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const Overview = styled(Paper)` @@ -40,6 +41,9 @@ export function RegistrationIndividualBioData({ }: RegistrationIndividualBioDataProps): ReactElement { const { baseUrl } = useBaseUrl(); const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const relationshipChoicesDict = choicesToDict( choicesData.relationshipChoices, ); @@ -150,7 +154,8 @@ export function RegistrationIndividualBioData({ </LabelizedField> </Grid> <Grid item xs={3}> - <LabelizedField label={t('Household ID')}> + <LabelizedField label={t(`${beneficiaryGroup?.groupLabel} ID`)}> + {' '} {individual?.household?.id ? ( <ContentLink href={`/${baseUrl}/registration-data-import/household/${individual?.household?.id}`} diff --git a/src/frontend/src/components/rdi/details/individual/RegistrationIndividualBioData/__snapshots__/RegistrationIndividualBioData.test.tsx.snap b/src/frontend/src/components/rdi/details/individual/RegistrationIndividualBioData/__snapshots__/RegistrationIndividualBioData.test.tsx.snap index 5bf71c0e9e..332291d84d 100644 --- a/src/frontend/src/components/rdi/details/individual/RegistrationIndividualBioData/__snapshots__/RegistrationIndividualBioData.test.tsx.snap +++ b/src/frontend/src/components/rdi/details/individual/RegistrationIndividualBioData/__snapshots__/RegistrationIndividualBioData.test.tsx.snap @@ -275,6 +275,7 @@ exports[`components/rdi/details/individual/RegistrationIndividualBioData should class="sc-egkSDF XYdBd" color="textSecondary" > + <a class="sc-blHHSb hLtlEI" href="/afghanistan/programs/UHJvZ3JhbU5vZGU6YzRkNTY1N2QtMWEyOS00NmUxLTgxOTAtZGY3Zjg1YTBkMmVm/registration-data-import/household/SG91c2Vob2xkTm9kZTowMTk1YTEzYi0wYThkLTRiYTEtYjM0OS00MDY4YWNmOTViNDE=" diff --git a/src/frontend/src/components/targeting/CreateTargetPopulation/EmptyTargetingCriteria.tsx b/src/frontend/src/components/targeting/CreateTargetPopulation/EmptyTargetingCriteria.tsx index d2464c727f..b0aafb0fd7 100644 --- a/src/frontend/src/components/targeting/CreateTargetPopulation/EmptyTargetingCriteria.tsx +++ b/src/frontend/src/components/targeting/CreateTargetPopulation/EmptyTargetingCriteria.tsx @@ -2,6 +2,7 @@ import { Typography } from '@mui/material'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; import { PaperContainer } from '../PaperContainer'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const Label = styled.p` @@ -10,10 +11,12 @@ const Label = styled.p` export function EmptyTargetingCriteria(): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; return ( <PaperContainer> <Typography variant="h6"> - {t('Target Population Entries (Households)')} + {t(`Target Population Entries (${beneficiaryGroup?.groupLabelPlural})`)} </Typography> <Label>{t('Add targeting criteria to see results.')}</Label> </PaperContainer> diff --git a/src/frontend/src/components/targeting/CreateTargetPopulation/Exclusions.tsx b/src/frontend/src/components/targeting/CreateTargetPopulation/Exclusions.tsx index 8bf6990b45..77daef5db1 100644 --- a/src/frontend/src/components/targeting/CreateTargetPopulation/Exclusions.tsx +++ b/src/frontend/src/components/targeting/CreateTargetPopulation/Exclusions.tsx @@ -16,15 +16,16 @@ export function Exclusions({ const [isExclusionsOpen, setExclusionsOpen] = useState(initialOpen); const { t } = useTranslation(); const { isSocialDctType } = useProgramContext(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + return ( <PaperContainer> <Box display="flex" justifyContent="space-between"> <Typography data-cy="title-excluded-entries" variant="h6"> {isSocialDctType ? t('Excluded Target Population Entries') - : t( - 'Excluded Target Population Entries (Households or Individuals)', - )} + : `Excluded Target Population Entries (${beneficiaryGroup?.groupLabelPlural} or ${beneficiaryGroup?.memberLabelPlural})`} </Typography> <Button variant="outlined" @@ -49,8 +50,10 @@ export function Exclusions({ variant="outlined" label={ isSocialDctType - ? t('Individual IDs to exclude') - : t('Household or Individual IDs to exclude') + ? t(`${beneficiaryGroup?.memberLabel} IDs to exclude`) + : t( + `${beneficiaryGroup?.groupLabel} or ${beneficiaryGroup?.memberLabel} IDs to exclude`, + ) } component={FormikTextField} /> diff --git a/src/frontend/src/components/targeting/EditTargetPopulation/EditTargetPopulation.tsx b/src/frontend/src/components/targeting/EditTargetPopulation/EditTargetPopulation.tsx index 7fe4e91e22..a860cf9ac2 100644 --- a/src/frontend/src/components/targeting/EditTargetPopulation/EditTargetPopulation.tsx +++ b/src/frontend/src/components/targeting/EditTargetPopulation/EditTargetPopulation.tsx @@ -57,14 +57,16 @@ export const EditTargetPopulation = ({ const [mutate, { loading }] = useUpdateTpMutation(); const { showMessage } = useSnackbar(); const { baseUrl } = useBaseUrl(); - const { isSocialDctType, isStandardDctType } = useProgramContext(); + const { selectedProgram, isSocialDctType, isStandardDctType } = + useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const handleValidate = (values): { targetingCriteria?: string } => { const { targetingCriteria, householdIds, individualIds } = values; const errors: { targetingCriteria?: string } = {}; if (!targetingCriteria.length && !householdIds && !individualIds) { errors.targetingCriteria = t( - 'You need to select at least one targeting criteria or individual ID or household ID', + `You need to select at least one targeting criteria or ${beneficiaryGroup?.memberLabel} ID or ${beneficiaryGroup?.groupLabel} ID`, ); } return errors; @@ -192,10 +194,14 @@ export const EditTargetPopulation = ({ data-cy="save-message-box" > <Typography style={{ color: '#b1b1b5' }} variant="h6"> - {t('Save to see the list of households')} + {t( + `Save to see the list of ${beneficiaryGroup?.groupLabelPlural}`, + )} </Typography> <Typography style={{ color: '#b1b1b5' }} variant="subtitle1"> - {t('List of households will be available after saving')} + {t( + `List of ${beneficiaryGroup?.groupLabelPlural} will be available after saving`, + )}{' '} </Typography> </Box> </Form> diff --git a/src/frontend/src/components/targeting/ResultsForHouseholds.tsx b/src/frontend/src/components/targeting/ResultsForHouseholds.tsx index 8987ceb780..6dab57e360 100644 --- a/src/frontend/src/components/targeting/ResultsForHouseholds.tsx +++ b/src/frontend/src/components/targeting/ResultsForHouseholds.tsx @@ -10,6 +10,7 @@ import { MiśTheme } from '../../theme'; import { FieldBorder } from '@core/FieldBorder'; import { LabelizedField } from '@core/LabelizedField'; import { PaperContainer } from './PaperContainer'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const colors = { @@ -57,6 +58,9 @@ export function ResultsForHouseholds({ targetPopulation, }: ResultsProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + if (targetPopulation.buildStatus !== TargetPopulationBuildStatus.Ok) { return null; } @@ -156,7 +160,9 @@ export function ResultsForHouseholds({ <Grid container spacing={0} justifyContent="flex-end"> <Grid item xs={6}> <SummaryBorder> - <LabelizedField label={t('Total Number of Households')}> + <LabelizedField + label={`Total Number of ${beneficiaryGroup?.groupLabelPlural}`} + > <SummaryValue data-cy="total-number-of-households-count"> {targetPopulation.totalHouseholdsCount || '0'} </SummaryValue> @@ -165,7 +171,9 @@ export function ResultsForHouseholds({ </Grid> <Grid item xs={6}> <SummaryBorder> - <LabelizedField label={t('Targeted Individuals')}> + <LabelizedField + label={`Targeted ${beneficiaryGroup?.memberLabelPlural}`} + > <SummaryValue> {targetPopulation.totalIndividualsCount || '0'} </SummaryValue> diff --git a/src/frontend/src/components/targeting/TargetPopulationCore.tsx b/src/frontend/src/components/targeting/TargetPopulationCore.tsx index 0942317575..98e05b4bfc 100644 --- a/src/frontend/src/components/targeting/TargetPopulationCore.tsx +++ b/src/frontend/src/components/targeting/TargetPopulationCore.tsx @@ -14,6 +14,7 @@ import { TargetingHouseholds } from './TargetingHouseholds'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { TargetPopulationPeopleTable } from '@containers/tables/targeting/TargetPopulationPeopleTable'; import { ResultsForPeople } from '@components/targeting/ResultsForPeople'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; import { AddFilterTargetingCriteriaDisplay } from './TargetingCriteriaDisplay/AddFilterTargetingCriteriaDisplay'; @@ -40,6 +41,9 @@ export const TargetPopulationCore = ({ }: TargetPopulationCoreProps): ReactElement => { const { t } = useTranslation(); const { businessArea } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + if (!targetPopulation) return null; const ResultComponent = targetPopulation.program.isSocialWorkerProgram @@ -75,8 +79,8 @@ export const TargetPopulationCore = ({ {t('Target Population is building')} </Typography> <Label> - Target population is processing, the list of households will be - available when the process is finished + {`Target population is processing, the list of ${beneficiaryGroup?.groupLabelPlural} will be + available when the process is finished`} </Label> </PaperContainer> ); @@ -103,7 +107,7 @@ export const TargetPopulationCore = ({ {isSocialDctType ? t('Excluded Target Population Entries') : t( - 'Excluded Target Population Entries (Households or Individuals)', + `Excluded Target Population Entries (${beneficiaryGroup?.groupLabelPlural} or ${beneficiaryGroup?.memberLabelPlural})`, )} </Typography> <Box mt={2}> diff --git a/src/frontend/src/components/targeting/TargetPopulationTableFilters.tsx b/src/frontend/src/components/targeting/TargetPopulationTableFilters.tsx index c51961864f..7cbb12c1a7 100644 --- a/src/frontend/src/components/targeting/TargetPopulationTableFilters.tsx +++ b/src/frontend/src/components/targeting/TargetPopulationTableFilters.tsx @@ -12,6 +12,7 @@ import { NumberTextField } from '@core/NumberTextField'; import { SearchTextField } from '@core/SearchTextField'; import { SelectFilter } from '@core/SelectFilter'; import { FiltersSection } from '@core/FiltersSection'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; interface TargetPopulationTableFiltersProps { @@ -32,6 +33,8 @@ export const TargetPopulationTableFilters = ({ const navigate = useNavigate(); const location = useLocation(); const isAccountability = location.pathname.includes('accountability'); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { handleFilterChange, applyFilterChanges, clearFilter } = createHandleApplyFilterChange( @@ -88,7 +91,7 @@ export const TargetPopulationTableFilters = ({ </Grid> <Grid item xs={3}> <NumberTextField - topLabel={t('Number of Households')} + topLabel={t(`Number of ${beneficiaryGroup?.groupLabelPlural}`)} value={filter.totalHouseholdsCountMin} placeholder={t('From')} onChange={(e) => diff --git a/src/frontend/src/components/targeting/TargetingCriteriaDisplay/Criteria.tsx b/src/frontend/src/components/targeting/TargetingCriteriaDisplay/Criteria.tsx index 251518f606..3b0ed4097d 100644 --- a/src/frontend/src/components/targeting/TargetingCriteriaDisplay/Criteria.tsx +++ b/src/frontend/src/components/targeting/TargetingCriteriaDisplay/Criteria.tsx @@ -23,6 +23,7 @@ import { BlueText } from '@components/grievances/LookUps/LookUpStyles'; import { ReactElement, useState } from 'react'; import { Fragment } from 'react/jsx-runtime'; import { t } from 'i18next'; +import { useProgramContext } from 'src/programContext'; interface CriteriaElementProps { alternative?: boolean; @@ -266,6 +267,8 @@ export function Criteria({ householdIds, individualIds, }: CriteriaProps): ReactElement { + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const [openHH, setOpenHH] = useState(false); const [openIND, setOpenIND] = useState(false); const [currentHouseholdIds, setCurrentHouseholdIds] = useState<string[]>([]); @@ -326,7 +329,7 @@ export function Criteria({ {householdIds && ( <div> <Typography data-cy="household-ids-modal-title" variant="body1"> - {t('Household IDs selected')}: + {t(`${beneficiaryGroup?.groupLabel} IDs selected`)}: </Typography> <BlueText onClick={() => handleOpenHouseholdIds(householdIds)} @@ -340,7 +343,7 @@ export function Criteria({ {individualIds && ( <div> <Typography data-cy="individual-ids-modal-title" variant="body1"> - {t('Individual IDs selected')}: + {t(`${beneficiaryGroup?.groupLabel} IDs selected`)}: </Typography> <BlueText onClick={() => handleOpenIndividualIds(individualIds)} @@ -408,7 +411,9 @@ export function Criteria({ </ButtonsContainer> )} <Dialog open={openHH} onClose={handleClose} maxWidth="sm" fullWidth> - <DialogTitle>{t('Selected Households')}</DialogTitle> + <DialogTitle> + {t(`Selected ${beneficiaryGroup?.groupLabelPlural}`)} + </DialogTitle> <DialogContent> <Table> <TableHead> diff --git a/src/frontend/src/components/targeting/TargetingCriteriaDisplay/ExcludeCheckboxes.tsx b/src/frontend/src/components/targeting/TargetingCriteriaDisplay/ExcludeCheckboxes.tsx index b0536ff30c..d877ba1d7f 100644 --- a/src/frontend/src/components/targeting/TargetingCriteriaDisplay/ExcludeCheckboxes.tsx +++ b/src/frontend/src/components/targeting/TargetingCriteriaDisplay/ExcludeCheckboxes.tsx @@ -4,6 +4,7 @@ import { Box, Checkbox, FormControlLabel, Grid } from '@mui/material'; import styled from 'styled-components'; import { FormikCheckboxField } from '@shared/Formik/FormikCheckboxField'; import { FC } from 'react'; +import { useProgramContext } from 'src/programContext'; const NoWrapCheckbox = styled(FormControlLabel)` white-space: nowrap; @@ -25,6 +26,8 @@ export const ExcludeCheckboxes: FC<ExcludeCheckboxesProps> = ({ targetPopulation, }) => { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; return ( <Box mt={3} p={3}> @@ -47,7 +50,7 @@ export const ExcludeCheckboxes: FC<ExcludeCheckboxesProps> = ({ /> } label={t( - 'Exclude Households with Active Adjudication Ticket', + `Exclude ${beneficiaryGroup?.groupLabelPlural} with Active Adjudication Ticket`, )} /> )} @@ -85,7 +88,7 @@ export const ExcludeCheckboxes: FC<ExcludeCheckboxesProps> = ({ ?.flagExcludeIfOnSanctionList, )} label={t( - 'Exclude People with an Active Sanction Screen Flag', + `Exclude ${beneficiaryGroup?.memberLabelPlural} with an Active Sanction Screen Flag`, )} /> )} @@ -104,7 +107,7 @@ export const ExcludeCheckboxes: FC<ExcludeCheckboxesProps> = ({ ?.flagExcludeIfOnSanctionList, )} label={t( - 'Exclude Households with an Active Sanction Screen Flag', + `Exclude ${beneficiaryGroup?.groupLabelPlural} with an Active Sanction Screen Flag`, )} /> )} @@ -119,7 +122,7 @@ export const ExcludeCheckboxes: FC<ExcludeCheckboxesProps> = ({ <Field name="flagExcludeIfActiveAdjudicationTicket" label={t( - 'Exclude Households with Active Adjudication Ticket', + `Exclude ${beneficiaryGroup?.groupLabelPlural} with Active Adjudication Ticket`, )} color="primary" component={FormikCheckboxField} @@ -131,7 +134,9 @@ export const ExcludeCheckboxes: FC<ExcludeCheckboxesProps> = ({ <Grid item xs={6}> <Field name="flagExcludeIfActiveAdjudicationTicket" - label={t('Exclude People with Active Adjudication Ticket')} + label={t( + `Exclude ${beneficiaryGroup?.memberLabelPlural} with Active Adjudication Ticket`, + )} color="primary" component={FormikCheckboxField} data-cy="input-active-adjudication-ticket" @@ -143,7 +148,7 @@ export const ExcludeCheckboxes: FC<ExcludeCheckboxesProps> = ({ <Field name="flagExcludeIfOnSanctionList" label={t( - 'Exclude Households with an Active Sanction Screen Flag', + `Exclude ${beneficiaryGroup?.groupLabelPlural} with an Active Sanction Screen Flag`, )} color="primary" component={FormikCheckboxField} @@ -156,7 +161,7 @@ export const ExcludeCheckboxes: FC<ExcludeCheckboxesProps> = ({ <Field name="flagExcludeIfOnSanctionList" label={t( - 'Exclude People with an Active Sanction Screen Flag', + `Exclude ${beneficiaryGroup?.memberLabelPlural} with an Active Sanction Screen Flag`, )} color="primary" component={FormikCheckboxField} diff --git a/src/frontend/src/containers/GlobalProgramSelect.tsx b/src/frontend/src/containers/GlobalProgramSelect.tsx index 7f778ca0b8..c48d801d13 100644 --- a/src/frontend/src/containers/GlobalProgramSelect.tsx +++ b/src/frontend/src/containers/GlobalProgramSelect.tsx @@ -155,6 +155,27 @@ export const GlobalProgramSelect = () => { }; }, []); + useEffect(() => { + // Initial setup for selectedProgram + setSelectedProgram({ + beneficiaryGroup: { + id: 'default-id', + name: 'Beneficiaries', + groupLabel: 'Group', + groupLabelPlural: 'Groups', + memberLabel: 'Member', + memberLabelPlural: 'Members', + masterDetail: false, + }, + id: 'all', + name: 'All Programmes', + status: ProgramStatus.Active, + dataCollectingType: null, + pduFields: null, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { if (programId !== 'all') { const program = programData?.program; @@ -163,7 +184,8 @@ export const GlobalProgramSelect = () => { isMounted.current && (!selectedProgram || selectedProgram?.id !== programId) ) { - const { id, name, status, dataCollectingType } = program; + const { id, name, status, dataCollectingType, beneficiaryGroup } = + program; setSelectedProgram({ id, @@ -182,10 +204,9 @@ export const GlobalProgramSelect = () => { // dataCollectingType?.collectorFieldsAvailable, }, pduFields: program.pduFields, + beneficiaryGroup: beneficiaryGroup, }); } - } else { - setSelectedProgram(null); } }, [programId, selectedProgram, setSelectedProgram, programData]); @@ -240,6 +261,22 @@ export const GlobalProgramSelect = () => { if (selectedValue) { handleClose(); if (selectedValue.id === 'all') { + setSelectedProgram({ + beneficiaryGroup: { + id: 'default-id', + name: 'Beneficiaries', + groupLabel: 'Group', + groupLabelPlural: 'Groups', + memberLabel: 'Member', + memberLabelPlural: 'Members', + masterDetail: false, + }, + id: 'all', + name: 'All Programmes', + status: ProgramStatus.Active, + dataCollectingType: null, + pduFields: null, + }); navigate(`/${businessArea}/programs/all/list`); } else { navigate( @@ -328,19 +365,22 @@ export const GlobalProgramSelect = () => { onChange={onChange} PopperComponent={PopperComponent} noOptionsText="No results" - renderOption={(props, option) => ( - <li {...props}> - <NameBox data-cy="select-option-name" title={option.name}> - {option.name} - </NameBox> - {option.status && ( - <StatusBox - status={option.status} - statusToColor={programStatusToColor} - /> - )} - </li> - )} + renderOption={(props, option) => { + const { key, ...restProps } = props; + return ( + <li key={key} {...restProps}> + <NameBox data-cy="select-option-name" title={option.name}> + {option.name} + </NameBox> + {option.status && ( + <StatusBox + status={option.status} + statusToColor={programStatusToColor} + /> + )} + </li> + ); + }} filterOptions={(x) => x} options={programs} getOptionLabel={(option) => option.name} diff --git a/src/frontend/src/containers/forms/ProgramForm.tsx b/src/frontend/src/containers/forms/ProgramForm.tsx index b698b0ef8d..1c24ca4f1d 100644 --- a/src/frontend/src/containers/forms/ProgramForm.tsx +++ b/src/frontend/src/containers/forms/ProgramForm.tsx @@ -1,7 +1,7 @@ import { Grid } from '@mui/material'; import CalendarTodayRoundedIcon from '@mui/icons-material/CalendarTodayRounded'; -import { Field, Form } from 'formik'; -import { ReactElement } from 'react'; +import { Field, Form, useFormikContext } from 'formik'; +import { ReactElement, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useDataCollectionTypeChoiceDataQuery, @@ -10,26 +10,58 @@ import { import { FormikCheckboxField } from '@shared/Formik/FormikCheckboxField'; import { FormikDateField } from '@shared/Formik/FormikDateField'; import { FormikRadioGroup } from '@shared/Formik/FormikRadioGroup'; +import { useQuery } from '@tanstack/react-query'; +import { fetchBeneficiaryGroups } from '@api/programsApi'; import { FormikSelectField } from '@shared/Formik/FormikSelectField'; import { FormikTextField } from '@shared/Formik/FormikTextField'; interface ProgramFormPropTypes { values; + programHasRdi?: boolean; } -export const ProgramForm = ({ values }: ProgramFormPropTypes): ReactElement => { +export const ProgramForm = ({ + values, + programHasRdi, +}: ProgramFormPropTypes): ReactElement => { const { t } = useTranslation(); const { data } = useProgrammeChoiceDataQuery(); const { data: dataCollectionTypeChoicesData } = useDataCollectionTypeChoiceDataQuery(); - if (!data || !dataCollectionTypeChoicesData) return null; + const { data: beneficiaryGroupsData } = useQuery({ + queryKey: ['beneficiaryGroups'], + queryFn: async () => fetchBeneficiaryGroups(), + }); + + const { setFieldValue } = useFormikContext(); const filteredDataCollectionTypeChoicesData = dataCollectionTypeChoicesData?.dataCollectionTypeChoices.filter( (el) => el.name !== '', ); + const mappedBeneficiaryGroupsData = useMemo(() => { + if (!beneficiaryGroupsData?.results) return []; + const filteredBeneficiaryGroups = + values.dataCollectingTypeCode === 'partial' + ? beneficiaryGroupsData.results.filter( + (el) => el.master_detail === false, + ) + : beneficiaryGroupsData.results.filter( + (el) => el.master_detail === true, + ); + + return filteredBeneficiaryGroups.map((el) => ({ + name: el.name, + value: el.id, + })); + }, [values.dataCollectingTypeCode, beneficiaryGroupsData]); + + const isCopyProgramPage = location.pathname.includes('duplicate'); + if (!data || !dataCollectionTypeChoicesData || !beneficiaryGroupsData) + return null; + return ( <Form> <Grid container spacing={3}> @@ -76,6 +108,7 @@ export const ProgramForm = ({ values }: ProgramFormPropTypes): ReactElement => { disabled={!values.startDate} initialFocusedDate={values.startDate} fullWidth + required={values.editMode} decoratorEnd={<CalendarTodayRoundedIcon color="disabled" />} minDate={values.startDate} data-cy="input-end-date" @@ -100,12 +133,27 @@ export const ProgramForm = ({ values }: ProgramFormPropTypes): ReactElement => { fullWidth variant="outlined" required + onChange={(e) => { + setFieldValue('beneficiaryGroup', ''); + setFieldValue('dataCollectingTypeCode', e.target.value); + }} choices={filteredDataCollectionTypeChoicesData || []} component={FormikSelectField} data-cy="input-data-collecting-type" /> </Grid> - <Grid item xs={6} /> + <Grid item xs={6}> + <Field + name="beneficiaryGroup" + label={t('Beneficiary Group')} + fullWidth + variant="outlined" + choices={mappedBeneficiaryGroupsData} + component={FormikSelectField} + data-cy="input-beneficiary-group" + disabled={programHasRdi || isCopyProgramPage} + /> + </Grid> <Grid item xs={12}> <Field name="description" diff --git a/src/frontend/src/containers/forms/TargetingCriteriaForm.tsx b/src/frontend/src/containers/forms/TargetingCriteriaForm.tsx index 9797183524..991462776e 100644 --- a/src/frontend/src/containers/forms/TargetingCriteriaForm.tsx +++ b/src/frontend/src/containers/forms/TargetingCriteriaForm.tsx @@ -41,6 +41,7 @@ import { DialogTitleWrapper } from '../dialogs/DialogTitleWrapper'; import { TargetingCriteriaIndividualFilterBlocks } from './TargetingCriteriaIndividualFilterBlocks'; import { AndDivider, AndDividerLabel } from '@components/targeting/AndDivider'; import { TargetingCriteriaHouseholdFilter } from './TargetingCriteriaHouseholdFilter'; +import { useProgramContext } from 'src/programContext'; import { FormikTextField } from '@shared/Formik/FormikTextField'; import { TargetingCriteriaCollectorFilterBlocks } from './TargetingCriteriaCollectorFilterBlocks'; import { useAllCollectorFieldsAttributesQuery } from '@generated/graphql'; @@ -148,6 +149,8 @@ export const TargetingCriteriaForm = ({ }: TargetingCriteriaFormPropTypes): ReactElement => { const { t } = useTranslation(); const { businessArea, programId } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { data, loading } = useCachedIndividualFieldsQuery( businessArea, @@ -269,7 +272,7 @@ export const TargetingCriteriaForm = ({ (!values.individualIds || values.individualIds.length === 0) ) { errors.nonFieldErrors = [ - 'You need to add at least one household filter, an individual block filter, a collector block filter, a household ID, or an individual ID.', + `You need to add at least one ${beneficiaryGroup?.groupLabel} filter or an ${beneficiaryGroup?.memberLabel} block filter.`, ]; } else if ( values.individualsFiltersBlocks.filter( @@ -280,7 +283,7 @@ export const TargetingCriteriaForm = ({ ).length > 0 ) { errors.nonFieldErrors = [ - 'You need to add at least one household filter, an individual block filter, a collector block filter, a household IDs, or an individual IDs.', + `You need to add at least one ${beneficiaryGroup?.groupLabel} filter or an ${beneficiaryGroup?.memberLabel} block filter.`, ]; } return errors; @@ -356,7 +359,7 @@ export const TargetingCriteriaForm = ({ <DialogDescription> {isSocialWorkingProgram ? '' - : 'All rules defined below have to be true for the entire household.'} + : `All rules defined below have to be true for the entire ${beneficiaryGroup?.groupLabelPlural}.`}{' '} </DialogDescription> <Grid container spacing={3}> {householdFiltersAvailable && ( @@ -367,7 +370,7 @@ export const TargetingCriteriaForm = ({ fullWidth multiline variant="outlined" - label={t('Household IDs')} + label={t(`${beneficiaryGroup?.groupLabelPlural} IDs`)} component={FormikTextField} /> </Grid> @@ -425,7 +428,10 @@ export const TargetingCriteriaForm = ({ startIcon={<AddCircleOutline />} data-cy="button-household-rule" > - ADD {isSocialWorkingProgram ? 'PEOPLE' : 'HOUSEHOLD'}{' '} + ADD{' '} + {isSocialWorkingProgram + ? 'PEOPLE' + : beneficiaryGroup?.groupLabel.toUpperCase()}{' '} RULE </Button> </ButtonBox> @@ -447,7 +453,9 @@ export const TargetingCriteriaForm = ({ name="individualIds" fullWidth variant="outlined" - label={t('Individual IDs')} + label={t( + `${beneficiaryGroup?.memberLabelPlural} IDs`, + )} component={FormikTextField} /> </Box> @@ -496,7 +504,8 @@ export const TargetingCriteriaForm = ({ color="primary" startIcon={<AddCircleOutline />} > - ADD INDIVIDUAL RULE GROUP + {`ADD ${beneficiaryGroup?.memberLabel.toUpperCase()} + RULE GROUP`} </Button> </ButtonBox> </Box> diff --git a/src/frontend/src/containers/forms/TargetingCriteriaIndividualFilterBlocks.tsx b/src/frontend/src/containers/forms/TargetingCriteriaIndividualFilterBlocks.tsx index f18c4ff7e5..a756f9c2ab 100644 --- a/src/frontend/src/containers/forms/TargetingCriteriaIndividualFilterBlocks.tsx +++ b/src/frontend/src/containers/forms/TargetingCriteriaIndividualFilterBlocks.tsx @@ -7,6 +7,7 @@ import styled from 'styled-components'; import { chooseFieldType, clearField } from '@utils/targetingUtils'; import { IndividualFieldsQuery } from '@generated/graphql'; import { TargetingCriteriaIndividualBlockFilter } from './TargetingCriteriaIndividualBlockFilter'; +import { useProgramContext } from 'src/programContext'; const Divider = styled.div` border-top: 1px solid #e2e2e2; @@ -84,9 +85,12 @@ export function TargetingCriteriaIndividualFilterBlocks({ const { t } = useTranslation(); const shouldShowAndDivider = blockIndex + 1 < values.individualsFiltersBlocks.length; + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + return ( <div> - Set Individual Criteria + {`Set ${beneficiaryGroup?.memberLabel} Criteria`} <FieldArray name={`individualsFiltersBlocks[${blockIndex}].individualBlockFilters`} render={(arrayHelpers) => ( diff --git a/src/frontend/src/containers/pages/accountability/feedback/CreateFeedbackPage.tsx b/src/frontend/src/containers/pages/accountability/feedback/CreateFeedbackPage.tsx index 8c922b8c51..abcf28ba97 100644 --- a/src/frontend/src/containers/pages/accountability/feedback/CreateFeedbackPage.tsx +++ b/src/frontend/src/containers/pages/accountability/feedback/CreateFeedbackPage.tsx @@ -48,13 +48,7 @@ import { FormikSelectField } from '@shared/Formik/FormikSelectField'; import { FormikTextField } from '@shared/Formik/FormikTextField'; import { FeedbackSteps } from '@utils/constants'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; - -const steps = [ - 'Category Selection', - 'Household/Individual Look up', - 'Identity Verification', - 'Description', -]; +import { useProgramContext } from 'src/programContext'; const BoxPadding = styled.div` padding: 15px 0; @@ -153,6 +147,15 @@ export function CreateFeedbackPage(): ReactElement { const { baseUrl, businessArea, isAllPrograms, programId } = useBaseUrl(); const permissions = usePermissions(); const { showMessage } = useSnackbar(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + + const steps = [ + 'Category Selection', + `${beneficiaryGroup?.groupLabel}/${beneficiaryGroup?.memberLabel} Look up`, + 'Identity Verification', + 'Description', + ]; const [activeStep, setActiveStep] = useState(FeedbackSteps.Selection); const [validateData, setValidateData] = useState(false); @@ -360,14 +363,18 @@ export function CreateFeedbackPage(): ReactElement { </Typography> */} <Box py={4}> <Typography variant="subtitle2"> - {t('Household Questionnaire')} + {t( + `${beneficiaryGroup?.groupLabel} Questionnaire`, + )} </Typography> <Box py={4}> <HouseholdQuestionnaire values={values} /> </Box> </Box> <Typography variant="subtitle2"> - {t('Individual Questionnaire')} + {t( + `${beneficiaryGroup?.memberLabel} Questionnaire`, + )} </Typography> <Box py={4}> <IndividualQuestionnaire values={values} /> @@ -407,12 +414,20 @@ export function CreateFeedbackPage(): ReactElement { </LabelizedField> </Grid> <Grid item xs={6}> - <LabelizedField label={t('Household')}> + <LabelizedField + label={t( + `${beneficiaryGroup?.groupLabel}`, + )} + > {values.selectedHousehold?.unicefId} </LabelizedField> </Grid> <Grid item xs={6}> - <LabelizedField label={t('Individual')}> + <LabelizedField + label={t( + `${beneficiaryGroup?.memberLabel}`, + )} + > {values.selectedIndividual?.unicefId} </LabelizedField> </Grid> diff --git a/src/frontend/src/containers/pages/accountability/feedback/EditFeedbackPage.tsx b/src/frontend/src/containers/pages/accountability/feedback/EditFeedbackPage.tsx index adf35bf44e..0d5811853e 100644 --- a/src/frontend/src/containers/pages/accountability/feedback/EditFeedbackPage.tsx +++ b/src/frontend/src/containers/pages/accountability/feedback/EditFeedbackPage.tsx @@ -31,6 +31,7 @@ import { FormikAdminAreaAutocomplete } from '@shared/Formik/FormikAdminAreaAutoc import { FormikTextField } from '@shared/Formik/FormikTextField'; import { FormikSelectField } from '@shared/Formik/FormikSelectField'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; export const validationSchema = Yup.object().shape({ @@ -51,6 +52,9 @@ export const EditFeedbackPage = (): ReactElement => { const { baseUrl, businessArea, isAllPrograms } = useBaseUrl(); const permissions = usePermissions(); const { showMessage } = useSnackbar(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const { data: feedbackData, loading: feedbackDataLoading } = useFeedbackQuery( { variables: { id }, @@ -219,7 +223,10 @@ export const EditFeedbackPage = (): ReactElement => { </Grid> <Grid container xs={6} spacing={6}> <Grid item xs={6}> - <LabelizedField label={t('Household ID')}> + <LabelizedField + label={t(`${beneficiaryGroup?.groupLabel} ID`)} + > + {' '} {feedback.householdLookup?.id && canViewHouseholdDetails && !isAllPrograms ? ( @@ -238,7 +245,10 @@ export const EditFeedbackPage = (): ReactElement => { </LabelizedField> </Grid> <Grid item xs={6}> - <LabelizedField label={t('Individual ID')}> + <LabelizedField + label={t(`${beneficiaryGroup?.memberLabel} ID`)} + > + {' '} {feedback.individualLookup?.id && canViewIndividualDetails && !isAllPrograms ? ( diff --git a/src/frontend/src/containers/pages/grievances/CreateGrievancePage.tsx b/src/frontend/src/containers/pages/grievances/CreateGrievancePage.tsx index b42c8df4c3..2ecac2541d 100644 --- a/src/frontend/src/containers/pages/grievances/CreateGrievancePage.tsx +++ b/src/frontend/src/containers/pages/grievances/CreateGrievancePage.tsx @@ -82,6 +82,8 @@ export const CreateGrievancePage = (): ReactElement => { const { isSocialDctType } = useProgramContext(); const permissions = usePermissions(); const { showMessage } = useSnackbar(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const [activeStep, setActiveStep] = useState(GrievanceSteps.Selection); const [validateData, setValidateData] = useState(false); @@ -235,13 +237,13 @@ export const CreateGrievancePage = (): ReactElement => { let steps = isSocialDctType ? [ 'Category Selection', - 'Individual Look up', + `${beneficiaryGroup?.memberLabel} Look up`, 'Identity Verification', 'Description', ] : [ 'Category Selection', - 'Household/Individual Look up', + `${beneficiaryGroup?.groupLabel}/${beneficiaryGroup?.memberLabel} Look up`, 'Identity Verification', 'Description', ]; @@ -317,6 +319,7 @@ export const CreateGrievancePage = (): ReactElement => { householdFieldsDict, activeStep, setValidateData, + beneficiaryGroup, ) } validationSchema={validationSchemaWithSteps(activeStep)} diff --git a/src/frontend/src/containers/pages/grievances/EditGrievancePage.tsx b/src/frontend/src/containers/pages/grievances/EditGrievancePage.tsx index ecb4b4d389..b078489cd1 100644 --- a/src/frontend/src/containers/pages/grievances/EditGrievancePage.tsx +++ b/src/frontend/src/containers/pages/grievances/EditGrievancePage.tsx @@ -56,12 +56,12 @@ import { FormikAdminAreaAutocomplete } from '@shared/Formik/FormikAdminAreaAutoc import { FormikSelectField } from '@shared/Formik/FormikSelectField'; import { FormikTextField } from '@shared/Formik/FormikTextField'; import { + getGrievanceCategoryDescriptions, + getGrievanceIssueTypeDescriptions, GRIEVANCE_CATEGORIES, GRIEVANCE_CATEGORIES_NAMES, - GRIEVANCE_CATEGORY_DESCRIPTIONS, GRIEVANCE_ISSUE_TYPES, GRIEVANCE_ISSUE_TYPES_NAMES, - GRIEVANCE_ISSUE_TYPE_DESCRIPTIONS, GRIEVANCE_TICKET_STATES, } from '@utils/constants'; import { @@ -72,8 +72,8 @@ import { } from '@utils/utils'; import { grievancePermissions } from './GrievancesDetailsPage/grievancePermissions'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; -import { ReactElement } from 'react'; import { useProgramContext } from 'src/programContext'; +import { ReactElement } from 'react'; const BoxPadding = styled.div` padding: 15px 0; @@ -92,10 +92,11 @@ export const EditGrievancePage = (): ReactElement => { const location = useLocation(); const { t } = useTranslation(); const { baseUrl, businessArea, isAllPrograms } = useBaseUrl(); - const { isSocialDctType } = useProgramContext(); + const { selectedProgram, isSocialDctType } = useProgramContext(); const permissions = usePermissions(); const { showMessage } = useSnackbar(); const { id } = useParams(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { data: ticketData, @@ -251,15 +252,6 @@ export const EditGrievancePage = (): ReactElement => { baseUrl, ); - const categoryDescription = - GRIEVANCE_CATEGORY_DESCRIPTIONS[ - GRIEVANCE_CATEGORIES_NAMES[ticket.category] - ] || ''; - const issueTypeDescription = - GRIEVANCE_ISSUE_TYPE_DESCRIPTIONS[ - GRIEVANCE_ISSUE_TYPES_NAMES[ticket.issueType] - ] || ''; - const mappedProgramChoices = programsData?.allPrograms?.edges?.map( (element) => ({ name: element.node.name, value: element.node.id }), ); @@ -317,6 +309,7 @@ export const EditGrievancePage = (): ReactElement => { allAddIndividualFieldsData, individualFieldsDictForValidation, householdFieldsDict, + beneficiaryGroup, ) } validationSchema={validationSchema} @@ -327,6 +320,18 @@ export const EditGrievancePage = (): ReactElement => { dataChangeComponentDict, EmptyComponent, ); + const categoryDescriptions = + getGrievanceCategoryDescriptions(beneficiaryGroup); + const issueTypeDescriptions = + getGrievanceIssueTypeDescriptions(beneficiaryGroup); + + const categoryDescription = + categoryDescriptions[GRIEVANCE_CATEGORIES_NAMES[values.category]] || + ''; + const issueTypeDescription = + issueTypeDescriptions[ + GRIEVANCE_ISSUE_TYPES_NAMES[values.issueType] + ] || ''; return ( <> <AutoSubmitFormOnEnter /> @@ -393,7 +398,9 @@ export const EditGrievancePage = (): ReactElement => { )} <Grid container xs={12} item> <Grid item xs={3}> - <LabelizedField label={t('Household ID')}> + <LabelizedField + label={`${beneficiaryGroup?.groupLabel}`} + > <span> {ticket.household?.id && canViewHouseholdDetails && diff --git a/src/frontend/src/containers/pages/grievances/GrievancesTablePage.tsx b/src/frontend/src/containers/pages/grievances/GrievancesTablePage.tsx index 41a3335d7e..43e7d22f10 100644 --- a/src/frontend/src/containers/pages/grievances/GrievancesTablePage.tsx +++ b/src/frontend/src/containers/pages/grievances/GrievancesTablePage.tsx @@ -24,7 +24,6 @@ import { ButtonTooltip } from '@components/core/ButtonTooltip'; import { t } from 'i18next'; import { useProgramContext } from 'src/programContext'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; - export const GrievancesTablePage = (): ReactElement => { const { baseUrl } = useBaseUrl(); const { isActiveProgram } = useProgramContext(); diff --git a/src/frontend/src/containers/pages/householdMembers/NewTemplatePage.tsx b/src/frontend/src/containers/pages/householdMembers/NewTemplatePage.tsx index ecfbeefe19..51ea746c33 100644 --- a/src/frontend/src/containers/pages/householdMembers/NewTemplatePage.tsx +++ b/src/frontend/src/containers/pages/householdMembers/NewTemplatePage.tsx @@ -20,8 +20,11 @@ import { ReactElement, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link, useLocation, useNavigate } from 'react-router-dom'; import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; +import { useProgramContext } from 'src/programContext'; export const NewTemplatePage = (): ReactElement => { + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { t } = useTranslation(); const navigate = useNavigate(); const location = useLocation(); @@ -73,7 +76,7 @@ export const NewTemplatePage = (): ReactElement => { const [activeStep, setActiveStep] = useState(0); const steps = [ - `Filter ${isPeople ? 'People' : 'Individuals'}`, + `Filter ${isPeople ? 'People' : beneficiaryGroup?.memberLabelPlural}`, 'Fields to Update', ]; @@ -120,7 +123,7 @@ export const NewTemplatePage = (): ReactElement => { const breadCrumbsItems: BreadCrumbsItem[] = [ { - title: t('Household Members'), + title: beneficiaryGroup?.memberLabelPlural, to: `/${baseUrl}/population/individuals`, }, ]; diff --git a/src/frontend/src/containers/pages/managerialConsole/ManagerialConsolePage.tsx b/src/frontend/src/containers/pages/managerialConsole/ManagerialConsolePage.tsx index 7796c4d327..07298b55dd 100644 --- a/src/frontend/src/containers/pages/managerialConsole/ManagerialConsolePage.tsx +++ b/src/frontend/src/containers/pages/managerialConsole/ManagerialConsolePage.tsx @@ -14,11 +14,12 @@ import { usePermissions } from '@hooks/usePermissions'; import { useSnackbar } from '@hooks/useSnackBar'; import { Box } from '@mui/material'; import { useMutation, useQuery } from '@tanstack/react-query'; -import React, { FC, SetStateAction, useState } from 'react'; +import { FC, SetStateAction, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { PERMISSIONS, hasPermissions } from '../../../config/permissions'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; import { useLocation } from 'react-router-dom'; + export const ManagerialConsolePage: FC = () => { const { t } = useTranslation(); const { businessArea } = useBaseUrl(); diff --git a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlansTable.tsx b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlansTable.tsx index bf231efdbb..3fa7943237 100644 --- a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlansTable.tsx +++ b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlansTable.tsx @@ -9,6 +9,8 @@ import { import { UniversalTable } from '@containers/tables/UniversalTable'; import { headCells } from '@containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlansHeadCells'; import { PaymentPlanTableRow } from '@containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlanTableRow'; +import { adjustHeadCells } from '@utils/utils'; +import { useProgramContext } from 'src/programContext'; interface PaymentPlansTableProps { programCycle: ProgramCycle; @@ -24,6 +26,9 @@ export const PaymentPlansTable = ({ title, }: PaymentPlansTableProps): ReactElement => { const { programId, businessArea } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const initialVariables: AllPaymentPlansForTableQueryVariables = { businessArea, search: filter.search, @@ -37,6 +42,17 @@ export const PaymentPlansTable = ({ programCycle: programCycle.id, }; + const replacements = { + totalHouseholdsCount: (_beneficiaryGroup) => + `Num. of ${_beneficiaryGroup?.groupLabelPlural}`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( <UniversalTable< AllPaymentPlansForTableQuery['allPaymentPlans']['edges'][0]['node'], @@ -44,7 +60,7 @@ export const PaymentPlansTable = ({ > defaultOrderBy="-createdAt" title={title} - headCells={headCells} + headCells={adjustedHeadCells} query={useAllPaymentPlansForTableQuery} queriedObjectName="allPaymentPlans" initialVariables={initialVariables} diff --git a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCyclePage.tsx b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCyclePage.tsx index 97c2a8fa50..4783394705 100644 --- a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCyclePage.tsx +++ b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCyclePage.tsx @@ -2,7 +2,7 @@ import React, { ReactElement, useState } from 'react'; import { PageHeader } from '@core/PageHeader'; import { useTranslation } from 'react-i18next'; import { ProgramCyclesFilters } from '@containers/tables/ProgramCyclesTablePaymentModule/ProgramCyclesFilters'; -import { getFilterFromQueryParams } from '@utils/utils'; +import { adjustHeadCells, getFilterFromQueryParams } from '@utils/utils'; import { useLocation } from 'react-router-dom'; import { usePermissions } from '@hooks/usePermissions'; import { hasPermissions, PERMISSIONS } from '../../../../config/permissions'; @@ -11,6 +11,7 @@ import { useProgramContext } from '../../../../programContext'; import { TableWrapper } from '@core/TableWrapper'; import { ProgramCyclesTablePaymentModule } from '@containers/tables/ProgramCyclesTablePaymentModule/ProgramCyclesTablePaymentModule'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; +import { headCells } from '@containers/tables/ProgramCyclesTablePaymentModule/HeadCells'; const initialFilter = { search: '', @@ -26,6 +27,7 @@ export const ProgramCyclePage = (): ReactElement => { const permissions = usePermissions(); const location = useLocation(); const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const [filter, setFilter] = useState( getFilterFromQueryParams(location, initialFilter), @@ -34,6 +36,17 @@ export const ProgramCyclePage = (): ReactElement => { getFilterFromQueryParams(location, initialFilter), ); + const replacements = { + totalHouseholdsCount: (_beneficiaryGroup) => + `Total ${_beneficiaryGroup?.groupLabelPlural} Count`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + if (permissions === null) return null; if (!selectedProgram) return null; if (!hasPermissions(PERMISSIONS.PM_VIEW_LIST, permissions)) @@ -61,6 +74,7 @@ export const ProgramCyclePage = (): ReactElement => { <ProgramCyclesTablePaymentModule program={selectedProgram} filters={appliedFilter} + adjustedHeadCells={adjustedHeadCells} /> </TableWrapper> </> diff --git a/src/frontend/src/containers/pages/population/HouseholdMembersPage.tsx b/src/frontend/src/containers/pages/population/HouseholdMembersPage.tsx index c40c1abc73..15a73cf506 100644 --- a/src/frontend/src/containers/pages/population/HouseholdMembersPage.tsx +++ b/src/frontend/src/containers/pages/population/HouseholdMembersPage.tsx @@ -23,7 +23,9 @@ import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary' export const HouseholdMembersPage = (): ReactElement => { const { t } = useTranslation(); const location = useLocation(); - const { programHasPdu } = useProgramContext(); + const { programHasPdu, selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const { businessArea } = useBaseUrl(); const isNewTemplateJustCreated = location.state?.isNewTemplateJustCreated || false; @@ -90,7 +92,7 @@ export const HouseholdMembersPage = (): ReactElement => { > <> <PageHeader - title={t('Household Members')} + title={beneficiaryGroup?.memberLabelPlural} tabs={ <Tabs value={currentTab} @@ -98,7 +100,10 @@ export const HouseholdMembersPage = (): ReactElement => { setCurrentTab(newValue); }} > - <Tab data-cy="tab-individuals" label="Individuals" /> + <Tab + data-cy="tab-individuals" + label={beneficiaryGroup?.memberLabelPlural} + /> {programHasPdu ? ( canViewPDUListAndDetails ? ( <Tab diff --git a/src/frontend/src/containers/pages/population/PopulationHouseholdDetailsPage.tsx b/src/frontend/src/containers/pages/population/PopulationHouseholdDetailsPage.tsx index 72e34c8885..674d6ba1e0 100644 --- a/src/frontend/src/containers/pages/population/PopulationHouseholdDetailsPage.tsx +++ b/src/frontend/src/containers/pages/population/PopulationHouseholdDetailsPage.tsx @@ -32,6 +32,7 @@ import { AdminButton } from '@core/AdminButton'; import { CollectorsTable } from '@containers/tables/population/CollectorsTable'; import { HouseholdMembersTable } from '@containers/tables/population/HouseholdMembersTable'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const Container = styled.div` @@ -62,8 +63,11 @@ export const PopulationHouseholdDetailsPage = (): ReactElement => { const { t } = useTranslation(); const { id } = useParams(); const { baseUrl, businessArea } = useBaseUrl(); + const location = useLocation(); const permissions = usePermissions(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { data, loading, error } = useHouseholdQuery({ variables: { id }, @@ -97,7 +101,7 @@ export const PopulationHouseholdDetailsPage = (): ReactElement => { let breadCrumbsItems: BreadCrumbsItem[] = [ { - title: t('Households'), + title: beneficiaryGroup?.groupLabelPlural, to: `/${baseUrl}/population/household`, }, ]; @@ -125,7 +129,7 @@ export const PopulationHouseholdDetailsPage = (): ReactElement => { componentName="PopulationHouseholdDetailsPage" > <PageHeader - title={`${t('Household ID')}: ${renderSomethingOrDash( + title={`${beneficiaryGroup?.memberLabel}: ${renderSomethingOrDash( household?.unicefId, )}`} breadCrumbs={ @@ -142,7 +146,7 @@ export const PopulationHouseholdDetailsPage = (): ReactElement => { {household?.hasDuplicates && ( <WarningTooltip confirmed - message={t('Houesehold has Duplicates')} + message={`${beneficiaryGroup?.groupLabel} has Duplicates`} /> )} </Box> diff --git a/src/frontend/src/containers/pages/population/PopulationHouseholdPage.tsx b/src/frontend/src/containers/pages/population/PopulationHouseholdPage.tsx index aced83580d..2b0062cb23 100644 --- a/src/frontend/src/containers/pages/population/PopulationHouseholdPage.tsx +++ b/src/frontend/src/containers/pages/population/PopulationHouseholdPage.tsx @@ -1,22 +1,23 @@ -import { Box } from '@mui/material'; -import { ReactElement, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useLocation } from 'react-router-dom'; -import { useHouseholdChoiceDataQuery } from '@generated/graphql'; import { LoadingComponent } from '@components/core/LoadingComponent'; import { PageHeader } from '@components/core/PageHeader'; import { PermissionDenied } from '@components/core/PermissionDenied'; +import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; import { HouseholdFilters } from '@components/population/HouseholdFilter'; -import { PERMISSIONS, hasPermissions } from '../../../config/permissions'; +import { useHouseholdChoiceDataQuery } from '@generated/graphql'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { usePermissions } from '@hooks/usePermissions'; +import { Box } from '@mui/material'; import { getFilterFromQueryParams } from '@utils/utils'; +import { ReactElement, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { useProgramContext } from 'src/programContext'; +import { PERMISSIONS, hasPermissions } from '../../../config/permissions'; import { HouseholdTable } from '../../tables/population/HouseholdTable'; -import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; export function PopulationHouseholdPage(): ReactElement { - const { t } = useTranslation(); const location = useLocation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { data: choicesData, loading: choicesLoading } = useHouseholdChoiceDataQuery({ fetchPolicy: 'cache-first', @@ -62,7 +63,7 @@ export function PopulationHouseholdPage(): ReactElement { componentName="PopulationHouseholdPage" > <> - <PageHeader title={t('Households')} /> + <PageHeader title={beneficiaryGroup?.groupLabelPlural} /> <HouseholdFilters filter={filter} choicesData={choicesData} diff --git a/src/frontend/src/containers/pages/population/PopulationIndividualsDetailsPage.tsx b/src/frontend/src/containers/pages/population/PopulationIndividualsDetailsPage.tsx index 17e117a02f..2c79908ce1 100644 --- a/src/frontend/src/containers/pages/population/PopulationIndividualsDetailsPage.tsx +++ b/src/frontend/src/containers/pages/population/PopulationIndividualsDetailsPage.tsx @@ -27,6 +27,7 @@ import { fetchPeriodicFields } from '@api/periodicDataUpdateApi'; import { useQuery } from '@tanstack/react-query'; import { IndividualDeliveryMechanisms } from '@components/population/IndividualDeliveryMechanisms'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; @@ -42,6 +43,8 @@ const Container = styled.div` export const PopulationIndividualsDetailsPage = (): ReactElement => { const { id } = useParams(); const location = useLocation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { t } = useTranslation(); const { baseUrl, businessArea, programId } = useBaseUrl(); @@ -93,7 +96,7 @@ export const PopulationIndividualsDetailsPage = (): ReactElement => { let breadCrumbsItems: BreadCrumbsItem[] = [ { - title: 'Individuals', + title: `${beneficiaryGroup?.groupLabelPlural}`, to: `/${baseUrl}/population/individuals`, }, ]; @@ -122,7 +125,7 @@ export const PopulationIndividualsDetailsPage = (): ReactElement => { componentName="PopulationIndividualsDetailsPage" > <PageHeader - title={`${t('Individual ID')}: ${individual?.unicefId}`} + title={`${t(`${beneficiaryGroup?.memberLabel} ID`)}: ${individual?.unicefId}`} breadCrumbs={ hasPermissions( PERMISSIONS.POPULATION_VIEW_INDIVIDUALS_LIST, diff --git a/src/frontend/src/containers/pages/program/CreateProgramPage.tsx b/src/frontend/src/containers/pages/program/CreateProgramPage.tsx index c32e31f8e8..47215e1e35 100644 --- a/src/frontend/src/containers/pages/program/CreateProgramPage.tsx +++ b/src/frontend/src/containers/pages/program/CreateProgramPage.tsx @@ -29,6 +29,7 @@ import { } from '@components/programs/CreateProgram/ProgramStepper'; import { programValidationSchema } from '@components/programs/CreateProgram/programValidationSchema'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; +import { useProgramContext } from 'src/programContext'; import { omit } from 'lodash'; export const CreateProgramPage = (): ReactElement => { @@ -39,6 +40,8 @@ export const CreateProgramPage = (): ReactElement => { const [step, setStep] = useState(0); const { showMessage } = useSnackbar(); const { baseUrl, businessArea } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { data: treeData, loading: treeLoading } = useAllAreasTreeQuery({ variables: { businessArea }, @@ -174,6 +177,7 @@ export const CreateProgramPage = (): ReactElement => { endDate: undefined, sector: '', dataCollectingTypeCode: '', + beneficiaryGroup: '', description: '', budget: '', administrativeAreasOfImplementation: '', @@ -280,7 +284,7 @@ export const CreateProgramPage = (): ReactElement => { { title: t('Programme Time Series Fields'), description: t( - 'The Time Series Fields feature allows serial updating of individual data through an XLSX file.', + `The Time Series Fields feature allows serial updating of ${beneficiaryGroup?.memberLabel} data through an XLSX file.`, ), dataCy: 'step-button-time-series-fields', }, diff --git a/src/frontend/src/containers/pages/program/DuplicateProgramPage.tsx b/src/frontend/src/containers/pages/program/DuplicateProgramPage.tsx index d8930ec4e0..19bf98011a 100644 --- a/src/frontend/src/containers/pages/program/DuplicateProgramPage.tsx +++ b/src/frontend/src/containers/pages/program/DuplicateProgramPage.tsx @@ -3,7 +3,6 @@ import { LoadingComponent } from '@components/core/LoadingComponent'; import { PageHeader } from '@components/core/PageHeader'; import { DetailsStep } from '@components/programs/CreateProgram/DetailsStep'; import { PartnersStep } from '@components/programs/CreateProgram/PartnersStep'; -import { programValidationSchema } from '@components/programs/CreateProgram/programValidationSchema'; import { AllProgramsForChoicesDocument, ProgramPartnerAccess, @@ -31,6 +30,7 @@ import { } from '@components/programs/CreateProgram/ProgramStepper'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; import { omit } from 'lodash'; +import { editProgramDetailsValidationSchema } from '@components/programs/CreateProgram/editProgramValidationSchema'; export const DuplicateProgramPage = (): ReactElement => { const navigate = useNavigate(); @@ -70,7 +70,7 @@ export const DuplicateProgramPage = (): ReactElement => { })) : []; - const requestValues = omit(values, ['editMode']); + const requestValues = omit(values, ['editMode', 'beneficiaryGroup']); const initialPduFieldState = { label: '', pduData: { @@ -177,6 +177,7 @@ export const DuplicateProgramPage = (): ReactElement => { endDate, sector, dataCollectingType, + beneficiaryGroup, description, budget = '', administrativeAreasOfImplementation, @@ -188,13 +189,14 @@ export const DuplicateProgramPage = (): ReactElement => { } = data.program; const initialValues = { - editMode: false, + editMode: true, name: `Copy of Programme: (${name})`, programmeCode: '', startDate, endDate, sector, dataCollectingTypeCode: dataCollectingType?.code, + beneficiaryGroup: decodeIdString(beneficiaryGroup?.id), description, budget, administrativeAreasOfImplementation, @@ -222,6 +224,7 @@ export const DuplicateProgramPage = (): ReactElement => { 'endDate', 'sector', 'dataCollectingTypeCode', + 'beneficiaryGroup', 'description', 'budget', 'administrativeAreasOfImplementation', @@ -283,7 +286,8 @@ export const DuplicateProgramPage = (): ReactElement => { onSubmit={(values) => { handleSubmit(values); }} - validationSchema={programValidationSchema(t)} + validationSchema={editProgramDetailsValidationSchema(t, initialValues)} + validateOnChange={true} > {({ submitForm, diff --git a/src/frontend/src/containers/pages/program/EditProgramPage.tsx b/src/frontend/src/containers/pages/program/EditProgramPage.tsx index 1f3138fe26..c4604f3651 100644 --- a/src/frontend/src/containers/pages/program/EditProgramPage.tsx +++ b/src/frontend/src/containers/pages/program/EditProgramPage.tsx @@ -104,6 +104,7 @@ export const EditProgramPage = (): ReactElement => { endDate, sector, dataCollectingType, + beneficiaryGroup, description, budget = '', administrativeAreasOfImplementation, @@ -232,6 +233,7 @@ export const EditProgramPage = (): ReactElement => { endDate, sector, dataCollectingTypeCode: dataCollectingType?.code, + beneficiaryGroup: decodeIdString(beneficiaryGroup?.id), description, budget, administrativeAreasOfImplementation, @@ -268,6 +270,7 @@ export const EditProgramPage = (): ReactElement => { 'endDate', 'sector', 'dataCollectingTypeCode', + 'beneficiaryGroup', 'description', 'budget', 'administrativeAreasOfImplementation', @@ -331,6 +334,7 @@ export const EditProgramPage = (): ReactElement => { t, initialValuesProgramDetails, )} + validateOnChange={true} > {({ submitForm, @@ -341,6 +345,7 @@ export const EditProgramPage = (): ReactElement => { errors, setErrors, }) => { + const handleNextStep = async () => { await handleNext({ validateForm, @@ -374,6 +379,7 @@ export const EditProgramPage = (): ReactElement => { handleNext={handleNextStep} programId={id} errors={errors} + programHasRdi={programHasRdi} /> )} </div> diff --git a/src/frontend/src/containers/pages/program/ProgramDetailsPage.tsx b/src/frontend/src/containers/pages/program/ProgramDetailsPage.tsx index fed6b60896..31e54259b9 100644 --- a/src/frontend/src/containers/pages/program/ProgramDetailsPage.tsx +++ b/src/frontend/src/containers/pages/program/ProgramDetailsPage.tsx @@ -67,7 +67,8 @@ export function ProgramDetailsPage(): ReactElement { if (isPermissionDeniedError(error)) return <PermissionDenied />; - if (!choices || !businessAreaData || permissions === null) return null; + if (!choices || !businessAreaData || permissions === null || !data) + return null; const { program } = data; const canFinish = hasPermissions(PERMISSIONS.PROGRAMME_FINISH, permissions); diff --git a/src/frontend/src/containers/pages/rdi/RegistrationDataImportDetailsPage.tsx b/src/frontend/src/containers/pages/rdi/RegistrationDataImportDetailsPage.tsx index eab7839cf4..9a73cbe62d 100644 --- a/src/frontend/src/containers/pages/rdi/RegistrationDataImportDetailsPage.tsx +++ b/src/frontend/src/containers/pages/rdi/RegistrationDataImportDetailsPage.tsx @@ -23,6 +23,7 @@ import { isPermissionDeniedError } from '@utils/utils'; import { ImportedHouseholdTable } from '../../tables/rdi/ImportedHouseholdsTable'; import { ImportedIndividualsTable } from '../../tables/rdi/ImportedIndividualsTable'; import { UniversalErrorBoundary } from '@components/core/UniversalErrorBoundary'; +import { useProgramContext } from 'src/programContext'; const Container = styled.div` && { @@ -59,6 +60,8 @@ export const RegistrationDataImportDetailsPage = (): ReactElement => { const { id } = useParams(); const permissions = usePermissions(); const location = useLocation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { businessArea } = useBaseUrl(); const { data, loading, error, stopPolling, startPolling } = @@ -124,8 +127,14 @@ export const RegistrationDataImportDetailsPage = (): ReactElement => { variant="fullWidth" aria-label="full width tabs example" > - <Tab data-cy="tab-Households" label={t('Households')} /> - <Tab data-cy="tab-Individuals" label={t('Individuals')} /> + <Tab + data-cy="tab-Households" + label={beneficiaryGroup?.groupLabelPlural} + /> + <Tab + data-cy="tab-Individuals" + label={beneficiaryGroup?.memberLabelPlural} + /> </StyledTabs> </TabsContainer> <TabPanel value={selectedTab} index={0}> diff --git a/src/frontend/src/containers/pages/targeting/TargetPopulationsPage.tsx b/src/frontend/src/containers/pages/targeting/TargetPopulationsPage.tsx index fd0aebc632..699a56d098 100644 --- a/src/frontend/src/containers/pages/targeting/TargetPopulationsPage.tsx +++ b/src/frontend/src/containers/pages/targeting/TargetPopulationsPage.tsx @@ -32,6 +32,7 @@ export const TargetPopulationsPage = (): ReactElement => { const { t } = useTranslation(); const permissions = usePermissions(); const { programId } = useBaseUrl(); + const { data: programData } = useProgramQuery({ variables: { id: programId }, }); @@ -54,6 +55,7 @@ export const TargetPopulationsPage = (): ReactElement => { Table = TargetPopulationForPeopleTable; Filters = TargetPopulationForPeopleFilters; } + return ( <UniversalErrorBoundary location={location} diff --git a/src/frontend/src/containers/tables/Communication/LookUpHouseholdTableCommunication/LookUpHouseholdTableCommunication.tsx b/src/frontend/src/containers/tables/Communication/LookUpHouseholdTableCommunication/LookUpHouseholdTableCommunication.tsx index 9ab2f2349b..b06126d9f5 100644 --- a/src/frontend/src/containers/tables/Communication/LookUpHouseholdTableCommunication/LookUpHouseholdTableCommunication.tsx +++ b/src/frontend/src/containers/tables/Communication/LookUpHouseholdTableCommunication/LookUpHouseholdTableCommunication.tsx @@ -12,6 +12,8 @@ import { useBaseUrl } from '@hooks/useBaseUrl'; import { UniversalTable } from '../../UniversalTable'; import { headCells } from './LookUpHouseholdComunicationTableHeadCells'; import { LookUpHouseholdTableRowCommunication } from './LookUpHouseholdTableRowCommunication'; +import { adjustHeadCells } from '@utils/utils'; +import { useProgramContext } from 'src/programContext'; interface LookUpHouseholdTableCommunicationProps { businessArea: string; @@ -48,6 +50,9 @@ export function LookUpHouseholdTableCommunication({ isFeedbackWithHouseholdOnly, }: LookUpHouseholdTableCommunicationProps): ReactElement { const { programId } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const matchWithdrawnValue = (): boolean | undefined => { if (filter.withdrawn === 'true') { return true; @@ -123,12 +128,27 @@ export function LookUpHouseholdTableCommunication({ setFieldValue('identityVerified', false); }; + const replacements = { + unicefId: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} ID`, + head_of_household__full_name: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + size: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} Size`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + const renderTable = (): ReactElement => ( <UniversalTable< AllHouseholdsQuery['allHouseholds']['edges'][number]['node'], AllHouseholdsQueryVariables > - headCells={householdMultiSelect ? headCells.slice(1) : headCells} + headCells={ + householdMultiSelect ? adjustedHeadCells.slice(1) : adjustedHeadCells + } rowsPerPageOptions={[5, 10, 15, 20]} query={useAllHouseholdsForPopulationTableQuery} queriedObjectName="allHouseholds" diff --git a/src/frontend/src/containers/tables/Communication/RecipientsTable/RecipientsTable.tsx b/src/frontend/src/containers/tables/Communication/RecipientsTable/RecipientsTable.tsx index a117cc4e72..3b3568bc43 100644 --- a/src/frontend/src/containers/tables/Communication/RecipientsTable/RecipientsTable.tsx +++ b/src/frontend/src/containers/tables/Communication/RecipientsTable/RecipientsTable.tsx @@ -8,6 +8,8 @@ import { import { UniversalTable } from '../../UniversalTable'; import { headCells } from './RecipientsTableHeadCells'; import { RecipientsTableRow } from './RecipientsTableRow'; +import { useProgramContext } from 'src/programContext'; +import { adjustHeadCells } from '@utils/utils'; import { ReactElement } from 'react'; interface RecipientsTableProps { @@ -20,11 +22,26 @@ export function RecipientsTable({ canViewDetails, }: RecipientsTableProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const initialVariables: AllAccountabilityCommunicationMessageRecipientsQueryVariables = { messageId: id, }; + const replacements = { + unicefId: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} ID`, + head_of_household__full_name: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + size: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} Size`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( <TableWrapper> <UniversalTable< @@ -32,7 +49,7 @@ export function RecipientsTable({ AllAccountabilityCommunicationMessageRecipientsQueryVariables > title={t('Recipients')} - headCells={headCells} + headCells={adjustedHeadCells} rowsPerPageOptions={[10, 15, 20]} query={useAllAccountabilityCommunicationMessageRecipientsQuery} queriedObjectName="allAccountabilityCommunicationMessageRecipients" diff --git a/src/frontend/src/containers/tables/Feedback/FeedbackTable.tsx b/src/frontend/src/containers/tables/Feedback/FeedbackTable.tsx index 55ac511424..d081a7efca 100644 --- a/src/frontend/src/containers/tables/Feedback/FeedbackTable.tsx +++ b/src/frontend/src/containers/tables/Feedback/FeedbackTable.tsx @@ -7,10 +7,11 @@ import { } from '@generated/graphql'; import { TableWrapper } from '@components/core/TableWrapper'; import { useBaseUrl } from '@hooks/useBaseUrl'; -import { dateToIsoString, decodeIdString } from '@utils/utils'; +import { adjustHeadCells, dateToIsoString, decodeIdString } from '@utils/utils'; import { UniversalTable } from '../UniversalTable'; import { headCells } from './FeedbackTableHeadCells'; import { FeedbackTableRow } from './FeedbackTableRow'; +import { useProgramContext } from 'src/programContext'; interface FeedbackTableProps { filter; @@ -22,6 +23,9 @@ export function FeedbackTable({ canViewDetails, }: FeedbackTableProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const { isAllPrograms, programId } = useBaseUrl(); const initialVariables: AllFeedbacksQueryVariables = { feedbackId: filter.feedbackId, @@ -30,16 +34,27 @@ export function FeedbackTable({ createdAtRange: filter.createdAtRangeMin || filter.createdAtRangeMax ? JSON.stringify({ - min: dateToIsoString(filter.createdAtRangeMin, 'startOfDay'), - max: dateToIsoString(filter.createdAtRangeMax, 'endOfDay'), - }) + min: dateToIsoString(filter.createdAtRangeMin, 'startOfDay'), + max: dateToIsoString(filter.createdAtRangeMax, 'endOfDay'), + }) : '', program: isAllPrograms ? filter.program : programId, isActiveProgram: filter.programState === 'active' ? 'true' : '', }; + const replacements = { + household_lookup: (_beneficiaryGroup) => + `${_beneficiaryGroup?.groupLabel} ID`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + const headCellsWithProgramColumn = [ - ...headCells, + ...adjustedHeadCells, { disablePadding: false, label: 'Programme', @@ -52,7 +67,9 @@ export function FeedbackTable({ return ( <TableWrapper> <UniversalTable<FeedbackNode, AllFeedbacksQueryVariables> - headCells={isAllPrograms ? headCellsWithProgramColumn : headCells} + headCells={ + isAllPrograms ? headCellsWithProgramColumn : adjustedHeadCells + } title={t('Feedbacks List')} rowsPerPageOptions={[10, 15, 20]} query={useAllFeedbacksQuery} diff --git a/src/frontend/src/containers/tables/ProgramCycle/ProgramCycleTable.tsx b/src/frontend/src/containers/tables/ProgramCycle/ProgramCycleTable.tsx index 7b7bac0fcc..cf9c93a8d4 100644 --- a/src/frontend/src/containers/tables/ProgramCycle/ProgramCycleTable.tsx +++ b/src/frontend/src/containers/tables/ProgramCycle/ProgramCycleTable.tsx @@ -1,6 +1,6 @@ import { ProgramQuery, ProgramStatus } from '@generated/graphql'; import { UniversalRestTable } from '@components/rest/UniversalRestTable/UniversalRestTable'; -import React, { ReactElement, useState } from 'react'; +import { ReactElement, useState } from 'react'; import { ClickableTableRow } from '@core/Table/ClickableTableRow'; import TableCell from '@mui/material/TableCell'; import { UniversalMoment } from '@core/UniversalMoment'; diff --git a/src/frontend/src/containers/tables/ProgramCyclesTablePaymentModule/ProgramCyclesTablePaymentModule.tsx b/src/frontend/src/containers/tables/ProgramCyclesTablePaymentModule/ProgramCyclesTablePaymentModule.tsx index ebdbaa2ee7..79c30ced5d 100644 --- a/src/frontend/src/containers/tables/ProgramCyclesTablePaymentModule/ProgramCyclesTablePaymentModule.tsx +++ b/src/frontend/src/containers/tables/ProgramCyclesTablePaymentModule/ProgramCyclesTablePaymentModule.tsx @@ -5,7 +5,6 @@ import { StatusBox } from '@core/StatusBox'; import { decodeIdString, programCycleStatusToColor } from '@utils/utils'; import { UniversalMoment } from '@core/UniversalMoment'; import { UniversalRestTable } from '@components/rest/UniversalRestTable/UniversalRestTable'; -import { headCells } from '@containers/tables/ProgramCyclesTablePaymentModule/HeadCells'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { @@ -23,11 +22,13 @@ import { useSnackbar } from '@hooks/useSnackBar'; interface ProgramCyclesTablePaymentModuleProps { program; filters; + adjustedHeadCells; } export const ProgramCyclesTablePaymentModule = ({ program, filters, + adjustedHeadCells, }: ProgramCyclesTablePaymentModuleProps) => { const { showMessage } = useSnackbar(); const [queryVariables, setQueryVariables] = useState<ProgramCyclesQuery>({ @@ -150,7 +151,7 @@ export const ProgramCyclesTablePaymentModule = ({ <UniversalRestTable title="Programme Cycles" renderRow={renderRow} - headCells={headCells} + headCells={adjustedHeadCells} data={data} error={error} isLoading={isLoading} diff --git a/src/frontend/src/containers/tables/Surveys/LookUpProgrammesTableSurveys/LookUpProgrammesTableSurveys.tsx b/src/frontend/src/containers/tables/Surveys/LookUpProgrammesTableSurveys/LookUpProgrammesTableSurveys.tsx index b9bef1a99a..4090dee13e 100644 --- a/src/frontend/src/containers/tables/Surveys/LookUpProgrammesTableSurveys/LookUpProgrammesTableSurveys.tsx +++ b/src/frontend/src/containers/tables/Surveys/LookUpProgrammesTableSurveys/LookUpProgrammesTableSurveys.tsx @@ -10,6 +10,8 @@ import { TableWrapper } from '@components/core/TableWrapper'; import { UniversalTable } from '../../UniversalTable'; import { headCells } from './LookUpProgrammesHeadCellsSurveys'; import { LookUpProgrammesTableRowSurveys } from './LookUpProgrammesTableRowSurveys'; +import { adjustHeadCells } from '@utils/utils'; +import { useProgramContext } from 'src/programContext'; const NoTableStyling = styled.div` .MuiPaper-elevation1 { @@ -35,6 +37,8 @@ export function LookUpProgrammesTableSurveys({ handleChange, setFieldValue, }: LookUpProgrammesTableSurveysProps): ReactElement { + const { selectedProgram: programFromContext } = useProgramContext(); + const beneficiaryGroup = programFromContext?.beneficiaryGroup; const initialVariables: AllActiveProgramsQueryVariables = { businessArea, search: filter.search, @@ -55,14 +59,25 @@ export function LookUpProgrammesTableSurveys({ setFieldValue('program', id); }; + const replacements = { + totalHhCount: (_beneficiaryGroup) => + `Num. of ${_beneficiaryGroup?.groupLabelPlural}`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( <NoTableStyling> <TableWrapper> <UniversalTable< - AllProgramsQuery['allPrograms']['edges'][number]['node'], - AllActiveProgramsQueryVariables + AllProgramsQuery['allPrograms']['edges'][number]['node'], + AllActiveProgramsQueryVariables > - headCells={headCells} + headCells={adjustedHeadCells} query={useAllActiveProgramsQuery} queriedObjectName="allActivePrograms" initialVariables={initialVariables} diff --git a/src/frontend/src/containers/tables/Surveys/RecipientsTable/RecipientsTable.tsx b/src/frontend/src/containers/tables/Surveys/RecipientsTable/RecipientsTable.tsx index e13245ed6b..ebe3056707 100644 --- a/src/frontend/src/containers/tables/Surveys/RecipientsTable/RecipientsTable.tsx +++ b/src/frontend/src/containers/tables/Surveys/RecipientsTable/RecipientsTable.tsx @@ -9,6 +9,8 @@ import { UniversalTable } from '../../UniversalTable'; import { headCells } from './RecipientsTableHeadCells'; import { RecipientsTableRow } from './RecipientsTableRow'; import { ReactElement } from 'react'; +import { adjustHeadCells } from '@utils/utils'; +import { useProgramContext } from 'src/programContext'; interface RecipientsTableProps { id: string; @@ -20,15 +22,30 @@ export function RecipientsTable({ canViewDetails, }: RecipientsTableProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const initialVariables: RecipientsQueryVariables = { survey: id, }; + const replacements = { + unicefId: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} ID`, + head_of_household__full_name: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + size: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} Size`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( <TableWrapper> <UniversalTable<RecipientNode, RecipientsQueryVariables> title={t('Recipients')} - headCells={headCells} + headCells={adjustedHeadCells} rowsPerPageOptions={[10, 15, 20]} query={useRecipientsQuery} queriedObjectName="recipients" diff --git a/src/frontend/src/containers/tables/paymentmodule/PaymentPlansTable/PaymentPlansTable.tsx b/src/frontend/src/containers/tables/paymentmodule/PaymentPlansTable/PaymentPlansTable.tsx index 8ca8999202..e4cda0f291 100644 --- a/src/frontend/src/containers/tables/paymentmodule/PaymentPlansTable/PaymentPlansTable.tsx +++ b/src/frontend/src/containers/tables/paymentmodule/PaymentPlansTable/PaymentPlansTable.tsx @@ -9,6 +9,8 @@ import { useBaseUrl } from '@hooks/useBaseUrl'; import { UniversalTable } from '../../UniversalTable'; import { PaymentPlanTableRow } from './PaymentPlanTableRow'; import { headCells } from './PaymentPlansHeadCells'; +import { useProgramContext } from 'src/programContext'; +import { adjustHeadCells } from '@utils/utils'; interface PaymentPlansTableProps { filter; @@ -21,6 +23,9 @@ export function PaymentPlansTable({ }: PaymentPlansTableProps): ReactElement { const { t } = useTranslation(); const { programId, businessArea } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const initialVariables: AllPaymentPlansForTableQueryVariables = { businessArea, search: filter.search, @@ -32,12 +37,22 @@ export function PaymentPlansTable({ isFollowUp: filter.isFollowUp ? true : null, program: programId, }; + const replacements = { + totalHouseholdsCount: (_beneficiaryGroup) => + `Num. of ${_beneficiaryGroup?.groupLabelPlural}`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); return ( <UniversalTable<PaymentPlanNode, AllPaymentPlansForTableQueryVariables> defaultOrderBy="-createdAt" title={t('Payment Plans')} - headCells={headCells} + headCells={adjustedHeadCells} query={useAllPaymentPlansForTableQuery} queriedObjectName="allPaymentPlans" initialVariables={initialVariables} diff --git a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTable.tsx b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTable.tsx index d6ac86da17..540f4253f6 100644 --- a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTable.tsx +++ b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTable.tsx @@ -16,6 +16,8 @@ import { headCells } from './PaymentsTableHeadCells'; import { PaymentsTableRow } from './PaymentsTableRow'; import { WarningTooltipTable } from './WarningTooltipTable'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { useProgramContext } from 'src/programContext'; +import { adjustHeadCells } from '@utils/utils'; const StyledBox = styled(Box)` background-color: #fff; @@ -35,6 +37,9 @@ export function PaymentsTable({ }: PaymentsTableProps): ReactElement { const { baseUrl } = useBaseUrl(); const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const [dialogPayment, setDialogPayment] = useState< AllPaymentsForTableQuery['allPayments']['edges'][number]['node'] | null >(); @@ -43,6 +48,19 @@ export function PaymentsTable({ paymentPlanId: paymentPlan.id, }; + const replacements = { + household__unicef_id: (_beneficiaryGroup) => + `${_beneficiaryGroup?.groupLabel} ${t('ID')}`, + household__size: (_beneficiaryGroup) => + `${_beneficiaryGroup?.groupLabel} ${t('Size')}`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( <> <TableWrapper> @@ -64,7 +82,7 @@ export function PaymentsTable({ AllPaymentsForTableQueryVariables > isOnPaper={false} - headCells={headCells} + headCells={adjustedHeadCells} query={useAllPaymentsForTableQuery} rowsPerPageOptions={[10, 25, 50]} queriedObjectName="allPayments" diff --git a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/WarningTooltipTable/WarningTooltipTable.tsx b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/WarningTooltipTable/WarningTooltipTable.tsx index c8358d1f41..15a91a9cfd 100644 --- a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/WarningTooltipTable/WarningTooltipTable.tsx +++ b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/WarningTooltipTable/WarningTooltipTable.tsx @@ -27,6 +27,7 @@ import { } from '@generated/graphql'; import { DialogFooter } from '../../../../dialogs/DialogFooter'; import { DialogTitleWrapper } from '../../../../dialogs/DialogTitleWrapper'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const StyledTable = styled(Table)` @@ -57,6 +58,9 @@ export function WarningTooltipTable({ canViewDetails = false, }: WarningTooltipTableProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + if (!payment) return null; const mappedPaymentPlanRows = (): ReactElement[] => { const { @@ -135,7 +139,8 @@ export function WarningTooltipTable({ </Grid> </GreyBox> <Box mt={10} mb={10} display="flex"> - {t('Household ID')} <Bold>{payment.household?.unicefId}</Bold>{' '} + {`${beneficiaryGroup?.groupLabel} ID`}{' '} + <Bold>{payment.household?.unicefId}</Bold>{' '} {t('is also included in the following Payment Plans')}: </Box> <StyledTable> diff --git a/src/frontend/src/containers/tables/paymentmodulePeople/PeoplePaymentsTable/WarningTooltipTable/WarningTooltipTable.tsx b/src/frontend/src/containers/tables/paymentmodulePeople/PeoplePaymentsTable/WarningTooltipTable/WarningTooltipTable.tsx index c8358d1f41..15a91a9cfd 100644 --- a/src/frontend/src/containers/tables/paymentmodulePeople/PeoplePaymentsTable/WarningTooltipTable/WarningTooltipTable.tsx +++ b/src/frontend/src/containers/tables/paymentmodulePeople/PeoplePaymentsTable/WarningTooltipTable/WarningTooltipTable.tsx @@ -27,6 +27,7 @@ import { } from '@generated/graphql'; import { DialogFooter } from '../../../../dialogs/DialogFooter'; import { DialogTitleWrapper } from '../../../../dialogs/DialogTitleWrapper'; +import { useProgramContext } from 'src/programContext'; import { ReactElement } from 'react'; const StyledTable = styled(Table)` @@ -57,6 +58,9 @@ export function WarningTooltipTable({ canViewDetails = false, }: WarningTooltipTableProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + if (!payment) return null; const mappedPaymentPlanRows = (): ReactElement[] => { const { @@ -135,7 +139,8 @@ export function WarningTooltipTable({ </Grid> </GreyBox> <Box mt={10} mb={10} display="flex"> - {t('Household ID')} <Bold>{payment.household?.unicefId}</Bold>{' '} + {`${beneficiaryGroup?.groupLabel} ID`}{' '} + <Bold>{payment.household?.unicefId}</Bold>{' '} {t('is also included in the following Payment Plans')}: </Box> <StyledTable> diff --git a/src/frontend/src/containers/tables/payments/PaymentRecordAndPaymentHouseholdTable/PaymentRecordAndPaymentHouseholdTable.tsx b/src/frontend/src/containers/tables/payments/PaymentRecordAndPaymentHouseholdTable/PaymentRecordAndPaymentHouseholdTable.tsx index 84df276f52..67c9c8f9f2 100644 --- a/src/frontend/src/containers/tables/payments/PaymentRecordAndPaymentHouseholdTable/PaymentRecordAndPaymentHouseholdTable.tsx +++ b/src/frontend/src/containers/tables/payments/PaymentRecordAndPaymentHouseholdTable/PaymentRecordAndPaymentHouseholdTable.tsx @@ -9,6 +9,8 @@ import { import { UniversalTable } from '../../UniversalTable'; import { headCells } from './PaymentRecordAndPaymentHouseholdTableHeadCells'; import { PaymentRecordAndPaymentHouseholdTableRow } from './PaymentRecordAndPaymentHouseholdTableRow'; +import { adjustHeadCells } from '@utils/utils'; +import { useProgramContext } from 'src/programContext'; import { useBaseUrl } from '@hooks/useBaseUrl'; interface PaymentRecordAndPaymentTableProps { @@ -30,13 +32,30 @@ export function PaymentRecordHouseholdTable({ businessArea, program: programId, }; + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + + const replacements = { + headOfHousehold: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + fullName: (_beneficiaryGroup) => `${_beneficiaryGroup?.memberLabel}`, + relationship: (_beneficiaryGroup) => + `Relationship to Head of ${_beneficiaryGroup?.groupLabel}`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( <UniversalTable< PaymentRecordAndPaymentNode, AllPaymentRecordsAndPaymentsQueryVariables > title={t('Payment Records')} - headCells={headCells} + headCells={adjustedHeadCells} query={useAllPaymentRecordsAndPaymentsQuery} queriedObjectName="allPaymentRecordsAndPayments" initialVariables={initialVariables} diff --git a/src/frontend/src/containers/tables/payments/VerificationRecordsTable/VerificationRecordsTable.tsx b/src/frontend/src/containers/tables/payments/VerificationRecordsTable/VerificationRecordsTable.tsx index ea2a842230..48bebfad65 100644 --- a/src/frontend/src/containers/tables/payments/VerificationRecordsTable/VerificationRecordsTable.tsx +++ b/src/frontend/src/containers/tables/payments/VerificationRecordsTable/VerificationRecordsTable.tsx @@ -8,6 +8,8 @@ import { import { UniversalTable } from '../../UniversalTable'; import { headCells } from './VerificationRecordsHeadCells'; import { VerificationRecordsTableRow } from './VerificationRecordsTableRow'; +import { useProgramContext } from 'src/programContext'; +import { adjustHeadCells } from '@utils/utils'; interface VerificationRecordsTableProps { paymentPlanId?: string; @@ -23,6 +25,23 @@ export function VerificationRecordsTable({ businessArea, }: VerificationRecordsTableProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + + const replacements = { + payment_record__head_of_household__family_name: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + payment_record__household__unicef_id: (_beneficiaryGroup) => + `${_beneficiaryGroup?.groupLabel} ID`, + payment_record__household__status: (_beneficiaryGroup) => + `${_beneficiaryGroup?.groupLabel} Status`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); const initialVariables: AllPaymentVerificationsQueryVariables = { ...filter, @@ -36,7 +55,7 @@ export function VerificationRecordsTable({ AllPaymentVerificationsQueryVariables > title={t('Verification Records')} - headCells={headCells} + headCells={adjustedHeadCells} query={useAllPaymentVerificationsQuery} queriedObjectName="allPaymentVerifications" initialVariables={initialVariables} diff --git a/src/frontend/src/containers/tables/payments/VerificationRecordsTable/VerificationsTable.tsx b/src/frontend/src/containers/tables/payments/VerificationRecordsTable/VerificationsTable.tsx index a97a137d82..17f7121168 100644 --- a/src/frontend/src/containers/tables/payments/VerificationRecordsTable/VerificationsTable.tsx +++ b/src/frontend/src/containers/tables/payments/VerificationRecordsTable/VerificationsTable.tsx @@ -8,6 +8,8 @@ import { import { UniversalTable } from '../../UniversalTable'; import { headCells } from './VerificationsHeadCells'; import { VerificationRecordsTableRow } from './VerificationRecordsTableRow'; +import { adjustHeadCells } from '@utils/utils'; +import { useProgramContext } from 'src/programContext'; interface VerificationsTableProps { paymentPlanId?: string; @@ -30,13 +32,29 @@ export function VerificationsTable({ paymentPlanId, }; + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + + const replacements = { + payment_record__head_of_household__family_name: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + payment_record__household__unicef_id: (_beneficiaryGroup) => + `${_beneficiaryGroup?.groupLabel} ID`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( <UniversalTable< PaymentVerificationNode, AllPaymentVerificationsQueryVariables > title={t('Verification Records')} - headCells={headCells} + headCells={adjustedHeadCells} query={useAllPaymentVerificationsQuery} queriedObjectName="allPaymentVerifications" initialVariables={initialVariables} diff --git a/src/frontend/src/containers/tables/population/CollectorsTable/CollectorsTable.tsx b/src/frontend/src/containers/tables/population/CollectorsTable/CollectorsTable.tsx index ebf129244f..151c197ac1 100644 --- a/src/frontend/src/containers/tables/population/CollectorsTable/CollectorsTable.tsx +++ b/src/frontend/src/containers/tables/population/CollectorsTable/CollectorsTable.tsx @@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom'; import { ClickableTableRow } from '@components/core/Table/ClickableTableRow'; import { HeadCell } from '@components/core/Table/EnhancedTableHead'; import { Order, TableComponent } from '@components/core/Table/TableComponent'; -import { choicesToDict } from '@utils/utils'; +import { adjustHeadCells, choicesToDict } from '@utils/utils'; import { HouseholdChoiceDataQuery, HouseholdNode, @@ -14,6 +14,7 @@ import { import { useBaseUrl } from '@hooks/useBaseUrl'; import { Bold } from '@components/core/Bold'; import { BlackLink } from '@components/core/BlackLink'; +import { useProgramContext } from 'src/programContext'; const headCells: HeadCell<IndividualNode>[] = [ { @@ -53,6 +54,20 @@ export const CollectorsTable = ({ const handleClick = (row): void => { navigate(`/${baseUrl}/population/individuals/${row.id}`); }; + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + + const replacements = { + fullName: (_beneficiaryGroup) => `${_beneficiaryGroup?.memberLabel}`, + relationship: (_beneficiaryGroup) => + `Relationship to ${_beneficiaryGroup?.groupLabel}`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); const roleChoicesDict = choicesToDict(choicesData?.roleChoices); const relationshipChoicesDict = choicesToDict( @@ -142,7 +157,7 @@ export const CollectorsTable = ({ </ClickableTableRow> ); }} - headCells={headCells} + headCells={adjustedHeadCells} rowsPerPageOptions={totalCount < 5 ? [totalCount] : [5, 10, 15]} rowsPerPage={totalCount > 5 ? rowsPerPage : totalCount} page={page} diff --git a/src/frontend/src/containers/tables/population/HouseholdCompositionTable/HouseholdCompositionTable.tsx b/src/frontend/src/containers/tables/population/HouseholdCompositionTable/HouseholdCompositionTable.tsx index 377b6753de..575cd0e1f4 100644 --- a/src/frontend/src/containers/tables/population/HouseholdCompositionTable/HouseholdCompositionTable.tsx +++ b/src/frontend/src/containers/tables/population/HouseholdCompositionTable/HouseholdCompositionTable.tsx @@ -12,6 +12,7 @@ import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; import { Title } from '@components/core/Title'; import { HouseholdNode } from '@generated/graphql'; +import { useProgramContext } from 'src/programContext'; const GreyTableCell = styled(TableCell)` background-color: #eeeeee; @@ -28,10 +29,12 @@ export function HouseholdCompositionTable({ household, }: HouseholdCompositionTableProps): ReactElement { const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; return ( <OverviewPaper> <Title> - <Typography variant="h6">{t('Household Composition')}</Typography> + <Typography variant="h6">{`${beneficiaryGroup?.groupLabel} Composition`}</Typography> diff --git a/src/frontend/src/containers/tables/population/HouseholdMembersTable/HouseholdMembersTable.tsx b/src/frontend/src/containers/tables/population/HouseholdMembersTable/HouseholdMembersTable.tsx index 6367da82b0..d1ab109106 100644 --- a/src/frontend/src/containers/tables/population/HouseholdMembersTable/HouseholdMembersTable.tsx +++ b/src/frontend/src/containers/tables/population/HouseholdMembersTable/HouseholdMembersTable.tsx @@ -8,6 +8,7 @@ import { HeadCell } from '@components/core/Table/EnhancedTableHead'; import { Order, TableComponent } from '@components/core/Table/TableComponent'; import { UniversalMoment } from '@components/core/UniversalMoment'; import { + adjustHeadCells, choicesToDict, populationStatusToColor, sexToCapitalize, @@ -19,6 +20,7 @@ import { } from '@generated/graphql'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { Bold } from '@components/core/Bold'; +import { useProgramContext } from 'src/programContext'; const headCells: HeadCell[] = [ { @@ -83,6 +85,9 @@ export const HouseholdMembersTable = ({ const allIndividuals = household?.individuals?.edges?.map( (edge) => edge.node, ); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + if (orderBy) { if (orderDirection === 'asc') { allIndividuals.sort((a, b) => (a[orderBy] < b[orderBy] ? 1 : -1)); @@ -92,9 +97,23 @@ export const HouseholdMembersTable = ({ } const totalCount = allIndividuals.length; + + const replacements = { + unicefId: (_beneficiaryGroup) => `${_beneficiaryGroup?.memberLabel} ID`, + fullName: (_beneficiaryGroup) => `${_beneficiaryGroup?.memberLabel}`, + relationship: (_beneficiaryGroup) => + `Relationship to Head of ${_beneficiaryGroup?.groupLabel}`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( - title="Household Members" + title={`${beneficiaryGroup?.groupLabel} Members`} data={allIndividuals.slice( page * rowsPerPage, page * rowsPerPage + rowsPerPage, @@ -148,7 +167,7 @@ export const HouseholdMembersTable = ({ ); }} - headCells={headCells} + headCells={adjustedHeadCells} rowsPerPageOptions={totalCount < 5 ? [totalCount] : [5, 10, 15]} rowsPerPage={totalCount > 5 ? rowsPerPage : totalCount} page={page} diff --git a/src/frontend/src/containers/tables/population/HouseholdTable/HouseholdTable.tsx b/src/frontend/src/containers/tables/population/HouseholdTable/HouseholdTable.tsx index 8ab502c0bd..b5c3536151 100644 --- a/src/frontend/src/containers/tables/population/HouseholdTable/HouseholdTable.tsx +++ b/src/frontend/src/containers/tables/population/HouseholdTable/HouseholdTable.tsx @@ -8,8 +8,9 @@ import { useAllHouseholdsForPopulationTableQuery, } from '@generated/graphql'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { adjustHeadCells } from '@utils/utils'; import { ReactElement } from 'react'; -import { useTranslation } from 'react-i18next'; +import { useProgramContext } from 'src/programContext'; import { UniversalTable } from '../../UniversalTable'; import { headCells } from './HouseholdTableHeadCells'; import { HouseholdTableRow } from './HouseholdTableRow'; @@ -27,7 +28,8 @@ export function HouseholdTable({ choicesData, canViewDetails, }: HouseholdTableProps): ReactElement { - const { t } = useTranslation(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const { programId } = useBaseUrl(); const matchWithdrawnValue = (): boolean | undefined => { if (filter.withdrawn === 'true') { @@ -56,11 +58,24 @@ export function HouseholdTable({ program: programId, rdiMergeStatus: HouseholdRdiMergeStatus.Merged, }; + const replacements = { + unicefId: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} ID`, + head_of_household__full_name: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + size: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} Size`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( - title={t('Households')} - headCells={headCells} + title={`${beneficiaryGroup?.groupLabelPlural}`} + headCells={adjustedHeadCells} rowsPerPageOptions={[10, 15, 20]} query={useAllHouseholdsForPopulationTableQuery} queriedObjectName="allHouseholds" diff --git a/src/frontend/src/containers/tables/population/IndividualsListTable/IndividualsListTable.tsx b/src/frontend/src/containers/tables/population/IndividualsListTable/IndividualsListTable.tsx index 93b371360e..5375101d66 100644 --- a/src/frontend/src/containers/tables/population/IndividualsListTable/IndividualsListTable.tsx +++ b/src/frontend/src/containers/tables/population/IndividualsListTable/IndividualsListTable.tsx @@ -1,4 +1,4 @@ -import { useTranslation } from 'react-i18next'; +import { TableWrapper } from '@components/core/TableWrapper'; import { AllIndividualsForPopulationTableQueryVariables, AllIndividualsQueryVariables, @@ -7,13 +7,13 @@ import { IndividualRdiMergeStatus, useAllIndividualsForPopulationTableQuery, } from '@generated/graphql'; -import { TableWrapper } from '@components/core/TableWrapper'; -import { dateToIsoString } from '@utils/utils'; -import { UniversalTable } from '../../UniversalTable'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { adjustHeadCells, dateToIsoString } from '@utils/utils'; +import { ReactElement } from 'react'; +import { useProgramContext } from 'src/programContext'; +import { UniversalTable } from '../../UniversalTable'; import { headCells } from './IndividualsListTableHeadCells'; import { IndividualsListTableRow } from './IndividualsListTableRow'; -import { ReactElement } from 'react'; interface IndividualsListTableProps { filter; @@ -28,8 +28,9 @@ export function IndividualsListTable({ canViewDetails, choicesData, }: IndividualsListTableProps): ReactElement { - const { t } = useTranslation(); const { programId } = useBaseUrl(); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const initialVariables: AllIndividualsForPopulationTableQueryVariables = { age: JSON.stringify({ min: filter.ageMin, max: filter.ageMax }), businessArea, @@ -48,11 +49,26 @@ export function IndividualsListTable({ rdiMergeStatus: IndividualRdiMergeStatus.Merged, }; + const replacements = { + unicefId: (_beneficiaryGroup) => `${_beneficiaryGroup?.memberLabel} ID`, + fullName: (_beneficiaryGroup) => _beneficiaryGroup?.memberLabel, + household__unicef_id: (_beneficiaryGroup) => + `${_beneficiaryGroup?.groupLabel} ID`, + relationship: (_beneficiaryGroup) => + `Relationship to Head of ${_beneficiaryGroup?.groupLabel}`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + return ( - title={t('Individuals')} - headCells={headCells} + title={beneficiaryGroup?.memberLabelPlural} + headCells={adjustedHeadCells} rowsPerPageOptions={[10, 15, 20]} query={useAllIndividualsForPopulationTableQuery} queriedObjectName="allIndividuals" diff --git a/src/frontend/src/containers/tables/population/IndividualsListTable/__snapshots__/IndividualsListTable.test.tsx.snap b/src/frontend/src/containers/tables/population/IndividualsListTable/__snapshots__/IndividualsListTable.test.tsx.snap index 08f88afd61..3c9345eb3d 100644 --- a/src/frontend/src/containers/tables/population/IndividualsListTable/__snapshots__/IndividualsListTable.test.tsx.snap +++ b/src/frontend/src/containers/tables/population/IndividualsListTable/__snapshots__/IndividualsListTable.test.tsx.snap @@ -95,7 +95,7 @@ exports[`containers/tables/population/IndividualsListTable should render loading class="sc-kLhKbu dUAYNZ" data-cy="table-label" > - Relationship to HoH + Relationship to Head of Household
[] = [ { @@ -63,6 +65,9 @@ export function HouseholdImportedIndividualsTable({ const [rowsPerPage, setRowsPerPage] = useState(5); const [orderBy, setOrderBy] = useState(null); const [orderDirection, setOrderDirection] = useState('asc'); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + const allIndividuals = household.individuals.edges.map((edge) => edge.node); if (orderBy) { if (orderDirection === 'asc') { @@ -72,6 +77,21 @@ export function HouseholdImportedIndividualsTable({ } } + const replacements = { + unicefId: (_beneficiaryGroup) => `${_beneficiaryGroup?.memberLabel} ID`, + fullName: (_beneficiaryGroup) => _beneficiaryGroup?.memberLabel, + household__unicef_id: (_beneficiaryGroup) => + `${_beneficiaryGroup?.groupLabel} ID`, + relationship: (_beneficiaryGroup) => + `Relationship to Head of ${_beneficiaryGroup?.groupLabel}`, + }; + + const adjustedHeadCells = adjustHeadCells( + headCells, + beneficiaryGroup, + replacements, + ); + const totalCount = allIndividuals.length; return ( @@ -90,19 +110,19 @@ export function HouseholdImportedIndividualsTable({ isMerged={household.rdiMergeStatus} /> )} - headCells={headCells} + headCells={adjustedHeadCells} rowsPerPageOptions={totalCount < 5 ? [totalCount] : [5, 10, 15]} rowsPerPage={totalCount > 5 ? rowsPerPage : totalCount} page={page} itemsCount={totalCount} - handleChangePage={(event, newPage) => { + handleChangePage={(_event, newPage) => { setPage(newPage); }} handleChangeRowsPerPage={(event) => { setRowsPerPage(Number(event.target.value)); setPage(0); }} - handleRequestSort={(event, property) => { + handleRequestSort={(_event, property) => { let direction = 'asc'; if (property === orderBy) { direction = orderDirection === 'asc' ? 'desc' : 'asc'; diff --git a/src/frontend/src/containers/tables/rdi/ImportedHouseholdsTable/ImportedHouseholdTable.tsx b/src/frontend/src/containers/tables/rdi/ImportedHouseholdsTable/ImportedHouseholdTable.tsx index afd826087f..64363c14f8 100644 --- a/src/frontend/src/containers/tables/rdi/ImportedHouseholdsTable/ImportedHouseholdTable.tsx +++ b/src/frontend/src/containers/tables/rdi/ImportedHouseholdsTable/ImportedHouseholdTable.tsx @@ -9,6 +9,8 @@ import { UniversalTable } from '../../UniversalTable'; import { headCells as importedHeadCells } from './ImportedHouseholdTableHeadCells'; import { ImportedHouseholdTableRow } from './ImportedHouseholdTableRow'; import { headCells as mergedHeadCells } from './MergedHouseholdTableHeadCells'; +import { useProgramContext } from 'src/programContext'; +import { adjustHeadCells } from '@utils/utils'; export function ImportedHouseholdTable({ rdi, @@ -23,10 +25,38 @@ export function ImportedHouseholdTable({ : HouseholdRdiMergeStatus.Pending, }; + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; + + const mergedReplacements = { + id: (_beneficiaryGroup) => `${_beneficiaryGroup?.memberLabel} ID`, + head_of_household__full_name: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + size: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} Size`, + }; + + const adjustedMergedHeadCells = adjustHeadCells( + mergedHeadCells, + beneficiaryGroup, + mergedReplacements, + ); + + const importedReplacements = { + id: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} ID`, + head_of_household__full_name: (_beneficiaryGroup) => + `Head of ${_beneficiaryGroup?.groupLabel}`, + size: (_beneficiaryGroup) => `${_beneficiaryGroup?.groupLabel} Size`, + }; + + const adjustedImportedHeadCells = adjustHeadCells( + importedHeadCells, + beneficiaryGroup, + importedReplacements, + ); if (isMerged) { return ( - headCells={mergedHeadCells} + headCells={adjustedMergedHeadCells} query={useAllHouseholdsQuery} queriedObjectName="allHouseholds" rowsPerPageOptions={[10, 15, 20]} @@ -45,7 +75,7 @@ export function ImportedHouseholdTable({ } return ( - headCells={importedHeadCells} + headCells={adjustedImportedHeadCells} query={useAllHouseholdsQuery} queriedObjectName="allHouseholds" rowsPerPageOptions={[10, 15, 20]} diff --git a/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/ImportedIndividualsTable.tsx b/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/ImportedIndividualsTable.tsx index 4ceb651e4d..a776e28aa2 100644 --- a/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/ImportedIndividualsTable.tsx +++ b/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/ImportedIndividualsTable.tsx @@ -10,6 +10,8 @@ import { ReactElement, useState } from 'react'; import { UniversalTable } from '../../UniversalTable'; import { headCells as importedIndividualHeadCells } from './ImportedIndividualsTableHeadCells'; import { ImportedIndividualsTableRow } from './ImportedIndividualsTableRow'; +import { useProgramContext } from 'src/programContext'; +import { adjustHeadCells } from '@utils/utils'; import { headCells as mergedIndividualHeadCells } from './MergedIndividualsTableHeadCells'; interface ImportedIndividualsTableProps { @@ -38,6 +40,8 @@ export function ImportedIndividualsTable({ isMerged, }: ImportedIndividualsTableProps): ReactElement { const [showDuplicates, setShowDuplicates] = useState(false); + const { selectedProgram } = useProgramContext(); + const beneficiaryGroup = selectedProgram?.beneficiaryGroup; const initialVariables = { rdiId, @@ -49,6 +53,23 @@ export function ImportedIndividualsTable({ : IndividualRdiMergeStatus.Pending, }; + const replacements = { + id: (_beneficiaryGroup) => `${_beneficiaryGroup?.memberLabel} ID`, + full_name: (_beneficiaryGroup) => _beneficiaryGroup?.memberLabel, + relationship: (_beneficiaryGroup) => + `Relationship to Head of ${_beneficiaryGroup?.groupLabel}`, + }; + + const adjustedMergedIndividualsHeadCells = adjustHeadCells( + mergedIndividualHeadCells, + beneficiaryGroup, + replacements, + ); + const adjustedImportedIndividualsHeadCells = adjustHeadCells( + importedIndividualHeadCells, + beneficiaryGroup, + replacements, + ); return (
{showCheckbox && ( @@ -72,7 +93,7 @@ export function ImportedIndividualsTable({ {isMerged ? ( title={title} - headCells={mergedIndividualHeadCells} + headCells={adjustedMergedIndividualsHeadCells} query={useAllIndividualsQuery} queriedObjectName="allIndividuals" rowsPerPageOptions={rowsPerPageOptions} @@ -91,7 +112,7 @@ export function ImportedIndividualsTable({ ) : ( title={title} - headCells={importedIndividualHeadCells} + headCells={adjustedImportedIndividualsHeadCells} query={useAllIndividualsQuery} queriedObjectName="allIndividuals" rowsPerPageOptions={rowsPerPageOptions} diff --git a/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/__snapshots__/ImportedIndividualsTable.test.tsx.snap b/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/__snapshots__/ImportedIndividualsTable.test.tsx.snap index b60c63f69e..bdaf66cbb3 100644 --- a/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/__snapshots__/ImportedIndividualsTable.test.tsx.snap +++ b/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/__snapshots__/ImportedIndividualsTable.test.tsx.snap @@ -109,7 +109,7 @@ exports[`containers/tables/rdi/ImportedIndividualsTable should render loading 1` role="button" tabindex="0" > - Relationship to HoH + Relationship to Head of Household -
+ /> diff --git a/src/frontend/src/containers/pages/payments/PaymentPlanVerificationDetailsPage.tsx b/src/frontend/src/containers/pages/payments/PaymentPlanVerificationDetailsPage.tsx index b96b63bec4..08d294500c 100644 --- a/src/frontend/src/containers/pages/payments/PaymentPlanVerificationDetailsPage.tsx +++ b/src/frontend/src/containers/pages/payments/PaymentPlanVerificationDetailsPage.tsx @@ -251,7 +251,6 @@ export function PaymentPlanVerificationDetailsPage(): ReactElement { hasPermissions(PERMISSIONS.ACTIVITY_LOG_VIEW, permissions) && ( )} diff --git a/src/frontend/src/containers/tables/UniversalActivityLogTablePaymentVerification.tsx b/src/frontend/src/containers/tables/UniversalActivityLogTablePaymentVerification.tsx index eb5218ba38..35ea66a75c 100644 --- a/src/frontend/src/containers/tables/UniversalActivityLogTablePaymentVerification.tsx +++ b/src/frontend/src/containers/tables/UniversalActivityLogTablePaymentVerification.tsx @@ -9,11 +9,9 @@ import { useBaseUrl } from '@hooks/useBaseUrl'; interface UniversalActivityLogTablePaymentVerificationProps { objectId: string; - objectType?: string; } export function UniversalActivityLogTablePaymentVerification({ objectId, - objectType, }: UniversalActivityLogTablePaymentVerificationProps): ReactElement { const [page, setPage] = useState(0); const { businessArea } = useBaseUrl(); @@ -22,7 +20,6 @@ export function UniversalActivityLogTablePaymentVerification({ variables: { businessArea, objectId: decodeIdString(objectId), - objectType, first: rowsPerPage, }, fetchPolicy: 'network-only', diff --git a/src/hct_mis_api/apps/core/exchange_rates/utils.py b/src/hct_mis_api/apps/core/exchange_rates/utils.py deleted file mode 100644 index 1d67b87654..0000000000 --- a/src/hct_mis_api/apps/core/exchange_rates/utils.py +++ /dev/null @@ -1,21 +0,0 @@ -import logging -from decimal import Decimal - -from hct_mis_api.apps.core.exchange_rates import ExchangeRates -from hct_mis_api.apps.payment.models import PaymentRecord - -logger = logging.getLogger(__name__) - - -def calculate_delivery_quantity_in_usd(exchange_rates_client: ExchangeRates, payment_record: PaymentRecord) -> None: - exchange_rate = exchange_rates_client.get_exchange_rate_for_currency_code( - payment_record.currency, payment_record.parent.dispersion_date - ) - - if exchange_rate is None: - logger.info(f"exchange_rate not found for {payment_record.ca_id}") - return - exchange_rate = Decimal(exchange_rate) - payment_record.delivered_quantity_usd = Decimal(payment_record.delivered_quantity / exchange_rate).quantize( - Decimal(".01") - ) diff --git a/src/hct_mis_api/apps/dashboard/services.py b/src/hct_mis_api/apps/dashboard/services.py index 748e93f9c4..e2b36afe34 100644 --- a/src/hct_mis_api/apps/dashboard/services.py +++ b/src/hct_mis_api/apps/dashboard/services.py @@ -11,7 +11,7 @@ from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.dashboard.serializers import DashboardHouseholdSerializer -from hct_mis_api.apps.payment.models import Payment, PaymentRecord +from hct_mis_api.apps.payment.models import Payment CACHE_TIMEOUT = 60 * 60 * 24 # 24 hours @@ -120,52 +120,6 @@ def refresh_data(cls, business_area_slug: str) -> ReturnDict: ) ) - payment_records_aggregated = ( - PaymentRecord.objects.using("read_only") - .select_related( - "business_area", "household", "program", "household__admin1", "service_provider", "delivery_type" - ) - .filter(business_area=area, household__is_removed=False) - .exclude(status__in=["Transaction Erroneous", "Not Distributed", "Force failed", "Manually Cancelled"]) - .annotate( - year=ExtractYear(Coalesce("delivery_date", "status_date")), - month=ExtractMonth(Coalesce("delivery_date", "status_date")), - programs=Coalesce(F("household__program__name"), Value("Unknown program")), - sectors=Coalesce(F("household__program__sector"), Value("Unknown sector")), - admin1=Coalesce(F("household__admin1__name"), Value("Unknown admin1")), - fsp=Coalesce(F("service_provider__short_name"), Value("Unknown fsp")), - delivery_types=F("delivery_type__name"), - ) - .distinct() - .values( - "currency", - "year", - "month", - "status", - "programs", - "sectors", - "admin1", - "fsp", - "delivery_types", - ) - .annotate( - total_usd=Sum( - Coalesce("delivered_quantity_usd", "entitlement_quantity_usd", Value(0.0)), - output_field=DecimalField(), - ), - total_quantity=Sum( - Coalesce("delivered_quantity", "entitlement_quantity", Value(0.0)), output_field=DecimalField() - ), - total_payments=Count("id", distinct=True), - individuals=Sum("household__size"), - households=Count("household", distinct=True), - children_counts=Sum("household__children_count"), - pwd_counts=pwdSum, - ) - ) - - combined_list = list(payments_aggregated) + list(payment_records_aggregated) - summary = defaultdict( lambda: { "total_usd": 0, @@ -178,7 +132,7 @@ def refresh_data(cls, business_area_slug: str) -> ReturnDict: "pwd_counts": 0, } ) - for item in combined_list: + for item in list(payments_aggregated): key = ( item["currency"], item["year"], diff --git a/src/hct_mis_api/apps/grievance/fixtures.py b/src/hct_mis_api/apps/grievance/fixtures.py index f2add77bea..60cf4c7fff 100644 --- a/src/hct_mis_api/apps/grievance/fixtures.py +++ b/src/hct_mis_api/apps/grievance/fixtures.py @@ -79,7 +79,7 @@ class Meta: ) household = None individual = None - payment_obj = None + payment = None @factory.post_generation def create_extras(obj, create: bool, extracted: bool, **kwargs: Any) -> None: @@ -105,8 +105,7 @@ class Meta: ) household = None individual = None - payment_object_id = None - payment_content_type_id = None + payment = None @factory.post_generation def create_extras(obj, create: bool, extracted: bool, **kwargs: Any) -> None: @@ -133,7 +132,7 @@ class Meta: ) household = None individual = None - payment_obj = None + payment = None class GrievanceComplaintTicketWithoutExtrasFactory(DjangoModelFactory): diff --git a/src/hct_mis_api/apps/grievance/migrations/0005_migration.py b/src/hct_mis_api/apps/grievance/migrations/0005_migration.py new file mode 100644 index 0000000000..f6ae054f09 --- /dev/null +++ b/src/hct_mis_api/apps/grievance/migrations/0005_migration.py @@ -0,0 +1,66 @@ +# Generated by Django 3.2.25 on 2024-12-11 13:11 +from django.db import migrations, models +import django.db.models.deletion + + +def migrate_onetoone_to_foreignkey(apps, schema_editor): + TicketSensitiveDetails = apps.get_model("grievance", "TicketSensitiveDetails") + TicketComplaintDetails = apps.get_model("grievance", "TicketComplaintDetails") + TicketSensitiveDetails.objects.filter( + payment_object_id__isnull=False + ).update(payment_fk=models.F("payment_object_id")) + TicketComplaintDetails.objects.filter( + payment_object_id__isnull=False + ).update(payment_fk=models.F("payment_object_id")) + + +class Migration(migrations.Migration): + + + dependencies = [ + ('grievance', '0004_migration'), + ] + + operations = [ + migrations.AddField( + model_name='ticketcomplaintdetails', + name='payment_fk', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='payment.payment'), + ), + migrations.AddField( + model_name='ticketsensitivedetails', + name='payment_fk', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='payment.payment'), + ), + migrations.RunPython(migrate_onetoone_to_foreignkey), + migrations.RemoveField( + model_name='ticketcomplaintdetails', + name='payment', + ), + migrations.RemoveField( + model_name='ticketsensitivedetails', + name='payment', + ), + migrations.RenameField( + model_name='ticketcomplaintdetails', + old_name='payment_fk', + new_name='payment', + ), + migrations.RenameField( + model_name='ticketsensitivedetails', + old_name='payment_fk', + new_name='payment', + ), + migrations.AlterField( + model_name='ticketcomplaintdetails', + name='payment', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, + related_name='ticket_complaint_details', to='payment.payment'), + ), + migrations.AlterField( + model_name='ticketsensitivedetails', + name='payment', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, + related_name='ticket_sensitive_details', to='payment.payment'), + ), + ] diff --git a/src/hct_mis_api/apps/grievance/migrations/0006_migration.py b/src/hct_mis_api/apps/grievance/migrations/0006_migration.py new file mode 100644 index 0000000000..384e90342c --- /dev/null +++ b/src/hct_mis_api/apps/grievance/migrations/0006_migration.py @@ -0,0 +1,29 @@ +# Generated by Django 3.2.25 on 2024-12-11 13:17 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('grievance', '0005_migration'), + ] + + operations = [ + migrations.RemoveField( + model_name='ticketcomplaintdetails', + name='payment_content_type', + ), + migrations.RemoveField( + model_name='ticketcomplaintdetails', + name='payment_object_id', + ), + migrations.RemoveField( + model_name='ticketsensitivedetails', + name='payment_content_type', + ), + migrations.RemoveField( + model_name='ticketsensitivedetails', + name='payment_object_id', + ), + ] diff --git a/src/hct_mis_api/apps/grievance/models.py b/src/hct_mis_api/apps/grievance/models.py index 67acda98ee..a43a7d477c 100644 --- a/src/hct_mis_api/apps/grievance/models.py +++ b/src/hct_mis_api/apps/grievance/models.py @@ -5,14 +5,12 @@ from django.conf import settings from django.contrib.auth import get_user_model -from django.contrib.contenttypes.fields import GenericForeignKey -from django.contrib.contenttypes.models import ContentType from django.core.cache import cache from django.core.exceptions import ValidationError from django.core.files.storage import default_storage from django.core.validators import MinValueValidator from django.db import models -from django.db.models import JSONField, Q, QuerySet, UniqueConstraint, UUIDField +from django.db.models import JSONField, Q, QuerySet, UniqueConstraint from django.db.models.signals import post_save from django.dispatch import receiver from django.utils.functional import cached_property @@ -101,15 +99,6 @@ def get_queryset(self) -> "QuerySet": return super().get_queryset().filter(is_original=True) -class GenericPaymentTicket(TimeStampedUUIDModel): - payment_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True) - payment_object_id = UUIDField(null=True) - payment_obj = GenericForeignKey("payment_content_type", "payment_object_id") - - class Meta: - abstract = True - - class GrievanceTicket(TimeStampedUUIDModel, AdminUrlMixin, ConcurrencyModel, UnicefIdentifiedModel): ACTIVITY_LOG_MAPPING = create_mapping_dict( [ @@ -628,7 +617,7 @@ class TicketNote(TimeStampedUUIDModel): } -class TicketComplaintDetails(GenericPaymentTicket): # TODO TP drop GenericPaymentTicket +class TicketComplaintDetails(TimeStampedUUIDModel): STATUS_FLOW = GENERAL_STATUS_FLOW ticket = models.OneToOneField( @@ -648,7 +637,7 @@ class TicketComplaintDetails(GenericPaymentTicket): # TODO TP drop GenericPayme on_delete=models.CASCADE, null=True, ) - payment = models.OneToOneField( + payment = models.ForeignKey( Payment, on_delete=models.CASCADE, null=True, @@ -659,7 +648,7 @@ class Meta: verbose_name_plural = "Ticket Complaint Details" -class TicketSensitiveDetails(GenericPaymentTicket): # TODO TP drop GenericPaymentTicket +class TicketSensitiveDetails(TimeStampedUUIDModel): STATUS_FLOW = GENERAL_STATUS_FLOW ticket = models.OneToOneField( @@ -679,7 +668,7 @@ class TicketSensitiveDetails(GenericPaymentTicket): # TODO TP drop GenericPayme on_delete=models.CASCADE, null=True, ) - payment = models.OneToOneField( + payment = models.ForeignKey( Payment, on_delete=models.CASCADE, null=True, diff --git a/src/hct_mis_api/apps/grievance/services/ticket_based_on_payment_record_services.py b/src/hct_mis_api/apps/grievance/services/ticket_based_on_payment_record_services.py index b29654ab34..5eb455eef7 100644 --- a/src/hct_mis_api/apps/grievance/services/ticket_based_on_payment_record_services.py +++ b/src/hct_mis_api/apps/grievance/services/ticket_based_on_payment_record_services.py @@ -29,8 +29,7 @@ def create_tickets_based_on_payment_records_service( model.objects.create( individual=individual, household=household, - payment_content_type=None, - payment_object_id=None, + payment=None, ticket=grievance_ticket, ) grievance_ticket.refresh_from_db() diff --git a/src/hct_mis_api/apps/household/migrations/0005_migration.py b/src/hct_mis_api/apps/household/migrations/0005_migration.py new file mode 100644 index 0000000000..daea327c0b --- /dev/null +++ b/src/hct_mis_api/apps/household/migrations/0005_migration.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.25 on 2024-12-16 11:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('household', '0004_migration'), + ] + + operations = [ + migrations.AlterField( + model_name='household', + name='internal_data', + field=models.JSONField(blank=True, default=dict), + ), + migrations.AlterField( + model_name='individual', + name='internal_data', + field=models.JSONField(blank=True, default=dict), + ), + ] diff --git a/src/hct_mis_api/apps/payment/admin.py b/src/hct_mis_api/apps/payment/admin.py index ab82df38ba..6f9ff7d9d1 100644 --- a/src/hct_mis_api/apps/payment/admin.py +++ b/src/hct_mis_api/apps/payment/admin.py @@ -12,7 +12,7 @@ from admin_extra_buttons.decorators import button from admin_extra_buttons.mixins import confirm_action -from adminfilters.autocomplete import AutoCompleteFilter, LinkedAutoCompleteFilter +from adminfilters.autocomplete import AutoCompleteFilter from adminfilters.depot.widget import DepotManager from adminfilters.filters import ChoicesFieldComboFilter, ValueFilter from adminfilters.querystring import QueryStringFilter @@ -20,7 +20,6 @@ from smart_admin.mixins import LinkedObjectsMixin from hct_mis_api.apps.payment.models import ( - CashPlan, DeliveryMechanism, DeliveryMechanismData, DeliveryMechanismPerPaymentPlan, @@ -31,10 +30,8 @@ PaymentHouseholdSnapshot, PaymentPlan, PaymentPlanSupportingDocument, - PaymentRecord, PaymentVerification, PaymentVerificationPlan, - ServiceProvider, ) from hct_mis_api.apps.payment.services.verification_plan_status_change_services import ( VerificationPlanStatusChangeServices, @@ -48,49 +45,6 @@ from django.forms import Form -@admin.register(PaymentRecord) -class PaymentRecordAdmin(AdminAdvancedFiltersMixin, LinkedObjectsMixin, HOPEModelAdminBase): - list_display = ( - "unicef_id", - "household", - "status", - "cash_plan_name", - "target_population", - "business_area", - ) - list_filter = ( - DepotManager, - QueryStringFilter, - ("status", ChoicesFieldComboFilter), - ("business_area", LinkedAutoCompleteFilter.factory(parent=None)), - ("target_population", LinkedAutoCompleteFilter.factory(parent="business_area")), - ("parent", LinkedAutoCompleteFilter.factory(parent="business_area")), - ("service_provider", LinkedAutoCompleteFilter.factory(parent="business_area")), - ) - advanced_filter_fields = ( - "status", - "delivery_date", - ("service_provider__name", "Service Provider"), - ("parent__name", "CashPlan"), - ("target_population__name", "TargetPopulation"), - ) - date_hierarchy = "updated_at" - raw_id_fields = ( - "business_area", - "parent", - "household", - "head_of_household", - "target_population", - "service_provider", - ) - - def cash_plan_name(self, obj: Any) -> str: - return obj.parent.name or "" - - def get_queryset(self, request: HttpRequest) -> QuerySet: - return super().get_queryset(request).select_related("household", "parent", "target_population", "business_area") - - @admin.register(PaymentVerificationPlan) class PaymentVerificationPlanAdmin(LinkedObjectsMixin, HOPEModelAdminBase): list_display = ("payment_plan", "status", "verification_channel") @@ -100,7 +54,7 @@ class PaymentVerificationPlanAdmin(LinkedObjectsMixin, HOPEModelAdminBase): ) date_hierarchy = "updated_at" search_fields = ("payment_plan__name",) - raw_id_fields = ("payment_plan_content_type",) + raw_id_fields = ("payment_plan",) @button() def verifications(self, request: HttpRequest, pk: "UUID") -> HttpResponseRedirect: @@ -154,7 +108,7 @@ class PaymentVerificationAdmin(HOPEModelAdminBase): ("payment__household__unicef_id", ValueFilter), ) date_hierarchy = "updated_at" - raw_id_fields = ("payment_verification_plan", "payment_content_type") + raw_id_fields = ("payment_verification_plan", "payment") def payment_plan_name(self, obj: PaymentVerification) -> str: # pragma: no cover payment_plan = obj.payment_verification_plan.payment_plan @@ -178,35 +132,6 @@ def get_queryset(self, request: HttpRequest) -> QuerySet: ) -@admin.register(ServiceProvider) -class ServiceProviderAdmin(HOPEModelAdminBase): - list_display = ("full_name", "short_name", "country", "vision_id") - search_fields = ("full_name", "vision_id", "short_name") - list_filter = (("business_area", AutoCompleteFilter),) - autocomplete_fields = ("business_area",) - - -@admin.register(CashPlan) -class CashPlanAdmin(HOPEModelAdminBase): - list_display = ("name", "program", "delivery_type", "status", "verification_status", "ca_id") - list_filter = ( - ("status", ChoicesFieldComboFilter), - ("business_area", AutoCompleteFilter), - ("delivery_type", ChoicesFieldComboFilter), - ("payment_verification_summary__status", ChoicesFieldComboFilter), - ("program__id", ValueFilter), - ("vision_id", ValueFilter), - ) - raw_id_fields = ("business_area", "program", "service_provider") - search_fields = ("name",) - - @button() - def payments(self, request: HttpRequest, pk: str) -> TemplateResponse: - context = self.get_common_context(request, pk, aeu_groups=[None], action="payments") - - return TemplateResponse(request, "admin/cashplan/payments.html", context) - - @admin.register(PaymentPlan) class PaymentPlanAdmin(HOPEModelAdminBase, PaymentPlanCeleryTasksMixin): list_display = ("unicef_id", "program_cycle", "status", "target_population") diff --git a/src/hct_mis_api/apps/payment/filters.py b/src/hct_mis_api/apps/payment/filters.py index 1c9b83d876..28ef1774ba 100644 --- a/src/hct_mis_api/apps/payment/filters.py +++ b/src/hct_mis_api/apps/payment/filters.py @@ -5,12 +5,10 @@ from django.db.models import Case, Count, IntegerField, Q, QuerySet, Value, When from django.db.models.functions import Lower from django.shortcuts import get_object_or_404 -from django.utils.translation import gettext_lazy as _ from django_filters import ( BooleanFilter, CharFilter, - ChoiceFilter, DateFilter, FilterSet, MultipleChoiceFilter, @@ -26,7 +24,6 @@ decode_id_string_required, ) from hct_mis_api.apps.payment.models import ( - CashPlan, DeliveryMechanism, FinancialServiceProvider, FinancialServiceProviderXlsxTemplate, @@ -99,10 +96,7 @@ def payment_plan_filter(self, qs: QuerySet, name: str, value: str) -> QuerySet: ) def business_area_filter(self, qs: QuerySet, name: str, value: str) -> QuerySet: - return qs.filter( - Q(payment_verification_plan__payment_plan__business_area__slug=value) - | Q(payment_verification_plan__cash_plan__business_area__slug=value) - ) + return qs.filter(payment_verification_plan__payment_plan__business_area__slug=value) class PaymentVerificationPlanFilter(FilterSet): @@ -124,26 +118,11 @@ class Meta: class PaymentVerificationLogEntryFilter(LogEntryFilter): - PLAN_TYPE_CASH = "CashPlan" - PLAN_TYPE_PAYMENT = "PaymentPlan" - PLAN_TYPE_CHOICES = ( - (PLAN_TYPE_CASH, _("CashPlan")), - (PLAN_TYPE_PAYMENT, _("PaymentPlan")), - ) object_id = UUIDFilter(method="object_id_filter") - object_type = ChoiceFilter(method="object_type_filter", choices=PLAN_TYPE_CHOICES) - - def filter_queryset(self, queryset: QuerySet) -> QuerySet: - cleaned_data = self.form.cleaned_data - object_type = cleaned_data.get("object_type") - object_id = cleaned_data.get("object_id") - plan_object = (PaymentPlan if object_type == self.PLAN_TYPE_PAYMENT else CashPlan).objects.get(pk=object_id) - verifications_ids = plan_object.payment_verification_plans.all().values_list("pk", flat=True) - return queryset.filter(object_id__in=verifications_ids) def object_id_filter(self, qs: QuerySet, name: str, value: UUID) -> QuerySet: - cash_plan = CashPlan.objects.get(pk=value) - verifications_ids = cash_plan.verifications.all().values_list("pk", flat=True) + payment_plan = PaymentPlan.objects.get(pk=value) + verifications_ids = payment_plan.payment_verification_plans.all().values_list("pk", flat=True) return qs.filter(object_id__in=verifications_ids) diff --git a/src/hct_mis_api/apps/payment/fixtures.py b/src/hct_mis_api/apps/payment/fixtures.py index d4fd7f32ad..77cb076a97 100644 --- a/src/hct_mis_api/apps/payment/fixtures.py +++ b/src/hct_mis_api/apps/payment/fixtures.py @@ -38,7 +38,6 @@ FinancialServiceProvider, FinancialServiceProviderXlsxTemplate, FspXlsxTemplatePerDeliveryMechanism, - GenericPayment, Payment, PaymentPlan, PaymentPlanSplit, @@ -288,7 +287,7 @@ class Meta: parent = factory.SubFactory(PaymentPlanFactory) business_area = factory.LazyAttribute(lambda o: BusinessArea.objects.first()) - status = GenericPayment.STATUS_PENDING + status = Payment.STATUS_PENDING status_date = factory.Faker( "date_time_this_decade", before_now=True, diff --git a/src/hct_mis_api/apps/payment/migrations/0008_migration.py b/src/hct_mis_api/apps/payment/migrations/0008_migration.py new file mode 100644 index 0000000000..24816b1caf --- /dev/null +++ b/src/hct_mis_api/apps/payment/migrations/0008_migration.py @@ -0,0 +1,87 @@ +# Generated by Django 3.2.25 on 2024-12-11 13:41 + +from django.db import migrations, models +import django.db.models.deletion + + +def migrate_onetoone_to_foreignkey(apps, schema_editor): + PaymentVerification = apps.get_model("payment", "PaymentVerification") + PaymentVerification.objects.filter( + payment_id__isnull=False + ).update(payment_fk=models.F("payment_id")) + + +class Migration(migrations.Migration): + + dependencies = [ + ('grievance', '0006_migration'), + ('payment', '0007_migration'), + ] + + operations = [ + migrations.RemoveConstraint( + model_name='paymentverification', + name='payment_content_type_and_payment_id', + ), + migrations.RemoveConstraint( + model_name='paymentverificationsummary', + name='payment_plan_content_type_and_payment_plan_id', + ), + migrations.RemoveIndex( + model_name='paymentverification', + name='payment_pay_payment_ec4a29_idx', + ), + migrations.RemoveIndex( + model_name='paymentverificationplan', + name='payment_pay_payment_3ba67e_idx', + ), + migrations.RemoveIndex( + model_name='paymentverificationsummary', + name='payment_pay_payment_8b7d61_idx', + ), + migrations.RemoveField( + model_name='paymentverification', + name='payment_content_type', + ), + migrations.RemoveField( + model_name='paymentverification', + name='payment_object_id', + ), + migrations.RemoveField( + model_name='paymentverificationplan', + name='payment_plan_content_type', + ), + migrations.RemoveField( + model_name='paymentverificationplan', + name='payment_plan_object_id', + ), + migrations.RemoveField( + model_name='paymentverificationsummary', + name='payment_plan_content_type', + ), + migrations.RemoveField( + model_name='paymentverificationsummary', + name='payment_plan_object_id', + ), + migrations.AddField( + model_name='paymentverification', + name='payment_fk', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='payment.payment'), + ), + migrations.RunPython(migrate_onetoone_to_foreignkey), + migrations.RemoveField( + model_name='paymentverification', + name='payment', + ), + migrations.RenameField( + model_name='paymentverification', + old_name='payment_fk', + new_name='payment', + ), + migrations.AlterField( + model_name='paymentverification', + name='payment', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + related_name='payment_verifications', to='payment.payment'), + ), + ] diff --git a/src/hct_mis_api/apps/payment/migrations/0009_migration.py b/src/hct_mis_api/apps/payment/migrations/0009_migration.py new file mode 100644 index 0000000000..5c58af088d --- /dev/null +++ b/src/hct_mis_api/apps/payment/migrations/0009_migration.py @@ -0,0 +1,69 @@ +# Generated by Django 3.2.25 on 2024-12-16 11:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0008_migration'), + ] + + operations = [ + migrations.RemoveField( + model_name='paymentrecord', + name='business_area', + ), + migrations.RemoveField( + model_name='paymentrecord', + name='delivery_type', + ), + migrations.RemoveField( + model_name='paymentrecord', + name='head_of_household', + ), + migrations.RemoveField( + model_name='paymentrecord', + name='household', + ), + migrations.RemoveField( + model_name='paymentrecord', + name='parent', + ), + migrations.RemoveField( + model_name='paymentrecord', + name='service_provider', + ), + migrations.RemoveField( + model_name='paymentrecord', + name='target_population', + ), + migrations.RemoveField( + model_name='serviceprovider', + name='business_area', + ), + migrations.AlterField( + model_name='financialserviceprovider', + name='internal_data', + field=models.JSONField(blank=True, default=dict), + ), + migrations.AlterField( + model_name='payment', + name='internal_data', + field=models.JSONField(blank=True, default=dict), + ), + migrations.AlterField( + model_name='paymentplan', + name='internal_data', + field=models.JSONField(blank=True, default=dict), + ), + migrations.DeleteModel( + name='CashPlan', + ), + migrations.DeleteModel( + name='PaymentRecord', + ), + migrations.DeleteModel( + name='ServiceProvider', + ), + ] diff --git a/src/hct_mis_api/apps/payment/models/__init__.py b/src/hct_mis_api/apps/payment/models/__init__.py index a26573da4a..64a34571ff 100644 --- a/src/hct_mis_api/apps/payment/models/__init__.py +++ b/src/hct_mis_api/apps/payment/models/__init__.py @@ -3,13 +3,6 @@ Approval, ApprovalProcess, ) -from hct_mis_api.apps.payment.models.cash_assist import ( - CashPlan, - GenericPayment, - GenericPaymentPlan, - PaymentRecord, - ServiceProvider, -) from hct_mis_api.apps.payment.models.payment import ( DeliveryMechanism, DeliveryMechanismData, @@ -37,11 +30,6 @@ "AcceptanceProcessThreshold", "Approval", "ApprovalProcess", - "CashPlan", - "GenericPayment", - "GenericPaymentPlan", - "PaymentRecord", - "ServiceProvider", "DeliveryMechanism", "DeliveryMechanismData", "DeliveryMechanismPerPaymentPlan", diff --git a/src/hct_mis_api/apps/payment/models/cash_assist.py b/src/hct_mis_api/apps/payment/models/cash_assist.py deleted file mode 100644 index 0f2a092af9..0000000000 --- a/src/hct_mis_api/apps/payment/models/cash_assist.py +++ /dev/null @@ -1,482 +0,0 @@ -import logging -from datetime import datetime -from decimal import Decimal -from functools import cached_property -from typing import TYPE_CHECKING, Any, Callable, Optional - -from django.contrib.contenttypes.fields import GenericRelation -from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ValidationError -from django.core.validators import MinValueValidator -from django.db import models -from django.db.models import Q, QuerySet -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ - -from model_utils import Choices - -from hct_mis_api.apps.core.currencies import USDC -from hct_mis_api.apps.core.exchange_rates import ExchangeRates -from hct_mis_api.apps.payment.delivery_mechanisms import DeliveryMechanismChoices -from hct_mis_api.apps.utils.models import ( - AdminUrlMixin, - ConcurrencyModel, - TimeStampedUUIDModel, -) - -if TYPE_CHECKING: - from hct_mis_api.apps.core.exchange_rates.api import ExchangeRateClient - - -logger = logging.getLogger(__name__) - - -class GenericPaymentPlan(TimeStampedUUIDModel): - usd_fields = [ - "total_entitled_quantity_usd", - "total_entitled_quantity_revised_usd", - "total_delivered_quantity_usd", - "total_undelivered_quantity_usd", - ] - - business_area = models.ForeignKey("core.BusinessArea", on_delete=models.CASCADE) - status_date = models.DateTimeField() - start_date = models.DateTimeField( - db_index=True, - blank=True, - null=True, - ) - end_date = models.DateTimeField( - db_index=True, - blank=True, - null=True, - ) - program = models.ForeignKey("program.Program", on_delete=models.CASCADE) - exchange_rate = models.DecimalField(decimal_places=8, blank=True, null=True, max_digits=14) - - total_entitled_quantity = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0"))], - db_index=True, - null=True, - ) - total_entitled_quantity_usd = models.DecimalField( - decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal("0"))], null=True, blank=True - ) - total_entitled_quantity_revised = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0"))], - db_index=True, - null=True, - blank=True, - ) - total_entitled_quantity_revised_usd = models.DecimalField( - decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal("0"))], null=True, blank=True - ) - total_delivered_quantity = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0"))], - db_index=True, - null=True, - blank=True, - ) - total_delivered_quantity_usd = models.DecimalField( - decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal("0"))], null=True, blank=True - ) - total_undelivered_quantity = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0"))], - db_index=True, - null=True, - blank=True, - ) - total_undelivered_quantity_usd = models.DecimalField( - decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal("0"))], null=True, blank=True - ) - - class Meta: - abstract = True - - @property - def get_unicef_id(self) -> str: - return self.ca_id if isinstance(self, CashPlan) else self.unicef_id - - def get_exchange_rate(self, exchange_rates_client: Optional["ExchangeRateClient"] = None) -> float: - if self.currency == USDC: - # exchange rate for Digital currency - return 1.0 - - if exchange_rates_client is None: - exchange_rates_client = ExchangeRates() - - return exchange_rates_client.get_exchange_rate_for_currency_code(self.currency, self.currency_exchange_date) - - @property - def get_payment_verification_summary(self) -> Any: - """PaymentPlan has only one payment_verification_summary""" - from hct_mis_api.apps.payment.models import PaymentVerificationSummary - - c_type = ContentType.objects.get_for_model(self.__class__) - try: - verification_summary = PaymentVerificationSummary.objects.get( - payment_plan_content_type_id=c_type.pk, payment_plan_object_id=self.pk - ) - except PaymentVerificationSummary.DoesNotExist: - return None - return verification_summary - - @property - def get_payment_verification_plans(self) -> Any: - from hct_mis_api.apps.payment.models import PaymentVerificationPlan - - c_type = ContentType.objects.get_for_model(self.__class__) - payment_verification_plans = PaymentVerificationPlan.objects.filter( - payment_plan_content_type_id=c_type.pk, payment_plan_object_id=self.pk - ) - return payment_verification_plans - - def available_payment_records( - self, - payment_verification_plan: Any = None, - extra_validation: Optional[Callable] = None, - ) -> QuerySet: - params = Q(status__in=GenericPayment.ALLOW_CREATE_VERIFICATION, delivered_quantity__gt=0) - - if payment_verification_plan: - params &= Q( - Q(payment_verification__isnull=True) - | Q(payment_verification__payment_verification_plan=payment_verification_plan) - ) - else: - params &= Q(payment_verification__isnull=True) - - payment_records = self.payment_items.select_related("head_of_household").filter(params).distinct() - - if extra_validation: - payment_records = list(map(lambda pr: pr.pk, filter(extra_validation, payment_records))) - - qs = PaymentRecord.objects.filter(pk__in=payment_records) - - return qs - - @property - def can_create_payment_verification_plan(self) -> int: - return self.available_payment_records().count() > 0 - - -class GenericPayment(TimeStampedUUIDModel): - usd_fields = ["delivered_quantity_usd", "entitlement_quantity_usd"] - - STATUS_SUCCESS = "Transaction Successful" - STATUS_ERROR = "Transaction Erroneous" - STATUS_DISTRIBUTION_SUCCESS = "Distribution Successful" - STATUS_NOT_DISTRIBUTED = "Not Distributed" - STATUS_FORCE_FAILED = "Force failed" - STATUS_DISTRIBUTION_PARTIAL = "Partially Distributed" - STATUS_PENDING = "Pending" - # Payment Gateway statuses - STATUS_SENT_TO_PG = "Sent to Payment Gateway" - STATUS_SENT_TO_FSP = "Sent to FSP" - STATUS_MANUALLY_CANCELLED = "Manually Cancelled" - - STATUS_CHOICE = ( - (STATUS_DISTRIBUTION_SUCCESS, _("Distribution Successful")), # Delivered Fully - (STATUS_NOT_DISTRIBUTED, _("Not Distributed")), # Not Delivered - (STATUS_SUCCESS, _("Transaction Successful")), # Delivered Fully - (STATUS_ERROR, _("Transaction Erroneous")), # Unsuccessful - (STATUS_FORCE_FAILED, _("Force failed")), # Force Failed - (STATUS_DISTRIBUTION_PARTIAL, _("Partially Distributed")), # Delivered Partially - (STATUS_PENDING, _("Pending")), # Pending - (STATUS_SENT_TO_PG, _("Sent to Payment Gateway")), - (STATUS_SENT_TO_FSP, _("Sent to FSP")), - (STATUS_MANUALLY_CANCELLED, _("Manually Cancelled")), - ) - - ALLOW_CREATE_VERIFICATION = (STATUS_SUCCESS, STATUS_DISTRIBUTION_SUCCESS, STATUS_DISTRIBUTION_PARTIAL) - PENDING_STATUSES = (STATUS_PENDING, STATUS_SENT_TO_PG, STATUS_SENT_TO_FSP) - - ENTITLEMENT_CARD_STATUS_ACTIVE = "ACTIVE" - ENTITLEMENT_CARD_STATUS_INACTIVE = "INACTIVE" - ENTITLEMENT_CARD_STATUS_CHOICE = Choices( - (ENTITLEMENT_CARD_STATUS_ACTIVE, _("Active")), - (ENTITLEMENT_CARD_STATUS_INACTIVE, _("Inactive")), - ) - - business_area = models.ForeignKey("core.BusinessArea", on_delete=models.CASCADE) - status = models.CharField( - max_length=255, - choices=STATUS_CHOICE, - default=STATUS_PENDING, - ) - status_date = models.DateTimeField() - household = models.ForeignKey("household.Household", on_delete=models.CASCADE) - head_of_household = models.ForeignKey("household.Individual", on_delete=models.CASCADE, null=True) - delivery_type = models.ForeignKey("payment.DeliveryMechanism", on_delete=models.SET_NULL, null=True) - currency = models.CharField( - max_length=4, - ) - entitlement_quantity = models.DecimalField( - decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal("0.00"))], null=True, blank=True - ) - entitlement_quantity_usd = models.DecimalField( - decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal("0.00"))], null=True, blank=True - ) - delivered_quantity = models.DecimalField( - decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal("0.00"))], null=True, blank=True - ) - delivered_quantity_usd = models.DecimalField( - decimal_places=2, max_digits=12, validators=[MinValueValidator(Decimal("0.00"))], null=True, blank=True - ) - delivery_date = models.DateTimeField(null=True, blank=True) - transaction_reference_id = models.CharField(max_length=255, null=True, blank=True) # transaction_id - transaction_status_blockchain_link = models.CharField(max_length=255, null=True, blank=True) - - class Meta: - abstract = True - - @property - def verification(self) -> Any: - from hct_mis_api.apps.payment.models import PaymentVerification - - c_type = ContentType.objects.get_for_model(self.__class__) - try: - verification = PaymentVerification.objects.get(payment_content_type_id=c_type.pk, payment_object_id=self.pk) - except PaymentVerification.DoesNotExist: - return None - return verification - - def get_revert_mark_as_failed_status(self, delivered_quantity: Decimal) -> str: - raise NotImplementedError() - - def mark_as_failed(self) -> None: - if self.status is self.STATUS_FORCE_FAILED: - raise ValidationError("Status shouldn't be failed") - self.status = self.STATUS_FORCE_FAILED - self.status_date = timezone.now() - self.delivered_quantity = 0 - self.delivered_quantity_usd = 0 - self.delivery_date = None - - def revert_mark_as_failed(self, delivered_quantity: Decimal, delivery_date: datetime) -> None: - if self.status != self.STATUS_FORCE_FAILED: - raise ValidationError("Only payment marked as force failed can be reverted") - if self.entitlement_quantity is None: - raise ValidationError("Entitlement quantity need to be set in order to revert") - - self.status = self.get_revert_mark_as_failed_status(delivered_quantity) - self.status_date = timezone.now() - self.delivered_quantity = delivered_quantity - self.delivery_date = delivery_date - - @property - def get_unicef_id(self) -> str: - return self.ca_id if isinstance(self, PaymentRecord) else self.unicef_id - - @property - def payment_status(self) -> str: - status = "-" - if self.status == GenericPayment.STATUS_PENDING: - status = "Pending" - - elif self.status in (GenericPayment.STATUS_DISTRIBUTION_SUCCESS, GenericPayment.STATUS_SUCCESS): - status = "Delivered Fully" - - elif self.status == GenericPayment.STATUS_DISTRIBUTION_PARTIAL: - status = "Delivered Partially" - - elif self.status == GenericPayment.STATUS_NOT_DISTRIBUTED: - status = "Not Delivered" - - elif self.status == GenericPayment.STATUS_ERROR: - status = "Unsuccessful" - - elif self.status == GenericPayment.STATUS_FORCE_FAILED: - status = "Force Failed" - - return status - - -class CashPlan(ConcurrencyModel, AdminUrlMixin, GenericPaymentPlan): - DISTRIBUTION_COMPLETED = "Distribution Completed" - DISTRIBUTION_COMPLETED_WITH_ERRORS = "Distribution Completed with Errors" - TRANSACTION_COMPLETED = "Transaction Completed" - TRANSACTION_COMPLETED_WITH_ERRORS = "Transaction Completed with Errors" - - STATUS_CHOICE = ( - (DISTRIBUTION_COMPLETED, _("Distribution Completed")), - ( - DISTRIBUTION_COMPLETED_WITH_ERRORS, - _("Distribution Completed with Errors"), - ), - (TRANSACTION_COMPLETED, _("Transaction Completed")), - ( - TRANSACTION_COMPLETED_WITH_ERRORS, - _("Transaction Completed with Errors"), - ), - ) - name = models.CharField(max_length=255, db_index=True) - ca_id = models.CharField(max_length=255, null=True, db_index=True) - ca_hash_id = models.UUIDField(unique=True, null=True) - status = models.CharField(max_length=255, choices=STATUS_CHOICE, db_index=True) - distribution_level = models.CharField(max_length=255) - dispersion_date = models.DateTimeField() - coverage_duration = models.PositiveIntegerField() - coverage_unit = models.CharField(max_length=255) - comments = models.CharField(max_length=255, null=True) - delivery_type = models.CharField( - choices=DeliveryMechanismChoices.DELIVERY_TYPE_CHOICES, - max_length=32, - null=True, - db_index=True, - ) - assistance_measurement = models.CharField(max_length=255, db_index=True) - assistance_through = models.CharField(max_length=255, db_index=True) - service_provider = models.ForeignKey( - "payment.ServiceProvider", - null=True, - related_name="cash_plans", - on_delete=models.CASCADE, - ) - vision_id = models.CharField(max_length=255, null=True) - funds_commitment = models.CharField(max_length=255, null=True) - down_payment = models.CharField(max_length=255, null=True) - validation_alerts_count = models.IntegerField() - total_persons_covered = models.IntegerField(db_index=True) - total_persons_covered_revised = models.IntegerField(db_index=True) - payment_verification_summary = GenericRelation( - "payment.PaymentVerificationSummary", - content_type_field="payment_plan_content_type", - object_id_field="payment_plan_object_id", - related_query_name="cash_plan", - ) - payment_verification_plan = GenericRelation( - "payment.PaymentVerificationPlan", - content_type_field="payment_plan_content_type", - object_id_field="payment_plan_object_id", - related_query_name="cash_plan", - ) - is_migrated_to_payment_plan = models.BooleanField(default=False) - - def __str__(self) -> str: - return self.name or "" - - @property - def payment_records_count(self) -> int: - return self.payment_items.count() - - @property - def bank_reconciliation_success(self) -> int: - return self.payment_items.filter(status__in=PaymentRecord.ALLOW_CREATE_VERIFICATION).count() - - @property - def bank_reconciliation_error(self) -> int: - return self.payment_items.filter(status=PaymentRecord.STATUS_ERROR).count() - - @cached_property - def total_number_of_households(self) -> int: - # https://unicef.visualstudio.com/ICTD-HCT-MIS/_workitems/edit/84040 - return self.payment_items.count() - - @property - def currency(self) -> Optional[str]: - payment_record = self.payment_items.first() - return payment_record.currency if payment_record else None - - @property - def currency_exchange_date(self) -> datetime: - return self.dispersion_date - - def unicef_id(self) -> str: - return self.ca_id - - @property - def verification_status(self) -> Optional[str]: - summary = self.payment_verification_summary.first() - return getattr(summary, "status", None) - - class Meta: - verbose_name = "Cash Plan" - ordering = ["created_at"] - - -class PaymentRecord(ConcurrencyModel, AdminUrlMixin, GenericPayment): - ENTITLEMENT_CARD_STATUS_ACTIVE = "ACTIVE" - ENTITLEMENT_CARD_STATUS_INACTIVE = "INACTIVE" - ENTITLEMENT_CARD_STATUS_CHOICE = Choices( - (ENTITLEMENT_CARD_STATUS_ACTIVE, _("Active")), - (ENTITLEMENT_CARD_STATUS_INACTIVE, _("Inactive")), - ) - - ca_id = models.CharField(max_length=255, null=True, db_index=True) - ca_hash_id = models.UUIDField(unique=True, null=True) - parent = models.ForeignKey( - "payment.CashPlan", - on_delete=models.CASCADE, - related_name="payment_items", - null=True, - ) - - full_name = models.CharField(max_length=255) - total_persons_covered = models.IntegerField() - distribution_modality = models.CharField( - max_length=255, - ) - target_population = models.ForeignKey( - "targeting.TargetPopulation", - on_delete=models.CASCADE, - related_name="payment_records", - ) - target_population_cash_assist_id = models.CharField(max_length=255) - entitlement_card_number = models.CharField(max_length=255, null=True) - entitlement_card_status = models.CharField( - choices=ENTITLEMENT_CARD_STATUS_CHOICE, default="ACTIVE", max_length=20, null=True - ) - entitlement_card_issue_date = models.DateField(null=True) - vision_id = models.CharField(max_length=255, null=True) - registration_ca_id = models.CharField(max_length=255, null=True) - service_provider = models.ForeignKey( - "payment.ServiceProvider", - on_delete=models.CASCADE, - ) - payment_verification = GenericRelation( - "payment.PaymentVerification", - content_type_field="payment_content_type", - object_id_field="payment_object_id", - related_query_name="payment_record", - ) - ticket_complaint_details = GenericRelation( - "grievance.TicketComplaintDetails", - content_type_field="payment_content_type", - object_id_field="payment_object_id", - related_query_name="payment_record", - ) - ticket_sensitive_details = GenericRelation( - "grievance.TicketSensitiveDetails", - content_type_field="payment_content_type", - object_id_field="payment_object_id", - related_query_name="payment_record", - ) - - @property - def unicef_id(self) -> str: - return self.ca_id - - def get_revert_mark_as_failed_status(self, delivered_quantity: Decimal) -> str: - return self.STATUS_SUCCESS - - -class ServiceProvider(TimeStampedUUIDModel): - business_area = models.ForeignKey("core.BusinessArea", on_delete=models.CASCADE) - ca_id = models.CharField(max_length=255, unique=True) - full_name = models.CharField(max_length=255, null=True) - short_name = models.CharField(max_length=100, null=True) - country = models.CharField(max_length=3) - vision_id = models.CharField(max_length=255, null=True) - is_migrated_to_payment_plan = models.BooleanField(default=False) - - def __str__(self) -> str: - return self.full_name or "" diff --git a/src/hct_mis_api/apps/payment/models/payment.py b/src/hct_mis_api/apps/payment/models/payment.py index 1b6e4874ae..ab890e285a 100644 --- a/src/hct_mis_api/apps/payment/models/payment.py +++ b/src/hct_mis_api/apps/payment/models/payment.py @@ -889,11 +889,11 @@ def available_payment_records( if payment_verification_plan: params &= Q( - Q(payment_verification__isnull=True) - | Q(payment_verification__payment_verification_plan=payment_verification_plan) + Q(payment_verifications__isnull=True) + | Q(payment_verifications__payment_verification_plan=payment_verification_plan) ) else: - params &= Q(payment_verification__isnull=True) + params &= Q(payment_verifications__isnull=True) payment_records = self.payment_items.select_related("head_of_household").filter(params).distinct() diff --git a/src/hct_mis_api/apps/payment/models/verification.py b/src/hct_mis_api/apps/payment/models/verification.py index 38dadc67d3..64aa5537e9 100644 --- a/src/hct_mis_api/apps/payment/models/verification.py +++ b/src/hct_mis_api/apps/payment/models/verification.py @@ -3,12 +3,10 @@ from typing import TYPE_CHECKING, Any, Optional from django.contrib.admin.options import get_content_type_for_model -from django.contrib.contenttypes.fields import GenericForeignKey -from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import ArrayField from django.core.validators import MinValueValidator from django.db import models -from django.db.models import Count, JSONField, Q, UniqueConstraint, UUIDField +from django.db.models import Count, JSONField, Q from django.db.models.signals import post_delete, post_save from django.dispatch import receiver from django.utils import timezone @@ -24,8 +22,9 @@ UnicefIdentifiedModel, ) -if TYPE_CHECKING: - from hct_mis_api.apps.program.models import Program # pragma: no cover +if TYPE_CHECKING: # pragma: no cover + from hct_mis_api.apps.payment.models import PaymentPlan + from hct_mis_api.apps.program.models import Program logger = logging.getLogger(__name__) @@ -81,11 +80,6 @@ class PaymentVerificationPlan(TimeStampedUUIDModel, ConcurrencyModel, UnicefIden ) status = models.CharField(max_length=50, choices=STATUS_CHOICES, default=STATUS_PENDING, db_index=True) - # TODO TP DROP - payment_plan_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True) - payment_plan_object_id = UUIDField(null=True) - payment_plan_obj = GenericForeignKey("payment_plan_content_type", "payment_plan_object_id") - payment_plan = models.ForeignKey( "payment.PaymentPlan", on_delete=models.CASCADE, related_name="payment_verification_plans", null=True ) @@ -112,10 +106,6 @@ class PaymentVerificationPlan(TimeStampedUUIDModel, ConcurrencyModel, UnicefIden class Meta: ordering = ("created_at",) - # TODO TP DROP - indexes = [ - models.Index(fields=["payment_plan_content_type", "payment_plan_object_id"]), - ] @property def business_area(self) -> BusinessArea: @@ -173,7 +163,7 @@ def get_program(self) -> Optional["Program"]: return self.payment_plan.program_cycle.program -def build_summary(payment_plan: Optional[Any]) -> None: +def build_summary(payment_plan: Optional["PaymentPlan"]) -> None: if not payment_plan: return @@ -240,16 +230,10 @@ class PaymentVerification(TimeStampedUUIDModel, ConcurrencyModel, AdminUrlMixin) related_name="payment_record_verifications", ) - # TODO TP DROP - payment_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True) - payment_object_id = UUIDField(null=True) - payment_obj = GenericForeignKey("payment_content_type", "payment_object_id") - - payment = models.OneToOneField( + payment = models.ForeignKey( "payment.Payment", on_delete=models.CASCADE, - related_name="payment_verification", - null=True, + related_name="payment_verifications", ) status = models.CharField(max_length=50, choices=STATUS_CHOICES, default=STATUS_PENDING) @@ -262,17 +246,6 @@ class PaymentVerification(TimeStampedUUIDModel, ConcurrencyModel, AdminUrlMixin) ) sent_to_rapid_pro = models.BooleanField(default=False) - class Meta: - indexes = [ - models.Index(fields=["payment_content_type", "payment_object_id"]), - ] - constraints = [ - UniqueConstraint( - fields=["payment_content_type", "payment_object_id"], - name="payment_content_type_and_payment_id", - ) - ] - @property def is_manually_editable(self) -> bool: if self.payment_verification_plan.verification_channel != PaymentVerificationPlan.VERIFICATION_CHANNEL_MANUAL: @@ -312,23 +285,6 @@ class PaymentVerificationSummary(TimeStampedUUIDModel): null=True, ) - # TODO TP drop - payment_plan_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True) - payment_plan_object_id = UUIDField(null=True) - payment_plan_obj = GenericForeignKey("payment_plan_content_type", "payment_plan_object_id") - - class Meta: - # TODO TP DROP - indexes = [ - models.Index(fields=["payment_plan_content_type", "payment_plan_object_id"]), - ] - constraints = [ - UniqueConstraint( - fields=["payment_plan_content_type", "payment_plan_object_id"], - name="payment_plan_content_type_and_payment_plan_id", - ) - ] - def mark_as_active(self) -> None: self.status = self.STATUS_ACTIVE self.completion_date = None diff --git a/src/hct_mis_api/apps/payment/pdf/payment_plan_export_pdf_service.py b/src/hct_mis_api/apps/payment/pdf/payment_plan_export_pdf_service.py index ab5112cac8..c8e6bf2fc6 100644 --- a/src/hct_mis_api/apps/payment/pdf/payment_plan_export_pdf_service.py +++ b/src/hct_mis_api/apps/payment/pdf/payment_plan_export_pdf_service.py @@ -6,7 +6,7 @@ from django.urls import reverse from hct_mis_api.apps.core.utils import encode_id_base64 -from hct_mis_api.apps.payment.models import Approval, GenericPayment, PaymentPlan +from hct_mis_api.apps.payment.models import Approval, Payment, PaymentPlan from hct_mis_api.apps.utils.pdf_generator import generate_pdf_from_html if TYPE_CHECKING: @@ -75,12 +75,12 @@ def generate_pdf_summary(self) -> Any: release = approval_process.approvals.filter(type=Approval.FINANCE_RELEASE).first() reconciliation_qs = self.payment_plan.eligible_payments.aggregate( - pending=Count("id", filter=Q(status__in=GenericPayment.PENDING_STATUSES)), - pending_usd=Sum("entitlement_quantity_usd", filter=Q(status__in=GenericPayment.PENDING_STATUSES)), - pending_local=Sum("entitlement_quantity", filter=Q(status__in=GenericPayment.PENDING_STATUSES)), - reconciled=Count("id", filter=~Q(status__in=GenericPayment.PENDING_STATUSES)), - reconciled_usd=Sum("delivered_quantity_usd", filter=~Q(status__in=GenericPayment.PENDING_STATUSES)), - reconciled_local=Sum("delivered_quantity", filter=~Q(status__in=GenericPayment.PENDING_STATUSES)), + pending=Count("id", filter=Q(status__in=Payment.PENDING_STATUSES)), + pending_usd=Sum("entitlement_quantity_usd", filter=Q(status__in=Payment.PENDING_STATUSES)), + pending_local=Sum("entitlement_quantity", filter=Q(status__in=Payment.PENDING_STATUSES)), + reconciled=Count("id", filter=~Q(status__in=Payment.PENDING_STATUSES)), + reconciled_usd=Sum("delivered_quantity_usd", filter=~Q(status__in=Payment.PENDING_STATUSES)), + reconciled_local=Sum("delivered_quantity", filter=~Q(status__in=Payment.PENDING_STATUSES)), ) pdf_context_data = { diff --git a/src/hct_mis_api/apps/payment/schema.py b/src/hct_mis_api/apps/payment/schema.py index ade4033e12..03b3ea2977 100644 --- a/src/hct_mis_api/apps/payment/schema.py +++ b/src/hct_mis_api/apps/payment/schema.py @@ -84,7 +84,6 @@ DeliveryMechanismPerPaymentPlan, FinancialServiceProvider, FinancialServiceProviderXlsxTemplate, - GenericPayment, Payment, PaymentHouseholdSnapshot, PaymentPlan, @@ -366,10 +365,11 @@ def resolve_target_population(self, info: Any) -> TargetPopulation: return self.parent.target_population def resolve_full_name(self, info: Any) -> str: + # TODO: add to test this one return self.head_of_household.full_name if self.head_of_household else "" def resolve_verification(self, info: Any) -> Optional[Any]: - return getattr(self, "payment_verification", None) + return self.payment_verifications.first() def resolve_distribution_modality(self, info: Any) -> str: return self.parent.unicef_id @@ -654,21 +654,21 @@ def resolve_total_withdrawn_households_count(self, info: Any) -> graphene.Int: @staticmethod def resolve_reconciliation_summary(parent: PaymentPlan, info: Any) -> Dict[str, int]: return parent.eligible_payments.aggregate( - delivered_fully=Count("id", filter=Q(status=GenericPayment.STATUS_DISTRIBUTION_SUCCESS)), - delivered_partially=Count("id", filter=Q(status=GenericPayment.STATUS_DISTRIBUTION_PARTIAL)), - not_delivered=Count("id", filter=Q(status=GenericPayment.STATUS_NOT_DISTRIBUTED)), + delivered_fully=Count("id", filter=Q(status=Payment.STATUS_DISTRIBUTION_SUCCESS)), + delivered_partially=Count("id", filter=Q(status=Payment.STATUS_DISTRIBUTION_PARTIAL)), + not_delivered=Count("id", filter=Q(status=Payment.STATUS_NOT_DISTRIBUTED)), unsuccessful=Count( "id", filter=Q( status__in=[ - GenericPayment.STATUS_ERROR, - GenericPayment.STATUS_FORCE_FAILED, - GenericPayment.STATUS_MANUALLY_CANCELLED, + Payment.STATUS_ERROR, + Payment.STATUS_FORCE_FAILED, + Payment.STATUS_MANUALLY_CANCELLED, ] ), ), - pending=Count("id", filter=Q(status__in=GenericPayment.PENDING_STATUSES)), - reconciled=Count("id", filter=~Q(status__in=GenericPayment.PENDING_STATUSES)), + pending=Count("id", filter=Q(status__in=Payment.PENDING_STATUSES)), + reconciled=Count("id", filter=~Q(status__in=Payment.PENDING_STATUSES)), number_of_payments=Count("id"), ) @@ -852,7 +852,7 @@ def resolve_status(self, info: Any, **kwargs: Any) -> str: return self.status.replace(" ", "_").upper() def resolve_verification(self, info: Any, **kwargs: Any) -> Any: - return getattr(self, "payment_verification", None) + return self.payment_verifications.first() class PageInfoNode(graphene.ObjectType): @@ -1288,8 +1288,8 @@ def resolve_chart_payment(self, info: Any, business_area_slug: str, year: int, * year, business_area_slug, chart_filters_decoder(kwargs) ) payment_items_dict = payment_items_qs.aggregate( - successful=Count("id", filter=~Q(status=GenericPayment.STATUS_ERROR)), - unsuccessful=Count("id", filter=Q(status=GenericPayment.STATUS_ERROR)), + successful=Count("id", filter=~Q(status=Payment.STATUS_ERROR)), + unsuccessful=Count("id", filter=Q(status=Payment.STATUS_ERROR)), ) dataset = [ diff --git a/src/hct_mis_api/apps/payment/services/create_payment_verifications.py b/src/hct_mis_api/apps/payment/services/create_payment_verifications.py index 32c597ff56..4873b22a25 100644 --- a/src/hct_mis_api/apps/payment/services/create_payment_verifications.py +++ b/src/hct_mis_api/apps/payment/services/create_payment_verifications.py @@ -3,16 +3,14 @@ from django.utils import timezone from hct_mis_api.apps.payment.models import ( - PaymentRecord, + Payment, PaymentVerification, PaymentVerificationPlan, ) class CreatePaymentVerifications: - def __init__( - self, payment_verification_plan: PaymentVerificationPlan, payment_records: Iterable[PaymentRecord] - ) -> None: + def __init__(self, payment_verification_plan: PaymentVerificationPlan, payment_records: Iterable[Payment]) -> None: self.payment_verification_plan = payment_verification_plan self.payment_records = payment_records diff --git a/src/hct_mis_api/apps/payment/services/dashboard_service.py b/src/hct_mis_api/apps/payment/services/dashboard_service.py index 6fc194460c..e11d5939bc 100644 --- a/src/hct_mis_api/apps/payment/services/dashboard_service.py +++ b/src/hct_mis_api/apps/payment/services/dashboard_service.py @@ -27,18 +27,12 @@ def payment_verification_chart_query( status_choices_mapping = dict(PaymentVerification.STATUS_CHOICES) params = Q() - params &= Q(Q(payment__delivery_date__year=year) | Q(payment_record__delivery_date__year=year)) - params &= Q( - Q(payment__business_area__slug=business_area_slug) | Q(payment_record__business_area__slug=business_area_slug) - ) - params &= Q( - Q(payment__household__collect_type=collect_type) | Q(payment_record__household__collect_type=collect_type) - ) + params &= Q(payment__delivery_date__year=year) + params &= Q(payment__business_area__slug=business_area_slug) + params &= Q(payment__household__collect_type=collect_type) if program: - params &= Q( - Q(payment__parent__program_cycle__program__id=program) | Q(payment_record__parent__program__id=program) - ) + params &= Q(payment__parent__program_cycle__program__id=program) if administrative_area: inner_params = Q() @@ -46,10 +40,6 @@ def payment_verification_chart_query( Q(payment__household__admin_area__id=administrative_area) & Q(payment__household__admin_area__area_type__area_level=2) ) - inner_params |= Q( - Q(payment_record__household__admin_area__id=administrative_area) - & Q(payment_record__household__admin_area__area_type__area_level=2) - ) params &= inner_params payment_verifications = PaymentVerification.objects.filter(params).distinct() @@ -69,9 +59,7 @@ def payment_verification_chart_query( for (dataset_percentage_value, status) in zip(dataset_percentage, status_choices_mapping.values()) ] - samples_count = payment_verifications.aggregate(payments_count=Count("payment") + Count("payment_record"))[ - "payments_count" - ] + samples_count = payment_verifications.aggregate(payments_count=Count("payment"))["payments_count"] all_payment_records_for_created_verifications = ( Payment.objects.filter(excluded=False, conflicted=False) .filter( @@ -90,12 +78,7 @@ def payment_verification_chart_query( ) households_number = ( - Household.objects.filter( - Q(pk__in=payment_verifications.values("payment__household")) - | Q(pk__in=payment_verifications.values("payment_record__household")) - ) - .distinct() - .count() + Household.objects.filter(Q(pk__in=payment_verifications.values("payment__household"))).distinct().count() ) return { @@ -117,20 +100,17 @@ def total_cash_transferred_by_administrative_area_table_query( return ( Area.objects.filter( - Q(household__paymentrecord__id__in=payment_items_ids) | Q(household__payment__id__in=payment_items_ids), + household__payment__id__in=payment_items_ids, area_type__area_level=2, ) .distinct() .annotate( - total_transferred_payment_records=Coalesce( - Sum("household__paymentrecord__delivered_quantity_usd", output_field=DecimalField()), Decimal(0.0) - ), total_transferred_payments=Coalesce( Sum("household__payment__delivered_quantity_usd", output_field=DecimalField()), Decimal(0.0) ), ) .annotate( num_households=Count("household", distinct=True), - total_transferred=F("total_transferred_payments") + F("total_transferred_payment_records"), + total_transferred=F("total_transferred_payments"), ) ) diff --git a/src/hct_mis_api/apps/payment/services/mark_as_failed.py b/src/hct_mis_api/apps/payment/services/mark_as_failed.py index 6bd43600d6..6e4fbf98de 100644 --- a/src/hct_mis_api/apps/payment/services/mark_as_failed.py +++ b/src/hct_mis_api/apps/payment/services/mark_as_failed.py @@ -1,26 +1,24 @@ import datetime from decimal import Decimal -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING from django.db.models import Sum -from hct_mis_api.apps.payment.models import Payment, PaymentRecord +from hct_mis_api.apps.payment.models import Payment from hct_mis_api.apps.payment.utils import get_quantity_in_usd if TYPE_CHECKING: from hct_mis_api.apps.household.models import Household -def mark_as_failed(payment_item: Union[PaymentRecord, Payment]) -> None: +def mark_as_failed(payment_item: Payment) -> None: payment_item.mark_as_failed() payment_item.save() recalculate_cash_received(payment_item.household) -def revert_mark_as_failed( - payment_item: Union[PaymentRecord, Payment], delivered_quantity: Decimal, delivery_date: datetime.datetime -) -> None: +def revert_mark_as_failed(payment_item: Payment, delivered_quantity: Decimal, delivery_date: datetime.datetime) -> None: payment_item.revert_mark_as_failed(delivered_quantity, delivery_date) payment_item.delivered_quantity_usd = get_quantity_in_usd( amount=delivered_quantity, diff --git a/src/hct_mis_api/apps/payment/tasks/CheckRapidProVerificationTask.py b/src/hct_mis_api/apps/payment/tasks/CheckRapidProVerificationTask.py index a30ce0df0e..1bf6967d0c 100644 --- a/src/hct_mis_api/apps/payment/tasks/CheckRapidProVerificationTask.py +++ b/src/hct_mis_api/apps/payment/tasks/CheckRapidProVerificationTask.py @@ -3,7 +3,7 @@ from hct_mis_api.apps.core.services.rapid_pro.api import RapidProAPI from hct_mis_api.apps.payment.models import ( - PaymentRecord, + Payment, PaymentVerification, PaymentVerificationPlan, ) @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) -def does_payment_record_have_right_hoh_phone_number(record: PaymentRecord) -> bool: +def does_payment_record_have_right_hoh_phone_number(record: Payment) -> bool: hoh = record.head_of_household if not hoh: logging.warning("Payment record has no head of household") diff --git a/src/hct_mis_api/apps/payment/xlsx/xlsx_payment_plan_per_fsp_import_service.py b/src/hct_mis_api/apps/payment/xlsx/xlsx_payment_plan_per_fsp_import_service.py index c7d0306249..b34460b5c1 100644 --- a/src/hct_mis_api/apps/payment/xlsx/xlsx_payment_plan_per_fsp_import_service.py +++ b/src/hct_mis_api/apps/payment/xlsx/xlsx_payment_plan_per_fsp_import_service.py @@ -347,9 +347,7 @@ def _import_row(self, row: Row, exchange_rate: float) -> None: self.payments_to_save.append(payment) # update PaymentVerification status - if hasattr(payment, "payment_verification"): - payment_verification = payment.payment_verification - + if payment_verification := payment.payment_verifications.first(): if payment_verification.status != PaymentVerification.STATUS_PENDING: if payment_verification.received_amount == delivered_quantity: pv_status = PaymentVerification.STATUS_RECEIVED diff --git a/src/hct_mis_api/apps/reporting/services/generate_report_service.py b/src/hct_mis_api/apps/reporting/services/generate_report_service.py index 0505ba7551..8741e869be 100644 --- a/src/hct_mis_api/apps/reporting/services/generate_report_service.py +++ b/src/hct_mis_api/apps/reporting/services/generate_report_service.py @@ -5,10 +5,7 @@ from typing import TYPE_CHECKING, List, Tuple from django.conf import settings -from django.contrib.postgres.aggregates.general import ArrayAgg -from django.contrib.postgres.fields import ArrayField from django.core.files import File -from django.db import models from django.db.models import ( Case, Count, @@ -24,7 +21,7 @@ Value, When, ) -from django.db.models.functions import Coalesce, Concat, Greatest, Least +from django.db.models.functions import Coalesce, Greatest, Least from django.template.loader import render_to_string import openpyxl @@ -46,9 +43,8 @@ ) from hct_mis_api.apps.payment.delivery_mechanisms import DeliveryMechanismChoices from hct_mis_api.apps.payment.models import ( - CashPlan, + Payment, PaymentPlan, - PaymentRecord, PaymentVerification, PaymentVerificationPlan, ) @@ -78,7 +74,7 @@ def get_individuals(report: Report) -> QuerySet[Individual]: return Individual.objects.filter(**filter_vars) @classmethod - def format_individual_row(self, individual: Individual) -> tuple: + def format_individual_row(cls, individual: Individual) -> tuple: return ( individual.household.id, individual.household.country_origin.name if individual.household.country_origin else "", @@ -97,7 +93,7 @@ def format_individual_row(self, individual: Individual) -> tuple: individual.selfcare_disability, individual.pregnant, individual.relationship, - self._to_values_list(individual.households_and_roles.all(), "role"), + cls._to_values_list(individual.households_and_roles.all(), "role"), dict(WORK_STATUS_CHOICE).get(individual.work_status, ""), individual.sanction_list_possible_match, individual.deduplication_batch_status, @@ -112,8 +108,8 @@ def format_individual_row(self, individual: Individual) -> tuple: if individual.deduplication_golden_record_results else "" ), - self._format_date(individual.first_registration_date), - self._format_date(individual.last_registration_date), + cls._format_date(individual.first_registration_date), + cls._format_date(individual.last_registration_date), ) @staticmethod @@ -131,7 +127,7 @@ def get_households(report: Report) -> QuerySet: return Household.objects.filter(**filter_vars) @classmethod - def format_household_row(self, household: Household) -> tuple: + def format_household_row(cls, household: Household) -> tuple: row = [ household.id, household.country_origin.name if household.country_origin else "", @@ -164,8 +160,8 @@ def format_household_row(self, household: Household) -> tuple: household.male_age_group_18_59_disabled_count, household.male_age_group_60_count, household.male_age_group_60_disabled_count, - self._format_date(household.first_registration_date), - self._format_date(household.last_registration_date), + cls._format_date(household.first_registration_date), + cls._format_date(household.last_registration_date), household.org_name_enumerator, ] for program in household.programs.all(): @@ -175,17 +171,19 @@ def format_household_row(self, household: Household) -> tuple: @staticmethod def get_cash_plan_verifications(report: Report) -> QuerySet: pp_business_area_ids = list( - CashPlan.objects.filter(business_area=report.business_area).values_list("id", flat=True) + PaymentPlan.objects.filter(business_area=report.business_area).values_list("id", flat=True) ) filter_vars = { - "payment_plan_object_id__in": pp_business_area_ids, + "payment_plan_id__in": pp_business_area_ids, "completion_date__isnull": False, "completion_date__gte": report.date_from, "completion_date__lte": report.date_to, } if report.program: - pp_program_ids = list(CashPlan.objects.filter(program=report.program).values_list("id", flat=True)) - filter_vars["payment_plan_object_id__in"] = pp_program_ids + pp_program_ids = list( + PaymentPlan.objects.filter(program_cycle__program=report.program).values_list("id", flat=True) + ) + filter_vars["payment_plan_id__in"] = pp_program_ids return PaymentVerificationPlan.objects.filter(**filter_vars) @staticmethod @@ -225,14 +223,14 @@ def get_payments(report: Report) -> QuerySet: filter_vars = { "business_area": report.business_area, "delivery_date__date__range": (report.date_from, report.date_to), - "parent__program": report.program, + "parent__program_cycle__program": report.program, } if report.admin_area.all().exists(): filter_vars["household__admin_area__in"] = report.admin_area.all() - return PaymentRecord.objects.filter(**filter_vars) + return Payment.objects.filter(**filter_vars) @classmethod - def format_payment_row(cls, payment: PaymentRecord) -> tuple: + def format_payment_row(cls, payment: Payment) -> tuple: cash_or_voucher = "" if payment.delivery_type: if payment.delivery_type in [ @@ -274,7 +272,9 @@ def get_payment_verifications(report: Report) -> QuerySet: "payment_verification_plan__completion_date__date__range": (report.date_from, report.date_to), } if report.program: - pp_program_ids = list(PaymentPlan.objects.filter(program=report.program).values_list("id", flat=True)) + pp_program_ids = list( + PaymentPlan.objects.filter(program_cycle__program=report.program).values_list("id", flat=True) + ) filter_vars["payment_verification_plan__payment_plan_id__in"] = pp_program_ids return PaymentVerification.objects.filter(**filter_vars) @@ -315,44 +315,6 @@ def format_payment_plan_row(cls, payment_plan: PaymentPlan) -> tuple: cls._format_date(payment_plan.dispersion_end_date), ) - @staticmethod - def get_cash_plans(report: Report) -> QuerySet: - filter_vars = { - "business_area": report.business_area, - "end_date__gte": report.date_from, - "end_date__lte": report.date_to, - } - if report.program: - filter_vars["program"] = report.program - return CashPlan.objects.filter(**filter_vars) - - @classmethod - def format_cash_plan_row(cls, cash_plan: CashPlan) -> tuple: - return ( - cash_plan.ca_id, - cash_plan.name, - cls._format_date(cash_plan.start_date), - cls._format_date(cash_plan.end_date), - cash_plan.program.name, - cash_plan.funds_commitment, - cash_plan.assistance_measurement, - cash_plan.assistance_through, - cash_plan.delivery_type, - cls._format_date(cash_plan.dispersion_date), - cash_plan.down_payment, - cash_plan.total_delivered_quantity, - cash_plan.total_undelivered_quantity, - cash_plan.total_entitled_quantity, - cash_plan.total_entitled_quantity_revised, - cash_plan.total_persons_covered, - cash_plan.total_persons_covered_revised, - cash_plan.status, - cls._format_date(cash_plan.status_date), - cash_plan.vision_id, - cash_plan.validation_alerts_count, - # cash_plan.verification_status, - ) - @staticmethod def get_payments_for_individuals(report: Report) -> QuerySet: if isinstance(report.date_to, str): @@ -362,54 +324,36 @@ def get_payments_for_individuals(report: Report) -> QuerySet: date_to_time += timedelta(days=1) filter_q = Q( # Q(household__program=report.program) & # TODO Uncomment after add program to household - Q( - Q(household__paymentrecord__business_area=report.business_area) - | Q(household__payment__business_area=report.business_area) - ) - & Q( - Q(household__paymentrecord__delivery_date__gte=report.date_from) - | Q(household__payment__delivery_date__gte=report.date_from) - ) - & Q( - Q(household__paymentrecord__delivery_date__lt=date_to_time) - | Q(household__payment__delivery_date__lt=date_to_time) - ) + Q(household__payment__business_area=report.business_area) + & Q(household__payment__delivery_date__gte=report.date_from) + & Q(household__payment__delivery_date__lt=date_to_time) ) if report.admin_area.all().exists(): filter_q &= Q(household__admin_area__in=report.admin_area.all()) if report.program: - filter_q &= Q( - Q(household__paymentrecord__parent__program=report.program) - | Q(household__payment__parent__program=report.program) - ) + filter_q &= Q(household__payment__parent__program_cycle__program=report.program) return ( Individual.objects.filter(filter_q) - .annotate(first_delivery_date_paymentrecord=Min("household__paymentrecord__delivery_date")) .annotate(first_delivery_date_payment=Min("household__payment__delivery_date")) .annotate( first_delivery_date=Least( - F("first_delivery_date_paymentrecord"), F("first_delivery_date_payment"), + Value("2099-12-31"), output_field=DateTimeField(), ) ) - .annotate(last_delivery_date_paymentrecord=Max("household__paymentrecord__delivery_date")) .annotate(last_delivery_date_payment=Max("household__payment__delivery_date")) .annotate( last_delivery_date=Greatest( - F("last_delivery_date_paymentrecord"), F("last_delivery_date_payment"), + Value("1990-01-01"), output_field=DateTimeField(), ) ) .annotate( payments_made=Count( Case( - When( - Q(household__paymentrecord__delivered_quantity__gte=0), - then=F("household__paymentrecord__id"), - ), When( Q(household__payment__delivered_quantity__gte=0), then=F("household__payment__id"), @@ -418,27 +362,16 @@ def get_payments_for_individuals(report: Report) -> QuerySet: ) ) ) - .annotate( - payment_currency=ArrayAgg( - Concat( - "household__paymentrecord__currency", - Value(" "), - "household__payment__currency", - output_field=ArrayField(models.CharField()), - ) - ) - ) + .annotate(payment_currency=F("household__payment__currency")) .annotate( total_delivered_quantity_local=Coalesce( - Sum("household__paymentrecord__delivered_quantity"), Value(0), output_field=DecimalField() + Sum("household__payment__delivered_quantity"), Value(0), output_field=DecimalField() ) - + Coalesce(Sum("household__payment__delivered_quantity"), Value(0), output_field=DecimalField()) ) .annotate( total_delivered_quantity_usd=Coalesce( - Sum("household__paymentrecord__delivered_quantity_usd"), Value(0), output_field=DecimalField() + Sum("household__payment__delivered_quantity_usd"), Value(0), output_field=DecimalField() ) - + Coalesce(Sum("household__payment__delivered_quantity_usd"), Value(0), output_field=DecimalField()) ) .order_by("household__id") .distinct() @@ -662,30 +595,6 @@ class GenerateReportService: "dispersion start date", "dispersion end date", ), - Report.CASH_PLAN: ( - "cash plan ID", # ANT-21-CSH-00001 - "cash plan name", - "start date", - "end date", - "programme", - "funds commitment", # 234567 - "assistance measurement", # Euro - "assistance through", # Cairo Amman Bank - "delivery type", # DEPOSIT_TO_CARD - "dispersion date", - "down payment", - "total delivered quantity", # 220,00 - "total undelivered quantity", # 10,00 - "total entitled quantity", # 230,00 - "total entitled quantity revised", # 230,00 - "total persons covered", # 12 - "total persons covered revised", # 12 - "status", # DISTRIBUTION_COMPLETED_WITH_ERRORS - "status date", - "VISION ID", # 2345253423 - "validation alerts count", # 2 - # "cash plan verification status", # FINISHED - ), Report.INDIVIDUALS_AND_PAYMENT: ( "household id", "country of origin", @@ -747,7 +656,6 @@ class GenerateReportService: Report.PAYMENTS: ("Delivery Date From", "Delivery Date To"), Report.PAYMENT_PLAN: ("Dispersion Start Date", "Dispersion End Date"), Report.INDIVIDUALS_AND_PAYMENT: ("Delivery Date From", "Delivery Date To"), - Report.CASH_PLAN: ("End Date From", "End Date To"), Report.GRIEVANCES: ("End Date From", "End Date To"), } ROW_CONTENT_METHODS = { @@ -772,10 +680,6 @@ class GenerateReportService: GenerateReportContentHelpers.get_payment_plans, GenerateReportContentHelpers.format_payment_plan_row, ), - Report.CASH_PLAN: ( - GenerateReportContentHelpers.get_cash_plans, - GenerateReportContentHelpers.format_cash_plan_row, - ), Report.INDIVIDUALS_AND_PAYMENT: ( GenerateReportContentHelpers.get_payments_for_individuals, GenerateReportContentHelpers.format_payments_for_individuals_row, diff --git a/src/hct_mis_api/apps/utils/models.py b/src/hct_mis_api/apps/utils/models.py index b9d42133b0..981aa3390d 100644 --- a/src/hct_mis_api/apps/utils/models.py +++ b/src/hct_mis_api/apps/utils/models.py @@ -636,7 +636,7 @@ def purge(cls) -> None: class InternalDataFieldModel(models.Model): - internal_data = models.JSONField(default=dict) + internal_data = models.JSONField(default=dict, blank=True) class Meta: abstract = True diff --git a/src/hct_mis_api/migrations_script/main.py b/src/hct_mis_api/migrations_script/main.py index be92686ad1..6a2c7c9038 100644 --- a/src/hct_mis_api/migrations_script/main.py +++ b/src/hct_mis_api/migrations_script/main.py @@ -50,6 +50,7 @@ def apply_migrations(): ("program", "0002_migration"), ("household", "0003_migration"), ("household", "0004_migration"), + ("household", "0005_migration"), ("grievance", "0004_migration"), ("payment", "0002_migration"), ("payment", "0003_migration"), @@ -57,6 +58,8 @@ def apply_migrations(): ("payment", "0005_migration"), ("payment", "0006_migration"), ("payment", "0007_migration"), + ("payment", "0008_migration"), + ("payment", "0009_migration"), ("aurora", "0003_migration"), ] fake_migrations(excluded_migrations) diff --git a/src/hct_mis_api/one_time_scripts/migrate_cash_assist_models.py b/src/hct_mis_api/one_time_scripts/migrate_cash_assist_models.py deleted file mode 100644 index 430d14baf6..0000000000 --- a/src/hct_mis_api/one_time_scripts/migrate_cash_assist_models.py +++ /dev/null @@ -1,309 +0,0 @@ -from decimal import Decimal - -from django.contrib.contenttypes.models import ContentType -from django.db import transaction -from django.db.models import Sum -from django.db.models.functions import Coalesce - -from hct_mis_api.apps.grievance.models import ( - TicketComplaintDetails, - TicketSensitiveDetails, -) -from hct_mis_api.apps.payment.models import ( - CashPlan, - DeliveryMechanism, - DeliveryMechanismPerPaymentPlan, - FinancialServiceProvider, - Payment, - PaymentPlan, - PaymentRecord, - PaymentVerification, - PaymentVerificationPlan, - PaymentVerificationSummary, - ServiceProvider, -) -from hct_mis_api.apps.payment.services.payment_household_snapshot_service import ( - create_payment_plan_snapshot_data, -) -from hct_mis_api.apps.targeting.models import TargetPopulation - - -def get_status(status: str) -> str: - mapping = {"Transaction Successful": "Distribution Successful"} - return mapping.get(status, status) - - -def migrate_cash_plan_to_payment_plan() -> None: - content_type_for_payment_plan = ContentType.objects.get_for_model(PaymentPlan) - content_type_for_cash_plan = ContentType.objects.get_for_model(CashPlan) - content_type_for_payment_record = ContentType.objects.get_for_model(PaymentRecord) - - print("**Migrating Cash Plan to Payment Plan**") - delivery_type_to_obj = {obj.name: obj for obj in DeliveryMechanism.objects.all()} - - print("Creating FinancialServiceProviders") - for sp in ServiceProvider.objects.filter(is_migrated_to_payment_plan=False): - print(f"\nProcessing Service Provider {sp}") - if not sp.cash_plans.exists(): - print(f"Service provider {sp} has no cash plans") - continue - - if FinancialServiceProvider.objects.filter(vision_vendor_number=sp.vision_id).exists(): - print(f"FinancialServiceProvider with vision_id {sp.vision_id} already exists") - continue - - if not sp.vision_id: - raise ValueError(f"Service Provider {sp} does not have vision_id") - - delivery_mechanisms = set(sp.cash_plans.all().values_list("delivery_type", flat=True)) - - fsp = FinancialServiceProvider.objects.create( - name=sp.full_name, - vision_vendor_number=sp.vision_id, - communication_channel="API", - internal_data={ - "is_cash_assist": True, - "business_area": sp.business_area.slug, - "country": sp.country, - "ca_id": sp.ca_id, - "short_name": sp.short_name, - }, - ) - fsp.delivery_mechanisms.set([delivery_type_to_obj[dt] for dt in delivery_mechanisms]) - sp.is_migrated_to_payment_plan = True - sp.save(update_fields=["is_migrated_to_payment_plan"]) - - fsp_vision_vendor_number_to_obj = {obj.vision_vendor_number: obj for obj in FinancialServiceProvider.objects.all()} - - dm_cash = delivery_type_to_obj["Cash"] - - cash_plans = CashPlan.objects.filter(is_migrated_to_payment_plan=False) - print(f"Total Cash Plans to migrate: {cash_plans.count()}") - cp_count = cash_plans.count() - cp_i = 0 - for cp in cash_plans.iterator(chunk_size=50): - if cp_i % 50 == 0: - print(f"Processing cash plan {cp_i}/{cp_count}") - cp_i += 1 - with transaction.atomic(): - if not cp.payment_items.exists(): - continue - - # get target populations from payment records - target_populations = cp.payment_items.values_list("target_population", flat=True).distinct() - tp_counter = 0 - # for each target population create a payment plan within tp.payment_cycle - for tp_id in target_populations: - tp = TargetPopulation.objects.get(id=tp_id) - payment_records = cp.payment_items.filter(target_population=tp) - first_record = payment_records.first() - if first_record.delivery_type: - delivery_mechanism = delivery_type_to_obj[first_record.delivery_type.name] - else: - delivery_mechanism = dm_cash - currency = first_record.currency - - # create payment plan - pp = PaymentPlan.objects.create( - status="FINISHED", - name=tp.name, - business_area_id=tp.business_area.id, - created_by_id=tp.created_by.id, - target_population_id=tp.id, - program_cycle_id=tp.program_cycle.id, - currency=currency, - dispersion_start_date=cp.start_date or tp.program_cycle.start_date, - dispersion_end_date=cp.dispersion_date or tp.program_cycle.end_date, - start_date=cp.start_date or tp.program_cycle.start_date, - end_date=cp.end_date or tp.program_cycle.end_date, - status_date=cp.status_date, - exchange_rate=cp.exchange_rate, - total_entitled_quantity=cp.total_entitled_quantity, - total_entitled_quantity_usd=cp.total_entitled_quantity_usd, - total_entitled_quantity_revised=cp.total_entitled_quantity_revised, - total_entitled_quantity_revised_usd=cp.total_entitled_quantity_revised_usd, - total_delivered_quantity=cp.total_delivered_quantity, - total_delivered_quantity_usd=cp.total_delivered_quantity_usd, - total_undelivered_quantity=cp.total_undelivered_quantity, - total_undelivered_quantity_usd=cp.total_undelivered_quantity_usd, - is_cash_assist=True, - internal_data={ - "name": cp.name, - "ca_hash_id": str(cp.ca_hash_id), - "distribution_level": cp.distribution_level, - "coverage_duration": cp.coverage_duration, - "coverage_unit": cp.coverage_unit, - "comments": cp.comments, - "assistance_measurement": cp.assistance_measurement, - "assistance_through": cp.assistance_through, - "vision_id": cp.vision_id, - "funds_commitment": cp.funds_commitment, - "down_payment": cp.down_payment, - "validation_alerts_count": cp.validation_alerts_count, - "total_persons_covered": cp.total_persons_covered, - "total_persons_covered_revised": cp.total_persons_covered_revised, - }, - ) - pp.created_at = cp.created_at - pp.unicef_id = cp.ca_id - pp.save(update_fields=["unicef_id", "created_at"]) - pp.update_population_count_fields() - - financial_service_provider = fsp_vision_vendor_number_to_obj.get(cp.service_provider.vision_id) - if not financial_service_provider: - raise ValueError( - f"FinancialServiceProvider not found for vision_id: {first_record.service_provider.vision_id}" - f"Cash Plan: {cp}" - f"Record: {first_record}" - f"Service Provider: {first_record.service_provider}" - ) - - dmppp = DeliveryMechanismPerPaymentPlan.objects.create( - payment_plan_id=pp.id, - delivery_mechanism_id=delivery_mechanism.id, - sent_date=cp.status_date, - delivery_mechanism_order=1, - created_by_id=tp.created_by.id, - financial_service_provider_id=financial_service_provider.id, - ) - dmppp.created_at = tp.created_at - dmppp.save(update_fields=["created_at"]) - - payment_verification_summary = PaymentVerificationSummary.objects.filter( - payment_plan_content_type_id=content_type_for_cash_plan.pk, payment_plan_object_id=cp.pk - ).first() - if payment_verification_summary: - if tp_counter > 0: - # create a copy of the summary for each target population - payment_verification_summary.pk = None - payment_verification_summary.payment_plan_content_type_id = content_type_for_payment_plan.id - payment_verification_summary.payment_plan_object_id = pp.id - payment_verification_summary.payment_plan = pp - payment_verification_summary.save() - else: - payment_verification_summary.payment_plan = pp - payment_verification_summary.save() - - payment_verification_plan = PaymentVerificationPlan.objects.filter( - payment_plan_content_type_id=content_type_for_cash_plan.pk, payment_plan_object_id=cp.pk - ).first() - if payment_verification_plan: - if tp_counter > 0: - # create a copy of the summary for each target population - _unicef_id = payment_verification_plan.unicef_id - payment_verification_plan.pk = None - payment_verification_plan.payment_plan_content_type_id = content_type_for_payment_plan.id - payment_verification_plan.payment_plan_object_id = pp.id - payment_verification_plan.payment_plan = pp - payment_verification_plan.save() - payment_verification_plan.unicef_id = _unicef_id - payment_verification_plan.save(update_fields=["unicef_id"]) - - else: - payment_verification_plan.payment_plan = pp - payment_verification_plan.save() - - with transaction.atomic(): - for record in cp.payment_items.filter(target_population=tp).prefetch_related("service_provider"): - financial_service_provider = fsp_vision_vendor_number_to_obj.get( - record.service_provider.vision_id - ) - payment = Payment.objects.create( - parent_id=pp.id, - business_area_id=pp.business_area.id, - status=get_status(record.status), - status_date=record.status_date, - household_id=record.household_id, - head_of_household_id=record.head_of_household_id or record.household.head_of_household_id, - collector_id=record.head_of_household_id or record.household.head_of_household_id, - delivery_type_id=record.delivery_type_id, - currency=record.currency, - entitlement_quantity=record.entitlement_quantity, - entitlement_quantity_usd=record.entitlement_quantity_usd, - delivered_quantity=record.delivered_quantity, - delivered_quantity_usd=record.delivered_quantity_usd, - delivery_date=record.delivery_date, - transaction_reference_id=record.transaction_reference_id, - transaction_status_blockchain_link=record.transaction_status_blockchain_link, - financial_service_provider=financial_service_provider, - program_id=tp.program_cycle.program_id, - is_cash_assist=True, - internal_data={ - "ca_hash_id": str(record.ca_hash_id), - "full_name": record.full_name, - "total_persons_covered": record.total_persons_covered, - "distribution_modality": record.distribution_modality, - "target_population_cash_assist_id": record.target_population_cash_assist_id, - "target_population": str(record.target_population_id), - "entitlement_card_number": record.entitlement_card_number, - "entitlement_card_status": record.entitlement_card_status, - "entitlement_card_issue_date": str(record.entitlement_card_issue_date), - "vision_id": record.vision_id, - "registration_ca_id": record.registration_ca_id, - "service_provider": str(record.service_provider_id), - }, - ) - payment.unicef_id = record.ca_id - payment.save(update_fields=["unicef_id"]) - - payment_record_verification = PaymentVerification.objects.filter( - payment_content_type_id=content_type_for_payment_record.pk, payment_object_id=record.pk - ).first() - if payment_record_verification: - payment_record_verification.payment_verification_plan = payment_verification_plan - payment_record_verification.payment = payment - payment_record_verification.save() - - ticket_complaint_details = TicketComplaintDetails.objects.filter( - payment_content_type_id=content_type_for_payment_record.pk, payment_object_id=record.pk - ).first() - if ticket_complaint_details: - ticket_complaint_details.payment = payment - ticket_complaint_details.save() - - ticket_sensitive_details = TicketSensitiveDetails.objects.filter( - payment_content_type_id=content_type_for_payment_record.pk, payment_object_id=record.pk - ).first() - if ticket_sensitive_details: - ticket_sensitive_details.payment = payment - ticket_sensitive_details.save() - - create_payment_plan_snapshot_data(pp) - - pp.update_population_count_fields() - - payments = pp.eligible_payments.aggregate( - total_entitled_quantity=Coalesce(Sum("entitlement_quantity"), Decimal(0.0)), - total_entitled_quantity_usd=Coalesce(Sum("entitlement_quantity_usd"), Decimal(0.0)), - total_delivered_quantity=Coalesce(Sum("delivered_quantity"), Decimal(0.0)), - total_delivered_quantity_usd=Coalesce(Sum("delivered_quantity_usd"), Decimal(0.0)), - ) - - pp.total_entitled_quantity = payments.get("total_entitled_quantity", 0.00) - pp.total_entitled_quantity_usd = payments.get("total_entitled_quantity_usd", 0.00) - pp.total_delivered_quantity = payments.get("total_delivered_quantity", 0.00) - pp.total_delivered_quantity_usd = payments.get("total_delivered_quantity_usd", 0.00) - - pp.total_undelivered_quantity = pp.total_entitled_quantity - pp.total_delivered_quantity - pp.total_undelivered_quantity_usd = pp.total_entitled_quantity_usd - pp.total_delivered_quantity_usd - - pp.save( - update_fields=[ - "total_entitled_quantity", - "total_entitled_quantity_usd", - "total_delivered_quantity", - "total_delivered_quantity_usd", - "total_undelivered_quantity", - "total_undelivered_quantity_usd", - ] - ) - - tp_counter += 1 - - cp.is_migrated_to_payment_plan = True - cp.save(update_fields=["is_migrated_to_payment_plan"]) - - -def migrate_cash_assist_models() -> None: - print("***Migrating Cash Assist models to Payment models***") - migrate_cash_plan_to_payment_plan() diff --git a/src/hct_mis_api/one_time_scripts/migrate_data_to_representations.py b/src/hct_mis_api/one_time_scripts/migrate_data_to_representations.py index 84eb814546..5d247a078c 100644 --- a/src/hct_mis_api/one_time_scripts/migrate_data_to_representations.py +++ b/src/hct_mis_api/one_time_scripts/migrate_data_to_representations.py @@ -23,7 +23,7 @@ IndividualIdentity, IndividualRoleInHousehold, ) -from hct_mis_api.apps.payment.models import Payment, PaymentRecord +from hct_mis_api.apps.payment.models import Payment from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.models import RegistrationDataImport from hct_mis_api.apps.targeting.models import HouseholdSelection, TargetPopulation @@ -598,8 +598,6 @@ def adjust_payment_objects(business_area: Optional[BusinessArea] = None) -> None for business_area in business_areas: logger.info(f"Adjusting payments for business area {business_area.name}") adjust_payments(business_area) # type: ignore - logger.info(f"Adjusting payment records for business area {business_area.name}") - adjust_payment_records(business_area) # type: ignore def adjust_payments(business_area: BusinessArea) -> None: @@ -654,46 +652,6 @@ def adjust_payments(business_area: BusinessArea) -> None: del payment_updates -def adjust_payment_records(business_area: BusinessArea) -> None: - """ - Adjust PaymentRecord individuals and households to their representations. - PaymentRecord is already related to program through TargetPopulation. - """ - payment_records_ids = list( - PaymentRecord.objects.filter( - target_population__program__business_area=business_area, household__is_original=True - ).values_list("pk", flat=True) - ) - payment_records_count = len(payment_records_ids) - for batch_start in range(0, payment_records_count, BATCH_SIZE): - batch_end = batch_start + BATCH_SIZE - logger.info(f"Adjusting payment records {batch_start} - {batch_end}/{payment_records_count}") - payment_record_updates = [] - - payment_records_batch = PaymentRecord.objects.filter(id__in=payment_records_ids[batch_start:batch_end]) - for payment_record in payment_records_batch: - payment_record_program = payment_record.target_population.program - if payment_record.head_of_household: - representation_head_of_household = get_individual_representation_per_program_by_old_individual_id( - program=payment_record_program, - old_individual_id=payment_record.head_of_household_id, - ) - else: - representation_head_of_household = None - representation_household = get_household_representation_per_program_by_old_household_id( - program=payment_record_program, - old_household_id=payment_record.household_id, - ) - payment_record.refresh_from_db() - if representation_household: - payment_record.head_of_household = representation_head_of_household - payment_record.household = representation_household - payment_record_updates.append(payment_record) - - PaymentRecord.objects.bulk_update(payment_record_updates, fields=["head_of_household_id", "household_id"]) - del payment_record_updates - - def handle_rdis(rdis: QuerySet, program: Program, hhs_to_ignore: Optional[QuerySet] = None) -> None: rdis_count = rdis.count() for i, rdi in enumerate(rdis): diff --git a/tests/selenium/filters/test_filters.py b/tests/selenium/filters/test_filters.py index 8ba36abb3c..8b78fb29ff 100644 --- a/tests/selenium/filters/test_filters.py +++ b/tests/selenium/filters/test_filters.py @@ -17,7 +17,7 @@ PaymentVerificationPlanFactory, PaymentVerificationSummaryFactory, ) -from hct_mis_api.apps.payment.models import GenericPayment, PaymentPlan +from hct_mis_api.apps.payment.models import Payment, PaymentPlan from hct_mis_api.apps.payment.models import PaymentVerification as PV from hct_mis_api.apps.payment.models import PaymentVerificationPlan from hct_mis_api.apps.program.fixtures import ProgramFactory @@ -195,7 +195,7 @@ def payment_verification_creator( entitlement_quantity=21.36, delivered_quantity=21.36, currency="PLN", - status=GenericPayment.STATUS_DISTRIBUTION_SUCCESS, + status=Payment.STATUS_DISTRIBUTION_SUCCESS, ) pv_summary = PaymentVerificationSummaryFactory(payment_plan=payment_plan) pv_summary.activation_date = datetime.now() - relativedelta(months=1) diff --git a/tests/selenium/payment_verification/test_payment_verification.py b/tests/selenium/payment_verification/test_payment_verification.py index 31505e9db8..ba88b48bc2 100644 --- a/tests/selenium/payment_verification/test_payment_verification.py +++ b/tests/selenium/payment_verification/test_payment_verification.py @@ -20,7 +20,7 @@ PaymentVerificationPlanFactory, PaymentVerificationSummaryFactory, ) -from hct_mis_api.apps.payment.models import GenericPayment, Payment, PaymentPlan +from hct_mis_api.apps.payment.models import Payment, PaymentPlan from hct_mis_api.apps.payment.models import PaymentVerification as PV from hct_mis_api.apps.payment.models import PaymentVerificationPlan from hct_mis_api.apps.program.fixtures import ProgramFactory @@ -131,7 +131,7 @@ def payment_verification_multiple_verification_plans(number_verification_plans: entitlement_quantity=Decimal(21.36), delivered_quantity=Decimal(21.36), currency="PLN", - status=GenericPayment.STATUS_DISTRIBUTION_SUCCESS, + status=Payment.STATUS_DISTRIBUTION_SUCCESS, ) ) @@ -178,7 +178,7 @@ def empty_payment_verification(social_worker_program: Program) -> None: entitlement_quantity=Decimal(21.36), delivered_quantity=Decimal(21.36), currency="PLN", - status=GenericPayment.STATUS_DISTRIBUTION_SUCCESS, + status=Payment.STATUS_DISTRIBUTION_SUCCESS, ) PaymentVerificationSummaryFactory(payment_plan=payment_plan) @@ -194,8 +194,9 @@ def add_payment_verification_xlsx() -> PV: def payment_verification_creator(channel: str = PaymentVerificationPlan.VERIFICATION_CHANNEL_MANUAL) -> PV: + user = User.objects.first() registration_data_import = RegistrationDataImportFactory( - imported_by=User.objects.first(), business_area=BusinessArea.objects.first() + imported_by=user, business_area=BusinessArea.objects.first() ) program = Program.objects.filter(name="Active Program").first() household, individuals = create_household( @@ -214,6 +215,7 @@ def payment_verification_creator(channel: str = PaymentVerificationPlan.VERIFICA business_area=BusinessArea.objects.first(), start_date=datetime.now() - relativedelta(months=1), end_date=datetime.now() + relativedelta(months=1), + created_by=user, ) payment_plan.unicef_id = "PP-0000-00-1122334" @@ -229,7 +231,7 @@ def payment_verification_creator(channel: str = PaymentVerificationPlan.VERIFICA entitlement_quantity=21.36, delivered_quantity=21.36, currency="PLN", - status=GenericPayment.STATUS_DISTRIBUTION_SUCCESS, + status=Payment.STATUS_DISTRIBUTION_SUCCESS, ) payment_verification_plan = PaymentVerificationPlanFactory( payment_plan=payment_plan, @@ -363,7 +365,7 @@ def test_happy_path_payment_verification( assert payment_record.household.unicef_id in pagePaymentRecord.getLabelHousehold().text assert payment_record.parent.target_population.name in pagePaymentRecord.getLabelTargetPopulation().text assert payment_record.parent.unicef_id in pagePaymentRecord.getLabelDistributionModality().text - assert payment_record.payment_verification.status in pagePaymentRecord.getLabelStatus()[1].text + assert payment_record.payment_verifications.first().status in pagePaymentRecord.getLabelStatus()[1].text assert "PLN 0.00" in pagePaymentRecord.getLabelAmountReceived().text assert payment_record.household.unicef_id in pagePaymentRecord.getLabelHouseholdId().text assert "21.36" in pagePaymentRecord.getLabelEntitlementQuantity().text diff --git a/tests/selenium/people/test_people.py b/tests/selenium/people/test_people.py index da78b48eaa..748fb297fb 100644 --- a/tests/selenium/people/test_people.py +++ b/tests/selenium/people/test_people.py @@ -15,7 +15,7 @@ ) from hct_mis_api.apps.household.models import HOST, SEEING, Individual from hct_mis_api.apps.payment.fixtures import PaymentFactory, PaymentPlanFactory -from hct_mis_api.apps.payment.models import GenericPayment, PaymentRecord +from hct_mis_api.apps.payment.models import Payment from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import BeneficiaryGroup, Program from tests.selenium.page_object.filters import Filters @@ -58,7 +58,7 @@ def add_people(social_worker_program: Program) -> List: @pytest.fixture -def add_people_with_payment_record(add_people: List) -> PaymentRecord: +def add_people_with_payment_record(add_people: List) -> Payment: program = Program.objects.filter(name="Worker Program").first() payment_plan = PaymentPlanFactory( @@ -74,7 +74,7 @@ def add_people_with_payment_record(add_people: List) -> PaymentRecord: entitlement_quantity=21.36, delivered_quantity=21.36, currency="PLN", - status=GenericPayment.STATUS_DISTRIBUTION_SUCCESS, + status=Payment.STATUS_DISTRIBUTION_SUCCESS, ) add_people[1].total_cash_received_usd = 21.36 add_people[1].save() @@ -239,7 +239,7 @@ def test_smoke_page_details_people( @pytest.mark.xfail(reason="UNSTABLE") def test_people_happy_path( self, - add_people_with_payment_record: PaymentRecord, + add_people_with_payment_record: Payment, pagePeople: People, pagePeopleDetails: PeopleDetails, ) -> None: diff --git a/tests/selenium/people/test_people_periodic_data_update.py b/tests/selenium/people/test_people_periodic_data_update.py index 0be5335452..6ce4586840 100644 --- a/tests/selenium/people/test_people_periodic_data_update.py +++ b/tests/selenium/people/test_people_periodic_data_update.py @@ -15,7 +15,7 @@ from hct_mis_api.apps.household.fixtures import create_household_and_individuals from hct_mis_api.apps.household.models import HOST, SEEING, Individual from hct_mis_api.apps.payment.fixtures import PaymentFactory, PaymentPlanFactory -from hct_mis_api.apps.payment.models import GenericPayment +from hct_mis_api.apps.payment.models import Payment from hct_mis_api.apps.periodic_data_update.fixtures import ( PeriodicDataUpdateTemplateFactory, PeriodicDataUpdateUploadFactory, @@ -97,7 +97,7 @@ def individual(add_people: Individual) -> Individual: entitlement_quantity=21.36, delivered_quantity=21.36, currency="PLN", - status=GenericPayment.STATUS_DISTRIBUTION_SUCCESS, + status=Payment.STATUS_DISTRIBUTION_SUCCESS, ) add_people.total_cash_received_usd = 21.36 add_people.save() diff --git a/tests/unit/apps/grievance/test_filter_already_existing_tickets.py b/tests/unit/apps/grievance/test_filter_already_existing_tickets.py index ceb871822b..16136a7631 100644 --- a/tests/unit/apps/grievance/test_filter_already_existing_tickets.py +++ b/tests/unit/apps/grievance/test_filter_already_existing_tickets.py @@ -130,7 +130,7 @@ def setUpTestData(cls) -> None: cls.ticket = SensitiveGrievanceTicketWithoutExtrasFactory( household=cls.household_1, individual=cls.individuals_1[0], - payment_obj=cls.payment, + payment=cls.payment, ticket=grievance_1, ) SensitiveGrievanceTicketWithoutExtrasFactory( @@ -141,7 +141,7 @@ def setUpTestData(cls) -> None: SensitiveGrievanceTicketWithoutExtrasFactory( household=cls.household_3, individual=cls.individuals_3[0], - payment_obj=cls.payment_record2, + payment=cls.payment_record2, ticket=grievance_3, ) GrievanceComplaintTicketFactory.create_batch(5) diff --git a/tests/unit/apps/payment/services/test_dashboard_service.py b/tests/unit/apps/payment/services/test_dashboard_service.py index 295cf562b6..a569aeb525 100644 --- a/tests/unit/apps/payment/services/test_dashboard_service.py +++ b/tests/unit/apps/payment/services/test_dashboard_service.py @@ -15,7 +15,7 @@ PaymentPlanFactory, generate_delivery_mechanisms, ) -from hct_mis_api.apps.payment.models import DeliveryMechanism, GenericPayment +from hct_mis_api.apps.payment.models import DeliveryMechanism, Payment from hct_mis_api.apps.payment.services.dashboard_service import ( payment_verification_chart_query, ) @@ -87,7 +87,7 @@ def setUp(self) -> None: delivery_type=self.dm_cash, delivered_quantity=10 + num, delivered_quantity_usd=10 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, currency="PLN", ) @@ -98,7 +98,7 @@ def setUp(self) -> None: delivery_type=self.dm_voucher, delivered_quantity=20 + num, delivered_quantity_usd=20 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, currency="PLN", ) @@ -109,7 +109,7 @@ def setUp(self) -> None: delivery_type=self.dm_cash, delivered_quantity=30 + num, delivered_quantity_usd=30 + num, - status=GenericPayment.STATUS_ERROR, + status=Payment.STATUS_ERROR, business_area=business_area, currency="PLN", ) @@ -121,7 +121,7 @@ def setUp(self) -> None: delivery_type=self.dm_cash, delivered_quantity=10 + num, delivered_quantity_usd=10 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, household=household4, currency="PLN", @@ -133,7 +133,7 @@ def setUp(self) -> None: delivery_type=self.dm_voucher, delivered_quantity=20 + num, delivered_quantity_usd=20 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, household=household5, currency="PLN", @@ -145,7 +145,7 @@ def setUp(self) -> None: delivery_type=self.dm_cash, delivered_quantity=30 + num, delivered_quantity_usd=30 + num, - status=GenericPayment.STATUS_ERROR, + status=Payment.STATUS_ERROR, business_area=business_area, household=household6, currency="PLN", diff --git a/tests/unit/apps/payment/snapshots/snap_test_all_payment_plan_queries.py b/tests/unit/apps/payment/snapshots/snap_test_all_payment_plan_queries.py index 7022088f32..7d078d975b 100644 --- a/tests/unit/apps/payment/snapshots/snap_test_all_payment_plan_queries.py +++ b/tests/unit/apps/payment/snapshots/snap_test_all_payment_plan_queries.py @@ -7,6 +7,22 @@ snapshots = Snapshot() +snapshots['TestPaymentPlanQueries::test_all_payment_verification_log_entries 1'] = { + 'data': { + 'allPaymentVerificationLogEntries': { + 'edges': [ + { + 'node': { + 'action': 'CREATE', + 'isUserGenerated': None + } + } + ], + 'totalCount': 1 + } + } +} + snapshots['TestPaymentPlanQueries::test_fetch_all_payment_plans 1'] = { 'data': { 'allPaymentPlans': { @@ -404,13 +420,15 @@ 'data': { 'payment': { 'additionalCollectorName': None, + 'fullName': 'First1 Mid1 Last1', 'reasonForUnsuccessfulPayment': 'reason 123', 'snapshotCollectorBankAccountNumber': None, 'snapshotCollectorBankName': None, 'snapshotCollectorDebitCardNumber': None, 'snapshotCollectorDeliveryPhoneNo': None, 'snapshotCollectorFullName': None, - 'totalPersonsCovered': 5 + 'totalPersonsCovered': 5, + 'verification': None } } } @@ -419,13 +437,15 @@ 'data': { 'payment': { 'additionalCollectorName': 'AddCollectorName11', + 'fullName': 'First2 Mid2 Last3', 'reasonForUnsuccessfulPayment': 'reason 222', 'snapshotCollectorBankAccountNumber': 'PrimaryCollBankNumber', 'snapshotCollectorBankName': 'PrimaryCollBankName', 'snapshotCollectorDebitCardNumber': 'PrimaryCollDebitCardNumber', 'snapshotCollectorDeliveryPhoneNo': '1111111', 'snapshotCollectorFullName': 'PrimaryCollectorFullName', - 'totalPersonsCovered': 99 + 'totalPersonsCovered': 99, + 'verification': None } } } @@ -434,13 +454,15 @@ 'data': { 'payment': { 'additionalCollectorName': 'AddCollectorName22', + 'fullName': 'First3 Mid3 Last3', 'reasonForUnsuccessfulPayment': 'reason 333', 'snapshotCollectorBankAccountNumber': 'AlternateCollBankNumber', 'snapshotCollectorBankName': 'AlternateCollBankName', 'snapshotCollectorDebitCardNumber': 'AlternateCollDebitCardNumber', 'snapshotCollectorDeliveryPhoneNo': '222222222', 'snapshotCollectorFullName': 'AlternateCollectorFullName', - 'totalPersonsCovered': 55 + 'totalPersonsCovered': 55, + 'verification': None } } } diff --git a/tests/unit/apps/payment/test_all_payment_plan_queries.py b/tests/unit/apps/payment/test_all_payment_plan_queries.py index f9274d3b5b..549ca2d9f8 100644 --- a/tests/unit/apps/payment/test_all_payment_plan_queries.py +++ b/tests/unit/apps/payment/test_all_payment_plan_queries.py @@ -11,15 +11,18 @@ from hct_mis_api.apps.account.fixtures import UserFactory from hct_mis_api.apps.account.permissions import Permissions +from hct_mis_api.apps.activity_log.models import LogEntry +from hct_mis_api.apps.activity_log.utils import create_diff from hct_mis_api.apps.core.base_test_case import APITestCase from hct_mis_api.apps.core.fixtures import create_afghanistan -from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.core.utils import encode_id_base64 from hct_mis_api.apps.household.fixtures import HouseholdFactory, IndividualFactory from hct_mis_api.apps.payment.fixtures import ( FinancialServiceProviderFactory, PaymentFactory, PaymentPlanFactory, + PaymentVerificationPlanFactory, + PaymentVerificationSummaryFactory, RealProgramFactory, ) from hct_mis_api.apps.payment.models import ( @@ -28,6 +31,7 @@ PaymentHouseholdSnapshot, PaymentPlan, PaymentPlanSupportingDocument, + PaymentVerificationPlan, ) from hct_mis_api.apps.program.fixtures import ProgramCycleFactory @@ -203,6 +207,7 @@ class TestPaymentPlanQueries(APITestCase): query Payment($id: ID!) { payment(id: $id) { totalPersonsCovered + fullName snapshotCollectorFullName snapshotCollectorDeliveryPhoneNo snapshotCollectorBankName @@ -210,6 +215,9 @@ class TestPaymentPlanQueries(APITestCase): snapshotCollectorDebitCardNumber additionalCollectorName reasonForUnsuccessfulPayment + verification { + status + } } } """ @@ -217,12 +225,12 @@ class TestPaymentPlanQueries(APITestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() - create_afghanistan() + cls.business_area = create_afghanistan() cls.user = UserFactory.create(username="qazxsw321") cls.create_user_role_with_permissions( cls.user, - [Permissions.PM_VIEW_LIST, Permissions.PM_VIEW_DETAILS], - BusinessArea.objects.get(slug="afghanistan"), + [Permissions.PM_VIEW_LIST, Permissions.PM_VIEW_DETAILS, Permissions.ACTIVITY_LOG_VIEW], + cls.business_area, ) with freeze_time("2020-10-10"): @@ -236,6 +244,7 @@ def setUpTestData(cls) -> None: dispersion_start_date=datetime(2020, 8, 10), dispersion_end_date=datetime(2020, 12, 10), is_follow_up=False, + created_by=cls.user, ) cls.pp.unicef_id = "PP-01" cls.pp.save() @@ -309,7 +318,7 @@ def setUpTestData(cls) -> None: IndividualFactory(household=hh2, sex="MALE", birth_date=datetime.now().date() - relativedelta(years=20)) AcceptanceProcessThreshold.objects.create( - business_area=BusinessArea.objects.first(), + business_area=cls.business_area, approval_number_required=2, authorization_number_required=2, finance_release_number_required=3, @@ -467,9 +476,9 @@ def test_payment_node_with_legacy_data(self) -> None: dispersion_end_date=datetime(2023, 12, 10), is_follow_up=False, ) - hoh_1 = IndividualFactory(household=None) - hoh_2 = IndividualFactory(household=None) - hoh_3 = IndividualFactory(household=None) + hoh_1 = IndividualFactory(household=None, given_name="First1", middle_name="Mid1", family_name="Last1") + hoh_2 = IndividualFactory(household=None, given_name="First2", middle_name="Mid2", family_name="Last3") + hoh_3 = IndividualFactory(household=None, given_name="First3", middle_name="Mid3", family_name="Last3") household_1 = HouseholdFactory(head_of_household=hoh_1, size=5) household_2 = HouseholdFactory(head_of_household=hoh_2, size=10) household_3 = HouseholdFactory(head_of_household=hoh_3, size=15) @@ -538,3 +547,45 @@ def test_payment_node_with_legacy_data(self) -> None: context={"user": self.user}, variables={"id": encode_id_base64(payment_id, "Payment")}, ) + + def test_all_payment_verification_log_entries(self) -> None: + query = """ + query allPaymentVerificationLogEntries($objectId: UUID, $businessArea: String!) { + allPaymentVerificationLogEntries(objectId: $objectId, businessArea: $businessArea) { + totalCount + edges { + node { + isUserGenerated + action + } + } + } + } + """ + payment_plan_id = str(self.pp.id) + PaymentVerificationSummaryFactory(payment_plan=self.pp) + PaymentVerificationSummaryFactory(payment_plan=self.pp_conflicted) + pvp = PaymentVerificationPlanFactory(payment_plan=self.pp) + pvp2 = PaymentVerificationPlanFactory(payment_plan=self.pp_conflicted) + LogEntry.objects.create( + action=LogEntry.CREATE, + content_object=pvp, + user=self.user, + business_area=self.business_area, + object_repr=str(pvp), + changes=create_diff(None, pvp, PaymentVerificationPlan.ACTIVITY_LOG_MAPPING), + ) + LogEntry.objects.create( + action=LogEntry.CREATE, + content_object=pvp2, + user=self.user, + business_area=self.business_area, + object_repr=str(pvp2), + changes=create_diff(None, pvp2, PaymentVerificationPlan.ACTIVITY_LOG_MAPPING), + ) + + self.snapshot_graphql_request( + request_string=query, + context={"user": self.user}, + variables={"objectId": payment_plan_id, "businessArea": "afghanistan"}, + ) diff --git a/tests/unit/apps/payment/test_dashboard_queries.py b/tests/unit/apps/payment/test_dashboard_queries.py index b81534f076..1cbc0cb99d 100644 --- a/tests/unit/apps/payment/test_dashboard_queries.py +++ b/tests/unit/apps/payment/test_dashboard_queries.py @@ -22,7 +22,7 @@ PaymentPlanFactory, generate_delivery_mechanisms, ) -from hct_mis_api.apps.payment.models import DeliveryMechanism, GenericPayment, Payment +from hct_mis_api.apps.payment.models import DeliveryMechanism, Payment from hct_mis_api.apps.program.fixtures import ProgramFactory @@ -244,7 +244,7 @@ def setUpTestData(cls) -> None: delivery_type=cls.dm_cash, delivered_quantity=10 + num, delivered_quantity_usd=10 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, currency="PLN", ) @@ -255,7 +255,7 @@ def setUpTestData(cls) -> None: delivery_type=cls.dm_voucher, delivered_quantity=20 + num, delivered_quantity_usd=20 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, currency="PLN", ) @@ -266,7 +266,7 @@ def setUpTestData(cls) -> None: delivery_type=cls.dm_voucher, delivered_quantity=20 + num, delivered_quantity_usd=20 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, currency="PLN", ) @@ -277,7 +277,7 @@ def setUpTestData(cls) -> None: delivery_type=cls.dm_cash, delivered_quantity=30 + num, delivered_quantity_usd=30 + num, - status=GenericPayment.STATUS_ERROR, + status=Payment.STATUS_ERROR, business_area=business_area, currency="PLN", ) @@ -288,7 +288,7 @@ def setUpTestData(cls) -> None: delivery_type=cls.dm_voucher, delivered_quantity=20 + num, delivered_quantity_usd=20 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, currency="PLN", ) @@ -299,7 +299,7 @@ def setUpTestData(cls) -> None: delivery_type=cls.dm_voucher, delivered_quantity=20 + num, delivered_quantity_usd=20 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, currency="PLN", ) @@ -311,7 +311,7 @@ def setUpTestData(cls) -> None: delivery_type=cls.dm_cash, delivered_quantity=10 + num, delivered_quantity_usd=10 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, household=household2_admin1, currency="PLN", @@ -323,7 +323,7 @@ def setUpTestData(cls) -> None: delivery_type=cls.dm_voucher, delivered_quantity=20 + num, delivered_quantity_usd=20 + num, - status=GenericPayment.STATUS_SUCCESS, + status=Payment.STATUS_SUCCESS, business_area=business_area, household=household2_admin2, currency="PLN", @@ -335,7 +335,7 @@ def setUpTestData(cls) -> None: delivery_type=cls.dm_cash, delivered_quantity=30 + num, delivered_quantity_usd=30 + num, - status=GenericPayment.STATUS_ERROR, + status=Payment.STATUS_ERROR, business_area=business_area, household=household2_admin3, currency="PLN", diff --git a/tests/unit/apps/payment/test_fsp_in_payment_plan.py b/tests/unit/apps/payment/test_fsp_in_payment_plan.py index b7255b85c7..b7bb9dca5a 100644 --- a/tests/unit/apps/payment/test_fsp_in_payment_plan.py +++ b/tests/unit/apps/payment/test_fsp_in_payment_plan.py @@ -26,7 +26,7 @@ from hct_mis_api.apps.payment.models import ( DeliveryMechanism, DeliveryMechanismPerPaymentPlan, - GenericPayment, + Payment, PaymentPlan, ) from hct_mis_api.apps.payment.services.payment_plan_services import PaymentPlanService @@ -774,7 +774,7 @@ def test_successful_fsp_assigment_with_no_limit(self) -> None: collector=self.individuals_2[0], entitlement_quantity=1000000, # a lot entitlement_quantity_usd=200000, # a lot - status=GenericPayment.STATUS_NOT_DISTRIBUTED, + status=Payment.STATUS_NOT_DISTRIBUTED, household=self.household_2, delivery_type=None, financial_service_provider=None, @@ -934,7 +934,7 @@ def test_getting_volume_by_delivery_mechanism(self) -> None: entitlement_quantity=500, entitlement_quantity_usd=100, delivery_type=self.dm_cash, - status=GenericPayment.STATUS_NOT_DISTRIBUTED, + status=Payment.STATUS_NOT_DISTRIBUTED, household=self.household_2, currency="PLN", ) @@ -945,7 +945,7 @@ def test_getting_volume_by_delivery_mechanism(self) -> None: entitlement_quantity=1000, entitlement_quantity_usd=200, delivery_type=self.dm_transfer, - status=GenericPayment.STATUS_NOT_DISTRIBUTED, + status=Payment.STATUS_NOT_DISTRIBUTED, household=self.household_3, currency="PLN", ) @@ -1139,7 +1139,7 @@ def test_all_payments_covered_with_fsp_limit(self) -> None: collector=self.individuals_2[0], # DELIVERY_TYPE_TRANSFER entitlement_quantity=100, entitlement_quantity_usd=500, - status=GenericPayment.STATUS_NOT_DISTRIBUTED, + status=Payment.STATUS_NOT_DISTRIBUTED, household=self.household_2, delivery_type=None, financial_service_provider=None, @@ -1150,7 +1150,7 @@ def test_all_payments_covered_with_fsp_limit(self) -> None: collector=self.individuals_3[0], # DELIVERY_TYPE_TRANSFER entitlement_quantity=100, entitlement_quantity_usd=500, - status=GenericPayment.STATUS_NOT_DISTRIBUTED, + status=Payment.STATUS_NOT_DISTRIBUTED, household=self.household_3, delivery_type=None, financial_service_provider=None, @@ -1161,7 +1161,7 @@ def test_all_payments_covered_with_fsp_limit(self) -> None: collector=self.individuals_1[0], # DELIVERY_TYPE_VOUCHER entitlement_quantity=100, entitlement_quantity_usd=1000, - status=GenericPayment.STATUS_NOT_DISTRIBUTED, + status=Payment.STATUS_NOT_DISTRIBUTED, household=self.household_1, delivery_type=None, financial_service_provider=None, @@ -1218,7 +1218,7 @@ def test_not_all_payments_covered_because_of_fsp_limit(self) -> None: collector=self.individuals_2[0], # DELIVERY_TYPE_TRANSFER entitlement_quantity=100, entitlement_quantity_usd=1000, - status=GenericPayment.STATUS_NOT_DISTRIBUTED, + status=Payment.STATUS_NOT_DISTRIBUTED, household=self.household_2, delivery_type=None, financial_service_provider=None, @@ -1229,7 +1229,7 @@ def test_not_all_payments_covered_because_of_fsp_limit(self) -> None: collector=self.individuals_3[0], # DELIVERY_TYPE_TRANSFER entitlement_quantity=100, entitlement_quantity_usd=1000, - status=GenericPayment.STATUS_NOT_DISTRIBUTED, + status=Payment.STATUS_NOT_DISTRIBUTED, household=self.household_3, delivery_type=None, financial_service_provider=None, diff --git a/tests/unit/apps/payment/test_models1.py b/tests/unit/apps/payment/test_models1.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/unit/apps/payment/test_payment_verification_mutations.py b/tests/unit/apps/payment/test_payment_verification_mutations.py index 2a192e5654..5953660434 100644 --- a/tests/unit/apps/payment/test_payment_verification_mutations.py +++ b/tests/unit/apps/payment/test_payment_verification_mutations.py @@ -25,7 +25,7 @@ PaymentVerificationSummaryFactory, ) from hct_mis_api.apps.payment.models import ( - GenericPayment, + Payment, PaymentPlan, PaymentVerification, PaymentVerificationPlan, @@ -220,7 +220,7 @@ def test_permissions(self) -> None: def test_edit_payment_verification_plan_mutation(self) -> None: payment_plan = PaymentPlanFactory(status=PaymentPlan.Status.FINISHED, business_area=self.business_area) PaymentVerificationSummaryFactory(payment_plan=payment_plan) - PaymentFactory(parent=payment_plan, currency="PLN", status=GenericPayment.STATUS_SUCCESS) + PaymentFactory(parent=payment_plan, currency="PLN", status=Payment.STATUS_SUCCESS) payment_verification_plan = PaymentVerificationPlanFactory( payment_plan=payment_plan, status=PaymentVerificationPlan.STATUS_PENDING, diff --git a/tests/unit/apps/payment/test_verification_plan_status_change_services.py b/tests/unit/apps/payment/test_verification_plan_status_change_services.py index 29f270cbe3..bcb0b7eeb8 100644 --- a/tests/unit/apps/payment/test_verification_plan_status_change_services.py +++ b/tests/unit/apps/payment/test_verification_plan_status_change_services.py @@ -125,7 +125,7 @@ def setUpTestData(cls) -> None: PaymentVerificationFactory( payment_verification_plan=other_payment_plan_payment_verification, - payment_obj=other_payment_record, + payment=other_payment_record, status=PaymentVerification.STATUS_PENDING, ) EntitlementCardFactory(household=other_household) diff --git a/tests/unit/apps/reporting/test_report_service.py b/tests/unit/apps/reporting/test_report_service.py index 450f987f35..10ba23b1de 100644 --- a/tests/unit/apps/reporting/test_report_service.py +++ b/tests/unit/apps/reporting/test_report_service.py @@ -1,5 +1,4 @@ from typing import Any -from unittest.mock import patch from django.conf import settings from django.test import TestCase @@ -10,7 +9,6 @@ from hct_mis_api.apps.account.fixtures import PartnerFactory, UserFactory from hct_mis_api.apps.core.fixtures import create_afghanistan -from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.geo import models as geo_models from hct_mis_api.apps.geo.fixtures import AreaFactory, AreaTypeFactory from hct_mis_api.apps.household.fixtures import create_household_and_individuals @@ -31,7 +29,7 @@ class TestGenerateReportService(TestCase): @classmethod def setUpTestData(self) -> None: - create_afghanistan() + self.business_area = create_afghanistan() PartnerFactory(name="UNICEF") from hct_mis_api.apps.reporting.services.generate_report_service import ( GenerateReportService, @@ -39,7 +37,6 @@ def setUpTestData(self) -> None: self.GenerateReportService = GenerateReportService - self.business_area = BusinessArea.objects.get(slug="afghanistan") self.partner = PartnerFactory(name="Test1") self.user = UserFactory.create(partner=self.partner) family_sizes_list = (2, 4, 5, 1, 3, 11, 14) @@ -86,20 +83,20 @@ def setUpTestData(self) -> None: self.payment_plan_1 = PaymentPlanFactory( business_area=self.business_area, program_cycle=self.program_1.cycles.first(), - # end_date=datetime.datetime.fromisoformat("2020-01-01 00:01:11+00:00"), + created_by=self.user, ) self.payment_plan_2 = PaymentPlanFactory( business_area=self.business_area, - # end_date=datetime.datetime.fromisoformat("2020-01-01 00:01:11+00:00") + created_by=self.user, ) self.payment_plan_3 = PaymentPlanFactory( business_area=self.business_area, program_cycle=self.program_1.cycles.first(), - # end_date=datetime.datetime.fromisoformat("2020-01-01 00:01:11+00:00"), + created_by=self.user, ) self.payment_plan_4 = PaymentPlanFactory( business_area=self.business_area, - # end_date=datetime.datetime.fromisoformat("2020-01-01 00:01:11+00:00") + created_by=self.user, ) PaymentVerificationSummary.objects.create(payment_plan=self.payment_plan_1) PaymentVerificationSummary.objects.create(payment_plan=self.payment_plan_2) @@ -174,12 +171,13 @@ def setUpTestData(self) -> None: ("payments_filter_admin_area", Report.PAYMENTS, True, False, 1), ("payment_verifications_no_filter", Report.PAYMENT_VERIFICATION, False, False, 2), ("payment_verifications_program", Report.PAYMENT_VERIFICATION, False, True, 1), - ("cash_plans_no_filter", Report.CASH_PLAN, False, False, 2), - ("cash_plans_program", Report.CASH_PLAN, False, True, 1), + ("cash_plans_no_filter", Report.PAYMENT_PLAN, False, False, 2), + ("cash_plans_program", Report.PAYMENT_PLAN, False, True, 1), ("individuals_payments_no_filter", Report.INDIVIDUALS_AND_PAYMENT, False, False, 4), ("individuals_payments_admin_area", Report.INDIVIDUALS_AND_PAYMENT, True, False, 2), ("individuals_payments_program", Report.INDIVIDUALS_AND_PAYMENT, False, True, 2), ("individuals_payments_admin_area_and_program", Report.INDIVIDUALS_AND_PAYMENT, True, True, 2), + ("cash_plan_verification", Report.CASH_PLAN_VERIFICATION, True, True, 2), ] ) def test_report_types( @@ -203,17 +201,17 @@ def test_report_types( report.save() report_service = self.GenerateReportService(report) - with ( - patch( - "hct_mis_api.apps.reporting.services.generate_report_service.GenerateReportService.save_wb_file_in_db" - ) as mock_save_wb_file_in_db, - patch( - "hct_mis_api.apps.reporting.services.generate_report_service.GenerateReportService.generate_workbook" - ) as mock_generate_workbook, - ): - report_service.generate_report() - assert mock_generate_workbook.called - assert mock_save_wb_file_in_db.called + # with ( + # # patch( + # # "hct_mis_api.apps.reporting.services.generate_report_service.GenerateReportService.save_wb_file_in_db" + # # ) as mock_save_wb_file_in_db, + # # patch( + # # "hct_mis_api.apps.reporting.services.generate_report_service.GenerateReportService.generate_workbook" + # # ) as mock_generate_workbook, + # ): + report_service.generate_report() + # assert mock_generate_workbook.called + # assert mock_save_wb_file_in_db.called report.refresh_from_db() self.assertEqual(report.status, Report.COMPLETED) # self.assertEqual(report.number_of_records, number_of_records) # when mocking generating workbook, this is not set diff --git a/tests/unit/one_time_scripts/test_migrate_cash_assist_models.py b/tests/unit/one_time_scripts/test_migrate_cash_assist_models.py deleted file mode 100644 index 659890a199..0000000000 --- a/tests/unit/one_time_scripts/test_migrate_cash_assist_models.py +++ /dev/null @@ -1,451 +0,0 @@ -import datetime - -from django.contrib.contenttypes.models import ContentType -from django.test import TestCase - -import factory -from factory.django import DjangoModelFactory -from pytz import utc - -from hct_mis_api.apps.core.fixtures import create_afghanistan -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.core.utils import CaIdIterator -from hct_mis_api.apps.grievance.fixtures import GrievanceTicketFactory -from hct_mis_api.apps.grievance.models import ( - GrievanceTicket, - TicketComplaintDetails, - TicketSensitiveDetails, -) -from hct_mis_api.apps.household.fixtures import HouseholdFactory, create_household -from hct_mis_api.apps.payment.delivery_mechanisms import DeliveryMechanismChoices -from hct_mis_api.apps.payment.fixtures import ( - DeliveryMechanismFactory, - PaymentVerificationFactory, - PaymentVerificationPlanFactory, - PaymentVerificationSummaryFactory, -) -from hct_mis_api.apps.payment.models import ( - CashPlan, - DeliveryMechanismPerPaymentPlan, - FinancialServiceProvider, - Payment, - PaymentPlan, - PaymentRecord, - PaymentVerification, - PaymentVerificationPlan, - PaymentVerificationSummary, - ServiceProvider, -) -from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.apps.targeting.fixtures import TargetPopulationFactory -from hct_mis_api.one_time_scripts.migrate_cash_assist_models import ( - get_status, - migrate_cash_plan_to_payment_plan, -) - - -class CashPlanFactory(DjangoModelFactory): - class Meta: - model = CashPlan - - ca_id = factory.Sequence(lambda n: f"PP-0000-00-1122334{n}") - business_area = factory.LazyAttribute(lambda o: BusinessArea.objects.first()) - program = factory.SubFactory(ProgramFactory) - status_date = factory.Faker( - "date_time_this_decade", - before_now=False, - after_now=True, - tzinfo=utc, - ) - status = factory.fuzzy.FuzzyChoice( - CashPlan.STATUS_CHOICE, - getter=lambda c: c[0], - ) - name = factory.Faker( - "sentence", - nb_words=6, - variable_nb_words=True, - ext_word_list=None, - ) - distribution_level = "Registration Group" - dispersion_date = factory.Faker( - "date_time_this_decade", - before_now=False, - after_now=True, - tzinfo=utc, - ) - coverage_duration = factory.fuzzy.FuzzyInteger(1, 4) - coverage_unit = factory.Faker( - "random_element", - elements=["Day(s)", "Week(s)", "Month(s)", "Year(s)"], - ) - comments = factory.Faker( - "sentence", - nb_words=6, - variable_nb_words=True, - ext_word_list=None, - ) - delivery_type = factory.fuzzy.FuzzyChoice( - DeliveryMechanismChoices.DELIVERY_TYPE_CHOICES, - getter=lambda c: c[0], - ) - assistance_measurement = factory.Faker("currency_name") - assistance_through = factory.Faker("random_element", elements=["ING", "Bank of America", "mBank"]) - vision_id = factory.Faker("uuid4") - funds_commitment = factory.fuzzy.FuzzyInteger(1000, 99999999) - exchange_rate = factory.fuzzy.FuzzyDecimal(0.1, 9.9) - down_payment = factory.fuzzy.FuzzyInteger(1000, 99999999) - validation_alerts_count = factory.fuzzy.FuzzyInteger(1, 3) - total_persons_covered = factory.fuzzy.FuzzyInteger(1, 4) - total_persons_covered_revised = factory.fuzzy.FuzzyInteger(1, 4) - - total_entitled_quantity = factory.fuzzy.FuzzyDecimal(20000.0, 90000000.0) - total_entitled_quantity_revised = factory.fuzzy.FuzzyDecimal(20000.0, 90000000.0) - total_delivered_quantity = factory.fuzzy.FuzzyDecimal(20000.0, 90000000.0) - total_undelivered_quantity = factory.fuzzy.FuzzyDecimal(20000.0, 90000000.0) - - total_entitled_quantity_usd = factory.fuzzy.FuzzyDecimal(20000.0, 90000000.0) - total_entitled_quantity_revised_usd = factory.fuzzy.FuzzyDecimal(20000.0, 90000000.0) - total_delivered_quantity_usd = factory.fuzzy.FuzzyDecimal(20000.0, 90000000.0) - total_undelivered_quantity_usd = factory.fuzzy.FuzzyDecimal(20000.0, 90000000.0) - - -class ServiceProviderFactory(DjangoModelFactory): - class Meta: - model = ServiceProvider - - business_area = factory.LazyAttribute(lambda o: BusinessArea.objects.first()) - ca_id = factory.Iterator(CaIdIterator("SRV")) - full_name = factory.Faker("company") - short_name = factory.LazyAttribute(lambda o: o.full_name[0:3]) - country = factory.Faker("country_code") - vision_id = factory.fuzzy.FuzzyInteger(1342342, 9999999932) - - -class PaymentRecordFactory(DjangoModelFactory): - class Meta: - model = PaymentRecord - - business_area = factory.LazyAttribute(lambda o: BusinessArea.objects.first()) - status = factory.fuzzy.FuzzyChoice( - PaymentRecord.STATUS_CHOICE, - getter=lambda c: c[0], - ) - full_name = factory.Faker("name") - status_date = factory.Faker( - "date_time_this_decade", - before_now=False, - after_now=True, - tzinfo=utc, - ) - ca_id = factory.Iterator(CaIdIterator("PR")) - ca_hash_id = factory.Faker("uuid4") - parent = factory.SubFactory(CashPlanFactory) - household = factory.SubFactory(HouseholdFactory) - total_persons_covered = factory.fuzzy.FuzzyInteger(1, 7) - distribution_modality = factory.Faker( - "sentence", - nb_words=6, - variable_nb_words=True, - ext_word_list=None, - ) - target_population = factory.SubFactory(TargetPopulationFactory) - entitlement_card_number = factory.Faker("ssn") - entitlement_card_status = factory.fuzzy.FuzzyChoice( - PaymentRecord.ENTITLEMENT_CARD_STATUS_CHOICE, - getter=lambda c: c[0], - ) - entitlement_card_issue_date = factory.Faker( - "date_time_this_decade", - before_now=True, - after_now=False, - tzinfo=utc, - ) - currency = factory.Faker("currency_code") - entitlement_quantity = factory.fuzzy.FuzzyDecimal(100.0, 10000.0) - entitlement_quantity_usd = factory.fuzzy.FuzzyDecimal(100.0, 10000.0) - delivered_quantity = factory.fuzzy.FuzzyDecimal(100.0, 10000.0) - delivered_quantity_usd = factory.fuzzy.FuzzyDecimal(100.0, 10000.0) - delivery_date = factory.Faker( - "date_time_this_decade", - before_now=True, - after_now=False, - tzinfo=utc, - ) - service_provider = factory.SubFactory(ServiceProviderFactory) - registration_ca_id = factory.Faker("uuid4") - - -class TestMigrateCashPlanToPaymentPlan(TestCase): - @classmethod - def setUpTestData(cls) -> None: - cls.business_area = create_afghanistan() - - cls.content_type_for_payment_plan = ContentType.objects.get_for_model(PaymentPlan) - cls.content_type_for_cash_plan = ContentType.objects.get_for_model(CashPlan) - cls.content_type_for_payment = ContentType.objects.get_for_model(Payment) - - cls.cash_delivery_mechanism = DeliveryMechanismFactory(name="Cash") - cls.digital_delivery_mechanism = DeliveryMechanismFactory(name="Digital") - - cls.service_provider1 = ServiceProviderFactory() - cls.service_provider2 = ServiceProviderFactory() - cls.service_provider3 = ServiceProviderFactory() - - cls.tp1 = TargetPopulationFactory() - cls.tp2 = TargetPopulationFactory() - - cls.cash_plan = CashPlanFactory( - delivery_type=cls.cash_delivery_mechanism.name, - service_provider=cls.service_provider1, - is_migrated_to_payment_plan=False, - start_date=datetime.date(2021, 1, 1), - dispersion_date=datetime.date(2021, 1, 2), - status_date=datetime.date(2021, 1, 3), - ) - - cls.cash_plan2 = CashPlanFactory( - delivery_type=cls.cash_delivery_mechanism.name, - service_provider=cls.service_provider2, - is_migrated_to_payment_plan=False, - ) # CP without payments, not migrating - - cls.cash_plan3 = CashPlanFactory( - delivery_type=cls.digital_delivery_mechanism.name, - service_provider=cls.service_provider1, - is_migrated_to_payment_plan=False, - ) # CP without payments, not migrating, same service provider as CP1 so fsp delivery mechanisms should be updated - - hh1, _ = create_household(household_args={"size": 2}) - cls.pr1 = PaymentRecordFactory( - parent=cls.cash_plan, - delivery_type=cls.cash_delivery_mechanism, - target_population=cls.tp1, - head_of_household=hh1.head_of_household, - service_provider=cls.service_provider1, - ) - hh2, _ = create_household(household_args={"size": 2}) - cls.pr2 = PaymentRecordFactory( - parent=cls.cash_plan, - delivery_type=cls.digital_delivery_mechanism, - target_population=cls.tp2, - head_of_household=hh2.head_of_household, - service_provider=cls.service_provider1, - ) - cls.pvs = PaymentVerificationSummaryFactory( - payment_plan_obj=cls.cash_plan, - ) - cls.pvp = PaymentVerificationPlanFactory( - payment_plan_obj=cls.cash_plan, - payment_plan=None, - ) - cls.pv1 = PaymentVerificationFactory( - payment_verification_plan=cls.pvp, - payment_obj=cls.pr1, - status=PaymentVerification.STATUS_PENDING, - payment=None, - ) - cls.pv2 = PaymentVerificationFactory( - payment_verification_plan=cls.pvp, - payment_obj=cls.pr2, - status=PaymentVerification.STATUS_PENDING, - payment=None, - ) - - cls.grievance_ticket1 = GrievanceTicketFactory(status=GrievanceTicket.STATUS_IN_PROGRESS) - cls.ticket_complaint_details = TicketComplaintDetails.objects.create( - ticket=cls.grievance_ticket1, - payment_obj=cls.pr1, - payment_object_id=cls.pr1.id, - payment_content_type=cls.content_type_for_payment, - ) - cls.grievance_ticket2 = GrievanceTicketFactory(status=GrievanceTicket.STATUS_IN_PROGRESS) - cls.ticket_sensitive_details = TicketSensitiveDetails.objects.create( - ticket=cls.grievance_ticket2, - payment_obj=cls.pr2, - payment_object_id=cls.pr2.id, - payment_content_type=cls.content_type_for_payment, - ) - - def test_migrate(self) -> None: - assert FinancialServiceProvider.objects.count() == 0 - assert PaymentPlan.objects.count() == 0 - assert DeliveryMechanismPerPaymentPlan.objects.count() == 0 - assert Payment.objects.count() == 0 - assert PaymentVerificationSummary.objects.count() == 1 - assert PaymentVerificationPlan.objects.count() == 1 - assert PaymentVerification.objects.count() == 2 - - migrate_cash_plan_to_payment_plan() - - self.service_provider1.refresh_from_db() - self.service_provider2.refresh_from_db() - self.service_provider3.refresh_from_db() - self.cash_plan.refresh_from_db() - self.pr1.refresh_from_db() - self.pr2.refresh_from_db() - self.pvs.refresh_from_db() - self.pvp.refresh_from_db() - self.pv1.refresh_from_db() - self.pv2.refresh_from_db() - self.grievance_ticket1.refresh_from_db() - self.grievance_ticket2.refresh_from_db() - self.ticket_complaint_details.refresh_from_db() - self.ticket_sensitive_details.refresh_from_db() - - # Assert FinancialServiceProvider is created - - assert self.service_provider1.is_migrated_to_payment_plan is True - assert self.service_provider2.is_migrated_to_payment_plan is True - assert self.service_provider3.is_migrated_to_payment_plan is False # no cash plan assigned so not migrated - - assert FinancialServiceProvider.objects.count() == 2 - - fsp = FinancialServiceProvider.objects.get(vision_vendor_number=self.service_provider1.vision_id) - assert fsp.name == self.service_provider1.full_name - assert fsp.vision_vendor_number == self.service_provider1.vision_id - assert fsp.communication_channel == "API" - assert fsp.internal_data == { - "is_cash_assist": True, - "business_area": self.service_provider1.business_area.slug, - "country": self.service_provider1.country, - "ca_id": self.service_provider1.ca_id, - "short_name": self.service_provider1.short_name, - } - assert self.service_provider1.is_migrated_to_payment_plan is True - assert fsp.delivery_mechanisms.count() == 2 - - assert self.cash_plan.is_migrated_to_payment_plan is True - assert self.cash_plan2.is_migrated_to_payment_plan is False # no payments assigned so not migrated - - # Assert PaymentPlans created - pps = PaymentPlan.objects.filter(unicef_id=self.cash_plan.ca_id) - assert pps.count() == 2 # One for each target population - pp1 = pps.get(target_population=self.pr1.target_population) - assert pp1.unicef_id == self.cash_plan.ca_id - assert pp1.status == "FINISHED" - assert pp1.name == self.pr1.target_population.name - assert pp1.is_cash_assist is True - assert pp1.business_area_id == self.cash_plan.business_area.id - assert pp1.created_by_id == self.pr1.target_population.created_by.id - assert pp1.created_at == self.cash_plan.created_at - assert pp1.target_population_id == self.pr1.target_population.id - assert pp1.program_cycle_id == self.pr1.target_population.program_cycle.id - assert pp1.currency == self.pr1.currency - assert pp1.dispersion_start_date == datetime.date(2021, 1, 1) - assert pp1.dispersion_end_date == datetime.date(2021, 1, 2) - assert pp1.status_date == datetime.datetime(2021, 1, 3, tzinfo=utc) - assert pp1.exchange_rate == self.cash_plan.exchange_rate - - assert pp1.total_entitled_quantity == self.pr1.entitlement_quantity - assert pp1.total_entitled_quantity_usd == self.pr1.entitlement_quantity_usd - assert pp1.total_entitled_quantity_revised == self.cash_plan.total_entitled_quantity_revised - assert pp1.total_entitled_quantity_revised_usd == self.cash_plan.total_entitled_quantity_revised_usd - assert pp1.total_delivered_quantity == self.pr1.delivered_quantity - assert pp1.total_delivered_quantity_usd == self.pr1.delivered_quantity_usd - assert pp1.total_undelivered_quantity == pp1.total_entitled_quantity - self.pr1.delivered_quantity - assert pp1.total_undelivered_quantity_usd == pp1.total_entitled_quantity_usd - self.pr1.delivered_quantity_usd - - assert pp1.internal_data == { - "name": self.cash_plan.name, - "ca_hash_id": str(self.cash_plan.ca_hash_id), - "distribution_level": self.cash_plan.distribution_level, - "coverage_duration": self.cash_plan.coverage_duration, - "coverage_unit": self.cash_plan.coverage_unit, - "comments": self.cash_plan.comments, - "assistance_measurement": self.cash_plan.assistance_measurement, - "assistance_through": self.cash_plan.assistance_through, - "vision_id": self.cash_plan.vision_id, - "funds_commitment": self.cash_plan.funds_commitment, - "down_payment": self.cash_plan.down_payment, - "validation_alerts_count": self.cash_plan.validation_alerts_count, - "total_persons_covered": self.cash_plan.total_persons_covered, - "total_persons_covered_revised": self.cash_plan.total_persons_covered_revised, - } - - pp2 = pps.get(target_population=self.pr2.target_population) - - # Assert DeliveryMechanismPerPaymentPlans created - assert DeliveryMechanismPerPaymentPlan.objects.count() == 2 - dmp1 = DeliveryMechanismPerPaymentPlan.objects.get(payment_plan=pp1) - assert dmp1.delivery_mechanism == self.cash_delivery_mechanism - assert dmp1.sent_date == self.cash_plan.status_date - assert dmp1.delivery_mechanism_order == 1 - assert dmp1.created_by == self.pr1.target_population.created_by - assert dmp1.created_at == self.pr1.target_population.created_at - assert dmp1.financial_service_provider == fsp - - dmp2 = DeliveryMechanismPerPaymentPlan.objects.get(payment_plan=pp2) - assert dmp2.delivery_mechanism == self.digital_delivery_mechanism - - # Assert PaymentRecords created - assert Payment.objects.count() == 2 - - ps = Payment.objects.filter(parent_id=pp1.id) - assert ps.count() == 1 - p1 = ps.first() - assert p1.business_area_id == self.pr1.business_area.id - assert p1.status == get_status(self.pr1.status) - assert p1.status_date == self.pr1.status_date - assert p1.household_id == self.pr1.household.id - assert p1.head_of_household_id == self.pr1.head_of_household.id - assert p1.collector_id == self.pr1.head_of_household.id - assert p1.delivery_type_id == self.pr1.delivery_type.id - assert p1.currency == self.pr1.currency - assert p1.entitlement_quantity == self.pr1.entitlement_quantity - assert p1.entitlement_quantity_usd == self.pr1.entitlement_quantity_usd - assert p1.delivered_quantity == self.pr1.delivered_quantity - assert p1.delivered_quantity_usd == self.pr1.delivered_quantity_usd - assert p1.delivery_date == self.pr1.delivery_date - assert p1.transaction_reference_id == self.pr1.transaction_reference_id - assert p1.transaction_status_blockchain_link == self.pr1.transaction_status_blockchain_link - assert p1.financial_service_provider == fsp - assert p1.program_id == self.pr1.target_population.program_cycle.program_id - assert p1.is_cash_assist is True - assert p1.internal_data == { - "ca_hash_id": str(self.pr1.ca_hash_id), - "full_name": self.pr1.full_name, - "total_persons_covered": self.pr1.total_persons_covered, - "distribution_modality": self.pr1.distribution_modality, - "target_population_cash_assist_id": self.pr1.target_population_cash_assist_id, - "target_population": str(self.pr1.target_population.id), - "entitlement_card_number": self.pr1.entitlement_card_number, - "entitlement_card_status": self.pr1.entitlement_card_status, - "entitlement_card_issue_date": str(self.pr1.entitlement_card_issue_date), - "vision_id": self.pr1.vision_id, - "registration_ca_id": self.pr1.registration_ca_id, - "service_provider": str(self.pr1.service_provider_id), - } - - ps = Payment.objects.filter(parent_id=pp2.id) - assert ps.count() == 1 - p2 = ps.first() - assert p2.delivery_type_id == self.digital_delivery_mechanism.id - - # Assert PaymentVerificationSummary is created - assert PaymentVerificationSummary.objects.count() == 2 - assert PaymentVerificationSummary.objects.filter(payment_plan=pp1).count() == 1 - assert PaymentVerificationSummary.objects.filter(payment_plan=pp2).count() == 1 - - # Assert PaymentVerificationPlan is created - assert PaymentVerificationPlan.objects.count() == 2 - pvp1 = PaymentVerificationPlan.objects.get(payment_plan=pp1) - pvp2 = PaymentVerificationPlan.objects.get(payment_plan=pp2) - assert pvp2.unicef_id == pvp1.unicef_id - - # Assert PaymentVerification is created - assert PaymentVerification.objects.count() == 2 - pv1 = PaymentVerification.objects.get(payment_verification_plan=pvp1) - assert pv1.payment == p1 - - pv2 = PaymentVerification.objects.get(payment_verification_plan=pvp2) - assert pv2.payment == p2 - - # Assert GrievanceTickets updated - assert GrievanceTicket.objects.count() == 2 - assert TicketComplaintDetails.objects.count() == 1 - assert TicketSensitiveDetails.objects.count() == 1 - self.ticket_complaint_details.refresh_from_db() - self.ticket_sensitive_details.refresh_from_db() - assert self.ticket_complaint_details.payment == p1 - assert self.ticket_sensitive_details.payment == p2 From d2b3fc2d5e59b298ce6a41dc4ae7daa88706165e Mon Sep 17 00:00:00 2001 From: Pavlo Mokiichuk Date: Wed, 18 Dec 2024 12:46:48 +0100 Subject: [PATCH 08/11] create PP use tp.household_list instead of households (#4526) --- src/hct_mis_api/apps/payment/services/payment_plan_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hct_mis_api/apps/payment/services/payment_plan_services.py b/src/hct_mis_api/apps/payment/services/payment_plan_services.py index 8378c21736..8cb36a605f 100644 --- a/src/hct_mis_api/apps/payment/services/payment_plan_services.py +++ b/src/hct_mis_api/apps/payment/services/payment_plan_services.py @@ -314,7 +314,7 @@ def check_payment_plan_and_update_status(self, approval_process: ApprovalProcess def create_payments(payment_plan: PaymentPlan) -> None: payments_to_create = [] households = ( - payment_plan.target_population.households.annotate( + payment_plan.target_population.household_list.annotate( collector=IndividualRoleInHousehold.objects.filter(household=OuterRef("pk"), role=ROLE_PRIMARY).values( "individual" )[:1] From b8d29a5678b048d7e9d4953ee55865a02895cb3a Mon Sep 17 00:00:00 2001 From: Domenico DiNicola Date: Fri, 20 Dec 2024 15:07:46 -0600 Subject: [PATCH 09/11] rename es container name --- development_tools/compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/development_tools/compose.yml b/development_tools/compose.yml index 30b4a41c46..7b89a34ce8 100644 --- a/development_tools/compose.yml +++ b/development_tools/compose.yml @@ -146,7 +146,6 @@ services: profiles: - default - services - container_name: elasticsearch build: context: elasticsearch dockerfile: ./Dockerfile From 9099556a28897a05e59743c8baeb81d4f8df1684 Mon Sep 17 00:00:00 2001 From: Pavlo Mokiichuk Date: Mon, 23 Dec 2024 17:15:15 +0100 Subject: [PATCH 10/11] Back merge STG > DEV (#4518) * skip unstable * STG // Fix RDI import dmd json field (#4504) * fix rdi import dmd data json * add migration * fix test * imports * more fixes * remove generateroles from upgrade, update generateroles based on admin history for these roles * [STG] Payment Plan export xlsx: added FSP template doc types list (#4508) * upd get_column_value_from_payment & fsp template doc types * document_types :star: * fix :star2: * Dashboard fix * 215787_drop_cash_assist_migration_script_fix (#4509) * 2226242_Payment_verification_page_not_accessible (#4514) * rollback generateroles to upgrade, skip updating existing roles * Back merge PROD > STG (#4516) * skip unstable * 2226242_Payment_verification_page_not_accessible (#4514) --------- Co-authored-by: Jan Romaniak Co-authored-by: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> * add correct permission for cycles page * fe fix * just new line * Master into stg (#4534) * AB#226656 Error Cannot read properties of undefined Grievance ticket * Fixes to managers for migretable models * added unicef id * fix frontend lint * unique unicef_id constraint * fix test_double_entries * fix household test_models * change exclusion for importing hh and inds to another program * fix test_create_pending_objects_from_objects * fix linters * fix test_create_targeting_for_people * add models for new constraints * fix test * remove one time scripts which are not valid with new contraint * fixed format * change unique name * fixed tests * fix more tests * fix laast e2e tests --------- Co-authored-by: Jan Romaniak Co-authored-by: Jan Romaniak Co-authored-by: Paulina Kujawa <42150286+pkujawa@users.noreply.github.com> Co-authored-by: Paulina Kujawa * fix test & migration --------- Co-authored-by: Jan Romaniak Co-authored-by: Domenico Co-authored-by: Paulina Kujawa Co-authored-by: Paulina Kujawa <42150286+pkujawa@users.noreply.github.com> Co-authored-by: Allan Stockman Rugano Co-authored-by: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> Co-authored-by: Allan Stockman RUGANO Co-authored-by: Maciej Szewczyk Co-authored-by: Jan Romaniak --- pyproject.toml | 2 +- .../paymentmodule/fakeApolloPaymentPlan.ts | 14 +- src/frontend/package.json | 2 +- src/frontend/src/__generated__/graphql.tsx | 5 +- .../queries/paymentmodule/PaymentPlan.ts | 3 + .../GrievancesSidebar/GrievancesSidebar.tsx | 53 ++-- .../LookUpReassignRole/LookUpReassignRole.tsx | 2 +- .../grievances/ReassignMultipleRoleBox.tsx | 13 +- .../components/grievances/ReassignRoleBox.tsx | 10 +- .../PaymentPlanDetailsHeader.tsx | 12 +- .../PaymentPlanDetailsPage.tsx | 5 +- .../ProgramCycle/ProgramCyclePage.tsx | 2 +- .../ImportedHouseholdTableRow.tsx | 2 +- .../ImportedIndividualsTableRow.tsx | 3 +- src/hct_mis_api/api/endpoints/rdi/upload.py | 2 + src/hct_mis_api/apps/account/permissions.py | 7 +- .../core/management/commands/generateroles.py | 9 +- src/hct_mis_api/apps/dashboard/services.py | 6 +- .../templates/dashboard/dashboard.html | 230 +++++++++--------- .../household/migrations/0005_migration.py | 12 +- .../household/migrations/0006_migration.py | 23 ++ src/hct_mis_api/apps/household/models.py | 14 ++ src/hct_mis_api/apps/household/schema.py | 32 ++- src/hct_mis_api/apps/payment/schema.py | 2 +- .../apps/registration_datahub/mutations.py | 10 +- .../tasks/import_program_population.py | 10 +- src/hct_mis_api/apps/utils/models.py | 4 +- src/hct_mis_api/migrations_script/main.py | 4 - ...lation_import_incorrect_hh_ind_relation.py | 21 -- .../migrate_cash_assist_models.py | 0 ..._production_duplicates_after_enrollment.py | 50 ---- .../test_grievance_tickets.py | 1 + .../test_programme_management.py | 1 + tests/selenium/targeting/test_targeting.py | 12 +- tests/unit/apps/household/test_models.py | 59 +++-- ...t_program_population_to_pending_objects.py | 49 ++-- .../registration_datahub/test_rdi_merge.py | 10 +- ...stration_program_population_import_task.py | 2 +- ...ap_test_copy_target_population_mutation.py | 2 +- .../test_copy_target_population_mutation.py | 2 +- .../targeting/test_targeting_validators.py | 6 +- ...lation_import_incorrect_hh_ind_relation.py | 98 -------- .../test_migrate_cash_assist_models.py | 0 ..._production_duplicates_after_enrollment.py | 103 -------- 44 files changed, 368 insertions(+), 541 deletions(-) create mode 100644 src/hct_mis_api/apps/household/migrations/0006_migration.py delete mode 100644 src/hct_mis_api/one_time_scripts/fix_program_population_import_incorrect_hh_ind_relation.py create mode 100644 src/hct_mis_api/one_time_scripts/migrate_cash_assist_models.py delete mode 100644 src/hct_mis_api/one_time_scripts/remove_production_duplicates_after_enrollment.py delete mode 100644 tests/unit/one_time_scripts/test_fix_program_population_import_incorrect_hh_ind_relation.py create mode 100644 tests/unit/one_time_scripts/test_migrate_cash_assist_models.py delete mode 100644 tests/unit/one_time_scripts/test_remove_production_duplicates_after_enrollment.py diff --git a/pyproject.toml b/pyproject.toml index e145b040bb..6b9c284d5a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -137,7 +137,7 @@ distribution = true [project] name = "hope" -version = "2.15.0" +version = "2.16.0" description = "HCT MIS is UNICEF's humanitarian cash transfer platform." authors = [ { name = "Tivix" }, diff --git a/src/frontend/fixtures/paymentmodule/fakeApolloPaymentPlan.ts b/src/frontend/fixtures/paymentmodule/fakeApolloPaymentPlan.ts index 603ef009c9..0c05a840d7 100644 --- a/src/frontend/fixtures/paymentmodule/fakeApolloPaymentPlan.ts +++ b/src/frontend/fixtures/paymentmodule/fakeApolloPaymentPlan.ts @@ -14,6 +14,9 @@ export const fakeApolloPaymentPlan: PaymentPlanQuery['paymentPlan'] = { version: 1000, unicefId: 'PP-0060-22-00000001', status: PaymentPlanStatus.Locked, + programCycle: { + id: 'UHJvZ3JhbUN5Y2xlOjljNDMzZWQzLTcwZjUtNDRhOC1iZmQ5LTA1Mjg3YTAwNzQxNQ==', + }, isFollowUp: false, sourcePaymentPlan: null, excludedHouseholds: null, @@ -43,7 +46,7 @@ export const fakeApolloPaymentPlan: PaymentPlanQuery['paymentPlan'] = { name: 'Report should property early adult.', __typename: 'TargetPopulationNode', }, - currency: "PLN", + currency: 'PLN', currencyName: 'Polish złoty', startDate: '2020-10-27', endDate: '2021-09-08', @@ -301,7 +304,8 @@ export const fakeApolloPaymentPlan: PaymentPlanQuery['paymentPlan'] = { __typename: 'PaymentPlanNode', }; -export const fakeApolloPaymentPlanWithWrongBackgroundActionStatus: PaymentPlanQuery['paymentPlan'] = { - ...fakeApolloPaymentPlan, - backgroundActionStatus: PaymentPlanBackgroundActionStatus.XlsxExporting, -}; +export const fakeApolloPaymentPlanWithWrongBackgroundActionStatus: PaymentPlanQuery['paymentPlan'] = + { + ...fakeApolloPaymentPlan, + backgroundActionStatus: PaymentPlanBackgroundActionStatus.XlsxExporting, + }; diff --git a/src/frontend/package.json b/src/frontend/package.json index 8b0ca87c11..71f65a10ae 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "2.15.0", + "version": "2.16.0", "private": true, "type": "module", "scripts": { diff --git a/src/frontend/src/__generated__/graphql.tsx b/src/frontend/src/__generated__/graphql.tsx index c69fe81fe5..5c1110a8bb 100644 --- a/src/frontend/src/__generated__/graphql.tsx +++ b/src/frontend/src/__generated__/graphql.tsx @@ -9671,7 +9671,7 @@ export type PaymentPlanQueryVariables = Exact<{ }>; -export type PaymentPlanQuery = { __typename?: 'Query', paymentPlan?: { __typename?: 'PaymentPlanNode', id: string, version: any, unicefId?: string | null, status: PaymentPlanStatus, canCreateFollowUp?: boolean | null, backgroundActionStatus?: PaymentPlanBackgroundActionStatus | null, canCreatePaymentVerificationPlan?: boolean | null, availablePaymentRecordsCount?: number | null, bankReconciliationSuccess?: number | null, bankReconciliationError?: number | null, exchangeRate?: number | null, adminUrl?: string | null, currency?: string | null, currencyName?: string | null, startDate?: any | null, endDate?: any | null, dispersionStartDate?: any | null, dispersionEndDate?: any | null, femaleChildrenCount: number, femaleAdultsCount: number, maleChildrenCount: number, maleAdultsCount: number, totalHouseholdsCount: number, totalIndividualsCount: number, totalEntitledQuantity?: number | null, totalDeliveredQuantity?: number | null, totalUndeliveredQuantity?: number | null, totalWithdrawnHouseholdsCount?: number | null, hasPaymentListExportFile?: boolean | null, hasFspDeliveryMechanismXlsxTemplate?: boolean | null, importedFileDate?: any | null, importedFileName?: string | null, totalEntitledQuantityUsd?: number | null, paymentsConflictsCount?: number | null, canSendToPaymentGateway?: boolean | null, canSplit?: boolean | null, exclusionReason: string, excludeHouseholdError: string, isFollowUp: boolean, unsuccessfulPaymentsCount?: number | null, createdBy: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string }, program?: { __typename?: 'ProgramNode', id: string, name: string, caId?: string | null } | null, targetPopulation: { __typename?: 'TargetPopulationNode', id: string, name: string }, approvalProcess: { __typename?: 'ApprovalProcessNodeConnection', totalCount?: number | null, edgeCount?: number | null, edges: Array<{ __typename?: 'ApprovalProcessNodeEdge', node?: { __typename?: 'ApprovalProcessNode', id: string, sentForApprovalDate?: any | null, sentForAuthorizationDate?: any | null, sentForFinanceReleaseDate?: any | null, approvalNumberRequired: number, authorizationNumberRequired: number, financeReleaseNumberRequired: number, rejectedOn?: string | null, sentForApprovalBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, sentForAuthorizationBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, sentForFinanceReleaseBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, actions?: { __typename?: 'FilteredActionsListNode', approval?: Array<{ __typename?: 'ApprovalNode', createdAt: any, comment?: string | null, info?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null, authorization?: Array<{ __typename?: 'ApprovalNode', createdAt: any, comment?: string | null, info?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null, financeRelease?: Array<{ __typename?: 'ApprovalNode', createdAt: any, comment?: string | null, info?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null, reject?: Array<{ __typename?: 'ApprovalNode', createdAt: any, comment?: string | null, info?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null } | null } | null> }, steficonRule?: { __typename?: 'RuleCommitNode', id: string, rule?: { __typename?: 'SteficonRuleNode', id: string, name: string } | null } | null, deliveryMechanisms?: Array<{ __typename?: 'DeliveryMechanismPerPaymentPlanNode', id: string, name?: string | null, code?: string | null, order?: number | null, sentToPaymentGateway: boolean, chosenConfiguration?: string | null, fsp?: { __typename?: 'FinancialServiceProviderNode', id: string, name: string, communicationChannel: FinancialServiceProviderCommunicationChannel, isPaymentGateway?: boolean | null } | null } | null> | null, splitChoices?: Array<{ __typename?: 'ChoiceObject', name?: string | null, value?: string | null } | null> | null, volumeByDeliveryMechanism?: Array<{ __typename?: 'VolumeByDeliveryMechanismNode', volume?: number | null, volumeUsd?: number | null, deliveryMechanism?: { __typename?: 'DeliveryMechanismPerPaymentPlanNode', id: string, name?: string | null, order?: number | null, fsp?: { __typename?: 'FinancialServiceProviderNode', id: string, name: string } | null } | null } | null> | null, verificationPlans?: { __typename?: 'PaymentVerificationPlanNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'PaymentVerificationPlanNodeEdge', node?: { __typename?: 'PaymentVerificationPlanNode', id: string, unicefId?: string | null, adminUrl?: string | null, status: PaymentVerificationPlanStatus, sampleSize?: number | null, receivedCount?: number | null, notReceivedCount?: number | null, respondedCount?: number | null, verificationChannel: PaymentVerificationPlanVerificationChannel, sampling: PaymentVerificationPlanSampling, receivedWithProblemsCount?: number | null, rapidProFlowId: string, confidenceInterval?: number | null, marginOfError?: number | null, activationDate?: any | null, completionDate?: any | null, excludedAdminAreasFilter?: Array | null, sexFilter?: string | null, xlsxFileExporting: boolean, hasXlsxFile?: boolean | null, xlsxFileWasDownloaded?: boolean | null, xlsxFileImported: boolean, ageFilter?: { __typename?: 'AgeFilterObject', min?: number | null, max?: number | null } | null } | null } | null> } | null, paymentVerificationSummary?: { __typename?: 'PaymentVerificationSummaryNode', id: string, createdAt: any, updatedAt: any, status: PaymentVerificationSummaryStatus, activationDate?: any | null, completionDate?: any | null } | null, paymentItems: { __typename?: 'PaymentNodeConnection', totalCount?: number | null, edgeCount?: number | null, edges: Array<{ __typename?: 'PaymentNodeEdge', node?: { __typename?: 'PaymentNode', id: string, status: PaymentStatus } | null } | null> }, reconciliationSummary?: { __typename?: 'ReconciliationSummaryNode', deliveredFully?: number | null, deliveredPartially?: number | null, notDelivered?: number | null, unsuccessful?: number | null, pending?: number | null, numberOfPayments?: number | null, reconciled?: number | null } | null, excludedHouseholds?: Array<{ __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null> | null, excludedIndividuals?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null } | null> | null, followUps: { __typename?: 'PaymentPlanNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'PaymentPlanNodeEdge', node?: { __typename?: 'PaymentPlanNode', id: string, unicefId?: string | null, createdAt: any, paymentItems: { __typename?: 'PaymentNodeConnection', totalCount?: number | null } } | null } | null> }, sourcePaymentPlan?: { __typename?: 'PaymentPlanNode', id: string, unicefId?: string | null } | null, supportingDocuments?: Array<{ __typename?: 'PaymentPlanSupportingDocumentNode', id: string, title: string, file: string } | null> | null } | null }; +export type PaymentPlanQuery = { __typename?: 'Query', paymentPlan?: { __typename?: 'PaymentPlanNode', id: string, version: any, unicefId?: string | null, status: PaymentPlanStatus, canCreateFollowUp?: boolean | null, backgroundActionStatus?: PaymentPlanBackgroundActionStatus | null, canCreatePaymentVerificationPlan?: boolean | null, availablePaymentRecordsCount?: number | null, bankReconciliationSuccess?: number | null, bankReconciliationError?: number | null, exchangeRate?: number | null, adminUrl?: string | null, currency?: string | null, currencyName?: string | null, startDate?: any | null, endDate?: any | null, dispersionStartDate?: any | null, dispersionEndDate?: any | null, femaleChildrenCount: number, femaleAdultsCount: number, maleChildrenCount: number, maleAdultsCount: number, totalHouseholdsCount: number, totalIndividualsCount: number, totalEntitledQuantity?: number | null, totalDeliveredQuantity?: number | null, totalUndeliveredQuantity?: number | null, totalWithdrawnHouseholdsCount?: number | null, hasPaymentListExportFile?: boolean | null, hasFspDeliveryMechanismXlsxTemplate?: boolean | null, importedFileDate?: any | null, importedFileName?: string | null, totalEntitledQuantityUsd?: number | null, paymentsConflictsCount?: number | null, canSendToPaymentGateway?: boolean | null, canSplit?: boolean | null, exclusionReason: string, excludeHouseholdError: string, isFollowUp: boolean, unsuccessfulPaymentsCount?: number | null, programCycle: { __typename?: 'ProgramCycleNode', id: string }, createdBy: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string }, program?: { __typename?: 'ProgramNode', id: string, name: string, caId?: string | null } | null, targetPopulation: { __typename?: 'TargetPopulationNode', id: string, name: string }, approvalProcess: { __typename?: 'ApprovalProcessNodeConnection', totalCount?: number | null, edgeCount?: number | null, edges: Array<{ __typename?: 'ApprovalProcessNodeEdge', node?: { __typename?: 'ApprovalProcessNode', id: string, sentForApprovalDate?: any | null, sentForAuthorizationDate?: any | null, sentForFinanceReleaseDate?: any | null, approvalNumberRequired: number, authorizationNumberRequired: number, financeReleaseNumberRequired: number, rejectedOn?: string | null, sentForApprovalBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, sentForAuthorizationBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, sentForFinanceReleaseBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, actions?: { __typename?: 'FilteredActionsListNode', approval?: Array<{ __typename?: 'ApprovalNode', createdAt: any, comment?: string | null, info?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null, authorization?: Array<{ __typename?: 'ApprovalNode', createdAt: any, comment?: string | null, info?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null, financeRelease?: Array<{ __typename?: 'ApprovalNode', createdAt: any, comment?: string | null, info?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null, reject?: Array<{ __typename?: 'ApprovalNode', createdAt: any, comment?: string | null, info?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null } | null } | null> }, steficonRule?: { __typename?: 'RuleCommitNode', id: string, rule?: { __typename?: 'SteficonRuleNode', id: string, name: string } | null } | null, deliveryMechanisms?: Array<{ __typename?: 'DeliveryMechanismPerPaymentPlanNode', id: string, name?: string | null, code?: string | null, order?: number | null, sentToPaymentGateway: boolean, chosenConfiguration?: string | null, fsp?: { __typename?: 'FinancialServiceProviderNode', id: string, name: string, communicationChannel: FinancialServiceProviderCommunicationChannel, isPaymentGateway?: boolean | null } | null } | null> | null, splitChoices?: Array<{ __typename?: 'ChoiceObject', name?: string | null, value?: string | null } | null> | null, volumeByDeliveryMechanism?: Array<{ __typename?: 'VolumeByDeliveryMechanismNode', volume?: number | null, volumeUsd?: number | null, deliveryMechanism?: { __typename?: 'DeliveryMechanismPerPaymentPlanNode', id: string, name?: string | null, order?: number | null, fsp?: { __typename?: 'FinancialServiceProviderNode', id: string, name: string } | null } | null } | null> | null, verificationPlans?: { __typename?: 'PaymentVerificationPlanNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'PaymentVerificationPlanNodeEdge', node?: { __typename?: 'PaymentVerificationPlanNode', id: string, unicefId?: string | null, adminUrl?: string | null, status: PaymentVerificationPlanStatus, sampleSize?: number | null, receivedCount?: number | null, notReceivedCount?: number | null, respondedCount?: number | null, verificationChannel: PaymentVerificationPlanVerificationChannel, sampling: PaymentVerificationPlanSampling, receivedWithProblemsCount?: number | null, rapidProFlowId: string, confidenceInterval?: number | null, marginOfError?: number | null, activationDate?: any | null, completionDate?: any | null, excludedAdminAreasFilter?: Array | null, sexFilter?: string | null, xlsxFileExporting: boolean, hasXlsxFile?: boolean | null, xlsxFileWasDownloaded?: boolean | null, xlsxFileImported: boolean, ageFilter?: { __typename?: 'AgeFilterObject', min?: number | null, max?: number | null } | null } | null } | null> } | null, paymentVerificationSummary?: { __typename?: 'PaymentVerificationSummaryNode', id: string, createdAt: any, updatedAt: any, status: PaymentVerificationSummaryStatus, activationDate?: any | null, completionDate?: any | null } | null, paymentItems: { __typename?: 'PaymentNodeConnection', totalCount?: number | null, edgeCount?: number | null, edges: Array<{ __typename?: 'PaymentNodeEdge', node?: { __typename?: 'PaymentNode', id: string, status: PaymentStatus } | null } | null> }, reconciliationSummary?: { __typename?: 'ReconciliationSummaryNode', deliveredFully?: number | null, deliveredPartially?: number | null, notDelivered?: number | null, unsuccessful?: number | null, pending?: number | null, numberOfPayments?: number | null, reconciled?: number | null } | null, excludedHouseholds?: Array<{ __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null> | null, excludedIndividuals?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null } | null> | null, followUps: { __typename?: 'PaymentPlanNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'PaymentPlanNodeEdge', node?: { __typename?: 'PaymentPlanNode', id: string, unicefId?: string | null, createdAt: any, paymentItems: { __typename?: 'PaymentNodeConnection', totalCount?: number | null } } | null } | null> }, sourcePaymentPlan?: { __typename?: 'PaymentPlanNode', id: string, unicefId?: string | null } | null, supportingDocuments?: Array<{ __typename?: 'PaymentPlanSupportingDocumentNode', id: string, title: string, file: string } | null> | null } | null }; export type AllCashPlansAndPaymentPlansQueryVariables = Exact<{ businessArea: Scalars['String']['input']; @@ -17839,6 +17839,9 @@ export const PaymentPlanDocument = gql` version unicefId status + programCycle { + id + } canCreateFollowUp backgroundActionStatus canCreatePaymentVerificationPlan diff --git a/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts b/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts index a0590581b6..706575f5a0 100644 --- a/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts +++ b/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts @@ -7,6 +7,9 @@ export const PAYMENT_PLAN_QUERY = gql` version unicefId status + programCycle { + id + } canCreateFollowUp backgroundActionStatus canCreatePaymentVerificationPlan diff --git a/src/frontend/src/components/grievances/GrievancesSidebar/GrievancesSidebar.tsx b/src/frontend/src/components/grievances/GrievancesSidebar/GrievancesSidebar.tsx index 10fc2d894b..e97cb3b1f1 100644 --- a/src/frontend/src/components/grievances/GrievancesSidebar/GrievancesSidebar.tsx +++ b/src/frontend/src/components/grievances/GrievancesSidebar/GrievancesSidebar.tsx @@ -22,23 +22,41 @@ export function GrievancesSidebar({ const shouldShowReassignBoxDataChange = (): boolean => { let { individual, household } = ticket; const { category, issueType, status } = ticket; + if (category.toString() === GRIEVANCE_CATEGORIES.NEEDS_ADJUDICATION) { individual = ticket.needsAdjudicationTicketDetails.selectedIndividual; household = ticket.needsAdjudicationTicketDetails.selectedIndividual?.household; } - const isOneIndividual = household?.activeIndividualsCount === 1; + const isOneIndividual = household?.activeIndividualsCount === 1; if (isOneIndividual) return false; - const isRightCategory = - (category.toString() === GRIEVANCE_CATEGORIES.DATA_CHANGE && - issueType.toString() === GRIEVANCE_ISSUE_TYPES.DELETE_INDIVIDUAL) || - (category.toString() === GRIEVANCE_CATEGORIES.DATA_CHANGE && - issueType.toString() === GRIEVANCE_ISSUE_TYPES.EDIT_INDIVIDUAL) || - (category.toString() === GRIEVANCE_CATEGORIES.SYSTEM_FLAGGING && - ticket?.systemFlaggingTicketDetails?.approveStatus) || - (category.toString() === GRIEVANCE_CATEGORIES.NEEDS_ADJUDICATION && - ticket?.needsAdjudicationTicketDetails?.selectedIndividual); + + const isRightCategory = [ + { + category: GRIEVANCE_CATEGORIES.DATA_CHANGE, + issueType: GRIEVANCE_ISSUE_TYPES.DELETE_INDIVIDUAL, + }, + { + category: GRIEVANCE_CATEGORIES.DATA_CHANGE, + issueType: GRIEVANCE_ISSUE_TYPES.EDIT_INDIVIDUAL, + }, + { + category: GRIEVANCE_CATEGORIES.SYSTEM_FLAGGING, + approveStatus: ticket?.systemFlaggingTicketDetails?.approveStatus, + }, + { + category: GRIEVANCE_CATEGORIES.NEEDS_ADJUDICATION, + selectedIndividual: + ticket?.needsAdjudicationTicketDetails?.selectedIndividual, + }, + ].some( + (condition) => + category.toString() === condition.category && + (issueType.toString() === condition.issueType || + condition.approveStatus || + condition.selectedIndividual), + ); if (!isRightCategory) return false; @@ -47,23 +65,22 @@ export function GrievancesSidebar({ const householdsAndRoles = individual?.householdsAndRoles || []; const isHeadOfHousehold = individual?.id === household?.headOfHousehold?.id; - const hasRolesToReassign = - householdsAndRoles?.filter((el) => el.role !== 'NO_ROLE').length > 0; + const hasRolesToReassign = householdsAndRoles.some( + (el) => el.role !== 'NO_ROLE', + ); let isProperDataChange = true; if ( category.toString() === GRIEVANCE_CATEGORIES.DATA_CHANGE && issueType.toString() === GRIEVANCE_ISSUE_TYPES.EDIT_INDIVIDUAL ) { - if ( - isEmpty(ticket.individualDataUpdateTicketDetails.individualData.role) && - isEmpty( - ticket.individualDataUpdateTicketDetails.individualData.relationship, - ) - ) { + const { role, relationship } = + ticket.individualDataUpdateTicketDetails.individualData; + if (isEmpty(role) && isEmpty(relationship)) { isProperDataChange = false; } } + return ( (isHeadOfHousehold || hasRolesToReassign) && isProperDataChange && diff --git a/src/frontend/src/components/grievances/LookUps/LookUpReassignRole/LookUpReassignRole.tsx b/src/frontend/src/components/grievances/LookUps/LookUpReassignRole/LookUpReassignRole.tsx index 65202a7b2a..f17f40daaa 100644 --- a/src/frontend/src/components/grievances/LookUps/LookUpReassignRole/LookUpReassignRole.tsx +++ b/src/frontend/src/components/grievances/LookUps/LookUpReassignRole/LookUpReassignRole.tsx @@ -119,7 +119,7 @@ export function LookUpReassignRole({ return ( el.role === IndividualRoleInHouseholdRole.Primary || el.role === 'HEAD', ); - const mappedReassignLookups = (): ReactElement => ( <> {selectedIndividualsToReassign.map((selectedIndividualToReassign) => { @@ -106,11 +105,7 @@ export function ReassignMultipleRoleBox({ ticket={ticket} household={householdAndRole.household} individualToReassign={selectedIndividualToReassign} - initialSelectedIndividualId={ - reassignDataDictByIndividualId[ - selectedIndividualToReassign.id - ].new_individual - } + initialSelectedIndividualId={reassignDataDictByIndividualId[selectedIndividualToReassign.id]?.new_individual} /> )); @@ -158,11 +153,7 @@ export function ReassignMultipleRoleBox({ ticket={ticket} household={household} individualToReassign={selectedIndividualToReassign} - initialSelectedIndividualId={ - reassignDataDictByIndividualId[ - selectedIndividualToReassign.id - ].new_individual - } + initialSelectedIndividualId={reassignDataDictByIndividualId[selectedIndividualToReassign.id]?.new_individual} /> )} diff --git a/src/frontend/src/components/grievances/ReassignRoleBox.tsx b/src/frontend/src/components/grievances/ReassignRoleBox.tsx index d9a9d6a790..d538c91328 100644 --- a/src/frontend/src/components/grievances/ReassignRoleBox.tsx +++ b/src/frontend/src/components/grievances/ReassignRoleBox.tsx @@ -91,10 +91,14 @@ export const ReassignRoleBox = ({ } } const reassignDataDictByIndividualId = {}; - for (const key of Object.keys(reassignData)) { - reassignDataDictByIndividualId[reassignData[key].individual] = - reassignData[key]; + if (reassignData && typeof reassignData === 'object') { + for (const key of Object.keys(reassignData)) { + if (reassignData[key] && reassignData[key].individual) { + reassignDataDictByIndividualId[reassignData[key].individual] = reassignData[key]; + } + } } + const mappedLookUpsForExternalHouseholds = householdsAndRoles .filter((el) => el.role !== 'NO_ROLE') .map((el) => ( diff --git a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsHeader.tsx b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsHeader.tsx index 75c15f4636..f21cff707c 100644 --- a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsHeader.tsx +++ b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsHeader.tsx @@ -21,7 +21,6 @@ import { import { useQuery } from '@tanstack/react-query'; import { fetchProgramCycle } from '@api/programCycleApi'; import { useBaseUrl } from '@hooks/useBaseUrl'; -import { useParams } from 'react-router-dom'; import { AdminButton } from '@core/AdminButton'; interface PaymentPlanDetailsHeaderProps { @@ -35,9 +34,8 @@ export const PaymentPlanDetailsHeader = ({ }: PaymentPlanDetailsHeaderProps): ReactElement => { const { t } = useTranslation(); const { businessArea, programId } = useBaseUrl(); - const { programCycleId } = useParams(); - - const { data: programCycleData, isLoading: isLoadingProgramCycle } = useQuery( + const programCycleId = paymentPlan.programCycle?.id; + const { data: programCycleData } = useQuery( { queryKey: [ 'programCyclesDetails', @@ -56,10 +54,6 @@ export const PaymentPlanDetailsHeader = ({ }, ); - if (isLoadingProgramCycle) { - return null; - } - const breadCrumbsItems: BreadCrumbsItem[] = []; if (programCycleId) { @@ -68,7 +62,7 @@ export const PaymentPlanDetailsHeader = ({ to: '../../..', }); breadCrumbsItems.push({ - title: `${programCycleData.title}`, + title: `${programCycleData?.title || ''}`, to: '../..', }); } else { diff --git a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsPage.tsx b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsPage.tsx index fd242d041c..233467dc1a 100644 --- a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsPage.tsx +++ b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsPage.tsx @@ -38,6 +38,8 @@ export const PaymentPlanDetailsPage = (): ReactElement => { fetchPolicy: 'network-only', }); + + const status = data?.paymentPlan?.status; const backgroundActionStatus = data?.paymentPlan?.backgroundActionStatus; @@ -73,6 +75,7 @@ export const PaymentPlanDetailsPage = (): ReactElement => { status === PaymentPlanStatus.Finished; const { paymentPlan } = data; + if (!paymentPlan) return null; return ( { )} {hasPermissions(PERMISSIONS.ACTIVITY_LOG_VIEW, permissions) && ( - + )} diff --git a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCyclePage.tsx b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCyclePage.tsx index 4783394705..dc0786cfa2 100644 --- a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCyclePage.tsx +++ b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCyclePage.tsx @@ -49,7 +49,7 @@ export const ProgramCyclePage = (): ReactElement => { if (permissions === null) return null; if (!selectedProgram) return null; - if (!hasPermissions(PERMISSIONS.PM_VIEW_LIST, permissions)) + if (!hasPermissions(PERMISSIONS.PM_PROGRAMME_CYCLE_VIEW_LIST, permissions)) return ; return ( diff --git a/src/frontend/src/containers/tables/rdi/ImportedHouseholdsTable/ImportedHouseholdTableRow.tsx b/src/frontend/src/containers/tables/rdi/ImportedHouseholdsTable/ImportedHouseholdTableRow.tsx index 4fec6547dd..9c53deeef1 100644 --- a/src/frontend/src/containers/tables/rdi/ImportedHouseholdsTable/ImportedHouseholdTableRow.tsx +++ b/src/frontend/src/containers/tables/rdi/ImportedHouseholdsTable/ImportedHouseholdTableRow.tsx @@ -57,7 +57,7 @@ export function ImportedHouseholdTableRow({ handleClick()}> - {isMerged ? household.unicefId : household.importId} + {household.unicefId} {household?.headOfHousehold?.fullName} diff --git a/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/ImportedIndividualsTableRow.tsx b/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/ImportedIndividualsTableRow.tsx index 308cce5f7a..f99dbb0c92 100644 --- a/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/ImportedIndividualsTableRow.tsx +++ b/src/frontend/src/containers/tables/rdi/ImportedIndividualsTable/ImportedIndividualsTableRow.tsx @@ -20,7 +20,6 @@ interface ImportedIndividualsTableRowProps { export function ImportedIndividualsTableRow({ individual, choices, - isMerged, rdi, }: ImportedIndividualsTableRowProps): ReactElement { const navigate = useNavigate(); @@ -56,7 +55,7 @@ export function ImportedIndividualsTableRow({ > - {isMerged ? individual.unicefId : individual.importId} + {individual.unicefId} {individual.fullName} diff --git a/src/hct_mis_api/api/endpoints/rdi/upload.py b/src/hct_mis_api/api/endpoints/rdi/upload.py index 39fc43705f..1eb1c84910 100644 --- a/src/hct_mis_api/api/endpoints/rdi/upload.py +++ b/src/hct_mis_api/api/endpoints/rdi/upload.py @@ -140,6 +140,7 @@ class Meta: "updated_at", "version", "vector_column", + "unicef_id", ] def validate_role(self, value: str) -> Optional[str]: @@ -175,6 +176,7 @@ class Meta: "geopoint", "detail_id", "version", + "unicef_id", ] validators = [HouseholdValidator()] diff --git a/src/hct_mis_api/apps/account/permissions.py b/src/hct_mis_api/apps/account/permissions.py index 3a5b197a25..93ccddc725 100644 --- a/src/hct_mis_api/apps/account/permissions.py +++ b/src/hct_mis_api/apps/account/permissions.py @@ -378,9 +378,12 @@ def check_node_permission(cls, info: Any, object_instance: Any) -> None: raise PermissionDenied("Permission Denied") @classmethod - def get_node(cls, info: Any, object_id: str) -> Optional[Model]: + def get_node(cls, info: Any, object_id: str, **kwargs: Any) -> Optional[Model]: try: - object_instance = cls._meta.model.objects.get(pk=object_id) + if "get_object_queryset" in kwargs: + object_instance = kwargs.get("get_object_queryset").get(pk=object_id) + else: + object_instance = cls.get_queryset(cls._meta.model.objects, info).get(pk=object_id) cls.check_node_permission(info, object_instance) except cls._meta.model.DoesNotExist: object_instance = None diff --git a/src/hct_mis_api/apps/core/management/commands/generateroles.py b/src/hct_mis_api/apps/core/management/commands/generateroles.py index 053cd66a33..ae6b684505 100644 --- a/src/hct_mis_api/apps/core/management/commands/generateroles.py +++ b/src/hct_mis_api/apps/core/management/commands/generateroles.py @@ -292,9 +292,8 @@ def handle(self, *args: Any, **options: Any) -> None: print("Old incompatible roles pairs were deleted.") roles_created = [] - roles_updated = [] for default_role in default_roles_matrix: - role, created = Role.objects.update_or_create( + role, created = Role.objects.get_or_create( subsystem=Role.HOPE, name=default_role["name"], defaults={"permissions": [permission.value for permission in default_role["permissions"]]}, @@ -302,17 +301,11 @@ def handle(self, *args: Any, **options: Any) -> None: if created: roles_created.append(role.name) - else: - roles_updated.append(role.name) if roles_created: print(f"New roles were created: {', '.join(roles_created)}") else: print("No new roles were created.") - if roles_updated: - print(f"These roles were updated: {', '.join(roles_updated)}") - else: - print("No roles were updated") incompatible_roles_created = [] for role_pair in default_incompatible_roles: diff --git a/src/hct_mis_api/apps/dashboard/services.py b/src/hct_mis_api/apps/dashboard/services.py index e2b36afe34..dc564b4369 100644 --- a/src/hct_mis_api/apps/dashboard/services.py +++ b/src/hct_mis_api/apps/dashboard/services.py @@ -88,7 +88,9 @@ def refresh_data(cls, business_area_slug: str) -> ReturnDict: month=ExtractMonth(Coalesce("delivery_date", "entitlement_date", "status_date")), programs=Coalesce(F("household__program__name"), Value("Unknown program")), sectors=Coalesce(F("household__program__sector"), Value("Unknown sector")), - admin1=Coalesce(F("household__admin1__name"), Value("Unknown admin1")), + admin1=Coalesce( + F("household__admin1__name"), F("household__admin_area__name"), Value("Unknown admin area") + ), fsp=Coalesce(F("financial_service_provider__name"), Value("Unknown fsp")), delivery_types=F("delivery_type__name"), ) @@ -113,7 +115,7 @@ def refresh_data(cls, business_area_slug: str) -> ReturnDict: Coalesce("delivered_quantity", "entitlement_quantity", Value(0.0)), output_field=DecimalField() ), total_payments=Count("id", distinct=True), - individuals=Sum("household__size"), + individuals=Sum(Coalesce("household__size", Value(1))), households=Count("household", distinct=True), children_counts=Sum("household__children_count"), pwd_counts=pwdSum, diff --git a/src/hct_mis_api/apps/dashboard/templates/dashboard/dashboard.html b/src/hct_mis_api/apps/dashboard/templates/dashboard/dashboard.html index f0e0434afc..95ec4ac63d 100644 --- a/src/hct_mis_api/apps/dashboard/templates/dashboard/dashboard.html +++ b/src/hct_mis_api/apps/dashboard/templates/dashboard/dashboard.html @@ -60,111 +60,120 @@

- {% translate "Households Reached" %} -

-
-
-
-

- - {% translate "Individuals Reached" %} -

-
+ + {% translate "Households Reached" %} +

+
+ +
+

+ + + {% translate "Individuals Reached" %} +

+
+
+
+

+ + + {% translate "Children Reached" %} +

+
+
+
+

+ + + {% translate "PWD Reached" %} +

+
+
+ + +
+

+ + {% translate "Months" %} +

+
+
+ + +
+ +
+

+ + {% translate "Payments by Sector" %} +

+
+
+ +
+

+ + {% translate "Payments by Programme" %} +

+
+
+ +
+

+ + {% translate "Payments by Admin area" %} +

+
+
+ +
+

+ + {% translate "Payments by FSP" %} +

+
+
+ +
+

+ + {% translate "Payments by Delivery Mechanism" %} +

+
+
+ +
+
+

+ + {% translate "Reconciliation" %} +

+
%
+

{% translate "Payment records are reconciled" %}

+
+
+

+ + {% translate "Pending Reconciliation" %} +

+
%
+

{% translate "Payments pending" %}

+
+
+
+ {% else %} +
{{ error_message }}
+ {% endif %} -
-

- - {% translate "Children Reached" %} -

-
-
-
-

- - {% translate "PWD Reached" %} -

-
-
- - -
-

- - {% translate "Months" %} -

-
-
- - -
- -
-

- - {% translate "Payments by Sector" %} -

-
-
- -
-

- - {% translate "Payments by Programme" %} -

-
-
- -
-

- - {% translate "Payments by Admin 1" %} -

-
-
- -
-

- - {% translate "Payments by FSP" %} -

-
-
- -
-

- - {% translate "Payments by Delivery Mechanism" %} -

-
-
- -
- -
-

- - {% translate "Reconciliation" %} -

-
%
-

{% translate "Payment records are reconciled" %}

-
- -
-

- - {% translate "Pending Reconciliation" %} -

-
%
-

{% translate "Payments pending" %}

-
-
-
- - {% else %} -
{{ error_message }}
- {% endif %} - - - - + + + diff --git a/src/hct_mis_api/apps/household/migrations/0005_migration.py b/src/hct_mis_api/apps/household/migrations/0005_migration.py index daea327c0b..836f54f346 100644 --- a/src/hct_mis_api/apps/household/migrations/0005_migration.py +++ b/src/hct_mis_api/apps/household/migrations/0005_migration.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.25 on 2024-12-16 11:50 +# Generated by Django 3.2.25 on 2024-12-19 11:34 from django.db import migrations, models @@ -10,14 +10,12 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AlterField( + migrations.AddConstraint( model_name='household', - name='internal_data', - field=models.JSONField(blank=True, default=dict), + constraint=models.UniqueConstraint(condition=models.Q(('is_removed', False)), fields=('unicef_id', 'program'), name='unique_hh_unicef_id_in_program'), ), - migrations.AlterField( + migrations.AddConstraint( model_name='individual', - name='internal_data', - field=models.JSONField(blank=True, default=dict), + constraint=models.UniqueConstraint(condition=models.Q(('is_removed', False), ('duplicate', False)), fields=('unicef_id', 'program'), name='unique_ind_unicef_id_in_program'), ), ] diff --git a/src/hct_mis_api/apps/household/migrations/0006_migration.py b/src/hct_mis_api/apps/household/migrations/0006_migration.py new file mode 100644 index 0000000000..827c5b7f27 --- /dev/null +++ b/src/hct_mis_api/apps/household/migrations/0006_migration.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.25 on 2024-12-23 15:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('household', '0005_migration'), + ] + + operations = [ + migrations.AlterField( + model_name='household', + name='internal_data', + field=models.JSONField(blank=True, default=dict), + ), + migrations.AlterField( + model_name='individual', + name='internal_data', + field=models.JSONField(blank=True, default=dict), + ), + ] diff --git a/src/hct_mis_api/apps/household/models.py b/src/hct_mis_api/apps/household/models.py index ca48ab5733..0e0635e948 100644 --- a/src/hct_mis_api/apps/household/models.py +++ b/src/hct_mis_api/apps/household/models.py @@ -576,6 +576,13 @@ class CollectType(models.TextChoices): class Meta: verbose_name = "Household" permissions = (("can_withdrawn", "Can withdrawn Household"),) + constraints = [ + UniqueConstraint( + fields=["unicef_id", "program"], + condition=Q(is_removed=False), + name="unique_hh_unicef_id_in_program", + ) + ] def save(self, *args: Any, **kwargs: Any) -> None: from hct_mis_api.apps.targeting.models import ( @@ -1187,6 +1194,13 @@ def __str__(self) -> str: class Meta: verbose_name = "Individual" indexes = (GinIndex(fields=["vector_column"]),) + constraints = [ + UniqueConstraint( + fields=["unicef_id", "program"], + condition=Q(is_removed=False) & Q(duplicate=False), + name="unique_ind_unicef_id_in_program", + ) + ] def recalculate_data(self, save: bool = True) -> Tuple[Any, List[str]]: update_fields = ["disability"] diff --git a/src/hct_mis_api/apps/household/schema.py b/src/hct_mis_api/apps/household/schema.py index 95652bc139..eb05f07f73 100644 --- a/src/hct_mis_api/apps/household/schema.py +++ b/src/hct_mis_api/apps/household/schema.py @@ -5,6 +5,7 @@ Case, F, Func, + Model, OuterRef, Prefetch, Q, @@ -264,13 +265,17 @@ class IndividualNode(BaseNodePermissionMixin, AdminUrlNodeMixin, DjangoObjectTyp IndividualIdentityNode, ) + @classmethod + def get_node(cls, info: Any, object_id: str, **kwargs: Any) -> Optional[Model]: + return super().get_node(info, object_id, get_object_queryset=Individual.all_merge_status_objects) + @staticmethod def resolve_documents(parent: Individual, info: Any) -> QuerySet[Document]: - return Document.objects.filter(pk__in=parent.documents.values("id")) + return parent.documents(manager="all_merge_status_objects") @staticmethod def resolve_identities(parent: Individual, info: Any) -> QuerySet[IndividualIdentity]: - return IndividualIdentity.objects.filter(pk__in=parent.identities.values("id")) + return parent.identities(manager="all_merge_status_objects") @staticmethod def resolve_import_id(parent: Individual, info: Any) -> str: @@ -282,16 +287,16 @@ def resolve_preferred_language(parent: Individual, info: Any) -> Optional[str]: @staticmethod def resolve_payment_channels(parent: Individual, info: Any) -> QuerySet[BankAccountInfo]: - return BankAccountInfo.objects.filter(individual=parent).annotate(type=Value("BANK_TRANSFER")) + return BankAccountInfo.all_merge_status_objects.filter(individual=parent).annotate(type=Value("BANK_TRANSFER")) def resolve_bank_account_info(parent, info: Any) -> Optional[BankAccountInfo]: - bank_account_info = parent.bank_account_info.first() + bank_account_info = parent.bank_account_info(manager="all_merge_status_objects").first() # type: ignore if bank_account_info: return bank_account_info return None def resolve_role(parent, info: Any) -> str: - role = parent.households_and_roles.first() + role = parent.households_and_roles(manager="all_merge_status_objects").first() if role is not None: return role.role return ROLE_NO_ROLE @@ -481,13 +486,14 @@ def resolve_selection(parent: Household, info: Any) -> HouseholdSelection: @staticmethod def resolve_individuals(parent: Household, info: Any, *arg: Any, **kwargs: Any) -> QuerySet: - individuals_ids = list(parent.individuals.values_list("id", flat=True)) - collectors_ids = list(parent.representatives.values_list("id", flat=True)) + individuals_ids = list(parent.individuals(manager="all_merge_status_objects").values_list("id", flat=True)) + + collectors_ids = list(parent.representatives(manager="all_merge_status_objects").values_list("id", flat=True)) ids = list(set(individuals_ids + collectors_ids)) - return Individual.objects.filter(id__in=ids).prefetch_related( + return Individual.all_merge_status_objects.filter(id__in=ids).prefetch_related( Prefetch( "households_and_roles", - queryset=IndividualRoleInHousehold.objects.filter(household=parent.id), + queryset=IndividualRoleInHousehold.all_merge_status_objects.filter(household=parent.id), ) ) @@ -578,6 +584,10 @@ def get_queryset(cls, queryset: QuerySet[Household], info: Any) -> QuerySet[Hous qs = super().get_queryset(queryset, info) return qs + @classmethod + def get_node(cls, info: Any, object_id: str, **kwargs: Any) -> Optional[Model]: + return super().get_node(info, object_id, get_object_queryset=Household.all_merge_status_objects) + class Meta: model = Household filter_fields = [] @@ -706,7 +716,7 @@ def resolve_all_individuals(self, info: Any, **kwargs: Any) -> QuerySet[Individu if program and program.status == Program.DRAFT: return Individual.objects.none() - queryset = Individual.objects.all() + queryset = Individual.all_merge_status_objects.all() if does_path_exist_in_query("edges.node.household", info): queryset = queryset.select_related("household") if does_path_exist_in_query("edges.node.household.admin2", info): @@ -762,7 +772,7 @@ def resolve_all_households(self, info: Any, **kwargs: Any) -> QuerySet: if program and program.status == Program.DRAFT: return Household.objects.none() - queryset = Household.objects.all() + queryset = Household.all_merge_status_objects.all() if not user.partner.is_unicef: # Unicef partner has full access to all AdminAreas business_area_id = BusinessArea.objects.get(slug=business_area_slug).id diff --git a/src/hct_mis_api/apps/payment/schema.py b/src/hct_mis_api/apps/payment/schema.py index 03b3ea2977..fba3932a0c 100644 --- a/src/hct_mis_api/apps/payment/schema.py +++ b/src/hct_mis_api/apps/payment/schema.py @@ -801,7 +801,7 @@ def resolve_total_number_of_households(self, info: Any, **kwargs: Any) -> int: return self.payment_items.count() def resolve_verification_status(self, info: Any, **kwargs: Any) -> Optional[graphene.String]: - return self.payment_verification_summary.status if self.payment_verification_summary else None + return self.payment_verification_summary.status if hasattr(self, "payment_verification_summary") else None def resolve_status(self, info: Any, **kwargs: Any) -> Optional[graphene.String]: return self.status diff --git a/src/hct_mis_api/apps/registration_datahub/mutations.py b/src/hct_mis_api/apps/registration_datahub/mutations.py index 2871b860bb..b95e038275 100644 --- a/src/hct_mis_api/apps/registration_datahub/mutations.py +++ b/src/hct_mis_api/apps/registration_datahub/mutations.py @@ -115,15 +115,21 @@ def create_registration_data_import_for_import_program_population( pull_pictures = registration_data_import_data.pop("pull_pictures", True) screen_beneficiary = registration_data_import_data.pop("screen_beneficiary", False) import_from_program_id = registration_data_import_data.pop("import_from_program_id", None) + households_to_exclude = Household.all_merge_status_objects.filter( + program=import_to_program_id, + ).values_list("unicef_id", flat=True) households = Household.objects.filter( program_id=import_from_program_id, withdrawn=False, - ).exclude(household_collection__households__program=import_to_program_id) + ).exclude(unicef_id__in=households_to_exclude) + individuals_to_exclude = Individual.all_merge_status_objects.filter( + program=import_to_program_id, + ).values_list("unicef_id", flat=True) individuals = Individual.objects.filter( program_id=import_from_program_id, withdrawn=False, duplicate=False, - ).exclude(individual_collection__individuals__program=import_to_program_id) + ).exclude(unicef_id__in=individuals_to_exclude) created_obj_hct = RegistrationDataImport( status=RegistrationDataImport.IMPORTING, imported_by=user, diff --git a/src/hct_mis_api/apps/registration_datahub/tasks/import_program_population.py b/src/hct_mis_api/apps/registration_datahub/tasks/import_program_population.py index 95b6ad9cac..0dfad51a54 100644 --- a/src/hct_mis_api/apps/registration_datahub/tasks/import_program_population.py +++ b/src/hct_mis_api/apps/registration_datahub/tasks/import_program_population.py @@ -8,17 +8,23 @@ def import_program_population( import_from_program_id: str, import_to_program_id: str, rdi: RegistrationDataImport ) -> None: + households_to_exclude = Household.all_merge_status_objects.filter( + program=import_to_program_id, + ).values_list("unicef_id", flat=True) copy_from_households = Household.objects.filter( program=import_from_program_id, withdrawn=False, - ).exclude(household_collection__households__program_id=import_to_program_id) + ).exclude(unicef_id__in=households_to_exclude) + individuals_to_exclude = Individual.all_merge_status_objects.filter( + program=import_to_program_id, + ).values_list("unicef_id", flat=True) copy_from_individuals = ( Individual.objects.filter( program_id=import_from_program_id, withdrawn=False, duplicate=False, ) - .exclude(individual_collection__individuals__program_id=import_to_program_id) + .exclude(unicef_id__in=individuals_to_exclude) .order_by("first_registration_date") ) import_to_program = Program.objects.get(id=import_to_program_id) diff --git a/src/hct_mis_api/apps/utils/models.py b/src/hct_mis_api/apps/utils/models.py index 981aa3390d..21bb5e21b2 100644 --- a/src/hct_mis_api/apps/utils/models.py +++ b/src/hct_mis_api/apps/utils/models.py @@ -152,9 +152,7 @@ class SoftDeletableRepresentationMergeStatusModel(MergeStatusModel): class Meta: abstract = True - # objects = SoftDeletableRepresentationMergedManager(_emit_deprecation_warnings=True) - # now we use 'rdi_merge_status' field for filtering - objects = SoftDeletableRepresentationManager() + objects = SoftDeletableRepresentationMergedManager(_emit_deprecation_warnings=True) all_merge_status_objects = SoftDeletableRepresentationManager() available_objects = SoftDeletableRepresentationMergedManager() all_objects = models.Manager() diff --git a/src/hct_mis_api/migrations_script/main.py b/src/hct_mis_api/migrations_script/main.py index 6a2c7c9038..f6968d622e 100644 --- a/src/hct_mis_api/migrations_script/main.py +++ b/src/hct_mis_api/migrations_script/main.py @@ -47,10 +47,8 @@ def apply_migrations(): clear_migration_table() excluded_migrations = [ ("targeting", "0002_migration"), - ("program", "0002_migration"), ("household", "0003_migration"), ("household", "0004_migration"), - ("household", "0005_migration"), ("grievance", "0004_migration"), ("payment", "0002_migration"), ("payment", "0003_migration"), @@ -58,8 +56,6 @@ def apply_migrations(): ("payment", "0005_migration"), ("payment", "0006_migration"), ("payment", "0007_migration"), - ("payment", "0008_migration"), - ("payment", "0009_migration"), ("aurora", "0003_migration"), ] fake_migrations(excluded_migrations) diff --git a/src/hct_mis_api/one_time_scripts/fix_program_population_import_incorrect_hh_ind_relation.py b/src/hct_mis_api/one_time_scripts/fix_program_population_import_incorrect_hh_ind_relation.py deleted file mode 100644 index a265de4886..0000000000 --- a/src/hct_mis_api/one_time_scripts/fix_program_population_import_incorrect_hh_ind_relation.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.db.models import F - -from hct_mis_api.apps.household.models import Household, Individual -from hct_mis_api.apps.registration_data.models import RegistrationDataImport - - -def fix_program_population_import_incorrect_hh_ind_relation() -> None: - individuals = Individual.all_objects.filter( - registration_data_import__data_source=RegistrationDataImport.PROGRAM_POPULATION, - ).exclude(household__registration_data_import=F("registration_data_import")) - - for individual in individuals: - household = Household.all_objects.filter( - registration_data_import=individual.registration_data_import, - copied_from_id=individual.household.copied_from_id, - copied_from__isnull=False, - program=individual.program, - ).first() - if household and household.unicef_id == individual.household.unicef_id: - individual.household = household - individual.save(update_fields=["household"]) diff --git a/src/hct_mis_api/one_time_scripts/migrate_cash_assist_models.py b/src/hct_mis_api/one_time_scripts/migrate_cash_assist_models.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/hct_mis_api/one_time_scripts/remove_production_duplicates_after_enrollment.py b/src/hct_mis_api/one_time_scripts/remove_production_duplicates_after_enrollment.py deleted file mode 100644 index 7b4f5a4a5d..0000000000 --- a/src/hct_mis_api/one_time_scripts/remove_production_duplicates_after_enrollment.py +++ /dev/null @@ -1,50 +0,0 @@ -import logging - -from django.db.models import Count - -from hct_mis_api.apps.household.models import Household - -logger = logging.getLogger(__name__) - - -def remove_production_duplicates_after_enrollment() -> None: - # Exceptions from the further rules - for household in Household.objects.filter( - id__in=[ - "8b9bf768-4837-49aa-a598-5ad3c5822ca8", - "33a7bdf0-650d-49b4-b333-c49a7eb05356", - ] - ): - household.delete(soft=False) - - households_with_duplicates = ( - Household.objects.values("unicef_id", "program") - .annotate(household_count=Count("id")) - .filter(household_count__gt=1) - .order_by("copied_from__registration_data_import") - ) - logger.info(f"Found {households_with_duplicates.count()} households with duplicates") - - for i, entry in enumerate(households_with_duplicates, 1): - unicef_id = entry["unicef_id"] - program = entry["program"] - - households = Household.objects.filter(unicef_id=unicef_id, program=program).order_by("created_at") - - # Keep the first household and delete the duplicates - first = True - households_to_remove = [] - for household in households: - if first: - first = False - continue - if household.payment_set.exists(): - logger.info(f"Skipping {household.id} because it has payments") - continue - else: - households_to_remove.append(household) - for duplicate in households_to_remove: - duplicate.delete(soft=False) - - if i % 100 == 0: - logger.info(f"Processed {i}/{households_with_duplicates.count()} households") diff --git a/tests/selenium/grievance/grievance_tickets/test_grievance_tickets.py b/tests/selenium/grievance/grievance_tickets/test_grievance_tickets.py index c69a12b17e..e4b5ec53a8 100644 --- a/tests/selenium/grievance/grievance_tickets/test_grievance_tickets.py +++ b/tests/selenium/grievance/grievance_tickets/test_grievance_tickets.py @@ -1218,6 +1218,7 @@ def test_grievance_tickets_go_to_admin_panel_button( assert "grievance_ticket_1" in pageAdminPanel.getUnicefID().text assert GrievanceTicket.objects.first().unicef_id in pageAdminPanel.getUnicefID().text + @pytest.mark.xfail(reason="UNSTABLE") def test_grievance_tickets_needs_adjudication( self, add_grievance_needs_adjudication: None, diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index 5f3adeb7c0..33822ade06 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -710,6 +710,7 @@ class TestManualCalendar: ), ], ) + @pytest.mark.xfail(reason="UNSTABLE") def test_create_programme_chose_dates_via_calendar( self, pageProgrammeManagement: ProgrammeManagement, pageProgrammeDetails: ProgrammeDetails, test_data: dict ) -> None: diff --git a/tests/selenium/targeting/test_targeting.py b/tests/selenium/targeting/test_targeting.py index 3b5d0202fc..ad266ac6f1 100644 --- a/tests/selenium/targeting/test_targeting.py +++ b/tests/selenium/targeting/test_targeting.py @@ -177,11 +177,13 @@ def create_flexible_attribute( return flexible_attribute -def create_custom_household(observed_disability: list[str], residence_status: str = HOST) -> Household: +def create_custom_household( + observed_disability: list[str], residence_status: str = HOST, unicef_id: str = "HH-00-0000.0442" +) -> Household: program = Program.objects.get(name="Test Programm") household, _ = create_household_and_individuals( household_data={ - "unicef_id": "HH-00-0000.0442", + "unicef_id": unicef_id, "rdi_merge_status": "MERGED", "business_area": program.business_area, "program": program, @@ -200,17 +202,17 @@ def create_custom_household(observed_disability: list[str], residence_status: st @pytest.fixture def household_with_disability() -> Household: - yield create_custom_household(observed_disability=[SEEING, HEARING]) + yield create_custom_household(observed_disability=[SEEING, HEARING], unicef_id="HH-00-0000.0443") @pytest.fixture def household_without_disabilities() -> Household: - yield create_custom_household(observed_disability=[]) + yield create_custom_household(observed_disability=[], unicef_id="HH-00-0000.0444") @pytest.fixture def household_refugee() -> Household: - yield create_custom_household(observed_disability=[], residence_status=REFUGEE) + yield create_custom_household(observed_disability=[], residence_status=REFUGEE, unicef_id="HH-00-0000.0445") def get_program_with_dct_type_and_name( diff --git a/tests/unit/apps/household/test_models.py b/tests/unit/apps/household/test_models.py index d4522a1ed0..485a4fe534 100644 --- a/tests/unit/apps/household/test_models.py +++ b/tests/unit/apps/household/test_models.py @@ -7,7 +7,12 @@ from hct_mis_api.apps.core.utils import IDENTIFICATION_TYPE_TO_KEY_MAPPING from hct_mis_api.apps.geo.fixtures import AreaFactory, AreaTypeFactory from hct_mis_api.apps.geo.models import Country -from hct_mis_api.apps.household.fixtures import BankAccountInfoFactory, create_household +from hct_mis_api.apps.household.fixtures import ( + BankAccountInfoFactory, + HouseholdFactory, + IndividualFactory, + create_household, +) from hct_mis_api.apps.household.models import ( IDENTIFICATION_TYPE_NATIONAL_PASSPORT, IDENTIFICATION_TYPE_OTHER, @@ -30,6 +35,7 @@ def setUpTestData(cls) -> None: super().setUpTestData() create_afghanistan() cls.business_area = BusinessArea.objects.get(slug="afghanistan") + cls.program = ProgramFactory(business_area=cls.business_area) area_type_level_1 = AreaTypeFactory( name="State1", @@ -114,15 +120,21 @@ def test_remove_household(self) -> None: household2.delete(soft=False) self.assertIsNone(Household.all_objects.filter(unicef_id="HH-9191").first()) + def test_unique_unicef_id_per_program_constraint(self) -> None: + HouseholdFactory(unicef_id="HH-123", program=self.program) + HouseholdFactory(unicef_id="HH-000", program=self.program) + with self.assertRaises(IntegrityError): + HouseholdFactory(unicef_id="HH-123", program=self.program) + class TestDocument(TestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() call_command("loadcountries") - business_area = create_afghanistan() + cls.business_area = create_afghanistan() afghanistan = Country.objects.get(name="Afghanistan") - _, (individual,) = create_household(household_args={"size": 1, "business_area": business_area}) + _, (individual,) = create_household(household_args={"size": 1, "business_area": cls.business_area}) cls.country = afghanistan cls.individual = individual @@ -187,10 +199,9 @@ def test_create_representation_with_the_same_number(self) -> None: program_3 = ProgramFactory() program_4 = ProgramFactory() - for _program in [program_1, program_2]: - (individual_to_create, documents_to_create, _, _) = copy_individual_fast(self.individual, _program) - Individual.objects.bulk_create([individual_to_create]) - Document.objects.bulk_create(documents_to_create) + (individual_to_create, documents_to_create, _, _) = copy_individual_fast(self.individual, program_2) + Individual.objects.bulk_create([individual_to_create]) + Document.objects.bulk_create(documents_to_create) # test regular create for _program in [program_3, program_4]: @@ -209,8 +220,9 @@ def test_create_representation_with_the_same_number(self) -> None: ) # don't allow to create representations with the same document number and programs - (individual_to_create, _, _, _) = copy_individual_fast(self.individual, _program) - (created_individual_representation,) = Individual.objects.bulk_create([individual_to_create]) + _, (individual,) = create_household( + household_args={"size": 1, "business_area": self.business_area, "program": program_1} + ) with self.assertRaises(IntegrityError): with transaction.atomic(): # bulk create @@ -218,7 +230,7 @@ def test_create_representation_with_the_same_number(self) -> None: [ Document( document_number="213123", - individual=created_individual_representation, + individual=individual, country=self.country, type=document_type, status=Document.STATUS_VALID, @@ -234,7 +246,7 @@ def test_create_representation_with_the_same_number(self) -> None: # regular create Document.objects.create( document_number="213123", - individual=created_individual_representation, + individual=individual, country=self.country, type=document_type, status=Document.STATUS_VALID, @@ -421,21 +433,16 @@ def test_create_representations_duplicated_documents_with_different_numbers_and_ # allow to create representations with the same document number within different programs self.individual.is_original = True self.individual.save() - - program_1 = self.individual.program program_2 = ProgramFactory() program_3 = ProgramFactory() - # make representations with the same number - for _program in [program_1, program_2]: - (individual_to_create, documents_to_create, _, _) = copy_individual_fast(self.individual, _program) - Individual.objects.bulk_create([individual_to_create]) - Document.objects.bulk_create(documents_to_create) + # make representation with the same number + (individual_to_create, documents_to_create, _, _) = copy_individual_fast(self.individual, program_2) + Individual.objects.bulk_create([individual_to_create]) + Document.objects.bulk_create(documents_to_create) # make representation with different number - program_3_individual_representation = (individual_to_create, _, _, _) = copy_individual_fast( - self.individual, program_3 - ) + (individual_to_create, _, _, _) = copy_individual_fast(self.individual, program_3) (program_3_individual_representation,) = Individual.objects.bulk_create([individual_to_create]) Document.objects.create( document_number="456", @@ -509,8 +516,8 @@ class TestIndividualModel(TestCase): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() - create_afghanistan() - ProgramFactory() + business_area = create_afghanistan() + cls.program = ProgramFactory(business_area=business_area) def test_bank_name(self) -> None: individual = create_household({"size": 1})[1][0] @@ -531,3 +538,9 @@ def test_bank_branch_name(self) -> None: individual = create_household({"size": 1})[1][0] bank_account_info = BankAccountInfoFactory(individual=individual) self.assertEqual(individual.bank_branch_name, bank_account_info.bank_branch_name) + + def test_unique_unicef_id_per_program_constraint(self) -> None: + IndividualFactory(unicef_id="IND-123", program=self.program) + IndividualFactory(unicef_id="IND-000", program=self.program) + with self.assertRaises(IntegrityError): + IndividualFactory(unicef_id="IND-123", program=self.program) diff --git a/tests/unit/apps/registration_datahub/test_program_population_to_pending_objects.py b/tests/unit/apps/registration_datahub/test_program_population_to_pending_objects.py index f57585a4a3..353d74ecf3 100644 --- a/tests/unit/apps/registration_datahub/test_program_population_to_pending_objects.py +++ b/tests/unit/apps/registration_datahub/test_program_population_to_pending_objects.py @@ -456,32 +456,27 @@ def test_create_pending_objects_from_objects(self) -> None: pending_individual_role_in_household.individual, pending_individuals.exclude(relationship=HEAD).first(), ) - for _ in range(10): - registration_data_import = RegistrationDataImportFactory( - business_area=self.afghanistan, - program=self.program_to, - ) - import_program_population( - import_from_program_id=str(self.program_from.id), - import_to_program_id=str(self.program_to.id), - rdi=registration_data_import, - ) - pending_household = Household.pending_objects.order_by("created_at").last() - pending_individual1 = Individual.pending_objects.order_by("-created_at")[0] - pending_individual2 = Individual.pending_objects.order_by("-created_at")[1] - - self.assertIn( - pending_household.head_of_household, - [pending_individual1, pending_individual2], - ) - self.assertEqual( - pending_individual1.household, - pending_household, - ) - self.assertEqual( - pending_individual2.household, - pending_household, - ) + registration_data_import = RegistrationDataImportFactory( + business_area=self.afghanistan, + program=self.program_to, + ) + import_program_population( + import_from_program_id=str(self.program_from.id), + import_to_program_id=str(self.program_to.id), + rdi=registration_data_import, + ) + pending_household_count = ( + Household.pending_objects.filter(registration_data_import=registration_data_import) + .order_by("created_at") + .count() + ) + pending_individual_count = ( + Individual.pending_objects.filter(registration_data_import=registration_data_import) + .order_by("-created_at") + .count() + ) + self.assertEqual(pending_household_count, 0) + self.assertEqual(pending_individual_count, 0) def test_not_import_excluded_objects(self) -> None: household_withdrawn, individuals = create_household_and_individuals( @@ -520,10 +515,12 @@ def test_not_import_excluded_objects(self) -> None: household_already_in_program.household_collection = household_collection household_already_in_program.save() household_already_in_program_repr.household_collection = household_collection + household_already_in_program_repr.unicef_id = household_already_in_program.unicef_id household_already_in_program_repr.save() individuals_already_in_program[0].individual_collection = individual_collection individuals_already_in_program[0].save() individuals_already_in_program_repr[0].individual_collection = individual_collection + individuals_already_in_program_repr[0].unicef_id = individuals_already_in_program[0].unicef_id individuals_already_in_program_repr[0].save() self._object_count_before_after() diff --git a/tests/unit/apps/registration_datahub/test_rdi_merge.py b/tests/unit/apps/registration_datahub/test_rdi_merge.py index fa6040da77..739d295675 100644 --- a/tests/unit/apps/registration_datahub/test_rdi_merge.py +++ b/tests/unit/apps/registration_datahub/test_rdi_merge.py @@ -447,6 +447,7 @@ def test_merge_rdi_create_collections(self, household_representation_exists: boo if False, another household representation exists, but it does not have collection, if None, household representation does not exist in another program """ + program_2 = ProgramFactory(business_area=self.rdi.business_area) self.rdi.data_source = RegistrationDataImport.PROGRAM_POPULATION self.rdi.save() imported_household = HouseholdFactory( @@ -454,11 +455,13 @@ def test_merge_rdi_create_collections(self, household_representation_exists: boo registration_data_import=self.rdi, unicef_id="HH-9", rdi_merge_status=MergeStatusModel.PENDING, + program=self.rdi.program, ) self.set_imported_individuals(imported_household) individual_without_collection = IndividualFactory( unicef_id="IND-9", business_area=self.rdi.business_area, + program=program_2, household=None, ) individual_without_collection.individual_collection = None @@ -468,6 +471,7 @@ def test_merge_rdi_create_collections(self, household_representation_exists: boo IndividualFactory( unicef_id="IND-8", business_area=self.rdi.business_area, + program=program_2, individual_collection=individual_collection, household=None, ) @@ -477,8 +481,8 @@ def test_merge_rdi_create_collections(self, household_representation_exists: boo household = HouseholdFactory( head_of_household=individual_without_collection, business_area=self.rdi.business_area, + program=program_2, unicef_id="HH-9", - rdi_merge_status=MergeStatusModel.PENDING, ) household.household_collection = None household.save() @@ -503,10 +507,10 @@ def test_merge_rdi_create_collections(self, household_representation_exists: boo if household_representation_exists is not None: if household_representation_exists: household_collection.refresh_from_db() - self.assertEqual(household_collection.households.count(), 2) # 1 + self.assertEqual(household_collection.households.count(), 2) else: household.refresh_from_db() - self.assertIsNotNone(household.household_collection) # None + self.assertIsNotNone(household.household_collection) self.assertEqual(household.household_collection.households.count(), 2) def test_merging_external_collector(self) -> None: diff --git a/tests/unit/apps/registration_datahub/test_registration_program_population_import_task.py b/tests/unit/apps/registration_datahub/test_registration_program_population_import_task.py index 113474a75a..624520b69a 100644 --- a/tests/unit/apps/registration_datahub/test_registration_program_population_import_task.py +++ b/tests/unit/apps/registration_datahub/test_registration_program_population_import_task.py @@ -207,7 +207,7 @@ def test_registration_program_population_import_task(self) -> None: str(self.program_from.id), str(self.program_to.id), ) - self._imported_objects_count_after(2) + self._imported_objects_count_after(1) def test_registration_program_population_import_task_error(self) -> None: rdi_id = self.registration_data_import.id diff --git a/tests/unit/apps/targeting/snapshots/snap_test_copy_target_population_mutation.py b/tests/unit/apps/targeting/snapshots/snap_test_copy_target_population_mutation.py index dd1bcfdf15..364024e09f 100644 --- a/tests/unit/apps/targeting/snapshots/snap_test_copy_target_population_mutation.py +++ b/tests/unit/apps/targeting/snapshots/snap_test_copy_target_population_mutation.py @@ -151,7 +151,7 @@ 'status': 'OPEN', 'targetingCriteria': { 'householdIds': '', - 'individualIds': "['IND-1']", + 'individualIds': "['IND-12']", 'rules': [ ] }, diff --git a/tests/unit/apps/targeting/test_copy_target_population_mutation.py b/tests/unit/apps/targeting/test_copy_target_population_mutation.py index 21b2c081de..5ca5fcc072 100644 --- a/tests/unit/apps/targeting/test_copy_target_population_mutation.py +++ b/tests/unit/apps/targeting/test_copy_target_population_mutation.py @@ -86,7 +86,7 @@ def setUpTestData(cls) -> None: }, ) individual = individuals[0] - individual.unicef_id = "IND-1" + individual.unicef_id = "IND-12" individual.save() cls.household = household cls.update_partner_access_to_program(partner, cls.program) diff --git a/tests/unit/apps/targeting/test_targeting_validators.py b/tests/unit/apps/targeting/test_targeting_validators.py index 29d609085f..7dd0ba3e6d 100644 --- a/tests/unit/apps/targeting/test_targeting_validators.py +++ b/tests/unit/apps/targeting/test_targeting_validators.py @@ -46,17 +46,17 @@ def setUpTestData(cls) -> None: def test_TargetingCriteriaInputValidator(self) -> None: validator = TargetingCriteriaInputValidator - create_household({"unicef_id": "HH-1", "size": 1}, {"unicef_id": "IND-1"}) + create_household({"unicef_id": "HH-1", "size": 1}, {"unicef_id": "IND-12"}) self._update_program(self.program_standard) validator.validate( - {"rules": [{"Rule1": {"test": "123"}, "household_ids": "HH-1", "individual_ids": "IND-1"}]}, + {"rules": [{"Rule1": {"test": "123"}, "household_ids": "HH-1", "individual_ids": "IND-12"}]}, self.program_standard, ) with self.assertRaisesMessage(ValidationError, "Target criteria can only have individual ids"): self._update_program(self.program_standard_ind_only) validator.validate( - {"rules": [{"household_ids": "HH-1", "individual_ids": "IND-1"}]}, self.program_standard_ind_only + {"rules": [{"household_ids": "HH-1", "individual_ids": "IND-12"}]}, self.program_standard_ind_only ) with self.assertRaisesMessage(ValidationError, "There should be at least 1 rule in target criteria"): diff --git a/tests/unit/one_time_scripts/test_fix_program_population_import_incorrect_hh_ind_relation.py b/tests/unit/one_time_scripts/test_fix_program_population_import_incorrect_hh_ind_relation.py deleted file mode 100644 index dbce506524..0000000000 --- a/tests/unit/one_time_scripts/test_fix_program_population_import_incorrect_hh_ind_relation.py +++ /dev/null @@ -1,98 +0,0 @@ -from django.test import TestCase - -from hct_mis_api.apps.core.fixtures import create_afghanistan -from hct_mis_api.apps.household.fixtures import ( - HouseholdFactory, - create_household_and_individuals, -) -from hct_mis_api.apps.household.models import Household -from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory -from hct_mis_api.apps.registration_data.models import RegistrationDataImport -from hct_mis_api.one_time_scripts.fix_program_population_import_incorrect_hh_ind_relation import ( - fix_program_population_import_incorrect_hh_ind_relation, -) - - -class TestFixProgramPopulationIncorrectHhIndRelation(TestCase): - def test_fix_program_population_import_incorrect_hh_ind_relation(self) -> None: - business_area = create_afghanistan() - program = ProgramFactory(business_area=business_area) - copied_from_hh = HouseholdFactory(program=program) - program2 = ProgramFactory(business_area=business_area) - - rdi = RegistrationDataImportFactory(program=program2, data_source=RegistrationDataImport.PROGRAM_POPULATION) - rdi2 = RegistrationDataImportFactory(program=program2, data_source=RegistrationDataImport.PROGRAM_POPULATION) - rdi3 = RegistrationDataImportFactory(program=program2, data_source=RegistrationDataImport.PROGRAM_POPULATION) - household1, individuals1 = create_household_and_individuals( - { - "program": program2, - "unicef_id": "HH-01", - "rdi_merge_status": Household.PENDING, - "registration_data_import": rdi, - "copied_from": copied_from_hh, - }, - [ - {"rdi_merge_status": Household.PENDING}, - {"rdi_merge_status": Household.PENDING}, - ], - ) - - household2, individuals2 = create_household_and_individuals( - { - "program": program2, - "unicef_id": "HH-01", - "rdi_merge_status": Household.PENDING, - "registration_data_import": rdi2, - "copied_from": copied_from_hh, - }, - [{"rdi_merge_status": Household.PENDING}, {"rdi_merge_status": Household.PENDING}], - ) - ind2_1 = individuals2[0] - ind2_1.household = household1 - ind2_1.save() - - household3, individuals3 = create_household_and_individuals( - { - "program": program2, - "unicef_id": "HH-01", - "rdi_merge_status": Household.MERGED, - "registration_data_import": rdi3, - "copied_from": copied_from_hh, - }, - [{"rdi_merge_status": Household.MERGED}, {"rdi_merge_status": Household.MERGED}], - ) - ind3_1 = individuals3[0] - ind3_1.household = household1 - ind3_1.save() - ind3_2 = individuals3[1] - ind3_2.household = household2 - ind3_2.save() - - # check incorrect data - self.assertEqual(household1.individuals(manager="all_objects").count(), 4) - self.assertEqual(household2.individuals(manager="all_objects").count(), 2) - self.assertEqual(household3.individuals(manager="all_objects").count(), 0) - - self.assertNotEqual(ind2_1.household.registration_data_import, household2.registration_data_import) - self.assertNotEqual(ind3_1.household.registration_data_import, household3.registration_data_import) - self.assertNotEqual(ind3_2.household.registration_data_import, household3.registration_data_import) - - fix_program_population_import_incorrect_hh_ind_relation() - - ind2_1.refresh_from_db() - ind3_1.refresh_from_db() - ind3_2.refresh_from_db() - - self.assertEqual(household1.individuals(manager="all_objects").count(), 2) - - self.assertEqual(ind2_1.household, household2) - self.assertEqual(household2.individuals(manager="all_objects").count(), 2) - - self.assertEqual(ind3_1.household, household3) - self.assertEqual(ind3_2.household, household3) - self.assertEqual(household3.individuals(manager="all_objects").count(), 2) - - self.assertEqual(ind2_1.household.registration_data_import, household2.registration_data_import) - self.assertEqual(ind3_1.household.registration_data_import, household3.registration_data_import) - self.assertEqual(ind3_2.household.registration_data_import, household3.registration_data_import) diff --git a/tests/unit/one_time_scripts/test_migrate_cash_assist_models.py b/tests/unit/one_time_scripts/test_migrate_cash_assist_models.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/unit/one_time_scripts/test_remove_production_duplicates_after_enrollment.py b/tests/unit/one_time_scripts/test_remove_production_duplicates_after_enrollment.py deleted file mode 100644 index 0edcc9d0a0..0000000000 --- a/tests/unit/one_time_scripts/test_remove_production_duplicates_after_enrollment.py +++ /dev/null @@ -1,103 +0,0 @@ -from django.test import TestCase - -from hct_mis_api.apps.core.fixtures import create_afghanistan -from hct_mis_api.apps.household.fixtures import create_household_and_individuals -from hct_mis_api.apps.household.models import Household, Individual -from hct_mis_api.apps.payment.fixtures import PaymentFactory -from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.one_time_scripts.remove_production_duplicates_after_enrollment import ( - remove_production_duplicates_after_enrollment, -) - - -class TestRemoveProductionDuplicatesAfterEnrollment(TestCase): - def test_remove_production_duplicates_after_enrollment(self) -> None: - business_area = create_afghanistan() - program = ProgramFactory(business_area=business_area) - program2 = ProgramFactory(business_area=business_area) - hh_unicef_id = "HH-20-0000.0001" - household_special_case1, individuals_special_case1 = create_household_and_individuals( - household_data={ - "id": "8b9bf768-4837-49aa-a598-5ad3c5822ca8", - "unicef_id": hh_unicef_id, - "business_area": program.business_area, - "program": program, - }, - individuals_data=[{}], - ) - household_special_case2, individuals_special_case2 = create_household_and_individuals( - household_data={ - "id": "33a7bdf0-650d-49b4-b333-c49a7eb05356", - "unicef_id": hh_unicef_id, - "business_area": program.business_area, - "program": program, - }, - individuals_data=[{}], - ) - household1, individuals1 = create_household_and_individuals( - household_data={ - "unicef_id": hh_unicef_id, - "business_area": program.business_area, - "program": program, - }, - individuals_data=[{}, {}], - ) - household2, individuals2 = create_household_and_individuals( - household_data={ - "unicef_id": hh_unicef_id, - "business_area": program.business_area, - "program": program, - }, - individuals_data=[{}, {}], - ) - household3, individuals3 = create_household_and_individuals( - household_data={ - "unicef_id": hh_unicef_id, - "business_area": program.business_area, - "program": program, - }, - individuals_data=[{}], - ) - PaymentFactory(household=household3) - - household4, individuals4 = create_household_and_individuals( - household_data={ - "unicef_id": hh_unicef_id, - "business_area": program.business_area, - "program": program, - }, - individuals_data=[{}], - ) - household_from_another_program, individuals_from_another_program = create_household_and_individuals( - household_data={ - "unicef_id": hh_unicef_id, - "business_area": program2.business_area, - "program": program2, - }, - individuals_data=[{}], - ) - - remove_production_duplicates_after_enrollment() - - self.assertIsNotNone(Household.all_objects.filter(id=household1.id).first()) - self.assertIsNotNone(Individual.all_objects.filter(id=individuals1[0].id).first()) - self.assertIsNotNone(Individual.all_objects.filter(id=individuals1[1].id).first()) - - self.assertIsNone(Household.all_objects.filter(id=household2.id).first()) - self.assertIsNone(Individual.all_objects.filter(id=individuals2[0].id).first()) - self.assertIsNone(Individual.all_objects.filter(id=individuals2[1].id).first()) - - self.assertIsNotNone(Individual.all_objects.filter(id=individuals3[0].id).first()) - self.assertIsNotNone(Household.all_objects.filter(id=household3.id).first()) - - self.assertIsNone(Household.all_objects.filter(id=household4.id).first()) - self.assertIsNone(Individual.all_objects.filter(id=individuals4[0].id).first()) - - self.assertIsNotNone(Household.all_objects.filter(id=household_from_another_program.id).first()) - self.assertIsNotNone(Individual.all_objects.filter(id=individuals_from_another_program[0].id).first()) - - self.assertIsNone(Household.all_objects.filter(id=household_special_case1.id).first()) - self.assertIsNone(Individual.all_objects.filter(id=individuals_special_case1[0].id).first()) - - self.assertIsNone(Household.all_objects.filter(id=household_special_case2.id).first()) - self.assertIsNone(Individual.all_objects.filter(id=individuals_special_case2[0].id).first()) From c3adb142d83cfa222dcc538d3d9f4ffc42081282 Mon Sep 17 00:00:00 2001 From: Jan Romaniak Date: Tue, 24 Dec 2024 11:13:35 +0100 Subject: [PATCH 11/11] change to use replica db (#4530) * skip unstable * STG // Fix RDI import dmd json field (#4504) * fix rdi import dmd data json * add migration * fix test * imports * more fixes * remove generateroles from upgrade, update generateroles based on admin history for these roles * [STG] Payment Plan export xlsx: added FSP template doc types list (#4508) * upd get_column_value_from_payment & fsp template doc types * document_types :star: * fix :star2: * Dashboard fix * 215787_drop_cash_assist_migration_script_fix (#4509) * 2226242_Payment_verification_page_not_accessible (#4514) * add correct permission for cycles page * fe fix * just new line * AB#226656 Error Cannot read properties of undefined Grievance ticket * change to use replica db * fix tests --------- Co-authored-by: pavlo-mk Co-authored-by: Domenico Co-authored-by: Paulina Kujawa Co-authored-by: Paulina Kujawa <42150286+pkujawa@users.noreply.github.com> Co-authored-by: Allan Stockman Rugano Co-authored-by: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> Co-authored-by: Allan Stockman RUGANO Co-authored-by: Maciej Szewczyk --- .github/helpers/.env-selenium | 1 + .github/helpers/.env-unit | 1 + development_tools/.env.example | 1 + development_tools/local_selenium_init.sh | 1 + src/frontend/data/schema.graphql | 2 +- src/frontend/src/__generated__/graphql.tsx | 13 +++++++++++-- src/hct_mis_api/config/env.py | 7 +++++++ src/hct_mis_api/config/settings.py | 10 +--------- 8 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.github/helpers/.env-selenium b/.github/helpers/.env-selenium index 053211d707..c0c20daafe 100644 --- a/.github/helpers/.env-selenium +++ b/.github/helpers/.env-selenium @@ -7,5 +7,6 @@ CELERY_RESULT_BACKEND=redis://redis:6379/0 CACHE_LOCATION=redis://redis:6379/1 CONSTANCE_REDIS_CONNECTION=redis://redis:6379/0 DATABASE_URL=postgis://postgres:postgres@db:5432/postgres +REP_DATABASE_URL=postgis://postgres:postgres@db:5432/postgres USE_DUMMY_EXCHANGE_RATES=yes CELERY_TASK_ALWAYS_EAGER=true \ No newline at end of file diff --git a/.github/helpers/.env-unit b/.github/helpers/.env-unit index 2d9b12ce49..f4d9629157 100644 --- a/.github/helpers/.env-unit +++ b/.github/helpers/.env-unit @@ -4,6 +4,7 @@ POSTGRES_DB=postgres POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres DATABASE_URL=postgis://postgres:postgres@db:5432/postgres +REP_DATABASE_URL=postgis://postgres:postgres@db:5432/postgres POSTGRES_SSL_MODE=off EMAIL_HOST=TBD EMAIL_HOST_USER=TBD diff --git a/development_tools/.env.example b/development_tools/.env.example index 8fb7fa57a3..039f56c32a 100644 --- a/development_tools/.env.example +++ b/development_tools/.env.example @@ -10,6 +10,7 @@ POSTGRES_PASS=postgres PGUSER=postgres POSTGRES_HOST_AUTH_METHOD=trust DATABASE_URL=postgis://postgres:postgres@db:5432/postgres +REP_DATABASE_URL=postgis://postgres:postgres@db:5432/postgres POSTGRES_SSL_MODE=off EMAIL_HOST=TBD EMAIL_HOST_USER=TBD diff --git a/development_tools/local_selenium_init.sh b/development_tools/local_selenium_init.sh index 435ae593e7..941c04b063 100755 --- a/development_tools/local_selenium_init.sh +++ b/development_tools/local_selenium_init.sh @@ -11,6 +11,7 @@ export POSTGRES_PASS=postgres export PGUSER=postgres export POSTGRES_HOST_AUTH_METHOD=trust export DATABASE_URL=postgis://postgres:postgres@localhost:5432/postgres +export REP_DATABASE_URL=postgis://postgres:postgres@localhost:5432/postgres export POSTGRES_SSL_MODE=off export EMAIL_HOST=TBD export EMAIL_HOST_USER=TBD diff --git a/src/frontend/data/schema.graphql b/src/frontend/data/schema.graphql index a4d8f3a996..19ac5b1a5d 100644 --- a/src/frontend/data/schema.graphql +++ b/src/frontend/data/schema.graphql @@ -2536,7 +2536,7 @@ type PaymentNode implements Node { isCashAssist: Boolean! followUps(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! householdSnapshot: PaymentHouseholdSnapshotNode - paymentVerification: PaymentVerificationNode + paymentVerifications(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationNodeConnection! ticketComplaintDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketComplaintDetailsNodeConnection! ticketSensitiveDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketSensitiveDetailsNodeConnection! adminUrl: String diff --git a/src/frontend/src/__generated__/graphql.tsx b/src/frontend/src/__generated__/graphql.tsx index 5c1110a8bb..2cf5624f5a 100644 --- a/src/frontend/src/__generated__/graphql.tsx +++ b/src/frontend/src/__generated__/graphql.tsx @@ -4206,7 +4206,7 @@ export type PaymentNode = Node & { paymentPlanHardConflictedData?: Maybe>>; paymentPlanSoftConflicted?: Maybe; paymentPlanSoftConflictedData?: Maybe>>; - paymentVerification?: Maybe; + paymentVerifications: PaymentVerificationNodeConnection; program?: Maybe; reasonForUnsuccessfulPayment?: Maybe; serviceProvider?: Maybe; @@ -4241,6 +4241,15 @@ export type PaymentNodeFollowUpsArgs = { }; +export type PaymentNodePaymentVerificationsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + offset?: InputMaybe; +}; + + export type PaymentNodeTicketComplaintDetailsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -25264,7 +25273,7 @@ export type PaymentNodeResolvers>>, ParentType, ContextType>; paymentPlanSoftConflicted?: Resolver, ParentType, ContextType>; paymentPlanSoftConflictedData?: Resolver>>, ParentType, ContextType>; - paymentVerification?: Resolver, ParentType, ContextType>; + paymentVerifications?: Resolver>; program?: Resolver, ParentType, ContextType>; reasonForUnsuccessfulPayment?: Resolver, ParentType, ContextType>; serviceProvider?: Resolver, ParentType, ContextType>; diff --git a/src/hct_mis_api/config/env.py b/src/hct_mis_api/config/env.py index e23d2865d2..65ca70a9bc 100644 --- a/src/hct_mis_api/config/env.py +++ b/src/hct_mis_api/config/env.py @@ -11,6 +11,13 @@ "", "https://django-environ.readthedocs.io/en/latest/types.html#environ-env-db-url", ), + "REP_DATABASE_URL": ( + str, + "sqlite://", + "", + "", + "https://django-environ.readthedocs.io/en/latest/types.html#environ-env-db-url", + ), "DEBUG": (bool, False), "ENV": (str, "dev"), "DOMAIN": (str, "localhost:8000"), diff --git a/src/hct_mis_api/config/settings.py b/src/hct_mis_api/config/settings.py index 9590f71859..5f2f88890a 100644 --- a/src/hct_mis_api/config/settings.py +++ b/src/hct_mis_api/config/settings.py @@ -10,7 +10,6 @@ from django.utils.translation import gettext_lazy as _ from single_source import get_version -from smart_env.exceptions import SmartEnvMissing from hct_mis_api.config.env import env @@ -110,15 +109,8 @@ else: EMAIL_SUBJECT_PREFIX = "" -try: - REPLICA_DB = env("REP_DATABASE_URL", default=None) -except SmartEnvMissing: - REPLICA_DB = None -if REPLICA_DB: - RO_CONN = dict(**env.db("REP_DATABASE_URL")).copy() -else: - RO_CONN = dict(**env.db("DATABASE_URL")).copy() +RO_CONN = env.db("REP_DATABASE_URL") RO_CONN.update( { "OPTIONS": {"options": "-c default_transaction_read_only=on"},
- 0 - - 45 + 0