From b52f149ca1a9866803239111dc3b1e4e27d80f13 Mon Sep 17 00:00:00 2001 From: Jan Romaniak Date: Tue, 16 Jul 2024 12:07:24 +0200 Subject: [PATCH 001/202] AB#207787 Information Exposure --- backend/hct_mis_api/apps/household/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/hct_mis_api/apps/household/views.py b/backend/hct_mis_api/apps/household/views.py index 76cb194aa7..95c30a447a 100644 --- a/backend/hct_mis_api/apps/household/views.py +++ b/backend/hct_mis_api/apps/household/views.py @@ -112,7 +112,7 @@ def get(self, request: Request) -> Response: try: data = get_household_or_individual(tax_id, registration_id, business_area_code) - except Exception as exception: - return Response({"status": "not found", "error_message": str(exception)}, status=404) + except Exception: # pragma: no cover + return Response({"status": "not found", "error_message": "Household not Found"}, status=404) return Response(data, status=200) From 644f8bc8f7f3849cebcb3cc0e09710914128fa78 Mon Sep 17 00:00:00 2001 From: Jan Romaniak Date: Tue, 27 Aug 2024 09:25:28 +0200 Subject: [PATCH 002/202] added logger exception --- backend/hct_mis_api/apps/household/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/hct_mis_api/apps/household/views.py b/backend/hct_mis_api/apps/household/views.py index 95c30a447a..b82fe2cca4 100644 --- a/backend/hct_mis_api/apps/household/views.py +++ b/backend/hct_mis_api/apps/household/views.py @@ -1,3 +1,4 @@ +import logging from typing import Dict, Optional from rest_framework.permissions import IsAuthenticated @@ -23,6 +24,7 @@ ) from hct_mis_api.apps.utils.profiling import profiling +logger = logging.getLogger(__name__) def get_individual(tax_id: str, business_area_code: Optional[str]) -> Document: documents = ( @@ -112,7 +114,8 @@ def get(self, request: Request) -> Response: try: data = get_household_or_individual(tax_id, registration_id, business_area_code) - except Exception: # pragma: no cover + except Exception as e: # pragma: no cover + logger.exception(e) return Response({"status": "not found", "error_message": "Household not Found"}, status=404) return Response(data, status=200) From 093c00b7d119e9f763890b4e2166487824502b02 Mon Sep 17 00:00:00 2001 From: Jan Romaniak Date: Tue, 27 Aug 2024 11:56:41 +0200 Subject: [PATCH 003/202] siwtch to pending objects --- backend/hct_mis_api/apps/household/views.py | 35 ++++++++++--------- .../apps/registration_datahub/admin.py | 4 +-- .../apps/registration_datahub/fixtures.py | 10 +++--- .../apps/registration_datahub/models.py | 8 ++--- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/backend/hct_mis_api/apps/household/views.py b/backend/hct_mis_api/apps/household/views.py index b82fe2cca4..37bbac602f 100644 --- a/backend/hct_mis_api/apps/household/views.py +++ b/backend/hct_mis_api/apps/household/views.py @@ -1,5 +1,5 @@ import logging -from typing import Dict, Optional +from typing import Dict, Optional, Union from rest_framework.permissions import IsAuthenticated from rest_framework.request import Request @@ -13,20 +13,21 @@ IDENTIFICATION_TYPE_TAX_ID, Document, Household, + Individual, + PendingDocument, + PendingIndividual, ) from hct_mis_api.apps.household.serializers import ( serialize_by_household, serialize_by_individual, ) -from hct_mis_api.apps.registration_datahub.models import ( - ImportedDocument, - ImportedHousehold, -) +from hct_mis_api.apps.registration_datahub.models import PendingHousehold from hct_mis_api.apps.utils.profiling import profiling logger = logging.getLogger(__name__) -def get_individual(tax_id: str, business_area_code: Optional[str]) -> Document: + +def get_individual(tax_id: str, business_area_code: Optional[str]) -> Union[Individual, PendingIndividual]: documents = ( Document.objects.all() if not business_area_code @@ -37,21 +38,21 @@ def get_individual(tax_id: str, business_area_code: Optional[str]) -> Document: if documents.count() == 1: return documents.first().individual - imported_documents = ( - ImportedDocument.objects.all() + pending_documents = ( + PendingDocument.objects.all() if not business_area_code - else ImportedDocument.objects.filter( + else PendingDocument.objects.filter( individual__household__registration_data_import__business_area__code=business_area_code ) ).filter(type__key=IDENTIFICATION_TYPE_TO_KEY_MAPPING[IDENTIFICATION_TYPE_TAX_ID], document_number=tax_id) - if imported_documents.count() > 1: - raise Exception(f"Multiple imported documents ({imported_documents.count()}) with given tax_id found") - if imported_documents.count() == 1: - return imported_documents.first().individual + if pending_documents.count() > 1: + raise Exception(f"Multiple imported documents ({pending_documents.count()}) with given tax_id found") + if pending_documents.count() == 1: + return pending_documents.first().individual raise Exception("Document with given tax_id not found") -def get_household(registration_id: str, business_area_code: Optional[str]) -> ImportedHousehold: +def get_household(registration_id: str, business_area_code: Optional[str]) -> Union[PendingHousehold, Household]: kobo_asset_value = _prepare_kobo_asset_id_value(registration_id) households = ( Household.objects.all() @@ -64,17 +65,17 @@ def get_household(registration_id: str, business_area_code: Optional[str]) -> Im return households.first() # type: ignore if business_area_code is None: - imported_households_by_business_area = ImportedHousehold.objects.all() + pending_households_by_business_area = PendingHousehold.objects.all() else: business_areas = BusinessArea.objects.filter(code=business_area_code) if not business_areas: raise Exception(f"Business area with code {business_area_code} not found") business_area = business_areas.first() # code is unique, so no need to worry here - imported_households_by_business_area = ImportedHousehold.objects.filter( + pending_households_by_business_area = PendingHousehold.objects.filter( registration_data_import__business_area_slug=business_area.slug ) - imported_households = imported_households_by_business_area.filter(detail_id__endswith=kobo_asset_value) + imported_households = pending_households_by_business_area.filter(detail_id__endswith=kobo_asset_value) if imported_households.count() > 1: raise Exception( f"Multiple imported households ({imported_households.count()}) with given registration_id found" diff --git a/backend/hct_mis_api/apps/registration_datahub/admin.py b/backend/hct_mis_api/apps/registration_datahub/admin.py index 0359f4f627..815c061551 100644 --- a/backend/hct_mis_api/apps/registration_datahub/admin.py +++ b/backend/hct_mis_api/apps/registration_datahub/admin.py @@ -24,8 +24,8 @@ RegistrationDataImportDatahub, ) from hct_mis_api.apps.registration_datahub.models import ( - ImportedHousehold, ImportedIndividual, + PendingHousehold, ) from hct_mis_api.apps.utils.admin import HOPEModelAdminBase @@ -67,7 +67,7 @@ def inspect(self, request: HttpRequest, pk: UUID) -> TemplateResponse: context["title"] = f"Import {obj.name} - {obj.import_done}" context["data"] = {} has_content = False - for model in [ImportedIndividual, ImportedHousehold]: + for model in [ImportedIndividual, PendingHousehold]: count = model.objects.filter(registration_data_import=obj).count() has_content = has_content or count context["data"][model] = {"count": count, "warnings": [], "errors": [], "meta": model._meta} diff --git a/backend/hct_mis_api/apps/registration_datahub/fixtures.py b/backend/hct_mis_api/apps/registration_datahub/fixtures.py index 581ec25d25..2eac8771cb 100644 --- a/backend/hct_mis_api/apps/registration_datahub/fixtures.py +++ b/backend/hct_mis_api/apps/registration_datahub/fixtures.py @@ -22,9 +22,9 @@ ImportedBankAccountInfo, ImportedDocument, ImportedDocumentType, - ImportedHousehold, ImportedIndividual, ImportedIndividualIdentity, + PendingHousehold, Record, RegistrationDataImportDatahub, ) @@ -48,7 +48,7 @@ class Meta: class ImportedHouseholdFactory(DjangoModelFactory): class Meta: - model = ImportedHousehold + model = PendingHousehold consent_sign = factory.django.ImageField(color="blue") consent = True @@ -138,7 +138,7 @@ class Meta: def create_imported_household( household_args: Optional[Dict] = None, individual_args: Optional[Dict] = None -) -> Tuple[ImportedHousehold, ImportedIndividual]: +) -> Tuple[PendingHousehold, ImportedIndividual]: if household_args is None: household_args = {} if individual_args is None: @@ -154,12 +154,12 @@ def create_imported_household( def create_imported_household_and_individuals( household_data: Optional[Dict] = None, individuals_data: Optional[List[Dict]] = None -) -> Tuple[ImportedHousehold, List[ImportedIndividual]]: +) -> Tuple[PendingHousehold, List[ImportedIndividual]]: if household_data is None: household_data = {} if individuals_data is None: individuals_data = [] - household: ImportedHousehold = ImportedHouseholdFactory.build(**household_data, size=len(individuals_data)) + household: PendingHousehold = ImportedHouseholdFactory.build(**household_data, size=len(individuals_data)) individuals: List[ImportedIndividual] = [ ImportedIndividualFactory(household=household, **individual_data) for individual_data in individuals_data ] diff --git a/backend/hct_mis_api/apps/registration_datahub/models.py b/backend/hct_mis_api/apps/registration_datahub/models.py index d888dbfb44..097752b192 100644 --- a/backend/hct_mis_api/apps/registration_datahub/models.py +++ b/backend/hct_mis_api/apps/registration_datahub/models.py @@ -83,7 +83,7 @@ ) -class ImportedHousehold(TimeStampedUUIDModel): +class PendingHousehold(TimeStampedUUIDModel): class CollectType(models.TextChoices): STANDARD = "STANDARD", "Standard" SINGLE = "SINGLE", "Single" @@ -239,7 +239,7 @@ class ImportedIndividual(TimeStampedUUIDModel): email = models.CharField(max_length=255, blank=True) payment_delivery_phone_no = PhoneNumberField(blank=True, default=BLANK) household = models.ForeignKey( - "ImportedHousehold", + "hct_mis_api.apps.registration_datahub.models.PendingHousehold", null=True, related_name="individuals", on_delete=models.CASCADE, @@ -353,7 +353,7 @@ class ImportedIndividualRoleInHousehold(TimeStampedUUIDModel): related_name="households_and_roles", ) household = models.ForeignKey( - "ImportedHousehold", + "hct_mis_api.apps.registration_datahub.models.PendingHousehold", on_delete=models.CASCADE, related_name="individuals_and_roles", ) @@ -517,7 +517,7 @@ class KoboImportedSubmission(models.Model): kobo_submission_time = models.DateTimeField() # ImportedHousehold.kobo_submission_time # we use on_delete=models.SET_NULL because we want to be able to delete # ImportedHousehold without loosing track of importing - imported_household = models.ForeignKey(ImportedHousehold, blank=True, null=True, on_delete=models.SET_NULL) + imported_household = models.ForeignKey(PendingHousehold, blank=True, null=True, on_delete=models.SET_NULL) amended = models.BooleanField(default=False, blank=True) registration_data_import = models.ForeignKey( From 380f90f248a67584535e418ee62b3023b9af41f0 Mon Sep 17 00:00:00 2001 From: Jan Romaniak Date: Wed, 28 Aug 2024 09:43:34 +0200 Subject: [PATCH 004/202] Revert "siwtch to pending objects" This reverts commit 093c00b7d119e9f763890b4e2166487824502b02. --- backend/hct_mis_api/apps/household/views.py | 35 +++++++++---------- .../apps/registration_datahub/admin.py | 4 +-- .../apps/registration_datahub/fixtures.py | 10 +++--- .../apps/registration_datahub/models.py | 8 ++--- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/backend/hct_mis_api/apps/household/views.py b/backend/hct_mis_api/apps/household/views.py index 37bbac602f..b82fe2cca4 100644 --- a/backend/hct_mis_api/apps/household/views.py +++ b/backend/hct_mis_api/apps/household/views.py @@ -1,5 +1,5 @@ import logging -from typing import Dict, Optional, Union +from typing import Dict, Optional from rest_framework.permissions import IsAuthenticated from rest_framework.request import Request @@ -13,21 +13,20 @@ IDENTIFICATION_TYPE_TAX_ID, Document, Household, - Individual, - PendingDocument, - PendingIndividual, ) from hct_mis_api.apps.household.serializers import ( serialize_by_household, serialize_by_individual, ) -from hct_mis_api.apps.registration_datahub.models import PendingHousehold +from hct_mis_api.apps.registration_datahub.models import ( + ImportedDocument, + ImportedHousehold, +) from hct_mis_api.apps.utils.profiling import profiling logger = logging.getLogger(__name__) - -def get_individual(tax_id: str, business_area_code: Optional[str]) -> Union[Individual, PendingIndividual]: +def get_individual(tax_id: str, business_area_code: Optional[str]) -> Document: documents = ( Document.objects.all() if not business_area_code @@ -38,21 +37,21 @@ def get_individual(tax_id: str, business_area_code: Optional[str]) -> Union[Indi if documents.count() == 1: return documents.first().individual - pending_documents = ( - PendingDocument.objects.all() + imported_documents = ( + ImportedDocument.objects.all() if not business_area_code - else PendingDocument.objects.filter( + else ImportedDocument.objects.filter( individual__household__registration_data_import__business_area__code=business_area_code ) ).filter(type__key=IDENTIFICATION_TYPE_TO_KEY_MAPPING[IDENTIFICATION_TYPE_TAX_ID], document_number=tax_id) - if pending_documents.count() > 1: - raise Exception(f"Multiple imported documents ({pending_documents.count()}) with given tax_id found") - if pending_documents.count() == 1: - return pending_documents.first().individual + if imported_documents.count() > 1: + raise Exception(f"Multiple imported documents ({imported_documents.count()}) with given tax_id found") + if imported_documents.count() == 1: + return imported_documents.first().individual raise Exception("Document with given tax_id not found") -def get_household(registration_id: str, business_area_code: Optional[str]) -> Union[PendingHousehold, Household]: +def get_household(registration_id: str, business_area_code: Optional[str]) -> ImportedHousehold: kobo_asset_value = _prepare_kobo_asset_id_value(registration_id) households = ( Household.objects.all() @@ -65,17 +64,17 @@ def get_household(registration_id: str, business_area_code: Optional[str]) -> Un return households.first() # type: ignore if business_area_code is None: - pending_households_by_business_area = PendingHousehold.objects.all() + imported_households_by_business_area = ImportedHousehold.objects.all() else: business_areas = BusinessArea.objects.filter(code=business_area_code) if not business_areas: raise Exception(f"Business area with code {business_area_code} not found") business_area = business_areas.first() # code is unique, so no need to worry here - pending_households_by_business_area = PendingHousehold.objects.filter( + imported_households_by_business_area = ImportedHousehold.objects.filter( registration_data_import__business_area_slug=business_area.slug ) - imported_households = pending_households_by_business_area.filter(detail_id__endswith=kobo_asset_value) + imported_households = imported_households_by_business_area.filter(detail_id__endswith=kobo_asset_value) if imported_households.count() > 1: raise Exception( f"Multiple imported households ({imported_households.count()}) with given registration_id found" diff --git a/backend/hct_mis_api/apps/registration_datahub/admin.py b/backend/hct_mis_api/apps/registration_datahub/admin.py index 815c061551..0359f4f627 100644 --- a/backend/hct_mis_api/apps/registration_datahub/admin.py +++ b/backend/hct_mis_api/apps/registration_datahub/admin.py @@ -24,8 +24,8 @@ RegistrationDataImportDatahub, ) from hct_mis_api.apps.registration_datahub.models import ( + ImportedHousehold, ImportedIndividual, - PendingHousehold, ) from hct_mis_api.apps.utils.admin import HOPEModelAdminBase @@ -67,7 +67,7 @@ def inspect(self, request: HttpRequest, pk: UUID) -> TemplateResponse: context["title"] = f"Import {obj.name} - {obj.import_done}" context["data"] = {} has_content = False - for model in [ImportedIndividual, PendingHousehold]: + for model in [ImportedIndividual, ImportedHousehold]: count = model.objects.filter(registration_data_import=obj).count() has_content = has_content or count context["data"][model] = {"count": count, "warnings": [], "errors": [], "meta": model._meta} diff --git a/backend/hct_mis_api/apps/registration_datahub/fixtures.py b/backend/hct_mis_api/apps/registration_datahub/fixtures.py index 2eac8771cb..581ec25d25 100644 --- a/backend/hct_mis_api/apps/registration_datahub/fixtures.py +++ b/backend/hct_mis_api/apps/registration_datahub/fixtures.py @@ -22,9 +22,9 @@ ImportedBankAccountInfo, ImportedDocument, ImportedDocumentType, + ImportedHousehold, ImportedIndividual, ImportedIndividualIdentity, - PendingHousehold, Record, RegistrationDataImportDatahub, ) @@ -48,7 +48,7 @@ class Meta: class ImportedHouseholdFactory(DjangoModelFactory): class Meta: - model = PendingHousehold + model = ImportedHousehold consent_sign = factory.django.ImageField(color="blue") consent = True @@ -138,7 +138,7 @@ class Meta: def create_imported_household( household_args: Optional[Dict] = None, individual_args: Optional[Dict] = None -) -> Tuple[PendingHousehold, ImportedIndividual]: +) -> Tuple[ImportedHousehold, ImportedIndividual]: if household_args is None: household_args = {} if individual_args is None: @@ -154,12 +154,12 @@ def create_imported_household( def create_imported_household_and_individuals( household_data: Optional[Dict] = None, individuals_data: Optional[List[Dict]] = None -) -> Tuple[PendingHousehold, List[ImportedIndividual]]: +) -> Tuple[ImportedHousehold, List[ImportedIndividual]]: if household_data is None: household_data = {} if individuals_data is None: individuals_data = [] - household: PendingHousehold = ImportedHouseholdFactory.build(**household_data, size=len(individuals_data)) + household: ImportedHousehold = ImportedHouseholdFactory.build(**household_data, size=len(individuals_data)) individuals: List[ImportedIndividual] = [ ImportedIndividualFactory(household=household, **individual_data) for individual_data in individuals_data ] diff --git a/backend/hct_mis_api/apps/registration_datahub/models.py b/backend/hct_mis_api/apps/registration_datahub/models.py index 097752b192..d888dbfb44 100644 --- a/backend/hct_mis_api/apps/registration_datahub/models.py +++ b/backend/hct_mis_api/apps/registration_datahub/models.py @@ -83,7 +83,7 @@ ) -class PendingHousehold(TimeStampedUUIDModel): +class ImportedHousehold(TimeStampedUUIDModel): class CollectType(models.TextChoices): STANDARD = "STANDARD", "Standard" SINGLE = "SINGLE", "Single" @@ -239,7 +239,7 @@ class ImportedIndividual(TimeStampedUUIDModel): email = models.CharField(max_length=255, blank=True) payment_delivery_phone_no = PhoneNumberField(blank=True, default=BLANK) household = models.ForeignKey( - "hct_mis_api.apps.registration_datahub.models.PendingHousehold", + "ImportedHousehold", null=True, related_name="individuals", on_delete=models.CASCADE, @@ -353,7 +353,7 @@ class ImportedIndividualRoleInHousehold(TimeStampedUUIDModel): related_name="households_and_roles", ) household = models.ForeignKey( - "hct_mis_api.apps.registration_datahub.models.PendingHousehold", + "ImportedHousehold", on_delete=models.CASCADE, related_name="individuals_and_roles", ) @@ -517,7 +517,7 @@ class KoboImportedSubmission(models.Model): kobo_submission_time = models.DateTimeField() # ImportedHousehold.kobo_submission_time # we use on_delete=models.SET_NULL because we want to be able to delete # ImportedHousehold without loosing track of importing - imported_household = models.ForeignKey(PendingHousehold, blank=True, null=True, on_delete=models.SET_NULL) + imported_household = models.ForeignKey(ImportedHousehold, blank=True, null=True, on_delete=models.SET_NULL) amended = models.BooleanField(default=False, blank=True) registration_data_import = models.ForeignKey( From 0823fdbed4d56e2beb021ca60d0c2026210c9de8 Mon Sep 17 00:00:00 2001 From: Jan Romaniak Date: Wed, 28 Aug 2024 10:37:36 +0200 Subject: [PATCH 005/202] fixes to tests --- .../hct_mis_api/apps/household/serializers.py | 3 +- .../tests/test_household_status_endpoint.py | 79 +++++++++---------- backend/hct_mis_api/apps/household/views.py | 37 ++++----- 3 files changed, 57 insertions(+), 62 deletions(-) diff --git a/backend/hct_mis_api/apps/household/serializers.py b/backend/hct_mis_api/apps/household/serializers.py index c409a21436..3597673c43 100644 --- a/backend/hct_mis_api/apps/household/serializers.py +++ b/backend/hct_mis_api/apps/household/serializers.py @@ -4,13 +4,14 @@ from hct_mis_api.apps.household.models import Household from hct_mis_api.apps.payment.models import PaymentRecord from hct_mis_api.apps.targeting.models import HouseholdSelection, TargetPopulation +from hct_mis_api.apps.utils.models import MergeStatusModel if TYPE_CHECKING: from hct_mis_api.apps.household.models import Individual def get_household_status(household: Household) -> Tuple[str, datetime]: - if isinstance(household, Household): + if household.rdi_merge_status == MergeStatusModel.MERGED: payment_records = PaymentRecord.objects.filter(household=household) if payment_records.exists(): return "paid", payment_records.first().updated_at diff --git a/backend/hct_mis_api/apps/household/tests/test_household_status_endpoint.py b/backend/hct_mis_api/apps/household/tests/test_household_status_endpoint.py index c019c207d5..e5054be9ff 100644 --- a/backend/hct_mis_api/apps/household/tests/test_household_status_endpoint.py +++ b/backend/hct_mis_api/apps/household/tests/test_household_status_endpoint.py @@ -11,24 +11,19 @@ from hct_mis_api.apps.household.fixtures import ( DocumentFactory, DocumentTypeFactory, + PendingDocumentFactory, + PendingHouseholdFactory, + PendingIndividualFactory, create_household, ) from hct_mis_api.apps.household.models import ( HEAD, IDENTIFICATION_TYPE_TAX_ID, ROLE_NO_ROLE, + PendingIndividualRoleInHousehold, ) from hct_mis_api.apps.payment.fixtures import PaymentRecordFactory -from hct_mis_api.apps.registration_datahub.fixtures import ( - ImportedDocumentFactory, - ImportedDocumentTypeFactory, - ImportedHouseholdFactory, - ImportedIndividualFactory, - RegistrationDataImportDatahubFactory, -) -from hct_mis_api.apps.registration_datahub.models import ( - ImportedIndividualRoleInHousehold, -) +from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory from hct_mis_api.apps.targeting.fixtures import TargetPopulationFactory from hct_mis_api.apps.targeting.models import HouseholdSelection, TargetPopulation @@ -73,19 +68,19 @@ def test_filtering_business_area_code_with_tax_id(self) -> None: self.assertEqual(response_nok.status_code, 404) def test_filtering_business_area_code_with_registration_id(self) -> None: - rdi_datahub = RegistrationDataImportDatahubFactory(business_area_slug=self.business_area.slug) - imported_household = ImportedHouseholdFactory(registration_data_import=rdi_datahub) - imported_individual = ImportedIndividualFactory(household=imported_household, relationship=HEAD) - imported_household.head_of_household = imported_individual - imported_household.detail_id = "HOPE-2022530111222" - imported_household.save() - ImportedIndividualRoleInHousehold.objects.create( - individual=imported_individual, + rdi = RegistrationDataImportFactory(business_area=self.business_area) + pending_household = PendingHouseholdFactory(registration_data_import=rdi) + pending_individual = PendingIndividualFactory(household=pending_household, relationship=HEAD) + pending_household.head_of_household = pending_individual + pending_household.detail_id = "HOPE-2022530111222" + pending_household.save() + PendingIndividualRoleInHousehold.objects.create( + individual=pending_individual, role=ROLE_NO_ROLE, - household=imported_household, + household=pending_household, ) - registration_id = imported_household.detail_id + registration_id = pending_household.detail_id response_ok = self.api_client.get( f"/api/hh-status?registration_id={registration_id}&business_area_code={self.business_area.code}" @@ -103,28 +98,26 @@ def test_getting_non_existent_individual(self) -> None: self.assertEqual(response.json()["status"], "not found") def test_getting_individual_with_status_imported(self) -> None: - imported_household = ImportedHouseholdFactory() - imported_individual = ImportedIndividualFactory(household=imported_household, relationship=HEAD) - imported_household.head_of_household = imported_individual - imported_household.save() - ImportedIndividualRoleInHousehold.objects.create( - individual=imported_individual, + pending_household = PendingHouseholdFactory() + pending_individual = PendingIndividualFactory(household=pending_household, relationship=HEAD) + pending_household.head_of_household = pending_individual + pending_household.save() + PendingIndividualRoleInHousehold.objects.create( + individual=pending_individual, role=ROLE_NO_ROLE, - household=imported_household, + household=pending_household, ) - imported_document_type = ImportedDocumentTypeFactory( - key=IDENTIFICATION_TYPE_TO_KEY_MAPPING[IDENTIFICATION_TYPE_TAX_ID] - ) - imported_document = ImportedDocumentFactory(individual=imported_individual, type=imported_document_type) - tax_id = imported_document.document_number + document_type = DocumentTypeFactory(key=IDENTIFICATION_TYPE_TO_KEY_MAPPING[IDENTIFICATION_TYPE_TAX_ID]) + pending_document = PendingDocumentFactory(individual=pending_individual, type=document_type) + tax_id = pending_document.document_number response = self.api_client.get(f"/api/hh-status?tax_id={tax_id}") self.assertEqual(response.status_code, 200) data = response.json() info = data["info"] self.assertEqual(info["status"], "imported") - self.assertEqual(info["date"], _time(imported_household.updated_at)) + self.assertEqual(info["date"], _time(pending_household.updated_at)) individual = info["individual"] self.assertIsNotNone(individual) @@ -209,23 +202,23 @@ def test_getting_non_existent_household(self) -> None: self.assertEqual(response.json()["status"], "not found") def test_getting_household_with_status_imported(self) -> None: - imported_household = ImportedHouseholdFactory() - imported_individual = ImportedIndividualFactory(household=imported_household, relationship=HEAD) - imported_household.head_of_household = imported_individual - imported_household.detail_id = "HOPE-2022530111222" - imported_household.save() - ImportedIndividualRoleInHousehold.objects.create( - individual=imported_individual, + pending_household = PendingHouseholdFactory() + pending_individual = PendingIndividualFactory(household=pending_household, relationship=HEAD) + pending_household.head_of_household = pending_individual + pending_household.detail_id = "HOPE-2022530111222" + pending_household.save() + PendingIndividualRoleInHousehold.objects.create( + individual=pending_individual, role=ROLE_NO_ROLE, - household=imported_household, + household=pending_household, ) - registration_id = imported_household.detail_id + registration_id = pending_household.detail_id response = self.api_client.get(f"/api/hh-status?registration_id={registration_id}") self.assertEqual(response.status_code, 200) data = response.json() info = data["info"] self.assertEqual(info["status"], "imported") - self.assertEqual(info["date"], _time(imported_household.updated_at)) + self.assertEqual(info["date"], _time(pending_household.updated_at)) self.assertTrue("individual" not in info) diff --git a/backend/hct_mis_api/apps/household/views.py b/backend/hct_mis_api/apps/household/views.py index b82fe2cca4..b52e91eaf7 100644 --- a/backend/hct_mis_api/apps/household/views.py +++ b/backend/hct_mis_api/apps/household/views.py @@ -1,5 +1,5 @@ import logging -from typing import Dict, Optional +from typing import Dict, Optional, Union from rest_framework.permissions import IsAuthenticated from rest_framework.request import Request @@ -13,20 +13,21 @@ IDENTIFICATION_TYPE_TAX_ID, Document, Household, + Individual, + PendingDocument, + PendingHousehold, + PendingIndividual, ) from hct_mis_api.apps.household.serializers import ( serialize_by_household, serialize_by_individual, ) -from hct_mis_api.apps.registration_datahub.models import ( - ImportedDocument, - ImportedHousehold, -) from hct_mis_api.apps.utils.profiling import profiling logger = logging.getLogger(__name__) -def get_individual(tax_id: str, business_area_code: Optional[str]) -> Document: + +def get_individual(tax_id: str, business_area_code: Optional[str]) -> Union[Individual, PendingIndividual]: documents = ( Document.objects.all() if not business_area_code @@ -37,21 +38,21 @@ def get_individual(tax_id: str, business_area_code: Optional[str]) -> Document: if documents.count() == 1: return documents.first().individual - imported_documents = ( - ImportedDocument.objects.all() + pending_documents = ( + PendingDocument.objects.all() if not business_area_code - else ImportedDocument.objects.filter( + else PendingDocument.objects.filter( individual__household__registration_data_import__business_area__code=business_area_code ) ).filter(type__key=IDENTIFICATION_TYPE_TO_KEY_MAPPING[IDENTIFICATION_TYPE_TAX_ID], document_number=tax_id) - if imported_documents.count() > 1: - raise Exception(f"Multiple imported documents ({imported_documents.count()}) with given tax_id found") - if imported_documents.count() == 1: - return imported_documents.first().individual + if pending_documents.count() > 1: + raise Exception(f"Multiple imported documents ({pending_documents.count()}) with given tax_id found") + if pending_documents.count() == 1: + return pending_documents.first().individual raise Exception("Document with given tax_id not found") -def get_household(registration_id: str, business_area_code: Optional[str]) -> ImportedHousehold: +def get_household(registration_id: str, business_area_code: Optional[str]) -> Union[PendingHousehold, Household]: kobo_asset_value = _prepare_kobo_asset_id_value(registration_id) households = ( Household.objects.all() @@ -64,17 +65,17 @@ def get_household(registration_id: str, business_area_code: Optional[str]) -> Im return households.first() # type: ignore if business_area_code is None: - imported_households_by_business_area = ImportedHousehold.objects.all() + pending_households_by_business_area = PendingHousehold.objects.all() else: business_areas = BusinessArea.objects.filter(code=business_area_code) if not business_areas: raise Exception(f"Business area with code {business_area_code} not found") business_area = business_areas.first() # code is unique, so no need to worry here - imported_households_by_business_area = ImportedHousehold.objects.filter( - registration_data_import__business_area_slug=business_area.slug + pending_households_by_business_area = PendingHousehold.objects.filter( + registration_data_import__business_area__slug=business_area.slug ) - imported_households = imported_households_by_business_area.filter(detail_id__endswith=kobo_asset_value) + imported_households = pending_households_by_business_area.filter(detail_id__endswith=kobo_asset_value) if imported_households.count() > 1: raise Exception( f"Multiple imported households ({imported_households.count()}) with given registration_id found" From 3c03995a796b6c76f0197a5c712d7589d7f607c2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2024 23:41:31 +0000 Subject: [PATCH 006/202] Bump dset from 3.1.3 to 3.1.4 in /frontend Bumps [dset](https://github.com/lukeed/dset) from 3.1.3 to 3.1.4. - [Release notes](https://github.com/lukeed/dset/releases) - [Commits](https://github.com/lukeed/dset/compare/v3.1.3...v3.1.4) --- updated-dependencies: - dependency-name: dset dependency-type: indirect ... Signed-off-by: dependabot[bot] --- frontend/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 118293b739..1a1c91b48a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -4952,9 +4952,9 @@ dotenv@^16.0.0: integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== dset@^3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.3.tgz#c194147f159841148e8e34ca41f638556d9542d2" - integrity sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ== + version "3.1.4" + resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" + integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== ecc-jsbn@~0.1.1: version "0.1.2" From 4efa5f27d3ce0d95cac3a14534428cbf915a9917 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 22:14:04 +0000 Subject: [PATCH 007/202] Bump vite from 5.2.10 to 5.2.14 in /frontend Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.2.10 to 5.2.14. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.2.14/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.2.14/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- frontend/package.json | 2 +- frontend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index e76f5b9665..77ef7dba48 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -77,7 +77,7 @@ "styled-components": "^6.1.8", "ts-node": "^10.9.2", "use-deep-compare-effect": "^1.8.1", - "vite": "^5.0.8", + "vite": "^5.2.14", "vite-tsconfig-paths": "^4.3.1", "waait": "^1.0.5", "yup": "^1.3.3" diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 118293b739..e43fe81754 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -10460,10 +10460,10 @@ vite-tsconfig-paths@^4.3.1: globrex "^0.1.2" tsconfck "^3.0.3" -vite@^5.0.8: - version "5.2.10" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.10.tgz#2ac927c91e99d51b376a5c73c0e4b059705f5bd7" - integrity sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw== +vite@^5.2.14: + version "5.2.14" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.2.14.tgz#fd5f60facf6b5f90ec7da6323c467a365d380c3d" + integrity sha512-TFQLuwWLPms+NBNlh0D9LZQ+HXW471COABxw/9TEUBrjuHMo9BrYBPrN/SYAwIuVL+rLerycxiLT41t4f5MZpA== dependencies: esbuild "^0.20.1" postcss "^8.4.38" From 076c2b331b3f8c51dbff514994d1b6b1953eadbc Mon Sep 17 00:00:00 2001 From: marekbiczysko Date: Mon, 23 Sep 2024 18:06:53 +0200 Subject: [PATCH 008/202] 2201639_Individuals_Add_a_new_section_to_Display_Delivery_mechanism_details_for_an_individual --- src/frontend/data/schema.graphql | 5552 ----------------- src/frontend/package.json | 2 +- src/frontend/src/__generated__/graphql.tsx | 142 +- .../src/__generated__/introspection-result.ts | 1 + .../apollo/fragments/IndividualFragments.ts | 5 + src/hct_mis_api/apps/account/permissions.py | 1 + src/hct_mis_api/apps/household/schema.py | 33 + src/hct_mis_api/apps/payment/admin.py | 1 + .../apps/payment/migrations/0144_migration.py | 18 + src/hct_mis_api/apps/payment/models.py | 16 +- .../snapshots/snap_test_individual_query.py | 38 + .../apps/household/test_individual_query.py | 334 +- 12 files changed, 527 insertions(+), 5616 deletions(-) create mode 100644 src/hct_mis_api/apps/payment/migrations/0144_migration.py diff --git a/src/frontend/data/schema.graphql b/src/frontend/data/schema.graphql index e1a3a1379d..e69de29bb2 100644 --- a/src/frontend/data/schema.graphql +++ b/src/frontend/data/schema.graphql @@ -1,5552 +0,0 @@ -schema { - query: Query - mutation: Mutations -} - -input AccountabilityCommunicationMessageAgeInput { - min: Int - max: Int -} - -input AccountabilityFullListArguments { - excludedAdminAreas: [String] -} - -input AccountabilityRandomSamplingArguments { - excludedAdminAreas: [String] - confidenceInterval: Float! - marginOfError: Float! - age: AccountabilityCommunicationMessageAgeInput - sex: String -} - -input AccountabilitySampleSizeInput { - targetPopulation: ID - program: ID - samplingType: String! - fullListArguments: AccountabilityFullListArguments - randomSamplingArguments: AccountabilityRandomSamplingArguments -} - -type AccountabilitySampleSizeNode { - numberOfRecipients: Int - sampleSize: Int -} - -enum Action { - LOCK - LOCK_FSP - UNLOCK - UNLOCK_FSP - SEND_FOR_APPROVAL - APPROVE - AUTHORIZE - REVIEW - REJECT - FINISH - SEND_TO_PAYMENT_GATEWAY -} - -input ActionPaymentPlanInput { - paymentPlanId: ID! - action: Action! - comment: String -} - -type ActionPaymentPlanMutation { - paymentPlan: PaymentPlanNode -} - -type ActivatePaymentVerificationPlan { - validationErrors: Arg - paymentPlan: GenericPaymentPlanNode -} - -input AddIndividualDataObjectType { - fullName: String! - givenName: String - middleName: String - familyName: String - sex: String! - birthDate: Date! - estimatedBirthDate: Boolean! - maritalStatus: String - phoneNo: String - phoneNoAlternative: String - email: String - relationship: String! - disability: String - workStatus: String - enrolledInNutritionProgramme: Boolean - administrationOfRutf: Boolean - pregnant: Boolean - observedDisability: [String] - seeingDisability: String - hearingDisability: String - physicalDisability: String - memoryDisability: String - selfcareDisability: String - commsDisability: String - whoAnswersPhone: String - whoAnswersAltPhone: String - role: String! - documents: [IndividualDocumentObjectType] - identities: [IndividualIdentityObjectType] - paymentChannels: [BankTransferObjectType] - businessArea: String - preferredLanguage: String - flexFields: Arg - paymentDeliveryPhoneNo: String - blockchainName: String - walletAddress: String - walletName: String -} - -input AddIndividualIssueTypeExtras { - household: ID! - individualData: AddIndividualDataObjectType! -} - -type AgeFilterObject { - min: Int - max: Int -} - -input AgeInput { - min: Int - max: Int -} - -type ApprovalNode { - createdAt: DateTime! - comment: String - createdBy: UserNode - info: String -} - -type ApprovalProcessNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - sentForApprovalBy: UserNode - sentForApprovalDate: DateTime - sentForAuthorizationBy: UserNode - sentForAuthorizationDate: DateTime - sentForFinanceReleaseBy: UserNode - sentForFinanceReleaseDate: DateTime - paymentPlan: PaymentPlanNode! - approvalNumberRequired: Int! - authorizationNumberRequired: Int! - financeReleaseNumberRequired: Int! - rejectedOn: String - actions: FilteredActionsListNode -} - -type ApprovalProcessNodeConnection { - pageInfo: PageInfo! - edges: [ApprovalProcessNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type ApprovalProcessNodeEdge { - node: ApprovalProcessNode - cursor: String! -} - -type AreaNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - originalId: UUID - name: String! - parent: AreaNode - pCode: String - areaType: AreaTypeNode! - validFrom: DateTime - validUntil: DateTime - extras: JSONString! - lft: Int! - rght: Int! - treeId: Int! - level: Int! - areaSet(offset: Int, before: String, after: String, first: Int, last: Int, name: String): AreaNodeConnection! - householdSet(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! - grievanceticketSet(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! - programs(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! - reports(offset: Int, before: String, after: String, first: Int, last: Int): ReportNodeConnection! - feedbackSet(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackNodeConnection! -} - -type AreaNodeConnection { - pageInfo: PageInfo! - edges: [AreaNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type AreaNodeEdge { - node: AreaNode - cursor: String! -} - -type AreaTreeNode { - id: ID - name: String - pCode: String - areas: [AreaTreeNode] - level: Int -} - -type AreaTypeNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - originalId: UUID - name: String! - areaLevel: Int! - parent: AreaTypeNode - validFrom: DateTime - validUntil: DateTime - extras: JSONString! - lft: Int! - rght: Int! - treeId: Int! - level: Int! - areatypeSet(offset: Int, before: String, after: String, first: Int, last: Int): AreaTypeNodeConnection! - areaSet(offset: Int, before: String, after: String, first: Int, last: Int, name: String): AreaNodeConnection! -} - -type AreaTypeNodeConnection { - pageInfo: PageInfo! - edges: [AreaTypeNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type AreaTypeNodeEdge { - node: AreaTypeNode - cursor: String! -} - -scalar Arg - -input AssignFspToDeliveryMechanismInput { - paymentPlanId: ID! - mappings: [FSPToDeliveryMechanismMappingInput]! -} - -type AssignFspToDeliveryMechanismMutation { - paymentPlan: PaymentPlanNode -} - -input AvailableFspsForDeliveryMechanismsInput { - paymentPlanId: ID! -} - -type BankAccountInfoNode implements Node { - id: ID! - rdiMergeStatus: BankAccountInfoRdiMergeStatus! - isOriginal: Boolean! - createdAt: DateTime! - updatedAt: DateTime! - isRemoved: Boolean! - removedDate: DateTime - lastSyncAt: DateTime - individual: IndividualNode! - bankName: String! - bankAccountNumber: String! - bankBranchName: String! - accountHolderName: String! - isMigrationHandled: Boolean! - copiedFrom: BankAccountInfoNode - copiedTo(offset: Int, before: String, after: String, first: Int, last: Int): BankAccountInfoNodeConnection! - type: String -} - -type BankAccountInfoNodeConnection { - pageInfo: PageInfo! - edges: [BankAccountInfoNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type BankAccountInfoNodeEdge { - node: BankAccountInfoNode - cursor: String! -} - -enum BankAccountInfoRdiMergeStatus { - PENDING - MERGED -} - -input BankTransferObjectType { - type: String! - bankName: String! - bankAccountNumber: String! - bankBranchName: String - accountHolderName: String! -} - -scalar BigInt - -type BulkGrievanceAddNoteMutation { - grievanceTickets: [GrievanceTicketNode] -} - -type BulkUpdateGrievanceTicketsAssigneesMutation { - grievanceTickets: [GrievanceTicketNode] -} - -type BulkUpdateGrievanceTicketsPriorityMutation { - grievanceTickets: [GrievanceTicketNode] -} - -type BulkUpdateGrievanceTicketsUrgencyMutation { - grievanceTickets: [GrievanceTicketNode] -} - -type BusinessAreaNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - code: String! - name: String! - longName: String! - regionCode: String! - regionName: String! - koboUsername: String - koboToken: String - koboUrl: String - rapidProHost: String - rapidProPaymentVerificationToken: String - rapidProMessagesToken: String - rapidProSurveyToken: String - slug: String! - customFields: JSONString! - hasDataSharingAgreement: Boolean! - parent: UserBusinessAreaNode - isSplit: Boolean! - postponeDeduplication: Boolean! - deduplicationDuplicateScore: Float! - deduplicationPossibleDuplicateScore: Float! - deduplicationBatchDuplicatesPercentage: Int! - deduplicationBatchDuplicatesAllowed: Int! - deduplicationGoldenRecordDuplicatesPercentage: Int! - deduplicationGoldenRecordDuplicatesAllowed: Int! - screenBeneficiary: Boolean! - deduplicationIgnoreWithdraw: Boolean! - biometricDeduplicationThreshold: Float! - isPaymentPlanApplicable: Boolean! - isAccountabilityApplicable: Boolean - active: Boolean! - enableEmailNotification: Boolean! - partners: [PartnerNode!]! - businessAreaPartnerThrough: [PartnerRoleNode!]! - children(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID): UserBusinessAreaNodeConnection! - dataCollectingTypes(offset: Int, before: String, after: String, first: Int, last: Int): DataCollectingTypeNodeConnection! - partnerSet: [PartnerNode!]! - userRoles: [UserRoleNode!]! - householdSet(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! - individualSet(offset: Int, before: String, after: String, first: Int, last: Int): IndividualNodeConnection! - registrationdataimportSet(offset: Int, before: String, after: String, first: Int, last: Int): RegistrationDataImportNodeConnection! - ruleSet(offset: Int, before: String, after: String, first: Int, last: Int): SteficonRuleNodeConnection! - paymentplanSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanNodeConnection! - financialserviceproviderSet(offset: Int, before: String, after: String, first: Int, last: Int): FinancialServiceProviderNodeConnection! - cashplanSet(offset: Int, before: String, after: String, first: Int, last: Int): CashPlanNodeConnection! - paymentrecordSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection! - paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! - serviceproviderSet(offset: Int, before: String, after: String, first: Int, last: Int): ServiceProviderNodeConnection! - tickets(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! - targetpopulationSet(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection! - programSet(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! - reports(offset: Int, before: String, after: String, first: Int, last: Int): ReportNodeConnection! - logentrySet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationLogEntryNodeConnection! - messageSet(offset: Int, before: String, after: String, first: Int, last: Int): CommunicationMessageNodeConnection! - feedbackSet(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackNodeConnection! - surveySet(offset: Int, before: String, after: String, first: Int, last: Int): SurveyNodeConnection! -} - -type BusinessAreaNodeConnection { - pageInfo: PageInfo! - edges: [BusinessAreaNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type BusinessAreaNodeEdge { - node: BusinessAreaNode - cursor: String! -} - -type CashPlanAndPaymentPlanEdges { - cursor: String - node: CashPlanAndPaymentPlanNode -} - -type CashPlanAndPaymentPlanNode { - adminUrl: String - objType: String - id: String - unicefId: String - verificationStatus: String - status: String - currency: String - totalDeliveredQuantity: Float - startDate: String - endDate: String - programName: String - updatedAt: String - verificationPlans: [PaymentVerificationPlanNode] - totalNumberOfHouseholds: Int - totalEntitledQuantity: Float - totalUndeliveredQuantity: Float - assistanceMeasurement: String - dispersionDate: String - serviceProviderFullName: String -} - -type CashPlanNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - version: BigInt! - businessArea: UserBusinessAreaNode! - statusDate: DateTime! - startDate: DateTime - endDate: DateTime - program: ProgramNode! - exchangeRate: Float - totalEntitledQuantity: Float - totalEntitledQuantityUsd: Float - totalEntitledQuantityRevised: Float - totalEntitledQuantityRevisedUsd: Float - totalDeliveredQuantity: Float - totalDeliveredQuantityUsd: Float - totalUndeliveredQuantity: Float - totalUndeliveredQuantityUsd: Float - name: String! - caId: String - caHashId: UUID - status: CashPlanStatus! - distributionLevel: String! - dispersionDate: DateTime! - coverageDuration: Int! - coverageUnit: String! - comments: String - deliveryType: String - assistanceMeasurement: String! - assistanceThrough: String! - serviceProvider: ServiceProviderNode - visionId: String - fundsCommitment: String - downPayment: String - validationAlertsCount: Int! - totalPersonsCovered: Int! - totalPersonsCoveredRevised: Int! - paymentItems(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection! - bankReconciliationSuccess: Int - bankReconciliationError: Int - totalNumberOfHouseholds: Int - currency: String - canCreatePaymentVerificationPlan: Boolean - availablePaymentRecordsCount: Int - verificationPlans(offset: Int, before: String, after: String, first: Int, last: Int, programId: String): PaymentVerificationPlanNodeConnection - paymentVerificationSummary: PaymentVerificationSummaryNode - unicefId: String -} - -type CashPlanNodeConnection { - pageInfo: PageInfo! - edges: [CashPlanNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type CashPlanNodeEdge { - node: CashPlanNode - cursor: String! -} - -enum CashPlanStatus { - DISTRIBUTION_COMPLETED - DISTRIBUTION_COMPLETED_WITH_ERRORS - TRANSACTION_COMPLETED - TRANSACTION_COMPLETED_WITH_ERRORS -} - -input CategoryExtrasInput { - sensitiveGrievanceTicketExtras: SensitiveGrievanceTicketExtras - grievanceComplaintTicketExtras: GrievanceComplaintTicketExtras - positiveFeedbackTicketExtras: PositiveFeedbackTicketExtras - negativeFeedbackTicketExtras: NegativeFeedbackTicketExtras - referralTicketExtras: ReferralTicketExtras -} - -type ChartDatasetNode { - labels: [String] - datasets: [_DatasetsNode] -} - -type ChartDetailedDatasetsNode { - labels: [String] - datasets: [_DetailedDatasetsNode] -} - -type ChartGrievanceTicketsNode { - labels: [String] - datasets: [_DatasetsNode] - totalNumberOfGrievances: Int - totalNumberOfFeedback: Int - totalNumberOfOpenSensitive: Int -} - -type ChartPaymentVerification { - labels: [String] - datasets: [_DetailedDatasetsNode] - households: Int - averageSampleSize: Float -} - -type CheckAgainstSanctionListMutation { - ok: Boolean - errors: [XlsxRowErrorNode] -} - -type ChoiceObject { - name: String - value: String -} - -type ChoiceObjectInt { - name: String - value: Int -} - -input ChooseDeliveryMechanismsForPaymentPlanInput { - paymentPlanId: ID! - deliveryMechanisms: [String]! -} - -type ChooseDeliveryMechanismsForPaymentPlanMutation { - paymentPlan: PaymentPlanNode -} - -type CommunicationMessageNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - unicefId: String - title: String! - body: String! - createdBy: UserNode - numberOfRecipients: Int! - businessArea: UserBusinessAreaNode! - households(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! - targetPopulation: TargetPopulationNode - registrationDataImport: RegistrationDataImportNode - samplingType: MessageSamplingType! - fullListArguments: JSONString - randomSamplingArguments: JSONString - sampleSize: Int! - program: ProgramNode - isOriginal: Boolean! - isMigrationHandled: Boolean! - migratedAt: DateTime - copiedFrom: CommunicationMessageNode - copiedTo(offset: Int, before: String, after: String, first: Int, last: Int): CommunicationMessageNodeConnection! - adminUrl: String -} - -type CommunicationMessageNodeConnection { - pageInfo: PageInfo! - edges: [CommunicationMessageNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type CommunicationMessageNodeEdge { - node: CommunicationMessageNode - cursor: String! -} - -type CommunicationMessageRecipientMapNode implements Node { - id: ID! - size: Int - headOfHousehold: IndividualNode -} - -type CommunicationMessageRecipientMapNodeConnection { - pageInfo: PageInfo! - edges: [CommunicationMessageRecipientMapNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type CommunicationMessageRecipientMapNodeEdge { - node: CommunicationMessageRecipientMapNode - cursor: String! -} - -type ContentTypeObjectType { - id: ID! - appLabel: String! - model: String! - paymentverificationplanSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationPlanNodeConnection! - paymentverificationSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationNodeConnection! - paymentverificationsummarySet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationSummaryNodeConnection! - ticketcomplaintdetailsSet(offset: Int, before: String, after: String, first: Int, last: Int): TicketComplaintDetailsNodeConnection! - ticketsensitivedetailsSet(offset: Int, before: String, after: String, first: Int, last: Int): TicketSensitiveDetailsNodeConnection! - logEntries(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationLogEntryNodeConnection! - name: String -} - -type CopyProgram { - validationErrors: Arg - program: ProgramNode -} - -input CopyProgramInput { - id: String! - name: String - startDate: Date - endDate: Date - description: String - budget: Decimal - frequencyOfPayments: String - sector: String - cashPlus: Boolean - populationGoal: Int - administrativeAreasOfImplementation: String - businessAreaSlug: String - dataCollectingTypeCode: String - partners: [ProgramPartnerThroughInput] - partnerAccess: String - programmeCode: String - pduFields: [PDUFieldInput] -} - -input CopyTargetPopulationInput { - id: ID - name: String - programCycleId: ID! -} - -input CopyTargetPopulationMutationInput { - targetPopulationData: CopyTargetPopulationInput - clientMutationId: String -} - -type CopyTargetPopulationMutationPayload { - targetPopulation: TargetPopulationNode - validationErrors: Arg - clientMutationId: String -} - -type CoreFieldChoiceObject { - labels: [LabelNode] - labelEn: String - value: String - admin: String - listName: String -} - -type CountAndPercentageNode { - count: Int - percentage: Float -} - -input CreateAccountabilityCommunicationMessageInput { - households: [ID] - targetPopulation: ID - registrationDataImport: ID - samplingType: SamplingChoices! - fullListArguments: AccountabilityFullListArguments - randomSamplingArguments: AccountabilityRandomSamplingArguments - title: String! - body: String! -} - -type CreateCommunicationMessageMutation { - message: CommunicationMessageNode -} - -type CreateDashboardReport { - success: Boolean -} - -input CreateDashboardReportInput { - reportTypes: [String]! - businessAreaSlug: String! - year: Int! - adminArea: ID - program: ID -} - -input CreateFeedbackInput { - issueType: String! - householdLookup: ID - individualLookup: ID - description: String! - comments: String - admin2: ID - area: String - language: String - consent: Boolean - program: ID -} - -input CreateFeedbackMessageInput { - description: String! - feedback: ID! -} - -type CreateFeedbackMessageMutation { - feedbackMessage: FeedbackMessageNode -} - -type CreateFeedbackMutation { - feedback: FeedbackNode -} - -type CreateFollowUpPaymentPlanMutation { - paymentPlan: PaymentPlanNode -} - -input CreateGrievanceTicketExtrasInput { - category: CategoryExtrasInput - issueType: IssueTypeExtrasInput -} - -input CreateGrievanceTicketInput { - description: String! - assignedTo: ID - category: Int! - issueType: Int - admin: ID - area: String - language: String! - consent: Boolean! - businessArea: ID! - linkedTickets: [ID] - extras: CreateGrievanceTicketExtrasInput - priority: Int - urgency: Int - partner: Int - program: ID - comments: String - linkedFeedbackId: ID - documentation: [GrievanceDocumentInput] -} - -type CreateGrievanceTicketMutation { - grievanceTickets: [GrievanceTicketNode] -} - -input CreatePaymentPlanInput { - businessAreaSlug: String! - targetingId: ID! - dispersionStartDate: Date! - dispersionEndDate: Date! - currency: String! -} - -type CreatePaymentPlanMutation { - paymentPlan: PaymentPlanNode -} - -input CreatePaymentVerificationInput { - sampling: String! - verificationChannel: String! - businessAreaSlug: String! - fullListArguments: FullListArguments - randomSamplingArguments: RandomSamplingArguments - rapidProArguments: RapidProArguments - cashOrPaymentPlanId: ID! -} - -type CreateProgram { - validationErrors: Arg - program: ProgramNode -} - -input CreateProgramInput { - name: String - startDate: Date - endDate: Date - description: String - budget: Decimal - frequencyOfPayments: String - sector: String - cashPlus: Boolean - populationGoal: Int - administrativeAreasOfImplementation: String - businessAreaSlug: String - dataCollectingTypeCode: String - partners: [ProgramPartnerThroughInput] - partnerAccess: String - programmeCode: String - pduFields: [PDUFieldInput] -} - -type CreateReport { - report: ReportNode -} - -input CreateReportInput { - reportType: Int! - businessAreaSlug: String! - dateFrom: Date! - dateTo: Date! - program: ID - adminArea1: ID - adminArea2: [ID] -} - -input CreateSurveyInput { - title: String! - body: String - category: String! - targetPopulation: ID - program: ID - samplingType: String! - fullListArguments: AccountabilityFullListArguments - randomSamplingArguments: AccountabilityRandomSamplingArguments - flow: String! -} - -type CreateSurveyMutation { - survey: SurveyNode -} - -input CreateTargetPopulationInput { - name: String! - targetingCriteria: TargetingCriteriaObjectType! - businessAreaSlug: String! - programId: ID! - programCycleId: ID! - excludedIds: String! - exclusionReason: String -} - -type CreateTargetPopulationMutation { - validationErrors: Arg - targetPopulation: TargetPopulationNode -} - -input CreateTicketNoteInput { - description: String! - ticket: ID! -} - -type CreateTicketNoteMutation { - grievanceTicketNote: TicketNoteNode -} - -type CreateVerificationPlanMutation { - paymentPlan: GenericPaymentPlanNode -} - -type DataCollectingTypeChoiceObject { - name: String - value: String - description: String -} - -type DataCollectingTypeNode implements Node { - id: ID! - created: DateTime! - modified: DateTime! - label: String! - code: String! - type: DataCollectingTypeType - description: String! - active: Boolean! - deprecated: Boolean! - individualFiltersAvailable: Boolean! - householdFiltersAvailable: Boolean! - recalculateComposition: Boolean! - weight: Int! - datacollectingtypeSet(offset: Int, before: String, after: String, first: Int, last: Int): DataCollectingTypeNodeConnection! - programs(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! -} - -type DataCollectingTypeNodeConnection { - pageInfo: PageInfo! - edges: [DataCollectingTypeNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type DataCollectingTypeNodeEdge { - node: DataCollectingTypeNode - cursor: String! -} - -enum DataCollectingTypeType { - STANDARD - SOCIAL -} - -scalar Date - -scalar DateTime - -scalar Decimal - -type DeduplicationEngineSimilarityPairNode implements Node { - id: ID! - program: ProgramNode! - individual1: IndividualNode! - individual2: IndividualNode! - similarityScore: String - ticketneedsadjudicationdetailsSet(offset: Int, before: String, after: String, first: Int, last: Int): TicketNeedsAdjudicationDetailsNodeConnection! - isDuplicate: Boolean -} - -type DeduplicationEngineSimilarityPairNodeConnection { - pageInfo: PageInfo! - edges: [DeduplicationEngineSimilarityPairNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type DeduplicationEngineSimilarityPairNodeEdge { - node: DeduplicationEngineSimilarityPairNode - cursor: String! -} - -type DeduplicationResultNode { - hitId: ID - fullName: String - score: Float - proximityToScore: Float - location: String - age: Int - duplicate: Boolean - distinct: Boolean -} - -type DeleteHouseholdApproveMutation { - grievanceTicket: GrievanceTicketNode -} - -type DeletePaymentPlanMutation { - paymentPlan: PaymentPlanNode -} - -type DeletePaymentVerificationPlan { - paymentPlan: GenericPaymentPlanNode -} - -type DeleteProgram { - ok: Boolean -} - -type DeleteRegistrationDataImport { - ok: Boolean -} - -input DeleteTargetPopulationMutationInput { - targetId: ID! - clientMutationId: String -} - -type DeleteTargetPopulationMutationPayload { - ok: Boolean - clientMutationId: String -} - -type DeliveredQuantityNode { - totalDeliveredQuantity: Decimal - currency: String -} - -input DeliveryMechanismDataObjectType { - label: String! - approveStatus: Boolean! - dataFields: [DeliveryMechanismDataPayloadFieldObjectType]! -} - -input DeliveryMechanismDataPayloadFieldObjectType { - name: String! - value: String! - previousValue: String -} - -type DeliveryMechanismNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - paymentGatewayId: String - code: String - name: String - optionalFields: [String!]! - requiredFields: [String!]! - uniqueFields: [String!]! - isActive: Boolean! - transferType: DeliveryMechanismTransferType! - financialserviceproviderSet(offset: Int, before: String, after: String, first: Int, last: Int): FinancialServiceProviderNodeConnection! - deliverymechanismperpaymentplanSet(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismPerPaymentPlanNodeConnection! - paymentrecordSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection! - paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! -} - -type DeliveryMechanismNodeConnection { - pageInfo: PageInfo! - edges: [DeliveryMechanismNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type DeliveryMechanismNodeEdge { - node: DeliveryMechanismNode - cursor: String! -} - -enum DeliveryMechanismPerPaymentPlanDeliveryMechanismChoice { - CARDLESS_CASH_WITHDRAWAL - CASH - CASH_BY_FSP - CHEQUE - DEPOSIT_TO_CARD - MOBILE_MONEY - PRE_PAID_CARD - REFERRAL - TRANSFER - TRANSFER_TO_ACCOUNT - VOUCHER - ATM_CARD - CASH_OVER_THE_COUNTER - TRANSFER_TO_DIGITAL_WALLET -} - -type DeliveryMechanismPerPaymentPlanNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - paymentPlan: PaymentPlanNode! - financialServiceProvider: FinancialServiceProviderNode - createdBy: UserNode! - sentDate: DateTime! - sentBy: UserNode - status: String! - deliveryMechanismChoice: DeliveryMechanismPerPaymentPlanDeliveryMechanismChoice - deliveryMechanism: DeliveryMechanismNode - deliveryMechanismOrder: Int! - sentToPaymentGateway: Boolean! - chosenConfiguration: String - name: String - code: String - order: Int - fsp: FinancialServiceProviderNode -} - -type DeliveryMechanismPerPaymentPlanNodeConnection { - pageInfo: PageInfo! - edges: [DeliveryMechanismPerPaymentPlanNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type DeliveryMechanismPerPaymentPlanNodeEdge { - node: DeliveryMechanismPerPaymentPlanNode - cursor: String! -} - -enum DeliveryMechanismTransferType { - CASH - VOUCHER - DIGITAL -} - -type DiscardPaymentVerificationPlan { - paymentPlan: GenericPaymentPlanNode -} - -type DjangoDebug { - sql: [DjangoDebugSQL] -} - -type DjangoDebugSQL { - vendor: String! - alias: String! - sql: String - duration: Float! - rawSql: String! - params: String! - startTime: Float! - stopTime: Float! - isSlow: Boolean! - isSelect: Boolean! - transId: String - transStatus: String - isoLevel: String - encoding: String -} - -type DocumentNode implements Node { - id: ID! - rdiMergeStatus: DocumentRdiMergeStatus! - isRemoved: Boolean! - isOriginal: Boolean! - createdAt: DateTime! - updatedAt: DateTime! - lastSyncAt: DateTime - documentNumber: String! - photo: String - individual: IndividualNode! - type: ImportedDocumentTypeNode! - country: String - status: DocumentStatus! - cleared: Boolean! - clearedDate: DateTime! - clearedBy: UserNode - issuanceDate: DateTime - expiryDate: DateTime - program: ProgramNode - isMigrationHandled: Boolean! - copiedFrom: DocumentNode - copiedTo(offset: Int, before: String, after: String, first: Int, last: Int): DocumentNodeConnection! - countryIso3: String -} - -type DocumentNodeConnection { - pageInfo: PageInfo! - edges: [DocumentNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type DocumentNodeEdge { - node: DocumentNode - cursor: String! -} - -enum DocumentRdiMergeStatus { - PENDING - MERGED -} - -enum DocumentStatus { - PENDING - VALID - NEED_INVESTIGATION - INVALID -} - -input EditBankTransferObjectType { - id: ID! - type: String! - bankName: String! - bankAccountNumber: String! - bankBranchName: String - accountHolderName: String! -} - -input EditDeliveryMechanismDataObjectType { - id: ID! - label: String! - approveStatus: Boolean! - dataFields: [DeliveryMechanismDataPayloadFieldObjectType]! -} - -input EditIndividualDocumentObjectType { - id: ID! - country: String! - key: String! - number: String! - photo: Arg - photoraw: Arg -} - -input EditIndividualIdentityObjectType { - id: ID! - country: String! - partner: String! - number: String! -} - -input EditPaymentVerificationInput { - sampling: String! - verificationChannel: String! - businessAreaSlug: String! - fullListArguments: FullListArguments - randomSamplingArguments: RandomSamplingArguments - rapidProArguments: RapidProArguments - paymentVerificationPlanId: ID! -} - -type EditPaymentVerificationMutation { - paymentPlan: GenericPaymentPlanNode -} - -type EraseRegistrationDataImportMutation { - registrationDataImport: RegistrationDataImportNode -} - -type ExcludeHouseholdsMutation { - paymentPlan: PaymentPlanNode -} - -type ExportPDFPaymentPlanSummaryMutation { - paymentPlan: PaymentPlanNode -} - -type ExportSurveySampleMutationMutation { - survey: SurveyNode -} - -type ExportXLSXPaymentPlanPaymentListMutation { - paymentPlan: PaymentPlanNode -} - -type ExportXLSXPaymentPlanPaymentListPerFSPMutation { - paymentPlan: PaymentPlanNode -} - -type ExportXlsxPaymentVerificationPlanFile { - paymentPlan: GenericPaymentPlanNode -} - -input FSPToDeliveryMechanismMappingInput { - fspId: ID! - deliveryMechanism: String! - chosenConfiguration: String - order: Int! -} - -enum FeedbackIssueType { - POSITIVE_FEEDBACK - NEGATIVE_FEEDBACK -} - -type FeedbackMessageNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - description: String! - createdBy: UserNode -} - -type FeedbackMessageNodeConnection { - pageInfo: PageInfo! - edges: [FeedbackMessageNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type FeedbackMessageNodeEdge { - node: FeedbackMessageNode - cursor: String! -} - -type FeedbackNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - unicefId: String - businessArea: UserBusinessAreaNode! - issueType: FeedbackIssueType! - householdLookup: HouseholdNode - individualLookup: IndividualNode - description: String! - comments: String - admin2: AreaNode - area: String! - language: String! - consent: Boolean! - program: ProgramNode - createdBy: UserNode - linkedGrievance: GrievanceTicketNode - isOriginal: Boolean! - isMigrationHandled: Boolean! - migratedAt: DateTime - copiedFrom: FeedbackNode - copiedTo(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackNodeConnection! - feedbackMessages(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackMessageNodeConnection! - adminUrl: String -} - -type FeedbackNodeConnection { - pageInfo: PageInfo! - edges: [FeedbackNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type FeedbackNodeEdge { - node: FeedbackNode - cursor: String! -} - -type FieldAttributeNode { - id: String - type: String - name: String - labels: [LabelNode] - labelEn: String - hint: String - required: Boolean - choices: [CoreFieldChoiceObject] - associatedWith: String - isFlexField: Boolean - pduData: PeriodicFieldDataNode -} - -type FilteredActionsListNode { - approval: [ApprovalNode] - authorization: [ApprovalNode] - financeRelease: [ApprovalNode] - reject: [ApprovalNode] -} - -type FinalizeTargetPopulationMutation { - targetPopulation: TargetPopulationNode -} - -enum FinancialServiceProviderCommunicationChannel { - API - SFTP - XLSX -} - -type FinancialServiceProviderNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - allowedBusinessAreas(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID): UserBusinessAreaNodeConnection! - createdBy: UserNode - name: String! - visionVendorNumber: String! - deliveryMechanismsChoices: [String!] - deliveryMechanisms(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismNodeConnection! - distributionLimit: Float - communicationChannel: FinancialServiceProviderCommunicationChannel! - dataTransferConfiguration: JSONString - xlsxTemplates(offset: Int, before: String, after: String, first: Int, last: Int): FinancialServiceProviderXlsxTemplateNodeConnection! - paymentGatewayId: String - deliveryMechanismsPerPaymentPlan(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismPerPaymentPlanNodeConnection! - paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! - fullName: String - isPaymentGateway: Boolean -} - -type FinancialServiceProviderNodeConnection { - pageInfo: PageInfo! - edges: [FinancialServiceProviderNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type FinancialServiceProviderNodeEdge { - node: FinancialServiceProviderNode - cursor: String! -} - -type FinancialServiceProviderXlsxTemplateNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - createdBy: UserNode - name: String! - columns: [String] - coreFields: [String!]! - flexFields: [String!]! - financialServiceProviders(offset: Int, before: String, after: String, first: Int, last: Int): FinancialServiceProviderNodeConnection! -} - -type FinancialServiceProviderXlsxTemplateNodeConnection { - pageInfo: PageInfo! - edges: [FinancialServiceProviderXlsxTemplateNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type FinancialServiceProviderXlsxTemplateNodeEdge { - node: FinancialServiceProviderXlsxTemplateNode - cursor: String! -} - -type FinishPaymentVerificationPlan { - paymentPlan: GenericPaymentPlanNode -} - -enum FlexFieldClassificationChoices { - NOT_FLEX_FIELD - FLEX_FIELD_BASIC - FLEX_FIELD_PDU -} - -scalar FlexFieldsScalar - -type FspChoice { - id: String - name: String - configurations: [FspConfiguration] -} - -type FspChoices { - deliveryMechanism: String - fsps: [FspChoice] -} - -type FspConfiguration { - id: String - key: String - label: String -} - -input FullListArguments { - excludedAdminAreas: [String] -} - -type GenericPaymentNode { - id: String - objType: String - unicefId: String - currency: String - deliveredQuantity: Float - deliveredQuantityUsd: Float - household: HouseholdNode -} - -type GenericPaymentPlanNode { - id: String - objType: String - paymentVerificationSummary: PaymentVerificationSummaryNode - availablePaymentRecordsCount: Int - verificationPlans(offset: Int, before: String, after: String, first: Int, last: Int, programId: String): PaymentVerificationPlanNodeConnection - statusDate: DateTime - status: String - bankReconciliationSuccess: Int - bankReconciliationError: Int - deliveryType: String - totalNumberOfHouseholds: Int - currency: String - totalDeliveredQuantity: Float - totalEntitledQuantity: Float - totalUndeliveredQuantity: Float - canCreatePaymentVerificationPlan: Boolean -} - -scalar GeoJSON - -input GetAccountabilityCommunicationMessageSampleSizeInput { - households: [ID] - targetPopulation: ID - registrationDataImport: ID - samplingType: SamplingChoices! - fullListArguments: AccountabilityFullListArguments - randomSamplingArguments: AccountabilityRandomSamplingArguments -} - -input GetCashplanVerificationSampleSizeInput { - cashOrPaymentPlanId: ID - paymentVerificationPlanId: ID - sampling: String! - verificationChannel: String! - businessAreaSlug: String! - fullListArguments: FullListArguments - randomSamplingArguments: RandomSamplingArguments - rapidProArguments: RapidProArguments -} - -type GetCashplanVerificationSampleSizeObject { - paymentRecordCount: Int - sampleSize: Int -} - -type GetCommunicationMessageSampleSizeNode { - numberOfRecipients: Int - sampleSize: Int -} - -input GrievanceComplaintTicketExtras { - household: ID - individual: ID - paymentRecord: [ID] -} - -input GrievanceDocumentInput { - name: String! - file: Upload! -} - -type GrievanceDocumentNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - name: String - createdBy: UserNode - grievanceTicket: GrievanceTicketNode - fileSize: Int - contentType: String! - filePath: String - fileName: String -} - -type GrievanceDocumentNodeConnection { - pageInfo: PageInfo! - edges: [GrievanceDocumentNodeEdge]! -} - -type GrievanceDocumentNodeEdge { - node: GrievanceDocumentNode - cursor: String! -} - -input GrievanceDocumentUpdateInput { - id: ID! - name: String - file: Upload -} - -type GrievanceStatusChangeMutation { - grievanceTicket: GrievanceTicketNode -} - -type GrievanceTicketNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - version: BigInt! - unicefId: String - userModified: DateTime - lastNotificationSent: DateTime - createdBy: UserNode - assignedTo: UserNode - status: Int! - category: Int! - issueType: Int - description: String! - admin2: AreaNode - area: String! - language: String! - consent: Boolean! - businessArea: UserBusinessAreaNode! - linkedTickets: [GrievanceTicketNode] - registrationDataImport: RegistrationDataImportNode - extras: JSONString! - ignored: Boolean! - householdUnicefId: String - priority: Int - urgency: Int - partner: PartnerType - programs: [ProgramNode] - comments: String - isOriginal: Boolean! - isMigrationHandled: Boolean! - migratedAt: DateTime - copiedFrom: GrievanceTicketNode - copiedTo(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! - ticketNotes(offset: Int, before: String, after: String, first: Int, last: Int): TicketNoteNodeConnection! - complaintTicketDetails: TicketComplaintDetailsNode - sensitiveTicketDetails: TicketSensitiveDetailsNode - householdDataUpdateTicketDetails: TicketHouseholdDataUpdateDetailsNode - individualDataUpdateTicketDetails: TicketIndividualDataUpdateDetailsNode - addIndividualTicketDetails: TicketAddIndividualDetailsNode - deleteIndividualTicketDetails: TicketDeleteIndividualDetailsNode - deleteHouseholdTicketDetails: TicketDeleteHouseholdDetailsNode - systemFlaggingTicketDetails: TicketSystemFlaggingDetailsNode - needsAdjudicationTicketDetails: TicketNeedsAdjudicationDetailsNode - paymentVerificationTicketDetails: TicketPaymentVerificationDetailsNode - positiveFeedbackTicketDetails: TicketPositiveFeedbackDetailsNode - negativeFeedbackTicketDetails: TicketNegativeFeedbackDetailsNode - referralTicketDetails: TicketReferralDetailsNode - supportDocuments(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceDocumentNodeConnection! - feedback: FeedbackNode - adminUrl: String - household: HouseholdNode - individual: IndividualNode - paymentRecord: PaymentRecordAndPaymentNode - admin: String - existingTickets: [GrievanceTicketNode] - relatedTickets: [GrievanceTicketNode] - totalDays: String - documentation: [GrievanceDocumentNode] -} - -type GrievanceTicketNodeConnection { - pageInfo: PageInfo! - edges: [GrievanceTicketNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type GrievanceTicketNodeEdge { - node: GrievanceTicketNode - cursor: String! -} - -type GroupAttributeNode { - id: UUID! - name: String! - label: JSONString! - flexAttributes(flexField: Boolean): [FieldAttributeNode] - labelEn: String -} - -enum HouseholdCollectIndividualData { - A_ - A_2 - A_1 - A_3 - A_0 -} - -enum HouseholdCollectType { - STANDARD - SINGLE -} - -enum HouseholdConsentSharing { - A_ - GOVERNMENT_PARTNER - HUMANITARIAN_PARTNER - PRIVATE_PARTNER - UNICEF -} - -enum HouseholdCurrency { - A_ - AED - AFN - ALL - AMD - ANG - AOA - ARS - AUD - AWG - AZN - BAM - BBD - BDT - BGN - BHD - BIF - BMD - BND - BOB - BOV - BRL - BSD - BTN - BWP - BYN - BZD - CAD - CDF - CHF - CLP - CNY - COP - CRC - CUC - CUP - CVE - CZK - DJF - DKK - DOP - DZD - EGP - ERN - ETB - EUR - FJD - FKP - GBP - GEL - GHS - GIP - GMD - GNF - GTQ - GYD - HKD - HNL - HRK - HTG - HUF - IDR - ILS - INR - IQD - IRR - ISK - JMD - JOD - JPY - KES - KGS - KHR - KMF - KPW - KRW - KWD - KYD - KZT - LAK - LBP - LKR - LRD - LSL - LYD - MAD - MDL - MGA - MKD - MMK - MNT - MOP - MRU - MUR - MVR - MWK - MXN - MYR - MZN - NAD - NGN - NIO - NOK - NPR - NZD - OMR - PAB - PEN - PGK - PHP - PKR - PLN - PYG - QAR - RON - RSD - RUB - RWF - SAR - SBD - SCR - SDG - SEK - SGD - SHP - SLL - SOS - SRD - SSP - STN - SVC - SYP - SZL - THB - TJS - TMT - TND - TOP - TRY - TTD - TWD - TZS - UAH - UGX - USD - UYU - UYW - UZS - VES - VND - VUV - WST - XAF - XAG - XAU - XCD - XOF - XPF - YER - ZAR - ZMW - ZWL - USDC -} - -type HouseholdDataChangeApproveMutation { - grievanceTicket: GrievanceTicketNode -} - -input HouseholdDataUpdateIssueTypeExtras { - household: ID! - householdData: HouseholdUpdateDataObjectType! -} - -input HouseholdDeleteIssueTypeExtras { - household: ID! -} - -type HouseholdNode implements Node { - id: ID! - size: Int - headOfHousehold: IndividualNode - rdiMergeStatus: HouseholdRdiMergeStatus! - isOriginal: Boolean! - createdAt: DateTime! - updatedAt: DateTime! - isRemoved: Boolean! - removedDate: DateTime - lastSyncAt: DateTime - version: BigInt! - unicefId: String - withdrawn: Boolean! - withdrawnDate: DateTime - consentSign: String! - consent: Boolean - consentSharing: [String] - residenceStatus: String - countryOrigin: String - country: String - address: String! - zipCode: String - adminArea: AreaNode - admin1: AreaNode - admin2: AreaNode - admin3: AreaNode - admin4: AreaNode - geopoint: GeoJSON - representatives(offset: Int, before: String, after: String, first: Int, last: Int): IndividualNodeConnection! - femaleAgeGroup05Count: Int - femaleAgeGroup611Count: Int - femaleAgeGroup1217Count: Int - femaleAgeGroup1859Count: Int - femaleAgeGroup60Count: Int - pregnantCount: Int - maleAgeGroup05Count: Int - maleAgeGroup611Count: Int - maleAgeGroup1217Count: Int - maleAgeGroup1859Count: Int - maleAgeGroup60Count: Int - femaleAgeGroup05DisabledCount: Int - femaleAgeGroup611DisabledCount: Int - femaleAgeGroup1217DisabledCount: Int - femaleAgeGroup1859DisabledCount: Int - femaleAgeGroup60DisabledCount: Int - maleAgeGroup05DisabledCount: Int - maleAgeGroup611DisabledCount: Int - maleAgeGroup1217DisabledCount: Int - maleAgeGroup1859DisabledCount: Int - maleAgeGroup60DisabledCount: Int - childrenCount: Int - maleChildrenCount: Int - femaleChildrenCount: Int - childrenDisabledCount: Int - maleChildrenDisabledCount: Int - femaleChildrenDisabledCount: Int - registrationDataImport: RegistrationDataImportNode - programs(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! - returnee: Boolean - flexFields: FlexFieldsScalar - firstRegistrationDate: DateTime! - lastRegistrationDate: DateTime! - fchildHoh: Boolean - childHoh: Boolean - businessArea: UserBusinessAreaNode! - start: DateTime - deviceid: String! - nameEnumerator: String! - orgEnumerator: HouseholdOrgEnumerator! - orgNameEnumerator: String! - village: String! - registrationMethod: HouseholdRegistrationMethod! - collectIndividualData: HouseholdCollectIndividualData! - currency: String - unhcrId: String! - userFields: JSONString! - detailId: String - registrationId: String - programRegistrationId: String - totalCashReceivedUsd: Decimal - totalCashReceived: Decimal - familyId: String - program: ProgramNode - copiedFrom: HouseholdNode - originUnicefId: String - isMigrationHandled: Boolean! - migratedAt: DateTime - isRecalculatedGroupAges: Boolean! - collectType: HouseholdCollectType! - koboSubmissionUuid: UUID - koboSubmissionTime: DateTime - enumeratorRecId: Int - misUnicefId: String - flexRegistrationsRecordId: Int - copiedTo(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! - individualsAndRoles: [IndividualRoleInHouseholdNode!]! - individuals(offset: Int, before: String, after: String, first: Int, last: Int, household_Id: UUID, businessArea: String, fullName: String, fullName_Startswith: String, fullName_Endswith: String, sex: [String], household_AdminArea: ID, withdrawn: Boolean, program: ID, age: String, programs: [ID], search: String, documentType: String, documentNumber: String, lastRegistrationDate: String, admin1: [ID], admin2: [ID], status: [String], excludedId: String, flags: [String], isActiveProgram: Boolean, orderBy: String): IndividualNodeConnection - paymentrecordSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection! - paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! - complaintTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketComplaintDetailsNodeConnection! - sensitiveTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketSensitiveDetailsNodeConnection! - householdDataUpdateTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketHouseholdDataUpdateDetailsNodeConnection! - addIndividualTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketAddIndividualDetailsNodeConnection! - deleteHouseholdTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketDeleteHouseholdDetailsNodeConnection! - positiveFeedbackTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketPositiveFeedbackDetailsNodeConnection! - negativeFeedbackTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketNegativeFeedbackDetailsNodeConnection! - referralTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketReferralDetailsNodeConnection! - targetPopulations(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection! - selections: [HouseholdSelectionNode!]! - messages(offset: Int, before: String, after: String, first: Int, last: Int): CommunicationMessageNodeConnection! - feedbacks(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackNodeConnection! - surveys(offset: Int, before: String, after: String, first: Int, last: Int): SurveyNodeConnection! - adminUrl: String - selection: HouseholdSelectionNode - sanctionListPossibleMatch: Boolean - sanctionListConfirmedMatch: Boolean - hasDuplicates: Boolean - adminAreaTitle: String - status: String - deliveredQuantities: [DeliveredQuantityNode] - activeIndividualsCount: Int - importId: String -} - -type HouseholdNodeConnection { - pageInfo: PageInfo! - edges: [HouseholdNodeEdge]! - totalCount: Int - individualsCount: Int - edgeCount: Int -} - -type HouseholdNodeEdge { - node: HouseholdNode - cursor: String! -} - -enum HouseholdOrgEnumerator { - A_ - PARTNER - UNICEF -} - -enum HouseholdRdiMergeStatus { - PENDING - MERGED -} - -enum HouseholdRegistrationMethod { - A_ - COMMUNITY - HH_REGISTRATION -} - -enum HouseholdResidenceStatus { - A_ - IDP - REFUGEE - OTHERS_OF_CONCERN - HOST - NON_HOST - RETURNEE -} - -type HouseholdSelectionNode { - id: UUID! - createdAt: DateTime! - updatedAt: DateTime! - household: HouseholdNode! - targetPopulation: TargetPopulationNode! - vulnerabilityScore: Float - isOriginal: Boolean! - isMigrationHandled: Boolean! -} - -input HouseholdUpdateDataObjectType { - adminAreaTitle: String - status: String - consent: Boolean - consentSharing: [String] - residenceStatus: String - countryOrigin: String - country: String - size: Int - address: String - femaleAgeGroup05Count: Int - femaleAgeGroup611Count: Int - femaleAgeGroup1217Count: Int - femaleAgeGroup1859Count: Int - femaleAgeGroup60Count: Int - pregnantCount: Int - maleAgeGroup05Count: Int - maleAgeGroup611Count: Int - maleAgeGroup1217Count: Int - maleAgeGroup1859Count: Int - maleAgeGroup60Count: Int - femaleAgeGroup05DisabledCount: Int - femaleAgeGroup611DisabledCount: Int - femaleAgeGroup1217DisabledCount: Int - femaleAgeGroup1859DisabledCount: Int - femaleAgeGroup60DisabledCount: Int - maleAgeGroup05DisabledCount: Int - maleAgeGroup611DisabledCount: Int - maleAgeGroup1217DisabledCount: Int - maleAgeGroup1859DisabledCount: Int - maleAgeGroup60DisabledCount: Int - returnee: Boolean - fchildHoh: Boolean - childHoh: Boolean - start: DateTime - end: DateTime - nameEnumerator: String - orgEnumerator: String - orgNameEnumerator: String - village: String - registrationMethod: String - collectIndividualData: String - currency: String - unhcrId: String - flexFields: Arg -} - -enum ImportDataDataType { - XLSX - JSON - FLEX -} - -type ImportDataNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - status: ImportDataStatus! - businessAreaSlug: String! - file: String - dataType: ImportDataDataType! - numberOfHouseholds: Int - numberOfIndividuals: Int - error: String! - validationErrors: String! - deliveryMechanismsValidationErrors: String! - createdById: UUID - registrationDataImportHope: RegistrationDataImportNode - koboimportdata: KoboImportDataNode - registrationDataImport: RegistrationDataImportDatahubNode - xlsxValidationErrors: [XlsxRowErrorNode] -} - -enum ImportDataStatus { - PENDING - RUNNING - FINISHED - ERROR - VALIDATION_ERROR - DELIVERY_MECHANISMS_VALIDATION_ERROR -} - -type ImportXLSXPaymentPlanPaymentListMutation { - paymentPlan: PaymentPlanNode - errors: [XlsxErrorNode] -} - -type ImportXLSXPaymentPlanPaymentListPerFSPMutation { - paymentPlan: PaymentPlanNode - errors: [XlsxErrorNode] -} - -type ImportXlsxPaymentVerificationPlanFile { - paymentPlan: GenericPaymentPlanNode - errors: [XlsxErrorNode] -} - -type ImportedDocumentNode implements Node { - id: ID! - rdiMergeStatus: DocumentRdiMergeStatus! - isRemoved: Boolean! - isOriginal: Boolean! - createdAt: DateTime! - updatedAt: DateTime! - lastSyncAt: DateTime - documentNumber: String! - photo: String - individual: IndividualNode! - type: ImportedDocumentTypeNode! - country: String - status: DocumentStatus! - cleared: Boolean! - clearedDate: DateTime! - clearedBy: UserNode - issuanceDate: DateTime - expiryDate: DateTime - program: ProgramNode - isMigrationHandled: Boolean! - copiedFrom: DocumentNode -} - -type ImportedDocumentNodeConnection { - pageInfo: PageInfo! - edges: [ImportedDocumentNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type ImportedDocumentNodeEdge { - node: ImportedDocumentNode - cursor: String! -} - -type ImportedDocumentTypeNode { - id: UUID! - createdAt: DateTime! - updatedAt: DateTime! - label: String! - key: String! - isIdentityDocument: Boolean! - uniqueForIndividual: Boolean! - validForDeduplication: Boolean! - documents(offset: Int, before: String, after: String, first: Int, last: Int): DocumentNodeConnection! -} - -type ImportedHouseholdNode implements Node { - id: ID! - size: Int - headOfHousehold: IndividualNode - rdiMergeStatus: HouseholdRdiMergeStatus! - isOriginal: Boolean! - createdAt: DateTime! - updatedAt: DateTime! - isRemoved: Boolean! - removedDate: DateTime - lastSyncAt: DateTime - version: BigInt! - unicefId: String - withdrawn: Boolean! - withdrawnDate: DateTime - consentSign: String! - consent: Boolean - consentSharing: HouseholdConsentSharing! - residenceStatus: HouseholdResidenceStatus - countryOrigin: String - country: String - address: String! - zipCode: String - adminArea: AreaNode - admin1: AreaNode - admin2: AreaNode - admin3: AreaNode - admin4: AreaNode - geopoint: GeoJSON - femaleAgeGroup05Count: Int - femaleAgeGroup611Count: Int - femaleAgeGroup1217Count: Int - femaleAgeGroup1859Count: Int - femaleAgeGroup60Count: Int - pregnantCount: Int - maleAgeGroup05Count: Int - maleAgeGroup611Count: Int - maleAgeGroup1217Count: Int - maleAgeGroup1859Count: Int - maleAgeGroup60Count: Int - femaleAgeGroup05DisabledCount: Int - femaleAgeGroup611DisabledCount: Int - femaleAgeGroup1217DisabledCount: Int - femaleAgeGroup1859DisabledCount: Int - femaleAgeGroup60DisabledCount: Int - maleAgeGroup05DisabledCount: Int - maleAgeGroup611DisabledCount: Int - maleAgeGroup1217DisabledCount: Int - maleAgeGroup1859DisabledCount: Int - maleAgeGroup60DisabledCount: Int - childrenCount: Int - maleChildrenCount: Int - femaleChildrenCount: Int - childrenDisabledCount: Int - maleChildrenDisabledCount: Int - femaleChildrenDisabledCount: Int - registrationDataImport: RegistrationDataImportNode - returnee: Boolean - flexFields: Arg - firstRegistrationDate: DateTime! - lastRegistrationDate: DateTime! - fchildHoh: Boolean - childHoh: Boolean - businessArea: UserBusinessAreaNode! - start: DateTime - deviceid: String! - nameEnumerator: String! - orgEnumerator: HouseholdOrgEnumerator! - orgNameEnumerator: String! - village: String! - registrationMethod: HouseholdRegistrationMethod! - collectIndividualData: HouseholdCollectIndividualData! - currency: HouseholdCurrency! - unhcrId: String! - userFields: JSONString! - detailId: String - registrationId: String - programRegistrationId: String - totalCashReceivedUsd: Float - totalCashReceived: Float - familyId: String - program: ProgramNode - copiedFrom: HouseholdNode - originUnicefId: String - isMigrationHandled: Boolean! - migratedAt: DateTime - isRecalculatedGroupAges: Boolean! - collectType: HouseholdCollectType! - koboSubmissionUuid: UUID - koboSubmissionTime: DateTime - enumeratorRecId: Int - misUnicefId: String - flexRegistrationsRecordId: Int - individuals(offset: Int, before: String, after: String, first: Int, last: Int): ImportedIndividualNodeConnection - hasDuplicates: Boolean - importId: String -} - -type ImportedHouseholdNodeConnection { - pageInfo: PageInfo! - edges: [ImportedHouseholdNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type ImportedHouseholdNodeEdge { - node: ImportedHouseholdNode - cursor: String! -} - -type ImportedIndividualIdentityNode implements Node { - id: ID! - created: DateTime! - modified: DateTime! - rdiMergeStatus: IndividualIdentityRdiMergeStatus! - isRemoved: Boolean! - isOriginal: Boolean! - individual: IndividualNode! - number: String! - partner: String - country: String - isMigrationHandled: Boolean! - copiedFrom: IndividualIdentityNode -} - -type ImportedIndividualIdentityNodeConnection { - pageInfo: PageInfo! - edges: [ImportedIndividualIdentityNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type ImportedIndividualIdentityNodeEdge { - node: ImportedIndividualIdentityNode - cursor: String! -} - -type ImportedIndividualNode implements Node { - id: ID! - rdiMergeStatus: IndividualRdiMergeStatus! - isOriginal: Boolean! - createdAt: DateTime! - updatedAt: DateTime! - isRemoved: Boolean! - removedDate: DateTime - lastSyncAt: DateTime - version: BigInt! - unicefId: String - duplicate: Boolean! - duplicateDate: DateTime - withdrawn: Boolean! - withdrawnDate: DateTime - individualId: String! - photo: String! - fullName: String! - givenName: String! - middleName: String! - familyName: String! - sex: IndividualSex! - birthDate: Date! - estimatedBirthDate: Boolean - maritalStatus: IndividualMaritalStatus! - phoneNo: String! - phoneNoValid: Boolean - phoneNoAlternative: String! - phoneNoAlternativeValid: Boolean - email: String - paymentDeliveryPhoneNo: String - relationship: String - household: HouseholdNode - registrationDataImport: RegistrationDataImportNode! - workStatus: String! - firstRegistrationDate: Date! - lastRegistrationDate: Date! - flexFields: FlexFieldsScalar - userFields: JSONString! - enrolledInNutritionProgramme: Boolean - administrationOfRutf: Boolean - deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus! - deduplicationBatchStatus: IndividualDeduplicationBatchStatus! - deduplicationGoldenRecordResults: [DeduplicationResultNode] - deduplicationBatchResults: [DeduplicationResultNode] - importedIndividualId: UUID - sanctionListPossibleMatch: Boolean! - sanctionListConfirmedMatch: Boolean! - pregnant: Boolean - disability: IndividualDisability! - observedDisability: [String] - disabilityCertificatePicture: String - seeingDisability: String! - hearingDisability: String! - physicalDisability: String! - memoryDisability: String! - selfcareDisability: String! - commsDisability: String! - whoAnswersPhone: String! - whoAnswersAltPhone: String! - businessArea: UserBusinessAreaNode! - fchildHoh: Boolean! - childHoh: Boolean! - detailId: String - registrationId: String - programRegistrationId: String - preferredLanguage: String - relationshipConfirmed: Boolean! - ageAtRegistration: Int - walletName: String! - blockchainName: String! - walletAddress: String! - program: ProgramNode - copiedFrom: IndividualNode - originUnicefId: String - isMigrationHandled: Boolean! - migratedAt: DateTime - misUnicefId: String - role: String - age: Int - importId: String - documents(offset: Int, before: String, after: String, first: Int, last: Int): ImportedDocumentNodeConnection - identities(offset: Int, before: String, after: String, first: Int, last: Int): ImportedIndividualIdentityNodeConnection -} - -type ImportedIndividualNodeConnection { - pageInfo: PageInfo! - edges: [ImportedIndividualNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type ImportedIndividualNodeEdge { - node: ImportedIndividualNode - cursor: String! -} - -type IndividualDataChangeApproveMutation { - grievanceTicket: GrievanceTicketNode -} - -input IndividualDataUpdateIssueTypeExtras { - individual: ID! - individualData: IndividualUpdateDataObjectType! -} - -enum IndividualDeduplicationBatchStatus { - DUPLICATE_IN_BATCH - NOT_PROCESSED - SIMILAR_IN_BATCH - UNIQUE_IN_BATCH -} - -enum IndividualDeduplicationGoldenRecordStatus { - DUPLICATE - NEEDS_ADJUDICATION - NOT_PROCESSED - POSTPONE - UNIQUE -} - -input IndividualDeleteIssueTypeExtras { - individual: ID! -} - -enum IndividualDisability { - DISABLED - NOT_DISABLED -} - -input IndividualDocumentObjectType { - country: String! - key: String! - number: String! - photo: Arg - photoraw: Arg -} - -type IndividualIdentityNode implements Node { - id: ID! - created: DateTime! - modified: DateTime! - rdiMergeStatus: IndividualIdentityRdiMergeStatus! - isRemoved: Boolean! - isOriginal: Boolean! - individual: IndividualNode! - number: String! - partner: String - country: String - isMigrationHandled: Boolean! - copiedFrom: IndividualIdentityNode - copiedTo(offset: Int, before: String, after: String, first: Int, last: Int): IndividualIdentityNodeConnection! - countryIso3: String -} - -type IndividualIdentityNodeConnection { - pageInfo: PageInfo! - edges: [IndividualIdentityNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type IndividualIdentityNodeEdge { - node: IndividualIdentityNode - cursor: String! -} - -input IndividualIdentityObjectType { - country: String! - partner: String! - number: String! -} - -enum IndividualIdentityRdiMergeStatus { - PENDING - MERGED -} - -enum IndividualMaritalStatus { - A_ - DIVORCED - MARRIED - SEPARATED - SINGLE - WIDOWED -} - -type IndividualNode implements Node { - id: ID! - rdiMergeStatus: IndividualRdiMergeStatus! - isOriginal: Boolean! - createdAt: DateTime! - updatedAt: DateTime! - isRemoved: Boolean! - removedDate: DateTime - lastSyncAt: DateTime - version: BigInt! - unicefId: String - duplicate: Boolean! - duplicateDate: DateTime - withdrawn: Boolean! - withdrawnDate: DateTime - individualId: String! - photo: String - fullName: String! - givenName: String! - middleName: String! - familyName: String! - sex: IndividualSex! - birthDate: Date! - estimatedBirthDate: Boolean - maritalStatus: IndividualMaritalStatus! - phoneNo: String! - phoneNoValid: Boolean - phoneNoAlternative: String! - phoneNoAlternativeValid: Boolean - email: String! - paymentDeliveryPhoneNo: String - relationship: IndividualRelationship - household: HouseholdNode - registrationDataImport: RegistrationDataImportNode! - workStatus: String! - firstRegistrationDate: Date! - lastRegistrationDate: Date! - flexFields: FlexFieldsScalar - userFields: JSONString! - enrolledInNutritionProgramme: Boolean - administrationOfRutf: Boolean - deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus! - deduplicationBatchStatus: IndividualDeduplicationBatchStatus! - deduplicationGoldenRecordResults: [DeduplicationResultNode] - deduplicationBatchResults: [DeduplicationResultNode] - importedIndividualId: UUID - sanctionListPossibleMatch: Boolean! - sanctionListConfirmedMatch: Boolean! - pregnant: Boolean - disability: IndividualDisability! - observedDisability: [String] - disabilityCertificatePicture: String - seeingDisability: String! - hearingDisability: String! - physicalDisability: String! - memoryDisability: String! - selfcareDisability: String! - commsDisability: String! - whoAnswersPhone: String! - whoAnswersAltPhone: String! - businessArea: UserBusinessAreaNode! - fchildHoh: Boolean! - childHoh: Boolean! - detailId: String - registrationId: String - programRegistrationId: String - preferredLanguage: String - relationshipConfirmed: Boolean! - ageAtRegistration: Int - walletName: String! - blockchainName: String! - walletAddress: String! - program: ProgramNode - copiedFrom: IndividualNode - originUnicefId: String - isMigrationHandled: Boolean! - migratedAt: DateTime - misUnicefId: String - representedHouseholds(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! - headingHousehold: HouseholdNode - documents(offset: Int, before: String, after: String, first: Int, last: Int): DocumentNodeConnection! - identities(offset: Int, before: String, after: String, first: Int, last: Int): IndividualIdentityNodeConnection! - householdsAndRoles: [IndividualRoleInHouseholdNode!]! - copiedTo(offset: Int, before: String, after: String, first: Int, last: Int): IndividualNodeConnection! - bankAccountInfo: BankAccountInfoNode - biometricDuplicates1(offset: Int, before: String, after: String, first: Int, last: Int): DeduplicationEngineSimilarityPairNodeConnection! - biometricDuplicates2(offset: Int, before: String, after: String, first: Int, last: Int): DeduplicationEngineSimilarityPairNodeConnection! - paymentrecordSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection! - collectorPayments(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! - paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! - complaintTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketComplaintDetailsNodeConnection! - sensitiveTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketSensitiveDetailsNodeConnection! - individualDataUpdateTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketIndividualDataUpdateDetailsNodeConnection! - deleteIndividualTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketDeleteIndividualDetailsNodeConnection! - ticketsystemflaggingdetailsSet(offset: Int, before: String, after: String, first: Int, last: Int): TicketSystemFlaggingDetailsNodeConnection! - ticketGoldenRecords(offset: Int, before: String, after: String, first: Int, last: Int): TicketNeedsAdjudicationDetailsNodeConnection! - ticketDuplicates(offset: Int, before: String, after: String, first: Int, last: Int): TicketNeedsAdjudicationDetailsNodeConnection! - selectedDistinct(offset: Int, before: String, after: String, first: Int, last: Int): TicketNeedsAdjudicationDetailsNodeConnection! - ticketSelected(offset: Int, before: String, after: String, first: Int, last: Int): TicketNeedsAdjudicationDetailsNodeConnection! - positiveFeedbackTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketPositiveFeedbackDetailsNodeConnection! - negativeFeedbackTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketNegativeFeedbackDetailsNodeConnection! - referralTicketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketReferralDetailsNodeConnection! - feedbacks(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackNodeConnection! - adminUrl: String - status: String - role: String - age: Int - sanctionListLastCheck: DateTime - paymentChannels: [BankAccountInfoNode] -} - -type IndividualNodeConnection { - pageInfo: PageInfo! - edges: [IndividualNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type IndividualNodeEdge { - node: IndividualNode - cursor: String! -} - -enum IndividualRdiMergeStatus { - PENDING - MERGED -} - -enum IndividualRelationship { - UNKNOWN - AUNT_UNCLE - BROTHER_SISTER - COUSIN - DAUGHTERINLAW_SONINLAW - GRANDDAUGHER_GRANDSON - GRANDMOTHER_GRANDFATHER - HEAD - MOTHER_FATHER - MOTHERINLAW_FATHERINLAW - NEPHEW_NIECE - NON_BENEFICIARY - OTHER - SISTERINLAW_BROTHERINLAW - SON_DAUGHTER - WIFE_HUSBAND - FOSTER_CHILD - FREE_UNION -} - -type IndividualRoleInHouseholdNode { - id: UUID! - rdiMergeStatus: IndividualRoleInHouseholdRdiMergeStatus! - isRemoved: Boolean! - isOriginal: Boolean! - createdAt: DateTime! - updatedAt: DateTime! - lastSyncAt: DateTime - individual: IndividualNode! - household: HouseholdNode! - role: IndividualRoleInHouseholdRole - isMigrationHandled: Boolean! - migratedAt: DateTime - copiedFrom: IndividualRoleInHouseholdNode - copiedTo: [IndividualRoleInHouseholdNode!]! -} - -enum IndividualRoleInHouseholdRdiMergeStatus { - PENDING - MERGED -} - -enum IndividualRoleInHouseholdRole { - NO_ROLE - ALTERNATE - PRIMARY -} - -enum IndividualSex { - MALE - FEMALE -} - -input IndividualUpdateDataObjectType { - status: String - fullName: String - givenName: String - middleName: String - familyName: String - sex: String - birthDate: Date - estimatedBirthDate: Boolean - maritalStatus: String - phoneNo: String - phoneNoAlternative: String - email: String - relationship: String - disability: String - workStatus: String - enrolledInNutritionProgramme: Boolean - administrationOfRutf: Boolean - pregnant: Boolean - observedDisability: [String] - seeingDisability: String - hearingDisability: String - physicalDisability: String - memoryDisability: String - selfcareDisability: String - commsDisability: String - whoAnswersPhone: String - whoAnswersAltPhone: String - role: String - documents: [IndividualDocumentObjectType] - documentsToRemove: [ID] - documentsToEdit: [EditIndividualDocumentObjectType] - identities: [IndividualIdentityObjectType] - identitiesToRemove: [ID] - identitiesToEdit: [EditIndividualIdentityObjectType] - paymentChannels: [BankTransferObjectType] - paymentChannelsToEdit: [EditBankTransferObjectType] - paymentChannelsToRemove: [ID] - preferredLanguage: String - flexFields: Arg - paymentDeliveryPhoneNo: String - blockchainName: String - walletAddress: String - walletName: String - deliveryMechanismData: [DeliveryMechanismDataObjectType] - deliveryMechanismDataToEdit: [EditDeliveryMechanismDataObjectType] - deliveryMechanismDataToRemove: [ID] -} - -type InvalidPaymentVerificationPlan { - paymentPlan: GenericPaymentPlanNode -} - -input IssueTypeExtrasInput { - householdDataUpdateIssueTypeExtras: HouseholdDataUpdateIssueTypeExtras - individualDataUpdateIssueTypeExtras: IndividualDataUpdateIssueTypeExtras - individualDeleteIssueTypeExtras: IndividualDeleteIssueTypeExtras - householdDeleteIssueTypeExtras: HouseholdDeleteIssueTypeExtras - addIndividualIssueTypeExtras: AddIndividualIssueTypeExtras -} - -type IssueTypesObject { - category: String - label: String - subCategories: [ChoiceObject] -} - -scalar JSONString - -type KoboAssetObject { - id: String - name: String - sector: String - country: String - assetType: String - dateModified: DateTime - deploymentActive: Boolean - hasDeployment: Boolean - xlsLink: String -} - -type KoboAssetObjectConnection { - pageInfo: PageInfo! - edges: [KoboAssetObjectEdge]! - totalCount: Int -} - -type KoboAssetObjectEdge { - node: KoboAssetObject - cursor: String! -} - -type KoboErrorNode { - header: String - message: String -} - -type KoboImportDataNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - status: ImportDataStatus! - businessAreaSlug: String! - file: String - dataType: ImportDataDataType! - numberOfHouseholds: Int - numberOfIndividuals: Int - error: String! - validationErrors: String! - deliveryMechanismsValidationErrors: String! - createdById: UUID - importdataPtr: ImportDataNode! - koboAssetId: String! - onlyActiveSubmissions: Boolean! - pullPictures: Boolean! - koboValidationErrors: [KoboErrorNode] -} - -type LabelNode { - language: String - label: String -} - -type LanguageObject { - english: String - code: String -} - -type LanguageObjectConnection { - pageInfo: PageInfo! - edges: [LanguageObjectEdge]! - totalCount: Int -} - -type LanguageObjectEdge { - node: LanguageObject - cursor: String! -} - -type LockTargetPopulationMutation { - targetPopulation: TargetPopulationNode -} - -enum LogEntryAction { - CREATE - UPDATE - DELETE - SOFT_DELETE -} - -type LogEntryNode implements Node { - id: ID! - contentType: ContentTypeObjectType - objectId: UUID - action: LogEntryAction! - objectRepr: String! - changes: Arg - user: UserNode - businessArea: UserBusinessAreaNode - programs(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! - timestamp: DateTime - isUserGenerated: Boolean -} - -type LogEntryNodeConnection { - pageInfo: PageInfo! - edges: [LogEntryNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type LogEntryNodeEdge { - node: LogEntryNode - cursor: String! -} - -type MarkPaymentAsFailedMutation { - payment: PaymentNode -} - -type MarkPaymentRecordAsFailedMutation { - paymentRecord: PaymentRecordNode -} - -type MergeRegistrationDataImportMutation { - registrationDataImport: RegistrationDataImportNode -} - -enum MessageSamplingType { - FULL_LIST - RANDOM -} - -type Mutations { - createAccountabilityCommunicationMessage(input: CreateAccountabilityCommunicationMessageInput!): CreateCommunicationMessageMutation - createFeedback(input: CreateFeedbackInput!): CreateFeedbackMutation - updateFeedback(input: UpdateFeedbackInput!): UpdateFeedbackMutation - createFeedbackMessage(input: CreateFeedbackMessageInput!): CreateFeedbackMessageMutation - createSurvey(input: CreateSurveyInput!): CreateSurveyMutation - exportSurveySample(surveyId: ID!): ExportSurveySampleMutationMutation - createReport(reportData: CreateReportInput!): CreateReport - restartCreateReport(reportData: RestartCreateReportInput!): RestartCreateReport - createDashboardReport(reportData: CreateDashboardReportInput!): CreateDashboardReport - createGrievanceTicket(input: CreateGrievanceTicketInput!): CreateGrievanceTicketMutation - updateGrievanceTicket(input: UpdateGrievanceTicketInput!, version: BigInt): UpdateGrievanceTicketMutation - grievanceStatusChange(grievanceTicketId: ID, status: Int, version: BigInt): GrievanceStatusChangeMutation - createTicketNote(noteInput: CreateTicketNoteInput!, version: BigInt): CreateTicketNoteMutation - approveIndividualDataChange(approvedDeliveryMechanismDataToCreate: [Int], approvedDeliveryMechanismDataToEdit: [Int], approvedDeliveryMechanismDataToRemove: [Int], approvedDocumentsToCreate: [Int], approvedDocumentsToEdit: [Int], approvedDocumentsToRemove: [Int], approvedIdentitiesToCreate: [Int], approvedIdentitiesToEdit: [Int], approvedIdentitiesToRemove: [Int], approvedPaymentChannelsToCreate: [Int], approvedPaymentChannelsToEdit: [Int], approvedPaymentChannelsToRemove: [Int], flexFieldsApproveData: JSONString, grievanceTicketId: ID!, individualApproveData: JSONString, version: BigInt): IndividualDataChangeApproveMutation - approveHouseholdDataChange(flexFieldsApproveData: JSONString, grievanceTicketId: ID!, householdApproveData: JSONString, version: BigInt): HouseholdDataChangeApproveMutation - approveAddIndividual(approveStatus: Boolean!, grievanceTicketId: ID!, version: BigInt): SimpleApproveMutation - approveDeleteIndividual(approveStatus: Boolean!, grievanceTicketId: ID!, version: BigInt): SimpleApproveMutation - approveDeleteHousehold(approveStatus: Boolean!, grievanceTicketId: ID!, reasonHhId: String, version: BigInt): DeleteHouseholdApproveMutation - approveSystemFlagging(approveStatus: Boolean!, grievanceTicketId: ID!, version: BigInt): SimpleApproveMutation - approveNeedsAdjudication(clearIndividualIds: [ID], distinctIndividualIds: [ID], duplicateIndividualIds: [ID], grievanceTicketId: ID!, selectedIndividualId: ID, version: BigInt): NeedsAdjudicationApproveMutation - approvePaymentDetails(approveStatus: Boolean!, grievanceTicketId: ID!, version: BigInt): PaymentDetailsApproveMutation - reassignRole(grievanceTicketId: ID!, householdId: ID!, householdVersion: BigInt, individualId: ID!, individualVersion: BigInt, newIndividualId: ID, role: String!, version: BigInt): ReassignRoleMutation - bulkUpdateGrievanceAssignee(assignedTo: String!, businessAreaSlug: String!, grievanceTicketIds: [ID]!): BulkUpdateGrievanceTicketsAssigneesMutation - bulkUpdateGrievancePriority(businessAreaSlug: String!, grievanceTicketIds: [ID]!, priority: Int!): BulkUpdateGrievanceTicketsPriorityMutation - bulkUpdateGrievanceUrgency(businessAreaSlug: String!, grievanceTicketIds: [ID]!, urgency: Int!): BulkUpdateGrievanceTicketsUrgencyMutation - bulkGrievanceAddNote(businessAreaSlug: String!, grievanceTicketIds: [ID]!, note: String!): BulkGrievanceAddNoteMutation - createPaymentVerificationPlan(input: CreatePaymentVerificationInput!, version: BigInt): CreateVerificationPlanMutation - editPaymentVerificationPlan(input: EditPaymentVerificationInput!, version: BigInt): EditPaymentVerificationMutation - exportXlsxPaymentVerificationPlanFile(paymentVerificationPlanId: ID!): ExportXlsxPaymentVerificationPlanFile - importXlsxPaymentVerificationPlanFile(file: Upload!, paymentVerificationPlanId: ID!): ImportXlsxPaymentVerificationPlanFile - activatePaymentVerificationPlan(paymentVerificationPlanId: ID!, version: BigInt): ActivatePaymentVerificationPlan - finishPaymentVerificationPlan(paymentVerificationPlanId: ID!, version: BigInt): FinishPaymentVerificationPlan - discardPaymentVerificationPlan(paymentVerificationPlanId: ID!, version: BigInt): DiscardPaymentVerificationPlan - invalidPaymentVerificationPlan(paymentVerificationPlanId: ID!, version: BigInt): InvalidPaymentVerificationPlan - deletePaymentVerificationPlan(paymentVerificationPlanId: ID!, version: BigInt): DeletePaymentVerificationPlan - updatePaymentVerificationStatusAndReceivedAmount(paymentVerificationId: ID!, receivedAmount: Decimal!, status: PaymentVerificationStatusForUpdate, version: BigInt): UpdatePaymentVerificationStatusAndReceivedAmount - markPaymentRecordAsFailed(paymentRecordId: ID!): MarkPaymentRecordAsFailedMutation - revertMarkPaymentRecordAsFailed(deliveredQuantity: Decimal!, deliveryDate: Date!, paymentRecordId: ID!): RevertMarkPaymentRecordAsFailedMutation - markPaymentAsFailed(paymentId: ID!): MarkPaymentAsFailedMutation - revertMarkPaymentAsFailed(deliveredQuantity: Decimal!, deliveryDate: Date!, paymentId: ID!): RevertMarkPaymentAsFailedMutation - updatePaymentVerificationReceivedAndReceivedAmount(paymentVerificationId: ID!, received: Boolean!, receivedAmount: Decimal!, version: BigInt): UpdatePaymentVerificationReceivedAndReceivedAmount - actionPaymentPlanMutation(input: ActionPaymentPlanInput!): ActionPaymentPlanMutation - createPaymentPlan(input: CreatePaymentPlanInput!): CreatePaymentPlanMutation - createFollowUpPaymentPlan(dispersionEndDate: Date!, dispersionStartDate: Date!, paymentPlanId: ID!): CreateFollowUpPaymentPlanMutation - updatePaymentPlan(input: UpdatePaymentPlanInput!): UpdatePaymentPlanMutation - deletePaymentPlan(paymentPlanId: ID!): DeletePaymentPlanMutation - chooseDeliveryMechanismsForPaymentPlan(input: ChooseDeliveryMechanismsForPaymentPlanInput!): ChooseDeliveryMechanismsForPaymentPlanMutation - assignFspToDeliveryMechanism(input: AssignFspToDeliveryMechanismInput!): AssignFspToDeliveryMechanismMutation - splitPaymentPlan(paymentPlanId: ID!, paymentsNo: Int, splitType: String!): SplitPaymentPlanMutation - exportXlsxPaymentPlanPaymentList(paymentPlanId: ID!): ExportXLSXPaymentPlanPaymentListMutation - exportXlsxPaymentPlanPaymentListPerFsp(paymentPlanId: ID!): ExportXLSXPaymentPlanPaymentListPerFSPMutation - importXlsxPaymentPlanPaymentList(file: Upload!, paymentPlanId: ID!): ImportXLSXPaymentPlanPaymentListMutation - importXlsxPaymentPlanPaymentListPerFsp(file: Upload!, paymentPlanId: ID!): ImportXLSXPaymentPlanPaymentListPerFSPMutation - setSteficonRuleOnPaymentPlanPaymentList(paymentPlanId: ID!, steficonRuleId: ID!): SetSteficonRuleOnPaymentPlanPaymentListMutation - excludeHouseholds(excludedHouseholdsIds: [String]!, exclusionReason: String, paymentPlanId: ID!): ExcludeHouseholdsMutation - exportPdfPaymentPlanSummary(paymentPlanId: ID!): ExportPDFPaymentPlanSummaryMutation - createTargetPopulation(input: CreateTargetPopulationInput!): CreateTargetPopulationMutation - updateTargetPopulation(input: UpdateTargetPopulationInput!, version: BigInt): UpdateTargetPopulationMutation - copyTargetPopulation(input: CopyTargetPopulationMutationInput!): CopyTargetPopulationMutationPayload - deleteTargetPopulation(input: DeleteTargetPopulationMutationInput!): DeleteTargetPopulationMutationPayload - lockTargetPopulation(id: ID!, version: BigInt): LockTargetPopulationMutation - unlockTargetPopulation(id: ID!, version: BigInt): UnlockTargetPopulationMutation - finalizeTargetPopulation(id: ID!, version: BigInt): FinalizeTargetPopulationMutation - setSteficonRuleOnTargetPopulation(input: SetSteficonRuleOnTargetPopulationMutationInput!): SetSteficonRuleOnTargetPopulationMutationPayload - targetPopulationRebuild(id: ID!): RebuildTargetPopulationMutation - createProgram(programData: CreateProgramInput!): CreateProgram - updateProgram(programData: UpdateProgramInput, version: BigInt): UpdateProgram - deleteProgram(programId: String!): DeleteProgram - copyProgram(programData: CopyProgramInput!): CopyProgram - uploadImportDataXlsxFileAsync(businessAreaSlug: String!, file: Upload!): UploadImportDataXLSXFileAsync - deleteRegistrationDataImport(registrationDataImportId: String!): DeleteRegistrationDataImport - registrationXlsxImport(registrationDataImportData: RegistrationXlsxImportMutationInput!): RegistrationXlsxImportMutation - registrationProgramPopulationImport(registrationDataImportData: RegistrationProgramPopulationImportMutationInput!): RegistrationProgramPopulationImportMutation - registrationKoboImport(registrationDataImportData: RegistrationKoboImportMutationInput!): RegistrationKoboImportMutation - saveKoboImportDataAsync(businessAreaSlug: String!, onlyActiveSubmissions: Boolean!, pullPictures: Boolean!, uid: Upload!): SaveKoboProjectImportDataAsync - mergeRegistrationDataImport(id: ID!, version: BigInt): MergeRegistrationDataImportMutation - refuseRegistrationDataImport(id: ID!, refuseReason: String, version: BigInt): RefuseRegistrationDataImportMutation - rerunDedupe(registrationDataImportId: ID!, version: BigInt): RegistrationDeduplicationMutation - eraseRegistrationDataImport(id: ID!, version: BigInt): EraseRegistrationDataImportMutation - checkAgainstSanctionList(file: Upload!): CheckAgainstSanctionListMutation -} - -type NeedsAdjudicationApproveMutation { - grievanceTicket: GrievanceTicketNode -} - -input NegativeFeedbackTicketExtras { - household: ID - individual: ID -} - -interface Node { - id: ID! -} - -input PDUFieldInput { - id: String - label: String - pduData: PeriodicFieldDataInput -} - -type PDUSubtypeChoiceObject { - value: String - displayName: String -} - -type PageInfo { - hasNextPage: Boolean! - hasPreviousPage: Boolean! - startCursor: String - endCursor: String -} - -type PageInfoNode { - startCursor: String - endCursor: String - hasNextPage: Boolean - hasPreviousPage: Boolean -} - -type PaginatedCashPlanAndPaymentPlanNode { - pageInfo: PageInfoNode - edges: [CashPlanAndPaymentPlanEdges] - totalCount: Int -} - -type PaginatedPaymentRecordsAndPaymentsNode { - pageInfo: PageInfoNode - edges: [PaymentRecordsAndPaymentsEdges] - totalCount: Int -} - -type PartnerNode { - id: ID! - allowedBusinessAreas(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID): UserBusinessAreaNodeConnection! - name: String - parent: PartnerNode - isUn: Boolean! - permissions: JSONString! - lft: Int! - rght: Int! - treeId: Int! - level: Int! - businessAreaPartnerThrough: [PartnerRoleNode!]! - businessAreas(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID): UserBusinessAreaNodeConnection! - partnerSet: [PartnerNode!]! - userSet(offset: Int, before: String, after: String, first: Int, last: Int): UserNodeConnection! - individualIdentities(offset: Int, before: String, after: String, first: Int, last: Int): IndividualIdentityNodeConnection! - grievanceticketSet(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! - programs(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! - areas: [AreaNode] - areaAccess: String -} - -type PartnerRoleNode { - createdAt: DateTime! - updatedAt: DateTime! - businessArea: UserBusinessAreaNode! - partner: PartnerNode! - roles: [RoleNode!]! -} - -type PartnerType { - id: ID! - allowedBusinessAreas(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID): UserBusinessAreaNodeConnection! - name: String! - parent: PartnerNode - isUn: Boolean! - permissions: JSONString! - lft: Int! - rght: Int! - treeId: Int! - level: Int! - businessAreaPartnerThrough: [PartnerRoleNode!]! - businessAreas(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID): UserBusinessAreaNodeConnection! - partnerSet: [PartnerNode!]! - userSet(offset: Int, before: String, after: String, first: Int, last: Int): UserNodeConnection! - individualIdentities(offset: Int, before: String, after: String, first: Int, last: Int): IndividualIdentityNodeConnection! - grievanceticketSet(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! - programs(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! -} - -type PaymentConflictDataNode { - paymentPlanId: String - paymentPlanUnicefId: String - paymentPlanStartDate: String - paymentPlanEndDate: String - paymentPlanStatus: String - paymentId: String - paymentUnicefId: String -} - -enum PaymentDeliveryTypeChoice { - CARDLESS_CASH_WITHDRAWAL - CASH - CASH_BY_FSP - CHEQUE - DEPOSIT_TO_CARD - MOBILE_MONEY - PRE_PAID_CARD - REFERRAL - TRANSFER - TRANSFER_TO_ACCOUNT - VOUCHER - ATM_CARD - CASH_OVER_THE_COUNTER - TRANSFER_TO_DIGITAL_WALLET -} - -type PaymentDetailsApproveMutation { - grievanceTicket: GrievanceTicketNode -} - -type PaymentHouseholdSnapshotNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - snapshotData: JSONString! - householdId: UUID! - payment: PaymentNode! -} - -type PaymentNode implements Node { - isRemoved: Boolean! - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - unicefId: String - signatureHash: String! - businessArea: UserBusinessAreaNode! - status: PaymentStatus! - statusDate: DateTime! - household: HouseholdNode! - headOfHousehold: IndividualNode - deliveryTypeChoice: PaymentDeliveryTypeChoice - deliveryType: DeliveryMechanismNode - currency: String! - entitlementQuantity: Float - entitlementQuantityUsd: Float - deliveredQuantity: Float - deliveredQuantityUsd: Float - deliveryDate: DateTime - transactionReferenceId: String - transactionStatusBlockchainLink: String - parent: PaymentPlanNode! - conflicted: Boolean! - excluded: Boolean! - entitlementDate: DateTime - financialServiceProvider: FinancialServiceProviderNode - collector: IndividualNode! - sourcePayment: PaymentNode - isFollowUp: Boolean! - reasonForUnsuccessfulPayment: String - program: ProgramNode - orderNumber: Int - tokenNumber: Int - additionalCollectorName: String - additionalDocumentType: String - additionalDocumentNumber: String - fspAuthCode: String - followUps(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! - householdSnapshot: PaymentHouseholdSnapshotNode - adminUrl: String - paymentPlanHardConflicted: Boolean - paymentPlanHardConflictedData: [PaymentConflictDataNode] - paymentPlanSoftConflicted: Boolean - paymentPlanSoftConflictedData: [PaymentConflictDataNode] - fullName: String - targetPopulation: TargetPopulationNode - verification: PaymentVerificationNode - distributionModality: String - serviceProvider: FinancialServiceProviderNode - debitCardNumber: String - debitCardIssuer: String - totalPersonsCovered: Int - snapshotCollectorFullName: String - snapshotCollectorDeliveryPhoneNo: String - snapshotCollectorBankName: String - snapshotCollectorBankAccountNumber: String - snapshotCollectorDebitCardNumber: String -} - -type PaymentNodeConnection { - pageInfo: PageInfo! - edges: [PaymentNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type PaymentNodeEdge { - node: PaymentNode - cursor: String! -} - -enum PaymentPlanBackgroundActionStatus { - RULE_ENGINE_RUN - RULE_ENGINE_ERROR - XLSX_EXPORTING - XLSX_EXPORT_ERROR - XLSX_IMPORT_ERROR - XLSX_IMPORTING_ENTITLEMENTS - XLSX_IMPORTING_RECONCILIATION - EXCLUDE_BENEFICIARIES - EXCLUDE_BENEFICIARIES_ERROR - SEND_TO_PAYMENT_GATEWAY - SEND_TO_PAYMENT_GATEWAY_ERROR -} - -enum PaymentPlanCurrency { - A_ - AED - AFN - ALL - AMD - ANG - AOA - ARS - AUD - AWG - AZN - BAM - BBD - BDT - BGN - BHD - BIF - BMD - BND - BOB - BOV - BRL - BSD - BTN - BWP - BYN - BZD - CAD - CDF - CHF - CLP - CNY - COP - CRC - CUC - CUP - CVE - CZK - DJF - DKK - DOP - DZD - EGP - ERN - ETB - EUR - FJD - FKP - GBP - GEL - GHS - GIP - GMD - GNF - GTQ - GYD - HKD - HNL - HRK - HTG - HUF - IDR - ILS - INR - IQD - IRR - ISK - JMD - JOD - JPY - KES - KGS - KHR - KMF - KPW - KRW - KWD - KYD - KZT - LAK - LBP - LKR - LRD - LSL - LYD - MAD - MDL - MGA - MKD - MMK - MNT - MOP - MRU - MUR - MVR - MWK - MXN - MYR - MZN - NAD - NGN - NIO - NOK - NPR - NZD - OMR - PAB - PEN - PGK - PHP - PKR - PLN - PYG - QAR - RON - RSD - RUB - RWF - SAR - SBD - SCR - SDG - SEK - SGD - SHP - SLL - SOS - SRD - SSP - STN - SVC - SYP - SZL - THB - TJS - TMT - TND - TOP - TRY - TTD - TWD - TZS - UAH - UGX - USD - UYU - UYW - UZS - VES - VND - VUV - WST - XAF - XAG - XAU - XCD - XOF - XPF - YER - ZAR - ZMW - ZWL - USDC -} - -type PaymentPlanNode implements Node { - isRemoved: Boolean! - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - version: BigInt! - unicefId: String - businessArea: UserBusinessAreaNode! - statusDate: DateTime! - startDate: Date - endDate: Date - program: ProgramNode! - exchangeRate: Float - totalEntitledQuantity: Float - totalEntitledQuantityUsd: Float - totalEntitledQuantityRevised: Float - totalEntitledQuantityRevisedUsd: Float - totalDeliveredQuantity: Float - totalDeliveredQuantityUsd: Float - totalUndeliveredQuantity: Float - totalUndeliveredQuantityUsd: Float - programCycle: ProgramCycleNode - createdBy: UserNode! - status: PaymentPlanStatus! - backgroundActionStatus: PaymentPlanBackgroundActionStatus - targetPopulation: TargetPopulationNode! - currency: PaymentPlanCurrency! - dispersionStartDate: Date - dispersionEndDate: Date - femaleChildrenCount: Int! - maleChildrenCount: Int! - femaleAdultsCount: Int! - maleAdultsCount: Int! - totalHouseholdsCount: Int! - totalIndividualsCount: Int! - importedFileDate: DateTime - steficonRule: RuleCommitNode - steficonAppliedDate: DateTime - sourcePaymentPlan: PaymentPlanNode - isFollowUp: Boolean! - exclusionReason: String! - excludeHouseholdError: String! - name: String - 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! - approvalProcess(offset: Int, before: String, after: String, first: Int, last: Int): ApprovalProcessNodeConnection! - adminUrl: String - currencyName: String - hasPaymentListExportFile: Boolean - hasFspDeliveryMechanismXlsxTemplate: Boolean - importedFileName: String - paymentsConflictsCount: Int - volumeByDeliveryMechanism: [VolumeByDeliveryMechanismNode] - splitChoices: [ChoiceObject] - verificationPlans(offset: Int, before: String, after: String, first: Int, last: Int, programId: String): PaymentVerificationPlanNodeConnection - paymentVerificationSummary: PaymentVerificationSummaryNode - bankReconciliationSuccess: Int - bankReconciliationError: Int - canCreatePaymentVerificationPlan: Boolean - availablePaymentRecordsCount: Int - reconciliationSummary: ReconciliationSummaryNode - excludedHouseholds: [HouseholdNode] - excludedIndividuals: [IndividualNode] - canCreateFollowUp: Boolean - totalWithdrawnHouseholdsCount: Int - unsuccessfulPaymentsCount: Int - canSendToPaymentGateway: Boolean - canSplit: Boolean -} - -type PaymentPlanNodeConnection { - pageInfo: PageInfo! - edges: [PaymentPlanNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type PaymentPlanNodeEdge { - node: PaymentPlanNode - cursor: String! -} - -enum PaymentPlanStatus { - PREPARING - OPEN - LOCKED - LOCKED_FSP - IN_APPROVAL - IN_AUTHORIZATION - IN_REVIEW - ACCEPTED - FINISHED -} - -type PaymentRecordAndPaymentNode { - objType: String - id: String - caId: String - status: String - fullName: String - parent: CashPlanAndPaymentPlanNode - entitlementQuantity: Float - deliveredQuantity: Float - deliveredQuantityUsd: Float - currency: String - deliveryDate: String - verification: PaymentVerificationNode -} - -enum PaymentRecordDeliveryTypeChoice { - CARDLESS_CASH_WITHDRAWAL - CASH - CASH_BY_FSP - CHEQUE - DEPOSIT_TO_CARD - MOBILE_MONEY - PRE_PAID_CARD - REFERRAL - TRANSFER - TRANSFER_TO_ACCOUNT - VOUCHER - ATM_CARD - CASH_OVER_THE_COUNTER - TRANSFER_TO_DIGITAL_WALLET -} - -enum PaymentRecordEntitlementCardStatus { - ACTIVE - INACTIVE -} - -type PaymentRecordNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - version: BigInt! - businessArea: UserBusinessAreaNode! - status: PaymentRecordStatus! - statusDate: DateTime! - household: HouseholdNode! - headOfHousehold: IndividualNode - deliveryTypeChoice: PaymentRecordDeliveryTypeChoice - deliveryType: DeliveryMechanismNode - currency: String! - entitlementQuantity: Float - entitlementQuantityUsd: Float - deliveredQuantity: Float - deliveredQuantityUsd: Float - deliveryDate: DateTime - transactionReferenceId: String - transactionStatusBlockchainLink: String - caId: String - caHashId: UUID - parent: CashPlanNode - fullName: String! - totalPersonsCovered: Int! - distributionModality: String! - targetPopulation: TargetPopulationNode! - targetPopulationCashAssistId: String! - entitlementCardNumber: String - entitlementCardStatus: PaymentRecordEntitlementCardStatus - entitlementCardIssueDate: Date - visionId: String - registrationCaId: String - serviceProvider: ServiceProviderNode! - adminUrl: String - verification: PaymentVerificationNode - unicefId: String -} - -type PaymentRecordNodeConnection { - pageInfo: PageInfo! - edges: [PaymentRecordNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type PaymentRecordNodeEdge { - node: PaymentRecordNode - cursor: String! -} - -enum PaymentRecordStatus { - DISTRIBUTION_SUCCESSFUL - NOT_DISTRIBUTED - TRANSACTION_SUCCESSFUL - TRANSACTION_ERRONEOUS - FORCE_FAILED - PARTIALLY_DISTRIBUTED - PENDING - SENT_TO_PAYMENT_GATEWAY - SENT_TO_FSP - MANUALLY_CANCELLED -} - -type PaymentRecordsAndPaymentsEdges { - cursor: String - node: PaymentRecordAndPaymentNode -} - -enum PaymentStatus { - DISTRIBUTION_SUCCESSFUL - NOT_DISTRIBUTED - TRANSACTION_SUCCESSFUL - TRANSACTION_ERRONEOUS - FORCE_FAILED - PARTIALLY_DISTRIBUTED - PENDING - SENT_TO_PAYMENT_GATEWAY - SENT_TO_FSP - MANUALLY_CANCELLED -} - -type PaymentVerificationLogEntryNode implements Node { - id: ID! - contentType: ContentTypeObjectType - objectId: UUID - action: LogEntryAction! - objectRepr: String! - changes: Arg - user: UserNode - businessArea: UserBusinessAreaNode - programs(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! - timestamp: DateTime - isUserGenerated: Boolean - contentObject: PaymentVerificationPlanNode -} - -type PaymentVerificationLogEntryNodeConnection { - pageInfo: PageInfo! - edges: [PaymentVerificationLogEntryNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type PaymentVerificationLogEntryNodeEdge { - node: PaymentVerificationLogEntryNode - cursor: String! -} - -type PaymentVerificationNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - version: BigInt! - paymentVerificationPlan: PaymentVerificationPlanNode! - paymentContentType: ContentTypeObjectType! - paymentObjectId: UUID! - status: PaymentVerificationStatus! - statusDate: DateTime - receivedAmount: Float - sentToRapidPro: Boolean! - ticketDetails(offset: Int, before: String, after: String, first: Int, last: Int): TicketPaymentVerificationDetailsNodeConnection! - ticketDetail(offset: Int, before: String, after: String, first: Int, last: Int): TicketPaymentVerificationDetailsNodeConnection! - adminUrl: String - isManuallyEditable: Boolean - payment: GenericPaymentNode -} - -type PaymentVerificationNodeConnection { - pageInfo: PageInfo! - edges: [PaymentVerificationNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type PaymentVerificationNodeEdge { - node: PaymentVerificationNode - cursor: String! -} - -type PaymentVerificationPlanNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - version: BigInt! - unicefId: String - status: PaymentVerificationPlanStatus! - paymentPlanContentType: ContentTypeObjectType! - paymentPlanObjectId: UUID! - sampling: PaymentVerificationPlanSampling! - verificationChannel: PaymentVerificationPlanVerificationChannel! - sampleSize: Int - respondedCount: Int - receivedCount: Int - notReceivedCount: Int - receivedWithProblemsCount: Int - confidenceInterval: Float - marginOfError: Float - rapidProFlowId: String! - rapidProFlowStartUuids: [String!]! - ageFilter: AgeFilterObject - excludedAdminAreasFilter: [String] - sexFilter: String - activationDate: DateTime - completionDate: DateTime - xlsxFileExporting: Boolean! - xlsxFileImported: Boolean! - error: String - paymentRecordVerifications(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationNodeConnection! - adminUrl: String - xlsxFileWasDownloaded: Boolean - hasXlsxFile: Boolean - paymentPlan: PaymentPlanNode -} - -type PaymentVerificationPlanNodeConnection { - pageInfo: PageInfo! - edges: [PaymentVerificationPlanNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type PaymentVerificationPlanNodeEdge { - node: PaymentVerificationPlanNode - cursor: String! -} - -enum PaymentVerificationPlanSampling { - FULL_LIST - RANDOM -} - -enum PaymentVerificationPlanStatus { - ACTIVE - FINISHED - PENDING - INVALID - RAPID_PRO_ERROR -} - -enum PaymentVerificationPlanVerificationChannel { - MANUAL - RAPIDPRO - XLSX -} - -enum PaymentVerificationStatus { - NOT_RECEIVED - PENDING - RECEIVED - RECEIVED_WITH_ISSUES -} - -enum PaymentVerificationStatusForUpdate { - NOT_RECEIVED - PENDING - RECEIVED - RECEIVED_WITH_ISSUES -} - -type PaymentVerificationSummaryNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - status: PaymentVerificationSummaryStatus! - activationDate: DateTime - completionDate: DateTime - paymentPlanContentType: ContentTypeObjectType! - paymentPlanObjectId: UUID! -} - -type PaymentVerificationSummaryNodeConnection { - pageInfo: PageInfo! - edges: [PaymentVerificationSummaryNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type PaymentVerificationSummaryNodeEdge { - node: PaymentVerificationSummaryNode - cursor: String! -} - -enum PaymentVerificationSummaryStatus { - ACTIVE - FINISHED - PENDING -} - -input PeriodicFieldDataInput { - subtype: String - numberOfRounds: Int - roundsNames: [String] -} - -type PeriodicFieldDataNode { - id: ID! - subtype: PeriodicFieldDataSubtype! - numberOfRounds: Int! - roundsNames: [String!]! -} - -enum PeriodicFieldDataSubtype { - DATE - DECIMAL - STRING - BOOL -} - -type PeriodicFieldNode implements Node { - name: String! - pduData: PeriodicFieldDataNode - label: JSONString! - id: ID! -} - -input PositiveFeedbackTicketExtras { - household: ID - individual: ID -} - -type ProgramCycleNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - version: BigInt! - title: String - status: ProgramCycleStatus! - startDate: Date! - endDate: Date - program: ProgramNode! - createdBy: UserNode - paymentPlans(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanNodeConnection! - targetPopulations(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection! - totalDeliveredQuantityUsd: Float - totalEntitledQuantityUsd: Float - totalUndeliveredQuantityUsd: Float -} - -type ProgramCycleNodeConnection { - pageInfo: PageInfo! - edges: [ProgramCycleNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type ProgramCycleNodeEdge { - node: ProgramCycleNode - cursor: String! -} - -enum ProgramCycleStatus { - DRAFT - ACTIVE - FINISHED -} - -enum ProgramFrequencyOfPayments { - ONE_OFF - REGULAR -} - -type ProgramNode implements Node { - isRemoved: Boolean! - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - lastSyncAt: DateTime - version: BigInt! - name: String! - status: ProgramStatus! - startDate: Date! - endDate: Date - description: String! - caId: String - caHashId: String - adminAreas(offset: Int, before: String, after: String, first: Int, last: Int, name: String): AreaNodeConnection! - businessArea: UserBusinessAreaNode! - budget: Decimal - frequencyOfPayments: ProgramFrequencyOfPayments! - sector: ProgramSector! - scope: ProgramScope - cashPlus: Boolean! - populationGoal: Int! - administrativeAreasOfImplementation: String! - dataCollectingType: DataCollectingTypeNode - isVisible: Boolean! - householdCount: Int! - individualCount: Int! - programmeCode: String - partnerAccess: ProgramPartnerAccess! - partners: [PartnerNode] - biometricDeduplicationEnabled: Boolean! - deduplicationSetId: UUID - 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! - individuals(offset: Int, before: String, after: String, first: Int, last: Int): IndividualNodeConnection! - registrationImports(offset: Int, before: String, after: String, first: Int, last: Int): RegistrationDataImportNodeConnection! - deduplicationEngineSimilarityPairs(offset: Int, before: String, after: String, first: Int, last: Int): DeduplicationEngineSimilarityPairNodeConnection! - paymentplanSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanNodeConnection! - cashplanSet(offset: Int, before: String, after: String, first: Int, last: Int): CashPlanNodeConnection! - paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! - grievanceTickets(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! - targetpopulationSet(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection! - cycles(offset: Int, before: String, after: String, first: Int, last: Int, search: String, status: [String], startDate: Date, endDate: Date, totalDeliveredQuantityUsdFrom: Float, totalDeliveredQuantityUsdTo: Float, orderBy: String): ProgramCycleNodeConnection - reports(offset: Int, before: String, after: String, first: Int, last: Int): ReportNodeConnection! - activityLogs(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationLogEntryNodeConnection! - messages(offset: Int, before: String, after: String, first: Int, last: Int): CommunicationMessageNodeConnection! - feedbackSet(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackNodeConnection! - surveys(offset: Int, before: String, after: String, first: Int, last: Int): SurveyNodeConnection! - adminUrl: String - totalEntitledQuantity: Decimal - totalDeliveredQuantity: Decimal - totalUndeliveredQuantity: Decimal - totalNumberOfHouseholds: Int - totalNumberOfHouseholdsWithTpInProgram: Int - isSocialWorkerProgram: Boolean - targetPopulationsCount: Int - canFinish: Boolean -} - -type ProgramNodeConnection { - pageInfo: PageInfo! - edges: [ProgramNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type ProgramNodeEdge { - node: ProgramNode - cursor: String! -} - -enum ProgramPartnerAccess { - ALL_PARTNERS_ACCESS - NONE_PARTNERS_ACCESS - SELECTED_PARTNERS_ACCESS -} - -input ProgramPartnerThroughInput { - partner: String - areas: [String] - areaAccess: String -} - -enum ProgramScope { - FOR_PARTNERS - UNICEF -} - -enum ProgramSector { - CHILD_PROTECTION - EDUCATION - HEALTH - MULTI_PURPOSE - NUTRITION - SOCIAL_POLICY - WASH -} - -enum ProgramStatus { - ACTIVE - DRAFT - FINISHED -} - -type Query { - accountabilityCommunicationMessage(id: ID!): CommunicationMessageNode - allAccountabilityCommunicationMessages(offset: Int, before: String, after: String, first: Int, last: Int, numberOfRecipients: Int, numberOfRecipients_Gte: Int, numberOfRecipients_Lte: Int, targetPopulation: ID, createdBy: ID, program: String, createdAtRange: String, title: String, body: String, samplingType: String, orderBy: String): CommunicationMessageNodeConnection - allAccountabilityCommunicationMessageRecipients(offset: Int, before: String, after: String, first: Int, last: Int, messageId: String!, recipientId: String, fullName: String, phoneNo: String, sex: String, orderBy: String): CommunicationMessageRecipientMapNodeConnection - accountabilityCommunicationMessageSampleSize(input: GetAccountabilityCommunicationMessageSampleSizeInput): GetCommunicationMessageSampleSizeNode - feedback(id: ID!): FeedbackNode - allFeedbacks(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String, issueType: String, createdAtRange: String, createdBy: String, feedbackId: String, isActiveProgram: String, program: String, orderBy: String): FeedbackNodeConnection - feedbackIssueTypeChoices: [ChoiceObject] - survey(id: ID!): SurveyNode - allSurveys(offset: Int, before: String, after: String, first: Int, last: Int, program: ID, targetPopulation: ID, businessArea: String, createdAtRange: String, search: String, createdBy: String, orderBy: String): SurveyNodeConnection - recipients(offset: Int, before: String, after: String, first: Int, last: Int, survey: String!, orderBy: String): RecipientNodeConnection - accountabilitySampleSize(input: AccountabilitySampleSizeInput): AccountabilitySampleSizeNode - surveyCategoryChoices: [ChoiceObject] - surveyAvailableFlows: [RapidProFlowNode] - adminArea(id: ID!): AreaNode - allAdminAreas(offset: Int, before: String, after: String, first: Int, last: Int, name: String, name_Istartswith: String, businessArea: String, level: Int, parentId: String): AreaNodeConnection - allAreasTree(businessArea: String!): [AreaTreeNode] - allLogEntries(offset: Int, before: String, after: String, first: Int, last: Int, objectId: UUID, user: ID, businessArea: String!, search: String, module: String, userId: String, programId: String): LogEntryNodeConnection - logEntryActionChoices: [ChoiceObject] - report(id: ID!): ReportNode - allReports(offset: Int, before: String, after: String, first: Int, last: Int, createdBy: ID, reportType: [String], status: [String], businessArea: String!, createdFrom: DateTime, createdTo: DateTime, orderBy: String): ReportNodeConnection - reportTypesChoices: [ChoiceObject] - reportStatusChoices: [ChoiceObject] - dashboardReportTypesChoices(businessAreaSlug: String!): [ChoiceObject] - dashboardYearsChoices(businessAreaSlug: String!): [String] - sanctionListIndividual(id: ID!): SanctionListIndividualNode - allSanctionListIndividuals(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID, fullName: String, fullName_Startswith: String, referenceNumber: String, orderBy: String): SanctionListIndividualNodeConnection - ticketsByType(businessAreaSlug: String!): TicketByType - ticketsByCategory(businessAreaSlug: String!): ChartDatasetNode - ticketsByStatus(businessAreaSlug: String!): ChartDatasetNode - ticketsByLocationAndCategory(businessAreaSlug: String!): ChartDetailedDatasetsNode - grievanceTicket(id: ID!): GrievanceTicketNode - allGrievanceTicket(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID, id_Startswith: UUID, category: String, area: String, area_Startswith: String, assignedTo: ID, registrationDataImport: ID, admin2: ID, createdBy: ID, businessArea: String!, search: String, documentType: String, documentNumber: String, status: [String], fsp: String, cashPlan: String, createdAtRange: String, permissions: [String], issueType: String, scoreMin: String, scoreMax: String, household: String, preferredLanguage: String, priority: String, urgency: String, grievanceType: String, grievanceStatus: String, totalDays: Int, program: String, isActiveProgram: Boolean, isCrossArea: Boolean, admin1: ID, orderBy: String): GrievanceTicketNodeConnection - crossAreaFilterAvailable: Boolean - existingGrievanceTickets(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID, businessArea: String!, category: String, issueType: String, household: ID, individual: ID, paymentRecord: [ID], permissions: [String], orderBy: String): GrievanceTicketNodeConnection - allTicketNotes(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID, ticket: UUID!): TicketNoteNodeConnection - chartGrievances(businessAreaSlug: String!, year: Int!, administrativeArea: String): ChartGrievanceTicketsNode - allAddIndividualsFieldsAttributes: [FieldAttributeNode] - allEditHouseholdFieldsAttributes: [FieldAttributeNode] - grievanceTicketStatusChoices: [ChoiceObject] - grievanceTicketCategoryChoices: [ChoiceObject] - grievanceTicketManualCategoryChoices: [ChoiceObject] - grievanceTicketSystemCategoryChoices: [ChoiceObject] - grievanceTicketIssueTypeChoices: [IssueTypesObject] - grievanceTicketPriorityChoices: [ChoiceObjectInt] - grievanceTicketUrgencyChoices: [ChoiceObjectInt] - allSteficonRules(offset: Int, before: String, after: String, first: Int, last: Int, enabled: Boolean, deprecated: Boolean, type: String!): SteficonRuleNodeConnection - payment(id: ID!): PaymentNode - allPayments(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, paymentPlanId: String!, programId: String, orderBy: String): PaymentNodeConnection - paymentRecord(id: ID!): PaymentRecordNode - allPaymentRecords(offset: Int, before: String, after: String, first: Int, last: Int, parent: ID, household: ID, individual: String, businessArea: String, programId: String, orderBy: String): PaymentRecordNodeConnection - allPaymentRecordsAndPayments(businessArea: String!, program: String, household: ID, orderBy: String, first: Int, last: Int, before: String, after: String): PaginatedPaymentRecordsAndPaymentsNode - financialServiceProviderXlsxTemplate(id: ID!): FinancialServiceProviderXlsxTemplateNode - allFinancialServiceProviderXlsxTemplates(offset: Int, before: String, after: String, first: Int, last: Int, name: String, createdBy: ID, orderBy: String): FinancialServiceProviderXlsxTemplateNodeConnection - financialServiceProvider(id: ID!): FinancialServiceProviderNode - allFinancialServiceProviders(offset: Int, before: String, after: String, first: Int, last: Int, createdBy: ID, name: String, visionVendorNumber: String, deliveryMechanisms: [String], distributionLimit: Float, communicationChannel: String, xlsxTemplates: [ID], orderBy: String): FinancialServiceProviderNodeConnection - paymentRecordVerification(id: ID!): PaymentVerificationNode - allPaymentVerifications(offset: Int, before: String, after: String, first: Int, last: Int, paymentVerificationPlan: ID, status: String, paymentPlanId: String, search: String, businessArea: String!, verificationChannel: String, orderBy: String): PaymentVerificationNodeConnection - paymentVerificationPlan(id: ID!): PaymentVerificationPlanNode - allPaymentVerificationPlan(offset: Int, before: String, after: String, first: Int, last: Int, programId: String): PaymentVerificationPlanNodeConnection - chartPaymentVerification(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartPaymentVerification - chartPaymentVerificationForPeople(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartPaymentVerification - chartVolumeByDeliveryMechanism(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDatasetNode - chartPayment(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDatasetNode - sectionTotalTransferred(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): SectionTotalNode - tableTotalCashTransferredByAdministrativeArea(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String, order: String, orderBy: String): TableTotalCashTransferred - tableTotalCashTransferredByAdministrativeAreaForPeople(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String, order: String, orderBy: String): TableTotalCashTransferredForPeople - chartTotalTransferredCashByCountry(year: Int!): ChartDetailedDatasetsNode - paymentRecordStatusChoices: [ChoiceObject] - paymentRecordEntitlementCardStatusChoices: [ChoiceObject] - paymentRecordDeliveryTypeChoices: [ChoiceObject] - cashPlanVerificationStatusChoices: [ChoiceObject] - cashPlanVerificationSamplingChoices: [ChoiceObject] - cashPlanVerificationVerificationChannelChoices: [ChoiceObject] - paymentVerificationStatusChoices: [ChoiceObject] - allRapidProFlows(businessAreaSlug: String!): [RapidProFlow] - sampleSize(input: GetCashplanVerificationSampleSizeInput): GetCashplanVerificationSampleSizeObject - allPaymentVerificationLogEntries(offset: Int, before: String, after: String, first: Int, last: Int, objectId: UUID, user: ID, businessArea: String!, search: String, module: String, userId: String, programId: String, objectType: String): PaymentVerificationLogEntryNodeConnection - paymentPlan(id: ID!): PaymentPlanNode - allPaymentPlans(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String!, search: String, status: [String], totalEntitledQuantityFrom: Float, totalEntitledQuantityTo: Float, dispersionStartDate: Date, dispersionEndDate: Date, isFollowUp: Boolean, sourcePaymentPlanId: String, program: String, programCycle: String, orderBy: String): PaymentPlanNodeConnection - paymentPlanStatusChoices: [ChoiceObject] - currencyChoices: [ChoiceObject] - allDeliveryMechanisms: [ChoiceObject] - paymentPlanBackgroundActionStatusChoices: [ChoiceObject] - availableFspsForDeliveryMechanisms(input: AvailableFspsForDeliveryMechanismsInput): [FspChoices] - allCashPlansAndPaymentPlans(businessArea: String!, program: String, search: String, serviceProvider: String, deliveryType: [String], verificationStatus: [String], startDateGte: String, endDateLte: String, orderBy: String, first: Int, last: Int, before: String, after: String, isPaymentVerificationPage: Boolean): PaginatedCashPlanAndPaymentPlanNode - businessArea(businessAreaSlug: String!): BusinessAreaNode - allBusinessAreas(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID, slug: String): BusinessAreaNodeConnection - allFieldsAttributes(flexField: Boolean, businessAreaSlug: String, programId: String): [FieldAttributeNode] - allPduFields(businessAreaSlug: String!, programId: String!): [FieldAttributeNode] - allGroupsWithFields: [GroupAttributeNode] - koboProject(uid: String!, businessAreaSlug: String!): KoboAssetObject - allKoboProjects(businessAreaSlug: String!, onlyDeployed: Boolean, before: String, after: String, first: Int, last: Int): KoboAssetObjectConnection - cashAssistUrlPrefix: String - allLanguages(code: String, before: String, after: String, first: Int, last: Int): LanguageObjectConnection - dataCollectingType(id: ID!): DataCollectingTypeNode - 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 - chartProgrammesBySector(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDetailedDatasetsNode - chartTotalTransferredByMonth(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDetailedDatasetsNode - cashPlan(id: ID!): CashPlanNode - allCashPlans(offset: Int, before: String, after: String, first: Int, last: Int, program: ID, assistanceThrough: String, assistanceThrough_Startswith: String, serviceProvider_FullName: String, serviceProvider_FullName_Startswith: String, startDate: DateTime, startDate_Lte: DateTime, startDate_Gte: DateTime, endDate: DateTime, endDate_Lte: DateTime, endDate_Gte: DateTime, businessArea: String, search: String, deliveryType: [String], verificationStatus: [String], orderBy: String): CashPlanNodeConnection - programStatusChoices: [ChoiceObject] - programCycleStatusChoices: [ChoiceObject] - programFrequencyOfPaymentsChoices: [ChoiceObject] - programSectorChoices: [ChoiceObject] - 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 - programCycle(id: ID!): ProgramCycleNode - canRunDeduplication: Boolean - isDeduplicationDisabled: Boolean - targetPopulation(id: ID!): TargetPopulationNode - allTargetPopulation(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection - targetPopulationHouseholds(targetPopulation: ID!, offset: Int, before: String, after: String, first: Int, last: Int, orderBy: String, businessArea: String): HouseholdNodeConnection - targetPopulationStatusChoices: [ChoiceObject] - allActiveTargetPopulations(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection - household(id: ID!): HouseholdNode - allHouseholds(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String, address: String, address_Startswith: String, headOfHousehold_FullName: String, headOfHousehold_FullName_Startswith: String, size_Range: [Int], size_Lte: Int, size_Gte: Int, adminArea: ID, admin1: ID, admin2: ID, targetPopulations: [ID], residenceStatus: String, withdrawn: Boolean, program: ID, size: String, search: String, documentType: String, documentNumber: String, headOfHousehold_PhoneNoValid: Boolean, lastRegistrationDate: String, countryOrigin: String, isActiveProgram: Boolean, orderBy: String): HouseholdNodeConnection - individual(id: ID!): IndividualNode - allIndividuals(offset: Int, before: String, after: String, first: Int, last: Int, household_Id: UUID, businessArea: String, fullName: String, fullName_Startswith: String, fullName_Endswith: String, sex: [String], household_AdminArea: ID, withdrawn: Boolean, program: ID, age: String, programs: [ID], search: String, documentType: String, documentNumber: String, lastRegistrationDate: String, admin1: [ID], admin2: [ID], status: [String], excludedId: String, flags: [String], isActiveProgram: Boolean, orderBy: String): IndividualNodeConnection - allMergedHouseholds(offset: Int, before: String, after: String, first: Int, last: Int, businessArea: String, rdiId: String, orderBy: String): HouseholdNodeConnection - allMergedIndividuals(offset: Int, before: String, after: String, first: Int, last: Int, household: ID, rdiId: String, duplicatesOnly: Boolean, businessArea: String, orderBy: String): IndividualNodeConnection - sectionHouseholdsReached(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): SectionTotalNode - sectionIndividualsReached(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): SectionTotalNode - sectionPeopleReached(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): SectionTotalNode - sectionChildReached(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): SectionTotalNode - chartIndividualsReachedByAgeAndGender(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDatasetNode - chartPeopleReachedByAgeAndGender(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDatasetNode - chartIndividualsWithDisabilityReachedByAge(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDetailedDatasetsNode - chartPeopleWithDisabilityReachedByAge(businessAreaSlug: String!, year: Int!, program: String, administrativeArea: String): ChartDetailedDatasetsNode - residenceStatusChoices: [ChoiceObject] - sexChoices: [ChoiceObject] - maritalStatusChoices: [ChoiceObject] - workStatusChoices: [ChoiceObject] - relationshipChoices: [ChoiceObject] - roleChoices: [ChoiceObject] - documentTypeChoices: [ChoiceObject] - identityTypeChoices: [ChoiceObject] - countriesChoices: [ChoiceObject] - observedDisabilityChoices: [ChoiceObject] - severityOfDisabilityChoices: [ChoiceObject] - flagChoices: [ChoiceObject] - allHouseholdsFlexFieldsAttributes: [FieldAttributeNode] - allIndividualsFlexFieldsAttributes: [FieldAttributeNode] - me: UserNode - allUsers(offset: Int, before: String, after: String, first: Int, last: Int, status: [String], partner: [String], businessArea: String!, program: String, search: String, roles: [String], isTicketCreator: Boolean, isSurveyCreator: Boolean, isMessageCreator: Boolean, isFeedbackCreator: Boolean, orderBy: String): UserNodeConnection - userRolesChoices: [RoleChoiceObject] - userStatusChoices: [ChoiceObject] - userPartnerChoices: [ChoiceObject] - partnerForGrievanceChoices(householdId: ID, individualId: ID): [ChoiceObject] - hasAvailableUsersToExport(businessAreaSlug: String!): Boolean - importedHousehold(id: ID!): ImportedHouseholdNode - allImportedHouseholds(offset: Int, before: String, after: String, first: Int, last: Int, rdiId: String, businessArea: String, orderBy: String): ImportedHouseholdNodeConnection - registrationDataImportDatahub(id: ID!): RegistrationDataImportDatahubNode - importedIndividual(id: ID!): ImportedIndividualNode - allImportedIndividuals(offset: Int, before: String, after: String, first: Int, last: Int, household: ID, rdiId: String, duplicatesOnly: Boolean, businessArea: String, orderBy: String): ImportedIndividualNodeConnection - importData(id: ID!): ImportDataNode - koboImportData(id: ID!): KoboImportDataNode - deduplicationBatchStatusChoices: [ChoiceObject] - deduplicationGoldenRecordStatusChoices: [ChoiceObject] - registrationDataImport(id: ID!): RegistrationDataImportNode - allRegistrationDataImports(offset: Int, before: String, after: String, first: Int, last: Int, importedBy_Id: UUID, importDate: Date, status: String, name: String, name_Startswith: String, businessArea: String, importDateRange: String, size: String, program: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, orderBy: String): RegistrationDataImportNodeConnection - registrationDataStatusChoices: [ChoiceObject] - _debug: DjangoDebug -} - -input RandomSamplingArguments { - confidenceInterval: Float! - marginOfError: Float! - excludedAdminAreas: [String] - age: AgeInput - sex: String -} - -input RapidProArguments { - flowId: String! -} - -type RapidProFlow { - id: String - name: String - type: String - archived: Boolean - labels: [String] - expires: Int - runs: [RapidProFlowRun] - results: [RapidProFlowResult] - createdOn: DateTime - modifiedOn: DateTime -} - -type RapidProFlowNode { - id: String - name: String -} - -type RapidProFlowResult { - key: String - name: String - categories: [String] - nodeUuids: [String] -} - -type RapidProFlowRun { - active: Int - completed: Int - interrupted: Int - expired: Int -} - -type ReassignRoleMutation { - household: HouseholdNode - individual: IndividualNode -} - -type RebuildTargetPopulationMutation { - targetPopulation: TargetPopulationNode -} - -type RecipientNode implements Node { - id: ID! - size: Int - headOfHousehold: IndividualNode -} - -type RecipientNodeConnection { - pageInfo: PageInfo! - edges: [RecipientNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type RecipientNodeEdge { - node: RecipientNode - cursor: String! -} - -type ReconciliationSummaryNode { - deliveredFully: Int - deliveredPartially: Int - notDelivered: Int - unsuccessful: Int - pending: Int - forceFailed: Int - numberOfPayments: Int - reconciled: Int -} - -input ReferralTicketExtras { - household: ID - individual: ID -} - -type RefuseRegistrationDataImportMutation { - registrationDataImport: RegistrationDataImportNode -} - -enum RegistrationDataImportDataSource { - XLS - KOBO - FLEX_REGISTRATION - API - EDOPOMOGA - PROGRAM_POPULATION - ENROLL_FROM_PROGRAM -} - -enum RegistrationDataImportDatahubImportDone { - LOADING - NOT_STARTED - STARTED - DONE -} - -type RegistrationDataImportDatahubNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - name: String! - importDate: DateTime! - hctId: UUID - importData: ImportDataNode - importDone: RegistrationDataImportDatahubImportDone! - businessAreaSlug: String! -} - -enum RegistrationDataImportDeduplicationEngineStatus { - PENDING - UPLOADED - IN_PROGRESS - FINISHED - ERROR - UPLOAD_ERROR -} - -type RegistrationDataImportNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - version: BigInt! - name: String! - status: RegistrationDataImportStatus! - importDate: DateTime! - importedBy: UserNode - dataSource: RegistrationDataImportDataSource! - numberOfIndividuals: Int! - numberOfHouseholds: Int! - batchDuplicates: Int! - batchPossibleDuplicates: Int! - batchUnique: Int! - goldenRecordDuplicates: Int! - goldenRecordPossibleDuplicates: Int! - goldenRecordUnique: Int! - dedupEngineBatchDuplicates: Int! - dedupEngineGoldenRecordDuplicates: Int! - datahubId: UUID - errorMessage: String! - sentryId: String - pullPictures: Boolean! - businessArea: UserBusinessAreaNode - screenBeneficiary: Boolean! - excluded: Boolean! - program: ProgramNode - erased: Boolean! - refuseReason: String - allowDeliveryMechanismsValidationErrors: Boolean! - importData: ImportDataNode - deduplicationEngineStatus: RegistrationDataImportDeduplicationEngineStatus - households(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! - individuals(offset: Int, before: String, after: String, first: Int, last: Int): IndividualNodeConnection! - grievanceticketSet(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! - messages(offset: Int, before: String, after: String, first: Int, last: Int): CommunicationMessageNodeConnection! - adminUrl: String - batchDuplicatesCountAndPercentage: [CountAndPercentageNode] - batchUniqueCountAndPercentage: [CountAndPercentageNode] - goldenRecordDuplicatesCountAndPercentage: [CountAndPercentageNode] - goldenRecordPossibleDuplicatesCountAndPercentage: [CountAndPercentageNode] - goldenRecordUniqueCountAndPercentage: [CountAndPercentageNode] - totalHouseholdsCountWithValidPhoneNo: Int - isDeduplicated: String - canMerge: Boolean - biometricDeduplicationEnabled: Boolean -} - -type RegistrationDataImportNodeConnection { - pageInfo: PageInfo! - edges: [RegistrationDataImportNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type RegistrationDataImportNodeEdge { - node: RegistrationDataImportNode - cursor: String! -} - -enum RegistrationDataImportStatus { - LOADING - DEDUPLICATION - DEDUPLICATION_FAILED - IMPORT_SCHEDULED - IMPORTING - IMPORT_ERROR - IN_REVIEW - MERGE_SCHEDULED - MERGED - MERGING - MERGE_ERROR - REFUSED -} - -type RegistrationDeduplicationMutation { - ok: Boolean -} - -type RegistrationKoboImportMutation { - validationErrors: Arg - registrationDataImport: RegistrationDataImportNode -} - -input RegistrationKoboImportMutationInput { - importDataId: String - name: String - pullPictures: Boolean - businessAreaSlug: String - screenBeneficiary: Boolean - allowDeliveryMechanismsValidationErrors: Boolean -} - -type RegistrationProgramPopulationImportMutation { - validationErrors: Arg - registrationDataImport: RegistrationDataImportNode -} - -input RegistrationProgramPopulationImportMutationInput { - importFromProgramId: String - name: String - businessAreaSlug: String - screenBeneficiary: Boolean -} - -type RegistrationXlsxImportMutation { - validationErrors: Arg - registrationDataImport: RegistrationDataImportNode -} - -input RegistrationXlsxImportMutationInput { - importDataId: ID - name: String - businessAreaSlug: String - screenBeneficiary: Boolean - allowDeliveryMechanismsValidationErrors: Boolean -} - -type ReportNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - businessArea: UserBusinessAreaNode! - file: String - createdBy: UserNode! - status: Int! - reportType: Int! - dateFrom: Date! - dateTo: Date! - numberOfRecords: Int - program: ProgramNode - adminArea(offset: Int, before: String, after: String, first: Int, last: Int, name: String): AreaNodeConnection! - fileUrl: String - adminArea1(offset: Int, before: String, after: String, first: Int, last: Int, name: String): AreaNodeConnection - adminArea2(offset: Int, before: String, after: String, first: Int, last: Int, name: String): AreaNodeConnection -} - -type ReportNodeConnection { - pageInfo: PageInfo! - edges: [ReportNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type ReportNodeEdge { - node: ReportNode - cursor: String! -} - -type RestartCreateReport { - report: ReportNode -} - -input RestartCreateReportInput { - reportId: ID! - businessAreaSlug: String! -} - -type RevertMarkPaymentAsFailedMutation { - payment: PaymentNode -} - -type RevertMarkPaymentRecordAsFailedMutation { - paymentRecord: PaymentRecordNode -} - -type RoleChoiceObject { - name: String - value: String - subsystem: String -} - -type RoleNode { - createdAt: DateTime! - updatedAt: DateTime! - name: String! - subsystem: RoleSubsystem! - permissions: [String!] - businessAreaPartnerThrough: [PartnerRoleNode!]! - userRoles: [UserRoleNode!]! -} - -enum RoleSubsystem { - HOPE - KOBO - CA - API -} - -enum RuleCommitLanguage { - PYTHON -} - -type RuleCommitNode implements Node { - id: ID! - timestamp: DateTime! - rule: SteficonRuleNode - updatedBy: UserNode - definition: String! - isRelease: Boolean! - enabled: Boolean! - deprecated: Boolean! - language: RuleCommitLanguage! - affectedFields: [String!]! - before: JSONString! - after: JSONString! - paymentPlans(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanNodeConnection! - targetPopulations(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection! -} - -type RuleCommitNodeConnection { - pageInfo: PageInfo! - edges: [RuleCommitNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type RuleCommitNodeEdge { - node: RuleCommitNode - cursor: String! -} - -enum RuleLanguage { - PYTHON -} - -enum RuleSecurity { - A_0 - A_2 - A_4 -} - -enum RuleType { - PAYMENT_PLAN - TARGETING -} - -enum SamplingChoices { - FULL_LIST - RANDOM -} - -type SanctionListIndividualAliasNameNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - name: String! -} - -type SanctionListIndividualAliasNameNodeConnection { - pageInfo: PageInfo! - edges: [SanctionListIndividualAliasNameNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type SanctionListIndividualAliasNameNodeEdge { - node: SanctionListIndividualAliasNameNode - cursor: String! -} - -type SanctionListIndividualCountriesNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - country: String -} - -type SanctionListIndividualCountriesNodeConnection { - pageInfo: PageInfo! - edges: [SanctionListIndividualCountriesNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type SanctionListIndividualCountriesNodeEdge { - node: SanctionListIndividualCountriesNode - cursor: String! -} - -type SanctionListIndividualDateOfBirthNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - date: Date! -} - -type SanctionListIndividualDateOfBirthNodeConnection { - pageInfo: PageInfo! - edges: [SanctionListIndividualDateOfBirthNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type SanctionListIndividualDateOfBirthNodeEdge { - node: SanctionListIndividualDateOfBirthNode - cursor: String! -} - -type SanctionListIndividualDocumentNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - documentNumber: String! - typeOfDocument: String! - dateOfIssue: String - issuingCountry: String - note: String! -} - -type SanctionListIndividualDocumentNodeConnection { - pageInfo: PageInfo! - edges: [SanctionListIndividualDocumentNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type SanctionListIndividualDocumentNodeEdge { - node: SanctionListIndividualDocumentNode - cursor: String! -} - -type SanctionListIndividualNationalitiesNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - nationality: String -} - -type SanctionListIndividualNationalitiesNodeConnection { - pageInfo: PageInfo! - edges: [SanctionListIndividualNationalitiesNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type SanctionListIndividualNationalitiesNodeEdge { - node: SanctionListIndividualNationalitiesNode - cursor: String! -} - -type SanctionListIndividualNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - dataId: Int! - versionNum: Int! - firstName: String! - secondName: String! - thirdName: String! - fourthName: String! - fullName: String! - nameOriginalScript: String! - unListType: String! - referenceNumber: String! - listedOn: DateTime! - comments: String! - designation: String! - listType: String! - street: String! - city: String! - stateProvince: String! - addressNote: String! - countryOfBirth: String - active: Boolean! - documents(offset: Int, before: String, after: String, first: Int, last: Int): SanctionListIndividualDocumentNodeConnection! - nationalities(offset: Int, before: String, after: String, first: Int, last: Int): SanctionListIndividualNationalitiesNodeConnection! - countries(offset: Int, before: String, after: String, first: Int, last: Int): SanctionListIndividualCountriesNodeConnection! - aliasNames(offset: Int, before: String, after: String, first: Int, last: Int): SanctionListIndividualAliasNameNodeConnection! - datesOfBirth(offset: Int, before: String, after: String, first: Int, last: Int): SanctionListIndividualDateOfBirthNodeConnection! -} - -type SanctionListIndividualNodeConnection { - pageInfo: PageInfo! - edges: [SanctionListIndividualNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type SanctionListIndividualNodeEdge { - node: SanctionListIndividualNode - cursor: String! -} - -type SaveKoboProjectImportDataAsync { - importData: KoboImportDataNode -} - -type SectionTotalNode { - total: Float -} - -input SensitiveGrievanceTicketExtras { - household: ID - individual: ID - paymentRecord: [ID] -} - -type ServiceProviderNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - businessArea: UserBusinessAreaNode! - caId: String! - fullName: String - shortName: String - country: String! - visionId: String - cashPlans(offset: Int, before: String, after: String, first: Int, last: Int): CashPlanNodeConnection! - paymentrecordSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection! -} - -type ServiceProviderNodeConnection { - pageInfo: PageInfo! - edges: [ServiceProviderNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type ServiceProviderNodeEdge { - node: ServiceProviderNode - cursor: String! -} - -type SetSteficonRuleOnPaymentPlanPaymentListMutation { - paymentPlan: PaymentPlanNode -} - -input SetSteficonRuleOnTargetPopulationMutationInput { - targetId: ID! - steficonRuleId: ID - version: BigInt - clientMutationId: String -} - -type SetSteficonRuleOnTargetPopulationMutationPayload { - targetPopulation: TargetPopulationNode - clientMutationId: String -} - -type SimpleApproveMutation { - grievanceTicket: GrievanceTicketNode -} - -type SplitPaymentPlanMutation { - paymentPlan: PaymentPlanNode -} - -type SteficonRuleNode implements Node { - id: ID! - allowedBusinessAreas(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID): UserBusinessAreaNodeConnection! - name: String! - definition: String! - description: String - enabled: Boolean! - deprecated: Boolean! - language: RuleLanguage! - security: RuleSecurity! - createdBy: UserNode - updatedBy: UserNode - createdAt: DateTime! - updatedAt: DateTime! - type: RuleType! - flags: JSONString! - history(offset: Int, before: String, after: String, first: Int, last: Int): RuleCommitNodeConnection! -} - -type SteficonRuleNodeConnection { - pageInfo: PageInfo! - edges: [SteficonRuleNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type SteficonRuleNodeEdge { - node: SteficonRuleNode - cursor: String! -} - -enum SurveyCategory { - RAPID_PRO - SMS - MANUAL -} - -type SurveyNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - unicefId: String - title: String! - body: String! - category: SurveyCategory! - numberOfRecipients: Int! - createdBy: UserNode - recipients(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! - targetPopulation: TargetPopulationNode - program: ProgramNode - businessArea: UserBusinessAreaNode! - sampleFile: String - sampleFileGeneratedAt: DateTime - samplingType: SurveySamplingType! - fullListArguments: JSONString! - randomSamplingArguments: JSONString! - sampleSize: Int! - flowId: String - successfulRapidProCalls: [JSONString!]! - adminUrl: String - sampleFilePath: String - hasValidSampleFile: Boolean - rapidProUrl: String -} - -type SurveyNodeConnection { - pageInfo: PageInfo! - edges: [SurveyNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type SurveyNodeEdge { - node: SurveyNode - cursor: String! -} - -enum SurveySamplingType { - FULL_LIST - RANDOM -} - -type TableTotalCashTransferred { - data: [_TableTotalCashTransferredDataNode] -} - -type TableTotalCashTransferredForPeople { - data: [_TableTotalCashTransferredDataForPeopleNode] -} - -enum TargetPopulationBuildStatus { - PENDING - BUILDING - FAILED - OK -} - -type TargetPopulationNode implements Node { - isRemoved: Boolean! - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - version: BigInt! - name: String! - caId: String - caHashId: String - createdBy: UserNode - changeDate: DateTime - changedBy: UserNode - finalizedAt: DateTime - finalizedBy: UserNode - businessArea: UserBusinessAreaNode - status: TargetPopulationStatus! - buildStatus: TargetPopulationBuildStatus! - builtAt: DateTime - households(offset: Int, before: String, after: String, first: Int, last: Int, orderBy: String, businessArea: String): HouseholdNodeConnection - program: ProgramNode - programCycle: ProgramCycleNode - targetingCriteria: TargetingCriteriaNode - sentToDatahub: Boolean! - steficonRule: RuleCommitNode - steficonAppliedDate: DateTime - vulnerabilityScoreMin: Float - vulnerabilityScoreMax: Float - excludedIds: String! - exclusionReason: String! - totalHouseholdsCount: Int - totalIndividualsCount: Int - childMaleCount: Int - childFemaleCount: Int - adultMaleCount: Int - adultFemaleCount: Int - paymentPlans(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanNodeConnection! - paymentRecords(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection! - selections: [HouseholdSelectionNode!]! - messages(offset: Int, before: String, after: String, first: Int, last: Int): CommunicationMessageNodeConnection! - surveys(offset: Int, before: String, after: String, first: Int, last: Int): SurveyNodeConnection! - adminUrl: String - totalFamilySize: Int - householdList(offset: Int, before: String, after: String, first: Int, last: Int, orderBy: String, businessArea: String): HouseholdNodeConnection - totalHouseholdsCountWithValidPhoneNo: Int - hasEmptyCriteria: Boolean - hasEmptyIdsCriteria: Boolean -} - -type TargetPopulationNodeConnection { - pageInfo: PageInfo! - edges: [TargetPopulationNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TargetPopulationNodeEdge { - node: TargetPopulationNode - cursor: String! -} - -enum TargetPopulationStatus { - OPEN - LOCKED - STEFICON_WAIT - STEFICON_RUN - STEFICON_COMPLETED - STEFICON_ERROR - PROCESSING - SENDING_TO_CASH_ASSIST - READY_FOR_CASH_ASSIST - READY_FOR_PAYMENT_MODULE - ASSIGNED -} - -type TargetingCriteriaNode { - id: UUID! - createdAt: DateTime! - updatedAt: DateTime! - flagExcludeIfActiveAdjudicationTicket: Boolean! - flagExcludeIfOnSanctionList: Boolean! - householdIds: String! - individualIds: String! - targetPopulation: TargetPopulationNode - rules: [TargetingCriteriaRuleNode] -} - -input TargetingCriteriaObjectType { - rules: [TargetingCriteriaRuleObjectType] - flagExcludeIfActiveAdjudicationTicket: Boolean - flagExcludeIfOnSanctionList: Boolean - householdIds: String - individualIds: String -} - -enum TargetingCriteriaRuleFilterComparisonMethod { - EQUALS - NOT_EQUALS - CONTAINS - NOT_CONTAINS - RANGE - NOT_IN_RANGE - GREATER_THAN - LESS_THAN - IS_NULL -} - -enum TargetingCriteriaRuleFilterFlexFieldClassification { - NOT_FLEX_FIELD - FLEX_FIELD_BASIC - FLEX_FIELD_PDU -} - -type TargetingCriteriaRuleFilterNode { - id: UUID! - createdAt: DateTime! - updatedAt: DateTime! - comparisonMethod: TargetingCriteriaRuleFilterComparisonMethod! - targetingCriteriaRule: TargetingCriteriaRuleNode! - flexFieldClassification: TargetingCriteriaRuleFilterFlexFieldClassification! - fieldName: String! - arguments: [Arg] - roundNumber: Int - fieldAttribute: FieldAttributeNode -} - -input TargetingCriteriaRuleFilterObjectType { - comparisonMethod: String! - flexFieldClassification: FlexFieldClassificationChoices! - fieldName: String! - arguments: [Arg]! - roundNumber: Int -} - -type TargetingCriteriaRuleNode { - id: UUID! - createdAt: DateTime! - updatedAt: DateTime! - targetingCriteria: TargetingCriteriaNode! - individualsFiltersBlocks: [TargetingIndividualRuleFilterBlockNode] - filters: [TargetingCriteriaRuleFilterNode] -} - -input TargetingCriteriaRuleObjectType { - filters: [TargetingCriteriaRuleFilterObjectType] - individualsFiltersBlocks: [TargetingIndividualRuleFilterBlockObjectType] -} - -enum TargetingIndividualBlockRuleFilterComparisonMethod { - EQUALS - NOT_EQUALS - CONTAINS - NOT_CONTAINS - RANGE - NOT_IN_RANGE - GREATER_THAN - LESS_THAN - IS_NULL -} - -enum TargetingIndividualBlockRuleFilterFlexFieldClassification { - NOT_FLEX_FIELD - FLEX_FIELD_BASIC - FLEX_FIELD_PDU -} - -type TargetingIndividualBlockRuleFilterNode { - id: UUID! - createdAt: DateTime! - updatedAt: DateTime! - comparisonMethod: TargetingIndividualBlockRuleFilterComparisonMethod! - individualsFiltersBlock: TargetingIndividualRuleFilterBlockNode! - flexFieldClassification: TargetingIndividualBlockRuleFilterFlexFieldClassification! - fieldName: String! - arguments: [Arg] - roundNumber: Int - fieldAttribute: FieldAttributeNode -} - -type TargetingIndividualRuleFilterBlockNode { - id: UUID! - createdAt: DateTime! - updatedAt: DateTime! - targetingCriteriaRule: TargetingCriteriaRuleNode! - targetOnlyHoh: Boolean! - individualBlockFilters: [TargetingIndividualBlockRuleFilterNode] -} - -input TargetingIndividualRuleFilterBlockObjectType { - individualBlockFilters: [TargetingCriteriaRuleFilterObjectType] -} - -type TicketAddIndividualDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - household: HouseholdNode - individualData: Arg - approveStatus: Boolean! -} - -type TicketAddIndividualDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketAddIndividualDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketAddIndividualDetailsNodeEdge { - node: TicketAddIndividualDetailsNode - cursor: String! -} - -type TicketByType { - userGeneratedCount: Int - systemGeneratedCount: Int - closedUserGeneratedCount: Int - closedSystemGeneratedCount: Int - userGeneratedAvgResolution: Float - systemGeneratedAvgResolution: Float -} - -type TicketComplaintDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - paymentContentType: ContentTypeObjectType - paymentObjectId: UUID - household: HouseholdNode - individual: IndividualNode - paymentRecord: PaymentRecordAndPaymentNode -} - -type TicketComplaintDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketComplaintDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketComplaintDetailsNodeEdge { - node: TicketComplaintDetailsNode - cursor: String! -} - -type TicketDeleteHouseholdDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - household: HouseholdNode - roleReassignData: JSONString! - approveStatus: Boolean! - reasonHousehold: HouseholdNode - householdData: Arg -} - -type TicketDeleteHouseholdDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketDeleteHouseholdDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketDeleteHouseholdDetailsNodeEdge { - node: TicketDeleteHouseholdDetailsNode - cursor: String! -} - -type TicketDeleteIndividualDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - individual: IndividualNode - roleReassignData: JSONString! - approveStatus: Boolean! - individualData: Arg -} - -type TicketDeleteIndividualDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketDeleteIndividualDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketDeleteIndividualDetailsNodeEdge { - node: TicketDeleteIndividualDetailsNode - cursor: String! -} - -type TicketHouseholdDataUpdateDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - household: HouseholdNode - householdData: Arg -} - -type TicketHouseholdDataUpdateDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketHouseholdDataUpdateDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketHouseholdDataUpdateDetailsNodeEdge { - node: TicketHouseholdDataUpdateDetailsNode - cursor: String! -} - -type TicketIndividualDataUpdateDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - individual: IndividualNode - individualData: Arg - roleReassignData: JSONString! -} - -type TicketIndividualDataUpdateDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketIndividualDataUpdateDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketIndividualDataUpdateDetailsNodeEdge { - node: TicketIndividualDataUpdateDetailsNode - cursor: String! -} - -type TicketNeedsAdjudicationDetailsExtraDataNode { - goldenRecords: [DeduplicationResultNode] - possibleDuplicate: [DeduplicationResultNode] -} - -type TicketNeedsAdjudicationDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - isMultipleDuplicatesVersion: Boolean! - goldenRecordsIndividual: IndividualNode! - possibleDuplicates: [IndividualNode] - selectedDistinct: [IndividualNode] - selectedIndividuals(offset: Int, before: String, after: String, first: Int, last: Int): IndividualNodeConnection! - roleReassignData: JSONString! - extraData: TicketNeedsAdjudicationDetailsExtraDataNode - scoreMin: Float! - scoreMax: Float! - isCrossArea: Boolean! - dedupEngineSimilarityPair: DeduplicationEngineSimilarityPairNode - selectedIndividual: IndividualNode - possibleDuplicate: IndividualNode - hasDuplicatedDocument: Boolean - selectedDuplicates: [IndividualNode] -} - -type TicketNeedsAdjudicationDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketNeedsAdjudicationDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketNeedsAdjudicationDetailsNodeEdge { - node: TicketNeedsAdjudicationDetailsNode - cursor: String! -} - -type TicketNegativeFeedbackDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - household: HouseholdNode - individual: IndividualNode -} - -type TicketNegativeFeedbackDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketNegativeFeedbackDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketNegativeFeedbackDetailsNodeEdge { - node: TicketNegativeFeedbackDetailsNode - cursor: String! -} - -type TicketNoteNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - description: String! - createdBy: UserNode -} - -type TicketNoteNodeConnection { - pageInfo: PageInfo! - edges: [TicketNoteNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketNoteNodeEdge { - node: TicketNoteNode - cursor: String! -} - -input TicketPaymentVerificationDetailsExtras { - newReceivedAmount: Float - newStatus: String -} - -enum TicketPaymentVerificationDetailsNewStatus { - NOT_RECEIVED - PENDING - RECEIVED - RECEIVED_WITH_ISSUES -} - -type TicketPaymentVerificationDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - paymentVerifications(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationNodeConnection! - paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus! - paymentVerification: PaymentVerificationNode - newStatus: TicketPaymentVerificationDetailsNewStatus - oldReceivedAmount: Float - newReceivedAmount: Float - approveStatus: Boolean! - hasMultiplePaymentVerifications: Boolean -} - -type TicketPaymentVerificationDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketPaymentVerificationDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketPaymentVerificationDetailsNodeEdge { - node: TicketPaymentVerificationDetailsNode - cursor: String! -} - -enum TicketPaymentVerificationDetailsPaymentVerificationStatus { - NOT_RECEIVED - PENDING - RECEIVED - RECEIVED_WITH_ISSUES -} - -type TicketPositiveFeedbackDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - household: HouseholdNode - individual: IndividualNode -} - -type TicketPositiveFeedbackDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketPositiveFeedbackDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketPositiveFeedbackDetailsNodeEdge { - node: TicketPositiveFeedbackDetailsNode - cursor: String! -} - -type TicketReferralDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - household: HouseholdNode - individual: IndividualNode -} - -type TicketReferralDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketReferralDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketReferralDetailsNodeEdge { - node: TicketReferralDetailsNode - cursor: String! -} - -type TicketSensitiveDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - paymentContentType: ContentTypeObjectType - paymentObjectId: UUID - household: HouseholdNode - individual: IndividualNode - paymentRecord: PaymentRecordAndPaymentNode -} - -type TicketSensitiveDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketSensitiveDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketSensitiveDetailsNodeEdge { - node: TicketSensitiveDetailsNode - cursor: String! -} - -type TicketSystemFlaggingDetailsNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - goldenRecordsIndividual: IndividualNode! - sanctionListIndividual: SanctionListIndividualNode! - approveStatus: Boolean! - roleReassignData: JSONString! -} - -type TicketSystemFlaggingDetailsNodeConnection { - pageInfo: PageInfo! - edges: [TicketSystemFlaggingDetailsNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type TicketSystemFlaggingDetailsNodeEdge { - node: TicketSystemFlaggingDetailsNode - cursor: String! -} - -scalar UUID - -type UnlockTargetPopulationMutation { - targetPopulation: TargetPopulationNode -} - -input UpdateAddIndividualIssueTypeExtras { - individualData: AddIndividualDataObjectType! -} - -input UpdateFeedbackInput { - feedbackId: ID! - issueType: String - householdLookup: ID - individualLookup: ID - description: String - comments: String - admin2: ID - area: String - language: String - consent: Boolean - program: ID -} - -type UpdateFeedbackMutation { - feedback: FeedbackNode -} - -input UpdateGrievanceTicketExtrasInput { - householdDataUpdateIssueTypeExtras: UpdateHouseholdDataUpdateIssueTypeExtras - individualDataUpdateIssueTypeExtras: UpdateIndividualDataUpdateIssueTypeExtras - addIndividualIssueTypeExtras: UpdateAddIndividualIssueTypeExtras - category: CategoryExtrasInput - ticketPaymentVerificationDetailsExtras: TicketPaymentVerificationDetailsExtras -} - -input UpdateGrievanceTicketInput { - ticketId: ID! - description: String - assignedTo: ID - admin: ID - area: String - language: String - linkedTickets: [ID] - household: ID - individual: ID - paymentRecord: ID - extras: UpdateGrievanceTicketExtrasInput - priority: Int - urgency: Int - partner: Int - program: ID - comments: String - documentation: [GrievanceDocumentInput] - documentationToUpdate: [GrievanceDocumentUpdateInput] - documentationToDelete: [ID] -} - -type UpdateGrievanceTicketMutation { - grievanceTicket: GrievanceTicketNode -} - -input UpdateHouseholdDataUpdateIssueTypeExtras { - householdData: HouseholdUpdateDataObjectType! -} - -input UpdateIndividualDataUpdateIssueTypeExtras { - individualData: IndividualUpdateDataObjectType! -} - -input UpdatePaymentPlanInput { - paymentPlanId: ID! - targetingId: ID - dispersionStartDate: Date - dispersionEndDate: Date - currency: String -} - -type UpdatePaymentPlanMutation { - paymentPlan: PaymentPlanNode -} - -type UpdatePaymentVerificationReceivedAndReceivedAmount { - paymentVerification: PaymentVerificationNode -} - -type UpdatePaymentVerificationStatusAndReceivedAmount { - paymentVerification: PaymentVerificationNode -} - -type UpdateProgram { - validationErrors: Arg - program: ProgramNode -} - -input UpdateProgramInput { - id: String! - name: String - status: String - startDate: Date - endDate: Date - description: String - budget: Decimal - frequencyOfPayments: String - sector: String - cashPlus: Boolean - populationGoal: Int - administrativeAreasOfImplementation: String - dataCollectingTypeCode: String - partners: [ProgramPartnerThroughInput] - partnerAccess: String - programmeCode: String - pduFields: [PDUFieldInput] -} - -input UpdateTargetPopulationInput { - id: ID! - name: String - targetingCriteria: TargetingCriteriaObjectType - programId: ID - programCycleId: ID - vulnerabilityScoreMin: Decimal - vulnerabilityScoreMax: Decimal - excludedIds: String - exclusionReason: String -} - -type UpdateTargetPopulationMutation { - validationErrors: Arg - targetPopulation: TargetPopulationNode -} - -scalar Upload - -type UploadImportDataXLSXFileAsync { - importData: ImportDataNode - errors: [XlsxRowErrorNode] -} - -type UserBusinessAreaNode implements Node { - id: ID! - createdAt: DateTime! - updatedAt: DateTime! - code: String! - name: String! - longName: String! - regionCode: String! - regionName: String! - koboUsername: String - koboToken: String - koboUrl: String - rapidProHost: String - rapidProPaymentVerificationToken: String - rapidProMessagesToken: String - rapidProSurveyToken: String - slug: String! - customFields: JSONString! - hasDataSharingAgreement: Boolean! - parent: UserBusinessAreaNode - isSplit: Boolean! - postponeDeduplication: Boolean! - deduplicationDuplicateScore: Float! - deduplicationPossibleDuplicateScore: Float! - deduplicationBatchDuplicatesPercentage: Int! - deduplicationBatchDuplicatesAllowed: Int! - deduplicationGoldenRecordDuplicatesPercentage: Int! - deduplicationGoldenRecordDuplicatesAllowed: Int! - screenBeneficiary: Boolean! - deduplicationIgnoreWithdraw: Boolean! - biometricDeduplicationThreshold: Float! - isPaymentPlanApplicable: Boolean! - isAccountabilityApplicable: Boolean - active: Boolean! - enableEmailNotification: Boolean! - partners: [PartnerNode!]! - businessAreaPartnerThrough: [PartnerRoleNode!]! - children(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID): UserBusinessAreaNodeConnection! - dataCollectingTypes(offset: Int, before: String, after: String, first: Int, last: Int): DataCollectingTypeNodeConnection! - partnerSet: [PartnerNode!]! - userRoles: [UserRoleNode!]! - householdSet(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! - individualSet(offset: Int, before: String, after: String, first: Int, last: Int): IndividualNodeConnection! - registrationdataimportSet(offset: Int, before: String, after: String, first: Int, last: Int): RegistrationDataImportNodeConnection! - ruleSet(offset: Int, before: String, after: String, first: Int, last: Int): SteficonRuleNodeConnection! - paymentplanSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanNodeConnection! - financialserviceproviderSet(offset: Int, before: String, after: String, first: Int, last: Int): FinancialServiceProviderNodeConnection! - cashplanSet(offset: Int, before: String, after: String, first: Int, last: Int): CashPlanNodeConnection! - paymentrecordSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection! - paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! - serviceproviderSet(offset: Int, before: String, after: String, first: Int, last: Int): ServiceProviderNodeConnection! - tickets(offset: Int, before: String, after: String, first: Int, last: Int): GrievanceTicketNodeConnection! - targetpopulationSet(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection! - programSet(offset: Int, before: String, after: String, first: Int, last: Int, name: String): ProgramNodeConnection! - reports(offset: Int, before: String, after: String, first: Int, last: Int): ReportNodeConnection! - logentrySet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationLogEntryNodeConnection! - messageSet(offset: Int, before: String, after: String, first: Int, last: Int): CommunicationMessageNodeConnection! - feedbackSet(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackNodeConnection! - surveySet(offset: Int, before: String, after: String, first: Int, last: Int): SurveyNodeConnection! - permissions: [String] -} - -type UserBusinessAreaNodeConnection { - pageInfo: PageInfo! - edges: [UserBusinessAreaNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type UserBusinessAreaNodeEdge { - node: UserBusinessAreaNode - cursor: String! -} - -type UserNode implements Node { - lastLogin: DateTime - isSuperuser: Boolean! - username: String! - firstName: String! - lastName: String! - isStaff: Boolean! - isActive: Boolean! - dateJoined: DateTime! - id: ID! - status: UserStatus! - partner: PartnerNode - email: String! - availableForExport: Boolean! - customFields: JSONString! - jobTitle: String! - adUuid: String - lastModifyDate: DateTime - lastDoapSync: DateTime - doapHash: String! - userRoles: [UserRoleNode!]! - documentSet(offset: Int, before: String, after: String, first: Int, last: Int): DocumentNodeConnection! - 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! - targetPopulations(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection! - changedTargetPopulations(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection! - finalizedTargetPopulations(offset: Int, before: String, after: String, first: Int, last: Int, program: [ID], createdAt: DateTime, createdAt_Lte: DateTime, createdAt_Gte: DateTime, updatedAt: DateTime, updatedAt_Lte: DateTime, updatedAt_Gte: DateTime, status: String, households: [ID], name: String, createdByName: String, totalHouseholdsCountMin: Int, totalHouseholdsCountMax: Int, totalIndividualsCountMin: Int, totalIndividualsCountMax: Int, businessArea: String, createdAtRange: String, paymentPlanApplicable: Boolean, statusNot: String, totalHouseholdsCountWithValidPhoneNoMax: Int, totalHouseholdsCountWithValidPhoneNoMin: Int, programCycle: String, orderBy: String): TargetPopulationNodeConnection! - reports(offset: Int, before: String, after: String, first: Int, last: Int): ReportNodeConnection! - logs(offset: Int, before: String, after: String, first: Int, last: Int): PaymentVerificationLogEntryNodeConnection! - messages(offset: Int, before: String, after: String, first: Int, last: Int): CommunicationMessageNodeConnection! - feedbacks(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackNodeConnection! - feedbackMessages(offset: Int, before: String, after: String, first: Int, last: Int): FeedbackMessageNodeConnection! - surveys(offset: Int, before: String, after: String, first: Int, last: Int): SurveyNodeConnection! - businessAreas(offset: Int, before: String, after: String, first: Int, last: Int, id: UUID): UserBusinessAreaNodeConnection - partnerRoles: [PartnerRoleNode] -} - -type UserNodeConnection { - pageInfo: PageInfo! - edges: [UserNodeEdge]! - totalCount: Int - edgeCount: Int -} - -type UserNodeEdge { - node: UserNode - cursor: String! -} - -type UserRoleNode { - createdAt: DateTime! - updatedAt: DateTime! - businessArea: UserBusinessAreaNode! - role: RoleNode! - expiryDate: Date -} - -enum UserStatus { - ACTIVE - INACTIVE - INVITED -} - -type VolumeByDeliveryMechanismNode implements Node { - id: ID! - deliveryMechanism: DeliveryMechanismPerPaymentPlanNode - volume: Float - volumeUsd: Float -} - -type XlsxErrorNode { - sheet: String - coordinates: String - message: String -} - -type XlsxRowErrorNode { - rowNumber: Int - header: String - message: String -} - -type _DatasetsNode { - data: [Float] -} - -type _DetailedDatasetsNode { - label: String - data: [Float] -} - -type _TableTotalCashTransferredDataForPeopleNode { - id: String - admin2: String - totalCashTransferred: Float - totalPeople: Int -} - -type _TableTotalCashTransferredDataNode { - id: String - admin2: String - totalCashTransferred: Float - totalHouseholds: Int -} diff --git a/src/frontend/package.json b/src/frontend/package.json index e76f5b9665..ac2ab90b40 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -12,7 +12,7 @@ "test": "TZ=UTC jest --config jest.config.ts", "preview": "vite preview", "download-dev-schema": "wget --no-check-certificate -O data/schema.graphql https://dev-hct.unitst.org/api/graphql/schema.graphql", - "download-local-schema": "wget --no-check-certificate -O data/schema.graphql http://localhost:8080/api/graphql/schema.graphql", + "download-local-schema": "wget --no-check-certificate -O data/schema.graphql http://localhost:3000/api/graphql/schema.graphql", "generate-types": "yarn download-dev-schema && graphql-codegen --config codegen.yml --debug", "generate-types-local": "yarn download-local-schema && graphql-codegen --config codegen.yml --debug" }, diff --git a/src/frontend/src/__generated__/graphql.tsx b/src/frontend/src/__generated__/graphql.tsx index 33d46bf97e..b71306b40d 100644 --- a/src/frontend/src/__generated__/graphql.tsx +++ b/src/frontend/src/__generated__/graphql.tsx @@ -1465,6 +1465,64 @@ export type DeliveredQuantityNode = { totalDeliveredQuantity?: Maybe; }; +export enum DeliveryMechanismDataDeliveryMechanismChoice { + AtmCard = 'ATM_CARD', + CardlessCashWithdrawal = 'CARDLESS_CASH_WITHDRAWAL', + Cash = 'CASH', + CashByFsp = 'CASH_BY_FSP', + CashOverTheCounter = 'CASH_OVER_THE_COUNTER', + Cheque = 'CHEQUE', + DepositToCard = 'DEPOSIT_TO_CARD', + MobileMoney = 'MOBILE_MONEY', + PrePaidCard = 'PRE_PAID_CARD', + Referral = 'REFERRAL', + Transfer = 'TRANSFER', + TransferToAccount = 'TRANSFER_TO_ACCOUNT', + TransferToDigitalWallet = 'TRANSFER_TO_DIGITAL_WALLET', + Voucher = 'VOUCHER' +} + +export type DeliveryMechanismDataNode = Node & { + __typename?: 'DeliveryMechanismDataNode'; + createdAt: Scalars['DateTime']['output']; + data: Scalars['JSONString']['output']; + deliveryMechanism: DeliveryMechanismNode; + deliveryMechanismChoice: DeliveryMechanismDataDeliveryMechanismChoice; + id: Scalars['ID']['output']; + individual: IndividualNode; + individualTabData?: Maybe; + isValid?: Maybe; + name?: Maybe; + possibleDuplicateOf?: Maybe; + possibleDuplicates: DeliveryMechanismDataNodeConnection; + rdiMergeStatus: DeliveryMechanismDataRdiMergeStatus; + updatedAt: Scalars['DateTime']['output']; + validationErrors: Scalars['JSONString']['output']; +}; + + +export type DeliveryMechanismDataNodePossibleDuplicatesArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + offset?: InputMaybe; +}; + +export type DeliveryMechanismDataNodeConnection = { + __typename?: 'DeliveryMechanismDataNodeConnection'; + edgeCount?: Maybe; + edges: Array>; + pageInfo: PageInfo; + totalCount?: Maybe; +}; + +export type DeliveryMechanismDataNodeEdge = { + __typename?: 'DeliveryMechanismDataNodeEdge'; + cursor: Scalars['String']['output']; + node?: Maybe; +}; + export type DeliveryMechanismDataObjectType = { approveStatus: Scalars['Boolean']['input']; dataFields: Array>; @@ -1477,10 +1535,16 @@ export type DeliveryMechanismDataPayloadFieldObjectType = { value: Scalars['String']['input']; }; +export enum DeliveryMechanismDataRdiMergeStatus { + Merged = 'MERGED', + Pending = 'PENDING' +} + export type DeliveryMechanismNode = Node & { __typename?: 'DeliveryMechanismNode'; code?: Maybe; createdAt: Scalars['DateTime']['output']; + deliverymechanismdataSet: DeliveryMechanismDataNodeConnection; deliverymechanismperpaymentplanSet: DeliveryMechanismPerPaymentPlanNodeConnection; financialserviceproviderSet: FinancialServiceProviderNodeConnection; id: Scalars['ID']['output']; @@ -1497,6 +1561,15 @@ export type DeliveryMechanismNode = Node & { }; +export type DeliveryMechanismNodeDeliverymechanismdataSetArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + offset?: InputMaybe; +}; + + export type DeliveryMechanismNodeDeliverymechanismperpaymentplanSetArgs = { after?: InputMaybe; before?: InputMaybe; @@ -3445,6 +3518,7 @@ export type IndividualNode = Node & { deduplicationGoldenRecordResults?: Maybe>>; deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus; deleteIndividualTicketDetails: TicketDeleteIndividualDetailsNodeConnection; + deliveryMechanismsData?: Maybe>>; detailId?: Maybe; disability: IndividualDisability; disabilityCertificatePicture?: Maybe; @@ -9680,7 +9754,7 @@ export type _TableTotalCashTransferredDataNode = { totalHouseholds?: Maybe; }; -export type GrievanceTicketDetailedFragment = { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', isDuplicate?: boolean | null, similarityScore?: string | null, individual1: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null }, individual2: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null } } | null, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null }; +export type GrievanceTicketDetailedFragment = { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', isDuplicate?: boolean | null, similarityScore?: string | null, individual1: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null }, individual2: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null } } | null, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null }; export type HouseholdMinimalFragment = { __typename?: 'HouseholdNode', id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, flexFields?: any | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, address: string, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } }; @@ -9690,7 +9764,7 @@ export type MergedHouseholdMinimalFragment = { __typename?: 'HouseholdNode', id: export type IndividualMinimalFragment = { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null }; -export type IndividualDetailedFragment = { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } }; +export type IndividualDetailedFragment = { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } }; 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, 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, datahubId?: any | null } }; @@ -9816,7 +9890,7 @@ export type ApproveIndividualDataChangeMutationVariables = Exact<{ }>; -export type ApproveIndividualDataChangeMutation = { __typename?: 'Mutations', approveIndividualDataChange?: { __typename?: 'IndividualDataChangeApproveMutation', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, status: number, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null } | null } | null }; +export type ApproveIndividualDataChangeMutation = { __typename?: 'Mutations', approveIndividualDataChange?: { __typename?: 'IndividualDataChangeApproveMutation', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, status: number, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null } | null } | null }; export type ApproveNeedsAdjudicationMutationVariables = Exact<{ grievanceTicketId: Scalars['ID']['input']; @@ -9827,7 +9901,7 @@ export type ApproveNeedsAdjudicationMutationVariables = Exact<{ }>; -export type ApproveNeedsAdjudicationMutation = { __typename?: 'Mutations', approveNeedsAdjudication?: { __typename?: 'NeedsAdjudicationApproveMutation', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, status: number, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null } | null } | null }; +export type ApproveNeedsAdjudicationMutation = { __typename?: 'Mutations', approveNeedsAdjudication?: { __typename?: 'NeedsAdjudicationApproveMutation', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, status: number, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null } | null } | null }; export type ApprovePaymentDetailsMutationVariables = Exact<{ grievanceTicketId: Scalars['ID']['input']; @@ -9901,7 +9975,7 @@ export type GrievanceTicketStatusChangeMutationVariables = Exact<{ }>; -export type GrievanceTicketStatusChangeMutation = { __typename?: 'Mutations', grievanceStatusChange?: { __typename?: 'GrievanceStatusChangeMutation', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', isDuplicate?: boolean | null, similarityScore?: string | null, individual1: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null }, individual2: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null } } | null, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null } | null }; +export type GrievanceTicketStatusChangeMutation = { __typename?: 'Mutations', grievanceStatusChange?: { __typename?: 'GrievanceStatusChangeMutation', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', isDuplicate?: boolean | null, similarityScore?: string | null, individual1: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null }, individual2: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null } } | null, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null } | null }; export type ReassignRoleGrievanceMutationVariables = Exact<{ grievanceTicketId: Scalars['ID']['input']; @@ -10675,7 +10749,7 @@ export type GrievanceTicketQueryVariables = Exact<{ }>; -export type GrievanceTicketQuery = { __typename?: 'Query', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', isDuplicate?: boolean | null, similarityScore?: string | null, individual1: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null }, individual2: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null } } | null, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null }; +export type GrievanceTicketQuery = { __typename?: 'Query', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', isDuplicate?: boolean | null, similarityScore?: string | null, individual1: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null }, individual2: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null } } | null, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null }; export type GrievanceTicketFlexFieldsQueryVariables = Exact<{ id: Scalars['ID']['input']; @@ -11088,7 +11162,7 @@ export type IndividualQueryVariables = Exact<{ }>; -export type IndividualQuery = { __typename?: 'Query', individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null }; +export type IndividualQuery = { __typename?: 'Query', individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, deliveryMechanismsData?: Array<{ __typename?: 'DeliveryMechanismDataNode', name?: string | null, isValid?: boolean | null, individualTabData?: any | null } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null }; export type IndividualChoiceDataQueryVariables = Exact<{ [key: string]: never; }>; @@ -11696,6 +11770,11 @@ export const IndividualDetailedFragmentDoc = gql` accountHolderName bankBranchName } + deliveryMechanismsData { + name + isValid + individualTabData + } documents { edges { node { @@ -24604,7 +24683,7 @@ export type DirectiveResolverFn> = { - Node: ( ApprovalProcessNode ) | ( AreaNode ) | ( AreaTypeNode ) | ( BankAccountInfoNode ) | ( BusinessAreaNode ) | ( CashPlanNode ) | ( CommunicationMessageNode ) | ( CommunicationMessageRecipientMapNode ) | ( DataCollectingTypeNode ) | ( DeduplicationEngineSimilarityPairNode ) | ( DeliveryMechanismNode ) | ( DeliveryMechanismPerPaymentPlanNode ) | ( DocumentNode ) | ( FeedbackMessageNode ) | ( FeedbackNode ) | ( FinancialServiceProviderNode ) | ( FinancialServiceProviderXlsxTemplateNode ) | ( GrievanceDocumentNode ) | ( GrievanceTicketNode ) | ( HouseholdNode ) | ( ImportDataNode ) | ( ImportedDocumentNode ) | ( ImportedHouseholdNode ) | ( ImportedIndividualIdentityNode ) | ( ImportedIndividualNode ) | ( IndividualIdentityNode ) | ( IndividualNode ) | ( KoboImportDataNode ) | ( LogEntryNode ) | ( PaymentHouseholdSnapshotNode ) | ( PaymentNode ) | ( PaymentPlanNode ) | ( PaymentRecordNode ) | ( PaymentVerificationLogEntryNode ) | ( PaymentVerificationNode ) | ( PaymentVerificationPlanNode ) | ( PaymentVerificationSummaryNode ) | ( PeriodicFieldNode ) | ( ProgramCycleNode ) | ( ProgramNode ) | ( RecipientNode ) | ( RegistrationDataImportDatahubNode ) | ( RegistrationDataImportNode ) | ( ReportNode ) | ( RuleCommitNode ) | ( SanctionListIndividualAliasNameNode ) | ( SanctionListIndividualCountriesNode ) | ( SanctionListIndividualDateOfBirthNode ) | ( SanctionListIndividualDocumentNode ) | ( SanctionListIndividualNationalitiesNode ) | ( SanctionListIndividualNode ) | ( ServiceProviderNode ) | ( 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 ) | ( BusinessAreaNode ) | ( CashPlanNode ) | ( CommunicationMessageNode ) | ( CommunicationMessageRecipientMapNode ) | ( DataCollectingTypeNode ) | ( DeduplicationEngineSimilarityPairNode ) | ( DeliveryMechanismDataNode ) | ( DeliveryMechanismNode ) | ( DeliveryMechanismPerPaymentPlanNode ) | ( DocumentNode ) | ( FeedbackMessageNode ) | ( FeedbackNode ) | ( FinancialServiceProviderNode ) | ( FinancialServiceProviderXlsxTemplateNode ) | ( GrievanceDocumentNode ) | ( GrievanceTicketNode ) | ( HouseholdNode ) | ( ImportDataNode ) | ( ImportedDocumentNode ) | ( ImportedHouseholdNode ) | ( ImportedIndividualIdentityNode ) | ( ImportedIndividualNode ) | ( IndividualIdentityNode ) | ( IndividualNode ) | ( KoboImportDataNode ) | ( LogEntryNode ) | ( PaymentHouseholdSnapshotNode ) | ( PaymentNode ) | ( PaymentPlanNode ) | ( PaymentRecordNode ) | ( PaymentVerificationLogEntryNode ) | ( PaymentVerificationNode ) | ( PaymentVerificationPlanNode ) | ( PaymentVerificationSummaryNode ) | ( PeriodicFieldNode ) | ( ProgramCycleNode ) | ( ProgramNode ) | ( RecipientNode ) | ( RegistrationDataImportDatahubNode ) | ( RegistrationDataImportNode ) | ( ReportNode ) | ( RuleCommitNode ) | ( SanctionListIndividualAliasNameNode ) | ( SanctionListIndividualCountriesNode ) | ( SanctionListIndividualDateOfBirthNode ) | ( SanctionListIndividualDocumentNode ) | ( SanctionListIndividualNationalitiesNode ) | ( SanctionListIndividualNode ) | ( ServiceProviderNode ) | ( 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 */ @@ -24727,8 +24806,13 @@ export type ResolversTypes = { DeleteTargetPopulationMutationInput: DeleteTargetPopulationMutationInput; DeleteTargetPopulationMutationPayload: ResolverTypeWrapper; DeliveredQuantityNode: ResolverTypeWrapper; + DeliveryMechanismDataDeliveryMechanismChoice: DeliveryMechanismDataDeliveryMechanismChoice; + DeliveryMechanismDataNode: ResolverTypeWrapper; + DeliveryMechanismDataNodeConnection: ResolverTypeWrapper; + DeliveryMechanismDataNodeEdge: ResolverTypeWrapper; DeliveryMechanismDataObjectType: DeliveryMechanismDataObjectType; DeliveryMechanismDataPayloadFieldObjectType: DeliveryMechanismDataPayloadFieldObjectType; + DeliveryMechanismDataRdiMergeStatus: DeliveryMechanismDataRdiMergeStatus; DeliveryMechanismNode: ResolverTypeWrapper; DeliveryMechanismNodeConnection: ResolverTypeWrapper; DeliveryMechanismNodeEdge: ResolverTypeWrapper; @@ -25261,6 +25345,9 @@ export type ResolversParentTypes = { DeleteTargetPopulationMutationInput: DeleteTargetPopulationMutationInput; DeleteTargetPopulationMutationPayload: DeleteTargetPopulationMutationPayload; DeliveredQuantityNode: DeliveredQuantityNode; + DeliveryMechanismDataNode: DeliveryMechanismDataNode; + DeliveryMechanismDataNodeConnection: DeliveryMechanismDataNodeConnection; + DeliveryMechanismDataNodeEdge: DeliveryMechanismDataNodeEdge; DeliveryMechanismDataObjectType: DeliveryMechanismDataObjectType; DeliveryMechanismDataPayloadFieldObjectType: DeliveryMechanismDataPayloadFieldObjectType; DeliveryMechanismNode: DeliveryMechanismNode; @@ -26341,9 +26428,42 @@ export type DeliveredQuantityNodeResolvers; }; +export type DeliveryMechanismDataNodeResolvers = { + createdAt?: Resolver; + data?: Resolver; + deliveryMechanism?: Resolver; + deliveryMechanismChoice?: Resolver; + id?: Resolver; + individual?: Resolver; + individualTabData?: Resolver, ParentType, ContextType>; + isValid?: Resolver, ParentType, ContextType>; + name?: Resolver, ParentType, ContextType>; + possibleDuplicateOf?: Resolver, ParentType, ContextType>; + possibleDuplicates?: Resolver>; + rdiMergeStatus?: Resolver; + updatedAt?: Resolver; + validationErrors?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type DeliveryMechanismDataNodeConnectionResolvers = { + edgeCount?: Resolver, ParentType, ContextType>; + edges?: Resolver>, ParentType, ContextType>; + pageInfo?: Resolver; + totalCount?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type DeliveryMechanismDataNodeEdgeResolvers = { + cursor?: Resolver; + node?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type DeliveryMechanismNodeResolvers = { code?: Resolver, ParentType, ContextType>; createdAt?: Resolver; + deliverymechanismdataSet?: Resolver>; deliverymechanismperpaymentplanSet?: Resolver>; financialserviceproviderSet?: Resolver>; id?: Resolver; @@ -27419,6 +27539,7 @@ export type IndividualNodeResolvers>>, ParentType, ContextType>; deduplicationGoldenRecordStatus?: Resolver; deleteIndividualTicketDetails?: Resolver>; + deliveryMechanismsData?: Resolver>>, ParentType, ContextType>; detailId?: Resolver, ParentType, ContextType>; disability?: Resolver; disabilityCertificatePicture?: Resolver, ParentType, ContextType>; @@ -27773,7 +27894,7 @@ export type NeedsAdjudicationApproveMutationResolvers = { - __resolveType: TypeResolveFn<'ApprovalProcessNode' | 'AreaNode' | 'AreaTypeNode' | 'BankAccountInfoNode' | 'BusinessAreaNode' | 'CashPlanNode' | 'CommunicationMessageNode' | 'CommunicationMessageRecipientMapNode' | 'DataCollectingTypeNode' | 'DeduplicationEngineSimilarityPairNode' | 'DeliveryMechanismNode' | 'DeliveryMechanismPerPaymentPlanNode' | 'DocumentNode' | 'FeedbackMessageNode' | 'FeedbackNode' | 'FinancialServiceProviderNode' | 'FinancialServiceProviderXlsxTemplateNode' | 'GrievanceDocumentNode' | 'GrievanceTicketNode' | 'HouseholdNode' | 'ImportDataNode' | 'ImportedDocumentNode' | 'ImportedHouseholdNode' | 'ImportedIndividualIdentityNode' | 'ImportedIndividualNode' | 'IndividualIdentityNode' | 'IndividualNode' | 'KoboImportDataNode' | 'LogEntryNode' | 'PaymentHouseholdSnapshotNode' | 'PaymentNode' | 'PaymentPlanNode' | 'PaymentRecordNode' | 'PaymentVerificationLogEntryNode' | 'PaymentVerificationNode' | 'PaymentVerificationPlanNode' | 'PaymentVerificationSummaryNode' | 'PeriodicFieldNode' | 'ProgramCycleNode' | 'ProgramNode' | 'RecipientNode' | 'RegistrationDataImportDatahubNode' | 'RegistrationDataImportNode' | 'ReportNode' | 'RuleCommitNode' | 'SanctionListIndividualAliasNameNode' | 'SanctionListIndividualCountriesNode' | 'SanctionListIndividualDateOfBirthNode' | 'SanctionListIndividualDocumentNode' | 'SanctionListIndividualNationalitiesNode' | 'SanctionListIndividualNode' | 'ServiceProviderNode' | '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' | 'BusinessAreaNode' | 'CashPlanNode' | 'CommunicationMessageNode' | 'CommunicationMessageRecipientMapNode' | 'DataCollectingTypeNode' | 'DeduplicationEngineSimilarityPairNode' | 'DeliveryMechanismDataNode' | 'DeliveryMechanismNode' | 'DeliveryMechanismPerPaymentPlanNode' | 'DocumentNode' | 'FeedbackMessageNode' | 'FeedbackNode' | 'FinancialServiceProviderNode' | 'FinancialServiceProviderXlsxTemplateNode' | 'GrievanceDocumentNode' | 'GrievanceTicketNode' | 'HouseholdNode' | 'ImportDataNode' | 'ImportedDocumentNode' | 'ImportedHouseholdNode' | 'ImportedIndividualIdentityNode' | 'ImportedIndividualNode' | 'IndividualIdentityNode' | 'IndividualNode' | 'KoboImportDataNode' | 'LogEntryNode' | 'PaymentHouseholdSnapshotNode' | 'PaymentNode' | 'PaymentPlanNode' | 'PaymentRecordNode' | 'PaymentVerificationLogEntryNode' | 'PaymentVerificationNode' | 'PaymentVerificationPlanNode' | 'PaymentVerificationSummaryNode' | 'PeriodicFieldNode' | 'ProgramCycleNode' | 'ProgramNode' | 'RecipientNode' | 'RegistrationDataImportDatahubNode' | 'RegistrationDataImportNode' | 'ReportNode' | 'RuleCommitNode' | 'SanctionListIndividualAliasNameNode' | 'SanctionListIndividualCountriesNode' | 'SanctionListIndividualDateOfBirthNode' | 'SanctionListIndividualDocumentNode' | 'SanctionListIndividualNationalitiesNode' | 'SanctionListIndividualNode' | 'ServiceProviderNode' | 'SteficonRuleNode' | 'SurveyNode' | 'TargetPopulationNode' | 'TicketAddIndividualDetailsNode' | 'TicketComplaintDetailsNode' | 'TicketDeleteHouseholdDetailsNode' | 'TicketDeleteIndividualDetailsNode' | 'TicketHouseholdDataUpdateDetailsNode' | 'TicketIndividualDataUpdateDetailsNode' | 'TicketNeedsAdjudicationDetailsNode' | 'TicketNegativeFeedbackDetailsNode' | 'TicketNoteNode' | 'TicketPaymentVerificationDetailsNode' | 'TicketPositiveFeedbackDetailsNode' | 'TicketReferralDetailsNode' | 'TicketSensitiveDetailsNode' | 'TicketSystemFlaggingDetailsNode' | 'UserBusinessAreaNode' | 'UserNode' | 'VolumeByDeliveryMechanismNode', ParentType, ContextType>; id?: Resolver; }; @@ -29985,6 +30106,9 @@ export type Resolvers = { DeleteRegistrationDataImport?: DeleteRegistrationDataImportResolvers; DeleteTargetPopulationMutationPayload?: DeleteTargetPopulationMutationPayloadResolvers; DeliveredQuantityNode?: DeliveredQuantityNodeResolvers; + DeliveryMechanismDataNode?: DeliveryMechanismDataNodeResolvers; + DeliveryMechanismDataNodeConnection?: DeliveryMechanismDataNodeConnectionResolvers; + DeliveryMechanismDataNodeEdge?: DeliveryMechanismDataNodeEdgeResolvers; DeliveryMechanismNode?: DeliveryMechanismNodeResolvers; DeliveryMechanismNodeConnection?: DeliveryMechanismNodeConnectionResolvers; DeliveryMechanismNodeEdge?: DeliveryMechanismNodeEdgeResolvers; diff --git a/src/frontend/src/__generated__/introspection-result.ts b/src/frontend/src/__generated__/introspection-result.ts index 30b0cfea56..79a522e3bc 100644 --- a/src/frontend/src/__generated__/introspection-result.ts +++ b/src/frontend/src/__generated__/introspection-result.ts @@ -17,6 +17,7 @@ "CommunicationMessageRecipientMapNode", "DataCollectingTypeNode", "DeduplicationEngineSimilarityPairNode", + "DeliveryMechanismDataNode", "DeliveryMechanismNode", "DeliveryMechanismPerPaymentPlanNode", "DocumentNode", diff --git a/src/frontend/src/apollo/fragments/IndividualFragments.ts b/src/frontend/src/apollo/fragments/IndividualFragments.ts index c4d00723d7..678b288c90 100644 --- a/src/frontend/src/apollo/fragments/IndividualFragments.ts +++ b/src/frontend/src/apollo/fragments/IndividualFragments.ts @@ -128,6 +128,11 @@ export const individualDetailed = gql` accountHolderName bankBranchName } + deliveryMechanismsData { + name + isValid + individualTabData + } documents { edges { node { diff --git a/src/hct_mis_api/apps/account/permissions.py b/src/hct_mis_api/apps/account/permissions.py index 0438ce5d24..0c97a2038d 100644 --- a/src/hct_mis_api/apps/account/permissions.py +++ b/src/hct_mis_api/apps/account/permissions.py @@ -43,6 +43,7 @@ def _generate_next_value_( # type: ignore # https://github.com/python/mypy/issu POPULATION_VIEW_HOUSEHOLDS_DETAILS = auto() POPULATION_VIEW_INDIVIDUALS_LIST = auto() POPULATION_VIEW_INDIVIDUALS_DETAILS = auto() + POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION = auto() # Programme PROGRAMME_VIEW_LIST_AND_DETAILS = auto() diff --git a/src/hct_mis_api/apps/household/schema.py b/src/hct_mis_api/apps/household/schema.py index ba0d921a0e..08ea4c51b2 100644 --- a/src/hct_mis_api/apps/household/schema.py +++ b/src/hct_mis_api/apps/household/schema.py @@ -82,6 +82,7 @@ from hct_mis_api.apps.household.services.household_programs_with_delivered_quantity import ( delivered_quantity_service, ) +from hct_mis_api.apps.payment.models import DeliveryMechanismData from hct_mis_api.apps.payment.utils import get_payment_items_for_dashboard from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.nodes import DeduplicationResultNode @@ -204,6 +205,26 @@ class Meta: connection_class = ExtendedConnection +class DeliveryMechanismDataNode(BaseNodePermissionMixin, DjangoObjectType): + permission_classes = (hopePermissionClass(Permissions.POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION),) + + name = graphene.String(required=False) + is_valid = graphene.Boolean() + individual_tab_data = graphene.JSONString() + + 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} + + class Meta: + model = DeliveryMechanismData + exclude = ("unique_key", "signature_hash") + interfaces = (relay.Node,) + connection_class = ExtendedConnection + + class IndividualNode(BaseNodePermissionMixin, AdminUrlNodeMixin, DjangoObjectType): permission_classes: Tuple[Type[BasePermission], ...] = ( hopePermissionClass(Permissions.POPULATION_VIEW_INDIVIDUALS_DETAILS), @@ -230,6 +251,7 @@ class IndividualNode(BaseNodePermissionMixin, AdminUrlNodeMixin, DjangoObjectTyp phone_no_alternative_valid = graphene.Boolean() payment_channels = graphene.List(BankAccountInfoNode) preferred_language = graphene.String() + delivery_mechanisms_data = graphene.List(DeliveryMechanismDataNode) @staticmethod def resolve_preferred_language(parent: Individual, info: Any) -> Optional[str]: @@ -287,6 +309,17 @@ def resolve_phone_no_valid(parent, info: Any) -> Boolean: def resolve_phone_no_alternative_valid(parent, info: Any) -> Boolean: return parent.phone_no_alternative_valid + def resolve_delivery_mechanisms_data(self, info): + program_id = get_program_id_from_headers(info.context.headers) + if not info.context.user.has_permission( + Permissions.POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION.value, + self.business_area, + program_id, + ): + return self.delivery_mechanisms_data.none() + + return self.delivery_mechanisms_data.all() + @classmethod def check_node_permission(cls, info: Any, object_instance: Individual) -> None: super().check_node_permission(info, object_instance) diff --git a/src/hct_mis_api/apps/payment/admin.py b/src/hct_mis_api/apps/payment/admin.py index a5f2414d21..9cfada2691 100644 --- a/src/hct_mis_api/apps/payment/admin.py +++ b/src/hct_mis_api/apps/payment/admin.py @@ -471,6 +471,7 @@ class DeliveryMechanismDataAdmin(HOPEModelAdminBase): list_display = ("individual", "delivery_mechanism", "is_valid") raw_id_fields = ("individual", "possible_duplicate_of") readonly_fields = ("possible_duplicate_of", "unique_key", "signature_hash", "validation_errors") + exclude = ("delivery_mechanism_choice",) @admin.register(DeliveryMechanism) diff --git a/src/hct_mis_api/apps/payment/migrations/0144_migration.py b/src/hct_mis_api/apps/payment/migrations/0144_migration.py new file mode 100644 index 0000000000..6ce4f70f91 --- /dev/null +++ b/src/hct_mis_api/apps/payment/migrations/0144_migration.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.25 on 2024-09-23 14:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0143_migration'), + ] + + operations = [ + migrations.AlterField( + model_name='deliverymechanismdata', + name='delivery_mechanism_choice', + field=models.CharField(blank=True, choices=[('Cardless cash withdrawal', 'Cardless cash withdrawal'), ('Cash', 'Cash'), ('Cash by FSP', 'Cash by FSP'), ('Cheque', 'Cheque'), ('Deposit to Card', 'Deposit to Card'), ('Mobile Money', 'Mobile Money'), ('Pre-paid card', 'Pre-paid card'), ('Referral', 'Referral'), ('Transfer', 'Transfer'), ('Transfer to Account', 'Transfer to Account'), ('Voucher', 'Voucher'), ('ATM Card', 'ATM Card'), ('Cash over the counter', 'Cash over the counter'), ('Transfer to Digital Wallet', 'Transfer to Digital Wallet')], max_length=255, null=True, verbose_name='Delivery Mechanism'), + ), + ] diff --git a/src/hct_mis_api/apps/payment/models.py b/src/hct_mis_api/apps/payment/models.py index af71414e68..69be7c479e 100644 --- a/src/hct_mis_api/apps/payment/models.py +++ b/src/hct_mis_api/apps/payment/models.py @@ -2242,7 +2242,11 @@ class DeliveryMechanismData(MergeStatusModel, TimeStampedUUIDModel, SignatureMix "household.Individual", on_delete=models.CASCADE, related_name="delivery_mechanisms_data" ) delivery_mechanism_choice = models.CharField( - max_length=255, verbose_name=_("Delivery Mechanism"), choices=DeliveryMechanismChoices.DELIVERY_TYPE_CHOICES + max_length=255, + verbose_name=_("Delivery Mechanism"), + choices=DeliveryMechanismChoices.DELIVERY_TYPE_CHOICES, + null=True, + blank=True, ) # TODO MB drop later delivery_mechanism = models.ForeignKey("DeliveryMechanism", on_delete=models.PROTECT) data = JSONField(default=dict, blank=True) @@ -2287,10 +2291,14 @@ def get_associated_object(self, associated_with: str) -> Any: associated_objects = { _INDIVIDUAL: self.individual, _HOUSEHOLD: self.individual.household, - _DELIVERY_MECHANISM_DATA: json.loads(self.data) if not isinstance(self.data, dict) else 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 = {} @@ -2360,6 +2368,10 @@ def delivery_mechanism_required_fields_definitions(self) -> List[dict]: def all_fields(self) -> List[dict]: return self.delivery_mechanism.all_fields + @property + def all_dm_fields(self) -> List[dict]: + return self.delivery_mechanism.all_dm_fields + @property def unique_fields(self) -> List[str]: return self.delivery_mechanism.unique_fields diff --git a/tests/unit/apps/household/snapshots/snap_test_individual_query.py b/tests/unit/apps/household/snapshots/snap_test_individual_query.py index 0d629a39fb..3ed1404fa6 100644 --- a/tests/unit/apps/household/snapshots/snap_test_individual_query.py +++ b/tests/unit/apps/household/snapshots/snap_test_individual_query.py @@ -812,6 +812,44 @@ ] } +snapshots['TestIndividualWithDeliveryMechanismsDataQuery::test_individual_query_delivery_mechanisms_data_0_with_permissions 1'] = { + 'data': { + 'individual': { + 'birthDate': '1943-07-30', + 'deliveryMechanismsData': [ + { + 'individualTabData': '{"card_number__atm_card": "123", "card_expiry_date__atm_card": "2022-01-01", "name_of_cardholder__atm_card": "Marek"}', + 'isValid': True, + 'name': 'ATM Card' + }, + { + 'individualTabData': '{"delivery_phone_number__mobile_money": "123456789", "provider__mobile_money": "Provider", "service_provider_code__mobile_money": "ABC"}', + 'isValid': False, + 'name': 'Mobile Money' + } + ], + 'familyName': 'Butler', + 'fullName': 'Benjamin Butler', + 'givenName': 'Benjamin', + 'phoneNo': '(953)682-4596' + } + } +} + +snapshots['TestIndividualWithDeliveryMechanismsDataQuery::test_individual_query_delivery_mechanisms_data_1_without_permissions 1'] = { + 'data': { + 'individual': { + 'birthDate': '1943-07-30', + 'deliveryMechanismsData': [ + ], + 'familyName': 'Butler', + 'fullName': 'Benjamin Butler', + 'givenName': 'Benjamin', + 'phoneNo': '(953)682-4596' + } + } +} + snapshots['TestIndividualWithFlexFieldsQuery::test_individual_query_single_with_flex_fields 1'] = { 'data': { 'individual': { diff --git a/tests/unit/apps/household/test_individual_query.py b/tests/unit/apps/household/test_individual_query.py index 0b7e2058a9..10269047d7 100644 --- a/tests/unit/apps/household/test_individual_query.py +++ b/tests/unit/apps/household/test_individual_query.py @@ -5,6 +5,11 @@ from constance.test import override_config from parameterized import parameterized +from hct_mis_api.apps.payment.fixtures import ( + generate_delivery_mechanisms, + DeliveryMechanismDataFactory, +) +from hct_mis_api.apps.payment.models import DeliveryMechanism from hct_mis_api.apps.account.fixtures import ( BusinessAreaFactory, PartnerFactory, @@ -113,7 +118,9 @@ def setUpTestData(cls) -> None: data_collecting_type=partial, ) - cls.household_one = HouseholdFactory.build(business_area=cls.business_area, program=cls.program) + cls.household_one = HouseholdFactory.build( + business_area=cls.business_area, program=cls.program + ) cls.household_one.household_collection.save() cls.household_one.registration_data_import.imported_by.save() cls.household_one.registration_data_import.program = cls.program @@ -180,17 +187,23 @@ def setUpTestData(cls) -> None: ] cls.individuals = [ - IndividualFactory(household=cls.household_one, program=cls.program, **individual) + IndividualFactory( + household=cls.household_one, program=cls.program, **individual + ) for index, individual in enumerate(cls.individuals_to_create) ] - cls.individuals_from_hh_one = [ind for ind in cls.individuals if ind.household == cls.household_one] + cls.individuals_from_hh_one = [ + ind for ind in cls.individuals if ind.household == cls.household_one + ] # cls.individuals_from_hh_two = [ind for ind in cls.individuals if ind.household == household_two] cls.household_one.head_of_household = cls.individuals_from_hh_one[0] # household_two.head_of_household = cls.individuals_from_hh_two[1] cls.household_one.save() # individual in program that cls.user does not have access to - cls.household_2 = HouseholdFactory.build(business_area=cls.business_area, program=cls.program) + cls.household_2 = HouseholdFactory.build( + business_area=cls.business_area, program=cls.program + ) cls.household_2.household_collection.save() cls.household_2.registration_data_import.imported_by.save() cls.household_2.registration_data_import.program = cls.program @@ -204,17 +217,25 @@ def setUpTestData(cls) -> None: "id": "8ff39244-2884-459b-ad14-8d63a6fe4a4a", } cls.individual_2 = IndividualFactory( - household=cls.household_2, program=cls.program_other, **cls.individual_to_create_2_data + household=cls.household_2, + program=cls.program_other, + **cls.individual_to_create_2_data, ) cls.household_2.head_of_household = cls.individual_2 cls.household_2.save() cls.bank_account_info = BankAccountInfoFactory( - individual=cls.individuals[5], bank_name="ING", bank_account_number=11110000222255558888999925 + individual=cls.individuals[5], + bank_name="ING", + bank_account_number=11110000222255558888999925, ) - cls.individual_unicef_id_to_search = Individual.objects.get(full_name="Benjamin Butler").unicef_id - cls.household_unicef_id_to_search = Individual.objects.get(full_name="Benjamin Butler").household.unicef_id + cls.individual_unicef_id_to_search = Individual.objects.get( + full_name="Benjamin Butler" + ).unicef_id + cls.household_unicef_id_to_search = Individual.objects.get( + full_name="Benjamin Butler" + ).household.unicef_id DocumentTypeFactory(key="national_id") DocumentTypeFactory(key="national_passport") @@ -274,13 +295,24 @@ def setUpTestData(cls) -> None: area_level=2, ) - cls.area1 = AreaFactory(name="City Test1", area_type=area_type_level_1, p_code="area1") - cls.area2 = AreaFactory(name="City Test2", area_type=area_type_level_2, p_code="area2", parent=cls.area1) + cls.area1 = AreaFactory( + name="City Test1", area_type=area_type_level_1, p_code="area1" + ) + cls.area2 = AreaFactory( + name="City Test2", + area_type=area_type_level_2, + p_code="area2", + parent=cls.area1, + ) cls.household_one.set_admin_areas(cls.area2) - cls.update_partner_access_to_program(cls.partner, cls.program, [cls.household_one.admin_area]) - cls.update_partner_access_to_program(cls.partner, cls.program_draft, [cls.household_one.admin_area]) + cls.update_partner_access_to_program( + cls.partner, cls.program, [cls.household_one.admin_area] + ) + cls.update_partner_access_to_program( + cls.partner, cls.program_draft, [cls.household_one.admin_area] + ) # remove after data migration migrate_data_to_representations() @@ -294,7 +326,9 @@ def setUpTestData(cls) -> None: ] ) def test_individual_query_all(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) self.snapshot_graphql_request( request_string=self.ALL_INDIVIDUALS_QUERY, @@ -313,8 +347,12 @@ def test_individual_query_all(self, _: Any, permissions: List[Permissions]) -> N ("without_permission", []), ] ) - def test_individual_query_single(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_individual_query_single( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) self.snapshot_graphql_request( request_string=self.INDIVIDUAL_QUERY, @@ -325,12 +363,17 @@ def test_individual_query_single(self, _: Any, permissions: List[Permissions]) - "Business-Area": self.business_area.slug, }, }, - variables={"id": self.id_to_base64(self.individuals[0].id, "IndividualNode")}, + variables={ + "id": self.id_to_base64(self.individuals[0].id, "IndividualNode") + }, ) def test_individual_query_single_different_program_in_header(self) -> None: self.create_user_role_with_permissions( - self.user, [Permissions.POPULATION_VIEW_INDIVIDUALS_DETAILS], self.business_area, self.program + self.user, + [Permissions.POPULATION_VIEW_INDIVIDUALS_DETAILS], + self.business_area, + self.program, ) self.snapshot_graphql_request( @@ -342,7 +385,9 @@ def test_individual_query_single_different_program_in_header(self) -> None: "Business-Area": self.business_area.slug, }, }, - variables={"id": self.id_to_base64(self.individuals[0].id, "IndividualNode")}, + variables={ + "id": self.id_to_base64(self.individuals[0].id, "IndividualNode") + }, ) @parameterized.expand( @@ -352,8 +397,12 @@ def test_individual_query_single_different_program_in_header(self) -> None: ] ) @skip("After merging GPF, remove 2nd program") - def test_individual_programme_filter(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program_two) + def test_individual_programme_filter( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program_two + ) self.snapshot_graphql_request( request_string=self.ALL_INDIVIDUALS_BY_PROGRAMME_QUERY, @@ -373,8 +422,12 @@ def test_individual_programme_filter(self, _: Any, permissions: List[Permissions ("without_permission", []), ] ) - def test_query_individuals_by_search_full_name_filter(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_query_individuals_by_search_full_name_filter( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) # Should be Jenna Franklin self.snapshot_graphql_request( @@ -391,7 +444,10 @@ def test_query_individuals_by_search_full_name_filter(self, _: Any, permissions: def test_individual_query_draft(self) -> None: self.create_user_role_with_permissions( - self.user, [Permissions.POPULATION_VIEW_INDIVIDUALS_LIST], self.business_area, self.program_draft + self.user, + [Permissions.POPULATION_VIEW_INDIVIDUALS_LIST], + self.business_area, + self.program_draft, ) self.snapshot_graphql_request( @@ -403,7 +459,9 @@ def test_individual_query_draft(self) -> None: "Business-Area": self.business_area.slug, }, }, - variables={"program": self.id_to_base64(self.program_draft.id, "ProgramNode")}, + variables={ + "program": self.id_to_base64(self.program_draft.id, "ProgramNode") + }, ) @parameterized.expand( @@ -412,8 +470,12 @@ def test_individual_query_draft(self) -> None: ("without_permission", []), ] ) - def test_query_individuals_by_search_phone_no_filter(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_query_individuals_by_search_phone_no_filter( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) # Should be Robin Ford self.snapshot_graphql_request( @@ -434,8 +496,12 @@ def test_query_individuals_by_search_phone_no_filter(self, _: Any, permissions: ("without_permission", []), ] ) - def test_query_individuals_by_search_national_id_filter(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_query_individuals_by_search_national_id_filter( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) # Should be Benjamin Butler self.snapshot_graphql_request( @@ -447,7 +513,10 @@ def test_query_individuals_by_search_national_id_filter(self, _: Any, permission "Business-Area": self.business_area.slug, }, }, - variables={"documentNumber": f"{self.national_id.document_number}", "documentType": "national_id"}, + variables={ + "documentNumber": f"{self.national_id.document_number}", + "documentType": "national_id", + }, ) @parameterized.expand( @@ -456,8 +525,12 @@ def test_query_individuals_by_search_national_id_filter(self, _: Any, permission ("without_permission", []), ] ) - def test_query_individuals_by_search_national_passport_filter(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_query_individuals_by_search_national_passport_filter( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) # Should be Robin Ford self.snapshot_graphql_request( @@ -481,8 +554,12 @@ def test_query_individuals_by_search_national_passport_filter(self, _: Any, perm ("without_permission", []), ] ) - def test_query_individuals_by_search_tax_id_filter(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_query_individuals_by_search_tax_id_filter( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) # Should be Timothy Perry self.snapshot_graphql_request( @@ -506,7 +583,9 @@ def test_query_individuals_by_search_tax_id_filter(self, _: Any, permissions: Li def test_query_individuals_by_search_bank_account_number_filter( self, _: Any, permissions: List[Permissions] ) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) # Should be James Bond self.snapshot_graphql_request( @@ -527,8 +606,12 @@ def test_query_individuals_by_search_bank_account_number_filter( ("without_permission", []), ] ) - def test_query_individuals_by_search_birth_certificate_filter(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_query_individuals_by_search_birth_certificate_filter( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) # Should be Jenna Franklin self.snapshot_graphql_request( @@ -540,7 +623,10 @@ def test_query_individuals_by_search_birth_certificate_filter(self, _: Any, perm "Business-Area": self.business_area.slug, }, }, - variables={"documentNumber": self.birth_certificate.document_number, "documentType": "birth_certificate"}, + variables={ + "documentNumber": self.birth_certificate.document_number, + "documentType": "birth_certificate", + }, ) @parameterized.expand( @@ -549,8 +635,12 @@ def test_query_individuals_by_search_birth_certificate_filter(self, _: Any, perm ("without_permission", []), ] ) - def test_query_individuals_by_search_disability_card_filter(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_query_individuals_by_search_disability_card_filter( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) # Should be Peter Parker self.snapshot_graphql_request( @@ -562,7 +652,10 @@ def test_query_individuals_by_search_disability_card_filter(self, _: Any, permis "Business-Area": self.business_area.slug, }, }, - variables={"documentNumber": self.disability_card.document_number, "documentType": "disability_card"}, + variables={ + "documentNumber": self.disability_card.document_number, + "documentType": "disability_card", + }, ) @parameterized.expand( @@ -571,8 +664,12 @@ def test_query_individuals_by_search_disability_card_filter(self, _: Any, permis ("without_permission", []), ] ) - def test_query_individuals_by_search_drivers_license_filter(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_query_individuals_by_search_drivers_license_filter( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) # Should be Benjamin Butler self.snapshot_graphql_request( @@ -584,7 +681,10 @@ def test_query_individuals_by_search_drivers_license_filter(self, _: Any, permis "Business-Area": self.business_area.slug, }, }, - variables={"documentNumber": self.drivers_license.document_number, "documentType": "drivers_license"}, + variables={ + "documentNumber": self.drivers_license.document_number, + "documentType": "drivers_license", + }, ) @parameterized.expand( @@ -593,8 +693,12 @@ def test_query_individuals_by_search_drivers_license_filter(self, _: Any, permis ("without_permission", []), ] ) - def test_query_individuals_by_admin2(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) + def test_query_individuals_by_admin2( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) self.snapshot_graphql_request( request_string=self.ALL_INDIVIDUALS_QUERY, @@ -610,7 +714,9 @@ def test_query_individuals_by_admin2(self, _: Any, permissions: List[Permissions def test_individual_query_all_for_all_programs(self) -> None: self.create_user_role_with_permissions( - self.user, [Permissions.POPULATION_VIEW_INDIVIDUALS_LIST], self.business_area + self.user, + [Permissions.POPULATION_VIEW_INDIVIDUALS_LIST], + self.business_area, ) self.snapshot_graphql_request( @@ -623,9 +729,13 @@ def test_individual_query_all_for_all_programs(self) -> None: }, ) - def test_individual_query_all_for_all_programs_user_with_no_program_access(self) -> None: + def test_individual_query_all_for_all_programs_user_with_no_program_access( + self, + ) -> None: self.create_user_role_with_permissions( - self.user_with_no_access, [Permissions.POPULATION_VIEW_INDIVIDUALS_LIST], self.business_area + self.user_with_no_access, + [Permissions.POPULATION_VIEW_INDIVIDUALS_LIST], + self.business_area, ) self.snapshot_graphql_request( request_string=self.ALL_INDIVIDUALS_QUERY, @@ -705,14 +815,26 @@ def setUpTestData(cls) -> None: # populate pdu fields with null values cls.individual.flex_fields = populate_pdu_with_null_values(cls.program, {}) # populate some values - in the Individual Query only populated values should be returned - cls.individual.flex_fields["pdu_field_1"]["1"] = {"value": 123.45, "collection_date": "2021-01-01"} - cls.individual.flex_fields["pdu_field_1"]["2"] = {"value": 234.56, "collection_date": "2021-01-01"} - cls.individual.flex_fields["pdu_field_2"]["4"] = {"value": "Value D", "collection_date": "2021-01-01"} + cls.individual.flex_fields["pdu_field_1"]["1"] = { + "value": 123.45, + "collection_date": "2021-01-01", + } + cls.individual.flex_fields["pdu_field_1"]["2"] = { + "value": 234.56, + "collection_date": "2021-01-01", + } + cls.individual.flex_fields["pdu_field_2"]["4"] = { + "value": "Value D", + "collection_date": "2021-01-01", + } cls.individual.save() def test_individual_query_single_with_flex_fields(self) -> None: self.create_user_role_with_permissions( - self.user, [Permissions.POPULATION_VIEW_INDIVIDUALS_DETAILS], self.business_area, self.program + self.user, + [Permissions.POPULATION_VIEW_INDIVIDUALS_DETAILS], + self.business_area, + self.program, ) self.snapshot_graphql_request( @@ -742,3 +864,111 @@ def test_individual_query_single_with_flex_fields(self) -> None: }, }, ) + + +class TestIndividualWithDeliveryMechanismsDataQuery(APITestCase): + databases = "__all__" + + INDIVIDUAL_QUERY = """ + query Individual($id: ID!) { + individual(id: $id) { + fullName + givenName + familyName + phoneNo + birthDate + deliveryMechanismsData { + name + isValid + individualTabData + } + } + } + """ + + @classmethod + def setUpTestData(cls) -> None: + super().setUpTestData() + cls.user = UserFactory() + cls.business_area = create_afghanistan() + generate_delivery_mechanisms() + cls.dm_atm_card = DeliveryMechanism.objects.get(code="atm_card") + cls.dm_mobile_money = DeliveryMechanism.objects.get(code="mobile_money") + + cls.program = ProgramFactory( + name="Test Program for Individual Query", + business_area=cls.business_area, + status=Program.ACTIVE, + ) + + household, individuals = create_household_and_individuals( + household_data={"business_area": cls.business_area, "program": cls.program}, + individuals_data=[ + { + "full_name": "Benjamin Butler", + "given_name": "Benjamin", + "family_name": "Butler", + "phone_no": "(953)682-4596", + "birth_date": "1943-07-30", + "id": "ffb2576b-126f-42de-b0f5-ef889b7bc1fe", + "business_area": cls.business_area, + }, + ], + ) + cls.individual = individuals[0] + DeliveryMechanismDataFactory( + individual=cls.individual, + delivery_mechanism=cls.dm_atm_card, + data={ + "card_number__atm_card": "123", + "card_expiry_date__atm_card": "2022-01-01", + "name_of_cardholder__atm_card": "Marek", + }, + is_valid=True, + ) + DeliveryMechanismDataFactory( + individual=cls.individual, + delivery_mechanism=cls.dm_mobile_money, + data={ + "service_provider_code__mobile_money": "ABC", + "delivery_phone_number__mobile_money": "123456789", + "provider__mobile_money": "Provider", + }, + is_valid=False, + ) + + @parameterized.expand( + [ + ( + "with_permissions", + [ + Permissions.POPULATION_VIEW_INDIVIDUALS_LIST, + Permissions.POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION, + ], + ), + ( + "without_permissions", + [ + Permissions.POPULATION_VIEW_INDIVIDUALS_LIST, + ], + ), + ] + ) + def test_individual_query_delivery_mechanisms_data( + self, _: Any, permissions: List[Permissions] + ) -> None: + self.create_user_role_with_permissions( + self.user, permissions, self.business_area, self.program + ) + + self.snapshot_graphql_request( + request_string=self.INDIVIDUAL_QUERY, + context={ + "user": self.user, + "headers": { + "Program": self.id_to_base64(self.program.id, "ProgramNode"), + "Business-Area": self.business_area.slug, + }, + }, + variables={"id": self.id_to_base64(self.individual.id, "IndividualNode")}, + ) From 856d56b29b91f25c0a728bc1b0a519674bf71d4e Mon Sep 17 00:00:00 2001 From: marekbiczysko Date: Mon, 23 Sep 2024 19:59:24 +0200 Subject: [PATCH 009/202] mypy --- src/hct_mis_api/apps/household/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hct_mis_api/apps/household/schema.py b/src/hct_mis_api/apps/household/schema.py index 08ea4c51b2..36d9045d8d 100644 --- a/src/hct_mis_api/apps/household/schema.py +++ b/src/hct_mis_api/apps/household/schema.py @@ -309,7 +309,7 @@ def resolve_phone_no_valid(parent, info: Any) -> Boolean: def resolve_phone_no_alternative_valid(parent, info: Any) -> Boolean: return parent.phone_no_alternative_valid - def resolve_delivery_mechanisms_data(self, info): + def resolve_delivery_mechanisms_data(self, info) -> QuerySet[DeliveryMechanismData]: program_id = get_program_id_from_headers(info.context.headers) if not info.context.user.has_permission( Permissions.POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION.value, From 03cf0db84e483c0cbdb85069c40aff4b856e8213 Mon Sep 17 00:00:00 2001 From: marekbiczysko Date: Mon, 23 Sep 2024 20:15:08 +0200 Subject: [PATCH 010/202] mypy --- src/hct_mis_api/apps/household/schema.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hct_mis_api/apps/household/schema.py b/src/hct_mis_api/apps/household/schema.py index 36d9045d8d..dca7079fbf 100644 --- a/src/hct_mis_api/apps/household/schema.py +++ b/src/hct_mis_api/apps/household/schema.py @@ -309,16 +309,16 @@ def resolve_phone_no_valid(parent, info: Any) -> Boolean: def resolve_phone_no_alternative_valid(parent, info: Any) -> Boolean: return parent.phone_no_alternative_valid - def resolve_delivery_mechanisms_data(self, info) -> QuerySet[DeliveryMechanismData]: + def resolve_delivery_mechanisms_data(parent, info: Any) -> QuerySet[DeliveryMechanismData]: program_id = get_program_id_from_headers(info.context.headers) if not info.context.user.has_permission( Permissions.POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION.value, - self.business_area, + parent.business_area, program_id, ): - return self.delivery_mechanisms_data.none() + return parent.delivery_mechanisms_data.none() - return self.delivery_mechanisms_data.all() + return parent.delivery_mechanisms_data.all() @classmethod def check_node_permission(cls, info: Any, object_instance: Individual) -> None: From 990417350af4e1af34f7ffd1174c58fd71ae1cf3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 00:39:17 +0000 Subject: [PATCH 011/202] Bump rollup from 4.16.4 to 4.22.4 in /src/frontend Bumps [rollup](https://github.com/rollup/rollup) from 4.16.4 to 4.22.4. - [Release notes](https://github.com/rollup/rollup/releases) - [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md) - [Commits](https://github.com/rollup/rollup/compare/v4.16.4...v4.22.4) --- updated-dependencies: - dependency-name: rollup dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/frontend/yarn.lock | 196 ++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index 118293b739..3ceaf4bf0f 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -2601,85 +2601,85 @@ resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.5.tgz#b77571685410217a548a9c753aa3cdfc215bfc78" integrity sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA== -"@rollup/rollup-android-arm-eabi@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.16.4.tgz#5e8930291f1e5ead7fb1171d53ba5c87718de062" - integrity sha512-GkhjAaQ8oUTOKE4g4gsZ0u8K/IHU1+2WQSgS1TwTcYvL+sjbaQjNHFXbOJ6kgqGHIO1DfUhI/Sphi9GkRT9K+Q== - -"@rollup/rollup-android-arm64@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.16.4.tgz#ffb84f1359c04ec8a022a97110e18a5600f5f638" - integrity sha512-Bvm6D+NPbGMQOcxvS1zUl8H7DWlywSXsphAeOnVeiZLQ+0J6Is8T7SrjGTH29KtYkiY9vld8ZnpV3G2EPbom+w== - -"@rollup/rollup-darwin-arm64@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.16.4.tgz#b2fcee8d4806a0b1b9185ac038cc428ddedce9f4" - integrity sha512-i5d64MlnYBO9EkCOGe5vPR/EeDwjnKOGGdd7zKFhU5y8haKhQZTN2DgVtpODDMxUr4t2K90wTUJg7ilgND6bXw== - -"@rollup/rollup-darwin-x64@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.16.4.tgz#fcb25ccbaa3dd33a6490e9d1c64bab2e0e16b932" - integrity sha512-WZupV1+CdUYehaZqjaFTClJI72fjJEgTXdf4NbW69I9XyvdmztUExBtcI2yIIU6hJtYvtwS6pkTkHJz+k08mAQ== - -"@rollup/rollup-linux-arm-gnueabihf@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.16.4.tgz#40d46bdfe667e5eca31bf40047460e326d2e26bb" - integrity sha512-ADm/xt86JUnmAfA9mBqFcRp//RVRt1ohGOYF6yL+IFCYqOBNwy5lbEK05xTsEoJq+/tJzg8ICUtS82WinJRuIw== - -"@rollup/rollup-linux-arm-musleabihf@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.16.4.tgz#7741df2448c11c56588b50835dbfe91b1a10b375" - integrity sha512-tJfJaXPiFAG+Jn3cutp7mCs1ePltuAgRqdDZrzb1aeE3TktWWJ+g7xK9SNlaSUFw6IU4QgOxAY4rA+wZUT5Wfg== - -"@rollup/rollup-linux-arm64-gnu@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.16.4.tgz#0a23b02d2933e4c4872ad18d879890b6a4a295df" - integrity sha512-7dy1BzQkgYlUTapDTvK997cgi0Orh5Iu7JlZVBy1MBURk7/HSbHkzRnXZa19ozy+wwD8/SlpJnOOckuNZtJR9w== - -"@rollup/rollup-linux-arm64-musl@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.16.4.tgz#e37ef259358aa886cc07d782220a4fb83c1e6970" - integrity sha512-zsFwdUw5XLD1gQe0aoU2HVceI6NEW7q7m05wA46eUAyrkeNYExObfRFQcvA6zw8lfRc5BHtan3tBpo+kqEOxmg== - -"@rollup/rollup-linux-powerpc64le-gnu@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.16.4.tgz#8c69218b6de05ee2ba211664a2d2ac1e54e43f94" - integrity sha512-p8C3NnxXooRdNrdv6dBmRTddEapfESEUflpICDNKXpHvTjRRq1J82CbU5G3XfebIZyI3B0s074JHMWD36qOW6w== - -"@rollup/rollup-linux-riscv64-gnu@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.16.4.tgz#d32727dab8f538d9a4a7c03bcf58c436aecd0139" - integrity sha512-Lh/8ckoar4s4Id2foY7jNgitTOUQczwMWNYi+Mjt0eQ9LKhr6sK477REqQkmy8YHY3Ca3A2JJVdXnfb3Rrwkng== - -"@rollup/rollup-linux-s390x-gnu@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.16.4.tgz#d46097246a187d99fc9451fe8393b7155b47c5ec" - integrity sha512-1xwwn9ZCQYuqGmulGsTZoKrrn0z2fAur2ujE60QgyDpHmBbXbxLaQiEvzJWDrscRq43c8DnuHx3QorhMTZgisQ== - -"@rollup/rollup-linux-x64-gnu@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.16.4.tgz#6356c5a03a4afb1c3057490fc51b4764e109dbc7" - integrity sha512-LuOGGKAJ7dfRtxVnO1i3qWc6N9sh0Em/8aZ3CezixSTM+E9Oq3OvTsvC4sm6wWjzpsIlOCnZjdluINKESflJLA== - -"@rollup/rollup-linux-x64-musl@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.16.4.tgz#03a5831a9c0d05877b94653b5ddd3020d3c6fb06" - integrity sha512-ch86i7KkJKkLybDP2AtySFTRi5fM3KXp0PnHocHuJMdZwu7BuyIKi35BE9guMlmTpwwBTB3ljHj9IQXnTCD0vA== - -"@rollup/rollup-win32-arm64-msvc@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.16.4.tgz#6cc0db57750376b9303bdb6f5482af8974fcae35" - integrity sha512-Ma4PwyLfOWZWayfEsNQzTDBVW8PZ6TUUN1uFTBQbF2Chv/+sjenE86lpiEwj2FiviSmSZ4Ap4MaAfl1ciF4aSA== - -"@rollup/rollup-win32-ia32-msvc@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.16.4.tgz#aea0b7e492bd9ed46971cb80bc34f1eb14e07789" - integrity sha512-9m/ZDrQsdo/c06uOlP3W9G2ENRVzgzbSXmXHT4hwVaDQhYcRpi9bgBT0FTG9OhESxwK0WjQxYOSfv40cU+T69w== - -"@rollup/rollup-win32-x64-msvc@4.16.4": - version "4.16.4" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.16.4.tgz#c09ad9a132ccb5a67c4f211d909323ab1294f95f" - integrity sha512-YunpoOAyGLDseanENHmbFvQSfVL5BxW3k7hhy0eN4rb3gS/ct75dVD0EXOWIqFT/nE8XYW6LP6vz6ctKRi0k9A== +"@rollup/rollup-android-arm-eabi@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz#8b613b9725e8f9479d142970b106b6ae878610d5" + integrity sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w== + +"@rollup/rollup-android-arm64@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz#654ca1049189132ff602bfcf8df14c18da1f15fb" + integrity sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA== + +"@rollup/rollup-darwin-arm64@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz#6d241d099d1518ef0c2205d96b3fa52e0fe1954b" + integrity sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q== + +"@rollup/rollup-darwin-x64@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz#42bd19d292a57ee11734c980c4650de26b457791" + integrity sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw== + +"@rollup/rollup-linux-arm-gnueabihf@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz#f23555ee3d8fe941c5c5fd458cd22b65eb1c2232" + integrity sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ== + +"@rollup/rollup-linux-arm-musleabihf@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz#f3bbd1ae2420f5539d40ac1fde2b38da67779baa" + integrity sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg== + +"@rollup/rollup-linux-arm64-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz#7abe900120113e08a1f90afb84c7c28774054d15" + integrity sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw== + +"@rollup/rollup-linux-arm64-musl@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz#9e655285c8175cd44f57d6a1e8e5dedfbba1d820" + integrity sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA== + +"@rollup/rollup-linux-powerpc64le-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz#9a79ae6c9e9d8fe83d49e2712ecf4302db5bef5e" + integrity sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg== + +"@rollup/rollup-linux-riscv64-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz#67ac70eca4ace8e2942fabca95164e8874ab8128" + integrity sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA== + +"@rollup/rollup-linux-s390x-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz#9f883a7440f51a22ed7f99e1d070bd84ea5005fc" + integrity sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q== + +"@rollup/rollup-linux-x64-gnu@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz#70116ae6c577fe367f58559e2cffb5641a1dd9d0" + integrity sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg== + +"@rollup/rollup-linux-x64-musl@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz#f473f88219feb07b0b98b53a7923be716d1d182f" + integrity sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g== + +"@rollup/rollup-win32-arm64-msvc@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz#4349482d17f5d1c58604d1c8900540d676f420e0" + integrity sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw== + +"@rollup/rollup-win32-ia32-msvc@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz#a6fc39a15db618040ec3c2a24c1e26cb5f4d7422" + integrity sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g== + +"@rollup/rollup-win32-x64-msvc@4.22.4": + version "4.22.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz#3dd5d53e900df2a40841882c02e56f866c04d202" + integrity sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q== "@sentry-internal/feedback@7.112.1": version "7.112.1" @@ -9141,28 +9141,28 @@ rimraf@^3.0.2: glob "^7.1.3" rollup@^4.13.0: - version "4.16.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.16.4.tgz#fe328eb41293f20c9593a095ec23bdc4b5d93317" - integrity sha512-kuaTJSUbz+Wsb2ATGvEknkI12XV40vIiHmLuFlejoo7HtDok/O5eDDD0UpCVY5bBX5U5RYo8wWP83H7ZsqVEnA== + version "4.22.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.22.4.tgz#4135a6446671cd2a2453e1ad42a45d5973ec3a0f" + integrity sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A== dependencies: "@types/estree" "1.0.5" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.16.4" - "@rollup/rollup-android-arm64" "4.16.4" - "@rollup/rollup-darwin-arm64" "4.16.4" - "@rollup/rollup-darwin-x64" "4.16.4" - "@rollup/rollup-linux-arm-gnueabihf" "4.16.4" - "@rollup/rollup-linux-arm-musleabihf" "4.16.4" - "@rollup/rollup-linux-arm64-gnu" "4.16.4" - "@rollup/rollup-linux-arm64-musl" "4.16.4" - "@rollup/rollup-linux-powerpc64le-gnu" "4.16.4" - "@rollup/rollup-linux-riscv64-gnu" "4.16.4" - "@rollup/rollup-linux-s390x-gnu" "4.16.4" - "@rollup/rollup-linux-x64-gnu" "4.16.4" - "@rollup/rollup-linux-x64-musl" "4.16.4" - "@rollup/rollup-win32-arm64-msvc" "4.16.4" - "@rollup/rollup-win32-ia32-msvc" "4.16.4" - "@rollup/rollup-win32-x64-msvc" "4.16.4" + "@rollup/rollup-android-arm-eabi" "4.22.4" + "@rollup/rollup-android-arm64" "4.22.4" + "@rollup/rollup-darwin-arm64" "4.22.4" + "@rollup/rollup-darwin-x64" "4.22.4" + "@rollup/rollup-linux-arm-gnueabihf" "4.22.4" + "@rollup/rollup-linux-arm-musleabihf" "4.22.4" + "@rollup/rollup-linux-arm64-gnu" "4.22.4" + "@rollup/rollup-linux-arm64-musl" "4.22.4" + "@rollup/rollup-linux-powerpc64le-gnu" "4.22.4" + "@rollup/rollup-linux-riscv64-gnu" "4.22.4" + "@rollup/rollup-linux-s390x-gnu" "4.22.4" + "@rollup/rollup-linux-x64-gnu" "4.22.4" + "@rollup/rollup-linux-x64-musl" "4.22.4" + "@rollup/rollup-win32-arm64-msvc" "4.22.4" + "@rollup/rollup-win32-ia32-msvc" "4.22.4" + "@rollup/rollup-win32-x64-msvc" "4.22.4" fsevents "~2.3.2" rsvp@^4.8.4: From a14f1169ac734238dc41036caa93ba2c777bdf0d Mon Sep 17 00:00:00 2001 From: Paulina Kujawa Date: Wed, 25 Sep 2024 21:03:20 +0200 Subject: [PATCH 012/202] move all the changes into new structure --- src/frontend/data/schema.graphql | 16 +- src/frontend/src/__generated__/graphql.tsx | 83 ++- .../program/UpdateProgramPartners.ts | 14 + .../editProgramValidationSchema.ts | 9 +- .../programs/EditProgram/DetailsStep.tsx | 62 ++ .../programs/EditProgram/EditProgramMenu.tsx | 91 +++ .../programs/EditProgram/PartnersStep.tsx | 161 ++++++ .../EditProgram/ProgramFieldSeriesStep.tsx | 260 +++++++++ .../ProgramDetails/ProgramDetails.tsx | 4 +- .../ProgramDetails.test.tsx.snap | 2 +- .../src/components/programs/constants.tsx | 6 +- .../ActiveProgramDetailsPageHeaderButtons.tsx | 10 +- .../DraftProgramDetailsPageHeaderButtons.tsx | 6 +- .../pages/program/EditProgramPage.tsx | 383 +++++++------ src/frontend/src/utils/en.json | 3 +- src/hct_mis_api/apps/account/admin/partner.py | 5 +- .../apps/account/fixtures/data.json | 14 +- .../apps/account/migrations/0077_migration.py | 26 + src/hct_mis_api/apps/account/models.py | 2 +- src/hct_mis_api/apps/account/schema.py | 2 +- src/hct_mis_api/apps/account/signals.py | 33 +- src/hct_mis_api/apps/core/mixins.py | 2 +- src/hct_mis_api/apps/core/signals.py | 16 +- .../apps/payment/migrations/0144_migration.py | 19 + src/hct_mis_api/apps/program/inputs.py | 8 +- src/hct_mis_api/apps/program/models.py | 1 + src/hct_mis_api/apps/program/mutations.py | 66 ++- src/hct_mis_api/apps/program/utils.py | 2 +- .../steficon/migrations/0020_migration.py | 19 + .../migrate_partner_permissions_and_access.py | 78 --- .../programme_details/programme_details.py | 10 +- .../programme_management.py | 19 +- .../program_details/test_program_details.py | 8 +- .../test_programme_management.py | 100 +++- .../account/test_signal_change_allowed_ba.py | 70 ++- .../apps/account/test_user_choice_data.py | 12 +- .../core/test_signal_remove_partner_role.py | 69 +++ .../snapshots/snap_test_update_program.py | 541 ------------------ .../snap_test_update_program_partners.py | 401 +++++++++++++ .../apps/program/test_all_programs_query.py | 5 +- .../program/test_change_program_status.py | 1 - tests/unit/apps/program/test_copy_program.py | 6 +- .../unit/apps/program/test_create_program.py | 17 +- .../test_signal_partner_access_change.py | 30 +- .../unit/apps/program/test_update_program.py | 250 +------- .../program/test_update_program_partners.py | 378 ++++++++++++ ..._migrate_partner_permissions_and_access.py | 364 ------------ 47 files changed, 2141 insertions(+), 1543 deletions(-) create mode 100644 src/frontend/src/apollo/mutations/program/UpdateProgramPartners.ts create mode 100644 src/frontend/src/components/programs/EditProgram/DetailsStep.tsx create mode 100644 src/frontend/src/components/programs/EditProgram/EditProgramMenu.tsx create mode 100644 src/frontend/src/components/programs/EditProgram/PartnersStep.tsx create mode 100644 src/frontend/src/components/programs/EditProgram/ProgramFieldSeriesStep.tsx create mode 100644 src/hct_mis_api/apps/account/migrations/0077_migration.py create mode 100644 src/hct_mis_api/apps/payment/migrations/0144_migration.py create mode 100644 src/hct_mis_api/apps/steficon/migrations/0020_migration.py delete mode 100644 src/hct_mis_api/one_time_scripts/migrate_partner_permissions_and_access.py create mode 100644 tests/unit/apps/core/test_signal_remove_partner_role.py create mode 100644 tests/unit/apps/program/snapshots/snap_test_update_program_partners.py create mode 100644 tests/unit/apps/program/test_update_program_partners.py delete mode 100644 tests/unit/one_time_scripts/test_migrate_partner_permissions_and_access.py diff --git a/src/frontend/data/schema.graphql b/src/frontend/data/schema.graphql index e1a3a1379d..398d37a178 100644 --- a/src/frontend/data/schema.graphql +++ b/src/frontend/data/schema.graphql @@ -2876,6 +2876,7 @@ type Mutations { targetPopulationRebuild(id: ID!): RebuildTargetPopulationMutation createProgram(programData: CreateProgramInput!): CreateProgram updateProgram(programData: UpdateProgramInput, version: BigInt): UpdateProgram + updateProgramPartners(programData: UpdateProgramPartnersInput, version: BigInt): UpdateProgramPartners deleteProgram(programId: String!): DeleteProgram copyProgram(programData: CopyProgramInput!): CopyProgram uploadImportDataXlsxFileAsync(businessAreaSlug: String!, file: Upload!): UploadImportDataXLSXFileAsync @@ -2947,7 +2948,6 @@ type PartnerNode { name: String parent: PartnerNode isUn: Boolean! - permissions: JSONString! lft: Int! rght: Int! treeId: Int! @@ -2977,7 +2977,6 @@ type PartnerType { name: String! parent: PartnerNode isUn: Boolean! - permissions: JSONString! lft: Int! rght: Int! treeId: Int! @@ -5334,12 +5333,21 @@ input UpdateProgramInput { populationGoal: Int administrativeAreasOfImplementation: String dataCollectingTypeCode: String - partners: [ProgramPartnerThroughInput] - partnerAccess: String programmeCode: String pduFields: [PDUFieldInput] } +type UpdateProgramPartners { + validationErrors: Arg + program: ProgramNode +} + +input UpdateProgramPartnersInput { + id: String! + partners: [ProgramPartnerThroughInput] + partnerAccess: String +} + input UpdateTargetPopulationInput { id: ID! name: String diff --git a/src/frontend/src/__generated__/graphql.tsx b/src/frontend/src/__generated__/graphql.tsx index 33d46bf97e..be6ae298b6 100644 --- a/src/frontend/src/__generated__/graphql.tsx +++ b/src/frontend/src/__generated__/graphql.tsx @@ -4104,6 +4104,7 @@ export type Mutations = { updatePaymentVerificationReceivedAndReceivedAmount?: Maybe; updatePaymentVerificationStatusAndReceivedAmount?: Maybe; updateProgram?: Maybe; + updateProgramPartners?: Maybe; updateTargetPopulation?: Maybe; uploadImportDataXlsxFileAsync?: Maybe; }; @@ -4591,6 +4592,12 @@ export type MutationsUpdateProgramArgs = { }; +export type MutationsUpdateProgramPartnersArgs = { + programData?: InputMaybe; + version?: InputMaybe; +}; + + export type MutationsUpdateTargetPopulationArgs = { input: UpdateTargetPopulationInput; version?: InputMaybe; @@ -4674,7 +4681,6 @@ export type PartnerNode = { name?: Maybe; parent?: Maybe; partnerSet: Array; - permissions: Scalars['JSONString']['output']; programs: ProgramNodeConnection; rght: Scalars['Int']['output']; treeId: Scalars['Int']['output']; @@ -4761,7 +4767,6 @@ export type PartnerType = { name: Scalars['String']['output']; parent?: Maybe; partnerSet: Array; - permissions: Scalars['JSONString']['output']; programs: ProgramNodeConnection; rght: Scalars['Int']['output']; treeId: Scalars['Int']['output']; @@ -8990,8 +8995,6 @@ export type UpdateProgramInput = { frequencyOfPayments?: InputMaybe; id: Scalars['String']['input']; name?: InputMaybe; - partnerAccess?: InputMaybe; - partners?: InputMaybe>>; pduFields?: InputMaybe>>; populationGoal?: InputMaybe; programmeCode?: InputMaybe; @@ -9000,6 +9003,20 @@ export type UpdateProgramInput = { status?: InputMaybe; }; + +export type UpdateProgramPartners = { + __typename?: 'UpdateProgramPartners'; + program?: Maybe; + validationErrors?: Maybe; +}; + +export type UpdateProgramPartnersInput = { + id: Scalars['String']['input']; + partnerAccess?: InputMaybe; + partners?: InputMaybe>>; +}; + + export type UpdateTargetPopulationInput = { excludedIds?: InputMaybe; exclusionReason?: InputMaybe; @@ -10179,6 +10196,14 @@ 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 UpdateProgramPartnersMutationVariables = Exact<{ + programData?: InputMaybe; + version?: InputMaybe; +}>; + + +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 CreateRegistrationKoboImportMutationVariables = Exact<{ registrationDataImportData: RegistrationKoboImportMutationInput; }>; @@ -15581,6 +15606,42 @@ export function useUpdateProgramMutation(baseOptions?: Apollo.MutationHookOption export type UpdateProgramMutationHookResult = ReturnType; export type UpdateProgramMutationResult = Apollo.MutationResult; export type UpdateProgramMutationOptions = Apollo.BaseMutationOptions; +export const UpdateProgramPartnersDocument = gql` + mutation UpdateProgramPartners($programData: UpdateProgramPartnersInput, $version: BigInt) { + updateProgramPartners(programData: $programData, version: $version) { + program { + ...programDetails + } + } +} + ${ProgramDetailsFragmentDoc}`; +export type UpdateProgramPartnersMutationFn = Apollo.MutationFunction; + +/** + * __useUpdateProgramPartnersMutation__ + * + * To run a mutation, you first call `useUpdateProgramPartnersMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateProgramPartnersMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateProgramPartnersMutation, { data, loading, error }] = useUpdateProgramPartnersMutation({ + * variables: { + * programData: // value for 'programData' + * version: // value for 'version' + * }, + * }); + */ +export function useUpdateProgramPartnersMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(UpdateProgramPartnersDocument, options); + } +export type UpdateProgramPartnersMutationHookResult = ReturnType; +export type UpdateProgramPartnersMutationResult = Apollo.MutationResult; +export type UpdateProgramPartnersMutationOptions = Apollo.BaseMutationOptions; export const CreateRegistrationKoboImportDocument = gql` mutation CreateRegistrationKoboImport($registrationDataImportData: RegistrationKoboImportMutationInput!) { registrationKoboImport(registrationDataImportData: $registrationDataImportData) { @@ -25124,6 +25185,8 @@ export type ResolversTypes = { UpdatePaymentVerificationStatusAndReceivedAmount: ResolverTypeWrapper; UpdateProgram: ResolverTypeWrapper; UpdateProgramInput: UpdateProgramInput; + UpdateProgramPartners: ResolverTypeWrapper; + UpdateProgramPartnersInput: UpdateProgramPartnersInput; UpdateTargetPopulationInput: UpdateTargetPopulationInput; UpdateTargetPopulationMutation: ResolverTypeWrapper; Upload: ResolverTypeWrapper; @@ -25588,6 +25651,8 @@ export type ResolversParentTypes = { UpdatePaymentVerificationStatusAndReceivedAmount: UpdatePaymentVerificationStatusAndReceivedAmount; UpdateProgram: UpdateProgram; UpdateProgramInput: UpdateProgramInput; + UpdateProgramPartners: UpdateProgramPartners; + UpdateProgramPartnersInput: UpdateProgramPartnersInput; UpdateTargetPopulationInput: UpdateTargetPopulationInput; UpdateTargetPopulationMutation: UpdateTargetPopulationMutation; Upload: Scalars['Upload']['output']; @@ -27763,6 +27828,7 @@ export type MutationsResolvers, ParentType, ContextType, RequireFields>; updatePaymentVerificationStatusAndReceivedAmount?: Resolver, ParentType, ContextType, RequireFields>; updateProgram?: Resolver, ParentType, ContextType, Partial>; + updateProgramPartners?: Resolver, ParentType, ContextType, Partial>; updateTargetPopulation?: Resolver, ParentType, ContextType, RequireFields>; uploadImportDataXlsxFileAsync?: Resolver, ParentType, ContextType, RequireFields>; }; @@ -27828,7 +27894,6 @@ export type PartnerNodeResolvers, ParentType, ContextType>; parent?: Resolver, ParentType, ContextType>; partnerSet?: Resolver, ParentType, ContextType>; - permissions?: Resolver; programs?: Resolver>; rght?: Resolver; treeId?: Resolver; @@ -27858,7 +27923,6 @@ export type PartnerTypeResolvers; parent?: Resolver, ParentType, ContextType>; partnerSet?: Resolver, ParentType, ContextType>; - permissions?: Resolver; programs?: Resolver>; rght?: Resolver; treeId?: Resolver; @@ -29690,6 +29754,12 @@ export type UpdateProgramResolvers; }; +export type UpdateProgramPartnersResolvers = { + program?: Resolver, ParentType, ContextType>; + validationErrors?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type UpdateTargetPopulationMutationResolvers = { targetPopulation?: Resolver, ParentType, ContextType>; validationErrors?: Resolver, ParentType, ContextType>; @@ -30258,6 +30328,7 @@ export type Resolvers = { UpdatePaymentVerificationReceivedAndReceivedAmount?: UpdatePaymentVerificationReceivedAndReceivedAmountResolvers; UpdatePaymentVerificationStatusAndReceivedAmount?: UpdatePaymentVerificationStatusAndReceivedAmountResolvers; UpdateProgram?: UpdateProgramResolvers; + UpdateProgramPartners?: UpdateProgramPartnersResolvers; UpdateTargetPopulationMutation?: UpdateTargetPopulationMutationResolvers; Upload?: GraphQLScalarType; UploadImportDataXLSXFileAsync?: UploadImportDataXlsxFileAsyncResolvers; diff --git a/src/frontend/src/apollo/mutations/program/UpdateProgramPartners.ts b/src/frontend/src/apollo/mutations/program/UpdateProgramPartners.ts new file mode 100644 index 0000000000..50ea386438 --- /dev/null +++ b/src/frontend/src/apollo/mutations/program/UpdateProgramPartners.ts @@ -0,0 +1,14 @@ +import { gql } from '@apollo/client'; + +export const UPDATE_PROGRAM_PARTNERS_MUTATION = gql` + mutation UpdateProgramPartners( + $programData: UpdateProgramPartnersInput + $version: BigInt + ) { + updateProgramPartners(programData: $programData, version: $version) { + program { + ...programDetails + } + } + } +`; diff --git a/src/frontend/src/components/programs/CreateProgram/editProgramValidationSchema.ts b/src/frontend/src/components/programs/CreateProgram/editProgramValidationSchema.ts index 0f33dcbfaa..2588c52eca 100644 --- a/src/frontend/src/components/programs/CreateProgram/editProgramValidationSchema.ts +++ b/src/frontend/src/components/programs/CreateProgram/editProgramValidationSchema.ts @@ -3,7 +3,7 @@ import moment from 'moment'; import { today } from '@utils/utils'; import { TFunction } from 'i18next'; -export const editProgramValidationSchema = ( +export const editProgramDetailsValidationSchema = ( t: TFunction<'translation', undefined>, initialValues: any, ): Yup.ObjectSchema => { @@ -58,6 +58,13 @@ export const editProgramValidationSchema = ( .max(255, t('Too long')) .nullable(), populationGoal: Yup.number().min(0).max(99999999, t('Number is too big')), + }); +}; + +export const editPartnersValidationSchema = ( + t: TFunction<'translation', undefined>, +): Yup.ObjectSchema => { + return Yup.object().shape({ partnerAccess: Yup.string().required(), partners: Yup.array().of( Yup.object().shape({ diff --git a/src/frontend/src/components/programs/EditProgram/DetailsStep.tsx b/src/frontend/src/components/programs/EditProgram/DetailsStep.tsx new file mode 100644 index 0000000000..c1fa7b6981 --- /dev/null +++ b/src/frontend/src/components/programs/EditProgram/DetailsStep.tsx @@ -0,0 +1,62 @@ +import { Box, Button } from '@mui/material'; +import * as React from 'react'; +import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { ProgramForm } from '@containers/forms/ProgramForm'; +import { useBaseUrl } from '@hooks/useBaseUrl'; + +interface DetailsStepProps { + values; + handleNext?: () => Promise; + errors: any; + programId?: string; +} + +export const DetailsStep: React.FC = ({ + values, + handleNext, + errors, + programId: formProgramId, +}) => { + const { t } = useTranslation(); + const { businessArea, programId, baseUrl } = useBaseUrl(); + + const handleNextClick = async (): Promise => { + if (handleNext) { + await handleNext(); + } + }; + + return ( + <> + + + + + + + ); +}; diff --git a/src/frontend/src/components/programs/EditProgram/EditProgramMenu.tsx b/src/frontend/src/components/programs/EditProgram/EditProgramMenu.tsx new file mode 100644 index 0000000000..1e39fc2239 --- /dev/null +++ b/src/frontend/src/components/programs/EditProgram/EditProgramMenu.tsx @@ -0,0 +1,91 @@ +import { ProgramQuery } from '@generated/graphql'; +import { useBaseUrl } from '@hooks/useBaseUrl'; +import EditIcon from '@mui/icons-material/EditRounded'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; +import { Button, ListItemText, Menu, MenuItem } from '@mui/material'; +import { styled } from '@mui/system'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; + +const StyledMenu = styled(Menu)(() => ({ + '.MuiPaper-root': { + border: '1px solid #d3d4d5', + }, +})); + +const StyledMenuItem = styled(MenuItem)(({ theme }) => ({ + '&:focus': { + backgroundColor: theme.palette.primary.main, + '& .MuiListItemIcon-root, & .MuiListItemText-primary': { + color: theme.palette.common.white, + }, + }, +})); + +interface EditProgramMenuProps { + program: ProgramQuery['program']; +} + +export const EditProgramMenu = ({ + program, +}: EditProgramMenuProps): React.ReactElement => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const { baseUrl } = useBaseUrl(); + + const [anchorEl, setAnchorEl] = useState(null); + + const handleClick = (event: React.MouseEvent): void => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = (): void => { + setAnchorEl(null); + }; + + const handleMenuItemClick = (option: string): void => { + navigate(`/${baseUrl}/edit/${program.id}`, { state: { option } }); + }; + + return ( + <> + + + + + handleMenuItemClick('details')} + primary={t('Edit Programme Details')} + /> + + + handleMenuItemClick('partners')} + primary={t('Edit Programme Partners')} + /> + + + + ); +}; diff --git a/src/frontend/src/components/programs/EditProgram/PartnersStep.tsx b/src/frontend/src/components/programs/EditProgram/PartnersStep.tsx new file mode 100644 index 0000000000..a00960198b --- /dev/null +++ b/src/frontend/src/components/programs/EditProgram/PartnersStep.tsx @@ -0,0 +1,161 @@ +import { Box, Button, Grid } from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import { Field, FieldArray } from 'formik'; +import * as React from 'react'; +import { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; +import { AllAreasTreeQuery, ProgramPartnerAccess } from '@generated/graphql'; +import { useBaseUrl } from '@hooks/useBaseUrl'; +import { ButtonTooltip } from '@core/ButtonTooltip'; +import { FormikSelectField } from '@shared/Formik/FormikSelectField'; +import { DividerLine } from '@core/DividerLine'; +import { partnerAccessChoices } from '@components/programs/constants'; +import { ProgramPartnerCard } from '../CreateProgram/ProgramPartnerCard'; + +interface PartnersStepProps { + values; + allAreasTreeData: AllAreasTreeQuery['allAreasTree']; + partnerChoices; + submitForm: () => void; + setFieldValue; + programId?: string; +} + +export const PartnersStep: React.FC = ({ + values, + allAreasTreeData, + partnerChoices, + submitForm, + setFieldValue, + programId: formProgramId, +}) => { + const { t } = useTranslation(); + const { baseUrl, programId, businessArea } = useBaseUrl(); + + useEffect(() => { + if ( + values.partnerAccess === ProgramPartnerAccess.SelectedPartnersAccess && + values.partners.length === 0 + ) { + setFieldValue('partners', [ + { + id: '', + areaAccess: 'BUSINESS_AREA', + }, + ]); + } + + if ( + values.partnerAccess !== ProgramPartnerAccess.SelectedPartnersAccess && + values.partners.length > 0 + ) { + setFieldValue('partners', []); + } + }, [values, setFieldValue]); + + const addPartnerDisabled = + partnerChoices.every((choice) => choice.disabled) || + values.partners.some((partner) => !partner.id); + + let tooltipText = ''; + if (addPartnerDisabled) { + if (values.partners.some((partner) => !partner.id)) { + tooltipText = t('Select partner first'); + } else { + tooltipText = t('All partners have been added'); + } + } + + return ( + <> + + + + + + <> + + { + const { + form: { setFieldValue: setArrayFieldValue }, + } = arrayHelpers; + return ( + <> + {values.partners.map((partner, index) => ( + 1} + /> + ))} + + {values.partnerAccess === + ProgramPartnerAccess.SelectedPartnersAccess && ( + + arrayHelpers.push({ + id: '', + areaAccess: 'BUSINESS_AREA', + }) + } + variant="outlined" + color="primary" + endIcon={} + > + {t('Add Partner')} + + )} + + + ); + }} + /> + + + + + + + + + + + ); +}; diff --git a/src/frontend/src/components/programs/EditProgram/ProgramFieldSeriesStep.tsx b/src/frontend/src/components/programs/EditProgram/ProgramFieldSeriesStep.tsx new file mode 100644 index 0000000000..324c25cfbb --- /dev/null +++ b/src/frontend/src/components/programs/EditProgram/ProgramFieldSeriesStep.tsx @@ -0,0 +1,260 @@ +import { useConfirmation } from '@components/core/ConfirmationDialog'; +import { DividerLine } from '@components/core/DividerLine'; +import { PduSubtypeChoicesDataQuery } from '@generated/graphql'; +import { useBaseUrl } from '@hooks/useBaseUrl'; +import AddIcon from '@mui/icons-material/Add'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { Box, Button, FormControl, Grid, IconButton } from '@mui/material'; +import { FormikSelectField } from '@shared/Formik/FormikSelectField'; +import { FormikTextField } from '@shared/Formik/FormikTextField'; +import { Field, FieldArray } from 'formik'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; + +interface ProgramFieldSeriesStepProps { + values: { + pduFields: Array; + editMode: boolean; + }; + submitForm?: () => Promise; + programHasRdi?: boolean; + programHasTp?: boolean; + pdusubtypeChoicesData?: PduSubtypeChoicesDataQuery; + programId?: string; + setFieldValue; + program?; + setStep: (step: number) => void; + step: number; +} + +export const ProgramFieldSeriesStep = ({ + values, + submitForm, + programHasRdi, + programHasTp, + pdusubtypeChoicesData, + programId: formProgramId, + setFieldValue, + program, + setStep, + step, +}: ProgramFieldSeriesStepProps) => { + const { t } = useTranslation(); + const { businessArea, programId, baseUrl } = useBaseUrl(); + + const confirm = useConfirmation(); + + const mappedPduSubtypeChoices = pdusubtypeChoicesData?.pduSubtypeChoices.map( + (el) => ({ + value: el.value, + name: el.displayName, + }), + ); + + const confirmationModalTitle = t('Deleting Time Series Field'); + const confirmationText = t( + 'Are you sure you want to delete this field? This action cannot be reversed.', + ); + + const fieldDisabled = programHasRdi || programHasTp; + + return ( + <> + ( +
+ {values.pduFields && values.pduFields.length > 0 + ? values.pduFields.map((_field, index) => { + return ( + + + + + + + + + + { + const numberOfRounds = parseInt( + e.target.value, + 10, + ); + const updatedRoundsNames = [ + ...values.pduFields[index].pduData.roundsNames, + ]; + + if (updatedRoundsNames.length < numberOfRounds) { + for ( + let i = updatedRoundsNames.length; + i < numberOfRounds; + i++ + ) { + updatedRoundsNames.push(''); + } + } else if ( + updatedRoundsNames.length > numberOfRounds + ) { + updatedRoundsNames.length = numberOfRounds; + } + + setFieldValue( + `pduFields.${index}.pduData.numberOfRounds`, + numberOfRounds, + ); + setFieldValue( + `pduFields.${index}.pduData.roundsNames`, + updatedRoundsNames, + ); + }} + component={FormikSelectField} + choices={[...Array(20).keys()].map((n) => { + const isDisabled = + values.editMode && + fieldDisabled && + n + 2 <= + (program?.pduFields[index]?.pduData + ?.numberOfRounds || 0); + + return { + value: n + 1, + label: `${n + 1}`, + disabled: isDisabled, + }; + })} + /> + + + + confirm({ + title: confirmationModalTitle, + content: confirmationText, + type: 'error', + }).then(() => arrayHelpers.remove(index)) + } + disabled={fieldDisabled} + > + + + + {_field.pduData.numberOfRounds && + [ + ...Array( + Number(_field.pduData.numberOfRounds), + ).keys(), + ].map((round) => { + const selectedNumberOfRounds = + program?.pduFields?.[index]?.pduData + ?.numberOfRounds || 0; + const isDisabled = + fieldDisabled && + values.editMode && + round + 1 <= selectedNumberOfRounds; + return ( + + + + + + ); + })} + + {values.pduFields.length > 1 && + index < values.pduFields.length - 1 && } + + ); + }) + : null} + + + +
+ )} + /> + + + + + + + + + + + ); +}; diff --git a/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx b/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx index fb0be28564..257ffae326 100644 --- a/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx +++ b/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx @@ -86,9 +86,7 @@ export const ProgramDetails = ({ (partner) => partner.name !== 'UNICEF', ); - const showPartners = - program.partnerAccess === ProgramPartnerAccess.SelectedPartnersAccess && - partners.length > 0; + const showPartners = partners.length > 0; return ( diff --git a/src/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap b/src/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap index 0f21bdc6b9..ac82cf4a5d 100644 --- a/src/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap +++ b/src/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap @@ -264,7 +264,7 @@ exports[`components/ProgramDetails should render 1`] = ` class="sc-dmyCSP bGPBYf" color="textSecondary" > - Only selected partners within the business area + Only Selected Partners within the business area </span> </div> </div> diff --git a/src/frontend/src/components/programs/constants.tsx b/src/frontend/src/components/programs/constants.tsx index b5150bf819..e04aa34d30 100644 --- a/src/frontend/src/components/programs/constants.tsx +++ b/src/frontend/src/components/programs/constants.tsx @@ -2,11 +2,11 @@ import { ProgramPartnerAccess } from '@generated/graphql'; export const PartnerAccess = { [ProgramPartnerAccess.NonePartnersAccess]: - 'None of the partners should have access', + 'None of the Partners should have access', [ProgramPartnerAccess.SelectedPartnersAccess]: - 'Only selected partners within the business area', + 'Only Selected Partners within the business area', [ProgramPartnerAccess.AllPartnersAccess]: - 'All partners within the business area', + 'All Current Partners within the business area', }; export const partnerAccessChoices = Object.entries(PartnerAccess).map( diff --git a/src/frontend/src/containers/pages/headers/ActiveProgramDetailsPageHeaderButtons.tsx b/src/frontend/src/containers/pages/headers/ActiveProgramDetailsPageHeaderButtons.tsx index 5312d870e8..e93c279b73 100644 --- a/src/frontend/src/containers/pages/headers/ActiveProgramDetailsPageHeaderButtons.tsx +++ b/src/frontend/src/containers/pages/headers/ActiveProgramDetailsPageHeaderButtons.tsx @@ -1,10 +1,10 @@ -import { Box, Button } from '@mui/material'; +import { LoadingComponent } from '@components/core/LoadingComponent'; +import { EditProgramMenu } from '@components/programs/EditProgram/EditProgramMenu'; +import { ProgramQuery, useCashAssistUrlPrefixQuery } from '@generated/graphql'; import OpenInNewRoundedIcon from '@mui/icons-material/OpenInNewRounded'; +import { Box, Button } from '@mui/material'; import * as React from 'react'; -import { ProgramQuery, useCashAssistUrlPrefixQuery } from '@generated/graphql'; -import { LoadingComponent } from '@components/core/LoadingComponent'; import { DuplicateProgramButtonLink } from '../../dialogs/programs/DuplicateProgramButtonLink'; -import { EditProgramButtonLink } from '../../dialogs/programs/EditProgramButtonLink'; import { FinishProgram } from '../../dialogs/programs/FinishProgram'; export interface ActiveProgramDetailsPageHeaderPropTypes { @@ -35,7 +35,7 @@ export function ActiveProgramDetailsPageHeaderButtons({ )} {canEdit && ( <Box m={2}> - <EditProgramButtonLink program={program} /> + <EditProgramMenu program={program} /> </Box> )} {!isPaymentPlanApplicable && ( diff --git a/src/frontend/src/containers/pages/headers/DraftProgramDetailsPageHeaderButtons.tsx b/src/frontend/src/containers/pages/headers/DraftProgramDetailsPageHeaderButtons.tsx index 98d1e05afe..28dfe712cb 100644 --- a/src/frontend/src/containers/pages/headers/DraftProgramDetailsPageHeaderButtons.tsx +++ b/src/frontend/src/containers/pages/headers/DraftProgramDetailsPageHeaderButtons.tsx @@ -1,10 +1,10 @@ +import { EditProgramMenu } from '@components/programs/EditProgram/EditProgramMenu'; +import { ProgramQuery } from '@generated/graphql'; import { Box } from '@mui/material'; import * as React from 'react'; -import { ProgramQuery } from '@generated/graphql'; import { ActivateProgram } from '../../dialogs/programs/ActivateProgram'; import { DeleteProgram } from '../../dialogs/programs/DeleteProgram'; import { DuplicateProgramButtonLink } from '../../dialogs/programs/DuplicateProgramButtonLink'; -import { EditProgramButtonLink } from '../../dialogs/programs/EditProgramButtonLink'; export interface DraftProgramDetailsPageHeaderPropTypes { program: ProgramQuery['program']; @@ -29,7 +29,7 @@ export function DraftProgramDetailsPageHeaderButtons({ )} {canEdit && ( <Box m={2}> - <EditProgramButtonLink program={program} /> + <EditProgramMenu program={program} /> </Box> )} {canActivate && ( diff --git a/src/frontend/src/containers/pages/program/EditProgramPage.tsx b/src/frontend/src/containers/pages/program/EditProgramPage.tsx index ec05e9703b..786708a5b8 100644 --- a/src/frontend/src/containers/pages/program/EditProgramPage.tsx +++ b/src/frontend/src/containers/pages/program/EditProgramPage.tsx @@ -1,11 +1,10 @@ -// @ts-nocheck import { BaseSection } from '@components/core/BaseSection'; import { BreadCrumbsItem } from '@components/core/BreadCrumbs'; 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 { ProgramFieldSeriesStep } from '@components/programs/CreateProgram/ProgramFieldSeriesStep'; +import { DetailsStep } from '@components/programs/EditProgram/DetailsStep'; +import { PartnersStep } from '@components/programs/EditProgram/PartnersStep'; +import { ProgramFieldSeriesStep } from '@components/programs/EditProgram/ProgramFieldSeriesStep'; import { handleNext, ProgramStepper, @@ -16,6 +15,7 @@ import { usePduSubtypeChoicesDataQuery, useProgramQuery, useUpdateProgramMutation, + useUpdateProgramPartnersMutation, useUserPartnerChoicesQuery, } from '@generated/graphql'; import { useBaseUrl } from '@hooks/useBaseUrl'; @@ -26,16 +26,21 @@ import { decodeIdString } from '@utils/utils'; import { Formik } from 'formik'; import { ReactElement, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useNavigate, useLocation, useParams } from 'react-router-dom'; import { ALL_LOG_ENTRIES_QUERY } from '../../../apollo/queries/core/AllLogEntries'; import { hasPermissionInModule } from '../../../config/permissions'; -import { editProgramValidationSchema } from '@components/programs/CreateProgram/editProgramValidationSchema'; +import { + editPartnersValidationSchema, + editProgramDetailsValidationSchema, +} from '@components/programs/CreateProgram/editProgramValidationSchema'; export const EditProgramPage = (): ReactElement => { const navigate = useNavigate(); const { t } = useTranslation(); const { id } = useParams(); const permissions = usePermissions(); + const location = useLocation(); + const option = location.state?.option; const [step, setStep] = useState(0); const { showMessage } = useSnackbar(); @@ -53,7 +58,20 @@ export const EditProgramPage = (): ReactElement => { const { data: pdusubtypeChoicesData, loading: pdusubtypeChoicesLoading } = usePduSubtypeChoicesDataQuery(); - const [mutate] = useUpdateProgramMutation({ + const [updateProgramDetails] = useUpdateProgramMutation({ + refetchQueries: [ + { + query: ALL_LOG_ENTRIES_QUERY, + variables: { + objectId: decodeIdString(id), + count: 5, + businessArea, + }, + }, + ], + }); + + const [updateProgramPartners] = useUpdateProgramPartnersMutation({ refetchQueries: [ { query: ALL_LOG_ENTRIES_QUERY, @@ -101,7 +119,7 @@ export const EditProgramPage = (): ReactElement => { const programHasRdi = registrationImports?.totalCount > 0; const programHasTp = targetPopulationsCount > 0; - const handleSubmit = async (values): Promise<void> => { + const handleSubmitProgramDetails = async (values): Promise<void> => { const budgetValue = parseFloat(values.budget) ?? 0; const budgetToFixed = !Number.isNaN(budgetValue) ? budgetValue.toFixed(2) @@ -110,14 +128,6 @@ export const EditProgramPage = (): ReactElement => { const populationGoalParsed = !Number.isNaN(populationGoalValue) ? populationGoalValue : 0; - const partnersToSet = - values.partnerAccess === ProgramPartnerAccess.SelectedPartnersAccess - ? values.partners.map(({ id: partnerId, areas, areaAccess }) => ({ - partner: partnerId, - areas: areaAccess === 'ADMIN_AREA' ? areas : [], - areaAccess, - })) - : []; const pduFieldsToSend = values.pduFields .filter((item) => item.label !== '') @@ -151,18 +161,19 @@ export const EditProgramPage = (): ReactElement => { try { const { editMode, - pduFields: pduFieldsFromValues, - ...requestValuesWithoutPdu + partners: _partners, + partnerAccess: _partnerAccess, + pduFields: _pduFields, + ...requestValuesDetails } = values; - const response = await mutate({ + const response = await updateProgramDetails({ variables: { programData: { id, - ...requestValuesWithoutPdu, + ...requestValuesDetails, budget: budgetToFixed, populationGoal: populationGoalParsed, - partners: partnersToSet, pduFields: pduFieldsToSend, }, version, @@ -175,6 +186,36 @@ export const EditProgramPage = (): ReactElement => { } }; + const handleSubmitPartners = async (values): Promise<void> => { + const partnersToSet = + values.partnerAccess === ProgramPartnerAccess.SelectedPartnersAccess + ? values.partners.map(({ id: partnerId, areas, areaAccess }) => ({ + partner: partnerId, + areas: areaAccess === 'ADMIN_AREA' ? areas : [], + areaAccess, + })) + : []; + + try { + const response = await updateProgramPartners({ + variables: { + programData: { + id, + partners: partnersToSet, + partnerAccess: values.partnerAccess, + }, + version, + }, + }); + showMessage(t('Programme Partners updated.')); + navigate( + `/${baseUrl}/details/${response.data.updateProgramPartners.program.id}`, + ); + } catch (e) { + e.graphQLErrors.map((x) => showMessage(x.message)); + } + }; + const mappedPduFields = Object.entries(pduFields).map(([, field]) => { const { ...rest } = field; return { @@ -183,7 +224,7 @@ export const EditProgramPage = (): ReactElement => { }; }); - const initialValues = { + const initialValuesProgramDetails = { editMode: true, name, programmeCode, @@ -197,22 +238,28 @@ export const EditProgramPage = (): ReactElement => { populationGoal, cashPlus, frequencyOfPayments, - partners: partners - .filter((partner) => partner.name !== 'UNICEF') - .map((partner) => ({ - id: partner.id, - areas: partner.areas.map((area) => decodeIdString(area.id)), - areaAccess: partner.areaAccess, - })), - partnerAccess, pduFields: mappedPduFields, }; - initialValues.budget = + initialValuesProgramDetails.budget = data.program.budget === '0.00' ? '' : data.program.budget; - initialValues.populationGoal = + initialValuesProgramDetails.populationGoal = data.program.populationGoal === 0 ? '' : data.program.populationGoal; + const initialValuesPartners = { + partners: + partners.length > 0 + ? partners + .filter((partner) => partner.name !== 'UNICEF') + .map((partner) => ({ + id: partner.id, + areas: partner.areas.map((area) => decodeIdString(area.id)), + areaAccess: partner.areaAccess, + })) + : [], + partnerAccess, + }; + const stepFields = [ [ 'name', @@ -240,149 +287,151 @@ export const EditProgramPage = (): ReactElement => { to: `/${baseUrl}/details/${id}`, }, ]; + const stepsData = [ + { + title: t('Details'), + description: t( + 'To create a new Programme, please complete all required fields on the form below and save.', + ), + dataCy: 'step-button-details', + }, + { + title: t('Programme Time Series Fields'), + description: t( + 'The Time Series Fields feature allows serial updating of individual data through an XLSX file.', + ), + dataCy: 'step-button-time-series-fields', + }, + ]; return ( - <Formik - initialValues={initialValues} - onSubmit={(values) => { - handleSubmit(values); - }} - validationSchema={editProgramValidationSchema(t, initialValues)} - > - {({ - submitForm, - values, - validateForm, - setFieldTouched, - setFieldValue, - errors, - setErrors, - }) => { - const mappedPartnerChoices = userPartnerChoices - .filter((partner) => partner.name !== 'UNICEF') - .map((partner) => ({ - value: partner.value, - label: partner.name, - disabled: values.partners.some((p) => p.id === partner.value), - })); - - const handleNextStep = async () => { - await handleNext({ + <> + <PageHeader + title={`${t('Edit Programme')}: (${name})`} + breadCrumbs={ + hasPermissionInModule('PROGRAMME_VIEW_LIST_AND_DETAILS', permissions) + ? breadCrumbsItems + : null + } + /> + {option === 'details' && ( + <Formik + initialValues={initialValuesProgramDetails} + onSubmit={(values) => { + handleSubmitProgramDetails(values); + }} + validationSchema={editProgramDetailsValidationSchema( + t, + initialValuesProgramDetails, + )} + > + {({ + submitForm, + values, validateForm, - stepFields, - step, - setStep, setFieldTouched, - values, + setFieldValue, + errors, setErrors, - }); - }; - - const stepsData = [ - { - title: t('Details'), - description: t( - 'To create a new Programme, please complete all required fields on the form below and save.', - ), - dataCy: 'step-button-details', - }, - { - title: t('Programme Time Series Fields'), - description: t( - 'The Time Series Fields feature allows serial updating of individual data through an XLSX file.', - ), - dataCy: 'step-button-time-series-fields', - }, - { - title: t('Programme Partners'), - description: '', - dataCy: 'step-button-partners', - }, - ]; - - const stepTitle = stepsData[step].title; - const stepDescription = stepsData[step].description - ? stepsData[step].description - : undefined; + }) => { + const handleNextStep = async () => { + await handleNext({ + validateForm, + stepFields, + step, + setStep, + setFieldTouched, + values, + setErrors, + }); + }; + return ( + <BaseSection + title={stepsData[step].title} + description={stepsData[step].description} + stepper={ + <ProgramStepper + step={step} + setStep={setStep} + stepsData={stepsData} + /> + } + > + <Box p={3}> + <> + <Fade in={step === 0} timeout={600}> + <div> + {step === 0 && ( + <DetailsStep + values={values} + handleNext={handleNextStep} + programId={id} + errors={errors} + /> + )} + </div> + </Fade> + <Fade in={step === 1} timeout={600}> + <div> + {step === 1 && ( + <ProgramFieldSeriesStep + values={values} + step={step} + setStep={setStep} + pdusubtypeChoicesData={pdusubtypeChoicesData} + programHasRdi={programHasRdi} + programHasTp={programHasTp} + programId={id} + program={data.program} + setFieldValue={setFieldValue} + submitForm={submitForm} + /> + )} + </div> + </Fade> + </> + </Box> + </BaseSection> + ); + }} + </Formik> + )} + {option === 'partners' && ( + <Formik + initialValues={initialValuesPartners} + onSubmit={(values) => { + handleSubmitPartners(values); + }} + validationSchema={editPartnersValidationSchema(t)} + > + {({ submitForm, values, setFieldValue }) => { + const mappedPartnerChoices = userPartnerChoices + .filter((partner) => partner.name !== 'UNICEF') + .map((partner) => ({ + value: partner.value, + label: partner.name, + disabled: values.partners.some((p) => p.id === partner.value), + })); - return ( - <> - <PageHeader - title={`${t('Edit Programme')}: (${name})`} - breadCrumbs={ - hasPermissionInModule( - 'PROGRAMME_VIEW_LIST_AND_DETAILS', - permissions, - ) - ? breadCrumbsItems - : null - } - /> - <BaseSection - title={stepTitle} - description={stepDescription} - stepper={ - <ProgramStepper - step={step} - setStep={setStep} - stepsData={stepsData} - /> - } - > - <Box p={3}> - <Fade in={step === 0} timeout={600}> - <div> - {step === 0 && ( - <DetailsStep - values={values} - handleNext={handleNextStep} - programId={id} - /> - )} - </div> - </Fade> - <Fade in={step === 1} timeout={600}> - <div> - {step === 1 && ( - <ProgramFieldSeriesStep - values={values} - handleNext={handleNextStep} - step={step} - setStep={setStep} - pdusubtypeChoicesData={pdusubtypeChoicesData} - errors={errors} - setErrors={setErrors} - setFieldTouched={setFieldTouched} - programHasRdi={programHasRdi} - programHasTp={programHasTp} - programId={id} - program={data.program} - setFieldValue={setFieldValue} - /> - )} - </div> - </Fade> - <Fade in={step === 2} timeout={600}> + return ( + <BaseSection title={t('Programme Partners')}> + <Fade in={option === 'partners'} timeout={600}> <div> - {step === 2 && ( - <PartnersStep - values={values} - allAreasTreeData={allAreasTree} - partnerChoices={mappedPartnerChoices} - step={step} - setStep={setStep} - submitForm={submitForm} - setFieldValue={setFieldValue} - programId={id} - /> - )} + <PartnersStep + values={values} + allAreasTreeData={allAreasTree} + partnerChoices={mappedPartnerChoices} + submitForm={submitForm} + setFieldValue={setFieldValue} + programId={id} + /> </div> </Fade> - </Box> - </BaseSection> - </> - ); - }} - </Formik> + </BaseSection> + ); + }} + </Formik> + )} + </> ); }; diff --git a/src/frontend/src/utils/en.json b/src/frontend/src/utils/en.json index d93c736f5f..ee24c0aa49 100644 --- a/src/frontend/src/utils/en.json +++ b/src/frontend/src/utils/en.json @@ -899,5 +899,6 @@ "Only Empty Values": "Only Empty Values", "Open Biometrics Results": "Open Biometrics Results", "Algorithm similarity score:": "Algorithm similarity score:", - "Face images matching suggests:": "Face images matching suggests:" + "Face images matching suggests:": "Face images matching suggests:", + "Programme Partners updated.": "Programme Partners updated." } diff --git a/src/hct_mis_api/apps/account/admin/partner.py b/src/hct_mis_api/apps/account/admin/partner.py index 0c86b024fa..bd15f5c2d7 100644 --- a/src/hct_mis_api/apps/account/admin/partner.py +++ b/src/hct_mis_api/apps/account/admin/partner.py @@ -49,12 +49,13 @@ class ProgramAreaForm(forms.Form): class PartnerAdmin(HopeModelAdminMixin, admin.ModelAdmin): list_filter = ("is_un", "parent") search_fields = ("name",) - readonly_fields = ("permissions", "sub_partners") + readonly_fields = ("sub_partners",) list_display = ( "__str__", "sub_partners", "is_un", ) + filter_horizontal = ("allowed_business_areas",) def sub_partners(self, obj: Any) -> Optional[str]: return self.links_to_objects(obj.get_children()) if obj else None @@ -74,6 +75,8 @@ def get_readonly_fields(self, request: HttpRequest, obj: Optional[account_models additional_fields = [] if obj and obj.is_unicef: additional_fields.append("name") + if not request.user.has_perm("account.can_change_allowed_business_areas"): + additional_fields.append("allowed_business_areas") return list(super().get_readonly_fields(request, obj)) + additional_fields def get_form( diff --git a/src/hct_mis_api/apps/account/fixtures/data.json b/src/hct_mis_api/apps/account/fixtures/data.json index 4becfe0a3f..7dee5ae230 100644 --- a/src/hct_mis_api/apps/account/fixtures/data.json +++ b/src/hct_mis_api/apps/account/fixtures/data.json @@ -8,8 +8,7 @@ "lft": 1, "rght": 2, "tree_id": 1, - "is_un": true, - "permissions": {} + "is_un": true } }, { @@ -22,7 +21,6 @@ "rght": 2, "tree_id": 2, "is_un": true, - "permissions": {}, "allowed_business_areas": [ "c259b1a0-ae3a-494e-b343-f7c8eb060c68" ] @@ -38,15 +36,6 @@ "lft": 1, "rght": 2, "tree_id": 3, - "permissions": { - "c259b1a0-ae3a-494e-b343-f7c8eb060c68": { - "roles": ["e9e8c91a-c711-45b7-be8c-501c14d46330"], - "programs": { - "939ff91b-7f89-4e3c-9519-26ed62f51718": ["18ff6f29-cd4d-4e80-8e80-13494b32ee53"], - "00000000-0000-0000-0000-faceb00c0000": [] - } - } - }, "allowed_business_areas": [ "c259b1a0-ae3a-494e-b343-f7c8eb060c68" ] @@ -62,7 +51,6 @@ "rght": 2, "tree_id": 4, "is_un": false, - "permissions": {}, "allowed_business_areas": [ "c259b1a0-ae3a-494e-b343-f7c8eb060c68" ] diff --git a/src/hct_mis_api/apps/account/migrations/0077_migration.py b/src/hct_mis_api/apps/account/migrations/0077_migration.py new file mode 100644 index 0000000000..62ad3c9f86 --- /dev/null +++ b/src/hct_mis_api/apps/account/migrations/0077_migration.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.25 on 2024-09-19 23:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0076_migration'), + ] + + operations = [ + migrations.AlterModelOptions( + name='user', + options={'permissions': (('can_load_from_ad', 'Can load users from ActiveDirectory'), ('can_sync_with_ad', 'Can synchronise user with ActiveDirectory'), ('can_create_kobo_user', 'Can create users in Kobo'), ('can_import_from_kobo', 'Can import and sync users from Kobo'), ('can_upload_to_kobo', 'Can upload CSV file to Kobo'), ('can_debug', 'Can access debug informations'), ('can_inspect', 'Can inspect objects'), ('quick_links', 'Can see quick links in admin'), ('restrict_help_desk', 'Limit fields to be editable for help desk'), ('can_reindex_programs', 'Can reindex programs'), ('can_add_business_area_to_partner', 'Can add business area to partner'))}, + ), + migrations.RemoveField( + model_name='partner', + name='permissions', + ), + migrations.AlterField( + model_name='partner', + name='allowed_business_areas', + field=models.ManyToManyField(blank=True, to='core.BusinessArea'), + ), + ] diff --git a/src/hct_mis_api/apps/account/models.py b/src/hct_mis_api/apps/account/models.py index ff150f2a3a..c4282f6905 100644 --- a/src/hct_mis_api/apps/account/models.py +++ b/src/hct_mis_api/apps/account/models.py @@ -71,7 +71,6 @@ class Partner(LimitBusinessAreaModelMixin, MPTTModel): } } """ - permissions = JSONField(default=dict, blank=True) # TODO: remove after partner-permission-and-access migration def __str__(self) -> str: return f"{self.name} [Sub-Partner of {self.parent.name}]" if self.parent else self.name @@ -304,6 +303,7 @@ class Meta: ("quick_links", "Can see quick links in admin"), ("restrict_help_desk", "Limit fields to be editable for help desk"), ("can_reindex_programs", "Can reindex programs"), + ("can_add_business_area_to_partner", "Can add business area to partner"), ) diff --git a/src/hct_mis_api/apps/account/schema.py b/src/hct_mis_api/apps/account/schema.py index d44df0103e..bfb3368d2f 100644 --- a/src/hct_mis_api/apps/account/schema.py +++ b/src/hct_mis_api/apps/account/schema.py @@ -209,7 +209,7 @@ def resolve_user_partner_choices(self, info: Any) -> List[Dict[str, Any]]: return to_choice_object( list( Partner.objects.exclude(name=settings.DEFAULT_EMPTY_PARTNER) - .allowed_to(business_area_slug) + .filter(business_areas__slug=business_area_slug) .values_list("id", "name") ) + [(unicef.id, unicef.name)] # unicef partner is always available diff --git a/src/hct_mis_api/apps/account/signals.py b/src/hct_mis_api/apps/account/signals.py index c5ffe04c2e..b12a616702 100644 --- a/src/hct_mis_api/apps/account/signals.py +++ b/src/hct_mis_api/apps/account/signals.py @@ -6,8 +6,8 @@ from django.utils import timezone from hct_mis_api.apps.account.models import Partner, Role, User, UserRole -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.program.models import Program, ProgramPartnerThrough +from hct_mis_api.apps.core.models import BusinessArea, BusinessAreaPartnerThrough +from hct_mis_api.apps.program.models import Program @receiver(post_save, sender=UserRole) @@ -41,34 +41,15 @@ def post_save_user(sender: Any, instance: User, created: bool, *args: Any, **kwa @receiver(m2m_changed, sender=Partner.allowed_business_areas.through) def allowed_business_areas_changed(sender: Any, instance: Partner, action: str, pk_set: set, **kwargs: Any) -> None: - if action == "post_add": - added_business_areas_ids = pk_set - programs_to_add_access = Program.objects.filter( - business_area_id__in=added_business_areas_ids, - partner_access=Program.ALL_PARTNERS_ACCESS, - ) - for program in programs_to_add_access: - program_partner = ProgramPartnerThrough.objects.create(program=program, partner=instance) - program_partner.full_area_access = True - program_partner.save() - - elif action == "post_remove": + if action == "post_remove": removed_business_areas_ids = pk_set - programs_to_remove_access = Program.objects.filter( - business_area_id__in=removed_business_areas_ids, - partner_access=Program.ALL_PARTNERS_ACCESS, - ) - for program in programs_to_remove_access: - program.partners.remove(instance) + BusinessAreaPartnerThrough.objects.filter( + partner=instance, business_area_id__in=removed_business_areas_ids + ).delete() elif action == "pre_clear": instance._removed_business_areas = list(instance.allowed_business_areas.all()) elif action == "post_clear": removed_business_areas = getattr(instance, "_removed_business_areas", []) - programs_to_remove_access = Program.objects.filter( - business_area__in=removed_business_areas, - partner_access=Program.ALL_PARTNERS_ACCESS, - ) - for program in programs_to_remove_access: - program.partners.remove(instance) + BusinessAreaPartnerThrough.objects.filter(partner=instance, business_area__in=removed_business_areas).delete() diff --git a/src/hct_mis_api/apps/core/mixins.py b/src/hct_mis_api/apps/core/mixins.py index a5ea1215b3..0103466348 100644 --- a/src/hct_mis_api/apps/core/mixins.py +++ b/src/hct_mis_api/apps/core/mixins.py @@ -14,7 +14,7 @@ class LimitBusinessAreaModelManager(models.Manager): class LimitBusinessAreaModelMixin(models.Model): - allowed_business_areas = models.ManyToManyField(to=BusinessArea) + allowed_business_areas = models.ManyToManyField(to=BusinessArea, blank=True) objects = LimitBusinessAreaModelManager() diff --git a/src/hct_mis_api/apps/core/signals.py b/src/hct_mis_api/apps/core/signals.py index 7ef2b15d51..1951302ebc 100644 --- a/src/hct_mis_api/apps/core/signals.py +++ b/src/hct_mis_api/apps/core/signals.py @@ -1,10 +1,11 @@ from typing import Any from django.core.exceptions import ValidationError -from django.db.models.signals import m2m_changed +from django.db.models.signals import m2m_changed, post_delete from django.dispatch import receiver -from hct_mis_api.apps.core.models import DataCollectingType +from hct_mis_api.apps.core.models import BusinessAreaPartnerThrough, DataCollectingType +from hct_mis_api.apps.program.models import ProgramPartnerThrough @receiver(m2m_changed, sender=DataCollectingType.compatible_types.through) @@ -15,3 +16,14 @@ def validate_compatible_types( incompatible_dcts = DataCollectingType.objects.filter(pk__in=pk_set).exclude(type=instance.type) if incompatible_dcts.exists(): raise ValidationError("DCTs of different types cannot be compatible with each other.") + + +@receiver(post_delete, sender=BusinessAreaPartnerThrough) +def partner_role_removed(sender: Any, instance: BusinessAreaPartnerThrough, **kwargs: Any) -> None: + """ + If roles are revoked for a Partner from a whole Business Area, Partner looses access to all Programs in this Business Area + """ + partner = instance.partner + business_area = instance.business_area + programs_in_business_area = business_area.program_set.all() + ProgramPartnerThrough.objects.filter(partner=partner, program__in=programs_in_business_area).delete() \ No newline at end of file diff --git a/src/hct_mis_api/apps/payment/migrations/0144_migration.py b/src/hct_mis_api/apps/payment/migrations/0144_migration.py new file mode 100644 index 0000000000..2ee611dd82 --- /dev/null +++ b/src/hct_mis_api/apps/payment/migrations/0144_migration.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.25 on 2024-09-19 23:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0087_migration'), + ('payment', '0143_migration'), + ] + + operations = [ + migrations.AlterField( + model_name='financialserviceprovider', + name='allowed_business_areas', + field=models.ManyToManyField(blank=True, to='core.BusinessArea'), + ), + ] diff --git a/src/hct_mis_api/apps/program/inputs.py b/src/hct_mis_api/apps/program/inputs.py index 31c423bd03..f3be0aa2db 100644 --- a/src/hct_mis_api/apps/program/inputs.py +++ b/src/hct_mis_api/apps/program/inputs.py @@ -52,12 +52,16 @@ class UpdateProgramInput(graphene.InputObjectType): population_goal = graphene.Int() administrative_areas_of_implementation = graphene.String() data_collecting_type_code = graphene.String() - partners = graphene.List(ProgramPartnerThroughInput) - partner_access = graphene.String() programme_code = graphene.String() pdu_fields = graphene.List(PDUFieldInput) +class UpdateProgramPartnersInput(graphene.InputObjectType): + id = graphene.String(required=True) + partners = graphene.List(ProgramPartnerThroughInput) + partner_access = graphene.String() + + class CopyProgramInput(graphene.InputObjectType): id = graphene.String(required=True) name = graphene.String() diff --git a/src/hct_mis_api/apps/program/models.py b/src/hct_mis_api/apps/program/models.py index 58a80f429b..d0f29e502f 100644 --- a/src/hct_mis_api/apps/program/models.py +++ b/src/hct_mis_api/apps/program/models.py @@ -82,6 +82,7 @@ class Program(SoftDeletableModel, TimeStampedUUIDModel, AbstractSyncable, Concur "cash_plus", "population_goal", "administrative_areas_of_implementation", + "partner_access", ], {"admin_areas_log": "admin_areas"}, ) diff --git a/src/hct_mis_api/apps/program/mutations.py b/src/hct_mis_api/apps/program/mutations.py index 8230e77ebe..8e9c780494 100644 --- a/src/hct_mis_api/apps/program/mutations.py +++ b/src/hct_mis_api/apps/program/mutations.py @@ -32,6 +32,7 @@ CopyProgramInput, CreateProgramInput, UpdateProgramInput, + UpdateProgramPartnersInput, ) from hct_mis_api.apps.program.models import Program, ProgramCycle from hct_mis_api.apps.program.schema import ProgramNode @@ -145,9 +146,6 @@ def processed_mutate(cls, root: Any, info: Any, program_data: Dict, **kwargs: An check_concurrency_version_in_mutation(kwargs.get("version"), program) old_program = Program.objects.get(id=program_id) business_area = program.business_area - partners_data = program_data.pop("partners", []) - partner = info.context.user.partner - partner_access = program_data.get("partner_access", program.partner_access) pdu_fields = program_data.pop("pdu_fields", None) programme_code = program_data.get("programme_code", "") @@ -163,12 +161,6 @@ def processed_mutate(cls, root: Any, info: Any, program_data: Dict, **kwargs: An elif status_to_set == Program.FINISHED: cls.has_permission(info, Permissions.PROGRAMME_FINISH, business_area) - if status_to_set not in [Program.ACTIVE, Program.FINISHED]: - cls.validate_partners_data( - partners_data=partners_data, - partner_access=partner_access, - partner=partner, - ) data_collecting_type_code = program_data.pop("data_collecting_type_code", None) data_collecting_type = old_program.data_collecting_type if data_collecting_type_code and data_collecting_type_code != data_collecting_type.code: @@ -199,13 +191,6 @@ def processed_mutate(cls, root: Any, info: Any, program_data: Dict, **kwargs: An if hasattr(program, attrib): setattr(program, attrib, value) program.full_clean() - # update partner access only for SELECTED_PARTNERS_ACCESS type, since NONE and ALL are handled through signal - if ( - status_to_set not in [Program.ACTIVE, Program.FINISHED] - and partner_access == Program.SELECTED_PARTNERS_ACCESS - ): - partners_data = create_program_partner_access(partners_data, program, partner_access) - remove_program_partner_access(partners_data, program) program.save() if status_to_set == Program.FINISHED and program.biometric_deduplication_enabled: @@ -219,6 +204,54 @@ def processed_mutate(cls, root: Any, info: Any, program_data: Dict, **kwargs: An return UpdateProgram(program=program) +class UpdateProgramPartners( + PartnersDataValidator, + PermissionMutation, + ValidationErrorMutationMixin, +): + program = graphene.Field(ProgramNode) + + class Arguments: + program_data = UpdateProgramPartnersInput() + version = BigInt(required=False) + + @classmethod + @transaction.atomic + @is_authenticated + def processed_mutate(cls, root: Any, info: Any, program_data: Dict, **kwargs: Any) -> "UpdateProgram": + program_id = decode_id_string(program_data.pop("id", None)) + program = Program.objects.select_for_update().get(id=program_id) + check_concurrency_version_in_mutation(kwargs.get("version"), program) + old_program = Program.objects.get(id=program_id) + business_area = program.business_area + partners_data = program_data.pop("partners", []) + partner = info.context.user.partner + partner_access = program_data.get("partner_access", None) + old_partner_access = old_program.partner_access + + cls.has_permission(info, Permissions.PROGRAMME_UPDATE, business_area) + + cls.validate_partners_data( + partners_data=partners_data, + partner_access=partner_access, + partner=partner, + ) + + program.partner_access = partner_access + + # update partner access for ALL_PARTNERS_ACCESS type if it was not changed but the partners need to be refetched + if partner_access == old_partner_access and partner_access == Program.ALL_PARTNERS_ACCESS: + create_program_partner_access([], program, partner_access) + # update partner access only for SELECTED_PARTNERS_ACCESS type, since update to NONE and ALL are handled through signal + if partner_access == Program.SELECTED_PARTNERS_ACCESS: + partners_data = create_program_partner_access(partners_data, program, partner_access) + remove_program_partner_access(partners_data, program) + program.save() + + log_create(Program.ACTIVITY_LOG_MAPPING, "business_area", info.context.user, program.pk, old_program, program) + return UpdateProgram(program=program) + + class DeleteProgram(ProgramDeletionValidator, PermissionMutation): ok = graphene.Boolean() @@ -297,5 +330,6 @@ def processed_mutate(cls, root: Any, info: Any, program_data: Dict) -> "CopyProg class Mutations(graphene.ObjectType): create_program = CreateProgram.Field() update_program = UpdateProgram.Field() + update_program_partners = UpdateProgramPartners.Field() delete_program = DeleteProgram.Field() copy_program = CopyProgram.Field() diff --git a/src/hct_mis_api/apps/program/utils.py b/src/hct_mis_api/apps/program/utils.py index fbdd5115b5..3db760a013 100644 --- a/src/hct_mis_api/apps/program/utils.py +++ b/src/hct_mis_api/apps/program/utils.py @@ -518,7 +518,7 @@ def create_program_partner_access( partners_data: List, program: Program, partner_access: Optional[str] = None ) -> List[Dict]: if partner_access == Program.ALL_PARTNERS_ACCESS: - partners = Partner.objects.filter(allowed_business_areas=program.business_area).exclude( + partners = Partner.objects.filter(business_areas=program.business_area).exclude( name=settings.DEFAULT_EMPTY_PARTNER ) partners_data = [{"partner": partner.id, "areas": []} for partner in partners] diff --git a/src/hct_mis_api/apps/steficon/migrations/0020_migration.py b/src/hct_mis_api/apps/steficon/migrations/0020_migration.py new file mode 100644 index 0000000000..b7d989c2fb --- /dev/null +++ b/src/hct_mis_api/apps/steficon/migrations/0020_migration.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.25 on 2024-09-19 23:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0087_migration'), + ('steficon', '0019_migration'), + ] + + operations = [ + migrations.AlterField( + model_name='rule', + name='allowed_business_areas', + field=models.ManyToManyField(blank=True, to='core.BusinessArea'), + ), + ] diff --git a/src/hct_mis_api/one_time_scripts/migrate_partner_permissions_and_access.py b/src/hct_mis_api/one_time_scripts/migrate_partner_permissions_and_access.py deleted file mode 100644 index 9c8c3edd94..0000000000 --- a/src/hct_mis_api/one_time_scripts/migrate_partner_permissions_and_access.py +++ /dev/null @@ -1,78 +0,0 @@ -from typing import Dict - -from django.conf import settings - -from hct_mis_api.apps.account.models import Partner, Role -from hct_mis_api.apps.core.models import BusinessArea, BusinessAreaPartnerThrough -from hct_mis_api.apps.geo.models import Area -from hct_mis_api.apps.program.models import Program, ProgramPartnerThrough - -""" -permissions structure -{ - "business_area_id": { - "roles": ["role_id_1", "role_id_2"], - "programs": {"program_id": ["admin_id"]} - } -} -""" - - -def migrate_partner_permissions_and_access() -> None: - for partner in Partner.objects.exclude(name=settings.DEFAULT_EMPTY_PARTNER): - if partner.is_unicef: - migrate_unicef_access(partner) - else: - roles_dict, access_dict = get_partner_data(partner.permissions) - migrate_partner_permissions(partner, roles_dict) - migrate_partner_access(partner, access_dict) - - -def get_partner_data(permissions_dict: Dict) -> tuple[Dict, Dict]: - roles_dict = {} - access_dict = {} - for business_area_id, perm_in_business_area in permissions_dict.items(): - roles_dict[business_area_id] = perm_in_business_area.get("roles") - if perm_in_business_area.get("programs"): - for program_id, admin_ids in perm_in_business_area.get("programs", {}).items(): - access_dict[program_id] = admin_ids - return roles_dict, access_dict - - -def migrate_partner_permissions(partner: Partner, roles_dict: Dict) -> None: - for business_area_id, roles in roles_dict.items(): - roles = Role.objects.filter(id__in=roles) - if roles: - ba_partner_through, _ = BusinessAreaPartnerThrough.objects.get_or_create( - partner=partner, business_area_id=business_area_id - ) - ba_partner_through.roles.set(roles) - - -def migrate_partner_access(partner: Partner, access_dict: Dict) -> None: - for program_id, admin_ids in access_dict.items(): - full_area_access = False - program = Program.objects.filter(id=program_id).first() - if admin_ids: - areas = Area.objects.filter(id__in=admin_ids) - else: - areas = Area.objects.filter(area_type__country__business_areas=program.business_area) - full_area_access = True - if program and areas: - program_partner_through, _ = ProgramPartnerThrough.objects.get_or_create( - partner=partner, program_id=program_id - ) - if full_area_access: - program_partner_through.full_area_access = full_area_access - program_partner_through.save(update_fields=["full_area_access"]) - program_partner_through.areas.set(areas) - - -def migrate_unicef_access(partner: Partner) -> None: - for ba in BusinessArea.objects.all(): - areas = Area.objects.filter(area_type__country__business_areas=ba) - for program in Program.objects.filter(business_area=ba): - program_partner_through, _ = ProgramPartnerThrough.objects.get_or_create(partner=partner, program=program) - program_partner_through.full_area_access = True - program_partner_through.save(update_fields=["full_area_access"]) - program_partner_through.areas.set(areas) diff --git a/tests/selenium/page_object/programme_details/programme_details.py b/tests/selenium/page_object/programme_details/programme_details.py index 2d12ee8e54..c9ebd352d5 100644 --- a/tests/selenium/page_object/programme_details/programme_details.py +++ b/tests/selenium/page_object/programme_details/programme_details.py @@ -21,7 +21,9 @@ class ProgrammeDetails(BaseComponents): labelPartnerName = 'h6[data-cy="label-partner-name"]' labelPartnerAccess = 'div[data-cy="label-Partner Access"]' buttonRemoveProgram = 'button[data-cy="button-remove-program"]' - buttonEditProgram = 'a[data-cy="button-edit-program"]' + buttonEditProgram = 'button[data-cy="button-edit-program"]' + selectEditProgramDetails = 'li[data-cy="menu-item-edit-details"]' + selectEditProgramPartners = 'li[data-cy="menu-item-edit-partners"]' buttonActivateProgram = 'button[data-cy="button-activate-program"]' buttonActivateProgramModal = 'button[data-cy="button-activate-program-modal"]' labelProgrammeCode = 'div[data-cy="label-Programme Code"]' @@ -189,6 +191,12 @@ def getButtonRemoveProgram(self) -> WebElement: def getButtonEditProgram(self) -> WebElement: return self.wait_for(self.buttonEditProgram) + def getSelectEditProgramDetails(self) -> WebElement: + return self.wait_for(self.selectEditProgramDetails) + + def getSselectEditProgramPartners(self) -> WebElement: + return self.wait_for(self.selectEditProgramPartners) + def getButtonActivateProgram(self) -> WebElement: return self.wait_for(self.buttonActivateProgram) diff --git a/tests/selenium/page_object/programme_management/programme_management.py b/tests/selenium/page_object/programme_management/programme_management.py index 35a964b01d..bc79c4c85b 100644 --- a/tests/selenium/page_object/programme_management/programme_management.py +++ b/tests/selenium/page_object/programme_management/programme_management.py @@ -42,7 +42,10 @@ class ProgrammeManagement(BaseComponents): calendarDays = "//*[@data-timestamp]" filtersSearch = '//*[@data-cy="filters-search"]/div/input' buttonApply = 'button[data-cy="button-filters-clear"]' - buttonEditProgram = 'a[data-cy="button-edit-program"]' + buttonEditProgram = 'button[data-cy="button-edit-program"]' + selectEditProgramDetails = 'li[data-cy="menu-item-edit-details"]' + selectEditProgramPartners = 'li[data-cy="menu-item-edit-partners"]' + selectOptionsContainer = 'ul[data-cy="select-options-container"]' inputProgrammeCode = 'input[data-cy="input-programmeCode"]' tableRow = 'tr[data-cy="table-row-{}"]' stepButtonDetails = 'button[data-cy="step-button-details"]' @@ -113,6 +116,9 @@ def getAccessToProgram(self) -> WebElement: def selectWhoAccessToProgram(self, name: str) -> None: self.select_option_by_name(name) + def getSelectOptionsContainer(self) -> WebElement: + return self.wait_for(self.selectOptionsContainer) + def getButtonAddPartner(self) -> WebElement: return self.wait_for(self.buttonAddPartner) @@ -122,9 +128,12 @@ def getButtonDelete(self) -> WebElement: def getButtonDeletePopup(self) -> WebElement: return self.wait_for("/html/body/div[2]/div[3]/div/div[3]/div/button[2]", By.XPATH) + def getInputPartner(self) -> WebElement: + return self.wait_for(self.inputPartner) + def choosePartnerOption(self, optionName: str) -> None: # Todo: Change undefined to name of Partner - self.wait_for(self.inputPartner).click() + self.getInputPartner().click() self.select_option_by_name(optionName) def getInputProgrammeName(self) -> WebElement: @@ -227,6 +236,12 @@ def getButtonApply(self) -> WebElement: def getButtonEditProgram(self) -> WebElement: return self.wait_for(self.buttonEditProgram) + def getSelectEditProgramDetails(self) -> WebElement: + return self.wait_for(self.selectEditProgramDetails) + + def getSelectEditProgramPartners(self) -> WebElement: + return self.wait_for(self.selectEditProgramPartners) + def getInputProgrammeCode(self) -> WebElement: return self.wait_for(self.inputProgrammeCode) diff --git a/tests/selenium/program_details/test_program_details.py b/tests/selenium/program_details/test_program_details.py index 453186ac8f..84ea63842f 100644 --- a/tests/selenium/program_details/test_program_details.py +++ b/tests/selenium/program_details/test_program_details.py @@ -277,6 +277,7 @@ def test_edit_programme_from_details( ) -> None: pageProgrammeDetails.selectGlobalProgramFilter("Test Programm") pageProgrammeDetails.getButtonEditProgram().click() + pageProgrammeDetails.getSelectEditProgramDetails().click() pageProgrammeManagement.getInputProgrammeName().send_keys(Keys.CONTROL + "a") pageProgrammeManagement.getInputProgrammeName().send_keys("New name after Edit") pageProgrammeManagement.getInputProgrammeCode().send_keys(Keys.CONTROL + "a") @@ -289,10 +290,7 @@ def test_edit_programme_from_details( pageProgrammeManagement.getInputEndDate().send_keys(FormatTime(1, 10, 2099).numerically_formatted_date) pageProgrammeManagement.getButtonNext().click() pageProgrammeManagement.getButtonAddTimeSeriesField() - pageProgrammeManagement.getButtonNext().click() programme_creation_url = pageProgrammeDetails.driver.current_url - pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("None of the partners should have access") pageProgrammeManagement.getButtonSave().click() # Check Details page assert "details" in pageProgrammeDetails.wait_for_new_url(programme_creation_url).split("/") @@ -753,6 +751,7 @@ def test_edit_program_details_with_wrong_date( pageProgrammeDetails.selectGlobalProgramFilter("ThreeCyclesProgramme") assert "ACTIVE" in pageProgrammeDetails.getProgramStatus().text pageProgrammeDetails.getButtonEditProgram().click() + pageProgrammeDetails.getSelectEditProgramDetails().click() pageProgrammeManagement.getInputProgrammeName() pageProgrammeManagement.getInputStartDate().click() pageProgrammeManagement.getInputStartDate().send_keys(Keys.CONTROL + "a") @@ -762,10 +761,7 @@ def test_edit_program_details_with_wrong_date( pageProgrammeManagement.getInputEndDate().send_keys(FormatTime(1, 10, 2022).numerically_formatted_date) pageProgrammeManagement.getButtonNext().click() pageProgrammeManagement.getButtonAddTimeSeriesField() - pageProgrammeManagement.getButtonNext().click() programme_creation_url = pageProgrammeDetails.driver.current_url - pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("None of the partners should have access") pageProgrammeManagement.getButtonSave().click() # Check Details page with pytest.raises(Exception): diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index 7b251a291d..97f9de1206 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -12,7 +12,11 @@ from tests.selenium.page_object.programme_management.programme_management import ProgrammeManagement from selenium import webdriver from selenium.webdriver import Keys +from selenium.webdriver.common.by import By +from hct_mis_api.apps.account.fixtures import RoleFactory +from hct_mis_api.apps.account.models import Partner +from hct_mis_api.apps.core.models import BusinessArea, BusinessAreaPartnerThrough from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory @@ -656,7 +660,6 @@ def test_create_programme_back_scenarios( assert "UNHCR" in pageProgrammeDetails.getLabelPartnerName().text -@pytest.mark.night @pytest.mark.usefixtures("login") class TestManualCalendar: @freeze_time("2024-09-09") @@ -762,6 +765,8 @@ def test_edit_programme( pageProgrammeManagement.getTableRowByProgramName("Test Programm").click() pageProgrammeManagement.getButtonEditProgram().click() + pageProgrammeManagement.getSelectEditProgramDetails().click() + # 1st step (Details) pageProgrammeManagement.getInputProgrammeName().send_keys(Keys.CONTROL + "a") pageProgrammeManagement.getInputProgrammeName().send_keys("New name after Edit") @@ -776,10 +781,6 @@ def test_edit_programme( pageProgrammeManagement.getButtonNext().click() # 2nd step (Time Series Fields) pageProgrammeManagement.getButtonAddTimeSeriesField() - pageProgrammeManagement.getButtonNext().click() - # 3rd step (Partners) - pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("None of the partners should have access") pageProgrammeManagement.getButtonSave().click() programme_creation_url = pageProgrammeManagement.driver.current_url # Check Details page @@ -788,7 +789,89 @@ def test_edit_programme( assert FormatTime(1, 1, 2022).date_in_text_format in pageProgrammeDetails.getLabelStartDate().text assert FormatTime(1, 10, 2099).date_in_text_format in pageProgrammeDetails.getLabelEndDate().text - @pytest.mark.skip(reason="Unskip after fix bug: 214927") + def test_programme_partners( + self, + create_programs: None, + pageProgrammeManagement: ProgrammeManagement, + pageProgrammeDetails: ProgrammeDetails, + ) -> None: + partner1 = Partner.objects.create(name="Test Partner 1") + partner2 = Partner.objects.create(name="Test Partner 2") + role = RoleFactory(name="Role in BA") + ba_partner_through = BusinessAreaPartnerThrough.objects.create( + business_area=BusinessArea.objects.get(slug="afghanistan"), + partner=partner1, + ) + ba_partner_through.roles.set([role]) + # Go to Programme Management + pageProgrammeManagement.getNavProgrammeManagement().click() + # Create Programme + pageProgrammeManagement.getButtonNewProgram().click() + # 1st step (Details) + pageProgrammeManagement.getInputProgrammeName().send_keys("Test Program Partners") + pageProgrammeManagement.getInputStartDate().click() + pageProgrammeManagement.getInputStartDate().send_keys(str(FormatTime(1, 1, 2022).numerically_formatted_date)) + pageProgrammeManagement.getInputEndDate().click() + pageProgrammeManagement.getInputEndDate().send_keys(FormatTime(1, 10, 2099).numerically_formatted_date) + pageProgrammeManagement.chooseOptionSelector("Health") + pageProgrammeManagement.chooseOptionDataCollectingType("Partial") + pageProgrammeManagement.getInputCashPlus().click() + pageProgrammeManagement.getButtonNext().click() + # 2nd step (Time Series Fields) + pageProgrammeManagement.getButtonAddTimeSeriesField() + pageProgrammeManagement.getButtonNext().click() + # 3rd step (Partners) + # only partners with role in business area can be selected + + assert partner1 in Partner.objects.filter(business_areas__slug="afghanistan").all() + assert partner2 not in Partner.objects.filter(business_areas__slug="afghanistan").all() + assert Partner.objects.get(name="UNHCR") in Partner.objects.filter(business_areas__slug="afghanistan").all() + + partner_access_selected = "Only Selected Partners within the business area" + pageProgrammeManagement.getAccessToProgram().click() + pageProgrammeManagement.selectWhoAccessToProgram(partner_access_selected) + + pageProgrammeManagement.wait_for(pageProgrammeManagement.inputPartner).click() + select_options_container = pageProgrammeManagement.getSelectOptionsContainer() + options = select_options_container.find_elements(By.TAG_NAME, "li") + assert any("Test Partner 1" == li.text for li in options) is True + assert any("Test Partner 2" == li.text for li in options) is False + + pageProgrammeManagement.driver.find_element(By.CSS_SELECTOR, "body").click() + + pageProgrammeManagement.choosePartnerOption("UNHCR") + pageProgrammeManagement.getButtonSave().click() + + programme_creation_url = pageProgrammeManagement.driver.current_url + # Check Details page + assert "details" in pageProgrammeDetails.wait_for_new_url(programme_creation_url).split("/") + assert "Test Program Partners" in pageProgrammeDetails.getHeaderTitle().text + assert partner_access_selected in pageProgrammeDetails.getLabelPartnerAccess().text + + partner_name_elements = pageProgrammeManagement.driver.find_elements( + By.CSS_SELECTOR, "[data-cy='label-partner-name']" + ) + assert len(partner_name_elements) == 1 + assert any("UNHCR" in partner.text.strip() for partner in partner_name_elements) + + # edit program + pageProgrammeManagement.getButtonEditProgram().click() + pageProgrammeManagement.getSelectEditProgramPartners().click() + pageProgrammeManagement.getAccessToProgram().click() + pageProgrammeManagement.selectWhoAccessToProgram("All Current Partners within the business area") + pageProgrammeManagement.getButtonSave().click() + + programme_details_url = pageProgrammeManagement.driver.current_url + # Check Details page + assert "details" in pageProgrammeDetails.wait_for_new_url(programme_details_url).split("/") + + partner_name_elements_new = pageProgrammeManagement.driver.find_elements( + By.CSS_SELECTOR, "[data-cy='label-partner-name']" + ) + assert len(partner_name_elements_new) == 2 + assert any("UNHCR" in partner.text.strip() for partner in partner_name_elements_new) + assert any("Test Partner 1" in partner.text.strip() for partner in partner_name_elements_new) + @pytest.mark.parametrize( "test_data", [ @@ -846,6 +929,8 @@ def test_edit_programme_with_rdi( ) # Edit Programme pageProgrammeManagement.getButtonEditProgram().click() + pageProgrammeManagement.getSelectEditProgramDetails().click() + # 1st step (Details) pageProgrammeManagement.getInputProgrammeName().send_keys(Keys.CONTROL + "a") pageProgrammeManagement.getInputProgrammeName().send_keys("New name after Edit") @@ -893,8 +978,5 @@ def test_edit_programme_with_rdi( pageProgrammeManagement.getInputPduFieldsRoundsNames(0, 2).send_keys("Round 3") - pageProgrammeManagement.getButtonNext().click() - # 3rd step (Partners) - pageProgrammeManagement.getAccessToProgram() pageProgrammeManagement.getButtonSave().click() assert program_name in pageProgrammeDetails.getHeaderTitle().text diff --git a/tests/unit/apps/account/test_signal_change_allowed_ba.py b/tests/unit/apps/account/test_signal_change_allowed_ba.py index 3014d02a55..7a04476c4b 100644 --- a/tests/unit/apps/account/test_signal_change_allowed_ba.py +++ b/tests/unit/apps/account/test_signal_change_allowed_ba.py @@ -1,9 +1,10 @@ from django.test import TestCase -from hct_mis_api.apps.account.fixtures import PartnerFactory +from hct_mis_api.apps.account.fixtures import PartnerFactory, RoleFactory from hct_mis_api.apps.core.fixtures import create_afghanistan, create_ukraine +from hct_mis_api.apps.core.models import BusinessAreaPartnerThrough from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.apps.program.models import Program +from hct_mis_api.apps.program.models import Program, ProgramPartnerThrough class TestSignalChangeAllowedBusinessAreas(TestCase): @@ -12,40 +13,69 @@ def setUpTestData(cls) -> None: super().setUpTestData() cls.business_area_afg = create_afghanistan() cls.business_area_ukr = create_ukraine() + + cls.partner = PartnerFactory(name="Partner") + cls.partner_unicef = PartnerFactory(name="UNICEF") # UNICEF partner has access to all programs + + cls.partner.allowed_business_areas.add(cls.business_area_afg) + role = RoleFactory(name="Role for Partner") + afg_partner_through = BusinessAreaPartnerThrough.objects.create( + business_area=cls.business_area_afg, + partner=cls.partner, + ) + afg_partner_through.roles.set([role]) + cls.partner.allowed_business_areas.add(cls.business_area_ukr) + ukr_partner_through = BusinessAreaPartnerThrough.objects.create( + business_area=cls.business_area_ukr, + partner=cls.partner, + ) + ukr_partner_through.roles.set([role]) + cls.program_afg = ProgramFactory.create( status=Program.DRAFT, business_area=cls.business_area_afg, partner_access=Program.ALL_PARTNERS_ACCESS ) cls.program_ukr = ProgramFactory.create( - status=Program.DRAFT, business_area=cls.business_area_ukr, partner_access=Program.ALL_PARTNERS_ACCESS + status=Program.DRAFT, business_area=cls.business_area_ukr, partner_access=Program.SELECTED_PARTNERS_ACCESS ) - cls.partner = PartnerFactory(name="Partner") - cls.partner_unicef = PartnerFactory(name="UNICEF") # UNICEF partner has access to all programs def test_signal_change_allowed_business_areas(self) -> None: - self.partner.allowed_business_areas.add(self.business_area_afg) - - self.assertEqual(self.program_afg.partners.count(), 2) - self.assertEqual(self.program_ukr.partners.count(), 1) + self.assertEqual( + self.program_afg.partners.count(), 2 + ) # ALL_PARTNERS_ACCESS - UNICEF and Partner that has access to AFG (signal on program) + self.assertEqual(self.program_ukr.partners.count(), 1) # SELECTED_PARTNERS_ACCESS - only UNICEF self.assertEqual(self.partner.programs.count(), 1) + self.assertEqual(self.partner.program_partner_through.first().full_area_access, True) - self.partner.allowed_business_areas.add(self.business_area_ukr) + # grant access to program in ukr + ProgramPartnerThrough.objects.create( + program=self.program_ukr, + partner=self.partner, + ) - self.assertEqual(self.program_afg.partners.count(), 2) self.assertEqual(self.program_ukr.partners.count(), 2) self.assertEqual(self.partner.programs.count(), 2) - self.assertEqual(self.partner.program_partner_through.first().full_area_access, True) - self.assertEqual(self.partner.program_partner_through.last().full_area_access, True) - self.partner.allowed_business_areas.remove(self.business_area_afg) + self.assertEqual(self.partner.program_partner_through.get(program=self.program_ukr).full_area_access, False) - self.assertEqual(self.program_afg.partners.count(), 1) - self.assertEqual(self.program_ukr.partners.count(), 2) + self.partner.allowed_business_areas.remove(self.business_area_afg) + # removing from allowed BA -> removing roles in this BA + self.assertIsNone( + self.partner.business_area_partner_through.filter(business_area=self.business_area_afg).first() + ) + self.assertIsNotNone( + self.partner.business_area_partner_through.filter(business_area=self.business_area_ukr).first() + ) + # removing the role -> removing access to the program + self.assertEqual(self.program_afg.partners.count(), 1) # only UNICEF left self.assertEqual(self.partner.programs.count(), 1) - self.assertEqual(self.partner.program_partner_through.first().full_area_access, True) - self.partner.allowed_business_areas.clear() + self.partner.allowed_business_areas.remove(self.business_area_ukr) + # removing from allowed BA -> removing roles in this BA + self.assertIsNone( + self.partner.business_area_partner_through.filter(business_area=self.business_area_ukr).first() + ) + # removing the role -> removing access to the program + self.assertEqual(self.program_ukr.partners.count(), 1) # only UNICEF left - self.assertEqual(self.program_afg.partners.count(), 1) - self.assertEqual(self.program_ukr.partners.count(), 1) self.assertEqual(self.partner.programs.count(), 0) diff --git a/tests/unit/apps/account/test_user_choice_data.py b/tests/unit/apps/account/test_user_choice_data.py index 908c28bbce..c4f4f2a478 100644 --- a/tests/unit/apps/account/test_user_choice_data.py +++ b/tests/unit/apps/account/test_user_choice_data.py @@ -1,4 +1,4 @@ -from hct_mis_api.apps.account.fixtures import PartnerFactory, UserFactory +from hct_mis_api.apps.account.fixtures import PartnerFactory, RoleFactory, UserFactory from hct_mis_api.apps.account.models import Partner from hct_mis_api.apps.core.base_test_case import APITestCase from hct_mis_api.apps.core.fixtures import create_afghanistan @@ -26,14 +26,20 @@ def setUpTestData(cls) -> None: partner_unicef, _ = Partner.objects.get_or_create(name="UNICEF") cls.user = UserFactory(partner=partner_unicef, username="unicef_user") - # partner allowed in BA + # partner with role in BA PartnerFactory(name="Partner with BA access") for partner in Partner.objects.exclude(name="UNICEF"): # unicef partner should be available everywhere partner.allowed_business_areas.add(cls.business_area) + role = RoleFactory(name=f"Role for {partner.name}") + cls.add_partner_role_in_business_area(partner, cls.business_area, [role]) + + # partner allowed in BA but without role -> is not listed + partner_without_role = PartnerFactory(name="Partner Without Role") + partner_without_role.allowed_business_areas.add(cls.business_area) # partner not allowed in BA - PartnerFactory(name="Partner Without Access") + PartnerFactory(name="Partner Not Allowed in BA") def test_user_choice_data(self) -> None: self.snapshot_graphql_request( diff --git a/tests/unit/apps/core/test_signal_remove_partner_role.py b/tests/unit/apps/core/test_signal_remove_partner_role.py new file mode 100644 index 0000000000..cf703635f2 --- /dev/null +++ b/tests/unit/apps/core/test_signal_remove_partner_role.py @@ -0,0 +1,69 @@ +from django.test import TestCase + +from hct_mis_api.apps.account.fixtures import PartnerFactory, RoleFactory +from hct_mis_api.apps.core.fixtures import create_afghanistan, create_ukraine +from hct_mis_api.apps.core.models import BusinessAreaPartnerThrough +from hct_mis_api.apps.program.fixtures import ProgramFactory +from hct_mis_api.apps.program.models import Program, ProgramPartnerThrough + + +class TestSignalRemovePartnerRole(TestCase): + @classmethod + def setUpTestData(cls) -> None: + super().setUpTestData() + cls.business_area_afg = create_afghanistan() + cls.business_area_ukr = create_ukraine() + + cls.partner = PartnerFactory(name="Partner") + cls.partner_unicef = PartnerFactory(name="UNICEF") # UNICEF partner has access to all programs + + cls.partner.allowed_business_areas.add(cls.business_area_afg) + role = RoleFactory(name="Role for Partner") + afg_partner_through = BusinessAreaPartnerThrough.objects.create( + business_area=cls.business_area_afg, + partner=cls.partner, + ) + afg_partner_through.roles.set([role]) + cls.partner.allowed_business_areas.add(cls.business_area_ukr) + ukr_partner_through = BusinessAreaPartnerThrough.objects.create( + business_area=cls.business_area_ukr, + partner=cls.partner, + ) + ukr_partner_through.roles.set([role]) + + cls.program_afg = ProgramFactory.create( + status=Program.DRAFT, business_area=cls.business_area_afg, partner_access=Program.ALL_PARTNERS_ACCESS + ) + cls.program_ukr = ProgramFactory.create( + status=Program.DRAFT, business_area=cls.business_area_ukr, partner_access=Program.SELECTED_PARTNERS_ACCESS + ) + + def test_signal_remove_partner_role(self) -> None: + self.assertEqual( + self.program_afg.partners.count(), 2 + ) # ALL_PARTNERS_ACCESS - UNICEF and Partner that has access to AFG (signal on program) + self.assertEqual(self.program_ukr.partners.count(), 1) # SELECTED_PARTNERS_ACCESS - only UNICEF + self.assertEqual(self.partner.programs.count(), 1) + + # grant access to program in ukr + ProgramPartnerThrough.objects.create( + program=self.program_ukr, + partner=self.partner, + ) + self.assertEqual(self.program_ukr.partners.count(), 2) + self.assertEqual(self.partner.programs.count(), 2) + + # remove role in afg + BusinessAreaPartnerThrough.objects.filter(business_area=self.business_area_afg, partner=self.partner).delete() + # removing the role -> removing access to the program + self.assertIsNone(self.partner.program_partner_through.filter(program=self.program_afg).first()) + self.assertEqual(self.program_afg.partners.count(), 1) # only UNICEF left + self.assertEqual(self.partner.programs.count(), 1) + + # remove role in ukr + BusinessAreaPartnerThrough.objects.filter(business_area=self.business_area_ukr, partner=self.partner).delete() + # removing the role -> removing access to the program + self.assertIsNone(self.partner.program_partner_through.filter(program=self.program_ukr).first()) + self.assertEqual(self.program_ukr.partners.count(), 1) # only UNICEF left + + self.assertEqual(self.partner.programs.count(), 0) diff --git a/tests/unit/apps/program/snapshots/snap_test_update_program.py b/tests/unit/apps/program/snapshots/snap_test_update_program.py index 101df873b9..2c481f16f9 100644 --- a/tests/unit/apps/program/snapshots/snap_test_update_program.py +++ b/tests/unit/apps/program/snapshots/snap_test_update_program.py @@ -56,21 +56,6 @@ 'label': 'Full' }, 'name': 'initial name', - 'partnerAccess': 'NONE_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], 'pduFields': [ { 'label': '{"English(EN)": "PDU Field To Be Preserved"}', @@ -155,175 +140,6 @@ ] } -snapshots['TestUpdateProgram::test_update_full_area_access_flag 1'] = { - 'data': { - 'updateProgram': { - 'program': { - 'dataCollectingType': { - 'code': 'partial_individuals', - 'label': 'Partial' - }, - 'name': 'updated name', - 'partnerAccess': 'ALL_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'Other Partner' - }, - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], - 'pduFields': [ - { - 'label': '{"English(EN)": "PDU Field To Be Preserved"}', - 'name': 'pdu_field_to_be_preserved', - 'pduData': { - 'numberOfRounds': 1, - 'roundsNames': [ - 'Round To Be Preserved' - ], - 'subtype': 'DATE' - } - }, - { - 'label': '{"English(EN)": "PDU Field To Be Removed"}', - 'name': 'pdu_field_to_be_removed', - 'pduData': { - 'numberOfRounds': 3, - 'roundsNames': [ - 'Round 1 To Be Removed', - 'Round 2 To Be Removed', - 'Round 3 To Be Removed' - ], - 'subtype': 'DECIMAL' - } - }, - { - 'label': '{"English(EN)": "PDU Field To Be Updated"}', - 'name': 'pdu_field_to_be_updated', - 'pduData': { - 'numberOfRounds': 2, - 'roundsNames': [ - 'Round 1 To Be Updated', - 'Round 2 To Be Updated' - ], - 'subtype': 'STRING' - } - } - ], - 'status': 'DRAFT' - } - } - } -} - -snapshots['TestUpdateProgram::test_update_full_area_access_flag 2'] = { - 'data': { - 'updateProgram': { - 'program': { - 'dataCollectingType': { - 'code': 'partial_individuals', - 'label': 'Partial' - }, - 'name': 'updated name', - 'partnerAccess': 'SELECTED_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'ADMIN_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - } - ], - 'name': 'Other Partner' - }, - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - }, - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'WFP' - } - ], - 'pduFields': [ - { - 'label': '{"English(EN)": "PDU Field To Be Preserved"}', - 'name': 'pdu_field_to_be_preserved', - 'pduData': { - 'numberOfRounds': 1, - 'roundsNames': [ - 'Round To Be Preserved' - ], - 'subtype': 'DATE' - } - }, - { - 'label': '{"English(EN)": "PDU Field To Be Removed"}', - 'name': 'pdu_field_to_be_removed', - 'pduData': { - 'numberOfRounds': 3, - 'roundsNames': [ - 'Round 1 To Be Removed', - 'Round 2 To Be Removed', - 'Round 3 To Be Removed' - ], - 'subtype': 'DECIMAL' - } - }, - { - 'label': '{"English(EN)": "PDU Field To Be Updated"}', - 'name': 'pdu_field_to_be_updated', - 'pduData': { - 'numberOfRounds': 2, - 'roundsNames': [ - 'Round 1 To Be Updated', - 'Round 2 To Be Updated' - ], - 'subtype': 'STRING' - } - } - ], - 'status': 'DRAFT' - } - } - } -} - snapshots['TestUpdateProgram::test_update_program_authenticated_0_with_permissions 1'] = { 'data': { 'updateProgram': { @@ -333,21 +149,6 @@ 'label': 'Partial' }, 'name': 'updated name', - 'partnerAccess': 'NONE_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], 'pduFields': [ { 'label': '{"English(EN)": "PDU Field To Be Preserved"}', @@ -512,258 +313,6 @@ ] } -snapshots['TestUpdateProgram::test_update_program_of_other_partner_raise_error 1'] = { - 'data': { - 'updateProgram': None - }, - 'errors': [ - { - 'locations': [ - { - 'column': 7, - 'line': 3 - } - ], - 'message': "['Please assign access to your partner before saving the programme.']", - 'path': [ - 'updateProgram' - ] - } - ] -} - -snapshots['TestUpdateProgram::test_update_program_partners_0_valid 1'] = { - 'data': { - 'updateProgram': { - 'program': { - 'dataCollectingType': { - 'code': 'partial_individuals', - 'label': 'Partial' - }, - 'name': 'updated name', - 'partnerAccess': 'SELECTED_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'ADMIN_AREA', - 'areas': [ - { - 'name': 'Area1' - }, - { - 'name': 'Area2' - } - ], - 'name': 'Partner to be added' - }, - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - }, - { - 'areaAccess': 'ADMIN_AREA', - 'areas': [ - { - 'name': 'Area1' - }, - { - 'name': 'Area2' - } - ], - 'name': 'WFP' - } - ], - 'pduFields': [ - { - 'label': '{"English(EN)": "PDU Field To Be Preserved"}', - 'name': 'pdu_field_to_be_preserved', - 'pduData': { - 'numberOfRounds': 1, - 'roundsNames': [ - 'Round To Be Preserved' - ], - 'subtype': 'DATE' - } - }, - { - 'label': '{"English(EN)": "PDU Field To Be Removed"}', - 'name': 'pdu_field_to_be_removed', - 'pduData': { - 'numberOfRounds': 3, - 'roundsNames': [ - 'Round 1 To Be Removed', - 'Round 2 To Be Removed', - 'Round 3 To Be Removed' - ], - 'subtype': 'DECIMAL' - } - }, - { - 'label': '{"English(EN)": "PDU Field To Be Updated"}', - 'name': 'pdu_field_to_be_updated', - 'pduData': { - 'numberOfRounds': 2, - 'roundsNames': [ - 'Round 1 To Be Updated', - 'Round 2 To Be Updated' - ], - 'subtype': 'STRING' - } - } - ], - 'status': 'DRAFT' - } - } - } -} - -snapshots['TestUpdateProgram::test_update_program_partners_1_invalid_all_partner_access 1'] = { - 'data': { - 'updateProgram': None - }, - 'errors': [ - { - 'locations': [ - { - 'column': 7, - 'line': 3 - } - ], - 'message': "['You cannot specify partners for the chosen access type']", - 'path': [ - 'updateProgram' - ] - } - ] -} - -snapshots['TestUpdateProgram::test_update_program_partners_2_invalid_none_partner_access 1'] = { - 'data': { - 'updateProgram': None - }, - 'errors': [ - { - 'locations': [ - { - 'column': 7, - 'line': 3 - } - ], - 'message': "['You cannot specify partners for the chosen access type']", - 'path': [ - 'updateProgram' - ] - } - ] -} - -snapshots['TestUpdateProgram::test_update_program_partners_all_partners_access 1'] = { - 'data': { - 'updateProgram': { - 'program': { - 'dataCollectingType': { - 'code': 'partial_individuals', - 'label': 'Partial' - }, - 'name': 'updated name', - 'partnerAccess': 'ALL_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'Other Partner' - }, - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], - 'pduFields': [ - { - 'label': '{"English(EN)": "PDU Field To Be Preserved"}', - 'name': 'pdu_field_to_be_preserved', - 'pduData': { - 'numberOfRounds': 1, - 'roundsNames': [ - 'Round To Be Preserved' - ], - 'subtype': 'DATE' - } - }, - { - 'label': '{"English(EN)": "PDU Field To Be Removed"}', - 'name': 'pdu_field_to_be_removed', - 'pduData': { - 'numberOfRounds': 3, - 'roundsNames': [ - 'Round 1 To Be Removed', - 'Round 2 To Be Removed', - 'Round 3 To Be Removed' - ], - 'subtype': 'DECIMAL' - } - }, - { - 'label': '{"English(EN)": "PDU Field To Be Updated"}', - 'name': 'pdu_field_to_be_updated', - 'pduData': { - 'numberOfRounds': 2, - 'roundsNames': [ - 'Round 1 To Be Updated', - 'Round 2 To Be Updated' - ], - 'subtype': 'STRING' - } - } - ], - 'status': 'DRAFT' - } - } - } -} - -snapshots['TestUpdateProgram::test_update_program_partners_invalid_access_type_from_object 1'] = { - 'data': { - 'updateProgram': None - }, - 'errors': [ - { - 'locations': [ - { - 'column': 7, - 'line': 3 - } - ], - 'message': "['You cannot specify partners for the chosen access type']", - 'path': [ - 'updateProgram' - ] - } - ] -} - snapshots['TestUpdateProgram::test_update_program_when_finished 1'] = { 'data': { 'updateProgram': None @@ -919,21 +468,6 @@ 'label': 'Full' }, 'name': 'Program with Updated PDU Fields', - 'partnerAccess': 'NONE_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], 'pduFields': [ { 'label': '{"English(EN)": "PDU Field - New"}', @@ -1057,21 +591,6 @@ 'label': 'Full' }, 'name': 'Program with Updated PDU Fields', - 'partnerAccess': 'NONE_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], 'pduFields': [ { 'label': '{"English(EN)": "PDU Field 1"}', @@ -1127,21 +646,6 @@ 'label': 'Full' }, 'name': 'Program with Updated PDU Fields', - 'partnerAccess': 'NONE_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], 'pduFields': [ { 'label': '{"English(EN)": "PDU Field 1"}', @@ -1217,21 +721,6 @@ 'label': 'Full' }, 'name': 'Program with Updated PDU Fields', - 'partnerAccess': 'NONE_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], 'pduFields': [ { 'label': '{"English(EN)": "PDU Field To Be Preserved"}', @@ -1327,21 +816,6 @@ 'label': 'Full' }, 'name': 'Program with Updated PDU Fields', - 'partnerAccess': 'NONE_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], 'pduFields': [ { 'label': '{"English(EN)": "PDU Field To Be Preserved"}', @@ -1395,21 +869,6 @@ 'label': 'Full' }, 'name': 'Program with Updated PDU Fields', - 'partnerAccess': 'NONE_PARTNERS_ACCESS', - 'partners': [ - { - 'areaAccess': 'BUSINESS_AREA', - 'areas': [ - { - 'name': 'Area in AFG 1' - }, - { - 'name': 'Area in AFG 2' - } - ], - 'name': 'UNICEF' - } - ], 'pduFields': [ { 'label': '{"English(EN)": "PDU Field To Be Preserved"}', diff --git a/tests/unit/apps/program/snapshots/snap_test_update_program_partners.py b/tests/unit/apps/program/snapshots/snap_test_update_program_partners.py new file mode 100644 index 0000000000..ab63f96512 --- /dev/null +++ b/tests/unit/apps/program/snapshots/snap_test_update_program_partners.py @@ -0,0 +1,401 @@ +# -*- coding: utf-8 -*- +# snapshottest: v1 - https://goo.gl/zC4yUc +from __future__ import unicode_literals + +from snapshottest import Snapshot + + +snapshots = Snapshot() + +snapshots['TestUpdateProgramPartners::test_update_full_area_access_flag 1'] = { + 'data': { + 'updateProgramPartners': { + 'program': { + 'partnerAccess': 'ALL_PARTNERS_ACCESS', + 'partners': [ + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'Other Partner' + }, + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'UNICEF' + } + ] + } + } + } +} + +snapshots['TestUpdateProgramPartners::test_update_full_area_access_flag 2'] = { + 'data': { + 'updateProgramPartners': { + 'program': { + 'partnerAccess': 'SELECTED_PARTNERS_ACCESS', + 'partners': [ + { + 'areaAccess': 'ADMIN_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + } + ], + 'name': 'Other Partner' + }, + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'UNICEF' + }, + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'WFP' + } + ] + } + } + } +} + +snapshots['TestUpdateProgramPartners::test_update_program_of_other_partner_raise_error 1'] = { + 'data': { + 'updateProgramPartners': None + }, + 'errors': [ + { + 'locations': [ + { + 'column': 7, + 'line': 3 + } + ], + 'message': "['Please assign access to your partner before saving the programme.']", + 'path': [ + 'updateProgramPartners' + ] + } + ] +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_0_valid 1'] = { + 'data': { + 'updateProgramPartners': { + 'program': { + 'partnerAccess': 'SELECTED_PARTNERS_ACCESS', + 'partners': [ + { + 'areaAccess': 'ADMIN_AREA', + 'areas': [ + { + 'name': 'Area1' + }, + { + 'name': 'Area2' + } + ], + 'name': 'Partner to be added' + }, + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'UNICEF' + }, + { + 'areaAccess': 'ADMIN_AREA', + 'areas': [ + { + 'name': 'Area1' + }, + { + 'name': 'Area2' + } + ], + 'name': 'WFP' + } + ] + } + } + } +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_1_invalid_all_partner_access 1'] = { + 'data': { + 'updateProgramPartners': None + }, + 'errors': [ + { + 'locations': [ + { + 'column': 7, + 'line': 3 + } + ], + 'message': "['You cannot specify partners for the chosen access type']", + 'path': [ + 'updateProgramPartners' + ] + } + ] +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_2_invalid_none_partner_access 1'] = { + 'data': { + 'updateProgramPartners': None + }, + 'errors': [ + { + 'locations': [ + { + 'column': 7, + 'line': 3 + } + ], + 'message': "['You cannot specify partners for the chosen access type']", + 'path': [ + 'updateProgramPartners' + ] + } + ] +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_all_partners_access 1'] = { + 'data': { + 'updateProgramPartners': { + 'program': { + 'partnerAccess': 'ALL_PARTNERS_ACCESS', + 'partners': [ + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'Other Partner' + }, + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'UNICEF' + } + ] + } + } + } +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_all_partners_access_refresh_partners_after_update 1'] = { + 'data': { + 'updateProgramPartners': { + 'program': { + 'partnerAccess': 'ALL_PARTNERS_ACCESS', + 'partners': [ + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'Other Partner' + }, + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'UNICEF' + } + ] + } + } + } +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_all_partners_access_refresh_partners_after_update 2'] = { + 'data': { + 'updateProgramPartners': { + 'program': { + 'partnerAccess': 'ALL_PARTNERS_ACCESS', + 'partners': [ + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'Other Partner' + }, + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'Partner without role in in BA' + }, + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'UNICEF' + } + ] + } + } + } +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_authenticated_0_with_permissions 1'] = { + 'data': { + 'updateProgramPartners': { + 'program': { + 'partnerAccess': 'NONE_PARTNERS_ACCESS', + 'partners': [ + { + 'areaAccess': 'BUSINESS_AREA', + 'areas': [ + { + 'name': 'Area in AFG 1' + }, + { + 'name': 'Area in AFG 2' + } + ], + 'name': 'UNICEF' + } + ] + } + } + } +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_authenticated_1_without_permissions 1'] = { + 'data': { + 'updateProgramPartners': None + }, + 'errors': [ + { + 'locations': [ + { + 'column': 7, + 'line': 3 + } + ], + 'message': 'Permission Denied: User does not have correct permission.', + 'path': [ + 'updateProgramPartners' + ] + } + ] +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_invalid_access_type_from_object 1'] = { + 'data': { + 'updateProgramPartners': None + }, + 'errors': [ + { + 'locations': [ + { + 'column': 7, + 'line': 3 + } + ], + 'message': "['You cannot specify partners for the chosen access type']", + 'path': [ + 'updateProgramPartners' + ] + } + ] +} + +snapshots['TestUpdateProgramPartners::test_update_program_partners_not_authenticated 1'] = { + 'data': { + 'updateProgramPartners': None + }, + 'errors': [ + { + 'locations': [ + { + 'column': 7, + 'line': 3 + } + ], + 'message': 'Permission Denied: User is not authenticated.', + 'path': [ + 'updateProgramPartners' + ] + } + ] +} diff --git a/tests/unit/apps/program/test_all_programs_query.py b/tests/unit/apps/program/test_all_programs_query.py index d5a780729d..6ac98fe5b9 100644 --- a/tests/unit/apps/program/test_all_programs_query.py +++ b/tests/unit/apps/program/test_all_programs_query.py @@ -5,7 +5,7 @@ from parameterized import parameterized -from hct_mis_api.apps.account.fixtures import PartnerFactory, UserFactory +from hct_mis_api.apps.account.fixtures import PartnerFactory, RoleFactory, UserFactory from hct_mis_api.apps.account.permissions import Permissions from hct_mis_api.apps.core.base_test_case import APITestCase from hct_mis_api.apps.core.fixtures import ( @@ -72,6 +72,9 @@ def setUpTestData(cls) -> None: cls.partner = PartnerFactory(name="WFP") cls.partner.allowed_business_areas.add(cls.business_area) + role = RoleFactory(name="Role for WFP") + cls.add_partner_role_in_business_area(cls.partner, cls.business_area, [role]) + cls.user = UserFactory.create(partner=cls.partner) cls.unicef_partner = PartnerFactory(name="UNICEF") diff --git a/tests/unit/apps/program/test_change_program_status.py b/tests/unit/apps/program/test_change_program_status.py index 86fd103a04..b7e05ee0f6 100644 --- a/tests/unit/apps/program/test_change_program_status.py +++ b/tests/unit/apps/program/test_change_program_status.py @@ -97,7 +97,6 @@ def test_status_change( "programData": { "id": self.id_to_base64(program.id, "ProgramNode"), "status": target_status, - "partners": [{"partner": str(self.user.partner.id), "areaAccess": "BUSINESS_AREA"}], } }, ) diff --git a/tests/unit/apps/program/test_copy_program.py b/tests/unit/apps/program/test_copy_program.py index ff7eb0782f..1055b9e3ed 100644 --- a/tests/unit/apps/program/test_copy_program.py +++ b/tests/unit/apps/program/test_copy_program.py @@ -3,7 +3,7 @@ from flaky import flaky from parameterized import parameterized -from hct_mis_api.apps.account.fixtures import PartnerFactory, UserFactory +from hct_mis_api.apps.account.fixtures import PartnerFactory, RoleFactory, UserFactory from hct_mis_api.apps.account.permissions import Permissions from hct_mis_api.apps.core.base_test_case import APITestCase from hct_mis_api.apps.core.fixtures import ( @@ -193,9 +193,11 @@ def setUpTestData(cls) -> None: # create UNICEF partner - it will always be granted access while creating program PartnerFactory(name="UNICEF") - # partner allowed within BA - will be granted access for ALL_PARTNERS_ACCESS type + # partner with role in BA - will be granted access for ALL_PARTNERS_ACCESS type partner_allowed_in_BA = PartnerFactory(name="Other Partner") partner_allowed_in_BA.allowed_business_areas.set([cls.business_area]) + role = RoleFactory(name="Role for WFP") + cls.add_partner_role_in_business_area(partner_allowed_in_BA, cls.business_area, [role]) PartnerFactory(name="Partner not allowed in BA") diff --git a/tests/unit/apps/program/test_create_program.py b/tests/unit/apps/program/test_create_program.py index e739ddea83..f4fe4879e0 100644 --- a/tests/unit/apps/program/test_create_program.py +++ b/tests/unit/apps/program/test_create_program.py @@ -6,6 +6,7 @@ from hct_mis_api.apps.account.fixtures import ( BusinessAreaFactory, PartnerFactory, + RoleFactory, UserFactory, ) from hct_mis_api.apps.account.permissions import Permissions @@ -17,6 +18,7 @@ ) from hct_mis_api.apps.core.models import ( BusinessArea, + BusinessAreaPartnerThrough, DataCollectingType, PeriodicFieldData, ) @@ -109,11 +111,18 @@ def setUpTestData(cls) -> None: # create UNICEF partner - it will always be granted access while creating program PartnerFactory(name="UNICEF") - # partner allowed within BA - will be granted access for ALL_PARTNERS_ACCESS type - partner_allowed_in_BA = PartnerFactory(name="Other Partner") - partner_allowed_in_BA.allowed_business_areas.set([cls.business_area]) + # partner with role in BA - will be granted access for ALL_PARTNERS_ACCESS type + partner_with_role_in_BA = PartnerFactory(name="Other Partner") + partner_with_role_in_BA.allowed_business_areas.set([cls.business_area]) + role = RoleFactory(name="Role in BA") + ba_partner_through = BusinessAreaPartnerThrough.objects.create( + business_area=cls.business_area, + partner=partner_with_role_in_BA, + ) + ba_partner_through.roles.set([role]) - PartnerFactory(name="Partner not allowed in BA") + partner_without_role_in_BA = PartnerFactory(name="Partner without role in BA") + partner_without_role_in_BA.allowed_business_areas.set([cls.business_area]) country_afg = CountryFactory(name="Afghanistan") country_afg.business_areas.set([cls.business_area]) diff --git a/tests/unit/apps/program/test_signal_partner_access_change.py b/tests/unit/apps/program/test_signal_partner_access_change.py index 030d077f2b..b1a71d78b6 100644 --- a/tests/unit/apps/program/test_signal_partner_access_change.py +++ b/tests/unit/apps/program/test_signal_partner_access_change.py @@ -1,7 +1,8 @@ from django.test import TestCase -from hct_mis_api.apps.account.fixtures import PartnerFactory +from hct_mis_api.apps.account.fixtures import PartnerFactory, RoleFactory from hct_mis_api.apps.core.fixtures import create_afghanistan +from hct_mis_api.apps.core.models import BusinessAreaPartnerThrough from hct_mis_api.apps.geo.fixtures import AreaFactory, AreaTypeFactory, CountryFactory from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program, ProgramPartnerThrough @@ -15,13 +16,24 @@ def setUpTestData(cls) -> None: cls.unicef_partner = PartnerFactory(name="UNICEF") - cls.partner_allowed_in_afg_1 = PartnerFactory(name="Partner Allowed in Afg 1") - cls.partner_allowed_in_afg_1.allowed_business_areas.set([cls.business_area]) + role = RoleFactory(name="Role for Partner") + cls.partner_with_role_in_afg_1 = PartnerFactory(name="Partner with role in Afg 1") + cls.partner_with_role_in_afg_1.allowed_business_areas.set([cls.business_area]) + afg_partner_through_1 = BusinessAreaPartnerThrough.objects.create( + business_area=cls.business_area, + partner=cls.partner_with_role_in_afg_1, + ) + afg_partner_through_1.roles.set([role]) - cls.partner_allowed_in_afg_2 = PartnerFactory(name="Partner Allowed in Afg 2") - cls.partner_allowed_in_afg_2.allowed_business_areas.set([cls.business_area]) + cls.partner_with_role_in_afg_2 = PartnerFactory(name="Partner with role in Afg 2") + cls.partner_with_role_in_afg_2.allowed_business_areas.set([cls.business_area]) + afg_partner_through_2 = BusinessAreaPartnerThrough.objects.create( + business_area=cls.business_area, + partner=cls.partner_with_role_in_afg_2, + ) + afg_partner_through_2.roles.set([role]) - cls.partner_not_allowed_in_BA = PartnerFactory(name="Partner not allowed in Afg") + cls.partner_not_allowed_in_BA = PartnerFactory(name="Partner without role in Afg") country_afg = CountryFactory(name="Afghanistan") country_afg.business_areas.set([cls.business_area]) @@ -73,7 +85,7 @@ def test_all_partners_access(self) -> None: self.assertEqual(self.program.partners.count(), 3) self.assertSetEqual( set(self.program.partners.all()), - {self.unicef_partner, self.partner_allowed_in_afg_1, self.partner_allowed_in_afg_2}, + {self.unicef_partner, self.partner_with_role_in_afg_1, self.partner_with_role_in_afg_2}, ) for program_partner_through in self.program.program_partner_through.all(): self.assertEqual(program_partner_through.areas.count(), 2) @@ -89,7 +101,7 @@ def test_selected_into_all_and_none_partners_access(self) -> None: self.assertEqual(self.program.partners.count(), 1) program_partner_through = ProgramPartnerThrough.objects.create( - program=self.program, partner=self.partner_allowed_in_afg_1 + program=self.program, partner=self.partner_with_role_in_afg_1 ) program_partner_through.areas.set([self.area_in_afg_1]) @@ -101,7 +113,7 @@ def test_selected_into_all_and_none_partners_access(self) -> None: self.assertEqual(self.program.partners.count(), 3) self.assertSetEqual( set(self.program.partners.all()), - {self.unicef_partner, self.partner_allowed_in_afg_1, self.partner_allowed_in_afg_2}, + {self.unicef_partner, self.partner_with_role_in_afg_1, self.partner_with_role_in_afg_2}, ) for program_partner_through in self.program.program_partner_through.all(): self.assertEqual(program_partner_through.areas.count(), 2) diff --git a/tests/unit/apps/program/test_update_program.py b/tests/unit/apps/program/test_update_program.py index beffcc1f1a..5df8a13abd 100644 --- a/tests/unit/apps/program/test_update_program.py +++ b/tests/unit/apps/program/test_update_program.py @@ -45,14 +45,6 @@ class TestUpdateProgram(APITestCase): label code } - partners { - name - areas { - name - } - areaAccess - } - partnerAccess pduFields { name label @@ -93,7 +85,6 @@ def setUpTestData(cls) -> None: cls.business_area = BusinessArea.objects.get(slug="afghanistan") cls.business_area.data_collecting_types.set(DataCollectingType.objects.all().values_list("id", flat=True)) - cls.unicef_partner = PartnerFactory(name="UNICEF") cls.program = ProgramFactory.create( name="initial name", @@ -104,10 +95,6 @@ def setUpTestData(cls) -> None: version=123, biometric_deduplication_enabled=True, ) - unicef_program, _ = ProgramPartnerThrough.objects.get_or_create( - program=cls.program, - partner=cls.unicef_partner, - ) cls.program_finished = ProgramFactory.create( status=Program.FINISHED, business_area=cls.business_area, @@ -117,29 +104,18 @@ def setUpTestData(cls) -> None: cls.partner = PartnerFactory(name="WFP") cls.user = UserFactory.create(partner=cls.partner) - # partner allowed within BA - will be granted access for ALL_PARTNERS_ACCESS type - cls.other_partner = PartnerFactory(name="Other Partner") - cls.other_partner.allowed_business_areas.set([cls.business_area]) - - cls.partner_not_allowed_in_BA = PartnerFactory(name="Partner not allowed in BA") + cls.unicef_partner = PartnerFactory(name="UNICEF") + unicef_program, _ = ProgramPartnerThrough.objects.get_or_create( + program=cls.program, + partner=cls.unicef_partner, + ) country_afg = CountryFactory(name="Afghanistan") country_afg.business_areas.set([cls.business_area]) area_type_afg = AreaTypeFactory(name="Area Type in Afg", country=country_afg) - country_other = CountryFactory( - name="Other Country", - short_name="Oth", - iso_code2="O", - iso_code3="OTH", - iso_num="111", - ) - cls.area_type_other = AreaTypeFactory(name="Area Type Other", country=country_other) cls.area_in_afg_1 = AreaFactory(name="Area in AFG 1", area_type=area_type_afg, p_code="AREA-IN-AFG1") cls.area_in_afg_2 = AreaFactory(name="Area in AFG 2", area_type=area_type_afg, p_code="AREA-IN-AFG2") - cls.area_not_in_afg = AreaFactory( - name="Area not in AFG", area_type=cls.area_type_other, p_code="AREA-NOT-IN-AFG2" - ) unicef_program.areas.set([cls.area_in_afg_1, cls.area_in_afg_2]) @@ -183,7 +159,6 @@ def test_update_program_not_authenticated(self) -> None: "id": self.id_to_base64(self.program.id, "ProgramNode"), "name": "updated name", "status": Program.ACTIVE, - "partnerAccess": Program.NONE_PARTNERS_ACCESS, }, "version": self.program.version, }, @@ -228,195 +203,6 @@ def test_update_program_authenticated( assert updated_program.status == Program.DRAFT assert updated_program.name == "initial name" - @parameterized.expand( - [ - ("valid", Program.SELECTED_PARTNERS_ACCESS), - ("invalid_all_partner_access", Program.ALL_PARTNERS_ACCESS), - ("invalid_none_partner_access", Program.NONE_PARTNERS_ACCESS), - ] - ) - def test_update_program_partners(self, _: Any, partner_access: str) -> None: - area1 = AreaFactory(name="Area1", area_type=self.area_type_other, p_code="AREA1") - area2 = AreaFactory(name="Area2", area_type=self.area_type_other, p_code="AREA2") - area_to_be_unselected = AreaFactory( - name="AreaToBeUnselected", area_type=self.area_type_other, p_code="AREA-TO-BE-UNSELECTED" - ) - program_partner = ProgramPartnerThrough.objects.create( - program=self.program, - partner=self.partner, - ) - program_partner.areas.set([area1, area_to_be_unselected]) - ProgramPartnerThrough.objects.create( - program=self.program, - partner=self.other_partner, - ) - partner_to_be_added = PartnerFactory(name="Partner to be added") - self.create_user_role_with_permissions( - self.user, - [Permissions.PROGRAMME_UPDATE], - self.business_area, - ) - - self.snapshot_graphql_request( - request_string=self.UPDATE_PROGRAM_MUTATION, - context={"user": self.user}, - variables={ - "programData": { - "id": self.id_to_base64(self.program.id, "ProgramNode"), - "name": "updated name", - "status": Program.DRAFT, - "dataCollectingTypeCode": "partial_individuals", - "partners": [ - { - "partner": str(self.partner.id), - "areas": [str(area1.id), str(area2.id)], - }, - { - "partner": str(partner_to_be_added.id), - "areas": [str(area1.id), str(area2.id)], - }, - ], - "partnerAccess": partner_access, - }, - "version": self.program.version, - }, - ) - - def test_update_program_partners_invalid_access_type_from_object(self) -> None: - area1 = AreaFactory(name="Area1", area_type=self.area_type_other, p_code="AREA1") - area2 = AreaFactory(name="Area2", area_type=self.area_type_other, p_code="AREA2") - area_to_be_unselected = AreaFactory( - name="AreaToBeUnselected", area_type=self.area_type_other, p_code="AREA-TO-BE-UNSELECTED" - ) - program_partner = ProgramPartnerThrough.objects.create( - program=self.program, - partner=self.partner, - ) - program_partner.areas.set([area1, area_to_be_unselected]) - ProgramPartnerThrough.objects.create( - program=self.program, - partner=self.other_partner, - ) - partner_to_be_added = PartnerFactory(name="Partner to be added") - self.create_user_role_with_permissions( - self.user, - [Permissions.PROGRAMME_UPDATE], - self.business_area, - ) - - self.snapshot_graphql_request( - request_string=self.UPDATE_PROGRAM_MUTATION, - context={"user": self.user}, - variables={ - "programData": { - "id": self.id_to_base64(self.program.id, "ProgramNode"), - "name": "updated name", - "status": Program.DRAFT, - "dataCollectingTypeCode": "partial_individuals", - "partners": [ - { - "partner": str(self.partner.id), - "areas": [str(area1.id), str(area2.id)], - }, - { - "partner": str(partner_to_be_added.id), - "areas": [str(area1.id), str(area2.id)], - }, - ], - }, - "version": self.program.version, - }, - ) - - def test_update_program_partners_all_partners_access(self) -> None: - self.create_user_role_with_permissions( - self.user, - [Permissions.PROGRAMME_UPDATE], - self.business_area, - ) - - self.snapshot_graphql_request( - request_string=self.UPDATE_PROGRAM_MUTATION, - context={"user": self.user}, - variables={ - "programData": { - "id": self.id_to_base64(self.program.id, "ProgramNode"), - "name": "updated name", - "status": Program.DRAFT, - "dataCollectingTypeCode": "partial_individuals", - "partnerAccess": Program.ALL_PARTNERS_ACCESS, - }, - "version": self.program.version, - }, - ) - - def test_update_full_area_access_flag(self) -> None: - self.create_user_role_with_permissions( - self.user, - [Permissions.PROGRAMME_UPDATE], - self.business_area, - ) - self.snapshot_graphql_request( - request_string=self.UPDATE_PROGRAM_MUTATION, - context={"user": self.user}, - variables={ - "programData": { - "id": self.id_to_base64(self.program.id, "ProgramNode"), - "name": "updated name", - "status": Program.DRAFT, - "dataCollectingTypeCode": "partial_individuals", - "partnerAccess": Program.ALL_PARTNERS_ACCESS, - }, - "version": self.program.version, - }, - ) - - for program_partner_through in Program.objects.get(name="updated name").program_partner_through.all(): - self.assertEqual(program_partner_through.full_area_access, True) - - self.program.refresh_from_db() - - self.snapshot_graphql_request( - request_string=self.UPDATE_PROGRAM_MUTATION, - context={"user": self.user}, - variables={ - "programData": { - "id": self.id_to_base64(self.program.id, "ProgramNode"), - "name": "updated name", - "status": Program.DRAFT, - "dataCollectingTypeCode": "partial_individuals", - "partnerAccess": Program.SELECTED_PARTNERS_ACCESS, - "partners": [ - { - "partner": str(self.partner.id), - "areas": [], - }, - { - "partner": str(self.other_partner.id), - "areas": [self.area_in_afg_1.id], - }, - ], - }, - "version": self.program.version, - }, - ) - - self.assertEqual( - ProgramPartnerThrough.objects.get(partner=self.partner, program__name="updated name").full_area_access, True - ) - self.assertEqual( - ProgramPartnerThrough.objects.get( - partner=self.other_partner, program__name="updated name" - ).full_area_access, - False, - ) - self.assertEqual( - ProgramPartnerThrough.objects.get( - partner=self.unicef_partner, program__name="updated name" - ).full_area_access, - True, - ) - def test_update_active_program_with_dct(self) -> None: self.create_user_role_with_permissions(self.user, [Permissions.PROGRAMME_UPDATE], self.business_area) data_collecting_type = DataCollectingType.objects.get(code="full_collection") @@ -536,31 +322,6 @@ def test_update_program_when_finished(self) -> None: }, ) - def test_update_program_of_other_partner_raise_error(self) -> None: - partner = PartnerFactory(name="UHCR") - another_partner = PartnerFactory(name="WFP") - user = UserFactory.create(partner=partner) - self.create_user_role_with_permissions(user, [Permissions.PROGRAMME_UPDATE], self.business_area) - - self.snapshot_graphql_request( - request_string=self.UPDATE_PROGRAM_MUTATION, - context={"user": user}, - variables={ - "programData": { - "id": self.id_to_base64(self.program.id, "ProgramNode"), - "name": "xyz", - "partnerAccess": Program.SELECTED_PARTNERS_ACCESS, - "partners": [ - { - "partner": str(another_partner.id), - "areas": [], - }, - ], - }, - "version": self.program.version, - }, - ) - def test_update_program_with_programme_code(self) -> None: self.create_user_role_with_permissions(self.user, [Permissions.PROGRAMME_UPDATE], self.business_area) @@ -1151,6 +912,7 @@ def test_finish_active_program_with_not_finished_program_cycle_or_end_date( self.assertEqual(self.program.status, Program.ACTIVE) self.assertEqual(self.program.cycles.count(), 1) program_cycle = self.program.cycles.first() + program_cycle.start_date = self.program.start_date program_cycle.status = ProgramCycle.ACTIVE program_cycle.save() # has active cycle diff --git a/tests/unit/apps/program/test_update_program_partners.py b/tests/unit/apps/program/test_update_program_partners.py new file mode 100644 index 0000000000..f99ef1f000 --- /dev/null +++ b/tests/unit/apps/program/test_update_program_partners.py @@ -0,0 +1,378 @@ +from typing import Any, List + +from parameterized import parameterized + +from hct_mis_api.apps.account.fixtures import PartnerFactory, RoleFactory, UserFactory +from hct_mis_api.apps.account.permissions import Permissions +from hct_mis_api.apps.core.base_test_case import APITestCase +from hct_mis_api.apps.core.fixtures import ( + create_afghanistan, + generate_data_collecting_types, +) +from hct_mis_api.apps.core.models import ( + BusinessArea, + BusinessAreaPartnerThrough, + DataCollectingType, +) +from hct_mis_api.apps.geo.fixtures import AreaFactory, AreaTypeFactory, CountryFactory +from hct_mis_api.apps.program.fixtures import ProgramFactory +from hct_mis_api.apps.program.models import Program, ProgramPartnerThrough + + +class TestUpdateProgramPartners(APITestCase): + UPDATE_PROGRAM_PARTNERS_MUTATION = """ + mutation UpdateProgramPartners($programData: UpdateProgramPartnersInput, $version: BigInt) { + updateProgramPartners(programData: $programData, version: $version) { + program { + partners { + name + areas { + name + } + areaAccess + } + partnerAccess + } + } + } + """ + + @classmethod + def setUpTestData(cls) -> None: + super().setUpTestData() + create_afghanistan() + generate_data_collecting_types() + data_collecting_type = DataCollectingType.objects.get(code="full_collection") + + cls.business_area = BusinessArea.objects.get(slug="afghanistan") + cls.business_area.data_collecting_types.set(DataCollectingType.objects.all().values_list("id", flat=True)) + cls.unicef_partner = PartnerFactory(name="UNICEF") + + cls.program = ProgramFactory.create( + name="initial name", + status=Program.DRAFT, + business_area=cls.business_area, + data_collecting_type=data_collecting_type, + partner_access=Program.NONE_PARTNERS_ACCESS, + version=123, + biometric_deduplication_enabled=True, + ) + unicef_program, _ = ProgramPartnerThrough.objects.get_or_create( + program=cls.program, + partner=cls.unicef_partner, + ) + + cls.partner = PartnerFactory(name="WFP") + cls.user = UserFactory.create(partner=cls.partner) + + # partner with role in BA - will be granted access for ALL_PARTNERS_ACCESS type + cls.other_partner = PartnerFactory(name="Other Partner") + cls.other_partner.allowed_business_areas.set([cls.business_area]) + role = RoleFactory(name="Role in BA") + ba_partner_through = BusinessAreaPartnerThrough.objects.create( + business_area=cls.business_area, + partner=cls.other_partner, + ) + ba_partner_through.roles.set([role]) + + cls.partner_without_role_in_BA = PartnerFactory(name="Partner without role in in BA") + cls.partner_without_role_in_BA.allowed_business_areas.set([cls.business_area]) + + country_afg = CountryFactory(name="Afghanistan") + country_afg.business_areas.set([cls.business_area]) + area_type_afg = AreaTypeFactory(name="Area Type in Afg", country=country_afg) + country_other = CountryFactory( + name="Other Country", + short_name="Oth", + iso_code2="O", + iso_code3="OTH", + iso_num="111", + ) + cls.area_type_other = AreaTypeFactory(name="Area Type Other", country=country_other) + + cls.area_in_afg_1 = AreaFactory(name="Area in AFG 1", area_type=area_type_afg, p_code="AREA-IN-AFG1") + cls.area_in_afg_2 = AreaFactory(name="Area in AFG 2", area_type=area_type_afg, p_code="AREA-IN-AFG2") + cls.area_not_in_afg = AreaFactory( + name="Area not in AFG", area_type=cls.area_type_other, p_code="AREA-NOT-IN-AFG2" + ) + + unicef_program.areas.set([cls.area_in_afg_1, cls.area_in_afg_2]) + + def test_update_program_partners_not_authenticated(self) -> None: + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partnerAccess": Program.NONE_PARTNERS_ACCESS, + }, + "version": self.program.version, + }, + ) + + @parameterized.expand( + [ + ("with_permissions", [Permissions.PROGRAMME_UPDATE]), + ("without_permissions", []), + ] + ) + def test_update_program_partners_authenticated( + self, + _: Any, + permissions: List[Permissions], + ) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area) + + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + context={"user": self.user}, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partnerAccess": Program.NONE_PARTNERS_ACCESS, + }, + "version": self.program.version, + }, + ) + + @parameterized.expand( + [ + ("valid", Program.SELECTED_PARTNERS_ACCESS), + ("invalid_all_partner_access", Program.ALL_PARTNERS_ACCESS), + ("invalid_none_partner_access", Program.NONE_PARTNERS_ACCESS), + ] + ) + def test_update_program_partners(self, _: Any, partner_access: str) -> None: + area1 = AreaFactory(name="Area1", area_type=self.area_type_other, p_code="AREA1") + area2 = AreaFactory(name="Area2", area_type=self.area_type_other, p_code="AREA2") + area_to_be_unselected = AreaFactory( + name="AreaToBeUnselected", area_type=self.area_type_other, p_code="AREA-TO-BE-UNSELECTED" + ) + program_partner = ProgramPartnerThrough.objects.create( + program=self.program, + partner=self.partner, + ) + program_partner.areas.set([area1, area_to_be_unselected]) + ProgramPartnerThrough.objects.create( + program=self.program, + partner=self.other_partner, + ) + partner_to_be_added = PartnerFactory(name="Partner to be added") + self.create_user_role_with_permissions( + self.user, + [Permissions.PROGRAMME_UPDATE], + self.business_area, + ) + + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + context={"user": self.user}, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partners": [ + { + "partner": str(self.partner.id), + "areas": [str(area1.id), str(area2.id)], + }, + { + "partner": str(partner_to_be_added.id), + "areas": [str(area1.id), str(area2.id)], + }, + ], + "partnerAccess": partner_access, + }, + "version": self.program.version, + }, + ) + + def test_update_program_partners_invalid_access_type_from_object(self) -> None: + area1 = AreaFactory(name="Area1", area_type=self.area_type_other, p_code="AREA1") + area2 = AreaFactory(name="Area2", area_type=self.area_type_other, p_code="AREA2") + area_to_be_unselected = AreaFactory( + name="AreaToBeUnselected", area_type=self.area_type_other, p_code="AREA-TO-BE-UNSELECTED" + ) + program_partner = ProgramPartnerThrough.objects.create( + program=self.program, + partner=self.partner, + ) + program_partner.areas.set([area1, area_to_be_unselected]) + ProgramPartnerThrough.objects.create( + program=self.program, + partner=self.other_partner, + ) + partner_to_be_added = PartnerFactory(name="Partner to be added") + self.create_user_role_with_permissions( + self.user, + [Permissions.PROGRAMME_UPDATE], + self.business_area, + ) + + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + context={"user": self.user}, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partners": [ + { + "partner": str(self.partner.id), + "areas": [str(area1.id), str(area2.id)], + }, + { + "partner": str(partner_to_be_added.id), + "areas": [str(area1.id), str(area2.id)], + }, + ], + }, + "version": self.program.version, + }, + ) + + def test_update_program_partners_all_partners_access(self) -> None: + self.create_user_role_with_permissions( + self.user, + [Permissions.PROGRAMME_UPDATE], + self.business_area, + ) + + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + context={"user": self.user}, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partnerAccess": Program.ALL_PARTNERS_ACCESS, + }, + "version": self.program.version, + }, + ) + + def test_update_full_area_access_flag(self) -> None: + self.create_user_role_with_permissions( + self.user, + [Permissions.PROGRAMME_UPDATE], + self.business_area, + ) + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + context={"user": self.user}, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partnerAccess": Program.ALL_PARTNERS_ACCESS, + }, + "version": self.program.version, + }, + ) + + for program_partner_through in Program.objects.get(name="initial name").program_partner_through.all(): + self.assertEqual(program_partner_through.full_area_access, True) + + self.program.refresh_from_db() + + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + context={"user": self.user}, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partnerAccess": Program.SELECTED_PARTNERS_ACCESS, + "partners": [ + { + "partner": str(self.partner.id), + "areas": [], + }, + { + "partner": str(self.other_partner.id), + "areas": [self.area_in_afg_1.id], + }, + ], + }, + "version": self.program.version, + }, + ) + + self.assertEqual( + ProgramPartnerThrough.objects.get(partner=self.partner, program__name="initial name").full_area_access, True + ) + self.assertEqual( + ProgramPartnerThrough.objects.get( + partner=self.other_partner, program__name="initial name" + ).full_area_access, + False, + ) + self.assertEqual( + ProgramPartnerThrough.objects.get( + partner=self.unicef_partner, program__name="initial name" + ).full_area_access, + True, + ) + + def test_update_program_of_other_partner_raise_error(self) -> None: + partner = PartnerFactory(name="UHCR") + another_partner = PartnerFactory(name="WFP") + user = UserFactory.create(partner=partner) + self.create_user_role_with_permissions(user, [Permissions.PROGRAMME_UPDATE], self.business_area) + + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + context={"user": user}, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partnerAccess": Program.SELECTED_PARTNERS_ACCESS, + "partners": [ + { + "partner": str(another_partner.id), + "areas": [], + }, + ], + }, + "version": self.program.version, + }, + ) + + def test_update_program_partners_all_partners_access_refresh_partners_after_update(self) -> None: + self.create_user_role_with_permissions( + self.user, + [Permissions.PROGRAMME_UPDATE], + self.business_area, + ) + + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + context={"user": self.user}, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partnerAccess": Program.ALL_PARTNERS_ACCESS, + }, + "version": self.program.version, + }, + ) + + self.assertEqual(ProgramPartnerThrough.objects.filter(program=self.program).count(), 2) + + self.program.refresh_from_db() + # new partner has a role in this BA + ba_partner_through_new = BusinessAreaPartnerThrough.objects.create( + business_area=self.business_area, + partner=self.partner_without_role_in_BA, + ) + role_new = RoleFactory(name="Role in BA new") + ba_partner_through_new.roles.set([role_new]) + + self.snapshot_graphql_request( + request_string=self.UPDATE_PROGRAM_PARTNERS_MUTATION, + context={"user": self.user}, + variables={ + "programData": { + "id": self.id_to_base64(self.program.id, "ProgramNode"), + "partnerAccess": Program.ALL_PARTNERS_ACCESS, + }, + "version": self.program.version, + }, + ) + + self.assertEqual(ProgramPartnerThrough.objects.filter(program=self.program).count(), 3) diff --git a/tests/unit/one_time_scripts/test_migrate_partner_permissions_and_access.py b/tests/unit/one_time_scripts/test_migrate_partner_permissions_and_access.py deleted file mode 100644 index 25f2e20bd3..0000000000 --- a/tests/unit/one_time_scripts/test_migrate_partner_permissions_and_access.py +++ /dev/null @@ -1,364 +0,0 @@ -from django.conf import settings -from django.test import TestCase - -from hct_mis_api.apps.account.fixtures import PartnerFactory, RoleFactory -from hct_mis_api.apps.core.fixtures import create_afghanistan, create_ukraine -from hct_mis_api.apps.geo.fixtures import AreaFactory, AreaTypeFactory, CountryFactory -from hct_mis_api.apps.geo.models import Area -from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.one_time_scripts.migrate_partner_permissions_and_access import ( - migrate_partner_permissions_and_access, -) - - -class TestMigratePartnerPermissionsAndAccess(TestCase): - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls.partner_unicef = PartnerFactory(name="UNICEF", permissions={}) - cls.afghanistan = create_afghanistan() - cls.ukraine = create_ukraine() - - cls.program_in_afg_1 = ProgramFactory(name="Program in Afg 1", business_area=cls.afghanistan) - cls.program_in_afg_2 = ProgramFactory(name="Program in Afg 2", business_area=cls.afghanistan) - - cls.program_in_ukr_1 = ProgramFactory(name="Program in Ukr 1", business_area=cls.ukraine) - cls.program_in_ukr_2 = ProgramFactory(name="Program in Ukr 2", business_area=cls.ukraine) - - cls.role_in_afg_1 = RoleFactory(name="Role in Afg 1") - cls.role_in_afg_2 = RoleFactory(name="Role in Afg 2") - - cls.role_in_ukr_1 = RoleFactory(name="Role in Ukr 1") - cls.role_in_ukr_2 = RoleFactory(name="Role in Ukr 2") - - country_afg = CountryFactory(name="Afghanistan") - country_afg.business_areas.set([cls.afghanistan]) - area_type_afg = AreaTypeFactory(name="Area Type in Afg", country=country_afg) - country_ukr = CountryFactory( - name="Ukraine", - short_name="Ukraine", - iso_code2="UA", - iso_code3="UKR", - iso_num="0804", - ) - country_ukr.business_areas.set([cls.ukraine]) - area_type_ukr = AreaTypeFactory(name="Area Type in Ukr", country=country_ukr) - - cls.area_in_afg_1 = AreaFactory(name="Area in AFG 1", area_type=area_type_afg, p_code="AREA-IN-AFG1") - cls.area_in_afg_2 = AreaFactory(name="Area in AFG 2", area_type=area_type_afg, p_code="AREA-IN-AFG2") - cls.area_in_afg_3 = AreaFactory(name="Area in AFG 3", area_type=area_type_afg, p_code="AREA-IN-AFG3") - - cls.area_in_ukr_1 = AreaFactory(name="Area in Ukr 1", area_type=area_type_ukr, p_code="AREA-IN-UKR1") - cls.area_in_ukr_2 = AreaFactory(name="Area in Ukr 2", area_type=area_type_ukr, p_code="AREA-IN-UKR2") - - cls.partner_default_empty = PartnerFactory(name=settings.DEFAULT_EMPTY_PARTNER, permissions={}) - - perm_no_access = { - str(cls.afghanistan.id): {"roles": [str(cls.role_in_afg_1.id), str(cls.role_in_afg_2.id)], "programs": {}}, - str(cls.ukraine.id): {"roles": [str(cls.role_in_ukr_2.id)], "programs": {}}, - } - cls.partner_without_access = PartnerFactory(name="Partner without access", permissions=perm_no_access) - - perm_no_role = { - str(cls.afghanistan.id): { - "roles": [], - "programs": { - str(cls.program_in_afg_1.pk): [str(cls.area_in_afg_1.id)], - str(cls.program_in_afg_2.pk): [], - }, - }, - str(cls.ukraine.id): { - "roles": [], - "programs": { - str(cls.program_in_ukr_1.pk): [], - str(cls.program_in_ukr_2.pk): [str(cls.area_in_ukr_2.id)], - }, - }, - } - cls.partner_without_role = PartnerFactory(name="Partner without perm", permissions=perm_no_role) - - perm = { - str(cls.afghanistan.id): { - "roles": [str(cls.role_in_afg_1.id), str(cls.role_in_afg_2.id)], - "programs": { - str(cls.program_in_afg_1.pk): [str(cls.area_in_afg_1.id)], - str(cls.program_in_afg_2.pk): [], - }, - }, - str(cls.ukraine.id): { - "roles": [str(cls.role_in_ukr_1.id), str(cls.role_in_ukr_2.id)], - "programs": { - str(cls.program_in_ukr_1.pk): [], - str(cls.program_in_ukr_2.pk): [str(cls.area_in_ukr_1.id), str(cls.area_in_ukr_2.id)], - }, - }, - } - cls.partner = PartnerFactory(name="Partner", permissions=perm) - - def test_migrate_partner_permissions_and_access(self) -> None: - migrate_partner_permissions_and_access() - - areas_count_in_afg = Area.objects.filter(area_type__country__business_areas=self.afghanistan).count() - areas_count_in_ukr = Area.objects.filter(area_type__country__business_areas=self.ukraine).count() - - # Default Empty Partner - no access, no roles - self.assertEqual(self.partner_default_empty.program_partner_through.count(), 0) - self.assertEqual(self.partner_default_empty.business_area_partner_through.count(), 0) - - # UNICEF Partner - full area access to all programs, no roles - self.assertEqual(self.partner_unicef.program_partner_through.count(), 4) - self.assertEqual(self.partner_unicef.business_area_partner_through.count(), 0) - self.assertEqual( - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_1).first().full_area_access, - True, - ) - self.assertEqual( - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_1).first().areas.count(), - areas_count_in_afg, - ) - self.assertIn( - self.area_in_afg_1, - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_1).first().areas.all(), - ) - self.assertIn( - self.area_in_afg_2, - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_1).first().areas.all(), - ) - self.assertIn( - self.area_in_afg_3, - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_1).first().areas.all(), - ) - self.assertEqual( - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_2).first().full_area_access, - True, - ) - self.assertEqual( - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_2).first().areas.count(), - areas_count_in_afg, - ) - self.assertIn( - self.area_in_afg_1, - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_2).first().areas.all(), - ) - self.assertIn( - self.area_in_afg_2, - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_2).first().areas.all(), - ) - self.assertIn( - self.area_in_afg_3, - self.partner_unicef.program_partner_through.filter(program=self.program_in_afg_2).first().areas.all(), - ) - self.assertEqual( - self.partner_unicef.program_partner_through.filter(program=self.program_in_ukr_1).first().full_area_access, - True, - ) - self.assertEqual( - self.partner_unicef.program_partner_through.filter(program=self.program_in_ukr_1).first().areas.count(), - areas_count_in_ukr, - ) - self.assertIn( - self.area_in_ukr_1, - self.partner_unicef.program_partner_through.filter(program=self.program_in_ukr_1).first().areas.all(), - ) - self.assertIn( - self.area_in_ukr_2, - self.partner_unicef.program_partner_through.filter(program=self.program_in_ukr_1).first().areas.all(), - ) - self.assertEqual( - self.partner_unicef.program_partner_through.filter(program=self.program_in_ukr_2).first().full_area_access, - True, - ) - self.assertEqual( - self.partner_unicef.program_partner_through.filter(program=self.program_in_ukr_2).first().areas.count(), - areas_count_in_ukr, - ) - self.assertIn( - self.area_in_ukr_2, - self.partner_unicef.program_partner_through.filter(program=self.program_in_ukr_2).first().areas.all(), - ) - self.assertIn( - self.area_in_ukr_2, - self.partner_unicef.program_partner_through.filter(program=self.program_in_ukr_2).first().areas.all(), - ) - - # Partner without access - roles, no access - self.assertEqual(self.partner_without_access.program_partner_through.count(), 0) - self.assertEqual(self.partner_without_access.business_area_partner_through.count(), 2) - self.assertEqual( - self.partner_without_access.business_area_partner_through.filter(business_area=self.afghanistan) - .first() - .roles.count(), - 2, - ) - self.assertIn( - self.role_in_afg_1, - self.partner_without_access.business_area_partner_through.filter(business_area=self.afghanistan) - .first() - .roles.all(), - ) - self.assertIn( - self.role_in_afg_2, - self.partner_without_access.business_area_partner_through.filter(business_area=self.afghanistan) - .first() - .roles.all(), - ) - self.assertEqual( - self.partner_without_access.business_area_partner_through.filter(business_area=self.ukraine) - .first() - .roles.count(), - 1, - ) - self.assertIn( - self.role_in_ukr_2, - self.partner_without_access.business_area_partner_through.filter(business_area=self.ukraine) - .first() - .roles.all(), - ) - - # Partner without role - access, no roles - self.assertEqual(self.partner_without_role.program_partner_through.count(), 4) - self.assertEqual(self.partner_without_role.business_area_partner_through.count(), 0) - self.assertEqual( - self.partner_without_role.program_partner_through.filter(program=self.program_in_afg_1) - .first() - .areas.count(), - 1, - ) - self.assertIn( - self.area_in_afg_1, - self.partner_without_role.program_partner_through.filter(program=self.program_in_afg_1).first().areas.all(), - ) - self.assertEqual( - self.partner_without_role.program_partner_through.filter(program=self.program_in_afg_2) - .first() - .full_area_access, - True, - ) - self.assertEqual( - self.partner_without_role.program_partner_through.filter(program=self.program_in_afg_2) - .first() - .areas.count(), - areas_count_in_afg, - ) - self.assertIn( - self.area_in_afg_1, - self.partner_without_role.program_partner_through.filter(program=self.program_in_afg_2).first().areas.all(), - ) - self.assertIn( - self.area_in_afg_2, - self.partner_without_role.program_partner_through.filter(program=self.program_in_afg_2).first().areas.all(), - ) - self.assertIn( - self.area_in_afg_3, - self.partner_without_role.program_partner_through.filter(program=self.program_in_afg_2).first().areas.all(), - ) - self.assertEqual( - self.partner_without_role.program_partner_through.filter(program=self.program_in_ukr_1) - .first() - .full_area_access, - True, - ) - self.assertEqual( - self.partner_without_role.program_partner_through.filter(program=self.program_in_ukr_1) - .first() - .areas.count(), - areas_count_in_ukr, - ) - self.assertIn( - self.area_in_ukr_1, - self.partner_without_role.program_partner_through.filter(program=self.program_in_ukr_1).first().areas.all(), - ) - self.assertIn( - self.area_in_ukr_2, - self.partner_without_role.program_partner_through.filter(program=self.program_in_ukr_1).first().areas.all(), - ) - self.assertEqual( - self.partner_without_role.program_partner_through.filter(program=self.program_in_ukr_2) - .first() - .areas.count(), - 1, - ) - self.assertIn( - self.area_in_ukr_2, - self.partner_without_role.program_partner_through.filter(program=self.program_in_ukr_2).first().areas.all(), - ) - - # Partner - access, roles - self.assertEqual(self.partner.program_partner_through.count(), 4) - self.assertEqual(self.partner.business_area_partner_through.count(), 2) - self.assertEqual( - self.partner.business_area_partner_through.filter(business_area=self.afghanistan).first().roles.count(), 2 - ) - self.assertIn( - self.role_in_afg_1, - self.partner.business_area_partner_through.filter(business_area=self.afghanistan).first().roles.all(), - ) - self.assertIn( - self.role_in_afg_2, - self.partner.business_area_partner_through.filter(business_area=self.afghanistan).first().roles.all(), - ) - self.assertEqual( - self.partner.business_area_partner_through.filter(business_area=self.ukraine).first().roles.count(), 2 - ) - self.assertIn( - self.role_in_ukr_1, - self.partner.business_area_partner_through.filter(business_area=self.ukraine).first().roles.all(), - ) - self.assertIn( - self.role_in_ukr_2, - self.partner.business_area_partner_through.filter(business_area=self.ukraine).first().roles.all(), - ) - self.assertEqual( - self.partner.program_partner_through.filter(program=self.program_in_afg_1).first().areas.count(), 1 - ) - self.assertIn( - self.area_in_afg_1, - self.partner.program_partner_through.filter(program=self.program_in_afg_1).first().areas.all(), - ) - self.assertEqual( - self.partner.program_partner_through.filter(program=self.program_in_afg_2).first().full_area_access, - True, - ) - self.assertEqual( - self.partner.program_partner_through.filter(program=self.program_in_afg_2).first().areas.count(), - areas_count_in_afg, - ) - self.assertIn( - self.area_in_afg_1, - self.partner.program_partner_through.filter(program=self.program_in_afg_2).first().areas.all(), - ) - self.assertIn( - self.area_in_afg_2, - self.partner.program_partner_through.filter(program=self.program_in_afg_2).first().areas.all(), - ) - self.assertIn( - self.area_in_afg_3, - self.partner.program_partner_through.filter(program=self.program_in_afg_2).first().areas.all(), - ) - self.assertEqual( - self.partner.program_partner_through.filter(program=self.program_in_ukr_1).first().full_area_access, - True, - ) - self.assertEqual( - self.partner.program_partner_through.filter(program=self.program_in_ukr_1).first().areas.count(), - areas_count_in_ukr, - ) - self.assertIn( - self.area_in_ukr_1, - self.partner.program_partner_through.filter(program=self.program_in_ukr_1).first().areas.all(), - ) - self.assertIn( - self.area_in_ukr_2, - self.partner.program_partner_through.filter(program=self.program_in_ukr_1).first().areas.all(), - ) - self.assertEqual( - self.partner.program_partner_through.filter(program=self.program_in_ukr_2).first().areas.count(), 2 - ) - self.assertIn( - self.area_in_ukr_2, - self.partner.program_partner_through.filter(program=self.program_in_ukr_2).first().areas.all(), - ) - self.assertIn( - self.area_in_ukr_2, - self.partner.program_partner_through.filter(program=self.program_in_ukr_2).first().areas.all(), - ) From d2ef462874b30448768f54674b72d4898d48a6b7 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Wed, 25 Sep 2024 23:09:14 +0200 Subject: [PATCH 013/202] linter --- src/hct_mis_api/apps/account/signals.py | 1 - src/hct_mis_api/apps/core/signals.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hct_mis_api/apps/account/signals.py b/src/hct_mis_api/apps/account/signals.py index b12a616702..2e3c8ae9e5 100644 --- a/src/hct_mis_api/apps/account/signals.py +++ b/src/hct_mis_api/apps/account/signals.py @@ -7,7 +7,6 @@ from hct_mis_api.apps.account.models import Partner, Role, User, UserRole from hct_mis_api.apps.core.models import BusinessArea, BusinessAreaPartnerThrough -from hct_mis_api.apps.program.models import Program @receiver(post_save, sender=UserRole) diff --git a/src/hct_mis_api/apps/core/signals.py b/src/hct_mis_api/apps/core/signals.py index 1951302ebc..72e6b040b1 100644 --- a/src/hct_mis_api/apps/core/signals.py +++ b/src/hct_mis_api/apps/core/signals.py @@ -26,4 +26,4 @@ def partner_role_removed(sender: Any, instance: BusinessAreaPartnerThrough, **kw partner = instance.partner business_area = instance.business_area programs_in_business_area = business_area.program_set.all() - ProgramPartnerThrough.objects.filter(partner=partner, program__in=programs_in_business_area).delete() \ No newline at end of file + ProgramPartnerThrough.objects.filter(partner=partner, program__in=programs_in_business_area).delete() From 0fa5a3547f2d98fbfc8a6aca172d2ddedb9e712e Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 26 Sep 2024 00:41:17 +0200 Subject: [PATCH 014/202] adjust options --- .../program_details/test_program_details.py | 2 +- .../test_programme_management.py | 16 ++++++++-------- tests/unit/apps/program/test_update_program.py | 1 - 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/selenium/program_details/test_program_details.py b/tests/selenium/program_details/test_program_details.py index 84ea63842f..dd9a30ef44 100644 --- a/tests/selenium/program_details/test_program_details.py +++ b/tests/selenium/program_details/test_program_details.py @@ -266,7 +266,7 @@ def test_program_details(self, standard_program: Program, pageProgrammeDetails: assert program.administrative_areas_of_implementation in pageProgrammeDetails.getLabelAdministrativeAreas().text assert program.description in pageProgrammeDetails.getLabelDescription().text assert "Yes" if program.cash_plus else "No" in pageProgrammeDetails.getLabelCashPlus().text - assert "Only selected partners within the business area" in pageProgrammeDetails.getLabelPartnerAccess().text + assert "Only Selected Partners within the business area" in pageProgrammeDetails.getLabelPartnerAccess().text assert "0" in pageProgrammeDetails.getLabelProgramSize().text def test_edit_programme_from_details( diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index 97f9de1206..ad8e0c23b7 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -372,7 +372,7 @@ def test_create_programme_delete_partners( pageProgrammeManagement.getButtonNext().click() # 3rd step (Partners) pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("Only selected partners within the business area") + pageProgrammeManagement.selectWhoAccessToProgram("Only Selected Partners within the business area") pageProgrammeManagement.choosePartnerOption("UNHCR") pageProgrammeManagement.getButtonAddPartner().click() pageProgrammeManagement.getButtonDelete().click() @@ -438,7 +438,7 @@ def test_create_programme_add_partners_Business_Area( pageProgrammeManagement.getButtonNext().click() # 3rd step (Partners) pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("Only selected partners within the business area") + pageProgrammeManagement.selectWhoAccessToProgram("Only Selected Partners within the business area") pageProgrammeManagement.choosePartnerOption("UNHCR") programme_creation_url = pageProgrammeManagement.driver.current_url pageProgrammeManagement.getButtonSave().click() @@ -496,7 +496,7 @@ def test_copy_programme( pageProgrammeManagement.getButtonNext().click() # 3rd step (Partners) pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("None of the partners should have access") + pageProgrammeManagement.selectWhoAccessToProgram("None of the Partners should have access") pageProgrammeManagement.getButtonSave().click() pageProgrammeDetails.getCopyProgram().click() # 1st step (Details) @@ -566,7 +566,7 @@ def test_create_programme_add_partners_Admin_Area( pageProgrammeManagement.getButtonNext().click() # 3rd step (Partners) pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("Only selected partners within the business area") + pageProgrammeManagement.selectWhoAccessToProgram("Only Selected Partners within the business area") pageProgrammeManagement.choosePartnerOption("UNHCR") pageProgrammeManagement.getLabelAdminArea().click() pageProgrammeManagement.chooseAreaAdmin1ByName("Baghlan").click() @@ -627,7 +627,7 @@ def test_create_programme_back_scenarios( pageProgrammeManagement.getButtonNext().click() # 3rd step (Partners) pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("Only selected partners within the business area") + pageProgrammeManagement.selectWhoAccessToProgram("Only Selected Partners within the business area") pageProgrammeManagement.choosePartnerOption("UNHCR") pageProgrammeManagement.getButtonBack().click() pageProgrammeManagement.getButtonBack().click() @@ -711,7 +711,7 @@ def test_create_programme_chose_dates_via_calendar( "startDate": FormatTime(1, 1, 2022), "endDate": FormatTime(1, 2, 2032), "dataCollectingType": "Partial", - "partners_access": "None of the partners should have access", + "partners_access": "None of the Partners should have access", }, id="none_of_the_partners_should_have_access", ), @@ -722,7 +722,7 @@ def test_create_programme_chose_dates_via_calendar( "startDate": FormatTime(1, 1, 2022), "endDate": FormatTime(1, 2, 2032), "dataCollectingType": "Partial", - "partners_access": "All partners within the business area", + "partners_access": "All Partners within the business area", }, id="all_partners_within_the_business_area", ), @@ -919,7 +919,7 @@ def test_edit_programme_with_rdi( pageProgrammeManagement.getButtonNext().click() # 3rd step (Partners) pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("All partners within the business area") + pageProgrammeManagement.selectWhoAccessToProgram("All Partners within the business area") pageProgrammeManagement.getButtonSave().click() pageProgrammeManagement.getButtonEditProgram() program_name = pageProgrammeDetails.getHeaderTitle().text diff --git a/tests/unit/apps/program/test_update_program.py b/tests/unit/apps/program/test_update_program.py index 5df8a13abd..57cc5d7fde 100644 --- a/tests/unit/apps/program/test_update_program.py +++ b/tests/unit/apps/program/test_update_program.py @@ -912,7 +912,6 @@ def test_finish_active_program_with_not_finished_program_cycle_or_end_date( self.assertEqual(self.program.status, Program.ACTIVE) self.assertEqual(self.program.cycles.count(), 1) program_cycle = self.program.cycles.first() - program_cycle.start_date = self.program.start_date program_cycle.status = ProgramCycle.ACTIVE program_cycle.save() # has active cycle From 2c6943785b878f2ff4cc3109e2282fd3625e5faf Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 26 Sep 2024 01:53:36 +0200 Subject: [PATCH 015/202] try with assert --- .../programme_management/test_programme_management.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index ad8e0c23b7..dfbc4bf36f 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -373,6 +373,7 @@ def test_create_programme_delete_partners( # 3rd step (Partners) pageProgrammeManagement.getAccessToProgram().click() pageProgrammeManagement.selectWhoAccessToProgram("Only Selected Partners within the business area") + assert Partner.objects.get(name="UNHCR").business_areas.all() == 1 pageProgrammeManagement.choosePartnerOption("UNHCR") pageProgrammeManagement.getButtonAddPartner().click() pageProgrammeManagement.getButtonDelete().click() @@ -394,7 +395,6 @@ def test_create_programme_cancel_scenario( # ToDo: Check Unicef partner! and delete classes -@pytest.mark.night @pytest.mark.usefixtures("login") class TestBusinessAreas: @pytest.mark.parametrize( @@ -522,7 +522,6 @@ def test_copy_programme( assert "New Programme" in pageProgrammeDetails.getHeaderTitle().text -@pytest.mark.night @pytest.mark.usefixtures("login") class TestAdminAreas: @pytest.mark.parametrize( @@ -722,7 +721,7 @@ def test_create_programme_chose_dates_via_calendar( "startDate": FormatTime(1, 1, 2022), "endDate": FormatTime(1, 2, 2032), "dataCollectingType": "Partial", - "partners_access": "All Partners within the business area", + "partners_access": "All Current Partners within the business area", }, id="all_partners_within_the_business_area", ), @@ -919,7 +918,7 @@ def test_edit_programme_with_rdi( pageProgrammeManagement.getButtonNext().click() # 3rd step (Partners) pageProgrammeManagement.getAccessToProgram().click() - pageProgrammeManagement.selectWhoAccessToProgram("All Partners within the business area") + pageProgrammeManagement.selectWhoAccessToProgram("All Current Partners within the business area") pageProgrammeManagement.getButtonSave().click() pageProgrammeManagement.getButtonEditProgram() program_name = pageProgrammeDetails.getHeaderTitle().text From 956d50b1e66bf48034327ea530d838b5f42f7434 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 26 Sep 2024 03:12:33 +0200 Subject: [PATCH 016/202] adjust factory --- tests/selenium/conftest.py | 8 +++++++- .../programme_management/test_programme_management.py | 1 - 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/selenium/conftest.py b/tests/selenium/conftest.py index 31a638fb7d..38825ff265 100644 --- a/tests/selenium/conftest.py +++ b/tests/selenium/conftest.py @@ -69,7 +69,7 @@ from selenium.webdriver import Chrome from selenium.webdriver.chrome.options import Options -from hct_mis_api.apps.account.fixtures import UserFactory +from hct_mis_api.apps.account.fixtures import RoleFactory, UserFactory from hct_mis_api.apps.account.models import Partner, Role, User, UserRole from hct_mis_api.apps.account.permissions import Permissions from hct_mis_api.apps.core.models import ( @@ -497,6 +497,12 @@ def create_super_user(business_area: BusinessArea) -> User: for partner in Partner.objects.exclude(name="UNICEF"): partner.allowed_business_areas.add(business_area) + role = RoleFactory(name=f"Role for {partner.name}") + partner_through = BusinessAreaPartnerThrough.objects.create( + business_area=business_area, + partner=partner, + ) + partner_through.roles.set([role]) assert User.objects.filter(email="test@example.com").first() assert user.is_superuser diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index dfbc4bf36f..a049add167 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -373,7 +373,6 @@ def test_create_programme_delete_partners( # 3rd step (Partners) pageProgrammeManagement.getAccessToProgram().click() pageProgrammeManagement.selectWhoAccessToProgram("Only Selected Partners within the business area") - assert Partner.objects.get(name="UNHCR").business_areas.all() == 1 pageProgrammeManagement.choosePartnerOption("UNHCR") pageProgrammeManagement.getButtonAddPartner().click() pageProgrammeManagement.getButtonDelete().click() From 2139a54df5baf10f02e452eea9cccff3e5116b0d Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 26 Sep 2024 05:04:54 +0200 Subject: [PATCH 017/202] fix e2e --- .../programme_management/test_programme_management.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index a049add167..04404d6b9a 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -838,9 +838,10 @@ def test_programme_partners( pageProgrammeManagement.driver.find_element(By.CSS_SELECTOR, "body").click() pageProgrammeManagement.choosePartnerOption("UNHCR") - pageProgrammeManagement.getButtonSave().click() programme_creation_url = pageProgrammeManagement.driver.current_url + pageProgrammeManagement.getButtonSave().click() + # Check Details page assert "details" in pageProgrammeDetails.wait_for_new_url(programme_creation_url).split("/") assert "Test Program Partners" in pageProgrammeDetails.getHeaderTitle().text @@ -857,11 +858,12 @@ def test_programme_partners( pageProgrammeManagement.getSelectEditProgramPartners().click() pageProgrammeManagement.getAccessToProgram().click() pageProgrammeManagement.selectWhoAccessToProgram("All Current Partners within the business area") + + programme_edit_url = pageProgrammeManagement.driver.current_url pageProgrammeManagement.getButtonSave().click() - programme_details_url = pageProgrammeManagement.driver.current_url # Check Details page - assert "details" in pageProgrammeDetails.wait_for_new_url(programme_details_url).split("/") + assert "details" in pageProgrammeDetails.wait_for_new_url(programme_edit_url).split("/") partner_name_elements_new = pageProgrammeManagement.driver.find_elements( By.CSS_SELECTOR, "[data-cy='label-partner-name']" From 9e99c95196b6ffd99b95b1e23afb0570c8e35a9a Mon Sep 17 00:00:00 2001 From: Pavlo Mokiichuk <pv.pasha.pv@gmail.com> Date: Thu, 26 Sep 2024 14:42:08 +0200 Subject: [PATCH 018/202] PaymentPlan details show Exchange Rate + Summary PDF (#4240) * update pdf template * black * add exchange rate in pp details * upd fe schema --------- Co-authored-by: Maciej Szewczyk <maciej.szewczyk@tivix.com> --- src/frontend/src/__generated__/graphql.tsx | 3 +- .../queries/paymentmodule/PaymentPlan.ts | 1 + .../FollowUpPaymentPlanDetails.tsx | 26 ++++++++++- .../PaymentPlanDetails/PaymentPlanDetails.tsx | 24 +++++++++- .../PaymentPlanDetails.test.tsx.snap | 44 +++++++++++++++++++ .../FollowUpPaymentPlanDetails.tsx | 26 ++++++++++- .../PaymentPlanDetails/PaymentPlanDetails.tsx | 24 +++++++++- .../PaymentPlanDetails.test.tsx.snap | 44 +++++++++++++++++++ .../PaymentPlanDetails/PaymentPlanDetails.tsx | 26 ++++++++++- src/frontend/src/utils/en.json | 4 +- src/hct_mis_api/apps/payment/fixtures.py | 1 + .../payment_plan_summary_pdf_template.html | 6 +++ ...ple_payment_plan_summary_pdf_template.html | 6 +++ 13 files changed, 228 insertions(+), 7 deletions(-) diff --git a/src/frontend/src/__generated__/graphql.tsx b/src/frontend/src/__generated__/graphql.tsx index 33d46bf97e..b952b85210 100644 --- a/src/frontend/src/__generated__/graphql.tsx +++ b/src/frontend/src/__generated__/graphql.tsx @@ -10756,7 +10756,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, adminUrl?: string | null, currency: PaymentPlanCurrency, 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 }, 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<string | null> | 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 } | 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: PaymentPlanCurrency, 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 }, 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<string | null> | 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 } | null }; export type AllCashPlansQueryVariables = Exact<{ program?: InputMaybe<Scalars['ID']['input']>; @@ -19272,6 +19272,7 @@ export const PaymentPlanDocument = gql` availablePaymentRecordsCount bankReconciliationSuccess bankReconciliationError + exchangeRate createdBy { id firstName diff --git a/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts b/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts index 99c164147a..de2483cf53 100644 --- a/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts +++ b/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts @@ -13,6 +13,7 @@ export const PAYMENT_PLAN_QUERY = gql` availablePaymentRecordsCount bankReconciliationSuccess bankReconciliationError + exchangeRate createdBy { id firstName diff --git a/src/frontend/src/components/paymentmodule/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails.tsx b/src/frontend/src/components/paymentmodule/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails.tsx index e6ebf97da4..24a2c98608 100644 --- a/src/frontend/src/components/paymentmodule/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails.tsx +++ b/src/frontend/src/components/paymentmodule/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails.tsx @@ -1,4 +1,4 @@ -import { Grid, Typography } from '@mui/material'; +import { Box, Grid, IconButton, Tooltip, Typography } from '@mui/material'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { PaymentPlanQuery } from '@generated/graphql'; @@ -9,6 +9,7 @@ import { LabelizedField } from '@core/LabelizedField'; import { OverviewContainer } from '@core/OverviewContainer'; import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; +import { Info } from '@mui/icons-material'; interface FollowUpPaymentPlanDetailsProps { baseUrl: string; @@ -33,6 +34,7 @@ export function FollowUpPaymentPlanDetails({ unicefId: sourcePaymentPlanUnicefId, }, targetPopulation, + exchangeRate, } = paymentPlan; return ( @@ -96,6 +98,28 @@ export function FollowUpPaymentPlanDetails({ <UniversalMoment>{dispersionEndDate}</UniversalMoment> </LabelizedField> </Grid> + <Grid item xs={3}> + <Box display="flex" alignItems="center"> + <Box mr={1}> + <LabelizedField label={t('FX Rate Applied')}> + {exchangeRate} + </LabelizedField> + </Box> + <Tooltip + title={t( + 'If displayed exchange rate differs from Vision, please contact your designated focal point for resolution', + )} + > + <IconButton + color="primary" + aria-label="exchange-rate" + data-cy="info-exchange-rate" + > + <Info /> + </IconButton> + </Tooltip> + </Box> + </Grid> </Grid> </OverviewContainer> </ContainerColumnWithBorder> diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails/PaymentPlanDetails.tsx b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails/PaymentPlanDetails.tsx index 5d0c5ba1ad..b180ff5a7a 100644 --- a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails/PaymentPlanDetails.tsx +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails/PaymentPlanDetails.tsx @@ -1,4 +1,4 @@ -import { Grid, Typography } from '@mui/material'; +import { Box, Grid, IconButton, Tooltip, Typography } from '@mui/material'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { renderUserName } from '@utils/utils'; @@ -11,6 +11,7 @@ import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; import { FieldBorder } from '@core/FieldBorder'; import { RelatedFollowUpPaymentPlans } from './RelatedFollowUpPaymentPlans'; +import { Info } from '@mui/icons-material'; interface PaymentPlanDetailsProps { baseUrl: string; @@ -29,6 +30,7 @@ export const PaymentPlanDetails = ({ currency, startDate, endDate, + exchangeRate, dispersionStartDate, dispersionEndDate, followUps, @@ -89,6 +91,26 @@ export const PaymentPlanDetails = ({ <UniversalMoment>{dispersionEndDate}</UniversalMoment> </LabelizedField> </Grid> + <Grid item xs={3}> + <Box mr={1}> + <LabelizedField label={t('FX Rate Applied')}> + {exchangeRate} + </LabelizedField> + </Box> + <Tooltip + title={t( + 'If displayed exchange rate differs from Vision, please contact your designated focal point for resolution', + )} + > + <IconButton + color="primary" + aria-label="exchange-rate" + data-cy="info-exchange-rate" + > + <Info /> + </IconButton> + </Tooltip> + </Grid> </Grid> <Grid container direction="column" item xs={3} spacing={6}> <Grid item xs={12}> diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails/__snapshots__/PaymentPlanDetails.test.tsx.snap b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails/__snapshots__/PaymentPlanDetails.test.tsx.snap index 7439af2ea4..634f0d0661 100644 --- a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails/__snapshots__/PaymentPlanDetails.test.tsx.snap +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails/__snapshots__/PaymentPlanDetails.test.tsx.snap @@ -228,6 +228,50 @@ exports[`components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails should r </div> </div> </div> + <div + class="MuiGrid-root MuiGrid-item MuiGrid-grid-xs-3 css-1equabv-MuiGrid-root" + > + <div + class="MuiBox-root css-15ro776" + > + <div> + <span + class="sc-hLQSwg hqLXmS" + color="textSecondary" + > + FX Rate Applied + </span> + <div + data-cy="label-FX Rate Applied" + > + - + </div> + </div> + </div> + <button + aria-label="exchange-rate" + class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeMedium css-zt49xb-MuiButtonBase-root-MuiIconButton-root" + data-cy="info-exchange-rate" + data-mui-internal-clone-element="true" + tabindex="0" + type="button" + > + <svg + aria-hidden="true" + class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root" + data-testid="InfoIcon" + focusable="false" + viewBox="0 0 24 24" + > + <path + d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-6h2zm0-8h-2V7h2z" + /> + </svg> + <span + class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root" + /> + </button> + </div> </div> <div class="MuiGrid-root MuiGrid-container MuiGrid-item MuiGrid-spacing-xs-6 MuiGrid-direction-xs-column MuiGrid-grid-xs-3 css-1ua2l6q-MuiGrid-root" diff --git a/src/frontend/src/components/paymentmodulepeople/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails.tsx b/src/frontend/src/components/paymentmodulepeople/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails.tsx index e6ebf97da4..24a2c98608 100644 --- a/src/frontend/src/components/paymentmodulepeople/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails.tsx +++ b/src/frontend/src/components/paymentmodulepeople/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails/FollowUpPaymentPlanDetails.tsx @@ -1,4 +1,4 @@ -import { Grid, Typography } from '@mui/material'; +import { Box, Grid, IconButton, Tooltip, Typography } from '@mui/material'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { PaymentPlanQuery } from '@generated/graphql'; @@ -9,6 +9,7 @@ import { LabelizedField } from '@core/LabelizedField'; import { OverviewContainer } from '@core/OverviewContainer'; import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; +import { Info } from '@mui/icons-material'; interface FollowUpPaymentPlanDetailsProps { baseUrl: string; @@ -33,6 +34,7 @@ export function FollowUpPaymentPlanDetails({ unicefId: sourcePaymentPlanUnicefId, }, targetPopulation, + exchangeRate, } = paymentPlan; return ( @@ -96,6 +98,28 @@ export function FollowUpPaymentPlanDetails({ <UniversalMoment>{dispersionEndDate}</UniversalMoment> </LabelizedField> </Grid> + <Grid item xs={3}> + <Box display="flex" alignItems="center"> + <Box mr={1}> + <LabelizedField label={t('FX Rate Applied')}> + {exchangeRate} + </LabelizedField> + </Box> + <Tooltip + title={t( + 'If displayed exchange rate differs from Vision, please contact your designated focal point for resolution', + )} + > + <IconButton + color="primary" + aria-label="exchange-rate" + data-cy="info-exchange-rate" + > + <Info /> + </IconButton> + </Tooltip> + </Box> + </Grid> </Grid> </OverviewContainer> </ContainerColumnWithBorder> diff --git a/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/PaymentPlanDetails/PaymentPlanDetails.tsx b/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/PaymentPlanDetails/PaymentPlanDetails.tsx index 5d0c5ba1ad..caffffd514 100644 --- a/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/PaymentPlanDetails/PaymentPlanDetails.tsx +++ b/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/PaymentPlanDetails/PaymentPlanDetails.tsx @@ -1,4 +1,4 @@ -import { Grid, Typography } from '@mui/material'; +import { Box, Grid, IconButton, Tooltip, Typography } from '@mui/material'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { renderUserName } from '@utils/utils'; @@ -11,6 +11,7 @@ import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; import { FieldBorder } from '@core/FieldBorder'; import { RelatedFollowUpPaymentPlans } from './RelatedFollowUpPaymentPlans'; +import { Info } from '@mui/icons-material'; interface PaymentPlanDetailsProps { baseUrl: string; @@ -32,6 +33,7 @@ export const PaymentPlanDetails = ({ dispersionStartDate, dispersionEndDate, followUps, + exchangeRate, } = paymentPlan; return ( @@ -89,6 +91,26 @@ export const PaymentPlanDetails = ({ <UniversalMoment>{dispersionEndDate}</UniversalMoment> </LabelizedField> </Grid> + <Grid item xs={3}> + <Box mr={1}> + <LabelizedField label={t('FX Rate Applied')}> + {exchangeRate} + </LabelizedField> + </Box> + <Tooltip + title={t( + 'If displayed exchange rate differs from Vision, please contact your designated focal point for resolution', + )} + > + <IconButton + color="primary" + aria-label="exchange-rate" + data-cy="info-exchange-rate" + > + <Info /> + </IconButton> + </Tooltip> + </Grid> </Grid> <Grid container direction="column" item xs={3} spacing={6}> <Grid item xs={12}> diff --git a/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/PaymentPlanDetails/__snapshots__/PaymentPlanDetails.test.tsx.snap b/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/PaymentPlanDetails/__snapshots__/PaymentPlanDetails.test.tsx.snap index 7439af2ea4..634f0d0661 100644 --- a/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/PaymentPlanDetails/__snapshots__/PaymentPlanDetails.test.tsx.snap +++ b/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/PaymentPlanDetails/__snapshots__/PaymentPlanDetails.test.tsx.snap @@ -228,6 +228,50 @@ exports[`components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails should r </div> </div> </div> + <div + class="MuiGrid-root MuiGrid-item MuiGrid-grid-xs-3 css-1equabv-MuiGrid-root" + > + <div + class="MuiBox-root css-15ro776" + > + <div> + <span + class="sc-hLQSwg hqLXmS" + color="textSecondary" + > + FX Rate Applied + </span> + <div + data-cy="label-FX Rate Applied" + > + - + </div> + </div> + </div> + <button + aria-label="exchange-rate" + class="MuiButtonBase-root MuiIconButton-root MuiIconButton-colorPrimary MuiIconButton-sizeMedium css-zt49xb-MuiButtonBase-root-MuiIconButton-root" + data-cy="info-exchange-rate" + data-mui-internal-clone-element="true" + tabindex="0" + type="button" + > + <svg + aria-hidden="true" + class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-i4bv87-MuiSvgIcon-root" + data-testid="InfoIcon" + focusable="false" + viewBox="0 0 24 24" + > + <path + d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-6h2zm0-8h-2V7h2z" + /> + </svg> + <span + class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root" + /> + </button> + </div> </div> <div class="MuiGrid-root MuiGrid-container MuiGrid-item MuiGrid-spacing-xs-6 MuiGrid-direction-xs-column MuiGrid-grid-xs-3 css-1ua2l6q-MuiGrid-root" diff --git a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetails.tsx b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetails.tsx index 5647cefe47..4c65c3705c 100644 --- a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetails.tsx +++ b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetails.tsx @@ -1,4 +1,4 @@ -import { Grid, Typography } from '@mui/material'; +import { Box, Grid, IconButton, Tooltip, Typography } from '@mui/material'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; import { renderUserName } from '@utils/utils'; @@ -10,6 +10,7 @@ import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; import { FieldBorder } from '@core/FieldBorder'; import { RelatedFollowUpPaymentPlans } from '@components/paymentmodule/PaymentPlanDetails/PaymentPlanDetails/RelatedFollowUpPaymentPlans'; +import { Info } from '@mui/icons-material'; interface PaymentPlanDetailsProps { baseUrl: string; @@ -29,6 +30,7 @@ export const PaymentPlanDetails = ({ dispersionStartDate, dispersionEndDate, followUps, + exchangeRate, } = paymentPlan; return ( @@ -70,6 +72,28 @@ export const PaymentPlanDetails = ({ <UniversalMoment>{dispersionEndDate}</UniversalMoment> </LabelizedField> </Grid> + <Grid item xs={3}> + <Box display="flex" alignItems="center"> + <Box mr={1}> + <LabelizedField label={t('FX Rate Applied')}> + {exchangeRate} + </LabelizedField> + </Box> + <Tooltip + title={t( + 'If displayed exchange rate differs from Vision, please contact your designated focal point for resolution', + )} + > + <IconButton + color="primary" + aria-label="exchange-rate" + data-cy="info-exchange-rate" + > + <Info /> + </IconButton> + </Tooltip> + </Box> + </Grid> </Grid> <Grid container direction="column" item xs={3} spacing={6}> <Grid item xs={12}> diff --git a/src/frontend/src/utils/en.json b/src/frontend/src/utils/en.json index d93c736f5f..4b18194cf6 100644 --- a/src/frontend/src/utils/en.json +++ b/src/frontend/src/utils/en.json @@ -899,5 +899,7 @@ "Only Empty Values": "Only Empty Values", "Open Biometrics Results": "Open Biometrics Results", "Algorithm similarity score:": "Algorithm similarity score:", - "Face images matching suggests:": "Face images matching suggests:" + "Face images matching suggests:": "Face images matching suggests:", + "FX Rate Applied": "FX Rate Applied", + "If displayed exchange rate differs from Vision, please contact your designated focal point for resolutionIf displayed exchange rate differs from Vision, please contact your designated focal point for resolution": "If displayed exchange rate differs from Vision, please contact your designated focal point for resolution" } diff --git a/src/hct_mis_api/apps/payment/fixtures.py b/src/hct_mis_api/apps/payment/fixtures.py index 23419c4244..5d8a3095b7 100644 --- a/src/hct_mis_api/apps/payment/fixtures.py +++ b/src/hct_mis_api/apps/payment/fixtures.py @@ -802,6 +802,7 @@ def generate_reconciled_payment_plan() -> None: total_delivered_quantity=999, total_entitled_quantity=2999, is_follow_up=False, + exchange_rate=234.6742, )[0] # update status payment_plan.status_finished() diff --git a/src/hct_mis_api/apps/payment/templates/payment/payment_plan_summary_pdf_template.html b/src/hct_mis_api/apps/payment/templates/payment/payment_plan_summary_pdf_template.html index 02153c6b5b..83923cf10e 100644 --- a/src/hct_mis_api/apps/payment/templates/payment/payment_plan_summary_pdf_template.html +++ b/src/hct_mis_api/apps/payment/templates/payment/payment_plan_summary_pdf_template.html @@ -41,6 +41,12 @@ <h2>LETTER OF AUTHORIZATION</h2> <td>Target Population</td> <td>{{ payment_plan.target_population.name }}</td> </tr> + <tr> + <td></td> + <td></td> + <td>FX Rate Applied</td> + <td>{{ payment_plan.exchange_rate|default_if_none:"-"|floatformat:8 }}</td> + </tr> <tr> <td colspan="4" class="header">SUMMARY</td> </tr> diff --git a/src/hct_mis_api/apps/payment/templates/payment/people_payment_plan_summary_pdf_template.html b/src/hct_mis_api/apps/payment/templates/payment/people_payment_plan_summary_pdf_template.html index 921ad2fce6..728c7c47b2 100644 --- a/src/hct_mis_api/apps/payment/templates/payment/people_payment_plan_summary_pdf_template.html +++ b/src/hct_mis_api/apps/payment/templates/payment/people_payment_plan_summary_pdf_template.html @@ -42,6 +42,12 @@ <h2>LETTER OF AUTHORIZATION</h2> <td>{{ payment_plan.target_population.name }}</td> </tr> <tr> + <tr> + <td></td> + <td></td> + <td>FX Rate Applied</td> + <td>{{ payment_plan.exchange_rate|default_if_none:"-"|floatformat:8 }}</td> + </tr> <td colspan="4" class="header">SUMMARY</td> </tr> <tr> From 629a32b9da49182ebd73b9fdeb1a0a12de230b33 Mon Sep 17 00:00:00 2001 From: Pavlo Mokiichuk <pv.pasha.pv@gmail.com> Date: Thu, 26 Sep 2024 15:47:24 +0200 Subject: [PATCH 019/202] 208491: RDI - Rename label to 'Total number of registered Individuals' (#4261) * upd label * upd fe test snap * fix e2e * snaps --------- Co-authored-by: Maciej Szewczyk <maciej.szewczyk@tivix.com> --- .../rdi/details/RegistrationDetails/RegistrationDetails.tsx | 4 ++-- .../__snapshots__/RegistrationDetails.test.tsx.snap | 4 ++-- .../page_object/registration_data_import/rdi_details_page.py | 2 +- tests/selenium/page_object/targeting/targeting_create.py | 2 +- .../registration_data_import/test_registration_data_import.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/frontend/src/components/rdi/details/RegistrationDetails/RegistrationDetails.tsx b/src/frontend/src/components/rdi/details/RegistrationDetails/RegistrationDetails.tsx index 959821ff93..c550337f50 100644 --- a/src/frontend/src/components/rdi/details/RegistrationDetails/RegistrationDetails.tsx +++ b/src/frontend/src/components/rdi/details/RegistrationDetails/RegistrationDetails.tsx @@ -95,7 +95,7 @@ export function RegistrationDetails({ <Grid item xs={6}> <BigValueContainer> <LabelizedField - label={t('Total Number of People')} + label={t('Total Number of Registered People')} dataCy="individuals" > <BigValue>{registration?.numberOfIndividuals}</BigValue> @@ -122,7 +122,7 @@ export function RegistrationDetails({ <Grid item xs={6}> <BigValueContainer> <LabelizedField - label={t('Total Number of Individuals')} + label={t('Total Number of Registered Individuals')} dataCy="individuals" > <BigValue>{registration?.numberOfIndividuals}</BigValue> 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 039aa962a0..4769581fb6 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-guDLey RwNuC" color="textSecondary" > - Total Number of Individuals + Total Number of Registered Individuals </span> <div - data-cy="label-Total Number of Individuals" + data-cy="label-Total Number of Registered Individuals" > <span class="sc-dmyCSP bGPBYf" diff --git a/tests/selenium/page_object/registration_data_import/rdi_details_page.py b/tests/selenium/page_object/registration_data_import/rdi_details_page.py index c4ddb45b38..d29fbc4669 100644 --- a/tests/selenium/page_object/registration_data_import/rdi_details_page.py +++ b/tests/selenium/page_object/registration_data_import/rdi_details_page.py @@ -16,7 +16,7 @@ class RDIDetailsPage(BaseComponents): labelizedFieldContainerHouseholds = 'div[data-cy="labelized-field-container-households"]' labelTotalNumberOfHouseholds = 'div[data-cy="label-Total Number of Households"]' labelizedFieldContainerIndividuals = 'div[data-cy="labelized-field-container-individuals"]' - labelTotalNumberOfIndividuals = 'div[data-cy="label-Total Number of Individuals"]' + labelTotalNumberOfIndividuals = 'div[data-cy="label-Total Number of Registered Individuals"]' tableLabel = 'span[data-cy="table-label"]' tablePagination = 'div[data-cy="table-pagination"]' importedIndividualsTable = 'div[data-cy="imported-individuals-table"]' diff --git a/tests/selenium/page_object/targeting/targeting_create.py b/tests/selenium/page_object/targeting/targeting_create.py index d2f19681a5..dafba16277 100644 --- a/tests/selenium/page_object/targeting/targeting_create.py +++ b/tests/selenium/page_object/targeting/targeting_create.py @@ -82,7 +82,7 @@ class TargetingCreate(BaseComponents): 'div[data-cy="select-individualsFiltersBlocks[{}].individualBlockFilters[{}].value"]' ) totalNumberOfHouseholdsCount = 'div[data-cy="total-number-of-households-count"]' - totalNumberOfPeopleCount = 'div[data-cy="label-Total Number of People"]' + totalNumberOfPeopleCount = 'div[data-cy="label-Total Number of Registered People"]' selectProgramCycleAutocomplete = 'div[data-cy="filters-program-cycle-autocomplete"]' programmeCycleInput = 'div[data-cy="Programme Cycle-input"]' diff --git a/tests/selenium/registration_data_import/test_registration_data_import.py b/tests/selenium/registration_data_import/test_registration_data_import.py index 0d561c6e65..7925ee11e9 100644 --- a/tests/selenium/registration_data_import/test_registration_data_import.py +++ b/tests/selenium/registration_data_import/test_registration_data_import.py @@ -145,7 +145,7 @@ def test_smoke_registration_data_details_page( ) assert "3" in pageDetailsRegistrationDataImport.getLabelTotalNumberOfHouseholds().text assert ( - "TOTAL NUMBER OF INDIVIDUALS" + "TOTAL NUMBER OF REGISTERED INDIVIDUALS" in pageDetailsRegistrationDataImport.getLabelizedFieldContainerIndividuals().text ) assert "9" in pageDetailsRegistrationDataImport.getLabelTotalNumberOfIndividuals().text From fef8aa6fb8563952ed7d42441d5751ef09c07b3d Mon Sep 17 00:00:00 2001 From: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:07:19 +0200 Subject: [PATCH 020/202] Deduplication engine integration demo fixes (#4253) --- src/frontend/data/schema.graphql | 33 +- src/frontend/package.json | 2 +- src/frontend/src/__generated__/graphql.tsx | 166 ++----- .../src/__generated__/introspection-result.ts | 1 - .../fragments/GrievanceTicketFragment.ts | 27 +- .../apollo/fragments/RegistrationFragments.ts | 2 +- .../GrievancesTable/GrievancesFilters.tsx | 1 + .../NeedsAdjudication/BiometricsResults.tsx | 8 +- .../NeedsAdjudicationActions.tsx | 7 +- .../pages/rdi/RegistrationDataImportPage.tsx | 2 +- .../PeopleRegistrationDataImportPage.tsx | 2 +- ...ationDataImportForPeopleTableHeadCells.tsx | 4 +- ...egistrationDataImportForPeopleTableRow.tsx | 2 +- ...tionDataImportForPeopleTable.test.tsx.snap | 4 +- .../RegistrationDataImportTable.tsx | 4 +- .../RegistrationDataImportTableRow.tsx | 2 +- .../grievance/migrations/0073_migration.py | 17 + src/hct_mis_api/apps/grievance/models.py | 3 - src/hct_mis_api/apps/grievance/schema.py | 11 +- .../needs_adjudication_ticket_services.py | 23 +- src/hct_mis_api/apps/program/schema.py | 20 +- .../apps/registration_data/admin.py | 7 +- .../apps/registration_data/models.py | 55 +-- .../apps/registration_data/nodes.py | 20 + .../apps/registration_data/schema.py | 45 +- .../apis/deduplication_engine.py | 22 +- .../services/biometric_deduplication.py | 90 ++-- .../snap_test_approve_automatic_tickets.py | 33 +- .../test_approve_automatic_tickets.py | 26 +- .../test_create_needs_adjudication_tickets.py | 93 +++- .../apps/grievance/test_services_utils.py | 254 ++++++++-- .../snapshots/snap_test_all_programs_query.py | 10 +- .../apps/program/test_all_programs_query.py | 21 + ...nap_test_registration_data_import_query.py | 8 +- .../apps/registration_data/test_models.py | 31 -- .../test_registration_data_import_query.py | 2 +- .../test_biometric_deduplication_service.py | 444 ++++++++++++++---- .../test_deduplication_engine_api.py | 81 +++- 38 files changed, 1022 insertions(+), 561 deletions(-) create mode 100644 src/hct_mis_api/apps/grievance/migrations/0073_migration.py diff --git a/src/frontend/data/schema.graphql b/src/frontend/data/schema.graphql index e1a3a1379d..6fd69bae45 100644 --- a/src/frontend/data/schema.graphql +++ b/src/frontend/data/schema.graphql @@ -895,26 +895,16 @@ scalar DateTime scalar Decimal -type DeduplicationEngineSimilarityPairNode implements Node { - id: ID! - program: ProgramNode! - individual1: IndividualNode! - individual2: IndividualNode! - similarityScore: String - ticketneedsadjudicationdetailsSet(offset: Int, before: String, after: String, first: Int, last: Int): TicketNeedsAdjudicationDetailsNodeConnection! - isDuplicate: Boolean -} - -type DeduplicationEngineSimilarityPairNodeConnection { - pageInfo: PageInfo! - edges: [DeduplicationEngineSimilarityPairNodeEdge]! - totalCount: Int - edgeCount: Int +type DeduplicationEngineSimilarityPairIndividualNode { + photo: String + fullName: String + unicefId: String } -type DeduplicationEngineSimilarityPairNodeEdge { - node: DeduplicationEngineSimilarityPairNode - cursor: String! +type DeduplicationEngineSimilarityPairNode { + individual1: DeduplicationEngineSimilarityPairIndividualNode + individual2: DeduplicationEngineSimilarityPairIndividualNode + similarityScore: String } type DeduplicationResultNode { @@ -2518,8 +2508,6 @@ type IndividualNode implements Node { householdsAndRoles: [IndividualRoleInHouseholdNode!]! copiedTo(offset: Int, before: String, after: String, first: Int, last: Int): IndividualNodeConnection! bankAccountInfo: BankAccountInfoNode - biometricDuplicates1(offset: Int, before: String, after: String, first: Int, last: Int): DeduplicationEngineSimilarityPairNodeConnection! - biometricDuplicates2(offset: Int, before: String, after: String, first: Int, last: Int): DeduplicationEngineSimilarityPairNodeConnection! paymentrecordSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentRecordNodeConnection! collectorPayments(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! @@ -3774,7 +3762,6 @@ type ProgramNode implements Node { householdSet(offset: Int, before: String, after: String, first: Int, last: Int): HouseholdNodeConnection! individuals(offset: Int, before: String, after: String, first: Int, last: Int): IndividualNodeConnection! registrationImports(offset: Int, before: String, after: String, first: Int, last: Int): RegistrationDataImportNodeConnection! - deduplicationEngineSimilarityPairs(offset: Int, before: String, after: String, first: Int, last: Int): DeduplicationEngineSimilarityPairNodeConnection! paymentplanSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentPlanNodeConnection! cashplanSet(offset: Int, before: String, after: String, first: Int, last: Int): CashPlanNodeConnection! paymentSet(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! @@ -4185,7 +4172,7 @@ type RegistrationDataImportNode implements Node { goldenRecordPossibleDuplicatesCountAndPercentage: [CountAndPercentageNode] goldenRecordUniqueCountAndPercentage: [CountAndPercentageNode] totalHouseholdsCountWithValidPhoneNo: Int - isDeduplicated: String + biometricDeduplicated: String canMerge: Boolean biometricDeduplicationEnabled: Boolean } @@ -5020,6 +5007,7 @@ type TicketIndividualDataUpdateDetailsNodeEdge { type TicketNeedsAdjudicationDetailsExtraDataNode { goldenRecords: [DeduplicationResultNode] possibleDuplicate: [DeduplicationResultNode] + dedupEngineSimilarityPair: DeduplicationEngineSimilarityPairNode } type TicketNeedsAdjudicationDetailsNode implements Node { @@ -5036,7 +5024,6 @@ type TicketNeedsAdjudicationDetailsNode implements Node { scoreMin: Float! scoreMax: Float! isCrossArea: Boolean! - dedupEngineSimilarityPair: DeduplicationEngineSimilarityPairNode selectedIndividual: IndividualNode possibleDuplicate: IndividualNode hasDuplicatedDocument: Boolean diff --git a/src/frontend/package.json b/src/frontend/package.json index 6420184892..f605e0a2bd 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -12,7 +12,7 @@ "test": "TZ=UTC jest --config jest.config.ts", "preview": "vite preview", "download-dev-schema": "wget --no-check-certificate -O data/schema.graphql https://dev-hct.unitst.org/api/graphql/schema.graphql", - "download-local-schema": "wget --no-check-certificate -O data/schema.graphql http://localhost:8080/api/graphql/schema.graphql", + "download-local-schema": "wget --no-check-certificate -O data/schema.graphql http://localhost:3000/api/graphql/schema.graphql", "generate-types": "yarn download-dev-schema && graphql-codegen --config codegen.yml --debug", "generate-types-local": "yarn download-local-schema && graphql-codegen --config codegen.yml --debug" }, diff --git a/src/frontend/src/__generated__/graphql.tsx b/src/frontend/src/__generated__/graphql.tsx index b952b85210..42c282feab 100644 --- a/src/frontend/src/__generated__/graphql.tsx +++ b/src/frontend/src/__generated__/graphql.tsx @@ -1377,38 +1377,18 @@ export enum DataCollectingTypeType { Standard = 'STANDARD' } -export type DeduplicationEngineSimilarityPairNode = Node & { - __typename?: 'DeduplicationEngineSimilarityPairNode'; - id: Scalars['ID']['output']; - individual1: IndividualNode; - individual2: IndividualNode; - isDuplicate?: Maybe<Scalars['Boolean']['output']>; - program: ProgramNode; - similarityScore?: Maybe<Scalars['String']['output']>; - ticketneedsadjudicationdetailsSet: TicketNeedsAdjudicationDetailsNodeConnection; -}; - - -export type DeduplicationEngineSimilarityPairNodeTicketneedsadjudicationdetailsSetArgs = { - after?: InputMaybe<Scalars['String']['input']>; - before?: InputMaybe<Scalars['String']['input']>; - first?: InputMaybe<Scalars['Int']['input']>; - last?: InputMaybe<Scalars['Int']['input']>; - offset?: InputMaybe<Scalars['Int']['input']>; -}; - -export type DeduplicationEngineSimilarityPairNodeConnection = { - __typename?: 'DeduplicationEngineSimilarityPairNodeConnection'; - edgeCount?: Maybe<Scalars['Int']['output']>; - edges: Array<Maybe<DeduplicationEngineSimilarityPairNodeEdge>>; - pageInfo: PageInfo; - totalCount?: Maybe<Scalars['Int']['output']>; +export type DeduplicationEngineSimilarityPairIndividualNode = { + __typename?: 'DeduplicationEngineSimilarityPairIndividualNode'; + fullName?: Maybe<Scalars['String']['output']>; + photo?: Maybe<Scalars['String']['output']>; + unicefId?: Maybe<Scalars['String']['output']>; }; -export type DeduplicationEngineSimilarityPairNodeEdge = { - __typename?: 'DeduplicationEngineSimilarityPairNodeEdge'; - cursor: Scalars['String']['output']; - node?: Maybe<DeduplicationEngineSimilarityPairNode>; +export type DeduplicationEngineSimilarityPairNode = { + __typename?: 'DeduplicationEngineSimilarityPairNode'; + individual1?: Maybe<DeduplicationEngineSimilarityPairIndividualNode>; + individual2?: Maybe<DeduplicationEngineSimilarityPairIndividualNode>; + similarityScore?: Maybe<Scalars['String']['output']>; }; export type DeduplicationResultNode = { @@ -3428,8 +3408,6 @@ export type IndividualNode = Node & { age?: Maybe<Scalars['Int']['output']>; ageAtRegistration?: Maybe<Scalars['Int']['output']>; bankAccountInfo?: Maybe<BankAccountInfoNode>; - biometricDuplicates1: DeduplicationEngineSimilarityPairNodeConnection; - biometricDuplicates2: DeduplicationEngineSimilarityPairNodeConnection; birthDate: Scalars['Date']['output']; blockchainName: Scalars['String']['output']; businessArea: UserBusinessAreaNode; @@ -3534,24 +3512,6 @@ export type IndividualNode = Node & { }; -export type IndividualNodeBiometricDuplicates1Args = { - after?: InputMaybe<Scalars['String']['input']>; - before?: InputMaybe<Scalars['String']['input']>; - first?: InputMaybe<Scalars['Int']['input']>; - last?: InputMaybe<Scalars['Int']['input']>; - offset?: InputMaybe<Scalars['Int']['input']>; -}; - - -export type IndividualNodeBiometricDuplicates2Args = { - after?: InputMaybe<Scalars['String']['input']>; - before?: InputMaybe<Scalars['String']['input']>; - first?: InputMaybe<Scalars['Int']['input']>; - last?: InputMaybe<Scalars['Int']['input']>; - offset?: InputMaybe<Scalars['Int']['input']>; -}; - - export type IndividualNodeCollectorPaymentsArgs = { after?: InputMaybe<Scalars['String']['input']>; before?: InputMaybe<Scalars['String']['input']>; @@ -5743,7 +5703,6 @@ export type ProgramNode = Node & { createdAt: Scalars['DateTime']['output']; cycles?: Maybe<ProgramCycleNodeConnection>; dataCollectingType?: Maybe<DataCollectingTypeNode>; - deduplicationEngineSimilarityPairs: DeduplicationEngineSimilarityPairNodeConnection; deduplicationSetId?: Maybe<Scalars['UUID']['output']>; description: Scalars['String']['output']; endDate?: Maybe<Scalars['Date']['output']>; @@ -5832,15 +5791,6 @@ export type ProgramNodeCyclesArgs = { }; -export type ProgramNodeDeduplicationEngineSimilarityPairsArgs = { - after?: InputMaybe<Scalars['String']['input']>; - before?: InputMaybe<Scalars['String']['input']>; - first?: InputMaybe<Scalars['Int']['input']>; - last?: InputMaybe<Scalars['Int']['input']>; - offset?: InputMaybe<Scalars['Int']['input']>; -}; - - export type ProgramNodeFeedbackSetArgs = { after?: InputMaybe<Scalars['String']['input']>; before?: InputMaybe<Scalars['String']['input']>; @@ -7426,6 +7376,7 @@ export type RegistrationDataImportNode = Node & { batchPossibleDuplicates: Scalars['Int']['output']; batchUnique: Scalars['Int']['output']; batchUniqueCountAndPercentage?: Maybe<Array<Maybe<CountAndPercentageNode>>>; + biometricDeduplicated?: Maybe<Scalars['String']['output']>; biometricDeduplicationEnabled?: Maybe<Scalars['Boolean']['output']>; businessArea?: Maybe<UserBusinessAreaNode>; canMerge?: Maybe<Scalars['Boolean']['output']>; @@ -7451,7 +7402,6 @@ export type RegistrationDataImportNode = Node & { importDate: Scalars['DateTime']['output']; importedBy?: Maybe<UserNode>; individuals: IndividualNodeConnection; - isDeduplicated?: Maybe<Scalars['String']['output']>; messages: CommunicationMessageNodeConnection; name: Scalars['String']['output']; numberOfHouseholds: Scalars['Int']['output']; @@ -8631,6 +8581,7 @@ export type TicketIndividualDataUpdateDetailsNodeEdge = { export type TicketNeedsAdjudicationDetailsExtraDataNode = { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode'; + dedupEngineSimilarityPair?: Maybe<DeduplicationEngineSimilarityPairNode>; goldenRecords?: Maybe<Array<Maybe<DeduplicationResultNode>>>; possibleDuplicate?: Maybe<Array<Maybe<DeduplicationResultNode>>>; }; @@ -8638,7 +8589,6 @@ export type TicketNeedsAdjudicationDetailsExtraDataNode = { export type TicketNeedsAdjudicationDetailsNode = Node & { __typename?: 'TicketNeedsAdjudicationDetailsNode'; createdAt: Scalars['DateTime']['output']; - dedupEngineSimilarityPair?: Maybe<DeduplicationEngineSimilarityPairNode>; extraData?: Maybe<TicketNeedsAdjudicationDetailsExtraDataNode>; goldenRecordsIndividual: IndividualNode; hasDuplicatedDocument?: Maybe<Scalars['Boolean']['output']>; @@ -9680,7 +9630,7 @@ export type _TableTotalCashTransferredDataNode = { totalHouseholds?: Maybe<Scalars['Int']['output']>; }; -export type GrievanceTicketDetailedFragment = { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', isDuplicate?: boolean | null, similarityScore?: string | null, individual1: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null }, individual2: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null } } | null, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null }; +export type GrievanceTicketDetailedFragment = { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', similarityScore?: string | null, individual1?: { __typename?: 'DeduplicationEngineSimilarityPairIndividualNode', unicefId?: string | null, fullName?: string | null, photo?: string | null } | null, individual2?: { __typename?: 'DeduplicationEngineSimilarityPairIndividualNode', unicefId?: string | null, fullName?: string | null, photo?: string | null } | null } | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null }; export type HouseholdMinimalFragment = { __typename?: 'HouseholdNode', id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, flexFields?: any | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, address: string, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } }; @@ -9698,9 +9648,9 @@ export type PaymentRecordDetailsFragment = { __typename?: 'PaymentRecordNode', i 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<string> } | null } | null> | 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, isDeduplicated?: 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 }; +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 }; -export type RegistrationDetailedFragment = { __typename?: 'RegistrationDataImportNode', numberOfIndividuals: number, datahubId?: any | null, errorMessage: string, canMerge?: boolean | null, biometricDeduplicationEnabled?: boolean | null, deduplicationEngineStatus?: RegistrationDataImportDeduplicationEngineStatus | null, id: string, createdAt: any, name: string, status: RegistrationDataImportStatus, erased: boolean, importDate: any, dataSource: RegistrationDataImportDataSource, numberOfHouseholds: number, refuseReason?: string | null, totalHouseholdsCountWithValidPhoneNo?: number | null, adminUrl?: string | null, isDeduplicated?: string | null, batchDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, batchUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordPossibleDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | 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 }; +export type RegistrationDetailedFragment = { __typename?: 'RegistrationDataImportNode', numberOfIndividuals: number, datahubId?: any | null, errorMessage: string, canMerge?: boolean | null, biometricDeduplicationEnabled?: boolean | null, deduplicationEngineStatus?: RegistrationDataImportDeduplicationEngineStatus | null, id: string, createdAt: any, name: string, status: RegistrationDataImportStatus, erased: boolean, importDate: any, dataSource: RegistrationDataImportDataSource, numberOfHouseholds: number, refuseReason?: string | null, totalHouseholdsCountWithValidPhoneNo?: number | null, adminUrl?: string | null, biometricDeduplicated?: string | null, batchDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, batchUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordPossibleDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | 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 }; export type ImportedHouseholdMinimalFragment = { __typename?: 'ImportedHouseholdNode', id: string, importId?: string | null, size?: number | null, flexFields?: any | null, deviceid: string, start?: any | null, detailId?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, hasDuplicates?: boolean | null, fchildHoh?: boolean | null, childHoh?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string } | null, admin1?: { __typename?: 'AreaNode', pCode?: string | null, name: string } | null, admin2?: { __typename?: 'AreaNode', pCode?: string | null, name: string } | null }; @@ -9901,7 +9851,7 @@ export type GrievanceTicketStatusChangeMutationVariables = Exact<{ }>; -export type GrievanceTicketStatusChangeMutation = { __typename?: 'Mutations', grievanceStatusChange?: { __typename?: 'GrievanceStatusChangeMutation', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', isDuplicate?: boolean | null, similarityScore?: string | null, individual1: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null }, individual2: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null } } | null, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null } | null }; +export type GrievanceTicketStatusChangeMutation = { __typename?: 'Mutations', grievanceStatusChange?: { __typename?: 'GrievanceStatusChangeMutation', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', similarityScore?: string | null, individual1?: { __typename?: 'DeduplicationEngineSimilarityPairIndividualNode', unicefId?: string | null, fullName?: string | null, photo?: string | null } | null, individual2?: { __typename?: 'DeduplicationEngineSimilarityPairIndividualNode', unicefId?: string | null, fullName?: string | null, photo?: string | null } | null } | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null } | null }; export type ReassignRoleGrievanceMutationVariables = Exact<{ grievanceTicketId: Scalars['ID']['input']; @@ -10212,7 +10162,7 @@ export type MergeRdiMutationVariables = Exact<{ }>; -export type MergeRdiMutation = { __typename?: 'Mutations', mergeRegistrationDataImport?: { __typename?: 'MergeRegistrationDataImportMutation', registrationDataImport?: { __typename?: 'RegistrationDataImportNode', numberOfIndividuals: number, datahubId?: any | null, errorMessage: string, canMerge?: boolean | null, biometricDeduplicationEnabled?: boolean | null, deduplicationEngineStatus?: RegistrationDataImportDeduplicationEngineStatus | null, id: string, createdAt: any, name: string, status: RegistrationDataImportStatus, erased: boolean, importDate: any, dataSource: RegistrationDataImportDataSource, numberOfHouseholds: number, refuseReason?: string | null, totalHouseholdsCountWithValidPhoneNo?: number | null, adminUrl?: string | null, isDeduplicated?: string | null, batchDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, batchUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordPossibleDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | 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 } | null } | null }; +export type MergeRdiMutation = { __typename?: 'Mutations', mergeRegistrationDataImport?: { __typename?: 'MergeRegistrationDataImportMutation', registrationDataImport?: { __typename?: 'RegistrationDataImportNode', numberOfIndividuals: number, datahubId?: any | null, errorMessage: string, canMerge?: boolean | null, biometricDeduplicationEnabled?: boolean | null, deduplicationEngineStatus?: RegistrationDataImportDeduplicationEngineStatus | null, id: string, createdAt: any, name: string, status: RegistrationDataImportStatus, erased: boolean, importDate: any, dataSource: RegistrationDataImportDataSource, numberOfHouseholds: number, refuseReason?: string | null, totalHouseholdsCountWithValidPhoneNo?: number | null, adminUrl?: string | null, biometricDeduplicated?: string | null, batchDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, batchUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordPossibleDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | 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 } | null } | null }; export type RefuseRdiMutationVariables = Exact<{ id: Scalars['ID']['input']; @@ -10675,7 +10625,7 @@ export type GrievanceTicketQueryVariables = Exact<{ }>; -export type GrievanceTicketQuery = { __typename?: 'Query', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', isDuplicate?: boolean | null, similarityScore?: string | null, individual1: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null }, individual2: { __typename?: 'IndividualNode', unicefId?: string | null, fullName: string, photo?: string | null } } | null, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null }; +export type GrievanceTicketQuery = { __typename?: 'Query', grievanceTicket?: { __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, category: number, consent: boolean, createdAt: any, updatedAt: any, description: string, language: string, admin?: string | null, area: string, adminUrl?: string | null, issueType?: number | null, priority?: number | null, urgency?: number | null, comments?: string | null, partner?: { __typename?: 'PartnerType', id: string, name: string } | null, businessArea: { __typename?: 'UserBusinessAreaNode', postponeDeduplication: boolean }, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, pCode?: string | null } | null, assignedTo?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, paymentRecord?: { __typename?: 'PaymentRecordAndPaymentNode', id?: string | null, caId?: string | null, deliveredQuantity?: number | null, entitlementQuantity?: number | null, objType?: string | null, parent?: { __typename?: 'CashPlanAndPaymentPlanNode', id?: string | null, unicefId?: string | null, objType?: string | null } | null, verification?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null, relatedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, linkedTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, unicefId?: string | null, category: number, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, existingTickets?: Array<{ __typename?: 'GrievanceTicketNode', id: string, category: number, unicefId?: string | null, status: number, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null> | null, addIndividualTicketDetails?: { __typename?: 'TicketAddIndividualDetailsNode', id: string, individualData?: any | null, approveStatus: boolean, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, individualDataUpdateTicketDetails?: { __typename?: 'TicketIndividualDataUpdateDetailsNode', id: string, individualData?: any | null, roleReassignData: any, individual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null } | null, householdDataUpdateTicketDetails?: { __typename?: 'TicketHouseholdDataUpdateDetailsNode', id: string, householdData?: any | null, household?: { __typename?: 'HouseholdNode', activeIndividualsCount?: number | null, countryOrigin?: string | null, country?: string | null, zipCode?: string | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, start?: any | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, address: string, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, id: string, status?: string | null, adminUrl?: string | null, createdAt: any, residenceStatus?: string | null, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, totalCashReceivedUsd?: any | null, currency?: string | null, firstRegistrationDate: any, lastRegistrationDate: any, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unicefId?: string | null, unhcrId: string, geopoint?: any | null, village: string, adminAreaTitle?: string | null, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null } | null, deleteIndividualTicketDetails?: { __typename?: 'TicketDeleteIndividualDetailsNode', id: string, roleReassignData: any, approveStatus: boolean } | null, deleteHouseholdTicketDetails?: { __typename?: 'TicketDeleteHouseholdDetailsNode', id: string, approveStatus: boolean, reasonHousehold?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } | null } | null, systemFlaggingTicketDetails?: { __typename?: 'TicketSystemFlaggingDetailsNode', id: string, approveStatus: boolean, roleReassignData: any, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, duplicate: boolean, fullName: string, birthDate: any, lastRegistrationDate: any, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, documentNumber: string, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> } }, sanctionListIndividual: { __typename?: 'SanctionListIndividualNode', id: string, fullName: string, referenceNumber: string, datesOfBirth: { __typename?: 'SanctionListIndividualDateOfBirthNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDateOfBirthNodeEdge', node?: { __typename?: 'SanctionListIndividualDateOfBirthNode', id: string, date: any } | null } | null> }, documents: { __typename?: 'SanctionListIndividualDocumentNodeConnection', edges: Array<{ __typename?: 'SanctionListIndividualDocumentNodeEdge', node?: { __typename?: 'SanctionListIndividualDocumentNode', id: string, documentNumber: string, typeOfDocument: string } | null } | null> } } } | null, paymentVerificationTicketDetails?: { __typename?: 'TicketPaymentVerificationDetailsNode', id: string, newStatus?: TicketPaymentVerificationDetailsNewStatus | null, oldReceivedAmount?: number | null, newReceivedAmount?: number | null, approveStatus: boolean, paymentVerificationStatus: TicketPaymentVerificationDetailsPaymentVerificationStatus, hasMultiplePaymentVerifications?: boolean | null, paymentVerification?: { __typename?: 'PaymentVerificationNode', id: string, receivedAmount?: number | null } | null, paymentVerifications: { __typename?: 'PaymentVerificationNodeConnection', edges: Array<{ __typename?: 'PaymentVerificationNodeEdge', node?: { __typename?: 'PaymentVerificationNode', id: string } | null } | null> } } | null, needsAdjudicationTicketDetails?: { __typename?: 'TicketNeedsAdjudicationDetailsNode', id: string, hasDuplicatedDocument?: boolean | null, isMultipleDuplicatesVersion: boolean, roleReassignData: any, extraData?: { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode', goldenRecords?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, possibleDuplicate?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null, dedupEngineSimilarityPair?: { __typename?: 'DeduplicationEngineSimilarityPairNode', similarityScore?: string | null, individual1?: { __typename?: 'DeduplicationEngineSimilarityPairIndividualNode', unicefId?: string | null, fullName?: string | null, photo?: string | null } | null, individual2?: { __typename?: 'DeduplicationEngineSimilarityPairIndividualNode', unicefId?: string | null, fullName?: string | null, photo?: string | null } | null } | null } | null, goldenRecordsIndividual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string, birthDate: any, lastRegistrationDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null }, possibleDuplicate?: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null, possibleDuplicates?: Array<{ __typename?: 'IndividualNode', id: string, unicefId?: string | null, lastRegistrationDate: any, fullName: string, birthDate: any, sex: IndividualSex, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, household?: { __typename?: 'HouseholdNode', unicefId?: string | null, id: string, village: string, admin2?: { __typename?: 'AreaNode', id: string, name: string } | null } | null, deduplicationGoldenRecordResults?: Array<{ __typename?: 'DeduplicationResultNode', hitId?: string | null, proximityToScore?: number | null, score?: number | null } | null> | null } | null> | null, selectedIndividual?: { __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null, selectedDuplicates?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null, selectedDistinct?: Array<{ __typename?: 'IndividualNode', givenName: string, familyName: string, estimatedBirthDate?: boolean | null, pregnant?: boolean | null, lastSyncAt?: any | null, deduplicationBatchStatus: IndividualDeduplicationBatchStatus, disability: IndividualDisability, importedIndividualId?: any | null, commsDisability: string, firstRegistrationDate: any, whoAnswersAltPhone: string, memoryDisability: string, middleName: string, whoAnswersPhone: string, phoneNoAlternative: string, phoneNoAlternativeValid?: boolean | null, email: string, hearingDisability: string, observedDisability?: Array<string | null> | null, individualId: string, seeingDisability: string, physicalDisability: string, selfcareDisability: string, photo?: string | null, workStatus: string, enrolledInNutritionProgramme?: boolean | null, administrationOfRutf?: boolean | null, flexFields?: any | null, preferredLanguage?: string | null, paymentDeliveryPhoneNo?: string | null, walletName: string, walletAddress: string, blockchainName: string, id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, household?: { __typename?: 'HouseholdNode', status?: string | null, id: string, residenceStatus?: string | null, address: string, village: string, zipCode?: string | null, geopoint?: any | null, country?: string | null, countryOrigin?: string | null, unicefId?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, activeIndividualsCount?: number | null, femaleAgeGroup05Count?: number | null, femaleAgeGroup611Count?: number | null, femaleAgeGroup1217Count?: number | null, femaleAgeGroup1859Count?: number | null, femaleAgeGroup60Count?: number | null, pregnantCount?: number | null, maleAgeGroup05Count?: number | null, maleAgeGroup611Count?: number | null, maleAgeGroup1217Count?: number | null, maleAgeGroup1859Count?: number | null, maleAgeGroup60Count?: number | null, femaleAgeGroup05DisabledCount?: number | null, femaleAgeGroup611DisabledCount?: number | null, femaleAgeGroup1217DisabledCount?: number | null, femaleAgeGroup1859DisabledCount?: number | null, femaleAgeGroup60DisabledCount?: number | null, maleAgeGroup05DisabledCount?: number | null, maleAgeGroup611DisabledCount?: number | null, maleAgeGroup1217DisabledCount?: number | null, maleAgeGroup1859DisabledCount?: number | null, maleAgeGroup60DisabledCount?: number | null, fchildHoh?: boolean | null, childHoh?: boolean | null, deviceid: string, orgNameEnumerator: string, returnee?: boolean | null, nameEnumerator: string, lastSyncAt?: any | null, consentSharing?: Array<string | null> | null, orgEnumerator: HouseholdOrgEnumerator, updatedAt: any, consent?: boolean | null, collectIndividualData: HouseholdCollectIndividualData, flexFields?: any | null, programRegistrationId?: string | null, adminUrl?: string | null, createdAt: any, maleChildrenCount?: number | null, femaleChildrenCount?: number | null, childrenDisabledCount?: number | null, size?: number | null, totalCashReceived?: any | null, currency?: string | null, sanctionListPossibleMatch?: boolean | null, sanctionListConfirmedMatch?: boolean | null, hasDuplicates?: boolean | null, unhcrId: string, adminAreaTitle?: string | null, registrationDataImport?: { __typename?: 'RegistrationDataImportNode', name: string, dataSource: RegistrationDataImportDataSource, importDate: any, importedBy?: { __typename?: 'UserNode', firstName: string, lastName: string, email: string, username: string } | null } | null, deliveredQuantities?: Array<{ __typename?: 'DeliveredQuantityNode', totalDeliveredQuantity?: any | null, currency?: string | null } | null> | null, adminArea?: { __typename?: 'AreaNode', id: string, name: string, level: number } | null, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> }, individuals?: { __typename?: 'IndividualNodeConnection', totalCount?: number | null, edges: Array<{ __typename?: 'IndividualNodeEdge', node?: { __typename?: 'IndividualNode', id: string, age?: number | null, lastRegistrationDate: any, adminUrl?: string | null, createdAt: any, updatedAt: any, fullName: string, sex: IndividualSex, unicefId?: string | null, birthDate: any, maritalStatus: IndividualMaritalStatus, phoneNo: string, phoneNoValid?: boolean | null, email: string, sanctionListPossibleMatch: boolean, sanctionListConfirmedMatch: boolean, deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus, sanctionListLastCheck?: any | null, role?: string | null, relationship?: IndividualRelationship | null, status?: string | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, countryIso3?: string | null, documentNumber: string, photo?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> }, household?: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null, status?: string | null, totalCashReceivedUsd?: any | null, lastRegistrationDate: any, start?: any | null, firstRegistrationDate: any, admin1?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin2?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin3?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, admin4?: { __typename?: 'AreaNode', id: string, name: string, level: number, pCode?: string | null } | null, programs: { __typename?: 'ProgramNodeConnection', edges: Array<{ __typename?: 'ProgramNodeEdge', node?: { __typename?: 'ProgramNode', id: string, name: string } | null } | null> } } | null } | null } | null> } | null, paymentrecordSet: { __typename?: 'PaymentRecordNodeConnection', edges: Array<{ __typename?: 'PaymentRecordNodeEdge', node?: { __typename?: 'PaymentRecordNode', id: string, fullName: string, parent?: { __typename?: 'CashPlanNode', id: string, totalPersonsCovered: number, totalDeliveredQuantity?: number | null, assistanceMeasurement: string, program: { __typename?: 'ProgramNode', id: string, name: string } } | null } | null } | null> }, headOfHousehold?: { __typename?: 'IndividualNode', id: string, fullName: string, givenName: string, familyName: string } | null } | null, householdsAndRoles: Array<{ __typename?: 'IndividualRoleInHouseholdNode', id: any, role?: IndividualRoleInHouseholdRole | null, individual: { __typename?: 'IndividualNode', id: string, unicefId?: string | null, fullName: string }, household: { __typename?: 'HouseholdNode', id: string, unicefId?: string | null } }>, paymentChannels?: Array<{ __typename?: 'BankAccountInfoNode', id: string, bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null> | null, documents: { __typename?: 'DocumentNodeConnection', edges: Array<{ __typename?: 'DocumentNodeEdge', node?: { __typename?: 'DocumentNode', id: string, country?: string | null, photo?: string | null, documentNumber: string, countryIso3?: string | null, type: { __typename?: 'ImportedDocumentTypeNode', label: string, key: string } } | null } | null> }, headingHousehold?: { __typename?: 'HouseholdNode', id: string, headOfHousehold?: { __typename?: 'IndividualNode', id: string, givenName: string, familyName: string, fullName: string } | null } | null, bankAccountInfo?: { __typename?: 'BankAccountInfoNode', bankName: string, bankAccountNumber: string, accountHolderName: string, bankBranchName: string } | null, identities: { __typename?: 'IndividualIdentityNodeConnection', edges: Array<{ __typename?: 'IndividualIdentityNodeEdge', node?: { __typename?: 'IndividualIdentityNode', id: string, partner?: string | null, country?: string | null, number: string } | null } | null> } } | null> | null } | null, ticketNotes: { __typename?: 'TicketNoteNodeConnection', edges: Array<{ __typename?: 'TicketNoteNodeEdge', node?: { __typename?: 'TicketNoteNode', id: string, createdAt: any, updatedAt: any, description: string, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null } | null> }, programs?: Array<{ __typename?: 'ProgramNode', name: string, id: string } | null> | null, documentation?: Array<{ __typename?: 'GrievanceDocumentNode', id: string, createdAt: any, updatedAt: any, name?: string | null, fileSize?: number | null, contentType: string, filePath?: string | null, fileName?: string | null, createdBy?: { __typename?: 'UserNode', id: string, firstName: string, lastName: string, email: string } | null } | null> | null } | null }; export type GrievanceTicketFlexFieldsQueryVariables = Exact<{ id: Scalars['ID']['input']; @@ -11291,7 +11241,7 @@ export type AllRegistrationDataImportsQueryVariables = Exact<{ }>; -export type AllRegistrationDataImportsQuery = { __typename?: 'Query', allRegistrationDataImports?: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null, endCursor?: string | null }, edges: Array<{ __typename?: 'RegistrationDataImportNodeEdge', cursor: string, node?: { __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, isDeduplicated?: 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 } | null } | null> } | null }; +export type AllRegistrationDataImportsQuery = { __typename?: 'Query', allRegistrationDataImports?: { __typename?: 'RegistrationDataImportNodeConnection', totalCount?: number | null, pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null, endCursor?: string | null }, edges: Array<{ __typename?: 'RegistrationDataImportNodeEdge', cursor: string, node?: { __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 } | null } | null> } | null }; export type ImportedHouseholdQueryVariables = Exact<{ id: Scalars['ID']['input']; @@ -11331,7 +11281,7 @@ export type RegistrationDataImportQueryVariables = Exact<{ }>; -export type RegistrationDataImportQuery = { __typename?: 'Query', registrationDataImport?: { __typename?: 'RegistrationDataImportNode', numberOfIndividuals: number, datahubId?: any | null, errorMessage: string, canMerge?: boolean | null, biometricDeduplicationEnabled?: boolean | null, deduplicationEngineStatus?: RegistrationDataImportDeduplicationEngineStatus | null, id: string, createdAt: any, name: string, status: RegistrationDataImportStatus, erased: boolean, importDate: any, dataSource: RegistrationDataImportDataSource, numberOfHouseholds: number, refuseReason?: string | null, totalHouseholdsCountWithValidPhoneNo?: number | null, adminUrl?: string | null, isDeduplicated?: string | null, batchDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, batchUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordPossibleDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | 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 } | null }; +export type RegistrationDataImportQuery = { __typename?: 'Query', registrationDataImport?: { __typename?: 'RegistrationDataImportNode', numberOfIndividuals: number, datahubId?: any | null, errorMessage: string, canMerge?: boolean | null, biometricDeduplicationEnabled?: boolean | null, deduplicationEngineStatus?: RegistrationDataImportDeduplicationEngineStatus | null, id: string, createdAt: any, name: string, status: RegistrationDataImportStatus, erased: boolean, importDate: any, dataSource: RegistrationDataImportDataSource, numberOfHouseholds: number, refuseReason?: string | null, totalHouseholdsCountWithValidPhoneNo?: number | null, adminUrl?: string | null, biometricDeduplicated?: string | null, batchDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, batchUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordUniqueCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | null, goldenRecordPossibleDuplicatesCountAndPercentage?: Array<{ __typename?: 'CountAndPercentageNode', count?: number | null, percentage?: number | null } | null> | 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 } | null }; export type XlsxImportDataQueryVariables = Exact<{ id: Scalars['ID']['input']; @@ -12146,20 +12096,6 @@ export const GrievanceTicketDetailedFragmentDoc = gql` } needsAdjudicationTicketDetails { id - dedupEngineSimilarityPair { - isDuplicate - similarityScore - individual1 { - unicefId - fullName - photo - } - individual2 { - unicefId - fullName - photo - } - } hasDuplicatedDocument extraData { goldenRecords { @@ -12172,6 +12108,19 @@ export const GrievanceTicketDetailedFragmentDoc = gql` proximityToScore score } + dedupEngineSimilarityPair { + similarityScore + individual1 { + unicefId + fullName + photo + } + individual2 { + unicefId + fullName + photo + } + } } goldenRecordsIndividual { id @@ -12595,7 +12544,7 @@ export const RegistrationMinimalFragmentDoc = gql` refuseReason totalHouseholdsCountWithValidPhoneNo adminUrl - isDeduplicated + biometricDeduplicated } `; export const RegistrationDetailedFragmentDoc = gql` @@ -24605,7 +24554,7 @@ export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs /** Mapping of interface types */ export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = { - Node: ( ApprovalProcessNode ) | ( AreaNode ) | ( AreaTypeNode ) | ( BankAccountInfoNode ) | ( BusinessAreaNode ) | ( CashPlanNode ) | ( CommunicationMessageNode ) | ( CommunicationMessageRecipientMapNode ) | ( DataCollectingTypeNode ) | ( DeduplicationEngineSimilarityPairNode ) | ( DeliveryMechanismNode ) | ( DeliveryMechanismPerPaymentPlanNode ) | ( DocumentNode ) | ( FeedbackMessageNode ) | ( FeedbackNode ) | ( FinancialServiceProviderNode ) | ( FinancialServiceProviderXlsxTemplateNode ) | ( GrievanceDocumentNode ) | ( GrievanceTicketNode ) | ( HouseholdNode ) | ( ImportDataNode ) | ( ImportedDocumentNode ) | ( ImportedHouseholdNode ) | ( ImportedIndividualIdentityNode ) | ( ImportedIndividualNode ) | ( IndividualIdentityNode ) | ( IndividualNode ) | ( KoboImportDataNode ) | ( LogEntryNode ) | ( PaymentHouseholdSnapshotNode ) | ( PaymentNode ) | ( PaymentPlanNode ) | ( PaymentRecordNode ) | ( PaymentVerificationLogEntryNode ) | ( PaymentVerificationNode ) | ( PaymentVerificationPlanNode ) | ( PaymentVerificationSummaryNode ) | ( PeriodicFieldNode ) | ( ProgramCycleNode ) | ( ProgramNode ) | ( RecipientNode ) | ( RegistrationDataImportDatahubNode ) | ( RegistrationDataImportNode ) | ( ReportNode ) | ( RuleCommitNode ) | ( SanctionListIndividualAliasNameNode ) | ( SanctionListIndividualCountriesNode ) | ( SanctionListIndividualDateOfBirthNode ) | ( SanctionListIndividualDocumentNode ) | ( SanctionListIndividualNationalitiesNode ) | ( SanctionListIndividualNode ) | ( ServiceProviderNode ) | ( 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 ) | ( BusinessAreaNode ) | ( CashPlanNode ) | ( CommunicationMessageNode ) | ( CommunicationMessageRecipientMapNode ) | ( DataCollectingTypeNode ) | ( DeliveryMechanismNode ) | ( DeliveryMechanismPerPaymentPlanNode ) | ( DocumentNode ) | ( FeedbackMessageNode ) | ( FeedbackNode ) | ( FinancialServiceProviderNode ) | ( FinancialServiceProviderXlsxTemplateNode ) | ( GrievanceDocumentNode ) | ( GrievanceTicketNode ) | ( HouseholdNode ) | ( ImportDataNode ) | ( ImportedDocumentNode ) | ( ImportedHouseholdNode ) | ( ImportedIndividualIdentityNode ) | ( ImportedIndividualNode ) | ( IndividualIdentityNode ) | ( IndividualNode ) | ( KoboImportDataNode ) | ( LogEntryNode ) | ( PaymentHouseholdSnapshotNode ) | ( PaymentNode ) | ( PaymentPlanNode ) | ( PaymentRecordNode ) | ( PaymentVerificationLogEntryNode ) | ( PaymentVerificationNode ) | ( PaymentVerificationPlanNode ) | ( PaymentVerificationSummaryNode ) | ( PeriodicFieldNode ) | ( ProgramCycleNode ) | ( ProgramNode ) | ( RecipientNode ) | ( RegistrationDataImportDatahubNode ) | ( RegistrationDataImportNode ) | ( ReportNode ) | ( RuleCommitNode ) | ( SanctionListIndividualAliasNameNode ) | ( SanctionListIndividualCountriesNode ) | ( SanctionListIndividualDateOfBirthNode ) | ( SanctionListIndividualDocumentNode ) | ( SanctionListIndividualNationalitiesNode ) | ( SanctionListIndividualNode ) | ( ServiceProviderNode ) | ( 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 */ @@ -24716,9 +24665,8 @@ export type ResolversTypes = { Date: ResolverTypeWrapper<Scalars['Date']['output']>; DateTime: ResolverTypeWrapper<Scalars['DateTime']['output']>; Decimal: ResolverTypeWrapper<Scalars['Decimal']['output']>; + DeduplicationEngineSimilarityPairIndividualNode: ResolverTypeWrapper<DeduplicationEngineSimilarityPairIndividualNode>; DeduplicationEngineSimilarityPairNode: ResolverTypeWrapper<DeduplicationEngineSimilarityPairNode>; - DeduplicationEngineSimilarityPairNodeConnection: ResolverTypeWrapper<DeduplicationEngineSimilarityPairNodeConnection>; - DeduplicationEngineSimilarityPairNodeEdge: ResolverTypeWrapper<DeduplicationEngineSimilarityPairNodeEdge>; DeduplicationResultNode: ResolverTypeWrapper<DeduplicationResultNode>; DeleteHouseholdApproveMutation: ResolverTypeWrapper<DeleteHouseholdApproveMutation>; DeletePaymentPlanMutation: ResolverTypeWrapper<DeletePaymentPlanMutation>; @@ -25250,9 +25198,8 @@ export type ResolversParentTypes = { Date: Scalars['Date']['output']; DateTime: Scalars['DateTime']['output']; Decimal: Scalars['Decimal']['output']; + DeduplicationEngineSimilarityPairIndividualNode: DeduplicationEngineSimilarityPairIndividualNode; DeduplicationEngineSimilarityPairNode: DeduplicationEngineSimilarityPairNode; - DeduplicationEngineSimilarityPairNodeConnection: DeduplicationEngineSimilarityPairNodeConnection; - DeduplicationEngineSimilarityPairNodeEdge: DeduplicationEngineSimilarityPairNodeEdge; DeduplicationResultNode: DeduplicationResultNode; DeleteHouseholdApproveMutation: DeleteHouseholdApproveMutation; DeletePaymentPlanMutation: DeletePaymentPlanMutation; @@ -26268,28 +26215,17 @@ export interface DecimalScalarConfig extends GraphQLScalarTypeConfig<ResolversTy name: 'Decimal'; } -export type DeduplicationEngineSimilarityPairNodeResolvers<ContextType = any, ParentType extends ResolversParentTypes['DeduplicationEngineSimilarityPairNode'] = ResolversParentTypes['DeduplicationEngineSimilarityPairNode']> = { - id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>; - individual1?: Resolver<ResolversTypes['IndividualNode'], ParentType, ContextType>; - individual2?: Resolver<ResolversTypes['IndividualNode'], ParentType, ContextType>; - isDuplicate?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>; - program?: Resolver<ResolversTypes['ProgramNode'], ParentType, ContextType>; - similarityScore?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>; - ticketneedsadjudicationdetailsSet?: Resolver<ResolversTypes['TicketNeedsAdjudicationDetailsNodeConnection'], ParentType, ContextType, Partial<DeduplicationEngineSimilarityPairNodeTicketneedsadjudicationdetailsSetArgs>>; - __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; -}; - -export type DeduplicationEngineSimilarityPairNodeConnectionResolvers<ContextType = any, ParentType extends ResolversParentTypes['DeduplicationEngineSimilarityPairNodeConnection'] = ResolversParentTypes['DeduplicationEngineSimilarityPairNodeConnection']> = { - edgeCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>; - edges?: Resolver<Array<Maybe<ResolversTypes['DeduplicationEngineSimilarityPairNodeEdge']>>, ParentType, ContextType>; - pageInfo?: Resolver<ResolversTypes['PageInfo'], ParentType, ContextType>; - totalCount?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>; +export type DeduplicationEngineSimilarityPairIndividualNodeResolvers<ContextType = any, ParentType extends ResolversParentTypes['DeduplicationEngineSimilarityPairIndividualNode'] = ResolversParentTypes['DeduplicationEngineSimilarityPairIndividualNode']> = { + fullName?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>; + photo?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>; + unicefId?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; }; -export type DeduplicationEngineSimilarityPairNodeEdgeResolvers<ContextType = any, ParentType extends ResolversParentTypes['DeduplicationEngineSimilarityPairNodeEdge'] = ResolversParentTypes['DeduplicationEngineSimilarityPairNodeEdge']> = { - cursor?: Resolver<ResolversTypes['String'], ParentType, ContextType>; - node?: Resolver<Maybe<ResolversTypes['DeduplicationEngineSimilarityPairNode']>, ParentType, ContextType>; +export type DeduplicationEngineSimilarityPairNodeResolvers<ContextType = any, ParentType extends ResolversParentTypes['DeduplicationEngineSimilarityPairNode'] = ResolversParentTypes['DeduplicationEngineSimilarityPairNode']> = { + individual1?: Resolver<Maybe<ResolversTypes['DeduplicationEngineSimilarityPairIndividualNode']>, ParentType, ContextType>; + individual2?: Resolver<Maybe<ResolversTypes['DeduplicationEngineSimilarityPairIndividualNode']>, ParentType, ContextType>; + similarityScore?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; }; @@ -27403,8 +27339,6 @@ export type IndividualNodeResolvers<ContextType = any, ParentType extends Resolv age?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>; ageAtRegistration?: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>; bankAccountInfo?: Resolver<Maybe<ResolversTypes['BankAccountInfoNode']>, ParentType, ContextType>; - biometricDuplicates1?: Resolver<ResolversTypes['DeduplicationEngineSimilarityPairNodeConnection'], ParentType, ContextType, Partial<IndividualNodeBiometricDuplicates1Args>>; - biometricDuplicates2?: Resolver<ResolversTypes['DeduplicationEngineSimilarityPairNodeConnection'], ParentType, ContextType, Partial<IndividualNodeBiometricDuplicates2Args>>; birthDate?: Resolver<ResolversTypes['Date'], ParentType, ContextType>; blockchainName?: Resolver<ResolversTypes['String'], ParentType, ContextType>; businessArea?: Resolver<ResolversTypes['UserBusinessAreaNode'], ParentType, ContextType>; @@ -27774,7 +27708,7 @@ export type NeedsAdjudicationApproveMutationResolvers<ContextType = any, ParentT }; export type NodeResolvers<ContextType = any, ParentType extends ResolversParentTypes['Node'] = ResolversParentTypes['Node']> = { - __resolveType: TypeResolveFn<'ApprovalProcessNode' | 'AreaNode' | 'AreaTypeNode' | 'BankAccountInfoNode' | 'BusinessAreaNode' | 'CashPlanNode' | 'CommunicationMessageNode' | 'CommunicationMessageRecipientMapNode' | 'DataCollectingTypeNode' | 'DeduplicationEngineSimilarityPairNode' | 'DeliveryMechanismNode' | 'DeliveryMechanismPerPaymentPlanNode' | 'DocumentNode' | 'FeedbackMessageNode' | 'FeedbackNode' | 'FinancialServiceProviderNode' | 'FinancialServiceProviderXlsxTemplateNode' | 'GrievanceDocumentNode' | 'GrievanceTicketNode' | 'HouseholdNode' | 'ImportDataNode' | 'ImportedDocumentNode' | 'ImportedHouseholdNode' | 'ImportedIndividualIdentityNode' | 'ImportedIndividualNode' | 'IndividualIdentityNode' | 'IndividualNode' | 'KoboImportDataNode' | 'LogEntryNode' | 'PaymentHouseholdSnapshotNode' | 'PaymentNode' | 'PaymentPlanNode' | 'PaymentRecordNode' | 'PaymentVerificationLogEntryNode' | 'PaymentVerificationNode' | 'PaymentVerificationPlanNode' | 'PaymentVerificationSummaryNode' | 'PeriodicFieldNode' | 'ProgramCycleNode' | 'ProgramNode' | 'RecipientNode' | 'RegistrationDataImportDatahubNode' | 'RegistrationDataImportNode' | 'ReportNode' | 'RuleCommitNode' | 'SanctionListIndividualAliasNameNode' | 'SanctionListIndividualCountriesNode' | 'SanctionListIndividualDateOfBirthNode' | 'SanctionListIndividualDocumentNode' | 'SanctionListIndividualNationalitiesNode' | 'SanctionListIndividualNode' | 'ServiceProviderNode' | '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' | 'BusinessAreaNode' | 'CashPlanNode' | 'CommunicationMessageNode' | 'CommunicationMessageRecipientMapNode' | 'DataCollectingTypeNode' | 'DeliveryMechanismNode' | 'DeliveryMechanismPerPaymentPlanNode' | 'DocumentNode' | 'FeedbackMessageNode' | 'FeedbackNode' | 'FinancialServiceProviderNode' | 'FinancialServiceProviderXlsxTemplateNode' | 'GrievanceDocumentNode' | 'GrievanceTicketNode' | 'HouseholdNode' | 'ImportDataNode' | 'ImportedDocumentNode' | 'ImportedHouseholdNode' | 'ImportedIndividualIdentityNode' | 'ImportedIndividualNode' | 'IndividualIdentityNode' | 'IndividualNode' | 'KoboImportDataNode' | 'LogEntryNode' | 'PaymentHouseholdSnapshotNode' | 'PaymentNode' | 'PaymentPlanNode' | 'PaymentRecordNode' | 'PaymentVerificationLogEntryNode' | 'PaymentVerificationNode' | 'PaymentVerificationPlanNode' | 'PaymentVerificationSummaryNode' | 'PeriodicFieldNode' | 'ProgramCycleNode' | 'ProgramNode' | 'RecipientNode' | 'RegistrationDataImportDatahubNode' | 'RegistrationDataImportNode' | 'ReportNode' | 'RuleCommitNode' | 'SanctionListIndividualAliasNameNode' | 'SanctionListIndividualCountriesNode' | 'SanctionListIndividualDateOfBirthNode' | 'SanctionListIndividualDocumentNode' | 'SanctionListIndividualNationalitiesNode' | 'SanctionListIndividualNode' | 'ServiceProviderNode' | 'SteficonRuleNode' | 'SurveyNode' | 'TargetPopulationNode' | 'TicketAddIndividualDetailsNode' | 'TicketComplaintDetailsNode' | 'TicketDeleteHouseholdDetailsNode' | 'TicketDeleteIndividualDetailsNode' | 'TicketHouseholdDataUpdateDetailsNode' | 'TicketIndividualDataUpdateDetailsNode' | 'TicketNeedsAdjudicationDetailsNode' | 'TicketNegativeFeedbackDetailsNode' | 'TicketNoteNode' | 'TicketPaymentVerificationDetailsNode' | 'TicketPositiveFeedbackDetailsNode' | 'TicketReferralDetailsNode' | 'TicketSensitiveDetailsNode' | 'TicketSystemFlaggingDetailsNode' | 'UserBusinessAreaNode' | 'UserNode' | 'VolumeByDeliveryMechanismNode', ParentType, ContextType>; id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>; }; @@ -28335,7 +28269,6 @@ export type ProgramNodeResolvers<ContextType = any, ParentType extends Resolvers createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>; cycles?: Resolver<Maybe<ResolversTypes['ProgramCycleNodeConnection']>, ParentType, ContextType, Partial<ProgramNodeCyclesArgs>>; dataCollectingType?: Resolver<Maybe<ResolversTypes['DataCollectingTypeNode']>, ParentType, ContextType>; - deduplicationEngineSimilarityPairs?: Resolver<ResolversTypes['DeduplicationEngineSimilarityPairNodeConnection'], ParentType, ContextType, Partial<ProgramNodeDeduplicationEngineSimilarityPairsArgs>>; deduplicationSetId?: Resolver<Maybe<ResolversTypes['UUID']>, ParentType, ContextType>; description?: Resolver<ResolversTypes['String'], ParentType, ContextType>; endDate?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>; @@ -28670,6 +28603,7 @@ export type RegistrationDataImportNodeResolvers<ContextType = any, ParentType ex batchPossibleDuplicates?: Resolver<ResolversTypes['Int'], ParentType, ContextType>; batchUnique?: Resolver<ResolversTypes['Int'], ParentType, ContextType>; batchUniqueCountAndPercentage?: Resolver<Maybe<Array<Maybe<ResolversTypes['CountAndPercentageNode']>>>, ParentType, ContextType>; + biometricDeduplicated?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>; biometricDeduplicationEnabled?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>; businessArea?: Resolver<Maybe<ResolversTypes['UserBusinessAreaNode']>, ParentType, ContextType>; canMerge?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>; @@ -28695,7 +28629,6 @@ export type RegistrationDataImportNodeResolvers<ContextType = any, ParentType ex importDate?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>; importedBy?: Resolver<Maybe<ResolversTypes['UserNode']>, ParentType, ContextType>; individuals?: Resolver<ResolversTypes['IndividualNodeConnection'], ParentType, ContextType, Partial<RegistrationDataImportNodeIndividualsArgs>>; - isDeduplicated?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>; messages?: Resolver<ResolversTypes['CommunicationMessageNodeConnection'], ParentType, ContextType, Partial<RegistrationDataImportNodeMessagesArgs>>; name?: Resolver<ResolversTypes['String'], ParentType, ContextType>; numberOfHouseholds?: Resolver<ResolversTypes['Int'], ParentType, ContextType>; @@ -29438,6 +29371,7 @@ export type TicketIndividualDataUpdateDetailsNodeEdgeResolvers<ContextType = any }; export type TicketNeedsAdjudicationDetailsExtraDataNodeResolvers<ContextType = any, ParentType extends ResolversParentTypes['TicketNeedsAdjudicationDetailsExtraDataNode'] = ResolversParentTypes['TicketNeedsAdjudicationDetailsExtraDataNode']> = { + dedupEngineSimilarityPair?: Resolver<Maybe<ResolversTypes['DeduplicationEngineSimilarityPairNode']>, ParentType, ContextType>; goldenRecords?: Resolver<Maybe<Array<Maybe<ResolversTypes['DeduplicationResultNode']>>>, ParentType, ContextType>; possibleDuplicate?: Resolver<Maybe<Array<Maybe<ResolversTypes['DeduplicationResultNode']>>>, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; @@ -29445,7 +29379,6 @@ export type TicketNeedsAdjudicationDetailsExtraDataNodeResolvers<ContextType = a export type TicketNeedsAdjudicationDetailsNodeResolvers<ContextType = any, ParentType extends ResolversParentTypes['TicketNeedsAdjudicationDetailsNode'] = ResolversParentTypes['TicketNeedsAdjudicationDetailsNode']> = { createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>; - dedupEngineSimilarityPair?: Resolver<Maybe<ResolversTypes['DeduplicationEngineSimilarityPairNode']>, ParentType, ContextType>; extraData?: Resolver<Maybe<ResolversTypes['TicketNeedsAdjudicationDetailsExtraDataNode']>, ParentType, ContextType>; goldenRecordsIndividual?: Resolver<ResolversTypes['IndividualNode'], ParentType, ContextType>; hasDuplicatedDocument?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>; @@ -29975,9 +29908,8 @@ export type Resolvers<ContextType = any> = { Date?: GraphQLScalarType; DateTime?: GraphQLScalarType; Decimal?: GraphQLScalarType; + DeduplicationEngineSimilarityPairIndividualNode?: DeduplicationEngineSimilarityPairIndividualNodeResolvers<ContextType>; DeduplicationEngineSimilarityPairNode?: DeduplicationEngineSimilarityPairNodeResolvers<ContextType>; - DeduplicationEngineSimilarityPairNodeConnection?: DeduplicationEngineSimilarityPairNodeConnectionResolvers<ContextType>; - DeduplicationEngineSimilarityPairNodeEdge?: DeduplicationEngineSimilarityPairNodeEdgeResolvers<ContextType>; DeduplicationResultNode?: DeduplicationResultNodeResolvers<ContextType>; DeleteHouseholdApproveMutation?: DeleteHouseholdApproveMutationResolvers<ContextType>; DeletePaymentPlanMutation?: DeletePaymentPlanMutationResolvers<ContextType>; diff --git a/src/frontend/src/__generated__/introspection-result.ts b/src/frontend/src/__generated__/introspection-result.ts index 30b0cfea56..5e44708d73 100644 --- a/src/frontend/src/__generated__/introspection-result.ts +++ b/src/frontend/src/__generated__/introspection-result.ts @@ -16,7 +16,6 @@ "CommunicationMessageNode", "CommunicationMessageRecipientMapNode", "DataCollectingTypeNode", - "DeduplicationEngineSimilarityPairNode", "DeliveryMechanismNode", "DeliveryMechanismPerPaymentPlanNode", "DocumentNode", diff --git a/src/frontend/src/apollo/fragments/GrievanceTicketFragment.ts b/src/frontend/src/apollo/fragments/GrievanceTicketFragment.ts index f1f7fdd5e3..b7e1808023 100644 --- a/src/frontend/src/apollo/fragments/GrievanceTicketFragment.ts +++ b/src/frontend/src/apollo/fragments/GrievanceTicketFragment.ts @@ -207,20 +207,6 @@ export const grievanceTicketDetailed = gql` } needsAdjudicationTicketDetails { id - dedupEngineSimilarityPair { - isDuplicate - similarityScore - individual1 { - unicefId - fullName - photo - } - individual2 { - unicefId - fullName - photo - } - } hasDuplicatedDocument extraData { goldenRecords { @@ -233,6 +219,19 @@ export const grievanceTicketDetailed = gql` proximityToScore score } + dedupEngineSimilarityPair { + similarityScore + individual1 { + unicefId + fullName + photo + } + individual2 { + unicefId + fullName + photo + } + } } goldenRecordsIndividual { id diff --git a/src/frontend/src/apollo/fragments/RegistrationFragments.ts b/src/frontend/src/apollo/fragments/RegistrationFragments.ts index 3afa41ecd4..18e49d4c58 100644 --- a/src/frontend/src/apollo/fragments/RegistrationFragments.ts +++ b/src/frontend/src/apollo/fragments/RegistrationFragments.ts @@ -27,7 +27,7 @@ export const registrationMinimal = gql` refuseReason totalHouseholdsCountWithValidPhoneNo adminUrl - isDeduplicated + biometricDeduplicated } `; diff --git a/src/frontend/src/components/grievances/GrievancesTable/GrievancesFilters.tsx b/src/frontend/src/components/grievances/GrievancesTable/GrievancesFilters.tsx index 53c4713a61..29f116ce72 100644 --- a/src/frontend/src/components/grievances/GrievancesTable/GrievancesFilters.tsx +++ b/src/frontend/src/components/grievances/GrievancesTable/GrievancesFilters.tsx @@ -90,6 +90,7 @@ export const GrievancesFilters = ({ const showIssueType = filter.category === GRIEVANCE_CATEGORIES.SENSITIVE_GRIEVANCE || filter.category === GRIEVANCE_CATEGORIES.DATA_CHANGE || + filter.category === GRIEVANCE_CATEGORIES.NEEDS_ADJUDICATION || filter.category === GRIEVANCE_CATEGORIES.GRIEVANCE_COMPLAINT; const updatedPriorityChoices = useMemo(() => { diff --git a/src/frontend/src/components/grievances/NeedsAdjudication/BiometricsResults.tsx b/src/frontend/src/components/grievances/NeedsAdjudication/BiometricsResults.tsx index 2ebed0baef..d5e555f5c9 100644 --- a/src/frontend/src/components/grievances/NeedsAdjudication/BiometricsResults.tsx +++ b/src/frontend/src/components/grievances/NeedsAdjudication/BiometricsResults.tsx @@ -19,16 +19,15 @@ import { useTranslation } from 'react-i18next'; import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; export interface Individual { - __typename?: 'IndividualNode'; + __typename?: 'DeduplicationEngineSimilarityPairIndividualNode'; unicefId?: string; - fullName: string; + fullName?: string; photo?: string; } export interface BiometricsResultsProps { ticketId: string; similarityScore: string; - faceMatchResult: 'Duplicates' | 'Uniqueness'; individual1?: Individual; individual2?: Individual; } @@ -50,7 +49,6 @@ const Placeholder: React.FC = () => ( export const BiometricsResults = ({ ticketId, similarityScore, - faceMatchResult, individual1, individual2, }: BiometricsResultsProps): React.ReactElement => { @@ -146,7 +144,7 @@ export const BiometricsResults = ({ </strong> </div> <div> - {t('Face images matching suggests:')} {faceMatchResult} + {t('Face images matching suggests: Duplicates')} </div> </Box> </DialogContainer> diff --git a/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationActions.tsx b/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationActions.tsx index 96ee0543cf..8be5a383fd 100644 --- a/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationActions.tsx +++ b/src/frontend/src/components/grievances/NeedsAdjudication/NeedsAdjudicationActions.tsx @@ -52,7 +52,7 @@ export const NeedsAdjudicationActions: React.FC< const { isActiveProgram } = useProgramContext(); const actionsDisabled = !isTicketForApproval || !isActiveProgram || !selectedIndividualIds.length; - const { dedupEngineSimilarityPair } = ticket.needsAdjudicationTicketDetails; + const { dedupEngineSimilarityPair } = ticket.needsAdjudicationTicketDetails.extraData; return ( <Box @@ -90,11 +90,6 @@ export const NeedsAdjudicationActions: React.FC< <BiometricsResults ticketId={ticket.id} similarityScore={dedupEngineSimilarityPair.similarityScore} - faceMatchResult={ - dedupEngineSimilarityPair.isDuplicate - ? t('Duplicates') - : t('Uniqueness') - } individual1={dedupEngineSimilarityPair.individual1} individual2={dedupEngineSimilarityPair.individual2} /> diff --git a/src/frontend/src/containers/pages/rdi/RegistrationDataImportPage.tsx b/src/frontend/src/containers/pages/rdi/RegistrationDataImportPage.tsx index 8f3756cfa3..8e01406ae3 100644 --- a/src/frontend/src/containers/pages/rdi/RegistrationDataImportPage.tsx +++ b/src/frontend/src/containers/pages/rdi/RegistrationDataImportPage.tsx @@ -78,7 +78,7 @@ export function RegistrationDataImportPage(): React.ReactElement { disabled={deduplicationFlags.isDeduplicationDisabled} title={t('Deduplication engine already in progress')} > - {t('RUN DEDUPLICATION ENGINE')} + {t('START DEDUPLICATION ENGINE')} </ButtonTooltip> </Box> )} diff --git a/src/frontend/src/containers/pages/rdi/people/PeopleRegistrationDataImportPage.tsx b/src/frontend/src/containers/pages/rdi/people/PeopleRegistrationDataImportPage.tsx index 32171e489c..3ea73044d6 100644 --- a/src/frontend/src/containers/pages/rdi/people/PeopleRegistrationDataImportPage.tsx +++ b/src/frontend/src/containers/pages/rdi/people/PeopleRegistrationDataImportPage.tsx @@ -78,7 +78,7 @@ export function PeopleRegistrationDataImportPage(): React.ReactElement { disabled={deduplicationFlags.isDeduplicationDisabled} title={t('Deduplication engine already in progress')} > - {t('RUN DEDUPLICATION ENGINE')} + {t('START DEDUPLICATION ENGINE')} </ButtonTooltip> </Box> )} diff --git a/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/RegistrationDataImportForPeopleTableHeadCells.tsx b/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/RegistrationDataImportForPeopleTableHeadCells.tsx index d79ea5b974..0cd781f05f 100644 --- a/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/RegistrationDataImportForPeopleTableHeadCells.tsx +++ b/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/RegistrationDataImportForPeopleTableHeadCells.tsx @@ -29,8 +29,8 @@ export const headCells: HeadCell<RegistrationDataImportNode>[] = [ }, { disablePadding: false, - label: 'Is Deduplicated?', - id: 'isDeduplicated', + label: 'Biometric Deduplicated', + id: 'biometricDeduplicated', numeric: false, disableSort: true, }, diff --git a/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/RegistrationDataImportForPeopleTableRow.tsx b/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/RegistrationDataImportForPeopleTableRow.tsx index 374bf569d4..416fc836e7 100644 --- a/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/RegistrationDataImportForPeopleTableRow.tsx +++ b/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/RegistrationDataImportForPeopleTableRow.tsx @@ -80,7 +80,7 @@ export function RegistrationDataImportForPeopleTableRow({ </UniversalMoment> </TableCell> <TableCell align="center"> - {registrationDataImport.isDeduplicated} + {registrationDataImport.biometricDeduplicated} </TableCell> <TableCell align="right"> {registrationDataImport.numberOfIndividuals} diff --git a/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/__snapshots__/RegistrationDataImportForPeopleTable.test.tsx.snap b/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/__snapshots__/RegistrationDataImportForPeopleTable.test.tsx.snap index f8328c8542..8d8022d1f6 100644 --- a/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/__snapshots__/RegistrationDataImportForPeopleTable.test.tsx.snap +++ b/src/frontend/src/containers/tables/rdi/RegistrationDataImportForPeopleTable/__snapshots__/RegistrationDataImportForPeopleTable.test.tsx.snap @@ -126,7 +126,7 @@ exports[`containers/tables/rdi/RegistrationDataImportTable should render loading class="sc-iBdnpw jMIPuq" data-cy="table-label" > - Is Deduplicated? + Biometric Deduplicated </span> </th> <th @@ -580,7 +580,7 @@ exports[`containers/tables/rdi/RegistrationDataImportTable should render with da class="sc-iBdnpw jMIPuq" data-cy="table-label" > - Is Deduplicated? + Biometric Deduplicated </span> </th> <th diff --git a/src/frontend/src/containers/tables/rdi/RegistrationDataImportTable/RegistrationDataImportTable.tsx b/src/frontend/src/containers/tables/rdi/RegistrationDataImportTable/RegistrationDataImportTable.tsx index 81aa6b8f6e..a33e96f24a 100644 --- a/src/frontend/src/containers/tables/rdi/RegistrationDataImportTable/RegistrationDataImportTable.tsx +++ b/src/frontend/src/containers/tables/rdi/RegistrationDataImportTable/RegistrationDataImportTable.tsx @@ -69,8 +69,8 @@ export function RegistrationDataImportTable({ if (deduplicationFlags?.canRunDeduplication) { header.splice(4, 0, { disablePadding: false, - label: 'Is Deduplicated?', - id: 'isDeduplicated', + label: 'Biometric Deduplicated', + id: 'biometricDeduplicated', numeric: false, disableSort: true, }); diff --git a/src/frontend/src/containers/tables/rdi/RegistrationDataImportTable/RegistrationDataImportTableRow.tsx b/src/frontend/src/containers/tables/rdi/RegistrationDataImportTable/RegistrationDataImportTableRow.tsx index 57b49a1c93..d95067c376 100644 --- a/src/frontend/src/containers/tables/rdi/RegistrationDataImportTable/RegistrationDataImportTableRow.tsx +++ b/src/frontend/src/containers/tables/rdi/RegistrationDataImportTable/RegistrationDataImportTableRow.tsx @@ -87,7 +87,7 @@ export function RegistrationDataImportTableRow({ </TableCell> {biometricDeduplicationEnabled && ( <TableCell align="center"> - {registrationDataImport.isDeduplicated} + {registrationDataImport.biometricDeduplicated} </TableCell> )} <TableCell align="right"> diff --git a/src/hct_mis_api/apps/grievance/migrations/0073_migration.py b/src/hct_mis_api/apps/grievance/migrations/0073_migration.py new file mode 100644 index 0000000000..568caaf704 --- /dev/null +++ b/src/hct_mis_api/apps/grievance/migrations/0073_migration.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.25 on 2024-09-25 17:16 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('grievance', '0072_migration'), + ] + + operations = [ + migrations.RemoveField( + model_name='ticketneedsadjudicationdetails', + name='dedup_engine_similarity_pair', + ), + ] diff --git a/src/hct_mis_api/apps/grievance/models.py b/src/hct_mis_api/apps/grievance/models.py index 81c027943a..f14a16298b 100644 --- a/src/hct_mis_api/apps/grievance/models.py +++ b/src/hct_mis_api/apps/grievance/models.py @@ -834,9 +834,6 @@ class TicketNeedsAdjudicationDetails(TimeStampedUUIDModel): score_min = models.FloatField(default=0.0) score_max = models.FloatField(default=0.0) is_cross_area = models.BooleanField(default=False) - dedup_engine_similarity_pair = models.ForeignKey( - "registration_data.DeduplicationEngineSimilarityPair", on_delete=models.CASCADE, null=True - ) # deprecated and will remove soon selected_individual = models.ForeignKey( diff --git a/src/hct_mis_api/apps/grievance/schema.py b/src/hct_mis_api/apps/grievance/schema.py index 1087254cdb..4e156328bc 100644 --- a/src/hct_mis_api/apps/grievance/schema.py +++ b/src/hct_mis_api/apps/grievance/schema.py @@ -75,7 +75,10 @@ from hct_mis_api.apps.payment.schema import PaymentRecordAndPaymentNode from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.program.schema import ProgramNode -from hct_mis_api.apps.registration_data.nodes import DeduplicationResultNode +from hct_mis_api.apps.registration_data.nodes import ( + DeduplicationEngineSimilarityPairNode, + DeduplicationResultNode, +) from hct_mis_api.apps.utils.exceptions import log_and_raise from hct_mis_api.apps.utils.schema import Arg, ChartDatasetNode @@ -395,6 +398,7 @@ def resolve_household_data(parent: TicketHouseholdDataUpdateDetails, info: Any) class TicketNeedsAdjudicationDetailsExtraDataNode(graphene.ObjectType): golden_records = graphene.List(DeduplicationResultNode) possible_duplicate = graphene.List(DeduplicationResultNode) + dedup_engine_similarity_pair = graphene.Field(DeduplicationEngineSimilarityPairNode) def resolve_golden_records(self, info: Any) -> List[Dict]: return encode_ids(self.golden_records, "Individual", "hit_id") @@ -419,7 +423,10 @@ class Meta: def resolve_extra_data(parent, info: Any) -> TicketNeedsAdjudicationDetailsExtraDataNode: golden_records = parent.extra_data.get("golden_records") possible_duplicate = parent.extra_data.get("possible_duplicate") - return TicketNeedsAdjudicationDetailsExtraDataNode(golden_records, possible_duplicate) + dedup_engine_similarity_pair = parent.extra_data.get("dedup_engine_similarity_pair") + return TicketNeedsAdjudicationDetailsExtraDataNode( + golden_records, possible_duplicate, dedup_engine_similarity_pair + ) def resolve_possible_duplicates(self, info: Any) -> QuerySet: return self.possible_duplicates.all() diff --git a/src/hct_mis_api/apps/grievance/services/needs_adjudication_ticket_services.py b/src/hct_mis_api/apps/grievance/services/needs_adjudication_ticket_services.py index 89e13a57c0..1047b5a7df 100644 --- a/src/hct_mis_api/apps/grievance/services/needs_adjudication_ticket_services.py +++ b/src/hct_mis_api/apps/grievance/services/needs_adjudication_ticket_services.py @@ -1,3 +1,4 @@ +import logging from typing import TYPE_CHECKING, Dict, List, Optional, Sequence, Tuple from django.contrib.auth.models import AbstractUser @@ -43,6 +44,9 @@ from hct_mis_api.apps.program.models import Program +logger = logging.getLogger(__name__) + + def _clear_deduplication_individuals_fields(individuals: Sequence[Individual]) -> None: for individual in individuals: individual.deduplication_golden_record_status = UNIQUE @@ -97,6 +101,22 @@ def close_needs_adjudication_new_ticket(ticket_details: TicketNeedsAdjudicationD mark_as_distinct_individual(individual_to_distinct, user, ticket_details.ticket.programs.all()) _clear_deduplication_individuals_fields(distinct_individuals) + if ticket_details.ticket.issue_type == GrievanceTicket.ISSUE_TYPE_BIOMETRICS_SIMILARITY: + # both individuals are distinct, report false positive + if not duplicate_individuals and distinct_individuals: + from hct_mis_api.apps.registration_datahub.services.biometric_deduplication import ( + BiometricDeduplicationService, + ) + + ids = [str(individual.id) for individual in distinct_individuals] + service = BiometricDeduplicationService() + try: + service.report_false_positive_duplicate( + ids[0], ids[1], ticket_details.ticket.registration_data_import.program.deduplication_set_id + ) + except service.api.API_EXCEPTION_CLASS: + logger.exception("Failed to report false positive duplicate to Deduplication Engine") + def close_needs_adjudication_ticket_service(grievance_ticket: GrievanceTicket, user: AbstractUser) -> None: ticket_details = grievance_ticket.ticket_details @@ -168,6 +188,8 @@ def create_grievance_ticket_with_details( "golden_records": golden_records, "possible_duplicate": possible_duplicate.get_deduplication_golden_record(), } + if dedup_engine_similarity_pair: + extra_data["dedup_engine_similarity_pair"] = dedup_engine_similarity_pair.serialize_for_ticket() # type: ignore score_min, score_max = _get_min_max_score(golden_records) ticket_details = TicketNeedsAdjudicationDetails.objects.create( ticket=ticket, @@ -178,7 +200,6 @@ def create_grievance_ticket_with_details( extra_data=extra_data, score_min=score_min, score_max=score_max, - dedup_engine_similarity_pair=dedup_engine_similarity_pair, ) ticket_details.possible_duplicates.add(*possible_duplicates) diff --git a/src/hct_mis_api/apps/program/schema.py b/src/hct_mis_api/apps/program/schema.py index a8593d2289..f9944f43bb 100644 --- a/src/hct_mis_api/apps/program/schema.py +++ b/src/hct_mis_api/apps/program/schema.py @@ -268,10 +268,28 @@ def resolve_can_run_deduplication(self, info: Any, **kwargs: Any) -> bool: def resolve_is_deduplication_disabled(self, info: Any, **kwargs: Any) -> bool: encoded_program_id = info.context.headers.get("Program") program = Program.objects.only("id").get(id=decode_id_string(encoded_program_id)) + # deduplication engine in progress is_still_processing = RegistrationDataImport.objects.filter( program=program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS ).exists() - return is_still_processing + # all rdis are deduplicated + all_rdis_deduplicated = ( + RegistrationDataImport.objects.filter(program=program).all().count() + == RegistrationDataImport.objects.filter( + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, + program=program, + ).count() + ) + # rdi merge in progress + rdi_merging = RegistrationDataImport.objects.filter( + program=program, + status__in=[ + RegistrationDataImport.MERGE_SCHEDULED, + RegistrationDataImport.MERGING, + RegistrationDataImport.MERGE_ERROR, + ], + ).exists() + return is_still_processing or all_rdis_deduplicated or rdi_merging def resolve_all_programs(self, info: Any, **kwargs: Any) -> QuerySet[Program]: user = info.context.user diff --git a/src/hct_mis_api/apps/registration_data/admin.py b/src/hct_mis_api/apps/registration_data/admin.py index e65f602dfb..792e634c95 100644 --- a/src/hct_mis_api/apps/registration_data/admin.py +++ b/src/hct_mis_api/apps/registration_data/admin.py @@ -290,12 +290,7 @@ def enroll_to_program(self, request: HttpRequest, pk: UUID) -> Optional[HttpResp @admin.register(DeduplicationEngineSimilarityPair) class DeduplicationEngineSimilarityPairAdmin(HOPEModelAdminBase): - list_display = ("program", "individual1", "individual2", "similarity_score", "is_duplicate") + list_display = ("program", "individual1", "individual2", "similarity_score") list_filter = (("program__name", ValueFilter),) raw_id_fields = ("program", "individual1", "individual2") search_fields = ("individual1", "individual2") - - def is_duplicate(self, obj: DeduplicationEngineSimilarityPair) -> bool: - return obj._is_duplicate - - is_duplicate.boolean = True diff --git a/src/hct_mis_api/apps/registration_data/models.py b/src/hct_mis_api/apps/registration_data/models.py index f7b7ceb215..bfffd4c4c6 100644 --- a/src/hct_mis_api/apps/registration_data/models.py +++ b/src/hct_mis_api/apps/registration_data/models.py @@ -9,7 +9,7 @@ ProhibitNullCharactersValidator, ) from django.db import models, transaction -from django.db.models import Count, ExpressionWrapper, OuterRef, Q, Subquery +from django.db.models import Count, OuterRef, Q, Subquery from django.utils.translation import gettext_lazy as _ from hct_mis_api.apps.activity_log.utils import create_mapping_dict @@ -364,30 +364,6 @@ class KoboImportedSubmission(models.Model): ) -class DeduplicationEngineSimilarityPairManager(models.Manager): - def get_queryset(self) -> models.QuerySet: - return ( - super() - .get_queryset() - .annotate( - is_duplicate=ExpressionWrapper( - models.Case( - models.When( - similarity_score__gte=models.F("program__business_area__biometric_deduplication_threshold"), - then=models.Value(True), - ), - default=models.Value(False), - output_field=models.BooleanField(), - ), - output_field=models.BooleanField(), - ) - ) - ) - - def duplicates(self) -> models.QuerySet: - return self.get_queryset().filter(is_duplicate=True) - - class DeduplicationEngineSimilarityPair(models.Model): program = models.ForeignKey( "program.Program", related_name="deduplication_engine_similarity_pairs", on_delete=models.CASCADE @@ -403,8 +379,6 @@ class DeduplicationEngineSimilarityPair(models.Model): decimal_places=2, ) - objects = DeduplicationEngineSimilarityPairManager() - class Meta: unique_together = ("individual1", "individual2") constraints = [ @@ -415,6 +389,13 @@ class Meta: ), ] + @classmethod + def remove_pairs(cls, deduplication_set_id: str) -> None: + from hct_mis_api.apps.program.models import Program + + program = Program.objects.get(deduplication_set_id=deduplication_set_id) + cls.objects.filter(program=program).delete() + @classmethod def bulk_add_pairs(cls, deduplication_set_id: str, duplicates_data: List[SimilarityPair]) -> None: from hct_mis_api.apps.program.models import Program @@ -441,9 +422,17 @@ def bulk_add_pairs(cls, deduplication_set_id: str, duplicates_data: List[Similar with transaction.atomic(): cls.objects.bulk_create(duplicates, ignore_conflicts=True) - @property - def _is_duplicate(self) -> bool: - from hct_mis_api.apps.registration_datahub.tasks.deduplicate import Thresholds - - thresholds = Thresholds.from_business_area(self.program.business_area) - return self.similarity_score >= thresholds.BIOMETRIC_DEDUPLICATION_THRESHOLD + def serialize_for_ticket(self) -> Dict[str, Any]: + return { + "individual1": { + "unicef_id": str(self.individual1.unicef_id), + "full_name": self.individual1.full_name, + "photo": str(self.individual1.photo.url) if self.individual1.photo else None, + }, + "individual2": { + "unicef_id": str(self.individual2.unicef_id), + "full_name": self.individual2.full_name, + "photo": str(self.individual2.photo.url) if self.individual2.photo else None, + }, + "similarity_score": float(self.similarity_score), + } diff --git a/src/hct_mis_api/apps/registration_data/nodes.py b/src/hct_mis_api/apps/registration_data/nodes.py index 0a1db4a04a..28ec933b85 100644 --- a/src/hct_mis_api/apps/registration_data/nodes.py +++ b/src/hct_mis_api/apps/registration_data/nodes.py @@ -5,6 +5,12 @@ from dateutil.parser import parse from dateutil.relativedelta import relativedelta +from hct_mis_api.apps.account.permissions import ( + BaseNodePermissionMixin, + Permissions, + hopePermissionClass, +) + class DeduplicationResultNode(graphene.ObjectType): hit_id = graphene.ID() @@ -31,3 +37,17 @@ def resolve_duplicate(self, info: Any) -> bool: def resolve_distinct(self, info: Any) -> bool: return self.get("distinct", False) + + +class DeduplicationEngineSimilarityPairIndividualNode(graphene.ObjectType): + photo = graphene.String() + full_name = graphene.String() + unicef_id = graphene.String() + + +class DeduplicationEngineSimilarityPairNode(BaseNodePermissionMixin, graphene.ObjectType): + permission_classes = (hopePermissionClass(Permissions.GRIEVANCES_VIEW_BIOMETRIC_RESULTS),) + + individual1 = graphene.Field(DeduplicationEngineSimilarityPairIndividualNode) + individual2 = graphene.Field(DeduplicationEngineSimilarityPairIndividualNode) + similarity_score = graphene.String() diff --git a/src/hct_mis_api/apps/registration_data/schema.py b/src/hct_mis_api/apps/registration_data/schema.py index 39f91bc166..6e08853591 100644 --- a/src/hct_mis_api/apps/registration_data/schema.py +++ b/src/hct_mis_api/apps/registration_data/schema.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional, Union +from typing import Any, Dict, List, Union import graphene from graphene_django import DjangoObjectType @@ -15,40 +15,8 @@ from hct_mis_api.apps.core.extended_connection import ExtendedConnection from hct_mis_api.apps.core.schema import ChoiceObject from hct_mis_api.apps.core.utils import get_count_and_percentage, to_choice_object -from hct_mis_api.apps.household.models import Individual from hct_mis_api.apps.registration_data.filters import RegistrationDataImportFilter -from hct_mis_api.apps.registration_data.models import ( - DeduplicationEngineSimilarityPair, - RegistrationDataImport, -) - - -class DeduplicationEngineSimilarityPairIndividualNode(graphene.ObjectType): - photo = graphene.String() - full_name = graphene.String() - unicef_id = graphene.String() - - @staticmethod - def resolve_photo(individual: Individual, info: Any) -> Optional[str]: - return individual.photo and individual.photo.url - - -class DeduplicationEngineSimilarityPairNode(BaseNodePermissionMixin, DjangoObjectType): - permission_classes = (hopePermissionClass(Permissions.GRIEVANCES_VIEW_BIOMETRIC_RESULTS),) - - is_duplicate = graphene.Boolean() - individual1 = DeduplicationEngineSimilarityPairIndividualNode() - individual2 = DeduplicationEngineSimilarityPairIndividualNode() - similarity_score = graphene.String() - - @staticmethod - def resolve_is_duplicate(similarity_pair: DeduplicationEngineSimilarityPair, info: Any) -> bool: - return similarity_pair._is_duplicate - - class Meta: - model = DeduplicationEngineSimilarityPair - interfaces = (graphene.relay.Node,) - connection_class = ExtendedConnection +from hct_mis_api.apps.registration_data.models import RegistrationDataImport class CountAndPercentageNode(graphene.ObjectType): @@ -65,7 +33,7 @@ class RegistrationDataImportNode(BaseNodePermissionMixin, AdminUrlNodeMixin, Dja golden_record_possible_duplicates_count_and_percentage = graphene.List(CountAndPercentageNode) golden_record_unique_count_and_percentage = graphene.List(CountAndPercentageNode) total_households_count_with_valid_phone_no = graphene.Int() - is_deduplicated = graphene.String() + biometric_deduplicated = graphene.String() can_merge = graphene.Boolean() biometric_deduplication_enabled = graphene.Boolean() @@ -142,11 +110,8 @@ def resolve_total_households_count_with_valid_phone_no(parent: RegistrationDataI ).count() @staticmethod - def resolve_is_deduplicated(parent: RegistrationDataImport, info: Any, **kwargs: Any) -> str: - if parent.deduplication_engine_status in [ - RegistrationDataImport.DEDUP_ENGINE_FINISHED, - RegistrationDataImport.DEDUP_ENGINE_ERROR, - ]: + def resolve_biometric_deduplicated(parent: RegistrationDataImport, info: Any, **kwargs: Any) -> str: + if parent.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_FINISHED: return "YES" return "NO" diff --git a/src/hct_mis_api/apps/registration_datahub/apis/deduplication_engine.py b/src/hct_mis_api/apps/registration_datahub/apis/deduplication_engine.py index 7dbd20fcd6..99f023b549 100644 --- a/src/hct_mis_api/apps/registration_datahub/apis/deduplication_engine.py +++ b/src/hct_mis_api/apps/registration_datahub/apis/deduplication_engine.py @@ -1,5 +1,5 @@ import dataclasses -from typing import List, Optional, Tuple +from typing import List, Tuple from hct_mis_api.apps.core.api.mixins import BaseAPI @@ -14,13 +14,18 @@ class SimilarityPair: @dataclasses.dataclass class DeduplicationSetData: state: str # "Clean", "Dirty", "Processing", "Error" - error: Optional[str] = None + + +@dataclasses.dataclass +class DeduplicationSetConfig: + face_distance_threshold: float # 0.0 - 1.0 @dataclasses.dataclass class DeduplicationSet: reference_pk: str # program.id notification_url: str # webhook url + config: DeduplicationSetConfig @dataclasses.dataclass @@ -29,6 +34,12 @@ class DeduplicationImage: filename: str # individual.photo.name +@dataclasses.dataclass +class IgnoredKeysPair: + first_reference_pk: str # individual.id + second_reference_pk: str # individual.id + + class DeduplicationEngineAPI(BaseAPI): API_KEY_ENV_NAME = "DEDUPLICATION_ENGINE_API_KEY" API_URL_ENV_NAME = "DEDUPLICATION_ENGINE_API_URL" @@ -53,6 +64,7 @@ class Endpoints: BULK_DELETE_IMAGES = "deduplication_sets/{deduplication_set_pk}/images_bulk/clear/" # DELETE - Delete all images for a deduplication set GET_DUPLICATES = "deduplication_sets/{deduplication_set_pk}/duplicates/" # GET - List view + IGNORED_KEYS = "deduplication_sets/{deduplication_set_pk}/ignored_keys/" # POST/GET def delete_deduplication_set(self, deduplication_set_id: str) -> dict: response_data, _ = self._delete(self.Endpoints.DELETE_DEDUPLICATION_SET.format(pk=deduplication_set_id)) @@ -88,3 +100,9 @@ def process_deduplication(self, deduplication_set_id: str) -> Tuple[dict, int]: self.Endpoints.PROCESS_DEDUPLICATION.format(pk=deduplication_set_id), validate_response=False ) return response_data, status + + def report_false_positive_duplicate(self, false_positive_pair: IgnoredKeysPair, deduplication_set_id: str) -> None: + self._post( + self.Endpoints.IGNORED_KEYS.format(deduplication_set_pk=deduplication_set_id), + dataclasses.asdict(false_positive_pair), + ) diff --git a/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py b/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py index 04116986e0..194edaaac1 100644 --- a/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py +++ b/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py @@ -16,7 +16,9 @@ DeduplicationEngineAPI, DeduplicationImage, DeduplicationSet, + DeduplicationSetConfig, DeduplicationSetData, + IgnoredKeysPair, SimilarityPair, ) @@ -35,6 +37,9 @@ def create_deduplication_set(self, program: Program) -> None: reference_pk=str(program.id), notification_url=f"https://{settings.DOMAIN_NAME}/api/rest/{program.business_area.slug}/programs/{str(program.id)}/registration-data/webhookdeduplication/", # notification_url=reverse("registration-data:webhook_deduplication", kwargs={"program_id": str(program.id), "business_area": program.business_area.slug}), # TODO MB why reverse is not working + config=DeduplicationSetConfig( + face_distance_threshold=program.business_area.biometric_deduplication_threshold / 100 + ), ) response_data = self.api.create_deduplication_set(deduplication_set) program.deduplication_set_id = uuid.UUID(response_data["id"]) @@ -45,7 +50,7 @@ def get_deduplication_set_results(self, deduplication_set_id: str) -> dict: def get_deduplication_set(self, deduplication_set_id: str) -> DeduplicationSetData: response_data = self.api.get_deduplication_set(deduplication_set_id) - return DeduplicationSetData(state=response_data["state"], error=response_data["error"]) + return DeduplicationSetData(state=response_data["state"]) def upload_individuals(self, deduplication_set_id: str, rdi: RegistrationDataImport) -> None: individuals = ( @@ -64,15 +69,21 @@ def upload_individuals(self, deduplication_set_id: str, rdi: RegistrationDataImp for individual in individuals ] - try: - self.api.bulk_upload_images(deduplication_set_id, images) - rdi.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_UPLOADED - rdi.save(update_fields=["deduplication_engine_status"]) + if rdi.deduplication_engine_status in [ + RegistrationDataImport.DEDUP_ENGINE_UPLOAD_ERROR, + RegistrationDataImport.DEDUP_ENGINE_PENDING, + ]: + try: + self.api.bulk_upload_images(deduplication_set_id, images) - except DeduplicationEngineAPI.DeduplicationEngineAPIException: - logging.exception(f"Failed to upload images for RDI {rdi} to deduplication set {deduplication_set_id}") - rdi.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_UPLOAD_ERROR - rdi.save(update_fields=["deduplication_engine_status"]) + except DeduplicationEngineAPI.DeduplicationEngineAPIException: + logging.exception(f"Failed to upload images for RDI {rdi} to deduplication set {deduplication_set_id}") + rdi.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_UPLOAD_ERROR + rdi.save(update_fields=["deduplication_engine_status"]) + return + + rdi.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_UPLOADED + rdi.save(update_fields=["deduplication_engine_status"]) def process_deduplication_set(self, deduplication_set_id: str, rdis: QuerySet[RegistrationDataImport]) -> None: response_data, status = self.api.process_deduplication(deduplication_set_id) @@ -108,13 +119,10 @@ def upload_and_process_deduplication_set(self, program: Program) -> None: deduplication_engine_status__in=[ RegistrationDataImport.DEDUP_ENGINE_PENDING, RegistrationDataImport.DEDUP_ENGINE_UPLOAD_ERROR, + RegistrationDataImport.DEDUP_ENGINE_ERROR, ], ).exclude( status__in=[ - RegistrationDataImport.MERGE_SCHEDULED, - RegistrationDataImport.MERGED, - RegistrationDataImport.MERGING, - RegistrationDataImport.MERGE_ERROR, RegistrationDataImport.REFUSED_IMPORT, ] ) @@ -127,10 +135,6 @@ def upload_and_process_deduplication_set(self, program: Program) -> None: deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_UPLOADED ).exclude( status__in=[ - RegistrationDataImport.MERGE_SCHEDULED, - RegistrationDataImport.MERGED, - RegistrationDataImport.MERGING, - RegistrationDataImport.MERGE_ERROR, RegistrationDataImport.REFUSED_IMPORT, ] ) @@ -141,6 +145,8 @@ def upload_and_process_deduplication_set(self, program: Program) -> None: def delete_deduplication_set(self, program: Program) -> None: if program.deduplication_set_id: self.api.delete_deduplication_set(str(program.deduplication_set_id)) + DeduplicationEngineSimilarityPair.objects.filter(program=program).delete() + program.deduplication_set_id = None program.save(update_fields=["deduplication_set_id"]) @@ -151,6 +157,7 @@ def mark_rdis_as_pending(cls, program: Program) -> None: ) def store_similarity_pairs(self, deduplication_set_id: str, similarity_pairs: List[SimilarityPair]) -> None: + DeduplicationEngineSimilarityPair.remove_pairs(deduplication_set_id) DeduplicationEngineSimilarityPair.bulk_add_pairs(deduplication_set_id, similarity_pairs) def mark_rdis_as_deduplicated(self, deduplication_set_id: str) -> None: @@ -162,7 +169,9 @@ def mark_rdis_as_deduplicated(self, deduplication_set_id: str) -> None: def store_rdis_deduplication_statistics(self, deduplication_set_id: str) -> None: program = Program.objects.get(deduplication_set_id=deduplication_set_id) rdis = RegistrationDataImport.objects.filter( - program=program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + status=RegistrationDataImport.IN_REVIEW, + program=program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, ) for rdi in rdis: rdi.dedup_engine_batch_duplicates = self.get_duplicate_individuals_for_rdi_against_batch_count(rdi) @@ -174,9 +183,9 @@ def store_rdis_deduplication_statistics(self, deduplication_set_id: str) -> None def update_rdis_deduplication_statistics(self, program_id: str) -> None: program = Program.objects.get(id=program_id) rdis = RegistrationDataImport.objects.filter( + status=RegistrationDataImport.IN_REVIEW, program=program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, - status=RegistrationDataImport.IN_REVIEW, ) for rdi in rdis: rdi.dedup_engine_batch_duplicates = self.get_duplicate_individuals_for_rdi_against_batch_count(rdi) @@ -197,14 +206,10 @@ def get_duplicates_for_rdi_against_batch( ) -> QuerySet[DeduplicationEngineSimilarityPair]: """Used in RDI statistics""" rdi_individuals = PendingIndividual.objects.filter(registration_data_import=rdi).only("id") - return ( - DeduplicationEngineSimilarityPair.objects.duplicates() - .filter( - Q(individual1__in=rdi_individuals) & Q(individual2__in=rdi_individuals), - program=rdi.program, - ) - .distinct() - ) + return DeduplicationEngineSimilarityPair.objects.filter( + Q(individual1__in=rdi_individuals) & Q(individual2__in=rdi_individuals), + program=rdi.program, + ).distinct() def get_duplicate_individuals_for_rdi_against_batch_count(self, rdi: RegistrationDataImport) -> int: """Used in RDI statistics""" @@ -225,18 +230,14 @@ def get_duplicates_for_merged_rdi_against_population( from hct_mis_api.apps.utils.models import MergeStatusModel rdi_individuals = rdi.individuals.filter(is_removed=False).only("id") - return ( - DeduplicationEngineSimilarityPair.objects.duplicates() - .filter( - Q(individual1__in=rdi_individuals) | Q(individual2__in=rdi_individuals), - Q(individual1__duplicate=False) & Q(individual2__duplicate=False), - Q(individual1__withdrawn=False) & Q(individual2__withdrawn=False), - Q(individual1__rdi_merge_status=MergeStatusModel.MERGED) - & Q(individual2__rdi_merge_status=MergeStatusModel.MERGED), - program=rdi.program, - ) - .distinct() - ) + return DeduplicationEngineSimilarityPair.objects.filter( + Q(individual1__in=rdi_individuals) | Q(individual2__in=rdi_individuals), + Q(individual1__duplicate=False) & Q(individual2__duplicate=False), + Q(individual1__withdrawn=False) & Q(individual2__withdrawn=False), + Q(individual1__rdi_merge_status=MergeStatusModel.MERGED) + & Q(individual2__rdi_merge_status=MergeStatusModel.MERGED), + program=rdi.program, + ).distinct() def get_duplicates_for_rdi_against_population( self, rdi: RegistrationDataImport @@ -252,8 +253,7 @@ def get_duplicates_for_rdi_against_population( from hct_mis_api.apps.utils.models import MergeStatusModel return ( - DeduplicationEngineSimilarityPair.objects.duplicates() - .filter( + DeduplicationEngineSimilarityPair.objects.filter( Q(individual1__in=rdi_pending_individuals) | Q(individual2__in=rdi_pending_individuals), Q(individual1__duplicate=False) & Q(individual2__duplicate=False), Q(individual1__withdrawn=False) & Q(individual2__withdrawn=False), @@ -313,5 +313,11 @@ def fetch_biometric_deduplication_results_and_process(self, deduplication_set_id self.mark_rdis_as_deduplication_error(deduplication_set_id) logger.error( f"Failed to process deduplication set {deduplication_set_id}," - f" dedupe engine state: {deduplication_set_data.state} error: {deduplication_set_data.error}" + f" dedupe engine state: {deduplication_set_data.state}" ) + + def report_false_positive_duplicate( + self, individual1_id: str, individual2_id: str, deduplication_set_id: str + ) -> None: + false_positive_pair = IgnoredKeysPair(first_reference_pk=individual1_id, second_reference_pk=individual2_id) + self.api.report_false_positive_duplicate(false_positive_pair, deduplication_set_id) diff --git a/tests/unit/apps/grievance/snapshots/snap_test_approve_automatic_tickets.py b/tests/unit/apps/grievance/snapshots/snap_test_approve_automatic_tickets.py index 4f3f059b45..0a18a2e731 100644 --- a/tests/unit/apps/grievance/snapshots/snap_test_approve_automatic_tickets.py +++ b/tests/unit/apps/grievance/snapshots/snap_test_approve_automatic_tickets.py @@ -88,15 +88,8 @@ 'grievanceTicket': { 'id': 'R3JpZXZhbmNlVGlja2V0Tm9kZToyYjQxOWNlMy0zMjk3LTQ3ZWUtYTQ3Zi00MzQ0MmFiYWM3M2U=', 'needsAdjudicationTicketDetails': { - 'dedupEngineSimilarityPair': { - 'individual1': { - 'fullName': 'Robin Ford' - }, - 'individual2': { - 'fullName': 'Benjamin Butler' - }, - 'isDuplicate': True, - 'similarityScore': '55.55' + 'extraData': { + 'dedupEngineSimilarityPair': None }, 'selectedDistinct': [ ], @@ -118,15 +111,8 @@ 'grievanceTicket': { 'id': 'R3JpZXZhbmNlVGlja2V0Tm9kZToyYjQxOWNlMy0zMjk3LTQ3ZWUtYTQ3Zi00MzQ0MmFiYWM3M2U=', 'needsAdjudicationTicketDetails': { - 'dedupEngineSimilarityPair': { - 'individual1': { - 'fullName': 'Robin Ford' - }, - 'individual2': { - 'fullName': 'Benjamin Butler' - }, - 'isDuplicate': True, - 'similarityScore': '55.55' + 'extraData': { + 'dedupEngineSimilarityPair': None }, 'selectedDistinct': [ { @@ -151,15 +137,8 @@ 'grievanceTicket': { 'id': 'R3JpZXZhbmNlVGlja2V0Tm9kZToyYjQxOWNlMy0zMjk3LTQ3ZWUtYTQ3Zi00MzQ0MmFiYWM3M2U=', 'needsAdjudicationTicketDetails': { - 'dedupEngineSimilarityPair': { - 'individual1': { - 'fullName': 'Robin Ford' - }, - 'individual2': { - 'fullName': 'Benjamin Butler' - }, - 'isDuplicate': True, - 'similarityScore': '55.55' + 'extraData': { + 'dedupEngineSimilarityPair': None }, 'selectedDistinct': [ ], diff --git a/tests/unit/apps/grievance/test_approve_automatic_tickets.py b/tests/unit/apps/grievance/test_approve_automatic_tickets.py index 8e3791c4df..42966cfcbb 100644 --- a/tests/unit/apps/grievance/test_approve_automatic_tickets.py +++ b/tests/unit/apps/grievance/test_approve_automatic_tickets.py @@ -104,15 +104,16 @@ class TestGrievanceApproveAutomaticMutation(APITestCase): selectedDuplicates { unicefId } - dedupEngineSimilarityPair { - individual1 { - fullName - } - individual2 { - fullName - } - similarityScore - isDuplicate + extraData{ + dedupEngineSimilarityPair { + individual1 { + fullName + } + individual2 { + fullName + } + similarityScore + } } } } @@ -239,18 +240,11 @@ def setUpTestData(cls) -> None: business_area=cls.business_area, status=GrievanceTicket.STATUS_FOR_APPROVAL, ) - dedup_engine_similarity_pair = DeduplicationEngineSimilarityPair.objects.create( - program=program_one, - individual1=ind1, - individual2=ind2, - similarity_score=55.55, - ) ticket_details = TicketNeedsAdjudicationDetailsFactory( ticket=cls.needs_adjudication_grievance_ticket, golden_records_individual=first_individual, possible_duplicate=second_individual, selected_individual=None, - dedup_engine_similarity_pair=dedup_engine_similarity_pair, ) ticket_details.possible_duplicates.add(first_individual, second_individual) diff --git a/tests/unit/apps/grievance/test_create_needs_adjudication_tickets.py b/tests/unit/apps/grievance/test_create_needs_adjudication_tickets.py index 06ae7c69be..6ec9df520f 100644 --- a/tests/unit/apps/grievance/test_create_needs_adjudication_tickets.py +++ b/tests/unit/apps/grievance/test_create_needs_adjudication_tickets.py @@ -50,14 +50,20 @@ def setUpTestData(cls) -> None: "given_name": "test", "family_name": "name", "birth_date": "1999-01-22", - "deduplication_golden_record_results": {"duplicates": [], "possible_duplicates": []}, + "deduplication_golden_record_results": { + "duplicates": [], + "possible_duplicates": [], + }, }, { "full_name": "Test2 Name2", "given_name": "Test2", "family_name": "Name2", "birth_date": "1999-01-22", - "deduplication_golden_record_results": {"duplicates": [], "possible_duplicates": []}, + "deduplication_golden_record_results": { + "duplicates": [], + "possible_duplicates": [], + }, }, ] individuals = [ @@ -104,8 +110,13 @@ def test_create_needs_adjudication_ticket_with_the_same_ind(self) -> None: "duplicates": [{"hit_id": str(ind_2.pk)}], "possible_duplicates": [{"hit_id": str(ind_2.pk)}], } - ind.deduplication_golden_record_results = deduplication_golden_record_results_data - ind_2.deduplication_golden_record_results = {"duplicates": [], "possible_duplicates": []} + ind.deduplication_golden_record_results = ( + deduplication_golden_record_results_data + ) + ind_2.deduplication_golden_record_results = { + "duplicates": [], + "possible_duplicates": [], + } ind.save() ind_2.save() create_needs_adjudication_tickets( @@ -158,7 +169,10 @@ def setUpTestData(cls) -> None: "given_name": "test", "family_name": "name", "birth_date": "1999-01-22", - "deduplication_golden_record_results": {"duplicates": [], "possible_duplicates": []}, + "deduplication_golden_record_results": { + "duplicates": [], + "possible_duplicates": [], + }, "photo": ContentFile(b"aaa", name="fooa.png"), }, { @@ -166,7 +180,10 @@ def setUpTestData(cls) -> None: "given_name": "Test2", "family_name": "Name2", "birth_date": "1999-01-22", - "deduplication_golden_record_results": {"duplicates": [], "possible_duplicates": []}, + "deduplication_golden_record_results": { + "duplicates": [], + "possible_duplicates": [], + }, "photo": ContentFile(b"bbb", name="foob.png"), }, ] @@ -176,7 +193,10 @@ def setUpTestData(cls) -> None: "given_name": "test", "family_name": "name", "birth_date": "1999-01-22", - "deduplication_golden_record_results": {"duplicates": [], "possible_duplicates": []}, + "deduplication_golden_record_results": { + "duplicates": [], + "possible_duplicates": [], + }, "photo": ContentFile(b"aaa", name="fooa.png"), }, ] @@ -196,18 +216,22 @@ def setUpTestData(cls) -> None: cls.ind1, cls.ind2 = sorted(individuals, key=lambda x: x.id) cls.ind3, cls.ind4 = sorted([cls.ind1, other_individual], key=lambda x: x.id) - cls.dedup_engine_similarity_pair = DeduplicationEngineSimilarityPair.objects.create( - program=program, - individual1=cls.ind1, - individual2=cls.ind2, - similarity_score=55.55, + cls.dedup_engine_similarity_pair = ( + DeduplicationEngineSimilarityPair.objects.create( + program=program, + individual1=cls.ind1, + individual2=cls.ind2, + similarity_score=55.55, + ) ) - cls.dedup_engine_similarity_pair_2 = DeduplicationEngineSimilarityPair.objects.create( - program=program, - individual1=cls.ind3, - individual2=cls.ind4, - similarity_score=75.25, + cls.dedup_engine_similarity_pair_2 = ( + DeduplicationEngineSimilarityPair.objects.create( + program=program, + individual1=cls.ind3, + individual2=cls.ind4, + similarity_score=75.25, + ) ) def test_create_na_tickets_biometrics(self) -> None: @@ -215,13 +239,18 @@ def test_create_na_tickets_biometrics(self) -> None: self.assertEqual(TicketNeedsAdjudicationDetails.objects.all().count(), 0) self.assertIsNone(self.rdi.deduplication_engine_status) - create_needs_adjudication_tickets_for_biometrics(DeduplicationEngineSimilarityPair.objects.none(), self.rdi) + create_needs_adjudication_tickets_for_biometrics( + DeduplicationEngineSimilarityPair.objects.none(), self.rdi + ) self.assertEqual(GrievanceTicket.objects.all().count(), 0) self.assertEqual(TicketNeedsAdjudicationDetails.objects.all().count(), 0) self.assertEqual(DeduplicationEngineSimilarityPair.objects.all().count(), 2) create_needs_adjudication_tickets_for_biometrics( - DeduplicationEngineSimilarityPair.objects.filter(pk=self.dedup_engine_similarity_pair.pk), self.rdi + DeduplicationEngineSimilarityPair.objects.filter( + pk=self.dedup_engine_similarity_pair.pk + ), + self.rdi, ) self.assertEqual(GrievanceTicket.objects.all().count(), 1) @@ -229,23 +258,35 @@ def test_create_na_tickets_biometrics(self) -> None: grievance_ticket = GrievanceTicket.objects.first() na_ticket = TicketNeedsAdjudicationDetails.objects.first() - self.assertEqual(grievance_ticket.category, GrievanceTicket.CATEGORY_NEEDS_ADJUDICATION) - self.assertEqual(grievance_ticket.issue_type, GrievanceTicket.ISSUE_TYPE_BIOMETRICS_SIMILARITY) + self.assertEqual( + grievance_ticket.category, GrievanceTicket.CATEGORY_NEEDS_ADJUDICATION + ) + self.assertEqual( + grievance_ticket.issue_type, + GrievanceTicket.ISSUE_TYPE_BIOMETRICS_SIMILARITY, + ) - # self.assertEqual(na_ticket.golden_records_individual, self.ind1) - # self.assertEqual(na_ticket.possible_duplicate, self.ind2) self.assertTrue(na_ticket.is_multiple_duplicates_version) - self.assertEqual(na_ticket.dedup_engine_similarity_pair, self.dedup_engine_similarity_pair) + self.assertEqual( + na_ticket.extra_data["dedup_engine_similarity_pair"], + self.dedup_engine_similarity_pair.serialize_for_ticket(), + ) # different RDI create_needs_adjudication_tickets_for_biometrics( - DeduplicationEngineSimilarityPair.objects.filter(pk=self.dedup_engine_similarity_pair_2.pk), self.rdi + DeduplicationEngineSimilarityPair.objects.filter( + pk=self.dedup_engine_similarity_pair_2.pk + ), + self.rdi, ) self.assertEqual(GrievanceTicket.objects.all().count(), 2) self.assertEqual(TicketNeedsAdjudicationDetails.objects.all().count(), 2) # run one time create_needs_adjudication_tickets_for_biometrics( - DeduplicationEngineSimilarityPair.objects.filter(pk=self.dedup_engine_similarity_pair_2.pk), self.rdi + DeduplicationEngineSimilarityPair.objects.filter( + pk=self.dedup_engine_similarity_pair_2.pk + ), + self.rdi, ) self.assertEqual(GrievanceTicket.objects.all().count(), 2) self.assertEqual(TicketNeedsAdjudicationDetails.objects.all().count(), 2) diff --git a/tests/unit/apps/grievance/test_services_utils.py b/tests/unit/apps/grievance/test_services_utils.py index eaf45cd1bb..bbb712dc8d 100644 --- a/tests/unit/apps/grievance/test_services_utils.py +++ b/tests/unit/apps/grievance/test_services_utils.py @@ -1,3 +1,4 @@ +import uuid from typing import Any from unittest.mock import MagicMock, patch @@ -6,6 +7,7 @@ import pytest +from hct_mis_api.apps.registration_data.models import DeduplicationEngineSimilarityPair from hct_mis_api.apps.account.fixtures import ( BusinessAreaFactory, PartnerFactory, @@ -31,6 +33,7 @@ ) from hct_mis_api.apps.grievance.services.needs_adjudication_ticket_services import ( close_needs_adjudication_ticket_service, + create_grievance_ticket_with_details, ) from hct_mis_api.apps.grievance.utils import ( validate_all_individuals_before_close_needs_adjudication, @@ -80,7 +83,11 @@ def test_cast_flex_fields(self, mock_filter: Any) -> None: MagicMock(values_list=MagicMock(return_value=["integer_field"])), ] - flex_fields = {"decimal_field": "321.11", "integer_field": "123", "string_field": "some_string"} + flex_fields = { + "decimal_field": "321.11", + "integer_field": "123", + "string_field": "some_string", + } cast_flex_fields(flex_fields) self.assertEqual(flex_fields["string_field"], "some_string") @@ -125,7 +132,10 @@ def test_handle_update_payment_channel(self) -> None: def test_verify_flex_fields(self) -> None: with pytest.raises(ValueError) as e: verify_flex_fields({"key": "value"}, "associated_with") - assert str(e.value) == "associated_with argument must be one of ['household', 'individual']" + assert ( + str(e.value) + == "associated_with argument must be one of ['household', 'individual']" + ) with pytest.raises(ValueError) as e: verify_flex_fields({"key": "value"}, "individuals") @@ -141,14 +151,23 @@ def test_handle_role(self) -> None: self.assertEqual(IndividualRoleInHousehold.objects.all().count(), 0) with pytest.raises(ValidationError) as e: - IndividualRoleInHouseholdFactory(household=household, individual=individuals[0], role=ROLE_PRIMARY) + IndividualRoleInHouseholdFactory( + household=household, individual=individuals[0], role=ROLE_PRIMARY + ) handle_role(ROLE_PRIMARY, household, individuals[0]) - assert str(e.value) == "Ticket cannot be closed, primary collector role has to be reassigned" + assert ( + str(e.value) + == "Ticket cannot be closed, primary collector role has to be reassigned" + ) # just remove exists roles - IndividualRoleInHousehold.objects.filter(household=household).update(role=ROLE_ALTERNATE) + IndividualRoleInHousehold.objects.filter(household=household).update( + role=ROLE_ALTERNATE + ) handle_role("OTHER_ROLE_XD", household, individuals[0]) - self.assertEqual(IndividualRoleInHousehold.objects.filter(household=household).count(), 0) + self.assertEqual( + IndividualRoleInHousehold.objects.filter(household=household).count(), 0 + ) # create new role handle_role(ROLE_ALTERNATE, household, individuals[0]) @@ -166,7 +185,13 @@ def test_handle_add_document(self) -> None: individuals_data=[{}], ) individual = individuals[0] - document_data = {"key": "TAX", "country": "AFG", "number": "111", "photo": "photo", "photoraw": "photo_raw"} + document_data = { + "key": "TAX", + "country": "AFG", + "number": "111", + "photo": "photo", + "photoraw": "photo_raw", + } with pytest.raises(ValidationError) as e: DocumentFactory( @@ -183,7 +208,10 @@ def test_handle_add_document(self) -> None: document_type.unique_for_individual = True document_type.save() handle_add_document(document_data, individual) - assert str(e.value) == "Document of type tax already exists for this individual" + assert ( + str(e.value) + == "Document of type tax already exists for this individual" + ) Document.objects.all().delete() self.assertEqual(Document.objects.all().count(), 0) @@ -194,9 +222,13 @@ def test_handle_add_document(self) -> None: def test_validate_individual_for_need_adjudication(self) -> None: area_type_level_1 = AreaTypeFactory(name="Province", area_level=1) - area_type_level_2 = AreaTypeFactory(name="District", area_level=2, parent=area_type_level_1) + area_type_level_2 = AreaTypeFactory( + name="District", area_level=2, parent=area_type_level_1 + ) ghazni = AreaFactory(name="Ghazni", area_type=area_type_level_1, p_code="area1") - doshi = AreaFactory(name="Doshi", area_type=area_type_level_2, p_code="area2", parent=ghazni) + doshi = AreaFactory( + name="Doshi", area_type=area_type_level_2, p_code="area2", parent=ghazni + ) business_area = BusinessAreaFactory(slug="afghanistan") program = ProgramFactory(business_area=business_area) grievance = GrievanceTicketFactory( @@ -208,13 +240,33 @@ def test_validate_individual_for_need_adjudication(self) -> None: grievance.programs.add(program) _, individuals_1 = create_household( - {"size": 1, "business_area": business_area, "program": program, "admin2": doshi}, - {"given_name": "John", "family_name": "Doe", "middle_name": "", "full_name": "John Doe"}, + { + "size": 1, + "business_area": business_area, + "program": program, + "admin2": doshi, + }, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, ) _, individuals_2 = create_household( - {"size": 1, "business_area": business_area, "program": program, "admin2": doshi}, - {"given_name": "John", "family_name": "Doe", "middle_name": "", "full_name": "John Doe"}, + { + "size": 1, + "business_area": business_area, + "program": program, + "admin2": doshi, + }, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, ) ticket_details = TicketNeedsAdjudicationDetailsFactory( @@ -231,17 +283,34 @@ def test_validate_individual_for_need_adjudication(self) -> None: partner_unicef = PartnerFactory() with pytest.raises(PermissionDenied) as e: - validate_individual_for_need_adjudication(partner, individuals_1[0], ticket_details) - assert str(e.value) == "Permission Denied: User does not have access to select individual" + validate_individual_for_need_adjudication( + partner, individuals_1[0], ticket_details + ) + assert ( + str(e.value) + == "Permission Denied: User does not have access to select individual" + ) with pytest.raises(ValidationError) as e: _, individuals = create_household( - {"size": 1, "business_area": business_area, "admin2": doshi, "program": program}, - {"given_name": "Tester", "family_name": "Test", "middle_name": "", "full_name": "Tester Test"}, + { + "size": 1, + "business_area": business_area, + "admin2": doshi, + "program": program, + }, + { + "given_name": "Tester", + "family_name": "Test", + "middle_name": "", + "full_name": "Tester Test", + }, ) individuals[0].unicef_id = "IND-333" individuals[0].save() - validate_individual_for_need_adjudication(partner_unicef, individuals[0], ticket_details) + validate_individual_for_need_adjudication( + partner_unicef, individuals[0], ticket_details + ) assert ( str(e.value) == "The selected individual IND-333 is not valid, must be one of those attached to the ticket" @@ -251,26 +320,45 @@ def test_validate_individual_for_need_adjudication(self) -> None: with pytest.raises(ValidationError) as e: individuals[0].withdraw() - validate_individual_for_need_adjudication(partner_unicef, individuals[0], ticket_details) - assert str(e.value) == "The selected individual IND-333 is not valid, must be not withdrawn" + validate_individual_for_need_adjudication( + partner_unicef, individuals[0], ticket_details + ) + assert ( + str(e.value) + == "The selected individual IND-333 is not valid, must be not withdrawn" + ) individuals[0].unwithdraw() - validate_individual_for_need_adjudication(partner_unicef, individuals[0], ticket_details) + validate_individual_for_need_adjudication( + partner_unicef, individuals[0], ticket_details + ) ticket_details.selected_distinct.remove(individuals[0]) individuals[0].unwithdraw() - validate_individual_for_need_adjudication(partner_unicef, individuals[0], ticket_details) + validate_individual_for_need_adjudication( + partner_unicef, individuals[0], ticket_details + ) def test_validate_all_individuals_before_close_needs_adjudication(self) -> None: BusinessAreaFactory(slug="afghanistan") _, individuals_1 = create_household( {"size": 1}, - {"given_name": "John", "family_name": "Doe", "middle_name": "", "full_name": "John Doe"}, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, ) _, individuals_2 = create_household( {"size": 1}, - {"given_name": "John", "family_name": "Doe", "middle_name": "", "full_name": "John Doe"}, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, ) ticket_details = TicketNeedsAdjudicationDetailsFactory( golden_records_individual=individuals_1[0], @@ -282,7 +370,10 @@ def test_validate_all_individuals_before_close_needs_adjudication(self) -> None: with pytest.raises(ValidationError) as e: validate_all_individuals_before_close_needs_adjudication(ticket_details) - assert str(e.value) == "Close ticket is not possible when all Individuals are flagged as duplicates" + assert ( + str(e.value) + == "Close ticket is not possible when all Individuals are flagged as duplicates" + ) with pytest.raises(ValidationError) as e: validate_all_individuals_before_close_needs_adjudication(ticket_details) @@ -295,7 +386,10 @@ def test_validate_all_individuals_before_close_needs_adjudication(self) -> None: ticket_details.selected_distinct.add(individuals_2[0]) ticket_details.save() validate_all_individuals_before_close_needs_adjudication(ticket_details) - assert str(e.value) == "Close ticket is possible when all active Individuals are flagged" + assert ( + str(e.value) + == "Close ticket is possible when all active Individuals are flagged" + ) ticket_details.selected_individuals.add(individuals_1[0]) validate_all_individuals_before_close_needs_adjudication(ticket_details) @@ -314,11 +408,21 @@ def test_close_needs_adjudication_ticket_service(self) -> None: grievance.programs.add(program) _, individuals_1 = create_household( {"size": 2, "business_area": ba, "program": program}, - {"given_name": "John", "family_name": "Doe", "middle_name": "", "full_name": "John Doe"}, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, ) _, individuals_2 = create_household( {"size": 1, "business_area": ba, "program": program}, - {"given_name": "John", "family_name": "Doe", "middle_name": "", "full_name": "John Doe"}, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, ) ind_1 = individuals_1[0] ind_2 = individuals_2[0] @@ -357,11 +461,21 @@ def test_close_needs_adjudication_ticket_service_when_just_duplicates(self) -> N grievance.programs.add(program) _, individuals_1 = create_household( {"size": 2, "business_area": ba, "program": program}, - {"given_name": "John", "family_name": "Doe", "middle_name": "", "full_name": "John Doe"}, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, ) _, individuals_2 = create_household( {"size": 1, "business_area": ba, "program": program}, - {"given_name": "John", "family_name": "Doe", "middle_name": "", "full_name": "John Doe"}, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, ) ind_1 = individuals_1[0] ind_2 = individuals_2[0] @@ -381,7 +495,10 @@ def test_close_needs_adjudication_ticket_service_when_just_duplicates(self) -> N with pytest.raises(ValidationError) as e: close_needs_adjudication_ticket_service(grievance, user) - assert str(e.value) == "Close ticket is not possible when all Individuals are flagged as duplicates" + assert ( + str(e.value) + == "Close ticket is not possible when all Individuals are flagged as duplicates" + ) gr = GrievanceTicketFactory( category=GrievanceTicket.CATEGORY_NEEDS_ADJUDICATION, @@ -401,3 +518,76 @@ def test_close_needs_adjudication_ticket_service_when_just_duplicates(self) -> N ticket_details_2.save() close_needs_adjudication_ticket_service(gr, user) + + @patch.dict( + "os.environ", + { + "DEDUPLICATION_ENGINE_API_KEY": "dedup_api_key", + "DEDUPLICATION_ENGINE_API_URL": "http://dedup-fake-url.com", + }, + ) + @patch( + "hct_mis_api.apps.registration_datahub.services.biometric_deduplication.BiometricDeduplicationService.report_false_positive_duplicate" + ) + def test_close_needs_adjudication_ticket_service_for_biometrics( + self, report_false_positive_duplicate_mock + ) -> None: + user = UserFactory() + ba = BusinessAreaFactory(slug="afghanistan") + program = ProgramFactory(business_area=ba) + program.deduplication_set_id = uuid.uuid4() + program.save() + + hh1, individuals_1 = create_household( + {"size": 2, "business_area": ba, "program": program}, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, + ) + hh2, individuals_2 = create_household( + {"size": 2, "business_area": ba, "program": program}, + { + "given_name": "John", + "family_name": "Doe", + "middle_name": "", + "full_name": "John Doe", + }, + ) + ind_1, ind_2 = sorted([individuals_1[1], individuals_2[1]], key=lambda x: x.id) + + ticket, ticket_details = create_grievance_ticket_with_details( + main_individual=ind_1, + possible_duplicate=ind_2, + business_area=ba, + registration_data_import=hh1.registration_data_import, + possible_duplicates=[ind_2], + is_multiple_duplicates_version=True, + issue_type=GrievanceTicket.ISSUE_TYPE_BIOMETRICS_SIMILARITY, + dedup_engine_similarity_pair=DeduplicationEngineSimilarityPair.objects.create( + program=program, + individual1=ind_1, + individual2=ind_2, + similarity_score=90.55, + ), + ) + + ticket_details.selected_distinct.set([ind_1]) + ticket_details.selected_individuals.set([ind_2]) + ticket_details.save() + + close_needs_adjudication_ticket_service(ticket, user) + report_false_positive_duplicate_mock.assert_not_called() + + ticket_details.selected_distinct.set([ind_1, ind_2]) + ticket_details.selected_individuals.set([]) + ticket_details.save() + + close_needs_adjudication_ticket_service(ticket, user) + report_false_positive_duplicate_mock.assert_called_once_with( + str(ind_1.id), + str(ind_2.id), + ticket_details.ticket.registration_data_import.program.deduplication_set_id, + ) diff --git a/tests/unit/apps/program/snapshots/snap_test_all_programs_query.py b/tests/unit/apps/program/snapshots/snap_test_all_programs_query.py index ef739ab76a..385e7f300f 100644 --- a/tests/unit/apps/program/snapshots/snap_test_all_programs_query.py +++ b/tests/unit/apps/program/snapshots/snap_test_all_programs_query.py @@ -279,11 +279,19 @@ snapshots['TestAllProgramsQuery::test_program_can_run_deduplication_and_is_deduplication_disabled 1'] = { 'data': { 'canRunDeduplication': True, - 'isDeduplicationDisabled': False + 'isDeduplicationDisabled': True } } snapshots['TestAllProgramsQuery::test_program_can_run_deduplication_and_is_deduplication_disabled 2'] = { + 'data': { + 'canRunDeduplication': True, + 'isDeduplicationDisabled': False + } +} + + +snapshots['TestAllProgramsQuery::test_program_can_run_deduplication_and_is_deduplication_disabled 3'] = { 'data': { 'canRunDeduplication': True, 'isDeduplicationDisabled': True diff --git a/tests/unit/apps/program/test_all_programs_query.py b/tests/unit/apps/program/test_all_programs_query.py index d5a780729d..54e34b943e 100644 --- a/tests/unit/apps/program/test_all_programs_query.py +++ b/tests/unit/apps/program/test_all_programs_query.py @@ -310,6 +310,27 @@ def test_program_can_run_deduplication_and_is_deduplication_disabled(self) -> No }, variables={}, ) + RegistrationDataImportFactory( + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING, + data_source=RegistrationDataImport.XLS, + program=program1, + ) + self.snapshot_graphql_request( + request_string=""" + query canRunDeduplicationAndIsDeduplicationDisabled { + canRunDeduplication + isDeduplicationDisabled + } + """, + context={ + "user": self.user, + "headers": { + "Business-Area": self.business_area.slug, + "Program": self.id_to_base64(program1.id, "ProgramNode"), + }, + }, + variables={}, + ) RegistrationDataImportFactory( deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, data_source=RegistrationDataImport.XLS, diff --git a/tests/unit/apps/registration_data/snapshots/snap_test_registration_data_import_query.py b/tests/unit/apps/registration_data/snapshots/snap_test_registration_data_import_query.py index eb00215d89..5c7412311b 100644 --- a/tests/unit/apps/registration_data/snapshots/snap_test_registration_data_import_query.py +++ b/tests/unit/apps/registration_data/snapshots/snap_test_registration_data_import_query.py @@ -46,7 +46,7 @@ 'percentage': 0.0 } ], - 'isDeduplicated': 'NO', + 'biometricDeduplicated': 'NO', 'name': 'Lorem Ipsum 4', 'numberOfHouseholds': 184, 'numberOfIndividuals': 423, @@ -105,7 +105,7 @@ 'percentage': 100.0 } ], - 'isDeduplicated': 'NO', + 'biometricDeduplicated': 'NO', 'name': 'Lorem Ipsum 3', 'numberOfHouseholds': 154, 'numberOfIndividuals': 323, @@ -164,7 +164,7 @@ 'percentage': 100.0 } ], - 'isDeduplicated': 'YES', + 'biometricDeduplicated': 'YES', 'name': 'Lorem Ipsum 2', 'numberOfHouseholds': 154, 'numberOfIndividuals': 323, @@ -207,7 +207,7 @@ 'percentage': 0.0 } ], - 'isDeduplicated': 'NO', + 'biometricDeduplicated': 'NO', 'name': 'Lorem Ipsum', 'numberOfHouseholds': 54, 'numberOfIndividuals': 123, diff --git a/tests/unit/apps/registration_data/test_models.py b/tests/unit/apps/registration_data/test_models.py index 7e989f5dad..696822d28e 100644 --- a/tests/unit/apps/registration_data/test_models.py +++ b/tests/unit/apps/registration_data/test_models.py @@ -145,34 +145,3 @@ def test_linked_rdi(self) -> None: self.rdi, ) - -class TestDeduplicationEngineSimilarityPair(TestCase): - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls.ba = create_afghanistan() - cls.program = ProgramFactory(business_area=cls.ba, status=Program.ACTIVE) - - def test_is_duplicate(self) -> None: - self.ba.biometric_deduplication_threshold = 50.55 - self.ba.save() - ind1, ind2 = sorted([IndividualFactory(), IndividualFactory()], key=lambda x: x.id) - ind3, ind4 = sorted([IndividualFactory(), IndividualFactory()], key=lambda x: x.id) - - desp1 = DeduplicationEngineSimilarityPair.objects.create( - program=self.program, - individual1=ind1, - individual2=ind2, - similarity_score=90.55, - ) - desp2 = DeduplicationEngineSimilarityPair.objects.create( - program=self.program, - individual1=ind3, - individual2=ind4, - similarity_score=40.55, - ) - - self.assertEqual(desp1._is_duplicate, True) - self.assertEqual(desp2._is_duplicate, False) - self.assertEqual(DeduplicationEngineSimilarityPair.objects.duplicates().count(), 1) - self.assertEqual(DeduplicationEngineSimilarityPair.objects.duplicates().first(), desp1) diff --git a/tests/unit/apps/registration_data/test_registration_data_import_query.py b/tests/unit/apps/registration_data/test_registration_data_import_query.py index 1d60196ee3..4fa2daf7d7 100644 --- a/tests/unit/apps/registration_data/test_registration_data_import_query.py +++ b/tests/unit/apps/registration_data/test_registration_data_import_query.py @@ -46,7 +46,7 @@ class TestRegistrationDataImportQuery(APITestCase): percentage } totalHouseholdsCountWithValidPhoneNo - isDeduplicated + biometricDeduplicated canMerge biometricDeduplicationEnabled } diff --git a/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py b/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py index a0eb301f6e..1bd2e2ae48 100644 --- a/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py +++ b/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py @@ -24,6 +24,8 @@ DeduplicationSet, DeduplicationSetData, SimilarityPair, + DeduplicationSetConfig, + IgnoredKeysPair, ) from hct_mis_api.apps.registration_datahub.services.biometric_deduplication import ( BiometricDeduplicationService, @@ -33,7 +35,13 @@ @pytest.fixture(autouse=True) def mock_deduplication_engine_env_vars() -> None: - with mock.patch.dict(os.environ, {"DEDUPLICATION_ENGINE_API_KEY": "TEST", "DEDUPLICATION_ENGINE_API_URL": "TEST"}): + with mock.patch.dict( + os.environ, + { + "DEDUPLICATION_ENGINE_API_KEY": "TEST", + "DEDUPLICATION_ENGINE_API_URL": "TEST", + }, + ): yield @@ -48,7 +56,9 @@ def setUpTestData(cls) -> None: @patch( "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.create_deduplication_set" ) - def test_create_deduplication_set(self, mock_create_deduplication_set: mock.Mock) -> None: + def test_create_deduplication_set( + self, mock_create_deduplication_set: mock.Mock + ) -> None: service = BiometricDeduplicationService() new_uuid = str(uuid.uuid4()) @@ -62,11 +72,19 @@ def test_create_deduplication_set(self, mock_create_deduplication_set: mock.Mock DeduplicationSet( reference_pk=str(self.program.id), notification_url=f"https://{settings.DOMAIN_NAME}/api/rest/{self.program.business_area.slug}/programs/{str(self.program.id)}/registration-data/webhookdeduplication/", + config=DeduplicationSetConfig( + face_distance_threshold=self.program.business_area.biometric_deduplication_threshold + / 100 + ), ) ) - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.get_duplicates") - def test_get_deduplication_set_results(self, mock_get_duplicates: mock.Mock) -> None: + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.get_duplicates" + ) + def test_get_deduplication_set_results( + self, mock_get_duplicates: mock.Mock + ) -> None: service = BiometricDeduplicationService() deduplication_set_id = str(uuid.uuid4()) @@ -84,34 +102,65 @@ def test_get_deduplication_set(self, mock_get_deduplication_set: mock.Mock) -> N mock_get_deduplication_set.return_value = dict(state="Clean", error=None) data = service.get_deduplication_set(deduplication_set_id) - self.assertEqual(data, DeduplicationSetData(state="Clean", error=None)) + self.assertEqual(data, DeduplicationSetData(state="Clean")) mock_get_deduplication_set.assert_called_once_with(deduplication_set_id) - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images") - def test_upload_individuals_success(self, mock_bulk_upload_images: mock.Mock) -> None: + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images" + ) + def test_upload_individuals_success( + self, mock_bulk_upload_images: mock.Mock + ) -> None: self.program.deduplication_set_id = uuid.uuid4() self.program.save() + rdi = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING, + ) + individual = IndividualFactory( + registration_data_import=rdi, photo="some_photo.jpg" ) - individual = IndividualFactory(registration_data_import=rdi, photo="some_photo.jpg") service = BiometricDeduplicationService() service.upload_individuals(str(self.program.deduplication_set_id), rdi) mock_bulk_upload_images.assert_called_once_with( str(self.program.deduplication_set_id), - [DeduplicationImage(reference_pk=str(individual.id), filename="some_photo.jpg")], + [ + DeduplicationImage( + reference_pk=str(individual.id), filename="some_photo.jpg" + ) + ], + ) + + rdi.refresh_from_db() + assert ( + rdi.deduplication_engine_status + == RegistrationDataImport.DEDUP_ENGINE_UPLOADED ) + rdi.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_ERROR + rdi.save() + service.upload_individuals(str(self.program.deduplication_set_id), rdi) rdi.refresh_from_db() - assert rdi.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_UPLOADED + assert ( + rdi.deduplication_engine_status + == RegistrationDataImport.DEDUP_ENGINE_UPLOADED + ) - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images") - def test_upload_individuals_failure(self, mock_bulk_upload_images: mock.Mock) -> None: - mock_bulk_upload_images.side_effect = DeduplicationEngineAPI.DeduplicationEngineAPIException + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images" + ) + def test_upload_individuals_failure( + self, mock_bulk_upload_images: mock.Mock + ) -> None: + mock_bulk_upload_images.side_effect = ( + DeduplicationEngineAPI.DeduplicationEngineAPIException + ) rdi = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING, ) IndividualFactory(registration_data_import=rdi, photo="some_photo.jpg") @@ -119,52 +168,80 @@ def test_upload_individuals_failure(self, mock_bulk_upload_images: mock.Mock) -> service.upload_individuals(str(self.program.deduplication_set_id), rdi) rdi.refresh_from_db() - assert rdi.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_UPLOAD_ERROR + assert ( + rdi.deduplication_engine_status + == RegistrationDataImport.DEDUP_ENGINE_UPLOAD_ERROR + ) def test_upload_individuals_no_individuals(self) -> None: rdi = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING, + ) + IndividualFactory( + registration_data_import=rdi, photo="some_photo.jpg", withdrawn=True ) - IndividualFactory(registration_data_import=rdi, photo="some_photo.jpg", withdrawn=True) service = BiometricDeduplicationService() service.upload_individuals(str(self.program.deduplication_set_id), rdi) rdi.refresh_from_db() - assert rdi.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_FINISHED + assert ( + rdi.deduplication_engine_status + == RegistrationDataImport.DEDUP_ENGINE_FINISHED + ) @patch( "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.process_deduplication" ) - def test_process_deduplication_set(self, mock_process_deduplication: mock.Mock) -> None: + def test_process_deduplication_set( + self, mock_process_deduplication: mock.Mock + ) -> None: self.program.deduplication_set_id = uuid.uuid4() self.program.save() rdi = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING, ) service = BiometricDeduplicationService() mock_process_deduplication.return_value = ({}, 200) - service.process_deduplication_set(str(self.program.deduplication_set_id), RegistrationDataImport.objects.all()) - mock_process_deduplication.assert_called_once_with(str(self.program.deduplication_set_id)) + service.process_deduplication_set( + str(self.program.deduplication_set_id), RegistrationDataImport.objects.all() + ) + mock_process_deduplication.assert_called_once_with( + str(self.program.deduplication_set_id) + ) rdi.refresh_from_db() - self.assertEqual(rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS) + self.assertEqual( + rdi.deduplication_engine_status, + RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, + ) mock_process_deduplication.return_value = ({}, 409) - with self.assertRaises(BiometricDeduplicationService.BiometricDeduplicationServiceException): + with self.assertRaises( + BiometricDeduplicationService.BiometricDeduplicationServiceException + ): service.process_deduplication_set( - str(self.program.deduplication_set_id), RegistrationDataImport.objects.all() + str(self.program.deduplication_set_id), + RegistrationDataImport.objects.all(), ) mock_process_deduplication.return_value = ({}, 400) - service.process_deduplication_set(str(self.program.deduplication_set_id), RegistrationDataImport.objects.all()) + service.process_deduplication_set( + str(self.program.deduplication_set_id), RegistrationDataImport.objects.all() + ) rdi.refresh_from_db() - self.assertEqual(rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_ERROR) + self.assertEqual( + rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_ERROR + ) @patch( "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.delete_deduplication_set" ) - def test_delete_deduplication_set(self, mock_delete_deduplication_set: mock.Mock) -> None: + def test_delete_deduplication_set( + self, mock_delete_deduplication_set: mock.Mock + ) -> None: service = BiometricDeduplicationService() service.delete_deduplication_set(self.program) @@ -178,7 +255,9 @@ def test_delete_deduplication_set(self, mock_delete_deduplication_set: mock.Mock self.program.refresh_from_db() self.assertIsNone(self.program.deduplication_set_id) - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images") + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images" + ) @patch( "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.process_deduplication" ) @@ -204,13 +283,17 @@ def test_upload_and_process_deduplication_set( self.program.save() rdi_1 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING, ) rdi_2 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, ) IndividualFactory(registration_data_import=rdi_1, photo="some_photo1.jpg") - PendingIndividualFactory(registration_data_import=rdi_2, photo="some_photo2.jpg") + PendingIndividualFactory( + registration_data_import=rdi_2, photo="some_photo2.jpg" + ) service.create_deduplication_set = mock.MagicMock() with self.assertRaisesMessage( @@ -226,14 +309,23 @@ def test_upload_and_process_deduplication_set( rdi_2.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_PENDING rdi_2.save() - mock_bulk_upload_images.side_effect = DeduplicationEngineAPI.DeduplicationEngineAPIException + mock_bulk_upload_images.side_effect = ( + DeduplicationEngineAPI.DeduplicationEngineAPIException + ) with self.assertRaisesMessage( - BiometricDeduplicationService.BiometricDeduplicationServiceException, "Failed to upload images for all RDIs" + BiometricDeduplicationService.BiometricDeduplicationServiceException, + "Failed to upload images for all RDIs", ): service.upload_and_process_deduplication_set(self.program) assert mock_bulk_upload_images.call_count == 2 - assert rdi_1.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_ERROR - assert rdi_1.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_ERROR + assert ( + rdi_1.deduplication_engine_status + == RegistrationDataImport.DEDUP_ENGINE_ERROR + ) + assert ( + rdi_1.deduplication_engine_status + == RegistrationDataImport.DEDUP_ENGINE_ERROR + ) # Test when all rdi images are uploaded successfully mock_bulk_upload_images.reset_mock() @@ -250,8 +342,14 @@ def test_upload_and_process_deduplication_set( assert mock_process_deduplication.call_count == 1 rdi_1.refresh_from_db() rdi_2.refresh_from_db() - assert rdi_1.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS - assert rdi_2.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + assert ( + rdi_1.deduplication_engine_status + == RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + ) + assert ( + rdi_2.deduplication_engine_status + == RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + ) def test_store_results(self) -> None: self.program.deduplication_set_id = uuid.uuid4() @@ -277,7 +375,9 @@ def test_store_results(self) -> None: ), ] - service.store_similarity_pairs(str(self.program.deduplication_set_id), similarity_pairs) + service.store_similarity_pairs( + str(self.program.deduplication_set_id), similarity_pairs + ) assert self.program.deduplication_engine_similarity_pairs.count() == 3 assert self.program.deduplication_engine_similarity_pairs.filter( @@ -296,19 +396,27 @@ def test_mark_rdis_as(self) -> None: self.program.deduplication_set_id = uuid.uuid4() self.program.save() rdi = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, ) service.mark_rdis_as_deduplicated(str(self.program.deduplication_set_id)) rdi.refresh_from_db() - self.assertEqual(rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_FINISHED) + self.assertEqual( + rdi.deduplication_engine_status, + RegistrationDataImport.DEDUP_ENGINE_FINISHED, + ) - rdi.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + rdi.deduplication_engine_status = ( + RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + ) rdi.save() service.mark_rdis_as_deduplication_error(str(self.program.deduplication_set_id)) rdi.refresh_from_db() - self.assertEqual(rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_ERROR) + self.assertEqual( + rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_ERROR + ) def test_get_duplicates_for_rdi_against_population(self) -> None: self.program.deduplication_set_id = uuid.uuid4() @@ -317,13 +425,16 @@ def test_get_duplicates_for_rdi_against_population(self) -> None: self.program.save() rdi1 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) rdi2 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) rdi3 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) individuals = IndividualFactory.create_batch(6, program=self.program) ind1, ind2, ind3, ind4, ind5, ind6 = sorted(individuals, key=lambda x: x.id) @@ -348,26 +459,50 @@ def test_get_duplicates_for_rdi_against_population(self) -> None: service = BiometricDeduplicationService() similarity_pairs = [ SimilarityPair(score=0.9, first=ind1.id, second=ind2.id), # within rdi1 - SimilarityPair(score=0.7, first=ind1.id, second=ind3.id), # across rdi1 and rdi2 - SimilarityPair(score=0.8, first=ind1.id, second=ind5.id), # across rdi1 and population - SimilarityPair(score=0.9, first=ind6.id, second=ind2.id), # across rdi1 and population + SimilarityPair( + score=0.7, first=ind1.id, second=ind3.id + ), # across rdi1 and rdi2 + SimilarityPair( + score=0.8, first=ind1.id, second=ind5.id + ), # across rdi1 and population + SimilarityPair( + score=0.9, first=ind6.id, second=ind2.id + ), # across rdi1 and population SimilarityPair(score=0.9, first=ind3.id, second=ind4.id), # within rdi2 - SimilarityPair(score=0.7, first=ind4.id, second=ind5.id), # across rdi2 and population - SimilarityPair(score=0.8, first=ind5.id, second=ind6.id), # within population + SimilarityPair( + score=0.7, first=ind4.id, second=ind5.id + ), # across rdi2 and population + SimilarityPair( + score=0.8, first=ind5.id, second=ind6.id + ), # within population ] - service.store_similarity_pairs(str(self.program.deduplication_set_id), similarity_pairs) + service.store_similarity_pairs( + str(self.program.deduplication_set_id), similarity_pairs + ) duplicates = service.get_duplicates_for_rdi_against_population(rdi1) assert len(duplicates) == 2 assert list( - duplicates.order_by("similarity_score").values("individual1", "individual2", "similarity_score") + duplicates.order_by("similarity_score").values( + "individual1", "individual2", "similarity_score" + ) ) == [ - {"individual1": ind1.id, "individual2": ind5.id, "similarity_score": Decimal("80.00")}, - {"individual1": ind2.id, "individual2": ind6.id, "similarity_score": Decimal("90.00")}, + { + "individual1": ind1.id, + "individual2": ind5.id, + "similarity_score": Decimal("80.00"), + }, + { + "individual1": ind2.id, + "individual2": ind6.id, + "similarity_score": Decimal("90.00"), + }, ] - duplicate_individuals_count = service.get_duplicate_individuals_for_rdi_against_population_count(rdi1) + duplicate_individuals_count = ( + service.get_duplicate_individuals_for_rdi_against_population_count(rdi1) + ) assert duplicate_individuals_count == 2 def test_get_duplicates_for_merged_rdi_against_population(self) -> None: @@ -377,13 +512,16 @@ def test_get_duplicates_for_merged_rdi_against_population(self) -> None: self.program.save() rdi1 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) rdi2 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) rdi3 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) individuals = IndividualFactory.create_batch(6, program=self.program) ind1, ind2, ind3, ind4, ind5, ind6 = sorted(individuals, key=lambda x: x.id) @@ -407,25 +545,55 @@ def test_get_duplicates_for_merged_rdi_against_population(self) -> None: service = BiometricDeduplicationService() similarity_pairs = [ - SimilarityPair(score=0.7, first=ind1.id, second=ind2.id), # within merged rdi1 - SimilarityPair(score=0.7, first=ind1.id, second=ind3.id), # across merged rdi1 and pending rdi2 - SimilarityPair(score=0.8, first=ind1.id, second=ind5.id), # across merged rdi1 and population - SimilarityPair(score=0.9, first=ind2.id, second=ind6.id), # across merged rdi1 and population - SimilarityPair(score=0.9, first=ind3.id, second=ind4.id), # within pending rdi2 - SimilarityPair(score=0.7, first=ind4.id, second=ind5.id), # across pending rdi2 and population - SimilarityPair(score=0.8, first=ind5.id, second=ind6.id), # within population + SimilarityPair( + score=0.7, first=ind1.id, second=ind2.id + ), # within merged rdi1 + SimilarityPair( + score=0.7, first=ind1.id, second=ind3.id + ), # across merged rdi1 and pending rdi2 + SimilarityPair( + score=0.8, first=ind1.id, second=ind5.id + ), # across merged rdi1 and population + SimilarityPair( + score=0.9, first=ind2.id, second=ind6.id + ), # across merged rdi1 and population + SimilarityPair( + score=0.9, first=ind3.id, second=ind4.id + ), # within pending rdi2 + SimilarityPair( + score=0.7, first=ind4.id, second=ind5.id + ), # across pending rdi2 and population + SimilarityPair( + score=0.8, first=ind5.id, second=ind6.id + ), # within population ] - service.store_similarity_pairs(str(self.program.deduplication_set_id), similarity_pairs) + service.store_similarity_pairs( + str(self.program.deduplication_set_id), similarity_pairs + ) duplicates = service.get_duplicates_for_merged_rdi_against_population(rdi1) assert len(duplicates) == 3 assert list( - duplicates.order_by("similarity_score").values("individual1", "individual2", "similarity_score") + duplicates.order_by("similarity_score").values( + "individual1", "individual2", "similarity_score" + ) ) == [ - {"individual1": ind1.id, "individual2": ind2.id, "similarity_score": Decimal("70.00")}, - {"individual1": ind1.id, "individual2": ind5.id, "similarity_score": Decimal("80.00")}, - {"individual1": ind2.id, "individual2": ind6.id, "similarity_score": Decimal("90.00")}, + { + "individual1": ind1.id, + "individual2": ind2.id, + "similarity_score": Decimal("70.00"), + }, + { + "individual1": ind1.id, + "individual2": ind5.id, + "similarity_score": Decimal("80.00"), + }, + { + "individual1": ind2.id, + "individual2": ind6.id, + "similarity_score": Decimal("90.00"), + }, ] def test_get_duplicates_for_rdi_against_batch(self) -> None: @@ -435,13 +603,16 @@ def test_get_duplicates_for_rdi_against_batch(self) -> None: self.program.save() rdi1 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) rdi2 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) rdi3 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) individuals = IndividualFactory.create_batch(6, program=self.program) ind1, ind2, ind3, ind4, ind5, ind6 = sorted(individuals, key=lambda x: x.id) @@ -466,24 +637,44 @@ def test_get_duplicates_for_rdi_against_batch(self) -> None: service = BiometricDeduplicationService() similarity_pairs = [ SimilarityPair(score=0.9, first=ind1.id, second=ind2.id), # within rdi1 - SimilarityPair(score=0.7, first=ind1.id, second=ind3.id), # across rdi1 and rdi2 - SimilarityPair(score=0.8, first=ind1.id, second=ind5.id), # across rdi1 and population - SimilarityPair(score=0.9, first=ind2.id, second=ind6.id), # across rdi1 and population + SimilarityPair( + score=0.7, first=ind1.id, second=ind3.id + ), # across rdi1 and rdi2 + SimilarityPair( + score=0.8, first=ind1.id, second=ind5.id + ), # across rdi1 and population + SimilarityPair( + score=0.9, first=ind2.id, second=ind6.id + ), # across rdi1 and population SimilarityPair(score=0.9, first=ind3.id, second=ind4.id), # within rdi2 - SimilarityPair(score=0.7, first=ind4.id, second=ind5.id), # across rdi2 and population - SimilarityPair(score=0.8, first=ind5.id, second=ind6.id), # within population + SimilarityPair( + score=0.7, first=ind4.id, second=ind5.id + ), # across rdi2 and population + SimilarityPair( + score=0.8, first=ind5.id, second=ind6.id + ), # within population ] - service.store_similarity_pairs(str(self.program.deduplication_set_id), similarity_pairs) + service.store_similarity_pairs( + str(self.program.deduplication_set_id), similarity_pairs + ) duplicates = service.get_duplicates_for_rdi_against_batch(rdi1) assert len(duplicates) == 1 assert list( - duplicates.order_by("similarity_score").values("individual1", "individual2", "similarity_score") + duplicates.order_by("similarity_score").values( + "individual1", "individual2", "similarity_score" + ) ) == [ - {"individual1": ind1.id, "individual2": ind2.id, "similarity_score": Decimal("90.00")}, + { + "individual1": ind1.id, + "individual2": ind2.id, + "similarity_score": Decimal("90.00"), + }, ] - duplicate_individuals_count = service.get_duplicate_individuals_for_rdi_against_batch_count(rdi1) + duplicate_individuals_count = ( + service.get_duplicate_individuals_for_rdi_against_batch_count(rdi1) + ) assert duplicate_individuals_count == 2 @patch( @@ -493,7 +684,8 @@ def test_create_grievance_tickets_for_duplicates( self, create_needs_adjudication_tickets_for_biometrics_mock: mock.Mock ) -> None: rdi1 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) service = BiometricDeduplicationService() @@ -501,17 +693,29 @@ def test_create_grievance_tickets_for_duplicates( service.get_duplicates_for_merged_rdi_against_population.return_value = [] service.create_grievance_tickets_for_duplicates(rdi1) - create_needs_adjudication_tickets_for_biometrics_mock.assert_called_once_with([], rdi1) + create_needs_adjudication_tickets_for_biometrics_mock.assert_called_once_with( + [], rdi1 + ) def test_fetch_biometric_deduplication_results_and_process_success(self) -> None: deduplication_set_id = str(uuid.uuid4()) service = BiometricDeduplicationService() - service.get_deduplication_set = mock.Mock(return_value=DeduplicationSetData(state="Clean", error=None)) + service.get_deduplication_set = mock.Mock( + return_value=DeduplicationSetData(state="Clean") + ) results_data = [ - {"first": {"reference_pk": "1"}, "second": {"reference_pk": "2"}, "score": 0.9}, - {"first": {"reference_pk": "3"}, "second": {"reference_pk": "4"}, "score": 0.8}, + { + "first": {"reference_pk": "1"}, + "second": {"reference_pk": "2"}, + "score": 0.9, + }, + { + "first": {"reference_pk": "3"}, + "second": {"reference_pk": "4"}, + "score": 0.8, + }, ] service.get_deduplication_set_results = mock.Mock(return_value=results_data) service.store_similarity_pairs = mock.Mock() @@ -521,7 +725,9 @@ def test_fetch_biometric_deduplication_results_and_process_success(self) -> None service.fetch_biometric_deduplication_results_and_process(deduplication_set_id) service.get_deduplication_set.assert_called_once_with(deduplication_set_id) - service.get_deduplication_set_results.assert_called_once_with(deduplication_set_id) + service.get_deduplication_set_results.assert_called_once_with( + deduplication_set_id + ) service.store_similarity_pairs.assert_called_once_with( deduplication_set_id, [ @@ -538,13 +744,17 @@ def test_fetch_biometric_deduplication_results_and_process_fail(self) -> None: deduplication_set_id = str(uuid.uuid4()) service = BiometricDeduplicationService() - service.get_deduplication_set = mock.Mock(return_value=DeduplicationSetData(state="Error", error="error")) + service.get_deduplication_set = mock.Mock( + return_value=DeduplicationSetData(state="Error") + ) service.mark_rdis_as_deduplication_error = mock.Mock() service.fetch_biometric_deduplication_results_and_process(deduplication_set_id) service.get_deduplication_set.assert_called_once_with(deduplication_set_id) - service.mark_rdis_as_deduplication_error.assert_called_once_with(deduplication_set_id) + service.mark_rdis_as_deduplication_error.assert_called_once_with( + deduplication_set_id + ) def test_store_rdis_deduplication_statistics(self) -> None: deduplication_set_id = str(uuid.uuid4()) @@ -554,16 +764,25 @@ def test_store_rdis_deduplication_statistics(self) -> None: service = BiometricDeduplicationService() rdi1 = RegistrationDataImportFactory( - program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + program=self.program, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, ) - service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock(return_value=8) - service.get_duplicate_individuals_for_rdi_against_population_count = mock.Mock(return_value=9) + service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock( + return_value=8 + ) + service.get_duplicate_individuals_for_rdi_against_population_count = mock.Mock( + return_value=9 + ) service.store_rdis_deduplication_statistics(deduplication_set_id) - service.get_duplicate_individuals_for_rdi_against_batch_count.assert_called_once_with(rdi1) - service.get_duplicate_individuals_for_rdi_against_population_count.assert_called_once_with(rdi1) + service.get_duplicate_individuals_for_rdi_against_batch_count.assert_called_once_with( + rdi1 + ) + service.get_duplicate_individuals_for_rdi_against_population_count.assert_called_once_with( + rdi1 + ) rdi1.refresh_from_db() assert rdi1.dedup_engine_batch_duplicates == 8 @@ -582,14 +801,37 @@ def test_update_rdis_deduplication_statistics(self) -> None: status=RegistrationDataImport.IN_REVIEW, ) - service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock(return_value=8) - service.get_duplicate_individuals_for_rdi_against_population_count = mock.Mock(return_value=9) + service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock( + return_value=8 + ) + service.get_duplicate_individuals_for_rdi_against_population_count = mock.Mock( + return_value=9 + ) service.update_rdis_deduplication_statistics(self.program.id) - service.get_duplicate_individuals_for_rdi_against_batch_count.assert_called_once_with(rdi1) - service.get_duplicate_individuals_for_rdi_against_population_count.assert_called_once_with(rdi1) + service.get_duplicate_individuals_for_rdi_against_batch_count.assert_called_once_with( + rdi1 + ) + service.get_duplicate_individuals_for_rdi_against_population_count.assert_called_once_with( + rdi1 + ) rdi1.refresh_from_db() assert rdi1.dedup_engine_batch_duplicates == 8 assert rdi1.dedup_engine_golden_record_duplicates == 9 + + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.report_false_positive_duplicate" + ) + def test_report_false_positive_duplicate( + self, mock_report_false_positive_duplicate: mock.Mock + ) -> None: + service = BiometricDeduplicationService() + deduplication_set_id = uuid.uuid4() + + service.report_false_positive_duplicate("123", "456", str(deduplication_set_id)) + mock_report_false_positive_duplicate.assert_called_once_with( + IgnoredKeysPair(first_reference_pk="123", second_reference_pk="456"), + str(deduplication_set_id), + ) diff --git a/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py b/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py index cd2cf87a70..db631e2bd5 100644 --- a/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py +++ b/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py @@ -12,17 +12,27 @@ DeduplicationEngineAPI, DeduplicationImage, DeduplicationSet, + DeduplicationSetConfig, + IgnoredKeysPair, ) @pytest.fixture(autouse=True) def mock_deduplication_engine_env_vars() -> None: - with mock.patch.dict(os.environ, {"DEDUPLICATION_ENGINE_API_KEY": "TEST", "DEDUPLICATION_ENGINE_API_URL": "TEST"}): + with mock.patch.dict( + os.environ, + { + "DEDUPLICATION_ENGINE_API_KEY": "TEST", + "DEDUPLICATION_ENGINE_API_URL": "TEST", + }, + ): yield class DeduplicationEngineApiTest(TestCase): - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._delete") + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._delete" + ) def test_delete_deduplication_set(self, mock_delete: mock.Mock) -> None: api = DeduplicationEngineAPI() @@ -31,23 +41,32 @@ def test_delete_deduplication_set(self, mock_delete: mock.Mock) -> None: api.delete_deduplication_set(deduplication_set_id) - mock_delete.assert_called_once_with(f"deduplication_sets/{deduplication_set_id}/") + mock_delete.assert_called_once_with( + f"deduplication_sets/{deduplication_set_id}/" + ) - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post") + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post" + ) def test_create_deduplication_set(self, mock_post: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set = DeduplicationSet( reference_pk=str(uuid.uuid4()), notification_url="http://test.com", + config=DeduplicationSetConfig(face_distance_threshold=0.5), ) mock_post.return_value = {}, 200 api.create_deduplication_set(deduplication_set) + print(dataclasses.asdict(deduplication_set)) + mock_post.assert_called_once_with( + "deduplication_sets/", dataclasses.asdict(deduplication_set) + ) - mock_post.assert_called_once_with("deduplication_sets/", dataclasses.asdict(deduplication_set)) - - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._get") + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._get" + ) def test_get_deduplication_set(self, get_mock: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -57,7 +76,9 @@ def test_get_deduplication_set(self, get_mock: mock.Mock) -> None: get_mock.assert_called_once_with(f"deduplication_sets/{deduplication_set_id}/") - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post") + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post" + ) def test_bulk_upload_images(self, mock_post: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -77,7 +98,9 @@ def test_bulk_upload_images(self, mock_post: mock.Mock) -> None: [dataclasses.asdict(image) for image in images], ) - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._delete") + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._delete" + ) def test_bulk_delete_images(self, mock_delete: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -85,9 +108,13 @@ def test_bulk_delete_images(self, mock_delete: mock.Mock) -> None: api.bulk_delete_images(deduplication_set_id) - mock_delete.assert_called_once_with(f"deduplication_sets/{deduplication_set_id}/images_bulk/") + mock_delete.assert_called_once_with( + f"deduplication_sets/{deduplication_set_id}/images_bulk/" + ) - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._get") + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._get" + ) def test_get_duplicates(self, get_mock: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -95,9 +122,13 @@ def test_get_duplicates(self, get_mock: mock.Mock) -> None: api.get_duplicates(deduplication_set_id) - get_mock.assert_called_once_with(f"deduplication_sets/{deduplication_set_id}/duplicates/") + get_mock.assert_called_once_with( + f"deduplication_sets/{deduplication_set_id}/duplicates/" + ) - @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post") + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post" + ) def test_process_deduplication(self, post_mock: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -106,5 +137,27 @@ def test_process_deduplication(self, post_mock: mock.Mock) -> None: api.process_deduplication(deduplication_set_id) post_mock.assert_called_once_with( - f"deduplication_sets/{deduplication_set_id}/process/", validate_response=False + f"deduplication_sets/{deduplication_set_id}/process/", + validate_response=False, + ) + + @patch( + "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post" + ) + def test_report_false_positive_duplicate(self, post_mock: mock.Mock) -> None: + api = DeduplicationEngineAPI() + deduplication_set_id = str(uuid.uuid4()) + post_mock.return_value = {}, 200 + + api.report_false_positive_duplicate( + IgnoredKeysPair(first_reference_pk="123", second_reference_pk="456"), + deduplication_set_id, + ) + + post_mock.assert_called_once_with( + f"deduplication_sets/{deduplication_set_id}/ignored_keys/", + { + "first_reference_pk": "123", + "second_reference_pk": "456", + }, ) From 9c6f9979e535b47fa8df6f87a04dbb6a4ee33ada Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 26 Sep 2024 22:07:28 +0200 Subject: [PATCH 021/202] e2e edjustment + increase retry --- .../programme_management/test_programme_management.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index 04404d6b9a..2e7db7e4a4 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -834,6 +834,8 @@ def test_programme_partners( options = select_options_container.find_elements(By.TAG_NAME, "li") assert any("Test Partner 1" == li.text for li in options) is True assert any("Test Partner 2" == li.text for li in options) is False + assert any("UNHCR" == li.text for li in options) is True + assert any("TEST" == li.text for li in options) is True pageProgrammeManagement.driver.find_element(By.CSS_SELECTOR, "body").click() @@ -863,14 +865,16 @@ def test_programme_partners( pageProgrammeManagement.getButtonSave().click() # Check Details page - assert "details" in pageProgrammeDetails.wait_for_new_url(programme_edit_url).split("/") + assert "details" in pageProgrammeDetails.wait_for_new_url(programme_edit_url, 20).split("/") partner_name_elements_new = pageProgrammeManagement.driver.find_elements( By.CSS_SELECTOR, "[data-cy='label-partner-name']" ) - assert len(partner_name_elements_new) == 2 + assert len(partner_name_elements_new) == 3 + pageProgrammeDetails.screenshot("partner_name_elements_new.png") assert any("UNHCR" in partner.text.strip() for partner in partner_name_elements_new) assert any("Test Partner 1" in partner.text.strip() for partner in partner_name_elements_new) + assert any("TEST" in partner.text.strip() for partner in partner_name_elements_new) @pytest.mark.parametrize( "test_data", From 1b174af7c996440c95e806fdf47dc2a1d03bb2af Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 26 Sep 2024 22:22:56 +0200 Subject: [PATCH 022/202] resolve migration conflict --- .../payment/migrations/{0144_migration.py => 0145_migration.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/hct_mis_api/apps/payment/migrations/{0144_migration.py => 0145_migration.py} (91%) diff --git a/src/hct_mis_api/apps/payment/migrations/0144_migration.py b/src/hct_mis_api/apps/payment/migrations/0145_migration.py similarity index 91% rename from src/hct_mis_api/apps/payment/migrations/0144_migration.py rename to src/hct_mis_api/apps/payment/migrations/0145_migration.py index 2ee611dd82..7749049ce4 100644 --- a/src/hct_mis_api/apps/payment/migrations/0144_migration.py +++ b/src/hct_mis_api/apps/payment/migrations/0145_migration.py @@ -7,7 +7,7 @@ class Migration(migrations.Migration): dependencies = [ ('core', '0087_migration'), - ('payment', '0143_migration'), + ('payment', '0144_migration'), ] operations = [ From 03e4560ae7ae47380eadbcdb4695689e5db46303 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 26 Sep 2024 22:37:12 +0200 Subject: [PATCH 023/202] add compose file for running selenium locally, adjust readme --- development_tools/compose.selenium.local.yml | 52 ++++++++++++++++++++ tests/selenium/README.md | 8 +-- 2 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 development_tools/compose.selenium.local.yml diff --git a/development_tools/compose.selenium.local.yml b/development_tools/compose.selenium.local.yml new file mode 100644 index 0000000000..38e8c103fe --- /dev/null +++ b/development_tools/compose.selenium.local.yml @@ -0,0 +1,52 @@ +version: "3.7" + +volumes: + backend-data-selenium: + backend-web-app-selenium: + db-selenium-data: + data_es-selenium: + +services: + redis: + restart: always + image: redis:4.0.11-alpine3.8 + ports: + - 6379:6379 + + db_selenium: + image: kartoza/postgis:14-3 + volumes: + - db-selenium-data:/var/lib/postgresql/data + - ./postgres/init:/docker-entrypoint-initdb.d + environment: + - POSTGRES_MULTIPLE_DATABASES=unicef_hct_mis_cashassist,rdi_datahub,mis_datahub,erp_datahub,ca_datahub + - POSTGRES_DB=postgres + - POSTGRES_USER=postgres + - POSTGRES_PASS=postgres + - PGUSER=postgres + - POSTGRES_HOST_AUTH_METHOD=trust + - POSTGRES_SSL_MODE=off + ports: + - 5432:5432 + + elasticsearch: + image: unicef/hct-elasticsearch + container_name: elasticsearch + build: + context: elasticsearch + dockerfile: Dockerfile + environment: + - node.name=es01 + - cluster.name=es-docker-cluster + - cluster.initial_master_nodes=es01 + - bootstrap.memory_lock=true + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - xpack.security.enabled=false + ulimits: + memlock: + soft: -1 + hard: -1 + volumes: + - data_es-selenium:/usr/share/elasticsearch/data + ports: + - 9200:9200 diff --git a/tests/selenium/README.md b/tests/selenium/README.md index 04c074b99c..6f972b19c2 100644 --- a/tests/selenium/README.md +++ b/tests/selenium/README.md @@ -44,11 +44,11 @@ brew install wkhtmltopdf pango postgis gdal <b><h3>Start tests:</h3></b> ```bash pyenv activate new-venv -cd backend -source ../local_selenium_env.sh +cd src +source ../development_tools/local_selenium_env.sh # second tab: -docker compose -f compose.selenium.local.yml up --build +docker compose -f ../development_tools/compose.selenium.local.yml up --build # first tab: -python -m pytest -svvv selenium_tests --html-report=./report/report.html +python -m pytest -svvv ../tests/selenium --html-report=../tests/selenium/report/report.html ``` \ No newline at end of file From be53494a45ec4bcdce484e49ef83187f153f4f67 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 26 Sep 2024 23:48:29 +0200 Subject: [PATCH 024/202] try --- .../programme_management/test_programme_management.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index 2e7db7e4a4..f3a36d6c8c 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -1,5 +1,6 @@ import random from datetime import datetime +from time import sleep from django.conf import settings from django.core.management import call_command @@ -865,6 +866,9 @@ def test_programme_partners( pageProgrammeManagement.getButtonSave().click() # Check Details page + sleep(20) + new_url = pageProgrammeDetails.wait_for_new_url(programme_edit_url, 20) + assert new_url == programme_edit_url assert "details" in pageProgrammeDetails.wait_for_new_url(programme_edit_url, 20).split("/") partner_name_elements_new = pageProgrammeManagement.driver.find_elements( From 2f5c913b9aaceb7e2d33b354001cb030a214ca40 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Fri, 27 Sep 2024 00:55:57 +0200 Subject: [PATCH 025/202] trial --- tests/selenium/programme_management/test_programme_management.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index f3a36d6c8c..421620b8a2 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -864,6 +864,7 @@ def test_programme_partners( programme_edit_url = pageProgrammeManagement.driver.current_url pageProgrammeManagement.getButtonSave().click() + pageProgrammeManagement.getButtonSave().click() # Check Details page sleep(20) From f8e2bab1471e4a8816293222c08af62cc2d6c556 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Fri, 27 Sep 2024 02:34:56 +0200 Subject: [PATCH 026/202] try --- .../programme_management/test_programme_management.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index 421620b8a2..5e213fbd74 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -859,12 +859,17 @@ def test_programme_partners( # edit program pageProgrammeManagement.getButtonEditProgram().click() pageProgrammeManagement.getSelectEditProgramPartners().click() + pageProgrammeManagement.getAccessToProgram().click() pageProgrammeManagement.selectWhoAccessToProgram("All Current Partners within the business area") + sleep(10) + + pageProgrammeManagement.wait_for(pageProgrammeManagement.inputPartner).click() + pageProgrammeManagement.choosePartnerOption("UNHCR") + programme_edit_url = pageProgrammeManagement.driver.current_url pageProgrammeManagement.getButtonSave().click() - pageProgrammeManagement.getButtonSave().click() # Check Details page sleep(20) From 5f6d67c28aee4dcec6d56f8ba882af4b2e018757 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Fri, 27 Sep 2024 03:23:17 +0200 Subject: [PATCH 027/202] ? --- .../programme_management/test_programme_management.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index 5e213fbd74..2efda1a430 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -863,18 +863,13 @@ def test_programme_partners( pageProgrammeManagement.getAccessToProgram().click() pageProgrammeManagement.selectWhoAccessToProgram("All Current Partners within the business area") - sleep(10) - - pageProgrammeManagement.wait_for(pageProgrammeManagement.inputPartner).click() - pageProgrammeManagement.choosePartnerOption("UNHCR") - programme_edit_url = pageProgrammeManagement.driver.current_url pageProgrammeManagement.getButtonSave().click() # Check Details page - sleep(20) + sleep(10) new_url = pageProgrammeDetails.wait_for_new_url(programme_edit_url, 20) - assert new_url == programme_edit_url + assert new_url != programme_edit_url assert "details" in pageProgrammeDetails.wait_for_new_url(programme_edit_url, 20).split("/") partner_name_elements_new = pageProgrammeManagement.driver.find_elements( From 614b3adcad8a9b7415b52542621d8b058b7c11bc Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Fri, 27 Sep 2024 04:18:02 +0200 Subject: [PATCH 028/202] ok --- .../selenium/programme_management/test_programme_management.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index 2efda1a430..ae26036093 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -868,8 +868,6 @@ def test_programme_partners( # Check Details page sleep(10) - new_url = pageProgrammeDetails.wait_for_new_url(programme_edit_url, 20) - assert new_url != programme_edit_url assert "details" in pageProgrammeDetails.wait_for_new_url(programme_edit_url, 20).split("/") partner_name_elements_new = pageProgrammeManagement.driver.find_elements( From c3af528aa7d32c39a9b3a40adeefa2b177e6d4bf Mon Sep 17 00:00:00 2001 From: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> Date: Fri, 27 Sep 2024 11:41:01 +0200 Subject: [PATCH 029/202] hotfix_add_financial_payment_provider_inline_validation (#4193) --- src/hct_mis_api/apps/payment/admin.py | 39 ++++ src/hct_mis_api/apps/payment/models.py | 17 -- .../apps/grievance/test_model_validation.py | 91 ++++++--- tests/unit/apps/payment/test_models1.py | 186 +++++++++++++----- 4 files changed, 244 insertions(+), 89 deletions(-) diff --git a/src/hct_mis_api/apps/payment/admin.py b/src/hct_mis_api/apps/payment/admin.py index 9cfada2691..743e8882d0 100644 --- a/src/hct_mis_api/apps/payment/admin.py +++ b/src/hct_mis_api/apps/payment/admin.py @@ -348,12 +348,50 @@ def has_add_permission(self, request: HttpRequest) -> bool: return request.user.can_change_fsp() +class FspXlsxTemplatePerDeliveryMechanismForm(forms.ModelForm): + class Meta: + model = FspXlsxTemplatePerDeliveryMechanism + fields = ("financial_service_provider", "delivery_mechanism", "xlsx_template") + + def clean(self) -> Optional[Dict[str, Any]]: + cleaned_data = super().clean() + delivery_mechanism = cleaned_data.get("delivery_mechanism") + financial_service_provider = cleaned_data.get("financial_service_provider") + xlsx_template = cleaned_data.get("xlsx_template") + + if not delivery_mechanism or not financial_service_provider: + return cleaned_data + + missing_required_core_fields = [ + required_field + for required_field in delivery_mechanism.required_fields + if required_field not in xlsx_template.core_fields + ] + if missing_required_core_fields: + raise ValidationError( + f"{missing_required_core_fields} fields are required by delivery mechanism " + f"{delivery_mechanism} and must be present in the template core fields" + ) + + error_message = f"Delivery Mechanism {delivery_mechanism} is not supported by Financial Service Provider {financial_service_provider}" + # to work both in inline and standalone + if delivery_mechanisms := self.data.get("delivery_mechanisms"): + if delivery_mechanism and str(delivery_mechanism.id) not in delivery_mechanisms: + raise ValidationError(error_message) + else: + if delivery_mechanism and delivery_mechanism not in financial_service_provider.delivery_mechanisms.all(): + raise ValidationError(error_message) + + return cleaned_data + + @admin.register(FspXlsxTemplatePerDeliveryMechanism) class FspXlsxTemplatePerDeliveryMechanismAdmin(HOPEModelAdminBase): list_display = ("financial_service_provider", "delivery_mechanism", "xlsx_template", "created_by") fields = ("financial_service_provider", "delivery_mechanism", "xlsx_template") autocomplete_fields = ("financial_service_provider", "xlsx_template") exclude = ("delivery_mechanism_choice",) + form = FspXlsxTemplatePerDeliveryMechanismForm def save_model( self, request: HttpRequest, obj: FspXlsxTemplatePerDeliveryMechanism, form: "Form", change: bool @@ -405,6 +443,7 @@ def clean(self) -> Optional[Dict[str, Any]]: class FspXlsxTemplatePerDeliveryMechanismAdminInline(admin.TabularInline): + form = FspXlsxTemplatePerDeliveryMechanismForm model = FspXlsxTemplatePerDeliveryMechanism extra = 0 readonly_fields = ("created_by",) diff --git a/src/hct_mis_api/apps/payment/models.py b/src/hct_mis_api/apps/payment/models.py index f0a0a1b415..cd2cbbb0c0 100644 --- a/src/hct_mis_api/apps/payment/models.py +++ b/src/hct_mis_api/apps/payment/models.py @@ -1354,23 +1354,6 @@ class Meta: def __str__(self) -> str: return f"{self.financial_service_provider.name} - {self.xlsx_template} - {self.delivery_mechanism}" # pragma: no cover - def clean(self) -> None: - missing_required_core_fields = [ - required_field - for required_field in self.delivery_mechanism.required_fields - if required_field not in self.xlsx_template.core_fields - ] - if missing_required_core_fields: - raise ValidationError( - f"{missing_required_core_fields} fields are required by delivery mechanism " - f"{self.delivery_mechanism} and must be present in the template core fields" - ) - - if self.delivery_mechanism not in self.financial_service_provider.delivery_mechanisms.all(): - raise ValidationError( - f"Delivery Mechanism {self.delivery_mechanism} is not supported by Financial Service Provider {self.financial_service_provider}" - ) - class FinancialServiceProvider(LimitBusinessAreaModelMixin, TimeStampedUUIDModel): COMMUNICATION_CHANNEL_API = "API" diff --git a/tests/unit/apps/grievance/test_model_validation.py b/tests/unit/apps/grievance/test_model_validation.py index 0c9ebd70e4..bd4c71b864 100644 --- a/tests/unit/apps/grievance/test_model_validation.py +++ b/tests/unit/apps/grievance/test_model_validation.py @@ -5,13 +5,13 @@ from hct_mis_api.apps.core.fixtures import create_afghanistan from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.grievance.models import GrievanceTicket +from hct_mis_api.apps.payment.admin import FspXlsxTemplatePerDeliveryMechanismForm from hct_mis_api.apps.payment.fixtures import ( FinancialServiceProviderFactory, FinancialServiceProviderXlsxTemplateFactory, - FspXlsxTemplatePerDeliveryMechanismFactory, generate_delivery_mechanisms, ) -from hct_mis_api.apps.payment.models import DeliveryMechanism +from hct_mis_api.apps.payment.models import DeliveryMechanism, FinancialServiceProvider class TestGrievanceModelValidation(TestCase): @@ -84,38 +84,77 @@ def setUpTestData(cls) -> None: create_afghanistan() cls.user = UserFactory.create() generate_delivery_mechanisms() + cls.dm_transfer_to_account = DeliveryMechanism.objects.get(code="transfer_to_account") - def test_clean(self) -> None: - dm_cash = DeliveryMechanism.objects.get(code="cash") - dm_atm_card = DeliveryMechanism.objects.get(code="atm_card") - fsp = FinancialServiceProviderFactory() - fsp.delivery_mechanisms.set([dm_atm_card]) - template = FinancialServiceProviderXlsxTemplateFactory() - template_per_dm_cash = FspXlsxTemplatePerDeliveryMechanismFactory( - financial_service_provider=fsp, - delivery_mechanism=dm_cash, - xlsx_template=template, + def test_admin_form_clean(self) -> None: + fsp_xls_template = FinancialServiceProviderXlsxTemplateFactory( + core_fields=["bank_name__transfer_to_account", "bank_account_number__transfer_to_account"] ) + fsp = FinancialServiceProviderFactory( + name="Test FSP", + vision_vendor_number="123", + communication_channel=FinancialServiceProvider.COMMUNICATION_CHANNEL_API, + ) + fsp.delivery_mechanisms.add(self.dm_transfer_to_account) + + # test valid form + form_data_standalone = { + "financial_service_provider": fsp.id, + "delivery_mechanism": self.dm_transfer_to_account.id, + "xlsx_template": fsp_xls_template.id, + } + form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_standalone) + self.assertTrue(form.is_valid()) + form.clean() + + # test inline form data valid + form_data_inline = { + "financial_service_provider": fsp.id, + "delivery_mechanism": self.dm_transfer_to_account.id, + "xlsx_template": fsp_xls_template.id, + "delivery_mechanisms": [str(self.dm_transfer_to_account.id)], + } + form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_inline) + self.assertTrue(form.is_valid()) + form.clean() + + # test missing required core fields + fsp_xls_template.core_fields = [] + fsp_xls_template.save() + + form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_standalone) + self.assertFalse(form.is_valid()) with self.assertRaisesMessage( ValidationError, - f"Delivery Mechanism {template_per_dm_cash.delivery_mechanism} is not supported by Financial Service Provider {fsp}", + "[\"['bank_name__transfer_to_account', 'bank_account_number__transfer_to_account'] fields are required by delivery mechanism Transfer to Account and must be present in the template core fields\"]", ): - template_per_dm_cash.clean() + form.clean() - template_per_dm_atm_card = FspXlsxTemplatePerDeliveryMechanismFactory( - financial_service_provider=fsp, - delivery_mechanism=dm_atm_card, - xlsx_template=template, - ) + fsp_xls_template.core_fields = ["bank_name__transfer_to_account", "bank_account_number__transfer_to_account"] + fsp_xls_template.save() + # test delivery mechanism not supported + fsp.delivery_mechanisms.remove(self.dm_transfer_to_account) + form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_standalone) + self.assertFalse(form.is_valid()) with self.assertRaisesMessage( ValidationError, - f"['card_number__atm_card', 'card_expiry_date__atm_card', 'name_of_cardholder__atm_card'] fields are required by delivery mechanism " - f"{template_per_dm_atm_card.delivery_mechanism} and must be present in the template core fields", + "['Delivery Mechanism Transfer to Account is not supported by Financial Service Provider Test FSP (123): API']", ): - template_per_dm_atm_card.clean() - - template.core_fields = ["card_number__atm_card", "card_expiry_date__atm_card", "name_of_cardholder__atm_card"] - template.save() - template_per_dm_atm_card.clean() + form.clean() + + # test inline form data invalid + form_data_inline = { + "financial_service_provider": fsp.id, + "delivery_mechanism": self.dm_transfer_to_account.id, + "xlsx_template": fsp_xls_template.id, + "delivery_mechanisms": ["12313213123"], + } + form = FspXlsxTemplatePerDeliveryMechanismForm(data=form_data_inline) + self.assertFalse(form.is_valid()) + with self.assertRaisesMessage( + ValidationError, + "['Delivery Mechanism Transfer to Account is not supported by Financial Service Provider Test FSP (123): API']", + ): + form.clean() diff --git a/tests/unit/apps/payment/test_models1.py b/tests/unit/apps/payment/test_models1.py index 29a06832f2..b39f426c7b 100644 --- a/tests/unit/apps/payment/test_models1.py +++ b/tests/unit/apps/payment/test_models1.py @@ -61,10 +61,26 @@ def test_update_population_count_fields(self) -> None: PaymentFactory(parent=pp, household=hh1, head_of_household=hoh1, currency="PLN") PaymentFactory(parent=pp, household=hh2, head_of_household=hoh2, currency="PLN") - IndividualFactory(household=hh1, sex="FEMALE", birth_date=datetime.now().date() - relativedelta(years=5)) - IndividualFactory(household=hh1, sex="MALE", birth_date=datetime.now().date() - relativedelta(years=5)) - IndividualFactory(household=hh2, sex="FEMALE", birth_date=datetime.now().date() - relativedelta(years=20)) - IndividualFactory(household=hh2, sex="MALE", birth_date=datetime.now().date() - relativedelta(years=20)) + IndividualFactory( + household=hh1, + sex="FEMALE", + birth_date=datetime.now().date() - relativedelta(years=5), + ) + IndividualFactory( + household=hh1, + sex="MALE", + birth_date=datetime.now().date() - relativedelta(years=5), + ) + IndividualFactory( + household=hh2, + sex="FEMALE", + birth_date=datetime.now().date() - relativedelta(years=20), + ) + IndividualFactory( + household=hh2, + sex="MALE", + birth_date=datetime.now().date() - relativedelta(years=20), + ) pp.update_population_count_fields() @@ -76,7 +92,10 @@ def test_update_population_count_fields(self) -> None: self.assertEqual(pp.total_households_count, 2) self.assertEqual(pp.total_individuals_count, 4) - @patch("hct_mis_api.apps.payment.models.PaymentPlan.get_exchange_rate", return_value=2.0) + @patch( + "hct_mis_api.apps.payment.models.PaymentPlan.get_exchange_rate", + return_value=2.0, + ) def test_update_money_fields(self, get_exchange_rate_mock: Any) -> None: pp = PaymentPlanFactory() PaymentFactory( @@ -129,8 +148,15 @@ def test_can_be_locked(self) -> None: program_cycle=program_cycle, ) p1 = PaymentFactory(parent=pp1, conflicted=False, currency="PLN") - PaymentFactory(parent=pp1_conflicted, household=p1.household, conflicted=False, currency="PLN") - self.assertEqual(pp1.payment_items.filter(payment_plan_hard_conflicted=True).count(), 1) + PaymentFactory( + parent=pp1_conflicted, + household=p1.household, + conflicted=False, + currency="PLN", + ) + self.assertEqual( + pp1.payment_items.filter(payment_plan_hard_conflicted=True).count(), 1 + ) self.assertEqual(pp1.can_be_locked, False) # create not conflicted payment @@ -217,9 +243,15 @@ def test_manager_annotations_pp_conflicts(self) -> None: program_cycle=program_cycle, ) p1 = PaymentFactory(parent=pp1, conflicted=False, currency="PLN") - p2 = PaymentFactory(parent=pp2, household=p1.household, conflicted=False, currency="PLN") - p3 = PaymentFactory(parent=pp3, household=p1.household, conflicted=False, currency="PLN") - p4 = PaymentFactory(parent=pp4, household=p1.household, conflicted=False, currency="PLN") + p2 = PaymentFactory( + parent=pp2, household=p1.household, conflicted=False, currency="PLN" + ) + p3 = PaymentFactory( + parent=pp3, household=p1.household, conflicted=False, currency="PLN" + ) + p4 = PaymentFactory( + parent=pp4, household=p1.household, conflicted=False, currency="PLN" + ) for obj in [pp1, pp2, pp3, pp4, p1, p2, p3, p4]: obj.refresh_from_db() # update unicef_id from trigger @@ -235,7 +267,9 @@ def test_manager_annotations_pp_conflicts(self) -> None: "payment_id": str(p2.id), "payment_plan_id": str(pp2.id), "payment_plan_status": str(pp2.status), - "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), + "payment_plan_start_date": program_cycle.start_date.strftime( + "%Y-%m-%d" + ), "payment_plan_end_date": program_cycle.end_date.strftime("%Y-%m-%d"), "payment_plan_unicef_id": str(pp2.unicef_id), "payment_unicef_id": str(p2.unicef_id), @@ -243,14 +277,21 @@ def test_manager_annotations_pp_conflicts(self) -> None: ) self.assertEqual(len(p1_data["payment_plan_soft_conflicted_data"]), 2) self.assertCountEqual( - [json.loads(conflict_data) for conflict_data in p1_data["payment_plan_soft_conflicted_data"]], + [ + json.loads(conflict_data) + for conflict_data in p1_data["payment_plan_soft_conflicted_data"] + ], [ { "payment_id": str(p3.id), "payment_plan_id": str(pp3.id), "payment_plan_status": str(pp3.status), - "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), - "payment_plan_end_date": program_cycle.end_date.strftime("%Y-%m-%d"), + "payment_plan_start_date": program_cycle.start_date.strftime( + "%Y-%m-%d" + ), + "payment_plan_end_date": program_cycle.end_date.strftime( + "%Y-%m-%d" + ), "payment_plan_unicef_id": str(pp3.unicef_id), "payment_unicef_id": str(p3.unicef_id), }, @@ -258,8 +299,12 @@ def test_manager_annotations_pp_conflicts(self) -> None: "payment_id": str(p4.id), "payment_plan_id": str(pp4.id), "payment_plan_status": str(pp4.status), - "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), - "payment_plan_end_date": program_cycle.end_date.strftime("%Y-%m-%d"), + "payment_plan_start_date": program_cycle.start_date.strftime( + "%Y-%m-%d" + ), + "payment_plan_end_date": program_cycle.end_date.strftime( + "%Y-%m-%d" + ), "payment_plan_unicef_id": str(pp4.unicef_id), "payment_unicef_id": str(p4.unicef_id), }, @@ -283,7 +328,9 @@ def test_manager_annotations_pp_conflicts(self) -> None: "payment_id": str(p2.id), "payment_plan_id": str(pp2.id), "payment_plan_status": str(pp2.status), - "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), + "payment_plan_start_date": program_cycle.start_date.strftime( + "%Y-%m-%d" + ), "payment_plan_end_date": None, "payment_plan_unicef_id": str(pp2.unicef_id), "payment_unicef_id": str(p2.unicef_id), @@ -291,13 +338,18 @@ def test_manager_annotations_pp_conflicts(self) -> None: ) self.assertEqual(len(payment_data["payment_plan_soft_conflicted_data"]), 2) self.assertCountEqual( - [json.loads(conflict_data) for conflict_data in payment_data["payment_plan_soft_conflicted_data"]], + [ + json.loads(conflict_data) + for conflict_data in payment_data["payment_plan_soft_conflicted_data"] + ], [ { "payment_id": str(p3.id), "payment_plan_id": str(pp3.id), "payment_plan_status": str(pp3.status), - "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), + "payment_plan_start_date": program_cycle.start_date.strftime( + "%Y-%m-%d" + ), "payment_plan_end_date": None, "payment_plan_unicef_id": str(pp3.unicef_id), "payment_unicef_id": str(p3.unicef_id), @@ -306,7 +358,9 @@ def test_manager_annotations_pp_conflicts(self) -> None: "payment_id": str(p4.id), "payment_plan_id": str(pp4.id), "payment_plan_status": str(pp4.status), - "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), + "payment_plan_start_date": program_cycle.start_date.strftime( + "%Y-%m-%d" + ), "payment_plan_end_date": None, "payment_plan_unicef_id": str(pp4.unicef_id), "payment_unicef_id": str(p4.unicef_id), @@ -383,7 +437,9 @@ def test_properties(self) -> None: order=0, ) pp_split1.payments.set([p1, p2]) - self.assertEqual(pp_split1.financial_service_provider, dm.financial_service_provider) + self.assertEqual( + pp_split1.financial_service_provider, dm.financial_service_provider + ) self.assertEqual(pp_split1.chosen_configuration, dm.chosen_configuration) self.assertEqual(pp_split1.delivery_mechanism, dm.delivery_mechanism) @@ -431,11 +487,19 @@ def test_fsp_template_get_column_from_core_field(self) -> None: }, ) document_type = DocumentTypeFactory(key="national_id") - document = DocumentFactory(individual=individuals[0], type=document_type, document_number="id_doc_number_123") + document = DocumentFactory( + individual=individuals[0], + type=document_type, + document_number="id_doc_number_123", + ) country = CountryFactory() admin_type_1 = AreaTypeFactory(country=country, area_level=1) - admin_type_2 = AreaTypeFactory(country=country, area_level=2, parent=admin_type_1) - admin_type_3 = AreaTypeFactory(country=country, area_level=3, parent=admin_type_2) + admin_type_2 = AreaTypeFactory( + country=country, area_level=2, parent=admin_type_1 + ) + admin_type_3 = AreaTypeFactory( + country=country, area_level=3, parent=admin_type_2 + ) area1 = AreaFactory(parent=None, p_code="AF01", area_type=admin_type_1) area2 = AreaFactory(parent=area1, p_code="AF0101", area_type=admin_type_2) area3 = AreaFactory(parent=area2, p_code="AF010101", area_type=admin_type_3) @@ -444,24 +508,34 @@ def test_fsp_template_get_column_from_core_field(self) -> None: household.admin3 = area3 household.save() - payment = PaymentFactory(program=ProgramFactory(), household=household, collector=individuals[0]) - data_collecting_type = DataCollectingTypeFactory(type=DataCollectingType.Type.SOCIAL) + payment = PaymentFactory( + program=ProgramFactory(), household=household, collector=individuals[0] + ) + data_collecting_type = DataCollectingTypeFactory( + type=DataCollectingType.Type.SOCIAL + ) fsp_xlsx_template = FinancialServiceProviderXlsxTemplate payment.parent.program.data_collecting_type = data_collecting_type payment.parent.program.save() # check invalid filed name - result = fsp_xlsx_template.get_column_from_core_field(payment, "invalid_people_field_name") + result = fsp_xlsx_template.get_column_from_core_field( + payment, "invalid_people_field_name" + ) self.assertIsNone(result) # People program given_name = fsp_xlsx_template.get_column_from_core_field(payment, "given_name") self.assertEqual(given_name, individuals[0].given_name) - ind_unicef_id = fsp_xlsx_template.get_column_from_core_field(payment, "individual_unicef_id") + ind_unicef_id = fsp_xlsx_template.get_column_from_core_field( + payment, "individual_unicef_id" + ) self.assertEqual(ind_unicef_id, individuals[0].unicef_id) # Standard program - payment.parent.program.data_collecting_type.type = DataCollectingType.Type.STANDARD + payment.parent.program.data_collecting_type.type = ( + DataCollectingType.Type.STANDARD + ) payment.parent.program.data_collecting_type.save() # check fields value @@ -475,21 +549,35 @@ def test_fsp_template_get_column_from_core_field(self) -> None: self.assertEqual(admin3, f"{area3.p_code} - {area3.name}") given_name = fsp_xlsx_template.get_column_from_core_field(payment, "given_name") self.assertEqual(given_name, individuals[0].given_name) - ind_unicef_id = fsp_xlsx_template.get_column_from_core_field(payment, "individual_unicef_id") + ind_unicef_id = fsp_xlsx_template.get_column_from_core_field( + payment, "individual_unicef_id" + ) self.assertEqual(ind_unicef_id, individuals[0].unicef_id) - hh_unicef_id = fsp_xlsx_template.get_column_from_core_field(payment, "household_unicef_id") + hh_unicef_id = fsp_xlsx_template.get_column_from_core_field( + payment, "household_unicef_id" + ) self.assertEqual(hh_unicef_id, household.unicef_id) phone_no = fsp_xlsx_template.get_column_from_core_field(payment, "phone_no") self.assertEqual(phone_no, individuals[0].phone_no) - phone_no_alternative = fsp_xlsx_template.get_column_from_core_field(payment, "phone_no_alternative") + phone_no_alternative = fsp_xlsx_template.get_column_from_core_field( + payment, "phone_no_alternative" + ) self.assertEqual(phone_no_alternative, individuals[0].phone_no_alternative) - national_id_no = fsp_xlsx_template.get_column_from_core_field(payment, "national_id_no") + national_id_no = fsp_xlsx_template.get_column_from_core_field( + payment, "national_id_no" + ) self.assertEqual(national_id_no, document.document_number) - wallet_name = fsp_xlsx_template.get_column_from_core_field(payment, "wallet_name") + wallet_name = fsp_xlsx_template.get_column_from_core_field( + payment, "wallet_name" + ) self.assertEqual(wallet_name, individuals[0].wallet_name) - blockchain_name = fsp_xlsx_template.get_column_from_core_field(payment, "blockchain_name") + blockchain_name = fsp_xlsx_template.get_column_from_core_field( + payment, "blockchain_name" + ) self.assertEqual(blockchain_name, individuals[0].blockchain_name) - wallet_address = fsp_xlsx_template.get_column_from_core_field(payment, "wallet_address") + wallet_address = fsp_xlsx_template.get_column_from_core_field( + payment, "wallet_address" + ) self.assertEqual(wallet_address, individuals[0].wallet_address) @@ -500,7 +588,8 @@ def setUp(self) -> None: def test_choices(self) -> None: field = DynamicChoiceArrayField( - base_field=models.CharField(max_length=255), choices_callable=self.mock_choices_callable + base_field=models.CharField(max_length=255), + choices_callable=self.mock_choices_callable, ) form_field = field.formfield() @@ -512,24 +601,29 @@ def test_choices(self) -> None: self.assertIsInstance(form_field, DynamicChoiceField) -class FinancialServiceProviderXlsxTemplateForm(forms.ModelForm): - class Meta: - model = FinancialServiceProviderXlsxTemplate - fields = ["core_fields"] - - class TestFinancialServiceProviderXlsxTemplate(TestCase): + class FinancialServiceProviderXlsxTemplateForm(forms.ModelForm): + class Meta: + model = FinancialServiceProviderXlsxTemplate + fields = ["core_fields"] + def test_model_form_integration(self) -> None: - form = FinancialServiceProviderXlsxTemplateForm( + form = self.FinancialServiceProviderXlsxTemplateForm( data={"core_fields": ["age", "residence_status"]} ) # real existing core fields self.assertTrue(form.is_valid()) template = form.save() self.assertEqual(template.core_fields, ["age", "residence_status"]) - form = FinancialServiceProviderXlsxTemplateForm(data={"core_fields": ["field1"]}) # fake core fields + form = self.FinancialServiceProviderXlsxTemplateForm( + data={"core_fields": ["field1"]} + ) # fake core fields self.assertFalse(form.is_valid()) self.assertEqual( form.errors, - {"core_fields": ["Select a valid choice. field1 is not one of the available choices."]}, + { + "core_fields": [ + "Select a valid choice. field1 is not one of the available choices." + ] + }, ) From d6fb6ba6065371d6c4c7612ca61ff832af1b3cf5 Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Fri, 27 Sep 2024 12:13:36 +0200 Subject: [PATCH 030/202] remove lazy query --- .../src/components/core/Drawer/Drawer.tsx | 18 +++++------------ .../components/core/Drawer/DrawerItems.tsx | 20 ++++--------------- .../src/containers/GlobalProgramSelect.tsx | 18 +++++------------ 3 files changed, 14 insertions(+), 42 deletions(-) diff --git a/src/frontend/src/components/core/Drawer/Drawer.tsx b/src/frontend/src/components/core/Drawer/Drawer.tsx index 330dd7fff7..6cee212ecd 100644 --- a/src/frontend/src/components/core/Drawer/Drawer.tsx +++ b/src/frontend/src/components/core/Drawer/Drawer.tsx @@ -14,7 +14,8 @@ import { DrawerItems } from './DrawerItems'; import { resourcesItems } from './menuItems'; import styled from 'styled-components'; import { useBaseUrl } from '@hooks/useBaseUrl'; -import { ProgramStatus, useProgramLazyQuery } from '@generated/graphql'; +import { ProgramStatus } from '@generated/graphql'; +import { useProgramContext } from 'src/programContext'; const matchColorToWindowOrigin = (): string => { const url = window.location.href; @@ -134,21 +135,12 @@ export const Drawer = ({ }: DrawerProps): React.ReactElement => { const { t } = useTranslation(); const [showMismatchedDialog, setShowMismatchedDialog] = useState(false); + const { selectedProgram } = useProgramContext(); + const { isAllPrograms } = useBaseUrl(); - const { programId, isAllPrograms } = useBaseUrl(); - const [getProgram, programResults] = useProgramLazyQuery({ - variables: { - id: programId, - }, - }); const backendVersion = useBackendVersion(); const frontendVersion = useFrontendVersion(); - useEffect(() => { - if (!isAllPrograms) { - getProgram(); - } - }, [programId, isAllPrograms, getProgram]); useEffect(() => { if ( !showMismatchedDialog && @@ -161,7 +153,7 @@ export const Drawer = ({ }, [backendVersion, frontendVersion, showMismatchedDialog]); let notActiveBar = null; - const programStatus = programResults?.data?.program?.status; + const programStatus = selectedProgram?.status; const isActive = programStatus === ProgramStatus.Active; const isDefined = programStatus !== undefined && programStatus !== null; if (!isAllPrograms && !isActive && isDefined) { diff --git a/src/frontend/src/components/core/Drawer/DrawerItems.tsx b/src/frontend/src/components/core/Drawer/DrawerItems.tsx index 161ffa111c..01b307a7e1 100644 --- a/src/frontend/src/components/core/Drawer/DrawerItems.tsx +++ b/src/frontend/src/components/core/Drawer/DrawerItems.tsx @@ -1,7 +1,4 @@ -import { - useBusinessAreaDataQuery, - useProgramLazyQuery, -} from '@generated/graphql'; +import { useBusinessAreaDataQuery } from '@generated/graphql'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { usePermissions } from '@hooks/usePermissions'; import ExpandLess from '@mui/icons-material/ExpandLess'; @@ -24,6 +21,7 @@ import { SCOPE_ALL_PROGRAMS, SCOPE_PROGRAM, } from './menuItems'; +import { useProgramContext } from 'src/programContext'; const Text = styled(ListItemText)` .MuiTypography-body1 { @@ -63,16 +61,7 @@ export const DrawerItems = ({ open, }: DrawerItemsProps): React.ReactElement => { const { baseUrl, businessArea, programId, isAllPrograms } = useBaseUrl(); - const [getProgram, programResults] = useProgramLazyQuery({ - variables: { - id: programId, - }, - }); - useEffect(() => { - if (!isAllPrograms) { - getProgram(); - } - }, [programId, getProgram, isAllPrograms]); + const { isSocialDctType } = useProgramContext(); const permissions = usePermissions(); const { data: businessAreaData } = useBusinessAreaDataQuery({ variables: { businessAreaSlug: businessArea }, @@ -121,8 +110,7 @@ export const DrawerItems = ({ let isVisible = isAllPrograms ? item.scopes.includes(SCOPE_ALL_PROGRAMS) : item.scopes.includes(SCOPE_PROGRAM); - const isSocialWorkerProgram = - programResults?.data?.program?.isSocialWorkerProgram; + const isSocialWorkerProgram = isSocialDctType; if (item.isSocialWorker === false) { isVisible &&= !isSocialWorkerProgram; } else if (item.isSocialWorker === true) { diff --git a/src/frontend/src/containers/GlobalProgramSelect.tsx b/src/frontend/src/containers/GlobalProgramSelect.tsx index 7396251ddd..57c4ea65ab 100644 --- a/src/frontend/src/containers/GlobalProgramSelect.tsx +++ b/src/frontend/src/containers/GlobalProgramSelect.tsx @@ -24,7 +24,7 @@ import { useNavigate } from 'react-router-dom'; import { ProgramStatus, useAllProgramsForChoicesLazyQuery, - useProgramLazyQuery, + useProgramQuery, } from '@generated/graphql'; import { KeyboardEvent, useEffect, useRef, useState } from 'react'; import ClearIcon from '@mui/icons-material/Clear'; @@ -131,12 +131,10 @@ export const GlobalProgramSelect = () => { }); const isMounted = useRef(false); const [inputValue, setInputValue] = useState<string>(''); - const [loadProgram, { data: programData, loading: loadingProgram }] = - useProgramLazyQuery({ - variables: { - id: programId, - }, - }); + const { data: programData, loading: loadingProgram } = useProgramQuery({ + variables: { id: programId }, + skip: programId === 'all' || !programId, + }); const [programs, setPrograms] = useState<ProgramRecord[]>([]); useEffect(() => { @@ -150,12 +148,6 @@ export const GlobalProgramSelect = () => { }; }, []); - useEffect(() => { - if (programId !== 'all') { - void loadProgram(); - } - }, [programId, loadProgram]); - useEffect(() => { if (programId !== 'all') { const program = programData?.program; From f3d8b2c5098d5df025b8228824a07314f8100d8f Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Fri, 27 Sep 2024 15:35:36 +0200 Subject: [PATCH 031/202] fixed health check --- development_tools/compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development_tools/compose.yml b/development_tools/compose.yml index 9f3b6c1716..2ad1ff97a7 100644 --- a/development_tools/compose.yml +++ b/development_tools/compose.yml @@ -127,7 +127,7 @@ services: ports: - "5433:5432" healthcheck: - test: [ "CMD-SHELL", "pg_isready -U postgres" ] + test: [ "CMD-SHELL", "su - postgres -c 'pg_isready -U postgres'" ] interval: 10s timeout: 10s retries: 5 From 068e0abf0fc02ef3ef6e274cc032e0d971f2fa78 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Mon, 30 Sep 2024 10:07:39 +0200 Subject: [PATCH 032/202] fix e2e --- tests/selenium/page_object/targeting/targeting_create.py | 2 +- tests/selenium/targeting/test_targeting.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/selenium/page_object/targeting/targeting_create.py b/tests/selenium/page_object/targeting/targeting_create.py index dafba16277..d2f19681a5 100644 --- a/tests/selenium/page_object/targeting/targeting_create.py +++ b/tests/selenium/page_object/targeting/targeting_create.py @@ -82,7 +82,7 @@ class TargetingCreate(BaseComponents): 'div[data-cy="select-individualsFiltersBlocks[{}].individualBlockFilters[{}].value"]' ) totalNumberOfHouseholdsCount = 'div[data-cy="total-number-of-households-count"]' - totalNumberOfPeopleCount = 'div[data-cy="label-Total Number of Registered People"]' + totalNumberOfPeopleCount = 'div[data-cy="label-Total Number of People"]' selectProgramCycleAutocomplete = 'div[data-cy="filters-program-cycle-autocomplete"]' programmeCycleInput = 'div[data-cy="Programme Cycle-input"]' diff --git a/tests/selenium/targeting/test_targeting.py b/tests/selenium/targeting/test_targeting.py index 5b5b1b127e..a76c25b2e7 100644 --- a/tests/selenium/targeting/test_targeting.py +++ b/tests/selenium/targeting/test_targeting.py @@ -756,7 +756,6 @@ def test_create_targeting_for_people_with_pdu( pageTargetingCreate.getFieldName().send_keys(targeting_name) pageTargetingCreate.getTargetPopulationSaveButton().click() pageTargetingDetails.getLockButton() - pageTargetingCreate.screenshot("asdasddas111") assert pageTargetingDetails.getTitlePage().text == targeting_name assert pageTargetingDetails.getCriteriaContainer().text == expected_criteria_text From 8c8b1f7306c9f632816d260e256c5a0002e4575a Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Mon, 30 Sep 2024 16:11:54 +0200 Subject: [PATCH 033/202] added db host --- development_tools/compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development_tools/compose.yml b/development_tools/compose.yml index 2ad1ff97a7..5c7b9d04f1 100644 --- a/development_tools/compose.yml +++ b/development_tools/compose.yml @@ -127,7 +127,7 @@ services: ports: - "5433:5432" healthcheck: - test: [ "CMD-SHELL", "su - postgres -c 'pg_isready -U postgres'" ] + test: [ "CMD-SHELL", "su - postgres -c 'pg_isready -h db -U postgres'" ] interval: 10s timeout: 10s retries: 5 From 9f69988acf97ee1c663aed508eaaedbc6c1f06d0 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Tue, 1 Oct 2024 01:28:41 +0200 Subject: [PATCH 034/202] validate unique ind for role in hh --- .../apps/registration_datahub/validators.py | 26 ++++++++++++++++ .../test_kobo_validators_methods.py | 31 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/hct_mis_api/apps/registration_datahub/validators.py b/src/hct_mis_api/apps/registration_datahub/validators.py index 85e3d44366..6f92633cdb 100644 --- a/src/hct_mis_api/apps/registration_datahub/validators.py +++ b/src/hct_mis_api/apps/registration_datahub/validators.py @@ -1460,6 +1460,27 @@ def _get_field_type_error( logger.exception(e) raise + @staticmethod + def validate_collectors_unique(household_collectors_data: list) -> Union[dict, None]: + collectors_unique_data = [] + for collector_info in household_collectors_data: + collector_data = [ + collector_info.get("full_name_i_c"), + collector_info.get("gender_i_c"), + collector_info.get("birth_date_i_c"), + collector_info.get("middle_name_i_c"), + collector_info.get("given_name_i_c"), + collector_info.get("family_name_i_c"), + collector_info.get("phone_no_i_c"), + collector_info.get("phone_no_alternative_i_c"), + ] + if collector_data in collectors_unique_data: + return { + "header": "role_i_c", + "message": "The same individual cannot be a primary and alternate collector for the same household.", + } + collectors_unique_data.append(collector_data) + def validate_everything( self, submissions: List, business_area: BusinessArea, skip_validate_pictures: Optional[bool] = False ) -> List: @@ -1554,11 +1575,14 @@ def validate_everything( } attachments = household.get("_attachments", []) hh_value: List[Dict] + household_collectors_data = [] for hh_field, hh_value in household.items(): expected_hh_fields.discard(hh_field) if hh_field == KOBO_FORM_INDIVIDUALS_COLUMN_NAME: individual: Dict for individual in hh_value: + if individual.get("role_i_c") in ["primary", "alternate"]: + household_collectors_data.append(individual) expected_i_fields = { *self.expected_individuals_fields, } @@ -1641,6 +1665,8 @@ def validate_everything( error = self._get_field_type_error(hh_field, hh_value, attachments, skip_validate_pictures) if error: errors.append(error) + if collectors_error := self.validate_collectors_unique(household_collectors_data): + errors.append(collectors_error) hh_expected_field_errors = [ {"header": field, "message": f"Missing household required field {field}"} for field in expected_hh_fields diff --git a/tests/unit/apps/registration_datahub/test_kobo_validators_methods.py b/tests/unit/apps/registration_datahub/test_kobo_validators_methods.py index fa8b0db7b4..b81a50b577 100644 --- a/tests/unit/apps/registration_datahub/test_kobo_validators_methods.py +++ b/tests/unit/apps/registration_datahub/test_kobo_validators_methods.py @@ -366,6 +366,29 @@ class TestKoboSaveValidatorsMethods(TestCase): "individual_questions/arm_picture_i_f": "signature-12_13_0.png", "individual_questions/identification/account_holder_name_i_c": "Name qwerty", }, + { + "individual_questions/role_i_c": "alternate", + "individual_questions/age": "37", + "individual_questions/given_name_i_c": "Tes", + "individual_questions/gender_i_c": "female", + "individual_questions/more_information/marital_status_i_c": "married", + "individual_questions/more_information/pregnant_i_f": "0", + "individual_questions/family_name_i_c": "Testowski", + "individual_questions/more_information/phone_no_i_c": "+49432123422", + "individual_questions/individual_vulnerabilities/formal_school_i_f": "0", + "individual_questions/individual_index": "2", + "individual_questions/full_name_i_c": "Tes Testowski", + "individual_questions/relationship_i_c": "wife_husband", + "individual_questions/individual_vulnerabilities/work_status_i_c": "0", + "individual_questions/estimated_birth_date_i_c": "FALSE", + "individual_questions/more_information/id_type_i_c": "birth_certificate", + "individual_questions/individual_vulnerabilities/disability_i_c": "not disabled", + "individual_questions/more_information/birth_certificate_no_i_c": "4442124124", + "individual_questions/birth_date_i_c": "1983-06-20", + "individual_questions/mas_treatment_i_f": "1", + "individual_questions/arm_picture_i_f": "signature-12_13_0.png", + "individual_questions/identification/account_holder_name_i_c": "Name qwerty", + }, { "individual_questions/role_i_c": "primary", "individual_questions/age": "23", @@ -730,8 +753,16 @@ def test_validate_everything(self) -> None: "header": "birth_certificate_no_i_c", "message": "Issuing country for birth_certificate_no_i_c is required, when any document data are provided", }, + { + "header": "birth_certificate_no_i_c", + "message": "Issuing country for birth_certificate_no_i_c is required, when any document data are provided", + }, # TODO: fix this? (rebase issue?) # {"header": "preferred_language_i_c", "message": "Invalid choice test for field preferred_language_i_c"}, {"header": "role_i_c", "message": "Only one person can be a primary collector"}, + { + "header": "role_i_c", + "message": "The same individual cannot be a primary and alternate collector for the same household." + }, ] self.assertEqual(result, expected) From 19a79b4dfb473cfe6a804b4bba7ded68afee6bee Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Tue, 1 Oct 2024 03:39:29 +0200 Subject: [PATCH 035/202] linter --- src/hct_mis_api/apps/registration_datahub/validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hct_mis_api/apps/registration_datahub/validators.py b/src/hct_mis_api/apps/registration_datahub/validators.py index 6f92633cdb..36235a072e 100644 --- a/src/hct_mis_api/apps/registration_datahub/validators.py +++ b/src/hct_mis_api/apps/registration_datahub/validators.py @@ -1461,7 +1461,7 @@ def _get_field_type_error( raise @staticmethod - def validate_collectors_unique(household_collectors_data: list) -> Union[dict, None]: + def validate_collectors_unique(household_collectors_data: list) -> Optional[dict]: collectors_unique_data = [] for collector_info in household_collectors_data: collector_data = [ From 06a178b5041967e022deb142a2ccbef57554e9d5 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Tue, 1 Oct 2024 05:06:46 +0200 Subject: [PATCH 036/202] noqa --- src/hct_mis_api/apps/registration_datahub/validators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hct_mis_api/apps/registration_datahub/validators.py b/src/hct_mis_api/apps/registration_datahub/validators.py index 36235a072e..ad087f116a 100644 --- a/src/hct_mis_api/apps/registration_datahub/validators.py +++ b/src/hct_mis_api/apps/registration_datahub/validators.py @@ -1461,7 +1461,7 @@ def _get_field_type_error( raise @staticmethod - def validate_collectors_unique(household_collectors_data: list) -> Optional[dict]: + def validate_collectors_unique(household_collectors_data: list) -> Optional[dict]: # noqa: F701 collectors_unique_data = [] for collector_info in household_collectors_data: collector_data = [ From a3a87397e60242aae646d5ee451b221b73883a8e Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@tivix.com> Date: Tue, 1 Oct 2024 09:46:11 +0200 Subject: [PATCH 037/202] Changes to Project structure (#4269) * structure * fix docker compose * Changes to project structure and Docker image building - Project installs as packaget on dist image - Rebuild dockerfile structure - removed unused file - addjusted entrypoint and manage.py * fix code style * selenium test fixes * changed settings --------- Co-authored-by: Domenico DiNicola <dom.dinicola@gmail.com> --- src/.flake8 => .flake8 | 0 .github/helpers/docker-compose.selenium.yml | 4 +- .gitignore | 2 + development_tools/compose.yml | 9 ++- docker/Dockerfile | 54 +++++++------- docker/entrypoint.sh | 2 +- src/manage.py => manage.py | 0 src/pdm.lock => pdm.lock | 78 +++++++++++++++++++- src/pyproject.toml => pyproject.toml | 15 ++-- src/README.md | 65 ---------------- src/data/RDI-VALID.xlsx | Bin 65764 -> 0 bytes src/hct_mis_api/config/settings.py | 10 +-- {src => tests}/.coveragerc | 0 tests/unit/conftest.py | 2 +- 14 files changed, 132 insertions(+), 109 deletions(-) rename src/.flake8 => .flake8 (100%) rename src/manage.py => manage.py (100%) rename src/pdm.lock => pdm.lock (98%) rename src/pyproject.toml => pyproject.toml (97%) delete mode 100644 src/README.md delete mode 100644 src/data/RDI-VALID.xlsx rename {src => tests}/.coveragerc (100%) diff --git a/src/.flake8 b/.flake8 similarity index 100% rename from src/.flake8 rename to .flake8 diff --git a/.github/helpers/docker-compose.selenium.yml b/.github/helpers/docker-compose.selenium.yml index c03d1381a5..9637daa92a 100644 --- a/.github/helpers/docker-compose.selenium.yml +++ b/.github/helpers/docker-compose.selenium.yml @@ -8,7 +8,7 @@ services: - ../src/report/:/code/report/ - type: volume source: backend-web-app - target: /code/hct_mis_api/apps/web + target: /code/src/hct_mis_api/apps/web volume: nocopy: false depends_on: @@ -31,7 +31,7 @@ services: - backend-web-app:/tmp/ command: | sh -c " - cp -r ./hct_mis_api/apps/web/* /tmp/ + cp -r /packages/__pypackages__/3.11/lib/hct_mis_api/apps/web/* /tmp/ " restart: "no" diff --git a/.gitignore b/.gitignore index 1719e999f2..aea161b528 100644 --- a/.gitignore +++ b/.gitignore @@ -101,6 +101,8 @@ ENV/ env.bak/ venv.bak/ +.pdm-python + # Spyder project settings .spyderproject .spyproject diff --git a/development_tools/compose.yml b/development_tools/compose.yml index 5c7b9d04f1..5f140205e0 100644 --- a/development_tools/compose.yml +++ b/development_tools/compose.yml @@ -25,11 +25,14 @@ services: ports: - "8080:8000" volumes: - - ../src:/code/ + - ../src:/code/src/ + - ../manage.py:/code/manage.py + - ../pyproject.toml:/code/pyproject.toml + - ../pdm.lock:/code/pdm.lock - ../tests:/tests/ - backend-data:/data - - ../src/pyproject.toml:/packages/pyproject.toml - - ../src/pdm.lock:/packages/pdm.lock + - ../pyproject.toml:/packages/pyproject.toml + - ../pdm.lock:/packages/pdm.lock - ipython_data_local:/root/.ipython command: "dev" depends_on: diff --git a/docker/Dockerfile b/docker/Dockerfile index c95249d117..727d72f63d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -41,11 +41,10 @@ RUN apt-get update \ ENV PDM_PACKAGES=/packages ENV CODE=/code -ENV PDM_NO_SELF=True ENV PDM_PROJECT=$PDM_PACKAGES ENV PYPACKAGES=$PDM_PACKAGES/__pypackages__/3.11 ENV PYTHONPYCACHEPREFIX=/tmp/pycache \ - PYTHONPATH=$PYPACKAGES/lib:$CODE:$PYTHONPATH \ + PYTHONPATH=$PYPACKAGES/lib:$PYTHONPATH \ PATH=$PYPACKAGES/bin:$PATH \ XDG_RUNTIME_DIR=/run/user/"${UID}" @@ -54,8 +53,7 @@ WORKDIR $CODE COPY --from=waitforit /data/waitforit /usr/local/bin/waitforit # Dist builder image -FROM base as builder - +FROM base as pdm RUN pip install --upgrade pip &&\ pip install pdm==2.15.2 &&\ pip install setuptools==71.1.0 &&\ @@ -63,13 +61,14 @@ RUN pip install --upgrade pip &&\ pdm config python.use_venv false &&\ pdm config venv.in_project true &&\ pdm config check_update false - WORKDIR $PDM_PACKAGES -COPY src/pyproject.toml src/pdm.lock ./ -RUN pdm sync --prod --no-editable --no-self --no-isolation +COPY README.md LICENSE pyproject.toml pdm.lock ./ + + + # Dev image -FROM builder AS dev +FROM pdm AS dev RUN apt-get update \ && apt-get install -y --no-install-recommends \ @@ -83,26 +82,14 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +ENV PYTHONPATH=$CODE/src:/test/:$PYTHONPATH RUN pdm sync --no-editable --no-self --no-isolation WORKDIR $CODE -COPY ./src/ ./ +COPY ./src/ ./src/ COPY ./tests /tests - - - -COPY ./docker/entrypoint.sh /bin/ -ENTRYPOINT ["entrypoint.sh"] - -## Dist (backend only) image -FROM base AS be-dist - -COPY ./src/ ./ -COPY --chown=hope:hope --from=builder $PDM_PACKAGES $PDM_PACKAGES -COPY --chown=hope:hope --from=certs /data/psql-cert.crt /code/psql-cert.crt - -USER hope - +COPY ./manage.py ./manage.py +COPY .flake8 pyproject.toml pdm.lock ./ COPY ./docker/entrypoint.sh /bin/ ENTRYPOINT ["entrypoint.sh"] @@ -117,7 +104,22 @@ RUN yarn install --frozen-lockfile --network-timeout 600000 COPY ./src/frontend ./ RUN NODE_ENV="production" NODE_OPTIONS="--max-old-space-size=4096" yarn build +# Dist builder image +FROM pdm as dist-builder +COPY ./src/ ./src/ +COPY --chown=hope:hope --from=frontend-builder /fe-build/build $PDM_PACKAGES/src/hct_mis_api/apps/web/static/web +RUN pdm sync --prod --no-editable --no-isolation + + +## Dist (backend only) image +FROM base AS dist + + +COPY ./src/gunicorn_config.py /conf/gunicorn_config.py +COPY --chown=hope:hope --from=dist-builder $PDM_PACKAGES $PDM_PACKAGES +COPY --chown=hope:hope --from=certs /data/psql-cert.crt /certs/psql-cert.crt -FROM be-dist AS dist +USER hope -COPY --chown=hope:hope --from=frontend-builder /fe-build/build ./hct_mis_api/apps/web/static/web +COPY ./docker/entrypoint.sh /bin/ +ENTRYPOINT ["entrypoint.sh"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 1f54479d16..acc61cdfb5 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -7,7 +7,7 @@ wait_for_db() { } if [ $# -eq 0 ]; then - exec gunicorn hct_mis_api.wsgi -c /code/gunicorn_config.py + exec gunicorn hct_mis_api.wsgi -c /conf/gunicorn_config.py else case "$1" in "dev") diff --git a/src/manage.py b/manage.py similarity index 100% rename from src/manage.py rename to manage.py diff --git a/src/pdm.lock b/pdm.lock similarity index 98% rename from src/pdm.lock rename to pdm.lock index ebb4f2962d..d6a7da44be 100644 --- a/src/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:5c974b2186f94a163bca26f624b14d0f6b33c0457acec95bb1e31f315ed72936" +content_hash = "sha256:65782357c859954ce329d6eb5d3d165e578591404bdafc138c077804a86bf640" [[metadata.targets]] requires_python = "==3.11.*" @@ -51,6 +51,9 @@ version = "3.8.1" requires_python = ">=3.8" summary = "ASGI specs, helper code, and adapters" groups = ["default", "dev"] +dependencies = [ + "typing-extensions>=4; python_version < \"3.11\"", +] files = [ {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, @@ -63,6 +66,7 @@ summary = "Annotate AST trees with source code positions" groups = ["default", "dev"] dependencies = [ "six>=1.12.0", + "typing; python_version < \"3.5\"", ] files = [ {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, @@ -76,6 +80,9 @@ requires_python = ">=3.7" summary = "Timeout context manager for asyncio programs" groups = ["default"] marker = "python_full_version < \"3.11.3\"" +dependencies = [ + "typing-extensions>=3.6.5; python_version < \"3.8\"", +] files = [ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, @@ -87,6 +94,9 @@ version = "24.2.0" requires_python = ">=3.7" summary = "Classes Without Boilerplate" groups = ["default", "dev"] +dependencies = [ + "importlib-metadata; python_version < \"3.8\"", +] files = [ {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, @@ -100,6 +110,7 @@ summary = "A tool that automatically formats Python code to conform to the PEP 8 groups = ["default"] dependencies = [ "pycodestyle>=2.11.0", + "tomli; python_version < \"3.11\"", ] files = [ {file = "autopep8-2.2.0-py2.py3-none-any.whl", hash = "sha256:05418a981f038969d8bdcd5636bf15948db7555ae944b9f79b5a34b35f1370d4"}, @@ -176,6 +187,8 @@ dependencies = [ "packaging>=22.0", "pathspec>=0.9.0", "platformdirs>=2", + "tomli>=1.1.0; python_version < \"3.11\"", + "typing-extensions>=4.0.1; python_version < \"3.11\"", ] files = [ {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, @@ -286,11 +299,13 @@ requires_python = ">=3.8" summary = "Distributed Task Queue." groups = ["default"] dependencies = [ + "backports-zoneinfo>=0.2.1; python_version < \"3.9\"", "billiard<5.0,>=4.2.0", "click-didyoumean>=0.3.0", "click-plugins>=1.1.1", "click-repl>=0.2.0", "click<9.0,>=8.1.2", + "importlib-metadata>=3.6; python_version < \"3.8\"", "kombu<6.0,>=5.3.4", "python-dateutil>=2.8.2", "tzdata>=2022.7", @@ -398,6 +413,7 @@ summary = "Composable command line interface toolkit" groups = ["default"] dependencies = [ "colorama; platform_system == \"Windows\"", + "importlib-metadata; python_version < \"3.8\"", ] files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, @@ -516,6 +532,7 @@ summary = "Code coverage measurement for Python" groups = ["dev"] dependencies = [ "coverage==7.6.1", + "tomli; python_full_version <= \"3.11.0a6\"", ] files = [ {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, @@ -768,9 +785,11 @@ summary = "Database-backed Periodic Tasks." groups = ["default"] dependencies = [ "Django<5.2,>=2.2", + "backports-zoneinfo; python_version < \"3.9\"", "celery<6.0,>=5.2.3", "cron-descriptor>=1.2.32", "django-timezone-field>=5.0", + "importlib-metadata<5.0; python_version < \"3.8\"", "python-crontab>=2.3.4", "tzdata", ] @@ -1380,6 +1399,7 @@ summary = "A Django app providing DB, form, and REST framework fields for zonein groups = ["default"] dependencies = [ "Django<6.0,>=3.2", + "backports-zoneinfo<0.3.0,>=0.2.1; python_version < \"3.9\"", ] files = [ {file = "django_timezone_field-7.0-py3-none-any.whl", hash = "sha256:3232e7ecde66ba4464abb6f9e6b8cc739b914efb9b29dc2cf2eee451f7cc2acb"}, @@ -1406,6 +1426,7 @@ requires_python = ">=3.6" summary = "Web APIs for Django, made easy." groups = ["default"] dependencies = [ + "backports-zoneinfo; python_version < \"3.9\"", "django>=3.0", ] files = [ @@ -1475,6 +1496,7 @@ dependencies = [ "djangorestframework>=3.10.3", "inflection>=0.3.1", "jsonschema>=2.6.0", + "typing-extensions; python_version < \"3.10\"", "uritemplate>=2.0.0", ] files = [ @@ -1520,6 +1542,7 @@ summary = "Transport classes and utilities shared among Python Elastic client li groups = ["default"] dependencies = [ "certifi", + "importlib-metadata; python_version < \"3.8\"", "urllib3<3,>=1.26.2", ] files = [ @@ -1612,6 +1635,7 @@ summary = "Faker is a Python package that generates fake data for you." groups = ["dev"] dependencies = [ "python-dateutil>=2.4", + "typing-extensions>=3.10.0.1; python_version < \"3.8\"", ] files = [ {file = "Faker-17.6.0-py3-none-any.whl", hash = "sha256:5aaa16fa9cfde7d117eef70b6b293a705021e57158f3fa6b44ed1b70202d2065"}, @@ -1781,6 +1805,7 @@ summary = "Coroutine-based network library" groups = ["default"] dependencies = [ "cffi>=1.12.2; platform_python_implementation == \"CPython\" and sys_platform == \"win32\"", + "greenlet>=2.0.0; platform_python_implementation == \"CPython\" and python_version < \"3.11\"", "greenlet>=3.0rc3; platform_python_implementation == \"CPython\" and python_version >= \"3.11\"", "zope-event", "zope-interface", @@ -1941,6 +1966,9 @@ version = "0.14.0" requires_python = ">=3.7" summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" groups = ["dev"] +dependencies = [ + "typing-extensions; python_version < \"3.8\"", +] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -2032,8 +2060,24 @@ requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" summary = "IPython-enabled pdb" groups = ["dev"] dependencies = [ + "decorator; python_version == \"3.5\"", + "decorator; python_version == \"3.6\"", + "decorator; python_version > \"3.6\" and python_version < \"3.11\"", "decorator; python_version >= \"3.11\"", + "decorator<5.0.0; python_version == \"2.7\"", + "decorator<5.0.0; python_version == \"3.4\"", + "ipython<6.0.0,>=5.1.0; python_version == \"2.7\"", + "ipython<7.0.0,>=6.0.0; python_version == \"3.4\"", + "ipython<7.10.0,>=7.0.0; python_version == \"3.5\"", + "ipython<7.17.0,>=7.16.3; python_version == \"3.6\"", + "ipython>=7.31.1; python_version > \"3.6\" and python_version < \"3.11\"", "ipython>=7.31.1; python_version >= \"3.11\"", + "pathlib; python_version == \"2.7\"", + "toml>=0.10.2; python_version == \"2.7\"", + "toml>=0.10.2; python_version == \"3.4\"", + "toml>=0.10.2; python_version == \"3.5\"", + "tomli; python_version == \"3.6\"", + "tomli; python_version > \"3.6\" and python_version < \"3.11\"", ] files = [ {file = "ipdb-0.13.13-py3-none-any.whl", hash = "sha256:45529994741c4ab6d2388bfa5d7b725c2cf7fe9deffabdb8a6113aa5ed449ed4"}, @@ -2049,6 +2093,7 @@ groups = ["default", "dev"] dependencies = [ "colorama; sys_platform == \"win32\"", "decorator", + "exceptiongroup; python_version < \"3.11\"", "jedi>=0.16", "matplotlib-inline", "pexpect>4.3; sys_platform != \"win32\" and sys_platform != \"emscripten\"", @@ -2144,7 +2189,9 @@ summary = "An implementation of JSON Schema validation for Python" groups = ["default"] dependencies = [ "attrs>=22.2.0", + "importlib-resources>=1.4.0; python_version < \"3.9\"", "jsonschema-specifications>=2023.03.6", + "pkgutil-resolve-name>=1.3.10; python_version < \"3.9\"", "referencing>=0.28.4", "rpds-py>=0.7.1", ] @@ -2160,6 +2207,7 @@ requires_python = ">=3.8" summary = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" groups = ["default"] dependencies = [ + "importlib-resources>=1.4.0; python_version < \"3.9\"", "referencing>=0.31.0", ] files = [ @@ -2188,6 +2236,8 @@ summary = "Messaging library for Python." groups = ["default"] dependencies = [ "amqp<6.0.0,>=5.1.1", + "backports-zoneinfo[tzdata]>=0.2.1; python_version < \"3.9\"", + "typing-extensions==4.12.2; python_version < \"3.10\"", "vine==5.1.0", ] files = [ @@ -2252,6 +2302,9 @@ version = "3.7" requires_python = ">=3.8" summary = "Python implementation of John Gruber's Markdown." groups = ["default"] +dependencies = [ + "importlib-metadata>=4.4; python_version < \"3.10\"", +] files = [ {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, @@ -2336,6 +2389,8 @@ summary = "Optional static typing for Python" groups = ["dev"] dependencies = [ "mypy-extensions>=0.4.3", + "tomli>=1.1.0; python_version < \"3.11\"", + "typed-ast<2,>=1.4.0; python_version < \"3.8\"", "typing-extensions>=3.10", ] files = [ @@ -2618,6 +2673,7 @@ summary = "Promises/A+ implementation for Python" groups = ["default"] dependencies = [ "six", + "typing>=3.6.4; python_version < \"3.5\"", ] files = [ {file = "promise-2.3.tar.gz", hash = "sha256:dfd18337c523ba4b6a58801c164c1904a9d4d1b1747c7d5dbf45b693a49d93d0"}, @@ -2797,6 +2853,10 @@ version = "3.0.1" requires_python = ">=3.6" summary = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" groups = ["default"] +dependencies = [ + "dataclasses; python_version < \"3.7\"", + "typing-extensions>=3.10.0.0; python_version < \"3.10\"", +] files = [ {file = "PyPDF2-3.0.1.tar.gz", hash = "sha256:a74408f69ba6271f71b9352ef4ed03dc53a31aa404d29b5d31f53bfecfee1440"}, {file = "pypdf2-3.0.1-py3-none-any.whl", hash = "sha256:d16e4205cfee272fbdc0568b68d82be796540b1537508cef59388f839c191928"}, @@ -2847,9 +2907,12 @@ summary = "pytest: simple powerful testing with Python" groups = ["dev"] dependencies = [ "colorama; sys_platform == \"win32\"", + "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", + "importlib-metadata>=0.12; python_version < \"3.8\"", "iniconfig", "packaging", "pluggy<2.0,>=0.12", + "tomli>=1.0.0; python_version < \"3.11\"", ] files = [ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, @@ -2950,6 +3013,7 @@ requires_python = ">=3.8" summary = "Pytest plugin to randomly order tests and control random.seed." groups = ["dev"] dependencies = [ + "importlib-metadata>=3.6.0; python_version < \"3.10\"", "pytest", ] files = [ @@ -2978,6 +3042,7 @@ requires_python = ">=3.7" summary = "pytest plugin to re-run tests to eliminate flaky failures" groups = ["dev"] dependencies = [ + "importlib-metadata>=1; python_version < \"3.8\"", "packaging>=17.1", "pytest>=7", ] @@ -3115,6 +3180,8 @@ summary = "Python client for Redis database and key-value store" groups = ["default"] dependencies = [ "async-timeout>=4.0.3; python_full_version < \"3.11.3\"", + "importlib-metadata>=1.0; python_version < \"3.8\"", + "typing-extensions; python_version < \"3.8\"", ] files = [ {file = "redis-5.0.8-py3-none-any.whl", hash = "sha256:56134ee08ea909106090934adc36f65c9bcbbaecea5b21ba704ba6fb561f8eb4"}, @@ -3292,7 +3359,9 @@ summary = "Python client for Sentry (https://sentry.io)" groups = ["default"] dependencies = [ "certifi", + "urllib3>=1.25.7; python_version <= \"3.4\"", "urllib3>=1.26.11; python_version >= \"3.6\"", + "urllib3>=1.26.9; python_version == \"3.5\"", ] files = [ {file = "sentry-sdk-1.40.5.tar.gz", hash = "sha256:d2dca2392cc5c9a2cc9bb874dd7978ebb759682fe4fe889ee7e970ee8dd1c61e"}, @@ -3623,6 +3692,7 @@ groups = ["dev"] dependencies = [ "attrs>=23.2.0", "cffi>=1.14; os_name == \"nt\" and implementation_name != \"pypy\"", + "exceptiongroup; python_version < \"3.11\"", "idna", "outcome", "sniffio>=1.3.0", @@ -3640,6 +3710,7 @@ requires_python = ">=3.7" summary = "WebSocket library for Trio" groups = ["dev"] dependencies = [ + "exceptiongroup; python_version < \"3.11\"", "trio>=0.11", "wsproto>=0.14", ] @@ -3912,6 +3983,7 @@ groups = ["dev"] dependencies = [ "PyYAML", "urllib3<2; platform_python_implementation == \"PyPy\"", + "urllib3<2; python_version < \"3.10\"", "wrapt", "yarl", ] @@ -3940,6 +4012,7 @@ groups = ["dev"] dependencies = [ "distlib<1,>=0.3.7", "filelock<4,>=3.12.2", + "importlib-metadata>=6.6; python_version < \"3.8\"", "platformdirs<5,>=3.9.1", ] files = [ @@ -4007,6 +4080,9 @@ name = "wcwidth" version = "0.2.13" summary = "Measures the displayed width of unicode strings in a terminal" groups = ["default", "dev"] +dependencies = [ + "backports-functools-lru-cache>=1.2.1; python_version < \"3.2\"", +] files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, diff --git a/src/pyproject.toml b/pyproject.toml similarity index 97% rename from src/pyproject.toml rename to pyproject.toml index 6ff76eef89..b6ef3ae60f 100644 --- a/src/pyproject.toml +++ b/pyproject.toml @@ -77,9 +77,6 @@ disable_error_code = [ [tool.django-stubs] django_settings_module = "hct_mis_api.settings" -[tool.pdm] -distribution = true - [tool.pdm.dev-dependencies] dev = [ "mypy==0.982", @@ -131,8 +128,16 @@ dev = [ "coverage<8.0.0,>=7.3.2", ] +[build-system] +requires = ["pdm-backend"] +build-backend = "pdm.backend" + [tool.pdm.build] -includes = [] +includes = ['src/hct_mis_api','src/data'] + +[tool.pdm] +distribution = true + [project] name = "hope" version = "3.0.0" @@ -240,4 +245,4 @@ readme = "README.md" license = {text = "None"} [tool.setuptools] -py-modules = [] +py-modules = ["hct_mis_api"] diff --git a/src/README.md b/src/README.md deleted file mode 100644 index c3f3088d42..0000000000 --- a/src/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Development - -## VSCode setup - -```sh -python3.11 -m venv venv -docker compose build -docker compose run --rm backend poetry export -f requirements.txt --output venv/requirements.txt -python3.11 -m pip install -r venv/requirements.txt --require-hashes -``` - -CMD + Shift + P => `Python: Select interpreter` -Provide path to `./backend/venv/bin/python3` - -Oneliner to refresh your packages (from `backend` dir): - -```sh -sh -c ". ./venv/bin/activate ; docker compose run --rm backend poetry export -f requirements.txt --output venv/requirements.txt ; python3.9 -m pip install -r venv/requirements.txt --require-hashes" -``` - -To ensure that your change will pass all the static checks, run this command: - -```shell -docker compose run --rm backend sh -c "black . && isort . && flake8 . && mypy ." -``` - -## Testing - -To run tests, you call `./manage.py test`. Example invocation: - -```shell -docker compose run --rm backend python3 manage.py test -v3 --keepdb --settings hct_mis_api.settings_test --parallel -``` - -## Linting - -To run linting, you use `flake8`. Example invocation: - -```shell -docker compose run --rm backend flake8 . -``` - -## Formatting - -To run formatting, you use `black`. Example invocation: - -```shell -docker compose run --rm backend black . -``` - -## Isort - -To run isort, you use `isort`. Example invocation: - -```shell -docker compose run --rm backend isort . -``` - -## Mypy - -To run mypy, you use `mypy`. Example invocation: - -```shell -docker compose run --rm backend mypy . -``` diff --git a/src/data/RDI-VALID.xlsx b/src/data/RDI-VALID.xlsx deleted file mode 100644 index 501870fbe72d0fb1a838dffb36eedfa1d33deddb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65764 zcmeFZ1ymgEx+WT&1PhYjlHd~Do#5`l>EIA7xO;F7?(Xhx2~OjI;1Zn1f;8Urbn^dO z{yq1cyU(3FGk4uJEY??5Z+%_;>hrwyRe_2;0wUfMR8-U_K8z(QPyXqL0JiUL!(!@a z<Yr}O;q<p1HfDEQo8xqOyJgnrqRLYOs7phMnY`qbt}3%KsuY$8C1UfOT=ii;DdVEH zE-y)S&6%1EsKj`X%%w3$hF(ER2YOSq^s4GHcv-5_c6LgKIat5Go2j(8g<KyNTnJtS zWJEApzfTqknylF+XKqQ#5WWote(X9^KbknF`Q$p9ijQG=BHfG>RM6y-j&jt_RYBtS zw*H-sK3>UK3#R)Hv48Ep)VWY>Pf&3irxm24zRu+h5>uu7?EAMU4!Q2V9Y3ROeDcHH zOeu*@zM{RqM-h|%QQ6``@U-H(j0zcNG$H26GD3kjhv`@5h1*jKNcooK2^rZk<#x0C zE5<TY_q8l0qw*%-p(c&^8q)cv^+Me|SoXs2zI1+#lgF#c&FoBLS4zB{FrU50r2|ig z4Yo6D2vl?=*M1Q0HZ4mtUI%#^c78Ik=h-|`PbbZw|DMrx>XOyRU5laPY$nLZI7#C{ zlYORWh?kH*tiw}~ly99nUWM0kFEKDbUIjGe0-iyLs?YM|#^+UHbiLJ&Sm~KZe$LHp zBny1wsYPV*d#gWkb&Ed5NWE-b_H)d8ege@K6Mn)zi<6F*rb^3Spo2w|5ZfCR6hx7} zQ0cIPBN4X0_JlazWU|FUTG%!VE9VP{i90SL@`7M5gScm?ke<!PMxt;`0F_ujQ3VHZ z9f*=W$Ma6`hMb+#?JVP)$OXU`;>Xm__EhZcWiaonS2AJU&v9`AKkcJA$i0nw?(=Fw z1{EitrJwKjgjR~*6U@Y<;;<1Y<=6wG2nfUm<k*cZ5j7b{!t$^+s;MYLq*S`%qX>w^ zkmP@#VETQc{`ZOTb3bll{765fOenmHJPIlnm0<EG_$N;k>7G1M`42Oh@?SC;;Am#^ z*L-IFC5fIC`+&fPjx>dPi79oj&0&zJHkhDDXQ~&EfU38}MhYBsm2<i$sF%%+WTrk_ zm;Uz1V+RVA$B;#j@_aI2oPf58k*S>1;il5`nRh9^Hh@uUVUpz=zWuCcarG;$IqWjz z@88c+jGGvx%lXZceRb5rXYaHH&Fj~<2?3!$Fn|Sc7YSuC6QX?W*s}-ew<4~($?tFD zj;QM!MHpE7*H*<CvNPbS*y_=o*JJr3Q8H=0?`*r_ez~DNa_hPoW+Wxtf98h&prF}* z7xb!_@-i#I;F)sm>GK_>1iaDQe93PN#j=*-HkKA!ywQ>*(#=*tVMrzo+_@bJMaOfY zmVU)GnjL&hzdT3RZgt68>_IsL=C7tWp#gMlN@i__pLj%AFqOXc^1mytoGQ}(g!MIK z4~S*aF+0}cY6kD;YqjU=hP^wXc|lh%h##>SUM+C|7;jV^yESNtQR{2=t!K!$A|aP< zW)Cdl#{XKCuJzTG%&U)cz;rPSnqM)kL(`^h<y#|hkp4;vZ|rvR>u!DhD(7QJ#ScD? z6NlcDnx}G(1yvI(pRDH$<#_s(Wn$8w8eel#dVu3}oQblg#6XfQ1*vTvHJA@8qka@E z^>HUgeio~#&Q)ta>k8wmFw|G$R(~jZT!WS)DD`frzbV!Q%b3?>vmX~TU8f73PUh1I zCkf6M>x@Rd8-F=Jz%DzHZ`YyskZlIMacEmx?lHb}<+Dvb9Jf1OoIW;exVRKH-U}p> zcO;rLhBBRp4jq^B<;Qm}((!1;1Ddo(du*!}D85PVD>I~>E0=57yeUo?715afCI+St z@H=8Rs=l7(+0@m()tj}DItE`#t-q{qj|=XxXkN!`h5BqfoF#oO!arfrU3~_f*tJfd zrCeCyrzDv?93RW^c+*-TE#mbGq+ES%quE25p*7gf^0p#(?Wa0q#Hee6JEfWQ8>t#g zN;}!Kfd>Z_$Va6!?(xi!Z7A?a?Ly#QzWqTOBENSl1l<~f_DGVvMU=RdAQAgDj#0qB z_Iw@l>endzTb?QUOP=|ApNdV!Um`~W9*1I@Ho)d7&L_lnYMJLas3{a;GP(MJh_3>i ze+id<3B!I14FeHxt;@y0jzmLe&fnuKTf{b369_a|#Ehe;kmuy%2q?qFz&KQcGR7)h zh~aGU|97W8QDLgGhy})pb|F4}3lIAw>eE^II0`D2|5SMXMzi#9>bwf@{G}u4myZ9r z@K(y@-?;X_YVmL_@?ZZ{zVasJf2YxHw(QY-v#VLM9}EjoP?IcscqS2^JTXfB&p_cn z#m3*z@PC1gx(Q5g>9*1oQBCz`{*I0Ej`u|ERuq3=<Cb@1#@()(I1Yk<kiT)=2+|p{ zaP{6iW$BU(iLLBHZx-X&H{i_#aZ`4g^{}RkM0r3{(?f{%@#yDrrL-bIvc~Hs;g_*# zN3<S;5LS}f2d?*?taAf|boea@E?pLnv02H~+QQWLFLag?o}t&dxy427nTuLS9l=9R zu76>pl=qG8-EVA+e+{Yme~gVBzpycD%E0a`D>}+g+#^iQH7i{LTyZ?ZDJ53S4Qe~$ z&#|MJ1uMSOa@<y4aAdYXk=xU8JLl#b31eJuN>DuxlIM%YGXqM@Q~QP_I^Mwf_d!Ea z`f&qx_l<;HRg;}1uaCV9OX{(GqWW~@-U;z<u%qvPYsA>5D30eZdpaTO`fj2EBzZ21 zn(o!#nnSY*`ht*-uyygMIRB}c>)Ewf7Rep&t4}YD?~ty|H!90es#S$l9^eN30y!e! zL%EX~S`X<LW!^#rpYlEufmessx<}^#!>f2haHPt8Ol{(x!=ZIFhTREfQf?;!EtUwR z0l?u_-kYt}I))MMCOSr&I>WW9v4+<zfL|GfVr&(J1fv)fSXTYVZ2Kq8xY;{eJ6W2U zIXkiZ-u&(xv$7T)@nq{mayB{<iths=xG0zWP11B#xZcpcX<922!>C7onlKCOgnSm& zRAV~7;dNrecp#Gx2`$m`eQ^Lj_k%+2b{5wywC49x@N1f9FQ&gYG(=*hkWaD*cP*u7 zA3<nsggUzUx^CR>cFs4C=Dzz)R<}G}gQ1-dd*`6Z!$}JZo=H1_)>hvp@a^vIG7F2L zzTb`a75MV*cBiEwQg0^~SW#2L)8f~4ce#1DJhyl-bQ<05>yal-3EezAxE=T&OXl15 z+DHHKZ0I($Aq$A*=Ofh7dOJNbx0v!RWBCB!Ck%D%IypY<k#qx0is%~{0v;VM9{P6X z@3ZYJe*PF(>>;1%R;HuEy}Jt-+dn**%klHMpR`R`x3f6AzfHNwfcoal7iaVUY#}Z# zXFwlv=%T__^*Bq*7WDjjp2d30FUjnJ`6Wkp*vJu7^8`AdK-8tea5$QsZ2hIg<<98a zH?8mEgOt~L+hE|Sfl{?$&x7mT!O_i;`JJsJcoPi7&+vmjhA!SNip<A8J{;L)Jo<Xv z?yUj_$pN;}&D_ys%O^f=``e(!(4K00=AM(BEWbN%Ct^&!N#SgW!`*yJ#xaoP;v$wO z#~!LEa&3NbDxCtgiCVol?ec4VaD+ghrviE{%br)@hs_~BWhmrv-R~r6y4a{IyxTsX zM={E?I%{EYrG+hnd-X!e&iFKAK@qo#n8WE>aiwp-c~bHD^36T>V_vkiVpWGdF1M|X zWi;+Qn$V_vF87+p$Jmo>9-=Bp0U1eQ-vx!TvaR4H?ghu;ZCdCp-@NYAhU{FA)b|!A z&2<YOIK=L8arIA8FKVePt&Uzdtl<obta+BR^?-|8@B8?f(*+J(%TDx!j;K#{Ve^VQ zL@HCd2m@UkF-xV%FvsciI~Ign7r28)s|{?(^=&dDjpmt~MSd7}xYoJE7Sz>c94!q^ zT;#1R4V-)#SZn4E(d$?ca&+jl2X77?-`*Se?OpC(f`Ju4ziflmrtahO6D)|W$o#r} za|;jnaM=%X58mI2>^V(e(?c%`mVeVM9~q~6l=yPAgr=h?d|cs*frG7VBx9z(V%D_B z*jPQmVSp+FSo0#`D1SYf#(+Czoq=XiAM@9+rm0Zh)>+d^cX3f@9A9-}G(feCCjBCT znTxMFwSRWIkTaa0EA5>^eNhH%W>JO~Y|ztCs6Sw4T=q(A^VDn@`QB0^Q9xv-e!>V> zCbd|2FQ>qe<k49ZY?i{;Sc6zRdaVR_s`#S*#XjSgvbVe6yK%A^Ok<82;mY53D<wVG zyfr*x6e~CObxC5?Wb0{{%Psf*c9o=}dE0!<_@(?jdRgnM=D-7p0j}a4va0p=>Pm-- zqa3k{Y)4D$sh+{nncN0QwHq;MYOcWB;$5H?{<PXtfY9iC8BT9IYMC73p{gq_0RwjB zMJq9E)NIZPkwdgjs=84Le_vJqg8a8^Q=B@m$y56bzlEHHJBRFY_gfJ2b;?9aVdJ#N z`Fi!DsnSx}qN(sw)uQReV#UJ#n7I81?%Tbvbj95qhjALG$r`SVF#&sPZXMo-cQA+C z=9(u|U38=?K04(8+AB0Rsdo{sTr@rO*7$Y0^_Ng~oJMcY>erw%P6O3eg`M#DJ?Q+} z)2yqzJ?_9MUD_qAd|AfbGc^BpZuj~|w{q!$41o47V|~kdI$Z?+vgu>ftx)&NnqM>E zzni12X}x~ewo>}CzWV09DhGqG_H0xH)=RE}l{ZbbE^T@r91a~@X5r^|?8+pofvak0 z1}PmyFFOVz<^$*V7!4!W9-h|K1lLSzb-wHo2At_W*;CH&c@yZ<yea-B@FH|3@8uU^ zy))Q&+i<87`98<uN*B@XHZ)5YQN$BdNmx5|90kp3ec$V&ke0|}O9mi9R@mJ%2Va5R zxcDS}w30oe3+A&QFrQ7XiP-p#?OhX@+#a9pEbXOv{?sPj0d+W+v4gu7Twj-m8c#|a zFPo?eVb7KEo+xPS<+nc{GKc`lY1|toqv)(9?~v-O8Q9mhchUmwO+5g~LsqFf;hVM$ zd-SgtZ`$`CM57`fvzTbT)}uz-lxId>C{LE@3-XjWX2-;%vJNb$dz^6SWCPFN8h$Tg zs*&*6A9zk2pSAYlym`f={+q$8QyS}RlTss=q?}=P*90>gYT*8B;KI;*E_wEM3halE zMO5l_BY6w3P*Uf&G|RB_*)-2&^VQD0+09Dwel)ZZz@8JY+oP;It}D$B18I=B(Ud!> z8iZ{zOS({(`>+6mnCyY)G47g>6VITz9U&cGO5lCHpwidM-Y@p*e3N1$gDj{0D8~Ai zBi~u<lTUZPCpdrbcob0B5kw#@a?B2h*(p=pDH}KSU<RUj9Q|oR6))9&XT0(DM1qf@ zEZaf)-lU*c_``lhAk{#tVuV8yo=F7WRy>PSJ;_!-NduYaDBFvShx<raJP!A&tc8bp zt-g`)9w1*%$SV1<G-ri@@)j;WFI;JBC`-67#0cQdwUsjWJEY{d<IHuZ?1&||n0ZJi zyUi0Hg`*Ju-2$dvs=aw#Z@h52;{q88rFJ$jit*1&S?@%vqb?@pnX(t*bV-pCiKCLQ zad@mnl2~kLfKEDw!;$TYJ4tj{UG7HWIOs_dRBqXb;?&qh)`nF1L-ih)!M5>X4>Z`f za<A(pU!UWsUKqbVH}l5}FKuMI6$^)1pe)gSTf0@H=(JI^JSD)w2l;o?=&by16^!h) zX(XMzK78KI6zfwDzL2=&PP)Lm%<wk)6@yb#zQy}?saaSW0f>v-;sQ&HsxC;VrA0-O z_Jk95+L&lzZZ|XapdJ-xxwDaR^`r(4XVD6D#hcA=O<sI4WDs#@8ew0h0Xio!ZACDB z6PjROL|#AWXZjTb_M+&K*!d`HZja=H^jLwcMk`EytDf#ds*)c3WhDu)%F(TijTcin zZ9e~tp{#I_z6SngtGuc~_=k1iIR$XJzLOHzafor%zZJvO^LfWdhs+I@GZT`<j=m*I z+`dR<;SSTZdOd-&@e_=)G--Oit`#B*b`;d=@~12V^W5hv#pg-?l1He-@zI8|?2daQ zQZ-a*vnIE7!Ce}@KegjcztW6+l8Zl+jDI?Eo5?hS$Tm`m5*M#vWpZv}wUw^u%S*vY zZWahLA<#L}1OLBZf}v(l$J%>k!O`J!Vx6^=owF@gj)AAWEmn9hLlOF;*U5(+>%ok& zyLBrsS}J|xmAZI|;}p8K&BFD49Vk0&7yye3$$Rykar0*MKt@$E&$F)~rUhE@QLt*B zhM9e)?fceoL4GH+B!+6FUok#BiR#-6SsJ-crU+ScA^${EIpTyIFA^=AbKGcqlMC3H zj9m&jw_G<>$2+gOV+jqcf$Z4+=AnGg*X8OCD`}+@k}j`eI`HO_O0T+@u<ASC00rth ze_Jr}4+|P@LxGR;a+yhh6{<YlY>Kd%tP+vuK_$wY1@YlHZL_oowuQqIW%{g563jhq z9)j*WiRs>Ef8tWgpGD0|5^q^*`nv6c)IkKj<G^lor^~c2otDgP)-oaDB!VBWjSjDE zf8uB)=tH<RO~LqcHSThP#fPjAhdH`Q>1(MOb!EqCGby=kc4{;E<UOi3utYM}&P43? z_n~_4*FbfTqco;TGmlrXU9bXVy7mz#C%SVLU$@%t$WXlQPf!u}`!;n=e8-FSWwX?| zJf#F>J8U0|$dd>+NiY%g;v`wAR&Xr&U_e|oU#olFO5x*DYp;%Y4$q~e$%C0VVB{`t zeJC<BaX+bWc3%X;Urtx<`}*$bD-5r+$NuFWkm}j<B>kz_emr1*Vd0i-xbBET5`F@3 zOrwk}o;bQf>Xr@UMR}k~-v*L1SDUfjH)uAOG{@0XHoa9jd62R5VFJ?D3nmR^kDTi# z<DW0c>lltWuJc^x<=OLGlHP9Kt&SIaZJw3o^;qrSWB6o-y!YtlpJuecj6bXpBp<>4 zR>GzVR2aFNV^?>C+c{`fVBt{r#A4{ilcQ1bQ23y~QJFVMe3xG*3JmFv2fyn1oRW4> zX9!eqgbJJ^6S^jVTW;@J`V5Oj;7${LhZXqSa%tC&R7V6>_`21NCl*)HU(poL4DSqR z4r&%Wuzv0Od^22FWZ)b(oYY@fB=pnJPU!QFc-sYK8@P@sXZT!$%<B&{G+{CGpa%M^ zPlbkbuTHrU-JO_!69NxY9oiG!`HGX5ywWBctsZ#)^g7;<A(Sv)vozB@ZmV3GvLv~? z|IMuO%xzN1ZauN8PA|O=N9}v>=v!styFX14{?nAjl*L1sDTa<vtHjxQEyukHtU`3J zG{0>3H=1doN1F70xxjN2hBCjnoC0+zB|stk+kxSoVBbNmg3g&qy*cK<-2RkwES$FZ z?rnZl$HQ<Y^3g2vk*Zj;TZ!b7^Myasn9K`Sk2)v!A;O%O$06O60R5hhMQ3FpEiML1 z&Pzy$u+rOF$3y|N>CGn+jy_5FA=_aG>s(7c_Lb{YS$-Zf%vpX&H`JcDJL)D6R$F)G z7(Q!L_|ae;?#rY~tXrQ>1%+tcr_qn!AOLxV*!gu$e*h>a^vcTElP)!v;RUYqP8;Y< zzJ6e_L!yd`MvW`acsrv0sb+x<x4mY^$3PrFu&~UwoN>H0?Qj)sy*HhkFI9_h@(!*} z_Nd9^KgiXz?UeX#YO00!PDy-y)uz29yMkyA?{_F@Uup9}vO+99$X>T~FX5^e6u-@% z$4StSG>)I>5zI=N==pjdf0bmP-%kVwX>{;u2obj5j~u*XBEFOR9{M0`NK-5o94rOP zr#p;}k-Phc7gI(o7nADY2YF+G<+D0dW?4u{c)E1{Ir>&&nuc&JxawBh7qoY<>R$c? zGhzv5MD!m<pfUfOM#OkKKz=7w|18~?so&N8^vh`Y8K(rJ1r-tX%RgdJBBokaAGi8; zv_BC<Jt_}AkEHYXk75vk_Kz57l^2$1;%B>)zY|I{P)TY>Yp?9cCF`-!%Un%BW3dT& zexU}lU}k?SUG;Gm`mQOGh3?D@gh1Ur3{eWN5${R)_o_Q9r2BU!v2ro6|2>n2J6{GR zvE&mq5u4&)qd4k=t+r?DQ&xQA*DM)O9@M(LE9JoP*CQ7h+`By1`xhbImQg)6`;0o| z=NUd}T|`MFM353yHJ~9ZmG*l4S&xeDPs{La=fwF?G|TpKfu5KQ^!ss&*3vGHcdw~Q z8Ki`vbHT7kWE|?vRfBT-=Ntd1hp(9bW<7kHa^uN3DH|%6DuY$}`qYpPYO?es@eUab zpU%3azJyBDP}4X#T_!j`CSCZH!IDLT1vf$P@jpl;aHtgY;IW3fc<Au-0-!;7H3PYx z1-60m5I>dA`cIi*peEr3(7EU6BN=mqq^8PI=JcF!RujEId00xKNWA%QE%sUqmb>V0 z|4m%ljy(LW|6aM2N7!%utx(@7W%I~M3}a7|b6#@Nd<d5{?2%>fS(@&0exyE?Urqr? z{BnP_{r9Oo44-KNzr`u7dg1NsGj=vE1}V-v<!iq;Y(j>9fQQ@}!+a4)^@;c*s%9B+ z9<Uvofu2N_WHo_Nx^$HB*vyzA+WS^cW1Ox=rEqDEGV!MPPxVFrUG@KjRah0!Ps6If z^9}px#h(2)(W=Y@OT3u4tzu=$g5>P}H#5rdw{w_3i!NAlGKl6V<Ik)C(p(^E&Pxw* z0Oxm$KP+&&U>D*VmEpWw|9m}En{i7!e~kkf;c*3v(?2z!r_KYa;oO~N^bWuY3*mhG zW{yrTq#5P?xFTFlWR~%>%5FY>YBO>jC0xIl_Dy#)<lkk6KO+#nR~+(KoOjA|>3|cM zl(1tJ24nia6LtTrc8@GS^!y2c8~U2fY#t!U!?nv}tH1sSC9pl#=hc|{44-aE8p~bE z8GpFUIf2R<!-)E2&%2HkkaAaT#oYIXNgg+p+iW+x!+&1kd5qO#Aw%cl;+&mQ6PLyi z;yNld$_>FLO2_6Z%x0VA2=N?LnIr%aiVK;PTA+iY^0Mn@Ig&jG)h4w;gz`crWfn}} zsG>iGs*}7RLTRC`QVSArR6(}GEJuvz;BTR_&{mm+AULWd`)rmY+jCHT(j7$jTgVSe zi}5Tj$UZ2w_}5(;l}SWUny3(NsRcY(AvgQ!EJuLnA3_-++%k*TV1>f$@L7(}o-}Hc z5}>r-!r8EPspwMs*)V?T=u$V>5p3fJJCcbm4TT-aMAydRU48y4thONi>|Vx*oc@S1 z*r)JM;mm?`Dmq;hGM+iYvp%@s1OKn?qDI2GtQ0D#&*`F2@yrpQiNgoe2YgKxHB!oD zrBg}8ql<cmXO8r&4?fr`;A^X>kzp<?t4b;XT@*T=Ir1}cgy6D(uM46^cDby)DybxN zQJ8q<D9`#3f)4_|-iR9c=CX>aq*BmDJ;yUgeI||=Odt4_K+Gs6msLh3m5MG32haTJ zvp&RNtH7_kVn*4ytSTz0baYX8c;?TZi6aG<1%A~QGpf#I)mBMmqKkToXO8x)4=MN{ z@T<F+QFkuudzDmHx+nrXbM$B8$iehMUsJ`5CUV&<rcya9#|Sxh(TSXpqxpgcGsR5) zO)&(Q_IYjW4I!d0YCz80jiKi^e2Bi!;r^?a`LBKH|I*p$yXeSH$U%HTUoyYEpLow| z!J5iJFh=-4B)%cWdPqWfoaj^Ty!7|!2Y79q3jM?<zsbOQm_zv=y!QVq3jP-^Vm$Gl z&7wAyLwk&nX&3#a6LKV9&_L#w|7qeKu9xngkqpfOQgm*@`Oy}nJ7?fontH9@{4_@U zU#G>tYayl+awuO=U*;F1i49hZkEtBIV}vBT=>L1fLlykz^)a2*lq^TXi~^4mR?<h5 zhAW@7+_rZ}+5UGZ_cN8leT-0W7oE%rIhHSIDD#WS#0Hzi{|Cj{&=^GMtEZ3hI1eRM zH}<kW$rEnE0SUdrn?1B&{8ZroXX(#BM)?1$-$!85LkZ)pz1BH-&32FBh+j93Q^*Oe zZ!gY$81xhME;{M|z%QIrh(DirZ|1A_6VF*KP*XV&#|Ytf(f^P5PoJ<^gr#ywj1j)x zMaOmu3F8y*&wOP(@%;aY|M1C&S!4{BYLT3*)uYU{%x>S?W6;CqE~F*n0vLIycd&d= z(h&5mLU|8=e$uz6v(46HdH2>f)=%GXEpzosr2QVa*YGViqsN=?erxw6)-A{YLa;Lg zuw(9lSbDFYo-K|8*0T)^vvLepGS@I}J{$rM28Ir?O4cV%Y=jN79-Vy84i@KF_F&In z4WdOJQ;$SawETQ?ejY**em!=-x|MEVBY*OwlNIhidF=k@iyZ&-*!_>!-2U<K{m&OU z<ez<IeU7|=>mJJLmGC_{jPqFPlU)(ndzoS^8#1c1>E(>*{Ee?WW0T%}04^+!2I62| zKE}LlB-Z-TgAq)eiB*R#gqOIw(G3k$MtP`;UFAxgCZjwKnck*^b*f)e<JZx!=VXv6 z-Lh?zJn_+s1w+K#HSkGfY$A*qzm~v(Q$}>(ylQMm{Qf5UvCd#!IRaD30AulTD|ia- zv`w3a4Ly<S<Kt3SU%bUj34`#O2r%w1A5@hdDmK90aAWv)-f;UTAN;od_iHR!S*MP~ z((%VR8{I)~$+-owJ6?7?rp#Yq5ClHOt}P7U)kg2O>3wa=rhuax7k}(?C$A<^P;qX0 zohE?YvE@K+s>M@}6>(c}boU5-^zZ>60bTvP+B$B3cw8KhTv#k;Tz5nQGuD0GA7d|a zpp-&>u3jx|Z43IxcFiJ!P;0OI2k7ICUpLgG1q!*^a`n1C67C&8#+npaTlKj+8HilI zCU2R~Shjb|Y38|>7CyR~&*->^wn2{vj!Ph}PG>iHhZXp{+f<#cYx-+~P@*3lLbxW^ zKl*m=kAbWAlk>|N9W`QC%N66Rke~J<hQ94W5*?4~pE|m|d)8M^gjam-ecd2<2fm1> z02`=xj}M6o1F|zDn{XOz4!z$~hB}a+e}mBZc6GLQ-XA*X+orO>)b8xLJ)s};YwUBs z8FRbdgPs7vb7Bt`TdfX}8EJrR<4Zs8M<|P$(|NvCxr!a-DUwL#_&xUw7k)8BDqC%P z=#*T%dlh;EDH*q4c3bZ7^Y-lUJKMTuS@MJ4U;%;0<Ski#zE^R@JtwX`A`h`z_#%QL z9&5g(6_DFAG@lN?tH+Du)f3P5u7~5TRmj%QhsA-bzTJztRd+WqAO+uz{1lpX%e`>5 z&f@CVXbe3zxxDVESRR5x@s57B?M+s**mpl(?@kUuAGZ!<?!Fhp7VIZeOD0pVICi)7 zw!B14a)eEmCwY0C$*wO6-AFR-xvz4DM0^Ixhpya1?}Tx)g>hr{o{Hy$E9Zo--gAG% zn)zlibH;JDTnrVwJ>a-?7aE^hNKfcX@tlFo%0fez6fE0Y>^9=l#hE#WOf5rD+=oMU zQ!#0<vJd016lq_bw;+)7Q%QUq3A?ccO?K|a;f%A<Mv>oDr;Dp`4pp+c(g8EXbvcJR zS?xZ43hlRayw-=T*YQz>@E!PCZhU4B9b=U=N6#$hdirhrOF+#62~OX_iI(8DbZ9^8 zP(Q2D&w9)>BkyYgvers*<J76HAOt`%?9<_^0=#ibyKw|GE*Ky8Uy|YUk>GAUw!H7n z!U{K1eheO3fQiU(`-<{_^`T?6y8u?A<wt)Kjy_D`p{qE;t5iZ}EVB?H(-0x^5D~)m zVA!0YF;v&z-xiyOy}3~W`&!rzvLDg$D~=;J4sQbf6oJAJf#Q%0ah^3&wLTX$ll_<N z;^wU;<~Lt5eqg5kh)MeanHv9iy#3Y15C1O5*G&f9O#z)}`_oArL3IMb!C|POcUG2= z(w(8*3Ac!E&pPDH2Kw{IeHa#$wH*ug9`2E^lA9OQzURUH91_WbMed{aaH(m;|4f}> zQ0<2|y~k6kx<mzks+9PgqJEyDZ9JLsA4&m*L??_CWg%Y3RAq@{{)O%NZHb>gwDvra z<yx5+Rl~TBPV5Io`l3-aCq9tMl-Du&7ZL#l=w;{T-7%@E6CV&{-qP#H$yPJp^}s&7 z{~}HUAEHcA#_aiqdR&$(bs9Ci{E#dFrJk>Bf*XF))Jctc9aS(%QN0MZNl{c^&Bu0U z&nPD2b?43~Zcm{6>CCMN?uzNXSmXr1d7^CjO(S+6Rj@!&eVz7aB6soQNw3pVDnpg@ zhODiE&(N2KrPRBappqswFe%EUwFyW7Gkalh){R84A-e38*Zl*rQ|A`TiJ$ipfa2RD zJmDGIY$}NOW@Nax`3`<iMtv2UvNH`DR95)}>V`&f&e@7crh}@LKNq(j)Wj7(6248p zb3?TPk-yG$m?M2evMLvNAhIgIe?a~<y!<t|6_)wM#1*$sp8gv4V3*wUqfm6oNfF&T zT&F55JGprFYnt+}JtC_Q#DuR@Z7KH&!A;S*p^e!((S|r}yl=6rh2V(uN}93*qo=lr zK;QEAxMojc$QC^LdEP49l&+Er)#p9xPI2fdV@U7kVSwiYvzI)P`QFCbmY(u@+KKxN zO$X^sN=fgJM1x7CW9=$oQda?v<xPu`W)b<T1B(OIN81aqNCK$%!8GSB41Gd}_Q`qf zj|VO+?vF!5gVrH<r_hIvF7K!dM`vM=ThN~U#8FR9j|bn~9*^JswcmZtWJd?|VQ(IK zw|vbaESOpOI8C%X(Q8`{t!Ie+?x(SMQnY^jgO~QIW5g))T8rKH!qZ!1cK#ZAJwAD5 z6*bxYINy`=9cugmYwOXfr>(8E(}TNx>CpqKybNi$y@~}cgRZ|toPN#HkadpW)WlQ_ zC@!;Gsi+~+vn;1QKB^e>>GByYdaz9jYp_cMOaq25`m_y6eCc+=eT3lL->~$~<NEW+ zsQdKU#}eMel??Ke8;>S{vYAy%<(!l(lRueDT7Lgz&S@@z`}55_j}_v~#JqYg_OO6Q zJN=E_KrHUCAU(f{1cwx-g#;aa@IDvLT=9Le44<9|Ye7pW1wg%C_~H7o4~_HF(#*O` z==*q7M?>^!w-|i8Q{5J~#ASh_D*(phKtZs^BfIN>i-t5HS-OJ!Cj4->fb+D*7vjDI zU|#oIF*LBv>1tb86TG)_>MFCDADOK#87+}f=bOQ^Dwp0)LIAu6S7!Ol-PAODi9p%D zx^}469+M5uEHU%e)f#++`|A3+xY=M{k#*HotW=-+f-g@8C;dg94sQB%Dz@VGIC693 z>$%s7Zkx{PKUa5`dR2FyP7_k5v*q(qzLlkys41e$ob|))q*t4eUlu}CwP}I=gcoXp zeT}$ia|I^sE7q5)OKj$`mh23gNbWb^y@LbX87_lYy^oq!FJhNh2kd%$xYt&`ELP)} zk@nl*TY(m<XNMQ7Y0W#jtveP4tU6Zd7F@ARtZec1MGe0Dyy2Spt?~LVji=*45tx5b zxVMGL7GDAf$@I6z9_<w6V-y#K`%&2A1k;(=kgs8el%MIm@aSoyFOFY@8FxTJ@f}fn zVcN!B9A6lJDnAVQ?t-w>ZZzdmanFa@RZQ%u$m85lH}9O1(ou1o^`T9y*u`L91c}I~ zO^xym&vTB|sXBcN{f}QAVSe3#`BfL@*VKbiw7BF*&ZeCZJJ*9`v=b}H-p+sL*yPC9 zPoIot+CA&m<NIbNVG(6H7`7XQN?nwBAg5Xi^(X0~S7Vj|!ko(Vr&Ap}#Gf}5PJ6Ju zopUs?Zo}5D#!e<idDC*&&+BS$iV<%p<xo`rmZXDk(Yo^j2=Id(E_MJTEkwq1x>k?; zYN~ifFROKC=x1gnkqgU0AaaK3na93!twA0ll|AK^EyOz&;CC-YM52nC3_UhgII`zA z;6L<Vz>p~e%<bBs&|-*Je#ga2%PIf1;WelATMi<QeA&-yfs}csIBdn|Xb4DpQqF^6 z=24atVWPzWfp2ia18qM`pHE)F@;%lG>!1YY?TzqK{Fvx9mr#mg(SnN;Bd7dZs}B%P zRo`@ENMn7r14C!MxouhNI2e!H1?G3zN$yj>p4C;7&dZe5<L0XwDs=%(9x$Her<8o& zxn2PES9$dxMa;laF`j4HgE0SlmI*qPYOQx6<HuE?xl?kT6?&qs6`yeAdeJ}A#5l;D zVtP6rF;)3=+;S|8=G=TJVTnmhZbU|%@adQe;omYF{ubH1l!D&u4Y!pqc)K)|Vidgz zme<~zif=sUNdqg9bKJ1>=2V>yOVb=@1@}wAA6gwws-A6czP6ShUv)lensr+S@U*z4 zWE7NjLu=bw7uGbo)6D|sf%3{lQg;jy>P0gZ3O654Zpv-P!Y}9S@Qx}%>l(Wmlt~UZ z=dC}&foG;*(6buW(w1N$)E)6w^ZM|wns1oTBb8vw&=XoQPzD$?QvMBTDAq;mUW7~3 zH4W3$Hf}tZNGrP{HynQD-}3-V@upB%ir;B8@mXbo^AwY7ztb-EG`4U2;K;HrOTiZm zK4N*&xJSypI6W|6X0g`3rhj?`mJ4EEnb)X+zUD)EZB@sr<`Q1hOFc?-l(4^1e~n44 z-y9ZX+f1r_*s#BykBP=oK=gx{svO8?FF-hR*u<*c1@gy!LpU9QT}3!;sZd6<(JWmz zOR$p~#T9%@If{@7ic%Y=o6A)Df|$q!p9?QJ&)(5{7swsOUPj8>{iH80_|k2Fwcm-K zpCv}1<j%fi;B0DZ0SsV-v{)fj^vXL6a|bs?R1X#37=spV)h~e`88Nae-5Ptkp}i-& zElWOPjJetl{t`1jVw8Zcj}LPRrG63jRCl0m8jrS*rtkVySFTyJ3+_h~5UwRtUajDV zfyjDVvL5!`0C<&#AAn)WQI-Q?-=i!!i#2dRE+^a|5dP9#5|GD4ty0b*$5o)=B!`5n z@*Ou$F4+=z1P?@CVokrlA_w>z`Lo-yq1X0S@SbS=oFiGjC+cgffhm2W$0NnLdL-v^ zwy~o=Tp#sXu+<Xt0KD<-*bch9v-|$b#qsf~E8xBc+v*fVzO7CH|HvpQt3l994G-lC zh88nDR;RykU`4eaa=HY3BsBhk-;S<++Y9shy}-IFd*QmDDl6MPwS>G>qp^gw6ep2- z`O~|J9Zy68d8V$S30@=%#NelLE>dOYi64~8eDgGFKFcNh07(!Tabc><Uy}nwCvl3_ zjS2Eiteg!=c;2~Kc{y7F+b-*B-ntyLvvkm#KLZ2avk)(y!hOJN&M4_(hU|h`zUTWb zt*sc?Ld%7gMhk0>ukphCr3++S1Q1(ARq|>LVkZWJ_#Am+*rkP|_V%YYwjE>8nF~&i zyn`fMM0S^zd@sE?pS?5TmTEMWV3cY!mndK>O7~yJOlDE3E8b^BbTXCto6hgy1Nqon z0rkB4=J?t}Qz6qUEzlcy;qrV@eN6G>v`MspIeEOHs|I9n4%|I8(NwvYfL*(gxok4s zo!(S>K3~ba`+UarHMHJ<;>xk3Y*CL=z()--kqvY8AdKwf`-!IVsc46)S=a8<0yx)x zylZq7l-@#mBV?RK(1~u;DWl;7w9m-DC?0YbtG&yT*^9Cq340f1IUXiuBF6>SLR#sV zYpf{7q(Nk^VUhSjs%$s$88rn1Mi3`b(U1I7G$uF8K0#rUUibT0mZ^ega($~t{=#9K zt!U+^WcQOqv;Mn<r<iWJbJ3ey#mlD1a((q@2zazN?;59fz|B>d%5GX(85xmFzGg$p zqk&@q%@kHWbZTaHHN7taJ!)8vc!I83@Eq{&UkF*jylh3-8ReP9j5mkol97*8GUTgT z>siK3smq@Kf>M_we>zo7QRN+Q;JHR6{U|x>so6hioPM2eA@MyV@(8}-^%Cn+r(5n* z452r<+$*HW`{8sk_oYjxb=Xy6S(d%Np?cH(%Nyw`rdm6{G6NC591|+1zLWyA?O-)_ zxMh9O-MSi1SbOwD&PDuz{y7|))Mtr<04o(Q98$voTp>Cvtd`QIEk4~^!8ZM1WESxa zKKfB`2gf?Mq`XorQo;L8aKWd4cCuItDO1e*-%h^yXO$Tq;9t-^lw$x;&`!9s@(4dd z¥xM1Qm>I9h#?TuwK<5#lc9D&@e?{`t{bS7?Bgr*4>8^(H#_O?Vs2dMbBEr!ep7 z!Q4sYC&zkax_Q7EOxJnI&mh+bWbiiX{4sY47Y((F8^MVZto)n}K050BluS`&c?{0? z1ly}Oz67^2KYQ2nIpAL~uRs!8p^|=#T*oa}_dDGj4D;w;D2S@KJyA5t-Ow||*TtK* zRTRfvf80Xj)z<lWjEOKu8gat4>JocwjR686R12*1zT<I<&d@N`0#kI`sMv@9H0(~Y z?QClm<~DeExCO~A)z~nMf)8IR1)wglglRx!y5Y4@N4r;jn+J(leB&)7Nz7&#`ImY} z0wDRo(n<&ho@Uu?^MUK_<s!>78h7GX|C|xMw$h!^;V_tcRVttYvHdfLX(&ns{O^%Q zyCq%o<}eX#?GT3W_f?V1*7FxJ7wm7JTb_8?xLB*yWTc!3tQ@^_Q;5#66Cp0W8^y#^ zaoy-xd$*k7TGzJ>LpMK0qBWvL&qV7!5v11}-E{E_d`RGhuo3%d@NPbC8yn*$-3jB} z+LYPw3#)y05dM~^D#SdqcpnMm0KikIM5Xx)y4_4*JDdE@1;^a>jX&Hw$C3!*@|H54 zYZ+#4T*_NM4*3ld7%w$^I{Kd2+tGzuao%$c`SJ$c{V0T_|L|?~oZ;lAm2}nYzNqJA zlE*CGCm<@lUh7<6TWLBb;Bg~&;{m_rpcII6?6M!De=d6%{2A2pnQ#g~Nz+|y84Ua( z0pIc(vw6)`unHWQ01Rj8M6VM-#R)$a8-B-2P<gjDNjM#kT}3dRjvYOi_yOKd;E5fq z=I);D^rxt0sg@HKrK>Zf`a5o-Y2+^bN{Q2{5Y<YzjT?9$+xjIvbcD{$Jl^UW4zC3* zuoZB3KPOT#M^8`6*0DD^ba@-ZNSIN)o@{0vQ7N06pH~W%G|RX?W^c^7!kX71=2)ny z${x*u$7VIhlRet{#P1K{t2=NVS<Eb8LdAE$GkIy?2!o1OL@=m86Qe6fzpkRcOZAK* zM5+Lwe)$UrCVs=|Q>`mz^^*`w{kz(U{<#!UGa4^H!3dD?)e>tg77FX`?_5@W&Kv@_ zhOJ6+{UK$gZ*4A$jd&t8R*^iNZQeR4n!dWh*XPscak_fl>Fng6JI5zCuG!ZakX30m z6Yg`5(iGRsKXe{T-9ZW@Y&alj5U6`OmfGRKS_Z2>PIC{(HVF*FkW@Cxz~Ck5Y3 z#^_R0*>LlzDRtTMxhRRE>E{qvTtz{p+Un2I##0IBU~X0|=PP>qG|YUALeZ?Ob&R=I zI3J^6JoPsiGsq7Nu=WBeBep>BMkaiK=H8T`%N-M*uxDo<0L)O~vh_6c(HvSf_2$); z;}W-y)so{!ZLu|bXp5^U8W|#+X7cNeVY>KK1|TNM#&Gk;ZFl(M(N*42jayikK?zI% zzOequ-mgIz-mkIfnv+H*E%l{eBklQLJ0t{m>4Efo?Rsy8vWY*zLQYgg7@0vun7IM1 zGRv~hQhf*JUQTTtVVHXj=~PKh%Zzv@;#J8>Yqx(0x8F$~4ubXA_XXmq(Yw>^oWVQw z2AXV;hacj$!1^Hi$ZLfs*tKSOvI>c5@A$vj7CDV~vB>L@*%iT@+Gu7D;BhB3&X1BX z&R1xj`SKpN`D+Ty#Dicd0ORBr=;cW<f9v01M;~xoVC`Fa1a}m-D%rvi2LETKH*TSD zYZR&^^C^vllZ)&h8W5TVSIo6~YF6=xyTUP8Bl_MKk6q8d5e7UoEdofGZpXU~cg#)I z)nDq_z+-<j{jKEjTDHWGgW85otOgxyX$RA30~94p5<-KJR61DT=@M35SW{E!{x#}| z_o>?ImoEZmkmd}e|LAsLox==LQ;%Mn!TT&pVkxKXfD?XBZ58!jNy?y#+G#fu&|o_2 ziNfU33a<90-(GOmJKZMY?Q2|1%NMcQXtLp1W=UCQvAw=f&VU#?uMtlD)PU;4lC;(W zrf6!~L<jA(mX}?9tT8_;48yLN_)n+I!puIxV*Uvh^G|6<ok3q<o4*EJKzcYfoC~Q= zg)|7%S)c*yDoZqaRLbXNkQjiH^2EB4Y_-HIy=O=S$mL8hcY2DBZh4YYI-W$epXWR8 z{iyEr6sfGEznYB?Mqk86ct+@LzqT#{SkosZmN4{v^wAUGu#7l2-W+q@`y%suBNUD~ zk@*FF=XK222MISa{<cis0^oC83o0C3i+TYpWL#W}w|{NJVp7R*ts}bMvO4^urJ<on z<yG$(O<D|oX@c&&hd~1Cd2^pRP5Yo^wh<I}6Hq!#MDbtybKvSt&SiNO`(5x+W|;MM zrk$<`bh+)`>s*{=o$q;_4RRy!7YC+taa4J+#;0-x?NT3%mu-#j_GkO+Tzot<6v!^% zDIjC7Q^ADAn+q0it_0Z3)Ksp&#(T|G77B}Zs!09VSm{5?M#H@Ls)Vrtt!ik$dH2=b zyq`B*NWkcruJziNj5(v^wM;&HDIcHSjL`c#P>G9;<7bbr8BS+K;kWGT(>&cPN3q{_ z&dmKN*8n!6l$r*gU^hYUN9oicM2daRmoPULljN@qRfiR%*6;5l(4s_cPnpfuOn=J1 z<4P~KGzYPnJ!g8S<uvh^`uVtweGj86)kpuQ90mM;#+wdq`cvA{O7$3XBb7;RfjtcO zh0E(_V67H|8D5`;;$pT+v5jg=4yT#G%CXjD!(j9B1no7nwsfSp*<7zt3ViydaoxME zQ%L)wV|+YxwcB^77%A%=SZ_u-K*p0JN~6aFS3kedUpb%F-cG4<1yUUr)YS&NZyVo# zjM#Df$qsU}d8-PEQCvQD1)Z4_RQe9l{G~~pvQ5E@i-2XRXj<Ao1OHf*M*Y~YMm%LY zM3rXjcT13Dwrfm~o2rVber;a8Gnt><L)GQcx>NJ0?rU@JHrP-<DfN=WW}0;Fbo{zu zSIK^4ZnvJ<Ond6Xi$fib=WrHMpFuhtt)hI^zBr_rivg=Sk-QNck=Y{7j;iJ%DXNp{ zd8OH0%lV8ILuq2Q8k6b&9kcqwn01U?bu{<y-7HNmlbL7|9Y@L{(szqSqePvH-vvd- zf#3J!^b1|g;r&~^KE8$@nYRm*`TS0+QMMgvv^<FPm_RituR+-Iy;VogeJdZc@!I-# zp9Qnp5^1rq<RVme_6RC-V91%Ny1OHymn6Oe>m3Y3FHJu>9*pd>_uIgV-e%D`C!I_g z2J|-nQuoaWm%o6%M&%tl-QS2Q@F^W5p(xTQ5Y}T!650}sC%|~AmsQlGjR4tQOv+&e zlx@SO+?+{EE}+Y)TwQj)u=M%@)H0{H_yK`|usNd-BDot2yE`c0u(xNvN`bANRIM%s zhUWX~2t9{$gE=;3E+`W5#HQoKhKLdH4$!Y^J{EdLvP!5QqRo!NQUIHWGv|fYb-XRp zBT(=j3<Ukvx!q1rpfEDULz`tM_?ymm!D?M045}6|a$*{Tk<-ZvtjfqWVFDPe#;amB zSd5N=A9ZH$QI0%XuWJmYxta&aJdHW98TIT$*@EmJGoUU_Ld>Zh*h%P9j+&?4&d(%z zQ&bB<Lys?7`Bh9@gIPM5u36q}(seU=LVHKPR1z9UfsZ-^@)>6I^qeigH|)>et*$!# z>#gGr`HSC}W&Ib-;!<DUPg+EJY1U}O2_V~th{rONvcX{0FkY3!4Wwoc{H<zA(HpGm z$2sfwL(R*pv^)g#m_c%bJ+*CX;H#4@K+Ex=u-{VnIGAa_9Y;U_4x9BCn<l4?gKb(M zlc{*UrXH=38q&$>L@_{;Ha!Z9xI%JKDId0;z+ygE`B0J(C!2w}hP+%;zJ|3N7m-u_ z(^lB~8LD53Sz}Gp(p3M}4km_`jw#sIYtayDXHJKBc+2?n!EVoUOOO-OU`^~PkS<1m z^ogT^|4SD*cqg(G_)1$F8*!rvurSrjV>fE1^iz0m4-c0n>-q6<H@E!nO!w6%<fv@6 z=KN0_=-BMdb)VR_AWbcJB_zKTT7h4{+_#+0ZIP?Y=$n5<GoeClK4FSW5kpfv5%oc~ z>|Npq#WE=qC2p}r3>k_SHW4^a6>|U4PNjv>OwBTTo5klz5+QW?!%$!<$#k(z0R88A z)Mo4BIXVywuRDu|D(KmYUwcwcyllrpp<Vdu-d@xo=oG1PE${4N$3Ca!ezlnU!&C^h zVIiDEZ%NVq<1G#&*S2Qhom9)KDeasc#=QvMXD{$D0yehaV~}=Y)@dM=_v>QRQ$Lrg zAP;tuYbw-Wlxr&1C}5+_@L!JIhbj6jFb|&dA25rn3YWgrQlr4$Xj-s*8@f9K8Nz>0 zQ2N9G)?lj@Jl|JbPPWEqEaiGm0RCwG9wO{t_l6sc%QD3Ri$BUb-i@P|SKGRs*Lt?S z8OwLb7Dcixt3iM$6(Gkxty)yi8ENTgM=w2+cT_^x87V&j2+!F`>7qyRZq6s5*(PlU zgGj1^7=eB+Uag95E?q5pFw+L!P6YmwMlHPpmCjM_671aXo5L{)m7`qOE5>;A>UDyJ z#b0!Qj-Z(ji+UE_fI~@l&7$LRz{^GyBCE;1)c~-y<6yJjya@2<&LYyh+*jEB_R!UD z-p=8C*WAlP)2q>Dyxs40KU2CDPJiAfSq_81=6xLJ>EZbguyuljmj;3R+cSkfTxL(_ zddu$yh?*@wgV?MHs(c%ei*(h>Uli%8mrtiADE1ypH^x2VmRzSUbyYbI{PV8R{59-| z_(`&CNU{TZBO2!KHXy>;ji8je$ZJJ<ypVLBd0(_6m(z-8<&8G<nB8?}*AHw?JNLsA z^Ekq{+|bL=Uf8htbQWOeSAj;N)NIa0YYY(0jbDorpR1Ys_}-fH$9VNJwWWa)&dZk$ zwLC2dAG!z)LbY{5YYMVOO*Dl9TqPqDPsVw5g#wEIQdBo5aBsE)6~$Ope*B#YK=^WC zqB2wJb_Mp@!~%>0_g_SCw+0i8y8IX)aOlE0ZgmwySwafn!E!_U9$jiOkJr-SOK;#n z;Lfp5t6lB==+Se#4zz{PScDQaf{I8)5s!2tPRJ`pJpg>gno}-%NEsVSrh0PN(sdTo z3}H&l#S=gIK|XD3?=`_Dm)~C;>kD0>7idnnD3v8AevmBtp7?=t*A(~TJscfO*=aZW zl8JvTiz+0lTjatc?X!>c8pm))nNK1J1^rBWfwT`J*lt+gUuV&`*pPR0yMSA;)R4-b z;p-(e&i}Pgktftk@eX_i+?jKDdg<(Tw%lx|MBhhB77lW|!z=Zd?^Bt(0R%)Qrv8+5 z{&;|D7^KnvCAF4xYPx7=WG6zvn=~n<=Sw*#yYtvownx6tQZq19y_~v8SGAn0h$xzG z5pl)W|7qKM_2=jsdGvoHg12d?<x_M3vlqXdn^}Ny!J7idyz1v2kgcN=3?qYQBy$b9 zX^lGyOYjMHh4SQHCK@T#>#BPZ%WtmUI@DW9zsp;`!0fo}m~0uspM$-$0PV<>(fcVH zW>D_`%$*aR`Wa~{i|kmT?R<d=f?=ozjJ#$-;Yfvav-O$V`sP*L+)XsQwyP5;A!HZ@ zbMhy2|6FK+c0E+V`nB|ORICaAAOy45>9Pf(l6ZMQDg=2x*eaJoJ8Uh{$G#r*7b5r+ zkQA#brdv}x=O#T*<E)E*AN?$g@u5X37}J&u_NQvzj6Z+|dpq|9%duLUw)slj4qUWg zUz!q8Bn6_7^7XH9%-9Nr9gt6ommA|Uk|YC#k|M8o+w|>2Ys3`_^W+o?M`GTRRH<eC z)wQd>vd@0IR<q!r5w?7Kwm48SK5vJ_mcaJ)FSmB{s6?M&B6P+S@K&?5IO&_@Aw&lc z7lsm}9pXF37}QndR=W9PGU=RdETj1?*)yL+w>2G9gzk_wDHixv-*L^s6ty7=!2x5M z2Z_xev)ULxtM@4iD_CgUXGNq%(X?_Wotx{1r2(VQVZ=!dSw_e-2#GZDFLdw}GxrsY z4!Y%4PIGn9{{dg8xvoS^3Fu7wu!g|tARr6*$9pr)b{HWT#%#7~>7kR(8}ExozN$a? z@v`r3Zqo!m0__DOe`++c!F14EkI^Wkr|o{B8}^S)9O~{HT&-rOHK7<EpK0_K8I`t= zJlCiEcwn6K#k5yGA%ZtTK9WZKqvOWRPyT*WqVj&zbQ|{mX$j4A)i?BW5o%TRbCzQx zG#gixt};j&`KLG<f~7R#e=gl~V8f0uP=Hk8@&d@|=qNEPQyggJGq4re7H7PnxuWTq zLpJB!p{XaXGDkexJxdcVUg$d$^q4N^VQrl2CcN$4g4I!m`rS}%0Z!0PIiSZ2remHF zooa&l6|wVxZ_r54JnahyJUhB2v}u>&0k@z05{g9Sp!t~g{GIZlJR{B@Q-9%CxOJh; z5EvhzhK!PGc)j{#x#D++g%w#Ti!U4U&JEp0koTi^05aN#UwUe};0<|=lIpwVA*==u z=Z8n59L_Jd+1=-^NDt&yb$^n)5DGcvz8@c&z|^{5)m-h~R1w#Lj1H;i!ZTxthkI~- zGudw!3Q-*0@R5Tp5|X{~M4ukn5hOB{hSXzL;%vtqvGq@XlIQrB>X*PQd#`?J5a{s_ za%IK+BAC7uSPLW9c9Yzpk1*1@i)WJi)mwdsL6uI>5SV~})&x~QeIc;ccRB0uzp?k# zQB`f<!iv(33W`b#iV`BJbRX$PC8Qgqq&X@eAkrNY62bvNx&)LE=?+0aTDqIV`PM$5 z*L&X^-}lBFzrVh59lBtym~*bV=9;nga&H?B2ifqHo30;RwO#l3W~_%x@;@H9JC$bZ ziq8v6X{gjxEgjn`ebzS4OBL4Pdg1Rw-~0d@DP1C*mX}Y{3p-D&j8hdinlYZhmUF!O z?~*K~P7z}sJkJ=G1H1F<S&a_|X<u5F04`YYg?klp-Z)pmvzkwzo8ugA6RaYFM1QEo zZ)-)TNNK<9&Jf7Ip(hZ#-H_p3n;|gYkj>OnZQ3|rJojK$yGzJw=fKs)7yFHNpz{N4 zMOg5cl=<nas~8r++atzxjMtc=ozB~SkOIC+fHFZV_cO3gZk+0U7tD0IA7@boruI?E z{nPsM0PC+r*A&Z{{yQl=_gR*5Iy*-iEPIp_$Xa4I(OFBTG{{;;ZcW=3kB*lciv`CK zzArM>x2bg{Ok62B*F2)m|Fa2fYTY8kt%sXa`>ysIE^dA5=}tA)JEi*EOCsSKDo#8} z7#E(HUqtNRaK+43DG<}ORvJ9}qqok(cj;M>JfU@grXTRHo@-at``D^CBxE#$RZ>kI z^CtQJ77}zWMK6Znb7OuMHj4WzEA&t1AXH7}&<1hqILHc(dq6yB`k{aWm{f-%psfi* zPvot5-wBp<byj}ex1Mi`^NmId6wGubIt^y&+OP(<jkA?r*6YF&Al_mu5XQXq@RhQ_ zrpAx`Pg+CgiIxHvO>Rs)emWR5`?K@M)LUl46=|f_M_{H*_N<sh_OF5<OZU6TKgD|w zh*yu!-xd3}c-hYxRh`KcR)f4%1qcP&(vNd3H4qBSb<VTP_?9GyJ!(%0mgC&CE~s`( zu_GpDt8^1CuX`F^1}7YS3N8j7m8tJ<S;JYZhCq_F?8}eI{J4ibBGct-_SrjTk9HlJ zA2W^wrnaULpSiowS=P|~4un}2>G=rHJ#8kreGkCu!2Q2l|L>l{#K8K4trds;J8oq^ zC(?H2JNtuZ)^LKHHDFQ&f&D-I2G*M%pqCscbz64iU3s-w@IKLVg>#QC_P4Blyy;#s z8q|=AMLpi*J~lp|m;ICuxomwfH@S_a$}gOxp~CYv0pr4|*+qmW>_*vr_9WI)4o;oi z25@X8$nuc5%h9jcdH1e(!x@BlgO{a%ch`Jk=HCPCWP<2J^x7*nc$o{N{dsO>-XjtA zT#X8(ukWz8InUM!7tg&tL$+H~v86xPqqUGC_1;&0pELhm$|v8Eelf+hkgI~p6Q4_| z65-#qZLEXi+jPES*%A+ZjN8AU1bmbh@KKIt85^C^ZkMs>2PS|$jV6EN3z7hC!aT9o zj1<H&|E7eBW-tg;9KCq|rlBBcJsXM+T4UsXTLH{Fq^%iQZG^z-XY)f(J&7HtUg#xP zIOMy~0>=_lA!Gl>m&%a}Bbt$$Cud+{Pv2fFpE^M55z7A?=a>s`C5mTwVOv{f80mR4 zO4hA&wlA$>izM@zT%F=pnAwxiPZ<G7LOJ=z90K26p}eXmIa21<7m!z3QX;(N!r%My z#(gl8{71I33j8Qu68VFKKcf?7d6k39<S;A>SBFRVq=Jh=)xu+!uggQHs*@?ig+*q$ zJDsVcVvl>oS5iLAX{PBQ@sgsc7OR=EsoXcl7p&HXy0xodW!P_Y<VNajfp1FHyM8;; zIF8t(%RNRe1V1By7f<+n8zjryR3C}PyA-eS$5{#ji!b$0i+`2`So{d-$INZN&)1s8 zIU_T+xq+W&S-WT|c!jt6Sx3$-S;*o~C|xP7kg{*{rQ)K-2xg2UUht^(D0k9Bk`c4T zy9<BzvJt#ulgn1Jba^c!exc>&x4{XKJPj45)*Fhaldw``N9Z^3w64X-P%QcKX_jMK zkxIqN1Z)F0|KU8bj<4p=(`x!D*xx+;QvH3X{%Q74ivzQddB(@=_akFP#<#Vc#SMC~ zhNdY}rBx*8tqwdrUJvk~1I`h3i?OoyNMEWgYFD$~I;1ZK&C_Ax>3eJZ<{P9cX4S|i z9}kMPr*79A+PQ2Dv#gqRE*Z(!yYgq8CiNx5U!PLLNLuEmsW=xs&FTxh(>sYXbQKK+ zoPoKi%R4S!YPIj0u*9#tuV9(|TkLeYlp4*cAZx*l2p5g=$ul7T<Qb7AJ59z*4}aeN z&@zs;_{?&UOlP1Iim<s)Thv&+@atB033jOa57`qoha7~5U-fZceTDV@IkKpv^bPk~ z<NJrq;Y7(JExohACYg2fs%meZ8n`ZZ*Wn(?2yA`ACi4L)cD%#A<o>kJP3f@7h0Fjd zO*IMu(b5G(W14MI#m~PR(1@`xYP-)FKPw|7h<{%mc+A-R7F+RotwXHg<4CE?lDFrc zPvZZSdCLyP&Mm3n;)CbKGNSrtcS)YZ&K+FsXB@E-jtJm?VYwd46|A^vycM?@zaY2J z8PHL>b#o2l=Q;;h4`e|*ud7xSFWr3N@hQ17(05tzl@_aZy%NDgPO7&HBrU6}j=hDZ zIS7-kl%Kc@w@oj;Pn7t(33cCjb_Fm?a}i{wza0M-e!456+FNGGDgEer*+t;`xK50n zKrxaP2zF8lXS0+AvyQK5{L22IY-&`xg)gf|ZbeYE2>0Gsb1AnWJ>Nu|(#D<pV;&!K zobKHcyPAl7+c%2&aTj|;j93f)S#m49fNj`QDso$PUQ29;BGV^M(^n{Ud}GAKB4fnj zt%tTXIbSo*{3F&YrG6&HI(XhA`Z3X8o2pfu9bi@rVGSc-Sq7$_{3L@40U6ZYhv4i# zrOuAX>bV&9TxTOUr6XbDho9><<VtfPNP*;Ty-FDWDI#z`yXK+YuItA1cw2h0!lO+^ zzpNtF(@Byk^G<9ZF*I&aIP?1_l8--#|D;-RSt|BEHI~Ds8F-q8L#*o?b04sLe^}mn zdk?Uj@%NzWd8sa%WyY}U(kj1X3xcZiAe}j%2J9(5u&2QaxT|yE0o7(z(+dz+b#^HO zy~R59e1V@JbAx1;v&%(OuJ=m{ys?hvz)d*cYlZ*g&fKuJMNeUT+dG9~m=_hB%F2!h zuvTfnTE!Ya<tLCBP6?FHjbNR)&d4%E^tox8ncw}YJ_n(iK8IY%)d^F5j<(EyWYb*e z)Sol<Pjeb4t8$Mj3WlW)qN$>$P2pkM{ZV0&idILn?uH;gB`&kvCHCYJvB%wYR=!)Q z0z@MvV&yV2!rGDmf9X~E?fBsZked>%bzIJ$)H@}o^W{}JXO9z(Sl@=1#wuNdR>iQB zse(S4&}t!FkbckG6_Nxk)>>fqXSx-y&2$@%(FQTj{9T|TrPNQLDD|wE(cjEf0cY_f zFjtRl!M=)zZsmM3QIATmtP|!6US3N?a1J<KR2-xIFgIRZ=Q`5Q>u^9cEzfegcn2qb z=70zsrHnXko5y}Xd|F(d75@<0T3f+&kZFCHQtv?NsD%p*7S&Kfr-N(R8kF|GixnVV zbM%|zRP~?Z{N@bI>Kte4pNBt+FVDDx^r==njEfkt7IN~`PxdSqv3W-wM^op1&~pJ| zkUo84SG1}yy>(Vl`^tbvoCR3%HTL^SRMz}CDE5oK){}aO;cq%v-zLtOQ-QdoLu0sd zhdQ`r^~j=d%x0mX`?P~=>=2K_bG%(nJT<XyjUUV+-%^L&f6BkURn`U$|HRO~>Jtc~ z{PqB^dz|0g0k3<1=N0&Tf@7b5-A%F0_xC{+fbLO42URWvoYcn$RYTyQ>W2UoIG6aw zIpB6taRn0TXx)Cg#H6C7`*oQfQx)OcZ)V(x#}=tGIx4+nYQBwWAK6%SXSTHjG;#L0 z_=)*LKQ6v%uOs=CEd!4J3?DiGI~8KF_QTPy3Gmp&`OO9J*z^w`oq|u%fyDX6^S9W2 zF4BM8#=`5(2)K&bB^vxZ@V7ug-b^06{(v1JV;|^ynhOLbkVN6uUs8r$u9sTiCbP+q z3+%Sn4GgozPqLm`5!BVz_9V@VA<Asa_MDrC79Q;V9m^UF4P5t%!F~2b=MOYCWgEv! z-!ArhFFmFDuX`zC{28q>Wc$yC|D~dj8DBJU7H^?FYNPNh<fbTZ9S0^;;Bha-ltk_Z z``dQ0xgOiH4fg(F^9JjM6s@16MghUMT<hQ7v3Oh<HC)1h?3qQjZRaxjkruo7@5r2q z<TSg2NR;)GiD0}MP(_@2CXhwqie>lGvxnhWOwrkE!1jlm<sg`P@=fu2<S3;;Qua@u zN+J$S>e(}qX21QEBIAo@&Ss4Vu^@WBwFzCmy4IbBPW5lz>;cdFE%;we>MZfU)|%B5 zf8z^_<ILaD8*#k|=r?$>kQVRthR<n&#|&|sY|#fTatAZr-)1tFiGnkGvXAIDL53=o zs+gZBU3W8QPH&N$n)};}7gD5BYo=Z9IP$C%lj94SOx>Dl3TXyiA$^lw><<M(|9(ta zhI~c^+|~8aDe*s(sK<1K)Z|K$P{hA(T6g^%<XkrgzapSa6)$+Atm5%q%J#Q}CS8rz zl&;uorg#ClrRM$xZBqUBhBk;EZC2LgR-b2Ei?OjDwV^tau<FASpwPd|qV@>^jOv>x z4ZMt=42!-S61Sydz<okt$ZMHDiEnrT8P>bNu->(*P*4h1t25QV`(JFUFxs{%#%KI~ zz-C03DMuffBNq+BB6kJGb<P1?y>ehuiU(dx2WZ4fmWyV4Hp^^ZV-Tg|NKvLs#594@ zM#y`?<Qd?zzAy9%S&gAat1adV2hSwGc5uE@Z}<@LOe|FqCYv$6^}MNQiMPnZ**44J zr}Y&h4v&kST`8ZNB-AK@zmf(1N_Kd;u4l57JuXJ}pK&WXor;kz`0@F7+zO7@S5NWC zs;5Jze!yW38A7LiMCz*PEqc#*T+U;7u;@(f6VuNNodu6Z;F_i6!3OyoDTCS9-U^&< zaEMzGI&ADaTKAA49)o|Z{z&3z*1Hs0FCJ$mT%Use@p=SH+#)A*_!Kx`k0&Se`-vqJ z{}Mb_0d9b?+wcrs5F_K?-2jLMKOKhxAo}}9Ea;}maOvR`h*>OMlTVWh6ehEdfE#$0 zH-g+o(#x^xolS+^yW+kKz$dAX<SvCSMEZ+}6Fd~RR&#nyr{7CwhfiWaMjB2Y`smiV zLcf?uKmWRlxwN3&O&ekHAdxglamyk<t)^SC2KDP3-;->c%#&tkE;gAZ%@zo`ENy7z zzDxE<Y*{>C++pvM5Vq~YYN4Vom{fkHjcI7M{B>&}s6iD(!DCx$a;9}>*4ZrfqRS6k z9v&I}_tjiXS{Q=!5ja|S=#mUwQld*bbjge^Ind=@bSa81?+ea9@qXFbB-z&Vzbav3 zGigfp-64Ety$PI#@|0Xmgt6h0ZU2MnB18FyTudyn;p%NLnVB>-`|dD4v%ZA?LABIO znu>jQDBoOff`g&_T`s1pvElM<u>W`UrlGtm7n4A2xK0~PVJ1z(zB`<6t}o%gREK@P zT)Ad0A9k;l8}zi3`TQlD54QZY?<^Nn(|gUh<Qax?HQ{r;2@JYMBT!v*4!1}fY@P6# zoT7c0JD(c5DZU$O3OZuU<byqphdLAxBm4o@fX(<!fZ=5k1l){|0|4$QRE6t<{{1<+ z^gx#`=+Y5g+Qy}UK33?`j54yDlOe-QRlX-AIYS91C-yc@(uj)?*Mc&#pOYciOjV~R z<Wq){znmCdyrdB~A+8l=<PaxAiJ7W(Psoo9r6w=}H1H7O+E7N0axzq#se1N=oO-8( zD=$WuAZf%)h-*t3Il;;B#Y{EGKsw0a-|}Ax{Kq8#3o=j(GWfUrR|5Z02~0`{O{xa{ zTmCD7|F8sLL6d4hlmC|gO5i^#fpgM9=Tw9KE&r9ke^>&rpmS<L=l(7KmB4>g0^!m@ z;i^IZmj6oNKP&-QP`Fx9_`l`968MiwpeG@OW;Xl7>$M5=%ggNSOUpHutXWxCSiyV4 zvy+?-7TQKerrT^ftWhR4$ko-k70KD)e8F3Sx>iXe$St#jx{ZV2?^dif)*YqrQSf%O zwOL>2bze0Kl?n$fBS-7YdA{}n!yZG+JWiD!EH#xV6nOvGN(;&pPQ6`+yd<P+<>`)` zIgHm>VGL-9Z{S&BOl@%9Z{tC23ne)@q4sDF_Z7g)du$}z#+FeV$bh=WYWSL?7BT7> z>ty4~im!wH&P-3;^WlRQRMDQ*L5neJ7-^b_tf;K4(c3XVZXX@F4=yd2>=+2R>{3&g z#s_S}wXB4`3mi_?35>$4%5_nVBg;Jl#6k@orBw%qbKj7JgQL33b+~Y6&tmY3ACH>7 zB9Bh3MIJc+_aUTH^iHV-YJOsYV!hECbuc-wHhKIqzv{-cYhLoFP8Gbt_}{+F@6WF? z{_SOcCHSOfLxDIW_XR$M$Wjk7()tIFj#{3_XVq#yY~?vLLMAXhWulg}V3^`RBPsbn zAWo+?UBGJRlc;#AZNW>1Tj!H=xu;_pU(YcTq~hPAWt6_C?|@DFg3|QMr}y;XVeR{u z3@AO=t{`r1GrYPrjEk}oF6MZUdTm#7_R~;9mr%)4g;G}+Vpx{($eFgNQHnWAdQ(6G zH=)KU>BA3yOr=PDwR~6fHyM3SZ(S(*-@eTc{Y}RAZ$|928e@%;9*;i%9DwUoubHzE z&a`1F?x<;ZZ;0Z~)33{kEkrSAuf2r5AQX){+*a{^oT=*Ok#j*{5<fnSLXr9=e4Fo{ zy@}^qhfuZ0#+1!KDYeenN4Fz~{l(SIrmac}9yZ;YgYCT@_`$}Cr$D0M-V>*-<>|io zc&CAmq$A{hT@U<dy~bm$u5C$I$lb}qV`pj?f7HYIXtT1iawkd1ZF{`IX}~5=(EaJg z(f-I9%Ei(CV0(UfH@I|6@L=_5dw+f!NnGWzVJUc}#Ka~~r^ao+$7c7i|1c<C13+a* zOKkxwV>&gJ2XoQib!y5h?nR--D+j<6p2^ZG<ZaK9;Q=;l8^P}%D-ISL+J=3{9CCg% z6fRKfYK>8FdMb4eQ)Ij?)hLA{kC1u6HS5UyS2Z6!H};oTb{qwc;%Wro`^crr4r<TF zJnEL+-8)ItLa2=zzz^|mqnV>)V|2ODFkA^4ta6Zt%$QrXjBEGobB!GHVOyAQn6z<S z@N6>(;CBdRt0_Z8!apL{19mjVbWn5q%VVRcwe4@wy8Ld3J3H{*oq+)%ciz2?@Zgb6 zHXa^0a+kOUzIQk`J~v7Hrl6+Mrc-EC&}9wjTNz)9az*YBuPg`Ejn9Qg59H-m!|ivr z+Tu|g`_m&%jc^`#2}>~QC_bK=YJB?}(gtq3F;j<x3mq1~55M;?*?<L`gXejEM2%5X z9VH<P%LNV@P_2incQz(Fa(p|!>`ryuLhZ&bt@-YoAdl=5k*X8OvBOdgCk<UsTepq6 z@Zn$~w?iLl)N1^s&E6A~OJmKT?_>>fIJ$?T<7j(LirOzPOw$wQ{-%Ckn*W~X#AIW8 zF!jC0;G)7{IsJPFgA8}!i|f1jEe!w^0$|1&`11a4`9ed7oc>)4zr542dtSqn*rfr~ z3H1)6)k)%>Yo7sZLm|ZLZzgc?SeO$21b+A<QOLgEwi+WX$%BK&OUQ-abG_`a=~;qP zp7_jLx252ukGe-w;xijZn9^M}8Fd{SjpOSkXYMp!!H~TjAj?!pA*;JK*VMXn*z^3` zD+QDbS-c%qvx0)L;EHAa`6{b@7uNX4bg$4APd?p}2#)tm>|%@woahOE(0=dgZMD1u zmxu9>??XNOb+Bdo7CaM&8DpZM_O}D4RkkXUn95+SU})FEvn<z<j_~gc#(a;XemXYg zKx5y=*UMtJQdSYgyd7v0>ToQKS$OuK!|m$rzH>dc`mWA1{DR#LDhmci`c!9)ug*qZ z>v^RfYoZYwL;u`tHW;J+yn&I9mXZF^S>t;N8d$Px&YXF<9Jfm`3f~0Ze(c)U5}z&G zTKQ=+^H!{qT1k0xeF8zNA8AOd;Dty>fF#eKuXQ#Z+?^-xsOw`iw%4B+@lMuhGpo#& zYu&2fbXH)<bXDWe93oFSTfe!n{IXN0cc$nu=|kaG-sl_NuZ~K1xiq~gNzT=sPnmbK zcm04PKw})d5||8tS08XBXpD(B=u-9+TWtb*yd_LA*js4L&tw*;lfT0YP{e#3^+A|~ zd8V{su9nW>bd1qmc%X2mGxx&;)?1<!&mPpa(U|7$$M^V1`~(BtbKl`yP_WMK@livA zM`Zzuw|#TuDb!Q&Ki%5c`wqaJ+!L^6uZ~w#0{-X?YYrNdrEsRX$~*k*PC#?|@Dgsy z#3eJwiVt|&r;H!Gtszz2;yG#L+P^HK2Dd+H>X_y6M(nlC)iOiu2@Krw#%p`{3+Gvp z%%3)GbE4-ePPKNoA6y3XEaWNTAQB%;p=r%Hq~HPqX0scYMH>$L6IeMRPM1E`kQQC& zs5%*0@TP+n=YqYoeMP4?>#0J^t}z@0G-pPLV!)B4V&B-wz(<=rFCb=)2^x6PXU6?% z68M54(kEKh<0A(|BNH5H_u^Il2R`jV2%|UZrQ&UJ?sxd{5NE4nB+OrGXB_k(t~uHp z!N-j>2NPH=At`;?(-h96#FwYwhWMlhI2#J5Pr8y-UlT>k1)9;f)Dw}RQ40JL+3%($ z6}yT~A3%F?)}BqY;rs9#RtE^>0Yu;;{>Ypt1)%QAOO20n!64<H_D_hq!BvtJ{aX(2 z@U_7l7RKFg(WnMuo<OwF{4}2&Bb5gtVpgxO?D4UFD#}>+g;mxY!R>h5mMEr6uUeed z<HHKX=lE(bFj?R8vOL90F!lbuk2SQatu}RYwIqN#<pKxJg^l4S73`3ZzEltZL0mlP zIVkx`vDo6<Nym6uT*T8@qIdXWfH4b85sAzgH{Tu~T0nuT|1}A7|C?V>Ngkc*M-#$# z_;rgZzBhzGBVtD9WL(ZAK)ebt4NcGDZ_t9P;11nijSH?vS6(QtT`9PyJqQW$iz_*_ zNN-pdAu*w~;0UY=BtpuC2RLd-pr6omx%vx9(5iDx!GspA)s@iH2Xg`yxpwYq56bQ1 zWgKc+h=En;`HWj9;FNXuMZadZKH{f+&u4!CmkY-189WHcdOIeE%|x>EeR}`#c5pgN z#;a<cs|nKI?V}gYVESES$auA9eU~?-Y?gHZo0+6VS~e2d;okraSA3y)UmvjumuFut zZ`pcIS#L+CVuZ4PYullnOMIR-$|{EWAn9C?+p?WUG9LW_wGwEYKsQFvMn%OS_Qx2u z#HQU&KQwUh&qAQ&l!de~E`p|*7{sPi*nSE}?Wy<qt@Wy0f{a<z%n}5hlrnO%YLf<_ z$?gki`d+WfCz$4$$-p7JL=vl-)_laYC1w<P+C0CgPW2lKv~b=LW{Glw*ytsWbi&HK zT7V)|+ZF=YNC_1Ku3gVS?72kk>v8m)Exs{hl7?8L_38<S$)D@hkA0nG?*4Um({LTM zX)+riD5*Kb1!B*0iLgYq#2~)-zE)OXl#x>mm{f{Ev?D^E&o?n3aLrv_;3@Z+k#ndE z#A<=A9hxEtO|}@rGFOf{^B0r}wW6wybT0`s6&Us86kEcTL(rUs7cgc{KVX(tLn>gs zX9_Q3%zWQfEw1+Nkt~O$Kx`40$a{$cI<?|F0xbnbTRBCGu4?JXrJGxIX}P%OEHe~o zh&>6cJf`rEjG4uYaPi1lei)K{&f>+x7W?F92)pay1dkOMpUWu@+qsjd1rcXaGy0qW z0;ndMZNsp^v&EY$p@>A3;YBwNGs_nb_jjb#I^Y~30<q%UA}mO^G{g@5rRGFg=J;gt zweuMe)S4*E5uY5jfG@2!DfY6EDZCxbVIM=eZ#U%hQWHZFu6cR&GxYMrL;xHE6d}xw zzP-fZy^NU+w%oF69c&yqU~VT((E5ZA9%C><h41Sr51%d8(iKL`!#T<XVy8g2AWVG- z`X~^)$t_|PJPTH9*5uNUMA;XBjUxfY;S^WFNZRG5v@+Zwx~R{p->Q{`WW(Z<S&-Uk z&1<wkTIIOlrJWK8SI^Mr%S{Xw01^TaJUj&qf}kXE1iy^Nyf5tOxuSHGuPR@SG?1h9 zixS>`zpVRXhM(LxaBToI<d<cB*c`HBs62YsN``-oUQJ-EC3(j%41oF960*R5$McLS z;t_e22z#x{<!SW8jl&-rR~&QP)C-y{KN|x^qCT%QG1MG0a(I;H8M6sB4I3e@Py^;M z5|UX;mO0eg;Q+u(NVdX<Q)JEW31fX`3O~zaDucqynm-8FTWw1Fj63AYPfepHK>byC z*N|-dtd&4V3lqXp7p(OK`A%{Zb9ydl(Fl@cIRGB-YFf3qcmiJq#;fw3+`qO2BkiBS zH8gO#I2x${B=YK9(b6}R-?6rmL$oo$hHd%;h)tfgvUT88s@l{wG<E05vVGyY&nB&Q z6hIK9z{n@B*n#v1y7Z2NUodu)S43ckiB1h@-Kjl8vZ=FMW%|z`QIhN#Dg%g@E&)tW z2djxGoPjB`G16EzlII94ftlS4*DJ8ks$1^NXQsnp5$Ad*HSsc-;mR+YnZlWwGG~vt z(h%E`pVylhT5#v*bZMcj60N|f4N<p{R@<jNGZPMrIoG>U!VZ+?%q5N_+2(}g#2tTH zv@KvX1063n`XN@X0L|;30i3u#&niHHUF2^MNPlZX8<Ut`KES^d-SHR~2>BZbdEIO{ zED7*bb|wRHkZyS7&b<dlEhZTvOvfI~beMz-fyo@f(h$h-!q%h?M!39UFfv>=vXrV> zW>fYkA(`rcDHgFz627<bZK?xzet7S&QFZNzyXA1ta((vf)}WBTD%(NQNA(V<RRC>9 z!=o2s5BWr@Oi=W|{;t0efQa}CwX3Go{Gve&PslR&j5<N;GRvS8DJ1v{=W1gtFkydk zv(tYc$qq5Ty8}$D*a_dh@Q&j>42TIFx}`Creq$UTyK`(z(DQjC8#yU9@7n|k!W}CE zF-s9Elq3gDZUJ9B(A+~0F!EsJ8)2v4#wB3*ld8%djeI|WM#na{MkIw!QRW|gR_#MA zUK^b!w|OR+17}yufwyim+c-w^2-9z`aHx&QS+Q-Z^SJgnj^x9mv*sBc+}@W~48*f; z2U#e`A`*!gr$^_XLSsAF!Dbl5ukIrpI~Ayzqz9y=EY!<#0suP#7mNpUq7Nc6LBu5) z9WUCKD;sx|`wnky%oc))a8BUZrXjXcPu9@1exFpOZzJhgXq|pr^uT#29LveH{>U<e z85wyjY)V^89^eOoE`j9>)D+T3BmiF&gC@6P{1>ntd`GSAFHY8sAs=(XS8j&UAJ8_C zbz8@;wstmJb5^A7iGV{FY@{QhO$xD0NB!woSJA|FjQ^UOq}IC@RW6|u1qkG=@y@dI z1g;NaktI>VpLt;HM0F(-h-+23P)*0`7pOHRiU5}NWNPKrKB`-utS1t(^9aba^N=|a z!qh&wa%A<N;~+Ce!|8c6wRb_pjF?n)TQG}?<dW~auztbDHO^=c1#|4PUs5W(=_rF0 zzip)132R+J$Xq8{7SEh_;&2ia)8x-Sb6x)cN3F0x_Du)L1>bqHS8^RVYN?{e?It*{ z#2j(cjU)yFG*U$w+fDrZ#T=hFIlsnxfl#8|n7QL!pK`ZMfL{_*WI8j%{4}X;CTj|3 z4-T`GPcI&hAL#4l9E4Y4{1G8<j#*!qa<@W^AK^8o8ibd1frQ9+{%6TxCQ-7Ts`UG3 zro5`yil8t5hfI0nRui0;CXQ!)Brp{nYnN}Go4He8n{t=8l3C{oF5*j0YjlT$lCyfW z;t<7IwdH5B0912U$1Y#HSa>ROG@>=C!{GsdD>Dl2Iu~Eh4u5#s_DN>Y=%u$6-U$0S z<Jg-?bG55EL^_w~cv@7a558*c$wt34@mMt`EmugpKPEcUY*N9?zi?)Qxt-7mv7T<2 z>z>h4S18DyH}PFDgLzCH>LAQNpStQ&?L@D(B?Mt4TgxGC&AtM{hOwkx!U}2h_?@eC zYMyXqzMmZE*6#z@z3!Pa=>DRhMu(Q@0kxvaRxh3X^c`*H#I4UV;#1ABh~pmmP!D=L z-3!Wn_K~fT9S-Hr{n$2_&E!bcmLG+cug9`p?ARxJXg5Zt+!vk%b!c+#kM0aRhzdc> zP(VDrUCSg+zSeGK2<a#^i7$oK-cetx?ws&k3n(nU<9ryxJghN~t$jCf>-p*8`VByE z15I$vxnD{U*y6WP)aS)Y-LWrx2_gq=JQ0cz6OUwqavR(%^P_Nx8(OHDZl_%}-G)O$ za8hDQiTb)&94mU~VCzUyZ9V6hs-{VMbyNx@{vtXwsZXkttb|0R;qatF3gBCEKQchX zpvYry>i*<d&moSWZ6}mKG*_{4XjFF^O+5f87c2D;F}i>B*dAdAOunPW*OpA&>Y|fG zGd#O#kkazCP*CwDG~ZTuKx-tM!zGt~>}HbLT*%?e3z1j62CHP`=OF?tYu#L<Xmltv zC0iR36%b;Tuluml2k)PHSc^hOroZX$!qL{dnc`%)n->(T{zlKP?@pr@Nc?*OdiA@) z2xmcUpheHwd3yo<qZ??`bz1L9CYawcAr#+(O-w+v)n)E-BJDG^KNP92*Cir@Ga(yt z(${W9R&ApYkRrkA0Gv(e00X&3Yd?+e{W(SO8PRRQkYi3ruzdGVz1oubvz0Eo14Q}1 zxKNy0k3~m|I9_5NrzDo&Wa&G?L(A7OA-cjUC2UGY2ltWBAchscKWadSdT0(kvlBp& zG7IASV<O-0sq`H(p;;&te4&p7fg7X_$9VERq0HhpO-ov1#2`yKSbhv>?#YAVuT_f! zoPU$e4~&8Q>F?3i4XXK`)qi!r->SNG#Q1Bv`W^xQ?9C@6gXUQ(-qzle@mTAhvjZ)L zri+*{0gD1n;`I4)ceMZf1uoc}EGb$V#XsT4N{g1N9g+|os2s~8x8^vYK~KJUiwB($ zp%+W!wh%q!K{Tp`FJ76azut(gG`$;wUb*08kTZbHW2Tt`$IHY6M*<}V#o5O_&s-lE z3jyvLy{cf@nzvX$wsG7SEX_2q_|SesYd|vODSj!->4=3#CvGW{K4L-Y_;*+oJ?HOm z^HGpuuFXjVrhWW7uvTokvej2oAXMGDvFy6CwZ{PPj0V*)ulYnF2Lx8F+5Pqndv7}r zd?-sx*weS0Ia|y-5PKZ8!R%i=%%wg-Z6UANvPF*;MO|7OuDNaQofGE_hCp-@l0dj# z&nLpt?uhol_SG@10D>m|#aH$X8>1tHfd@(s?Pmj;-kj@&qz=eV?dgZj0<s@{v&=n2 zj5{d81(-4$)&ykL7VtSds$(_*7NS)>#6#VXy^RJCyqT9Dy;LneV%z{|K%fM1kB%}y z*!A$YC_p$B;C3k#A&>b0ION+<+xM2Qs(sZQ@M<WAU}=AEgfQ2Q71D)bNAlTz4z)J6 zKilGTe~-KDuNs>adP3aRhCu~dbjalwm+^#wSQ~^d6j0=t4i8q{I%GQGtGh-D;d%tZ zQ8dqUAit@i23}ns8rRxE^9KZFXi&^@%oxb=&_N{|6ymmQVL}r7J=N1$cJ3gSho+OT zQU|Ef{n7N76xe8v!|M|akZrGnBxfr~rXqoQ1OhcKfMUq==&)6F?eJss;hs6f*6ND? z5W?+}29OFAbh4~d%SJ9OY#KnFOIDp?nZGnLZ_H)`sTcXww1ANXCnK%KlQ`5q*1QLE zhy%iXv>PsaVMQvtH7O5FqLciHT4nlh;sg(GjenNtnEEdo?Kb)d%9@jb!*)mR9ja^Y zHNWuYgYX;Sal&s2?`kZR=p1uvWjh5_5Lhrs8YP2CZGX%J%@6R(LeS!}Krt^mVTX7* z0Mmd>8^l=2n}bS_Lu3DDTSHcD41}h~T<@^nhEl;_)K<t;)DC{r-sM&9tCIOWL#Xdu z{K?3_azQ}jpTJ2j2+8oTTu|sZ7yPw=CA>oMnL7KBK0@LYKvIB|L1Lyu<2VTt2Zl}M zM1E8U=Rm}c0=fz<lI!=HPf)IW0wn~ugLIGWc*%MvCj>!s4K`69lmXdRV}dw5>zHN5 z;0=}-#3AfA(t#K#UO*=zJvE}o8wH(*91MU{C6Lz~v$99<hxi0o-3%a`cfxe8n;4>E z#R`PH$Mgix{Ep_gc)_y%nyl;MiXqzI9Vc=3V*kXCjXvTy!MLr!0dmq`93#V})ee|` z=_)$=ON3Gov>c$kG47XC_Q^M>mHQ4(mIq23rMG@l?>LhB9qXSYg2!r;c*Lt5d33Vi zTbqrc%(EtFwNX@nCuDY0N1se=E|?yQ1&+52nhhu=gj7gT0YwE_xML04f)0oNXiCiN zuKA4vScel01?>HISs;2tCNAI$2ikdv9E;zy9uhxrIq1bWhE{A{pUg!VYcy8LyE z8028Tm<&B|3Pl|L(x79A@MHp{J9Qj?X?>tpk_bp!PIQK{T+r*ctmp?!PLADx^49N7 zqNA2$YB2_$lE*0%v=)*HLP4tKo(#uY$Z3bfIX0P)2ANLnlY&$cm`up1aN5DS{}Op1 z?^yhAC6R<25tQYjHw1k<$jN?ZgF9dW(1$x<uanNY-T7-ZNOu-E{(?dezyg2(;drG; zXQN5DqL0c>_UzXaKM$En%2;CI-x`U=2xX1Ol$TLPe@;yo<akgv2$|1662Rkmg9Pw4 ziYr#g2C_ANTi{4|wg-5vHi$$%2z=H&M-7iYLMIUP{iPv@{?fSCBpygA_50*1KGlDY zY+f7ROcn6{a?r%~raf4G4<S<IepmcO#iwT435GO*sV{7iG4oFj7q2k*MW)tgNxS(4 zp$h76+?Rd+$%Zt4{gH!cH&yp!^nKaD6Zl~_6~<J}E!!Y66~@&^;yL@|Mpvo?48O8P z;K1zn$uDJBOT<ds5Z_+cjVhD$zd^pq!lCXNpsY$2HU+R$9b<{8SSMjb8TsF~6`OLu z>uoPSsP*PnzadAJKz$RNTFhJ99QLA1AIf{lW94klnaQY2za?EJ)w6dz5DA~#<PI$| z^hvukPEA^(Iy=b|t6+2P2Ivw-dM^a`5#n4)yliHvzxTD93=KHM{vXnZ3`PwltK`yd zKA^?@5=ZZ2UH+a?JXYD}&e!I>)B3txILQjWtK~THEj#C9Rc!8L;;Bm8k;+p5YT@$X zuq|${Oktli^8A5>59JjtX-zq<@gge8qTLZsDsdhmIL*S%K$i^A<wpKk!iV&SY}Ook zqsE;PRZ^g8{3|K929r5=tftK!Q-C;*%xwZUK3l*`&bWV=AP(a~ScbQS^q^LHZM^LJ z(o39}%nWk=9&6q#ebuRq_6t^6a?kN#D*7h;dM#Rf^c>}*<lPaOB%*UYwC%>UmRbFY zajm(fKAcA45P7$x59JOuhOSjCX*V9wn%z%elLcZ;8Zz|!UGqZ*eD?2?CCBH{sNRS@ zk~oa<O#`2l8_(MMF4f5y4-SXLi2{}qY7XFXpyOL>jt0>2JR*ei+IGr0?iC{+Wt9_H zo|4g$Pc@D%e9;Of7u&%G+ee5<BQ^Dc&et~wU6(L|DW#CXR42;xYG(>WFPYd_yuwq( zfo&Q1+*UmmpN+o~M^IGCCE>%&pybao2j&~~gOPs8N=_*S4~**6tBqQBRDC%#9QPJ5 zlv!WmQX4ft6vJq+WQD2p98W|wgEz+aL%CK=*yrt%7hr*Cn0sh-Vs2m4ty*j{b$cYD znk-5Zd4s#-TuD@b3CH(Qb&?C2B;rP|mNvYL*J&Sp86HN6V}>m>;@PKlV$vJkl#zQ7 znIZhtBThd+NN+f0u(sQ<zKSdTMu?7(`bB!o4_1h3u}gL~a%Ol_1Hl)*Oj0}^R{mDc zF*6$1eO~$l_089z7=!9oQx+UQp_;EJH6>~Yrqt#4H{_n5Kj4(OnDVJPg-#;<hSGDI zgSA4jZT7pmdvqKtqm=&l1HiQDLWKvlMh3eLC#tx}B6C8-uW{gdx2Z7}H?_K1Ffc!$ z^}o*=j8USR)xfB-@;)@v(1(E;1eJO%DX!F!<vS&znc_YqX2<-d-%sR>b+@_fe;@1} zvTsB{e2a;Ug)3I{N=-m~`V1<sJ#W;A+_-TWcE4XLjI8twiF(IN4&*3zSVT@p5#9Q2 z@As*e23gl5;2jt+#ffKJwI%ZyJ&b2o12I+~(9FS>>rPqT7`SSLQLTg_h|j>+WOebG zRr(kW{c0c`=#T|7e2rtF_VQg5fqN=}JGrzzS+p=oNpzj&%O970exwRN3&Q+zJ>$Il z-U~XF)j(x11O}^rjdM9&m)>ACP#?fmiBqiP&7Y%FHD`WYp8ioN92WMy9%E34b*P!f zJ(ULFydUPKc^y|b_ggcAdnyA|K_*2?R-z&Gfb)`%&3PZdIm`|l|FPR%D|6v$U;#jS z17`hLFXzfN<@?tOlP-isOkt6K+Rn1OWOxO<7*eqR<LS7J?^iJfm2(H0`Q20b-OuB6 z=6||)&4Jdf{DC~y#AVD0G#{5SG}(&f87D4lrzT>CMbw7|dB5)qrIC}IxIBWcnHs&Y zubqB@>CPJAi^bWb!waHj#yZLE^Wpa~M4n~_M=-Do(B-H_S94u@WfNlP3o&X!h|Hbu z?O{||BYeB4NCOkEUte{Kyqsrrt6C)LDjox^zx{381$(|0X<8SY!eHI%4-!6U4CNNn z^iR7h`gS8@cehClYZNcnkG%UT=OL%b=G54v>h9)Opo<7mWw3X7wv|pv%Rw<lRl69? z%y5ZM0pTvEY16k+B>Sb*m-iqY4<=>TL2NYHV)>#K)Chx`@iz=LBF>dJkRq{xHpI`j zWwt^?>gU2=N2=GnZY=ecJz$f0ZHmD#7M%3v3?{2-9xI6{2hA9j@)97x(sWhGLBg$k zQc7}aSS%N*?8CrlE6u3)=msMP!`M@@aAyYZ!+RwhT0J_&>x>5L!_OI}Kh_eGt#&qh zN522qlzjM<2G)cJQ+^wXd-IV?xYFK|aF|{71t+ib{6!pkNv1|jH^3NR=3&p(UuuL` zM7~M0#D=ujlGW76nJ??nH~;3RG)mu6MO6bVjhyd+3Nuh4*Mz5}QV=PkGOzwLuHdS) zN`Yu^1ZWpJZa*c*H#W7_Cwbf1+~^_a4fL2(zzQFaX;zPVuxEt<aNIac{9ZP?H2VFm z)b-)BaU4lW{vVXEs2*PWP!VaQ>=`c%d_-@AsnI9Fu4ZKw#?JGZBwW`XUD|f+dw+|| z2m??d-0G7Q+2eaULDr8DBj>pkF$VQg<kRuC>dPKw^n%c0TzESji6ps|XMV5kSIvuz zR^N>QBd{M4xpG+3rT(1L?i-^oMfP}OQa|B!=!L`ftmu@Q$kt3F7ybOy#@@bewz-ql zT<%z=rz%(@@daU{WISCOYj{njK2>evkzvjud3tVZ+RfRO*2_NAvx5&52K%!wNxcMp z9Io9ws~u}#BB1AiD0*;G!=UZ|X3M*{n4OR3Qq-7ncVrHU!}RxL`b^HBCHP8vCQMWB zOm<Y(dkPnC?9DYTdw(QJ7rZ7Uu&L0b5c~<$(~n0SIt24y6B@ZA4;zuBpi6$2W}S-f z|MlFuD92hvuayIBmec)t?urntd)FK<YfIa`39ATXWr#=j4iiN8Zi-e>F75JR_<AF2 z_@^lQ1bW!@C(y7Dwnq@8N52QP)%MS$dsclxkE^s%fM{-~CPFwDJNY3BgN4rMfdMP% zfscmK110m&1K%s42gW}~lizMkKo7jghaM<xiyoN&13fSWiT*LYhyHPGJEF*{mn}y& zF{~tT-zTy{rMpC_=7XK%{UPpo3Uxbq*svtBw)qQ0>_rb8LiEEu3Bu0%8BMbNC%Q=m z&D@W8v=qvu(M`F{ph^0GK>PpSzhVaj|Ni|SN}zK4($l3oi$f-Z#T_3klJo`y>vBC_ z0_FI(;r}WU_N%Oy2|EPQS7e8?E3?^ZoNYIjrxz**cvf;p50_`Qm-<FV7M#We_g0fi z2l5hykZqfLHp}qwP`9nN9h=;w??@zaYvWxbbw#Bsa=9)k5B||@e`-27&l9!2*E88z zUE|?le*kW_QVY#CwH+(~D)Gbihs(QT;HGZl0?OI;usAO$X%88n&4${cK03@EL(S<{ z@68a89nLGD*5kovq6c)5^EIg19Udpt2K{bN($O6JaGs|}=qhTXp%8vJNYUYmSeT^# z$+nK#SmAkdgq#V0*Q_j#tyH4k)r{q*IXS_dQERB(CzeC#i?Dgw!#2Tv{Ch2JBl*+8 zKJcfOaT+{4P6Le(iK)Rg-i7SwVASf&e&0x=kmus9QlYJ|q@(fW8Ki>H;i?<T!D9z` zz|_e0V}Ex5x%<2$sk+>CXT2^UduM<;Ho8<p$o<gHMhNb4SYZuEt-ovEhHfFR-1{Dl zqJTb^zME*aahc`RDgMU)^(W)e*R21$PsXc@h{rvp7cEsLW@qTZE`do;q?YnrZg_zo zeMe}65UwQfE_9^H_cJ9fU&pghC3WJA@F!;S3j!ZVx=gRUr^UU4nc1vnn?Y(&fs6cs zA>)TjwtsO%#4p)3j{V8P*R-@^&)OKtF<jsIS6BVzqL#c-+{s!oIsIhr@9iJAE&ik) zE?G+cY1(H+cMkrMWN4E9f(*lc(A%@^W0eYJ)8LjlxxP}qJNlOSTf@$X|Mx9(Qg9F5 z(8fT~&gO|dn}Lm;5v#M6rB$e$d>uQ9q{64GbSvSu-7i8S)9@8FXlRwr>YIEMGbcrT z<z^@sfDK`(Rum$uT&RLu<no56E_*2Ut1@(4hjrrIj5K3jFBr}!Wxx%+-1Nxe1Kk;{ zf~>sz<1rtol0G{AOsx%hO?NKl$%Su?KQ9WCxb^ZI((8R$t#fUgEZZXFWfgSsQdD$3 zLrEXaZx`d8_2~6fW`w>{1>0CV#*I^tdQJ78@wbde2mW*{(`09YOTVA^u}bHPM^Qm+ ze!4QZrK0b35OQ{Pdv}#zh)d0wGE!JMx=}J_^(%d|;rS1Bj_;-%9i3{1!zz+7<>nmk z4^+ChnSQ?R!8V@bNk-M*KhPsQ(ph@0_)663ip)GUq*1HFatlPKPn{YC{}g4;oIQUE z2M6br$IW+&Kz_gfoCCi(Te3Nr8d(_~m$xBtwrObF4AY!PVjd9t?etOShx3{%bIrXK zu#79R9J-UA-}WjW(@-0QWPfUT*r#s6+{gC3r98T>{L{4o9{3%+qsOe$?fsqQ1+<@T zwbIvyaUJd%plorUhxkeO%@E{`)03|nZ;VsEH+*%6AmZM+n?*0PA{v?t#4}r^wWcwR z&2P24yiA*4uYbkss;GCzyu3u9ltcVt4suWCaD$hTwz{QG<z;Nnizl+7s=X5T@lKx> zi=A+mz8!^O-OeDSq~+?@O``0H!?kgmyTYlN`g8bXkliYF7gy01_WQQtzEhc3Cbsvp zZMkSFoRR`&)z@B@>F^T@>t(9nP8c3$d$Xh+$R(_1f6>k4pk$!o@|wvR_YWi1$Yigi zVrQFYx<mFW{YyWp$XAH>Y;2q=U9eY5clQ+ICD?Z=oC)vW(A!CU#dDuKg!f%il5gqt zmO;Kqp>UOh>tZ$b)f%VXXcY@w3JJyxQp$XCPrNBK+zz9t*3_Kg{3N|Po4e#-e0$%^ z(pCuz+=5(;vCXw!yn63$FZZ<FfX;exd03H#<Vcr%;13Bz+?K;tt=drQGl?y_;u*f@ zFPKwGb%glcb)XXt#gF9Sz4S=NzPWlv$;dHb<B}?objedk*7m2@(sFV(B-|dyuU16k zgg?vks!s5}{UEyD(z{-Nn}aOK;|BR>oPhL=^Y|@aU)|+A6?vh4hgnkC+ixPXRb?99 zzT9v};F_`1x8<+v7%Td*%OUI~kD|Tv302OISMSx96TMhdtxV-4Zz=s+%=Pi|^@Z(E zUU%*9==$I)<-g3Y=vBRQQ<eWS84ihDuh_MS;!c!(;d!mE_ZYhIGJFwRCgS=;*PEK@ zjMJVMM1eQYE$+y$ei%!#Qx|ID4YIw?bjwO3P)$TqN*9;)-X1JeFs<thZ}&M=P=w-9 z?G4P=ej$Nzq~l*^tko8^HMW$lxGc>z60j(%YMkHT$oo+-(s{c-C3aiGC=li>yB(cb z(Q$8!wap)+Am(GkXztg`ImVMYhh3GdnnMa2{sR<ey@u=(hAb0?RF}$rczueo7EFo1 z7u%+x?sb$~{MspfdF$xY8<u!DwA1ir9&_C|bLvz%(f@6yoj=}b_NIDvMusX5c4pQl z_Rv;)@1bos{Iy^P&zSgvXIUdtZPDqFvrUOdxc+Y$9?~_?D{q@A$g;l@_7}%L>%2<O zh2&%_^n38R-XrPiro8dBaNK8P@7Y9}5;uNWU%qarW@KdK8T%meaIaKoAf<u_Mp@s* zX0y1{f7ytRZQILquO&YIn_23CWO{6n`_(U@d(&<GX*Qk*TQ!FX8L@8_Qa7V{vTqF^ zZjOu1zi)l`Ze=pi|7V+3LXM*(N;ZnRTTZ_yDfe|#|2TGI!FOVdtKv2h6tha(E%8xS zO5kN@hc|hU>!C{yZZ@7sK0@&C!12Ot)`Nx=A>zX%A;X%Z!{<rSEAnT~?VINe%<NsR z@|KEr)vKK)7Mf${VGii4y*}TlA(F;XIO=`(42D1KOwvWenfi!Z3bWbeJ0IJM%iE%{ z1goeLMiU6JI;h=jBjT=y;{?7idYei>Mck1a{H1U<_&|i*or{#8s{N%d*5qukFH`tz zaBe{)?8o?``;$O}46gen8@#wtA`FDcpHIuXl|F@?$&*hK+FO9XWO!^IvyM_WMJYIw zvHF#~Y>Xa!Vn$}^ypBnkoAu@$Jy+ktMO=k(*i<9<Pf7|O#yN=cM<P2}iA7VUNgf&B zUE`o+`MBrX$*no_@U~O1W5HLC!=D6+3^HMjG_@Ht*iubH()hP)U+!VVd%R?FiMw|9 z{Pocvl4(n@yEnBl<6WL%l$}!&;ST5J>}HZ!8Qig=db#1Z8L&%eS!eXb=Izk-!l#|A zor-~>9ax>;!|9*dF!H;*XLx$w>tK(Uc>2*SW$me$m*Iq2a_zttU^a4bm}#AXLPFn{ zHS;)QPbYl4>t|`EAFr0sKzD8MDJH2DT|qxAWbVnkNKEtWK6kSTxhr2Qq9jSXD*Vng zrA9`#`V0S%N$P%muE4nXykkr(1D#nF#igj2TMBdr;@z|BbS{idFCKNDelQZFQh-S& zaabLNGAxQF{g$*hltaN7nz;x2G^j^~l}LL>{Z7<yIhRdG)0K{BSb~iqm1K+J0i*VK z<MQ_sVmQ+M;uaky-u_eTd-j#~G1a(%(G6*_Gl}wtLl4Y_i0{~L2+}Lws~+B??~z~( z#acBOe6$d~_vqCVMV{?RgS0)4VQT;Wb`vD!vZN0k)2O9Gob?1l_>De$3fwD?*$bPA zm8)=SY0fQ)U9<JQoK7|V-Sy>i{XBc90f#<}XkqU<EboeFRT+vkpjoft_M~t|ycJIY zsT-jeRn}L6hc!_mEi}3Vv9E_`*%Gk`^AbDm?{ky+XM73aDH2jn>1qzabj|NGE|!BW zykz5~_qlxTDc|?&T;nWe=Qc#|u<7LWL=WX)tiS*Ca-z&Z^pyWL2P*XC)cXnEgm^z1 z^{>*OLnw9;QCo8JQ5G@;??faw5N|lY(=^`+Z-=SRUw*UVF2lvIM96ie1Iy&++7*^S zR~h`->BS5C@H`urDs0(rS^P{L8V#lMSxGPBh*+_a-ww_Bli>_Ax-KCXpIGMMW_b2& zyLhQ!WcW<Byf+@GTl;p}+=M`;#){C>=($_~x0zLH4~8WbBPSQU`e|4rfmAQ<S_I{r zD{Vx@R~Jb#UH9jC%;D~in2BTFTxZ$3fBq!6$is}OEr%}xUs7axJ*ygVhx{(hb%n<Y zRytb}e6a%J+nYYEI){(#O-q<CdG{Y7m>o9BTuk91`;UDs&(p_8n+jQYgsivieuTHh zuVcmqFA*tU*0?CL#9k|7xaQ@xDU>AN8$q~!Q}R%5*h^J+?I^uk{jN%m*aK|ap9nI_ zxcGGgHYRLA6%hnC%v~W5IYxv?7=O>a`14+*^=pdn*2Ft94q01NQkg2A9~O2?hdQ!S zyB8F1zF`(BOBXrEFofv7SffJhwy7e9A(OGxkl(^^vj=IDzL#8g?`_tExQpHgoz=0P zR?BX<1)Kkkq!rg!^h5a%M6(YJdiebNxZATRly%%jK6>CtkF$@Ovj0#tTYeuQkCHEB zxGCjf!6CjTkZ1Gu)hR5Y!<0+6A2y|m3Zvi}-hw>{VQvKnX$Lh-dsSR}os2IrjU<G% z{I7P%2z?e6^OLN-7}|P?E+EzOCa0D5^2cdCr2QEb<XH&5{k+OJ!R~<j$kXu4K~WVy zOq%tpCH<0DT8$D_-lZ0oL*Yfg5`?fx!vsgotJ{%<2gb@=-(Ay`PnX$ooF<eQyCe}} z7kqv0i%~0TT8ebb+*D$-ib%AIhJ8a{w~^g<YvCiCo!pB&k}08=kB9U9*|{AKNj=P# zLRW1Z#A~_PPVeD$nd>|Ic6hPB=9SkRUN)0yqww##WTTQbk`L<_Rpp(u={I_aQS*Ft zS!LtV(h{%w<nyZBr8Q;>W$Z>0D?%@~#0QjyE_yu-57U=o8%sABf(EjB83Mkotx*$L z1&LeQUa{ABSoOx6Z*KT1?_->%%E=Y>Z(q-kTxG;_d1glwyvMqhjXlp1`_4AeS<*FW z=HvG1!ZJ%{d8Y2Oc4Jv)!rSnWLJk5PeEW>Yh}-H4f`n}k7nd(~6IrCzIaH^=F(uOt zSa7@C$S^&1ZF)g&q=K}T1qZee$rh7d|3lUtZWG05fJswbCATho6eKBccPZ`W+%zMx zN^G*i#|Q~Q9Gl`Y|892}<@)D~Yb>^3h@HdzMo4K`I4p_|eVp9OU{%RlD`BybEbi}_ zR6lz&+wTn%B~b0Y)ipLyjb-OI6@4m4T+GMZ!%+Nlt!aRDqxeSro6;USyzEyDq9Nq$ z{!iqkqj*%e$8<kfjLCGmuzW6Lz9G`qfu#|iO>6vAhWK5ciprPk<}`PPhcGY}Iq>F{ zf_uxG4z45f^C!N4wrJg8Rew*VAEwsO8KU;_2H8(>aNXnJ)!=DbJ}wKnd+N414dz8P z2bW9rhR2YhOPq`CPVVVJcu!UlY>fAgzN=rYo=NR;+IYN)ymArw=}>Bn@8Fg0HEubz zaj^t`Q3QrOe2kfzVe3r*Jfz>LMVuvnz}{kWWuh;@yIMy2NY4GP?EIG2T|KF1mI`m1 z(|B&MygUfq#}d@Y{fupQ;o^()DA&(6T?8H_i!<kY0~EfRAl%9AYyw2wY^${{T%0Re z9dJ;0S1a?1Y{%y!{HkO)xT3SovNvg$GX)DD+FBmX>bN<dZ}(k&XtqhswC0KztCR?p z&X1L$NRlKi`nHX9TXv~#EsZ*P4co9P75~vrCZpj9?X6kQqtth+>z^p5mJ7#sN7AOH zFOsBjnmuegct(PG&+J|r>Sw_cqg{Ocg8uq7Zocm$v$bg-dmE_&ySgr}>)aF5$g!T| z%#?aJo_>^MImP<gF&j_g1w~b|<}>#Uv!l)01mcdYN}3Jl8z`|n9+Vo3U;bg!>B5RJ zg>kIKtrFT0;d_~syBj*h*i_8n2?$56i>6_OL^5ozJI?HLe<{F=y7@FY$(0DlZxmaw zNx5P`;GDDG-9i%;=XJBEkFHH$skLU|9=hkvSN)(yjx5KqAe@SEIow{kei+Yp7IRIm zMSZ?Wf%$BNmnNnQ<?Q7Dq3oT4L<zR7-L}ouw$0VHZQHhO+qP}nwr$()f9-wl!+rTr z-1|^b&owJEBgV*Y%=xF6UycrJW&za@znaqa{O)h>?N_1e(j@I(KO8&-e1U+&^oMC@ z?F}5wQ_jaZc|pca{IM_3P2R#KWgg}<C(L2!b_}|{aqrW>PB{slL;f$ij4H`WG2Tm} zgc)bgp_qHgE~>>)>F@DABX8X6TNl)rGcASEk;H=SUt$jRFB#OEc*<XA{KJ>?`Sr@N zpb!+SXv*!l!khStCx_a&LDvcD)XX^ib<Pa~OKq?f<BHPY#J9wU0P1n`4G%%iIFn+? z`FY~^H${!#=dhI9Yy;T_dh{ilcn;Q+7aW>-kA98aPNsRz6kps`?LAQwO>aaYPLhLj zazf9P<1G^8@>_cax6)g)QSKVJbBPUW-g%YPE<52|titnJf>Xc&!9)0gPJ^Rh%gJ>> z-q$poO}F-m^^7<Nrt<PqJJcX6f9ID(kEapT_d#mX{f@6q!db=~Zi;N+q>?L2<Z1zI zGl(o@A{F<Km{U!*g-r$EIo@3~9=UOTjl6^FopK#-zL<|#nZK?r1o0&^XULejG6Q{{ zN$E<bIqRCmPSYKNwd;<?)=efS*U#dbH!m-9Fumc8Jnc7~G@oHEY#*bvCdFB9z454@ ziOiltq~D;(ZVyp^s>gkFV_Dg0rNC&c%XJoQ<Dx%1dTjR2au!>bOr28@ay%fqk~0SC zPg0(H4F6_wzh+xFY24Cm-f&c(MyF(%ghlibn-?DtYbNcNAHM+_ghsMW3Mh&UT@aZZ zT$0b61Y3Y}dElMq*qLQ8{Q(=Gk=gtKL~DYt!zZO6p^D@;h^cva|BN%VQZL{>vp}|r zqH{DXOr*vS-|6W~Ng#0yNqf8^$f>=%!WDfFFq0|4P*cUm6+~m_?ITo;VYYn$z1}#z zojGvBfI9tPufXjdY9eksB}#k^nf0CABEow2Xt7Uf3$ac%>))>n-Gp_heh1dNT$-+t zsj?{#bl*1__VLd=?qe=b<lN$aghCE?Bx#v-rA7DZ7y^zRwZ{ZKu77b%^kQiu33|L3 z6&)*Efv2yC+%`QAMB@koz8<yrk+o>)S_h!x?T3mpt7>i)PNxJ-q~@SvXp!fmp4SCl z0s&b!V&EQ;QbRI|;t4SV9T#nTfM6+07eBMl>Wd4B>tK_47z4Qol4UN*kb`6PlO)fP zQGF!CBDOVbsAGsDv?>p7{1|mpBb62VG;^*{ECTu~pXKri3P|9=&Tz)!uo!5&&nTLU zZT=kI1(4>67gQ+Q#_(c4gKj61rGXi+;D!o;<m#PZF!l4Se|V)7i1&DpQ`A86xA5j? z1~d=YMbo(a3Vjq^z}ZPtXSAKQ0qJ&d;#n1wbqz+wGxdEz;A*H+`M4U%0r!L%fJmxD z7R0U^zeZ+Sj{AxK=d&_M$hUeC6L!C88-1uAKD7`;+)4w&xjPNJ8rqP}=kB$m`119B zkY0yQ4&pNp?!$-_NogCMJdnZKZsb}dJI(pZ9&^}J>m4daz^57BQghBf^lWzIUi;dM zu8yUg)R|_h<)ZNSZuOTSbme#Rjt3421!?O`TuKpi;8aYljcYj+Oa7Z{-uK%!ni`;T zY1bx5@$?k9t7j#&o3kA^CZ=ZJ{<ku}JGe0D5P^(T;}Qg50MhQ~C{_>g6vJ!-nT^99 z$?*qP^$bMFdbL5PKFbdqzDw(~>=CSv1#I#f?B$e?4XC_?qbt=+hW`})CpmFYJcL%s zls%s;?GS@_7u={8*@`^NivaZOa6YMU?n|6b?l1fTH5%=wc19?G5oB$9ObqS)6?uMH z6dK4IfC3YKEzgeLDezcw&c&V0FKfBNueHz>sdLtks~NO_YenMPl6k0NT=e1|{GTt1 zu~Tyd@riE~6oKQGq!zRcG9)1m)B_b#LPh=qLi{DBx=iIqacmo+M8Z%qXI&|C#|67w zd;>i{L#DU4C;<4dEsVWrwT=j{5|T+ru+Fckp>YK>hzC~_B)HfBxV{jo@KnFfnE+7# zRH?%;{qpsaLyMe#3y$lVS5Inb%?lZn0VY4%C8dENQ$4I1F}e);nZInXZ5lGx{WxsE z&?420GGzv*b<?vBNZm_)8`=In)iyp`n1l5lr?%g0AHBbazQ&K8<UV~oLebFVHCC%^ zaAViw`y^_+jb2pZ$Z1NY+u#1hu6<RP0MTdc^iNxN{$C1@s4>rDocqW&&8rWy`n%bQ zaPo@OUU!f!ukP6IK`L>icP$#+Jum$#ckh>sXd^%LJ%utU7lb1$yx`5gIilD?A*1;D zg0o_sW)S-`1K}Z|YQY4AfJsX?2rZj&PrV+Kk@Y`lxWLS~1`g!&zX7Ix*dgIu>~f(p zov#5#3-pm{FA%eK5(1k+xBP7(<^}amqX<i@am0XusQ5Q{&l=Ei_e4*h^0szxc>Mj| z%;9;_JV2K`BjI#&z()4@y%(LyN<4=^ee=QLEq_q^1c-ufEx)u}yy*gn)y$Mo(e*lM z4F*#8>ZBu3o_7}l3)}&tHi=yB1mn&lzx<i8xImpR+j!&L%mfbh4Kk@)b%#L#6CKMB z$y^|KXqv!5#c5Zb?EpCPHm`eXfB57Y7SFr>y^U5~GI1tdtd=bec=i+IB^#8z83FQq z4q0~TwzCoZSB>Nuup1&(czl1{-8j;6cco%uN7TrU9a!6N@uuf&^H>gnmu7Qy-~Ukd zEUkAVAQx4`{c*v6CraJ()y@cxL8#LiaDJCeL^1*0{X~KRw%4Q-cZ7V9hP7AK1(Ilk z8M~j6N-_cSZkJr^OyzHxC_Z3M%_F3H<ekpYx&whZGt3D}3MVrGqV0ExX$s8`M?C9B z<ex2;;U1bJ>Z+9xy9az)hF}J0-GpLw0huaF;Sv4aV}}519?I$e6TODz7ut6oFSCGJ z8fTfe0>K44>P?Z%c0jBnF9*Hjx1WlFUltxe^S+aW&q2h+E!PA~Kf;W3WPoLQcKrh< z)v&EId9hA&%wa9|7Cn{gA9?`VFTX%44%xP}sd$)K)Fz{)bs$F4=om3Sp?tH=0v7G# z{-v*!W3B}nmxMK(kGI=9&=hR%!Z#elr>qiwyF}~-0jDlihNCXOZ4fwuFzm2}H`d3h z&tIZ?_&z>Az5Xu6hS9=Uj|YvY5Unu9pq-Dq1QEyFOiW{z0x1D__8zrp5WvKS6EizU zNpEc65>VKvU1{oERZ{TlzsoW}gm432WS*%vC2Ei48Bhltwzt-dPUS@Mri2KMvk=zn z5GAaD>Bk{1KN3zBhT`68$?rWVgPBM~G~_t)Z+HzqHs;iDspbRh;u`YS0K+-n-O%VC zEqh;loGp+e7FH~M2tOz5)$4UQ|2mgWqPe}W)UxkUJ(ZCY|2YjaFA7{wWYp%kH$9e` zuRkxGiys1*!ggOHbtmxtc)WWFzld}v_aV1qr-<d=OX7wLV55QedroV3P~*V=e#s?e zHE@yY=+PXBM><gpBpS3_m0_RAVxQ`$nz#U&D_g!^_`NvSfjTp?5Vq|kylf3XD|5BZ zU^8_bYUjQSN3=`jwi$AXD1+mM%BF*IW3bMY;J-P_0kwo?Sm159qk47uet*1>$Xe~) zR1+7l4QIB!DaO^25z&JRK+|O~rCSt&c#gLj1Jva10Y{tLCBkByXvhO$WpsJWl9>A1 zR4ttdYlHd%T~iMTqYqGCSA}-Ni&@F&J0Y)~=Aw`hx*>Pr3BShBg(|UX=0L7CgW7^? zRzRNK$L|)Go$<OgSDP`fV?f6o3;wpNRFxftU4gVD9{%txb%AP{D^wt>EkK&lcg~+T zVF}glw}5bfRmWeqXit({?X_sFl_1X%?<%zo4)q(M2KAc=Zt<@Joa!SP<-j2&p#dm6 zv>6~tVtEE+B*G3T&_}C2UQ!LIBl~?RKJ<t~fws?xe-CA)fI~3xn+|m@_aM-`g`zRC z`~sMF|6KBpSL};)g+NMyCl+27VnDTQpRI3;q@uKJH}i9FY|9iL6V7zTtR}QN!+s38 zXmVFd_XXUMTjgAGNx!qH9$$+g1ETg;UV_u$wZSoSfnGB#d*wc-am_93{$gaXSm8G{ z9Fv`rGJ7q2lOInAY88YqAvPAt*-Wff$O;AmO7Ra!TA=f%B>&wQJ;8NcLdU-m?ZJbf zY%Ow0_iX#4`@}KA*q7?pAc0gYE0bfI&VTIKvIiT}$CI)v1LVLjm7cEehkL+ArY9(N z=V4R0*z^XUHWFj}SA#g&Av8CjAWdqXsI$&kM{a?h*afHZm|3}Y9q8{_Vjpz=(1U3m zX{<1gJX~o<(+uL(4@I3`g&|AiD#<D7Q#88XfI~!u=&4U$3=)kSx#Aq6;tQAjyfptn z2`M53F3BacWRTP{i=DAWi!m39O8&ADaM1oX1uZ~if=Vjs$YGn}R{9b7W%OQ+Z2gTU z#S!sJNYNMZ>_w0llz+PUpIziIejVU|{S<}ybBY6Zo=UXU9LX#J1Xl@6RR~ro{zEiV zyb8gHJR~`eh?V8?pwVw79uoN%=r9GQF{UKHhfXaqBI){j5ppT6J`?dgT4$%w44Adu zb9JL}dM<K!2<iOIW}40Vq|IwTk}R}j<+`|Elv<GOOS^tZ%afB)0UWFO0oi@>nbu^3 zeD3!aAXMfjc>b5>r<L5^i$a=>1}yoqgbk266o6wXaQLj-Y&-RqvaTaFY|7wnX5dH8 zMPHBxUV7k=sECiB$3>#@sb48kU5tP(Dv*~DFVDl<P{w?25Ev6G+Myt;48Tm<$j!Jp zTKvxq@|}LFIi<!wv%WKY;?oD5L~nddaZeZ&Mr#O6Lb};Dh0B+KUPMbspgpw0PH1$@ za5FY+(jy@@h1SpbSq#Vmdo9wqy~Q2Vv~Ms>0_6e;hG48_DP*o`6*>F-j1TGP_AsI> z@1C@ubWnfRbZxW)>U!Yb=Io;jGX2vtj+@5qv++?K8<XVDVOPSt4(sT~=3&n+CFUY= zIMu+ji2Cq~`y7&wRnd)Tzul%kpQCO|sCrcRzf;(p&^620E$qQa&(BP_GIygs&uEpu zmD?i`{qu#ikJa0bvBFuTdi;;yQ=niwK1iZYuWJOhp#O@~x{r=(S-^o_fzd(wX#Y`{ z-S#7VFDleB#fQ!9bAA6e<(#VDuzClLq%#74rvGb_Oz*n1&q!+@EoV^Yf}>lca~st1 zjY6z#X8s1|V)McV_61f!;-k+Qvzk?XJtxrY17kb$b@7#N$;Fd<(tcyYLAwZm{=5-x zFmJ1%lzo>u1Z*$Ci#CbnS7Sn=@RJGP_=i(hJs`C2mF@s=mJYv3H{e--4p)!Lx4+S4 zRrD}%Vg%JU6?Pl_x;p4lmkdmJkrc0I{j8Z9Xp=U?Wb^8@H3;~R^}}>_&P5aFUMLQz zB^R?RZfHWt9v`y16P%}w^;ysIHH*eHuF;{@>Ul!w%JC9#UX6B!4O<V_2Uo66bYySd z(KdKn$SNSy40@IcX4!T=?@+A{IQWv;3r94oI`8O+5rqFx%W_L+1qrrsHe6|JAa@UK ziVF6fupgPsx-&o&KWELRb(>A&J_l`<&}F>lvV--SXj4JVva^=)^V~U&Q1#xW%uQG| z0BSQGNI-kp-<bM;2d_&fc%kL|m}Yhja|bPdaLh%EJm!zNU97Cs2-(_Q6RvpX@+A<P zxpP&>veSP1b;8Ag8cH~4REw`O(=bz<avq?Ia8k7aSC)06P7`L)E++SA0B)8}<@`Am z1#+vkCNurA{hV_<ij7vnfKCnl{-#+1UuO}(1}(0DT^8JZ5tWgV)&_V?p&7qk@fb9? z)QvkfM<4^cbSmlY;IK_o7>-Nh>KV)ZLjE-?q&5eQ1|pZ~dh8*r#O4t3$>n3Hbmz92 zYn)K4H1+Ksj+Ftsq=2Hjc2W>gPdp$cT`y>A!D$C`BV3*~5^%y4+GW3sCW5a6BMsUw z+{M}D)-IIjb`26gQ&TpY^Fm?wX@QI_!e2AHZIRB)J{a@)^y{EybMibqh@v=-?$L*O z4sp5Ege#;=ZF=k}j|;q<O`B5jg&p}U2cMq@Ja>B>&?-J3@Irg2Z>zzOd>Xy@7nkuB zmis;6=bc8Hi|+LXKXYNQ&?Nc}N647+?Q>=i9O4D@MTnqn*@Bxn9!rW+60Hqd>*1~@ zygEc68koG=M2PsRMHj5;7*NJ@1VN5$+=fYOmPnj4hy}4nG=oLOY0)c?%U!~6LCO;S z_-&LOR?TQ!e$iEPXoR79>d=&7`}jg8z>#=NsNge13?~P*ZQM(~CjoOs1Xqo1w0?tg zY)VKNeZ*s@c{5xr1rIkYMp=cq!E4-WUo8E<=A$7bSuptVPUC8>O0|mvjfS#+3L*l- z+zNUVwx%4B-;e0_ApsNQ2Eu4cx9u{4Jn)GY3WA+5rNJ3E>ce5Q78PnX1KPE0?&+_@ zaUm~-JbLvti+DX->SR1kA&O|W2tXxH8bVu=xP%yG+Q>)iK~%Hh%;h`jquQuH)fM#- zVgv)v;-*K+Uc?cgFybcJkgZ%o^Km3a;Qif`y^sL{g-do^lvl3<hO)pWw}{h*&ep2I z_=V>rl?#OF<BP~UnFnoJ1DTl3<MMR*kfay`_e&^gsrY~4*aK4tw~27ROQgY}ZYUYm zBm!KtQeAPz6|qD+(I*-h7KH-yzD?UJX!yqIJW&14zm4d3_@u7dVHI$i5(4_SU9(6W ztq?H@3YYBl?|Pxxt54xa02cL9O0@z6ALuk<mi0218}6<6jv1hU#7-KO@m0X#n-M$^ zW`=S{e-y-E5Q8k9<%XzC<>eN*8!n7$h^xF_-1a51SChimZ56PUmA&gl7MW#(0Ss=o zA$bKR<(XIz4Ns#Y#<_uTr6&XhV+X(H;NNdr^GI{K<ooTK*pDPE^43QbF<F*1T)1<? zh8Zi`$3!z~a4RNs@?WAb(U$j7Hue(L`0PZ_B_KMrA2}e1U5AA#2N<1EV|CuLd}sGT zp^hLNs(@BX9jfM8_J9OCp#-%a;wRzC;U&6|w1l9>7L}#@MPPL0S;%%P8tBt%hXRO8 zwNR_elAX`IoA->>$<K)9d+O*#2`+Wwe${9mtd;W`$j4QFlqoBO#HL89S1njMSUrN3 z&G_uz2MR3Sb<nZVQ%gm^NSe)GQQT8&^NUbdUEbwTPoBp8+%a@UsUk+egt=!;S=idS zw1<?T`OD9O<iPp<UVq-_61w-a4?WX`3jgzHC~$b}Fr<)^j~%7&p+&S#qK4fwYF7o; zf^q03Yr=i>h-2e<&Y?5CbWpDBziQr+Q>$xREo$L(Gf3VY#+RwkM;6^Rjy}v<|22mk zR@RrW7s{<^^;F(`A602OO-D&Y{U@8bLT`j-P&qT9yU;jq%7Fn9>d?h_>>5Ky<toKs z>Tb#-uLw?{dW4lXN)PJTRGUAu5uQ_gHFVxjQi>vy0ukO!<|a%pf}AiBDXFAUol#th zQS?>5JiD7g%ViGNc5BoB2*J`?iz;Yk`H0Zb&i5UjGGAPDJ+q@A1UQobTB-%e>VW3* z!E)WkxT<THvjB;22l#H&et>vSH%nQnFVan@IU19Y=+JS`8pFgHLmcvsE2EYk!=o*4 z9l4H2<ClutG3FE)szH<&sJr%NUG*9}U&UBxo=vAH`g8%4&3?{9Ebk;&@-1YT9HWj% z^sG7(ym=on8m<_qICc)3IBw=a#tQ~*8aB9Q7r4MESIPQC(Kd$T7X~A}G3b;9wgU6q z;z{u08ypHypdRN<03EftGIV<)iTX}11<URHyLr1LALTs$d#LXVKv8>{cI&=>f!aF_ z$iy+ci5K;QCH(-Bc@zrO@kEhD!A7Ueb8bsT!Oy_cc~CG#3X=k8F0W87iK9GKg>*Zr z+V_YQ40mq$oU&G8Gg49JZQ?aovD%qohF9_L8ROQfL1Vb6N?13bi3SeF9O3Ben@FQ+ zsqnFG>N0-Sd5+vTe&Tzatx4i_J^;lEfxiv5KK4~~)#^Uhts87KWB!D{5JDzLKv_E7 zOX)v#&!G*n!X}V?5f6S^(J}jj_Q^&;a|zA@TO>#)Jk%q$^Mt?}>$00;0f~TT4|xE8 zLHZ!4nPuN!`h4?wpxa6q-w+F?-YUn-<)ZwJwbFI4rRaEICcVjoyo;0to~-<^A1*MQ zk4b5$Ql=&pc6z89xIQhrR%h6#j&Wmz4T0)55;*?8_vpoi{Y1b$eIKnecnl#1PK58B z!1T0KfzTa?Y=NY<pJs<FxSUwg)2#|*<wW7a%V>F#83n36);n<jwa(kBW>fm5jI34^ z#f~oSs&F(}49C6_6L}ihD~{*K^Eu@@1>(|NMM}iE3FeZ$v?Y|45lu_`77(m8Y)Q@I zm>_ARkr=Y!Og|}qj4zki>5^%iSSBM$4xsT5d+hzWSvs`6)k*2?rqaOL*VzSa7X^FR z)=Hnm5Ayml*4+2myIC&!nAc3h4Rz!!yRX;^Mes2qU9Yid<p>Z>DlgoYWhr2oJ?XeP zPLg$6)y?2h(AQEjsI^_7yXm9krS%yS)=N-wI>_o{mS|jb!EDOJ3i6vH+L|4Q;OB8r z7i&veM8bCNMw2PbX%t4GXWEBq75+15vBnJva$9Vy_eZflO4PoyeR#(&ntyZlL3++7 z3Y~^7Quj?Pm|+#9|A=k3uB1Y&bdh+p6g-jAk>8CVuJK+ak?0f0Y44Nj{*MKsn)gqg zC}y6izS>ddR*c`V)#$~Yd=_rZyF?qCcXsKX;08{pUzbyr30)<T^R&6W>a)k$l$Jq- z{tz!1F>%WVjm$r#nv&PuD}KHtm9tf@gRGCp-lY|UpuE8CIZ$_1m71BBU4g_QK%6QI zqL$?$bei6QHnkyK^jDzTIr3weOEUQoQHw~1Tcd?g5<<i%?#wA#nXyE@M*i7_Gnla7 zlFRr+l*7?Pom-_Ftt49(X5SS?21X1qtaK(!Es)zv;ZR`5o$i$gAIHFZvR<h&Qs>m% zjB$MVw+fkpXWp#YV|c$ch9)iJ8zfFI)g0AKwM+*~d#Un(ns;msb={l|+|aS3WWFUM z%Ur}tvBVMN-cd}IEye<}zM;Myosw4OF>baN2wm%<V|!>?_t+7HXwd>ynKFZh3zdGQ zOgrX%l&DyUvf6Nvgdd-i&ZsH(fqWqxrBxEwN>yEYN+2eHxNoop9TU(h6umXLxwb+p zpzbV7=r94AW@%{A+KtZQQxeBr!)YoDEu7=zu0pPJ&O4IHT-un^j6XIS9P#@R+aIDD zI~@ZgLm5V6&9({j&7gj{zo<_-4Wd=HYnkJY!Cp~~L8!;rNY#&{W|LZmhPm^T<3KRO zr1yvSKZqAn-XwoM2>^g;(*K5d!Tvvpmy^4d@qgK_=6}vfCOyJ;zz=ZYtI$Y&b^;(H zBy2Vhvv(NhqAEsEbvSKQoMNZ$E@}hdCNY8e1^8t{WuvHRG3x%ah}~`YMFa%~!LL^k z7bR0NQ+w3hth>hxhI=vrBbk;sW<E{zobwk&)l|x2C~ENtFo?RanX#06uzhlM8UAuR zWjTmseiJ@eh(~y(f?|TIOZ=W27v^*wz7%1oUGqH|F=sy_^<srWF#Bv$Uj@0Eh-^F% zwqC+@{1Al{JP!ZUVFwrRTEd?ey{vTWX{h0Rtw^P`iOD6642bAVidmLj2Lwx2{v2Q_ zCAf_vb~GT{{ZPBw->11>&KWG*uE;;3!3oxTl1HQmbsvY^h;ZbrqILL+TIRETHvr?c zz)1Yrv(Y9&t7SIVn}|pQ>jI6)QLVN8bfK%0!a)m8O20e&?tY%g2QJJw-6HKaR%m}t zrO{~PGQ$MfC_*gK2l_UjPdAaq#M*G+N?tsNGaHX5yq1kPy~K;zCTU`g7q*vE@zcPN zp&Q+1{5aF3n>zfxX_IF5UtHlkG6z0oupKnkm@OnRH3}y-%wV*{_%=GPIRG-`XYj22 z-;|lhc<^nZX5ZH?0g^`?)?E2tv|GCw=<$umTmR;zqAc~dl`FN!lgz=M2Dl!8-9~m& zfFXqmHvIKyA?M%!(idEgtV}j}u&+@txnMxi7-aN($nqNdtQKc4&D^khZJ+>~f9`bp zHtg`Uv2p4}gRWTYxVs|8LeT#zz{hz9*VF!jx8KcM0l`VV?uYpx`NG%vq3Yrxj8$~{ z0{NDN>5XRaF~K8y?^c${5YYfQ>mCDkVZ!pDysv?AikRo8urSet_@ov(+quamvaL7S zqCd9+R|cbhl(xTPJ7V46MsIL;H2K+Ye5oPKow`{#swo|74cQ^wRdUcm_KGo%i@H*s z)k_hz-QFW#oP?hvl)kkIEE0`ep%;SwgbyFOcD4HYFR@-_=c2d<1ONa7{l9Gq`2X1w zuC@-A2DY}A{{bGe6L)Pk=ut*;^==W~aiugQHpArPD_q2^GFF@uT}-VK-PU;0uZzw% zZ}spCge~T63^+%3>2}`}@1-rB7Q{|b*RqPf0mX}s@snsA9-a<+T!oSF(om%WliA(l zbGD>-qI3JJQvohPo>xwbVDcx(pSzMm-g%T342(c$>ik4i<#gG{gXoH3L>3$S^@Ly* zQ>-A<IfDT!lMf!?+^maWHNz4!K%H(SmEXCin?%_P+koLK=T7xph@JY%t=gkPYIHCd zzfeR&+MTtZ5!CY|ix()ngmiIA#>KC#S`42!u&|^<c4sjQUsE@o+%j$p4RjVSRc|(* z4t~YX{YuWUmf~<$51cfkT!|eqOD@N3gh;pByd9kwW4wAb|1M(K(PJGiD@Z1&ncTf$ zSkOt%0f`dsWI|)xCbUWIk4z88&Amc8QYjS*O+OJFTmjhMBO0gxl{k{~^2WuoI&{d2 zj3qZHcnRl^zGj)@CMH{}y&<d?xloe90|ckU%ClB_12~QXeYSrGD^@83ru!mqFZcS@ z{BVr`(AU`tBlg$BgVWO4!#AV$ms<pSC<PnmyQJ+rNt)3~vs0PQzMi*dtF$g(-|E8q zF7(Pq&c8bR8)@x6h1hMI%YfWrdvf$&_#DXgYiREuc<ux8zby{T|5+Tm4#rlFbpP)q z^4}CgcjAAM{@{N|KlN>RA_|m%bO*oKwg9_dJsGdWRrnP}+@@ifm?DZWbwN$lYnA(L zqUeqluk_?j|2wAjNFw<OOqGE4CgWl4XX&Nq43~JNaU>e!PT=Y0m&GGYbrJnxWyd3R zkXr72M42bTKqXYv6QYaWptS#t7}P>?L1INbNf>j?>OeG;n@`HP3&){NDe}yVH~8=P zNI@AYR{_VLI%atS@+kp`q#BTkw^*(Wag>3!G!wB@qr0WneOU0Y!kkK78Vy}>@Q+|E zwYxL}tJ(f7YRE_bntN1$Y(B6{EEep>$o6h}DGu<)9{MQ@xRV?BC=NG)$4Di@6dEC@ zX`yQ@14F-}6k^%yKxC-l_0!qje=I*Z#4dTF75cyO3jMF;{kJgz{-?J8t!HtZw8eiT z5_6nk>!XK8@)F@$(iBGM5uSeK%q_t*(Q!$v?cT!qO(E1!+B37SZcLwp!*mzfiY`dZ zR+(rh;6y9I@`IMYUG4zM(4dl$AS-sqFrha#zXQ<88RLnO>dIncm?a7aL<X$Lo)ghR zaL$Zk4@ws4B2h*rm>90sp0VeDfjdwYajHX{nl$CUSss4Z&f4RO1<Z)=NlP+hhL;tU zWX+EX`XktvU4AyqC^7C@=5B5U7-44a64M$_<!s!m|N2<bLi>ni><XROqHB3~eS-X_ z9BPDrj931BZ2SM0sDrh!k-0vdxwXEjF(a*=jp_ADC@%^eFxcJVKK8oiKzIXFWG)kj zXq%%GEdkv6VZ(UU6}e|{6kC>{QBr!;m7S3-0imgJ=j-ba*6r!H56|r57xXL@(UP}_ z2ag8;KwnuYwCHcA<S#7F-7eT+kTW0vejh~m{LcorRKA=B2N;=YDu;)7sBf&;p<|{d zCwCxki>nK`xT0fZ<?hF*%BnA3ZWe7#74XmP?@#E{CfUQkBi7pE&NIz)vGVp{!E&gk z$FEBiS&;nlv;rzrpVj#mxAFvfdr-QJg}viY5cUk8kgmI(7UH)kCBN(#k37u8pvJ() zlF6n_%jW@Yj~RltAoiz#<%7#&)h<X;U_iX0JE~be54_IDeC-A4FgYa^U0c`yW#wZ) z3iOJd9dL2obhY)vtSH0y%dCTaZ_sbe8Z|+4UBv%ZFw*UeF_<tSh@DgvU2x|_-Vp}V zzn`^xUgC9aasiaqGp$wLr6iQIeA0A*yG7@*T?99ane)o5l*1eg+PTG6pswh?2$=&n zFA4BxstC(y^{nk*76~Kym)w4z+^}^r0*wJM<CAYyk)>Y4gtnhJP1#<jgE00l)c`U{ zmc*n{$UJPa{$mg$bT#>aQxc~?8HklGqdHfU8T2o|2k&sAm|3*FfY=Q5bXH7|o@Js1 z!<x8K&5E<NP}blz`Z1+;xFqQAiP{l2sSUS9scu@;l<}ty%Kc6Y1w?iAlos;Gm#}+? zY>;<Tg_x;*JbjaK=&~Twnr=)Vv3vWx)tX6TXW&L^meok)B*^?7FL9!JF$cB36eAiD z^Dd_dRe1qcPPq^VXKqtREBD;_4@ewbdx83wodjuj-!)xhL(dktP0O03y&uu9sr{PX z&4xl&<|D~d@O_Nr$U^&jNd?Kf6;2QMm5$B`0Zb(B9$Nl31^L`m7h1LVNc6K(<^CwT zYh!H$e1v+bESIZix|tusBH2!*Aw&L`1q(VueKMp4*{!t|>Ve}>(V1su^Q@G=%EtMm zHUHH0ttd)hGiqDuw%0R<A-2u*MZ|e?=h3S#^0Rz$^{N#WETNj8H|#ORphSeD=O04? z9$iYaFoZ_^U79nHoWTgcp2<smdmof}FJ??1$kz1g;4t=diA9{XST@Pmmx)jK!eD^o z^XUXqY!wR>*NfD8APh2?q_^TfkAN*KxFxgT3{(qWRiBjCo`0#Em>yCxh)Dk4R<3d2 zZ7qdkW|Caqi(rU|Su;$a?zB4!F+@-KT-GrTT^U%&`FuGSN|AdRV2OFM+eP54t2erz z5Wq522S<sE^p{$oVhFMrEI-8{3O@rH)cu6M4=8#!wYpzcsF@uaL3`rlJVy#I6hooU zpK@FZ(!L)Q7*;-zsAL~T;p<$nW=)^%GZ$(TiK*)*^Hm|Pl{1ie#>3mi1F@AMt|?N* zKD`skPz-G*A}akAs)0JnrPAY?qZY9|G#0s|@qY&55P_|*meq6sCh~R+nP%|K{e!Aj z3QRm*#t^jt9J+hkHkE8VmVGjenpO4|#YQ?)Z=+!&{w02^bwv6uO8p+s63=zfUdkpk zZ8xTmn)SW1bYcLjOFl?TPob_uzJ2nhj@wbL`toJ=P1d>P-UxV%J;*wK*L`?=oBo~Y zgrf>nW5>u#_dZ=PzQWOdGrf1maiGuZvYT!OVOU5oLOry}PxnNMe)yr*5b6_zaXR*= zwikWn<?|(-v}<ErS#E*MptT|%GKMu=e1=(q=a=cMQY71pHi&YRic-Gs{((?2TK1{9 zsUhjdoZ*z}Vw$!Jeza|Ijp5E_F!GJ^?M%noggDx?;5z-=AC%C`{uwZUDBp{P6=;BH zrM@Ty3d0Sdbc3#jnHM6b*p=(=xE+@I^-{EtcC-27+REP~seyUQ#c|z58k!e<!shLd zU;?y90=1tBozl%Q{bW23B26?j7d?5!E@<@nUK6;vn0amfI1t9LrGpeWxs^k7lAmjC z-pUkPRBh<TA8Ioeh1-0pkj=3D_eFqp710Pk`%gx5L`%LDBJ^*CJX%If-T042kTvrk zS#B^ZQaU6gbeV0-wd46dIK&$6V5zvHKRF0>cBZl^&Z%K=`|yNiC?DN^rUdE*da#~w z)5L9$*%KTSRQDRQ=UaEHw~><FEG`kBHrOl0{+!ul5b&yNyAm+sd5;N6qT{}qtD0Rq z;x|_Y{sN1GC89xXjL4?_7qEy8OPE647L-1%8qT}&wo=;5UuI4~i5C24?#Le0=xfBY zjm9C8)JT?GDL!>cWBZ-Xo<;M|)iCKZ5lS@_49eSoTIC8xzZa#})WFFup(4}O$$5vY zl&4MiTxv63mxDJ3LA1InZJjpQn5j-Q75VD)PF>o&SQsgQoJ=!@yi3`5&(zm<(<B9- zn>l9nJa^vWXR5`nr6#}4t^E2(?=4nu`wTXPHpGQoR2HyqCq=F@4-43kzMM<(*_sGf z8-hVl4+~hm6C9^0(tUYgT2jw^(Mj{t>ow44o0q9GBgftRD<&Zs?h{2WLWp?tU>SU1 zAR}CG=u;stX+wIPB<1ks?;69;HK2uW{iDPmo$WA3k3(pm;*-Dlfw<}S5;?;%yM+x` zU8;v2nAcPlFMUqOhlk^o1$6>dEBUVg`{|z&CByKZ&r|j#wA~z9j5jmB`?(`>W$1sm zFW5zH{~94LSgY;)C7zzbyFY0PENMH!B$@XYRe5Hp5G27*J3Pex`{Nx8HSdUFbCKWg z3ELxrFA18bM<;Tc#81Xn_xXCPwvW~k)J`{t^)gPzmwI6G9y95tVA)n1(8kqBzvg#0 zkN;tr!Ac-GNbs*^A|pHAa-Nh_CSDmG$mDBgFThNae2>UTC)GsKw9;9?{o>taJl4#P zM8Z=9cQ{Bg7BfGr$n*NuB<H<h<fpD?nKU8tkU%roIk&B-p%4E?;Ff(^8a^t<^P&Gi zqZPh+*X8|yWU=cShjM5V$<bM8B(|%OW$j90VT|=j(pJZKe>)z9_Q{lKMIy;UoJKVW zI#V;#Y;@PHC}hTsHpP_|<hn4X>vVP|)WFGGtSLvv7|Gc+h|@a9#1WTSuBYWzW8dVj zMbepL6%Bnw!JV=F*cZU`8TOuu->qEL9|w_Uxl*?<IRo!!qS$p4NC(8DZjF)7js8a* z?S+LACQ;12*6NbW(^`e?bFGEI8QFJ8k2cmy{Fsh3Jqh#W<0|0-vCtGhN6uE(iu42w zo{?tZ*z{8KX7%#f-eV>km7vzdiDq&CwgYyuD?S*wNZ?Ep5~&)IoH|)fu5P>42r#i= z@~x;;Q%)v#Tfa_`JHnrA5O>FL!eqX60T+WnTj?qL!bvnA%ki<CggNbQXRq%om%}{m zYki-!iP_cw_s65B54rZC35Nke)$Uq$Y4#uaLA>I+o>^Zf^QFDv(l-5hz=;bm)^1?K zlglAmL2>MP_6|dr*DSn;PA)L@h3m-jz!@)?{ok^42-e5nvGfy*_x?sgsnzFyfso(f zG3uffBae}{9kIVqnd?k9%VMC<C=P;c904aO1hhQ9BPEs(_%sb(b@tzl;kx(Q>-R61 z=Eaxim)?%58wv<~>vOB(5ol|fu~c`m_o^nz9Lo@%QYj~=p`Qfff|T$|T*8)A?>9)L zmVPg!DsL55;lzFNWsg_FC!bT%-&DAU5#H(rP|jZ$+SEXZQe?0KrYpgJD;~nWj(84x z<2Lf>bT!^lK>!XOQXNdQY{{q(O<awrxO~nj2UZXa;q^I1t}A1yRG&OdJ~6A9o_(ur zo|AIj#o8Xgs2q#k)r6+O9`ED~N7H(QfRnavw7_v_m$K>#J1*kV6R|(KUc<;O<%C`N z-^u<d8FP<?)j>s8TsKv{+VYGU%IU6yRi7>rRW7t0;9oVJksqf6xsNI9uNGC8k7t2_ zjZ@tnSF9q_gSW<OK>`(ae<gqIaM=x@Cms__Zo-e1-YS;iotqUx_7X=Z&}-2W{g=uz z=onpJRBeCC1u?E?(RrG;+jwD~B<@%h*09~_D7#ygmBUQ+8j#|z7LMh3!;WI!6YlrJ zBV_^iA^}JN&pvR#qwlTnj~|A#RLSoSj*DgQqzAmRcMsu$$HQ`Bn?78eHqvW~Yaq`t zus%tEhGkb^+3&MyyX^@)vWMccLO((j@FoAF@4=i+vt!r|$O2FTc=XBXws>~&Xnc<W zQm=b;ezeSMBvT636x%2rnl~i2MImWR)x?D7UD?{w=m6D$s=*)N|A-Yfp!szx7ytm! zf7ZzVJ(v9-?e;I1ZEftNZ=~;}4~PMH@&ho8wqXpkaDvq$yLA*ondL$NHWkz?yex<R zGXwND+7B9bq&=WJat@s+U8?!{i{F)9Jjd81)Q4Q0q4O@h*~ns{<jYR7hl6V$@A2!J zSj!Wx;c_2O=6hsrCmO*S|FMVvm!y@A<p91xowJsm8m|{gkDPvjTYK%666Xt<7Sfql z*r?}OeE-Cs(){Oc!q%Y}!HdET`r{>PI;t8d3{;I6DZD!iqbqb3jZ1*1P%lbQXRBXp zi{;YAuQKDM@kE)iY$(@>t`2YbFF_@k3p}a$ExNk+>^@t~BS%LGV``GonD2y=H4wf| zDCw*X32_*WObVR^2O}?uZkYY$RihY|b6~v9*XVNBq1WWg>g$b|thCsjqKWc<HDHhI zB<?@`hw=YY12X(q1Ev~lBEt-Zh=w|y{!u8z8s85ShhaUp5lO`_4<uNi5GxIGat-qf z8vqgaM~ny(V}Aex+4=l_z#e_P{sz2#-{-11`~WuvTDU`GAp!sA6X&fg&ocr6K#cuv z2ObIiYVdS7;{f0T;qQ}fprfO^{u_Uov%0!^dU~3ws!BFG8X5}j_LUp?_!#`o&H<q3 zY3ca7I&%SK<8E&M0Nv7aVx8A(>+m&MKOf(9Rpt8aeO@k}eQnKZG8WNb;7hlv2Gdd9 zUf)$DrYL{nZdR9>*xis7)g0Vjw)Y*C<c6-9I7h|<CTCn0&_<o62_rTL1_0vL!I6gX z=h6aV4mfG}{mmSZ&%qB+VRd093QMfEh8#K%h`eGfBaOgaoxIq%KY{BiLj!@86bVuu z&;-CQGkF|FzfVTCw7i6HXcYR*_b2xkt0_mq0sufds|Ia}*y+8`Wfr?MaT`VcZ_agN zQnyUupWyn;amVxQX{wOatJ^}pHL>P|>u&~@*2wVjZ_4$8MSL7s0C`hKso|-%qUn3+ zgJb=1Wesp2oA1Vs=TqndIUGB6n7|!h=AV!Q?-V*#)@RFU5yyDUN_$z&8u~GB{$&;J z*q`uy%MUcunH3$>k<i`auZVZj;Jgg_a!`atn9F^tb+a`2sDjKIY5FPsVeYJ!rO4$s zA7NT1Q!p;2Zn?R)hGfOwhY&TNCO5BV{8+s;L+@eW=NF=OAYBJ%v3pjra(-}CDcFUI zw)8s?SzG6Y*UG%ptU#Yv2`s<5X!_yCPxtN~yAe0F;o+Jjo0wp)j^YhrfTW=X3jf~t zg!<Bjy1yl1;hG5Q)Q6e~v^3L-(5Nz`?JpHd1inFP@Tx{dORZkDZ#uHK>dt<vB{w^_ zgVArtp0L;z(2k0ds#oRjKY1g~KpWsZceD&2CbPq%XQ0Z(lH4v(!&fCY=57m;DD|#J zOyaVx-<&z6J~)!ws!F@a;&MatytV-)bCy=M)9bAlcE`g{;?LMplki`N#tLf9^}aDE z;HG8Snr~0ffl=_dw<B5IzOYBXV;Q2hlcxrsHUT2cH=og*?*2|7V2jucNC61bZw#Cv zp=DV<jN%J~VR=3X^>;~2Md_+f`A$PxVM~TevI*#TLiPY&ES*tTdk8$d5|^m@45_Q- z!dpCl)~EA)JwV_6uG>XXfv*nVmWdiE;ALeqWnmnKxUl!)rLyo(%&VE9hMk7g*7|<> zmP<J|n9XB2>&)x1Q&{V?D#sSGt>-XsGi#8aS@T-i*MD2KVu@P?MG(!qaGV=9TRWjW zZDA_VFAz|Vlh@b=#iG|Lx+m^V;pjybo>_b8R3$pt2y6YpZa#Dqe&HB?7u=JiWviZL zyhs0yrIU)Qsx(<pQzuT(diVx~48Ch6TRRM;km8ALDWn9YD?}e}ob3U8tZ^6CDB_}( zB?p;a;thP%tSKlC3D(Y~(a<*T*afUXT|`8i$kiL1a6Lde^4fNZd!&>2qBv`+IsuQ9 z6q#5Uu3938k3iTV@4)tIu80C_{3Ig<XHKsvf6nJ?U++n58ix*5$8{IQyWV@E%Y=W8 z5}6VOdSt>T&r5Um=orU~uc`TZR`w!JESJ!o=5papVl$f-s4Z*ErBEHG2;C3sE^?<8 z<&Lts^fT2tUS8ez(mh0nG;td=Q$uglrOmKYb|<TvRR0D(7#hNcLyO?a-&E+iBSs*_ z(#C+?diN=N-H-Ke2^xm{eLLB7rc7)pwv8Lv&n0#07ED{_)D)D$>stK=>B?_Ja!{ki z_oB1duI)^Tol@XJ(;2Hf*jQ|GWp1xEpjTLOL4jD$+R&)mAePYk$^Ipa(w3X^Te7Ks zU;N0h-o=!IMiD*vpaEw%6+rt5G4Px+^#KpjUW$+dup}XPf>k=Udck}>^9JvF%>82p z@QP5s{Y|hL|C-M-V8ouRiiai=jNAY|7%{i%-`FLCJ=M&M$NvO1s>NQaJy)7iNg^NY z7J&!Au^FTBY*?^fs;n;f^BqP*Pu=1toK)5bkRTjsg+Jw$b(c~4uH1BV!c=~A(svkn zoK&AKgapcn8#pj)Ayj@G;+M)1YNW$^yz8DHybfs@FM;!nitbq0P0I*v`axn-Tp@6R z1S?Dsco;9M)@TCLdi!(NM{1Ol%%G5vXj*he-{=*Xrkl7B&aSQEoq}Hc0~79|U0;bZ zVumB5sVSWFis3+5B(4AR41`J};rYxJ3#7|~4@;h#A3N&2d>InZFX{6Vzk1ly4#n-B zTVBae`xrk`J6S0@<ZAtf-yY>saXu`x(L0Te(a-Jco>2S!uTe0?8!nW{D#tWw@Ku2| zdRj5~5*g{FpiSx@HI8=$Qc-NEV=rY2ODYK)Sy^4SpX_|eXar^aea@{2mI)zs9uckD zv|Ix|bL=ZoneN)Ib#((Q;5VT&kcc03X~C^ym@dcGwW*vIyS2kJcuj`57J>aEZrdyr z%FAQ`(3YO=pmb+g9+y|m`!`~By6E=rhXkM?@0<@ChT%?s9w<tOQKHZ`Jn$<RSIp>e zjB!%t<boA2T2jd2r?TUJw_2#Eyw0AoVEBhvUUMhG93KUhpCf5!7-|)B#Fs9y@ul;K zGG!SKi$_QpOio9v#@PkZoQnVxC_zv9ERazxLaYf-SCqv_DQ&cE;QnHw%_X!9;Q0#x z<8j=NUD%dlqB|ajcu=!p_Uvwa+zrWz_1<8E5R4HIFcntCLTKnP&>te)LA$3)sm(}d ziDhib?mT~&O`TlAS~W2~9r!@BhtaUtDmz-dl12~_t-7{q+rO}uYpE?0fzcUCN{D_> zY+YzJeA|xIdV!zEa#jf=YbHtkPV^<_*9tskHg*VTd-q5W0?Cw_V{HXXo^;PMnvsxT z9ihDES(|W`=3!*Ce56&z7nja`p+az(=iPW8#=56=^N&+b5<dU1b@*srr@XwW%2b@R zltQJF4XRE!2KNq<4}>dYp{8)dEftg@x-GK*wJ~Ku?U_psz)K14Oq)gMlmu-UI8<$i zyx_GyZ%WQlRM4#S1P&CuL4i_<EdP};W><2z@p1c&NTL3;0Psa%(BP(rurPv|K#+q- zgf=eIQW4G~I2@X~u&Zo%hL8CPBk)1+s{~D76F+to(A+^FG~R)`L|Co(<1B~WK`9Dn zpZDhQDu&??^Zmf_9uG2>zVO1crFmeXSY?WKp1}e9@b$>|^@pD@w%esuod3;WQk?ur z+tMI(IL4?ySFX00f<(7JB|~+_x~(A)Z)mb`!?r?6A^rG_vG*`;>E0U>t;b@RpLK@l zQ98tXGjYSaMt%`l=p_Ey&09sKHS;tZMM{l;_e$PKvVR+bb~$RM0BO8zgrn=ak=B&* zV^%F<B(^!>MD;^M74J~(Dv<sX?^prEKSMM^CVT8i&Slu6q~UBrL;^3hYzp_}E!X-L z5@CBYS;(?@e^a3dRDbfSYkfe<t!dM9SyK74Sd?n=8S^?x@pK{NN?doJc+(I+gI>lC zgz_6ZrYC{5a~D^DyVh^ftG{pIb`F`vqihfuc*xXpmOZTGAiX<RvL>~&NQZS4w-WQ* zOLY4OtV$-x4$*Z677LjbL0`A*V}0fQ=MCbCgb|VE*>=tWPKuCmxV1>S_|Qs32VFN6 z+)b8Se_;BXVPCgbYa54~!AQ9-km5W<Gup`r#f%R12h%o^PmnP0aq@%8^MFjy#Mol= zZ%8A9y@q(y*e;ZdpxMHT5yjA+NO?I?2BD!<Sta3;`?A1lzg9J8eLidWGRNBZomuV( z-q%xz8ukN_4t0R_AtFy=Uze=jV8OAxELPFtZL$D&Y)=_F=&;9)2hP|g#tjyn^=H<s zB*e3)JSvB2D<eBCu1Q+G^D|sNZUvvr*s(%MzM&{!aZp9IeF1p8d~C_4!_`VjwYg4) zToN1WdgBFys%u4VoA^!IWGb$&IW84l9>91rsWRmmzUtQrD3HPxr8KYW7<OC!kqKF- zr9n0Dt+_Wjn=4EOYAJsXBuL>J++Y<15y*luBf)IOb)sO>qJ*b;Cu_?d!1-UG#8-*x z$bgJPYA+95BEn;GcJ@iU^NE8WAzp8tLM?&3yF?-uuh|jL7aiP~0Hf(imMOT&D2&-{ z?ocv~Z=P@NtIIGrOna;czWITgQ#2V-q(h#y=lwuIQW=S?N;b`PtO@qxh)8pNB^JWW ze)>U1mHG&uuPeu(h;7m4a4y(!Qs@^#ZD0GdK%N|;4z|xJ(27-~;{3Qfsj|mur;+{< zo1xvv{^2j0%n^;x{@KSmG5MAZn=OK;6E!X*xZB18SE0<}_=d5eNPI!aC$Cjmr1P0F z7aK?C!Vqv(a7`fWs6bGg<2DUXAhVc~59*TJ!yt-Znq|mT&l0Uhe()LrL_~rfARkz4 z;eH)nJ9vsDeDYqe+UuqP)UQaf%0H~ThJN|9xAGU9(2>W_&OfLmPT@q!JF+c!g+ocY z(gGP`{eVE`vQ9^Uqfpc~r~yRLlntj}z!y+^YAoZ|(qJAtZ9Tb2r?xLD>x%pcy(q7} z5?kVn$bSy;drb*JRL%}M@(z~aX}m+~k3~IOE0cizwR|fQIu+H`FBWq+OH>qk1EXQ` zq*j6Dtd81>S|xEr7c)_xvW$kX6c6ugp?jn}_KhuZcvRH2Mi=}$Rg$7)=E&tBg@>F< z1^OhYYu%4gldnRSy;|*JWS^F0z36ywH=7C3@YFL0Ms?!AccyD#_pnG%8INJGb)DU@ z)F$VZL)h-^HRRcrZ;uu-Wyr~uQ|#8NAT9lB%)7*EIPbC+&(#KeoV}&EH!+;-aP(S+ z<GQiLktX~>D)u*j^~XrbQn59Fk&KCNUd9-l#_=v$AlpWnh_%3%{qTUV$>E{Vd8JoA zkz_ls3w|?ro!yho#u5_w=x(T;zep@$e=P?oNd+<5A)fLL;%M$@rWII)DEX13*149T zXcXTdANg~F@$~px0RnB;bE$P`?0it(#cIGCP%{u<BBIJG)l{0(-mHvane`$h>7K=M zs;O6jMzovyu1I}jvEGo2z&3&?ZArt*#jFfV7|V;zuudi&DC>6k@+M2WLGw<bmqM&d z)YLs|U!!LR^)9ZMKMbsi1DJU$+-UuPfEkrMC!7W@--Z(-d$d(tSiRUM-F-PPXRN(r z*bt=*PO|YGipvje?k7hlKCPr1MjE@bZy8Ti<%w|Iknhja7M2|-jZO3!(?v_Bc~TE7 z#|%cjPAHc*Z)SCvQ*uDoQ~hPCP9)d9?30TUUrXE@e>(R580s-+@A#7K`ifK6rqgE7 zB9`;tYfxV5LVt<Y0A{*^=%DfGgYW1T{$`eRY=me;W5Uh<{?}1~O9!3k_~)if{p$?) ze|J>=)9FrA(ALJu*v3g$$=%M_QTsnM%Cz#P%>X^jcfhw`@@hC(8nZNVaHux!-=cbW z`sjSbY2o_z@9zbmMLl6mOfygO1KiEiA?K|fJ>v-@fB&Y&^s^%C_`K=t1(%HQ*LlZF z8R8*5)T9}M|1-5~9!VGWzv{gvr`dUMno*#^?`lm={R<3B&n@mfafFpw=B&X~xlfWl zfn~SfM$fc9ow4(1DBs2RwvSkzsjBnO{vP_ur{Vk3bB&AjBqw)1$rL@<d*;+M_eWb+ zR_vMk&NbxQ#*Xzq+^hMXY~Ob2azpA{=hkl}wHlg>LJapDi(*^*L50hF<_nprr=P2} zEqTJ+w1{b<?@Ybe8CQSvJ{Mrx-_Ys#bk&sk3MZ>xF1dZ{&&IxY_7@937t1E+Dz4!d zmP!dYP%v|I03+j7>Gq1dD_+`E^F3d)l50bc<!p=X0&mUKuY6duC;jbH;fng}yLT5( z+})rnuOt=m_<O(fR9<DTO+|a|<SNEZ=Y12Jef5OpOv}FdoJFe|&wg%VUKJP2d`oUN zPxA9L>$=GQ2Y-PVixeMHnOqMXwqaxk#UCS+2m|7*LVgvSX)VB2M!p&h3=+T?L<PVR zvXr94a?nv~Fb4SeLUdDhbwB>T4X9NaXp1t65vPD=Bbx$QF99_aY!-O!1iFD!GxnRG z2OjyBI0M~T7lES4211Q80;M%{Qzj|>U60*a&wxRJYzm6Az-D1}7JE`=Gb_-K>_A7L zhSpmSR0E;T0-qU*?yPS=-8UP`GcfEiU_>*;f(vX4XsJ$dMrvwFu|Al>8s=+Py!PD; z3~McnFki2UZVJSE2(z$yZr|}Gs?B~344dXMp*d@_7P^68qp*f~E8|yD*cqd!Nuj0^ z-4rBe;SO`;lUq^4yb-t@1Q?0vp@lWfk&iw_b=ITh=%ye!3v3qF&_X``6LexK0z6-V z<@{9iG=O}jA*kd;0FzZ<_n@B`if#b%idoRnhX^olE!Y5zgAmb8Kwe-7T3L$#jh7)N zz}MNLYeimu23moL08@bLgrIQ(TaSpY8F_IPXoVUAY`g*13<-aX^=jzmAg@XS4d)|( zz%7)8S?C&(X9!TkUj8=N0q9GZ&<%({8Vg6Y`OI^u0g!10bdAV0DXPuNFVSrV51gYL gfLxTL8X)%*YxNu8&B_J}7%?FHzzRIA_7{i;04=^u0ssI2 diff --git a/src/hct_mis_api/config/settings.py b/src/hct_mis_api/config/settings.py index 6aafb0a0a0..8558b88c54 100644 --- a/src/hct_mis_api/config/settings.py +++ b/src/hct_mis_api/config/settings.py @@ -131,26 +131,26 @@ if env("POSTGRES_SSL", default=False): DATABASES["default"]["OPTIONS"] = { "sslmode": "verify-full", - "sslrootcert": "/code/psql-cert.crt", + "sslrootcert": "/certs/psql-cert.crt", } DATABASES["cash_assist_datahub_mis"]["OPTIONS"] = { "sslmode": "verify-full", - "sslrootcert": "/code/psql-cert.crt", + "sslrootcert": "/certs/psql-cert.crt", "options": "-c search_path=mis", } DATABASES["cash_assist_datahub_ca"]["OPTIONS"] = { "sslmode": "verify-full", - "sslrootcert": "/code/psql-cert.crt", + "sslrootcert": "/certs/psql-cert.crt", "options": "-c search_path=ca", } DATABASES["cash_assist_datahub_erp"]["OPTIONS"] = { "sslmode": "verify-full", - "sslrootcert": "/code/psql-cert.crt", + "sslrootcert": "/certs/psql-cert.crt", "options": "-c search_path=erp", } DATABASES["registration_datahub"]["OPTIONS"] = { "sslmode": "verify-full", - "sslrootcert": "/code/psql-cert.crt", + "sslrootcert": "/certs/psql-cert.crt", } # If app is not specified here it will use default db diff --git a/src/.coveragerc b/tests/.coveragerc similarity index 100% rename from src/.coveragerc rename to tests/.coveragerc diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 7b7437a9ce..c0fb59b405 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -52,7 +52,7 @@ def pytest_configure(config: Config) -> None: settings.SECURE_REFERRER_POLICY = "same-origin" settings.CACHE_ENABLED = False - settings.TESTS_ROOT = os.path.join(settings.PROJECT_ROOT, "../../tests/unit") + settings.TESTS_ROOT = "/tests/unit" settings.CACHES = { "default": { "BACKEND": "hct_mis_api.apps.core.memcache.LocMemCache", From 41e189f60eac5f7af4d1986ed592f62264b54252 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Tue, 1 Oct 2024 10:14:35 +0200 Subject: [PATCH 038/202] mypy --- src/hct_mis_api/apps/registration_datahub/validators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hct_mis_api/apps/registration_datahub/validators.py b/src/hct_mis_api/apps/registration_datahub/validators.py index ad087f116a..f69c067291 100644 --- a/src/hct_mis_api/apps/registration_datahub/validators.py +++ b/src/hct_mis_api/apps/registration_datahub/validators.py @@ -1461,7 +1461,7 @@ def _get_field_type_error( raise @staticmethod - def validate_collectors_unique(household_collectors_data: list) -> Optional[dict]: # noqa: F701 + def validate_collectors_unique(household_collectors_data: list) -> Optional[dict]: collectors_unique_data = [] for collector_info in household_collectors_data: collector_data = [ @@ -1480,6 +1480,7 @@ def validate_collectors_unique(household_collectors_data: list) -> Optional[dict "message": "The same individual cannot be a primary and alternate collector for the same household.", } collectors_unique_data.append(collector_data) + return None def validate_everything( self, submissions: List, business_area: BusinessArea, skip_validate_pictures: Optional[bool] = False From 700ce260faa616be41cd198cb9e0f18e47579f14 Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Tue, 1 Oct 2024 14:03:41 +0200 Subject: [PATCH 039/202] fixes after structure change --- .github/helpers/docker-compose.selenium.yml | 3 +- .github/workflows/ci.yml | 6 +-- .gitignore | 2 + development_tools/compose.selenium.local.yml | 52 -------------------- development_tools/compose.yml | 6 +-- development_tools/local_selenium_env.sh | 10 +++- src/frontend/package.json | 2 +- tests/selenium/conftest.py | 31 +++++++----- 8 files changed, 38 insertions(+), 74 deletions(-) delete mode 100644 development_tools/compose.selenium.local.yml mode change 100644 => 100755 development_tools/local_selenium_env.sh diff --git a/.github/helpers/docker-compose.selenium.yml b/.github/helpers/docker-compose.selenium.yml index 9637daa92a..663e12ec39 100644 --- a/.github/helpers/docker-compose.selenium.yml +++ b/.github/helpers/docker-compose.selenium.yml @@ -4,8 +4,7 @@ volumes: services: backend: volumes: - - ../src/report/screenshot/:/code/screenshot/ - - ../src/report/:/code/report/ + - ../tests/report/:/tests/selenium/output_data/report/ - type: volume source: backend-web-app target: /code/src/hct_mis_api/apps/web diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abddcdc030..21da0f1780 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -265,7 +265,7 @@ jobs: -f ./.github/helpers/docker-compose.selenium.yml \ run backend bash -c " waitforit -host=db -port=5432 -timeout=30 - pytest -svvv $extra_options /tests/selenium --cov-report xml:./coverage.xml --html-report=./report/report.html --randomly-seed=42 + pytest -svvv $extra_options /tests/selenium --cov-report xml:/tests/selenium/output_data/report/coverage.xml --html-report=/tests/selenium/output_data/report/report.html --randomly-seed=42 " - name: Upload Artifact uses: actions/upload-artifact@v4 @@ -273,14 +273,14 @@ jobs: continue-on-error: true with: name: report - path: ./src/report/ + path: ./tests/report/ retention-days: 5 - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 if: always() continue-on-error: true with: - files: ./src/coverage.xml + files: ./tests/report/coverage.xml flags: e2e token: ${{ secrets.CODECOV_TOKEN }} verbose: true diff --git a/.gitignore b/.gitignore index aea161b528..705c1ce84e 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,5 @@ venv.bak/ cov.json .vscode/ +output_data/ +.coverage diff --git a/development_tools/compose.selenium.local.yml b/development_tools/compose.selenium.local.yml deleted file mode 100644 index 38e8c103fe..0000000000 --- a/development_tools/compose.selenium.local.yml +++ /dev/null @@ -1,52 +0,0 @@ -version: "3.7" - -volumes: - backend-data-selenium: - backend-web-app-selenium: - db-selenium-data: - data_es-selenium: - -services: - redis: - restart: always - image: redis:4.0.11-alpine3.8 - ports: - - 6379:6379 - - db_selenium: - image: kartoza/postgis:14-3 - volumes: - - db-selenium-data:/var/lib/postgresql/data - - ./postgres/init:/docker-entrypoint-initdb.d - environment: - - POSTGRES_MULTIPLE_DATABASES=unicef_hct_mis_cashassist,rdi_datahub,mis_datahub,erp_datahub,ca_datahub - - POSTGRES_DB=postgres - - POSTGRES_USER=postgres - - POSTGRES_PASS=postgres - - PGUSER=postgres - - POSTGRES_HOST_AUTH_METHOD=trust - - POSTGRES_SSL_MODE=off - ports: - - 5432:5432 - - elasticsearch: - image: unicef/hct-elasticsearch - container_name: elasticsearch - build: - context: elasticsearch - dockerfile: Dockerfile - environment: - - node.name=es01 - - cluster.name=es-docker-cluster - - cluster.initial_master_nodes=es01 - - bootstrap.memory_lock=true - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - - xpack.security.enabled=false - ulimits: - memlock: - soft: -1 - hard: -1 - volumes: - - data_es-selenium:/usr/share/elasticsearch/data - ports: - - 9200:9200 diff --git a/development_tools/compose.yml b/development_tools/compose.yml index 5f140205e0..1e9c897272 100644 --- a/development_tools/compose.yml +++ b/development_tools/compose.yml @@ -128,7 +128,7 @@ services: env_file: - .env ports: - - "5433:5432" + - "5432:5432" healthcheck: test: [ "CMD-SHELL", "su - postgres -c 'pg_isready -h db -U postgres'" ] interval: 10s @@ -141,8 +141,8 @@ services: profiles: - default - services - expose: - - "6379" + ports: + - "6379:6379" healthcheck: test: [ "CMD", "redis-cli", "ping" ] interval: 10s diff --git a/development_tools/local_selenium_env.sh b/development_tools/local_selenium_env.sh old mode 100644 new mode 100755 index bd230dd602..f14ba3c450 --- a/development_tools/local_selenium_env.sh +++ b/development_tools/local_selenium_env.sh @@ -38,5 +38,11 @@ export CACHE_LOCATION=redis://localhost:6379/1 export USE_DUMMY_EXCHANGE_RATES=yes export ELASTICSEARCH_HOST=http://localhost:9200 export CELERY_TASK_ALWAYS_EAGER=true -export DATA_VOLUME="./data1" -export LIBRARY_PATHS=true \ No newline at end of file +export LIBRARY_PATHS=true +export PYTHONPATH=./src:$PYTHONPATH +export OUTPUT_DATA_ROOT=$(pwd)/tests/selenium/output_data +export MEDIA_VOLUME=$OUTPUT_DATA_ROOT/data +cd src/frontend +yarn +yarn build-for-backend +cd ../../ diff --git a/src/frontend/package.json b/src/frontend/package.json index f605e0a2bd..31926d3e61 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -6,7 +6,7 @@ "scripts": { "dev": "vite", "build": "tsc && vite build", - "build-for-backend": "yarn build --outDir '../backend/hct_mis_api/apps/web/static/web'", + "build-for-backend": "yarn build --outDir '../hct_mis_api/apps/web/static/web' --emptyOutDir", "build-and-watch": "yarn build-for-backend --watch", "lint": "eslint 'src/**/*.{ts,tsx}'", "test": "TZ=UTC jest --config jest.config.ts", diff --git a/tests/selenium/conftest.py b/tests/selenium/conftest.py index 38825ff265..573d94211d 100644 --- a/tests/selenium/conftest.py +++ b/tests/selenium/conftest.py @@ -6,6 +6,7 @@ from django.core.management import call_command import pytest +from environ import Env from _pytest.fixtures import FixtureRequest from _pytest.nodes import Item from _pytest.runner import CallInfo @@ -87,12 +88,20 @@ def pytest_addoption(parser) -> None: # type: ignore 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 - for file in os.listdir("report/screenshot"): - os.remove(os.path.join("report/screenshot", file)) - from django.conf import settings + 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" + if not os.path.exists(settings.SCREENSHOT_DIRECTORY): + os.makedirs(settings.SCREENSHOT_DIRECTORY) + print('settings.SCREENSHOT_DIRECTORY',settings.SCREENSHOT_DIRECTORY) + print('*'*70) + + for file in os.listdir(settings.SCREENSHOT_DIRECTORY): + os.remove(os.path.join(settings.SCREENSHOT_DIRECTORY, file)) settings.DEBUG = True settings.ALLOWED_HOSTS = ["localhost", "127.0.0.1", "10.0.2.2", os.getenv("DOMAIN", "")] @@ -179,7 +188,7 @@ def create_session(host: str, username: str, password: str, csrf: str = "") -> o @pytest.fixture def driver() -> Chrome: chrome_options = Options() - chrome_options.add_argument("--headless") + # chrome_options.add_argument("--headless") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-extensions") chrome_options.add_argument("--disable-plugins") @@ -187,10 +196,10 @@ def driver() -> Chrome: chrome_options.add_argument("--disable-notifications") chrome_options.add_argument("--disable-gpu") chrome_options.add_argument("--window-size=1920,1080") - if not os.path.exists("./report/downloads/"): - os.makedirs("./report/downloads/") + if not os.path.exists(settings.DOWNLOAD_DIRECTORY): + os.makedirs(settings.DOWNLOAD_DIRECTORY) prefs = { - "download.default_directory": "./report/downloads/", + "download.default_directory": settings.DOWNLOAD_DIRECTORY, } chrome_options.add_experimental_option("prefs", prefs) driver = webdriver.Chrome(options=chrome_options) @@ -600,9 +609,9 @@ def test_failed_check(request: FixtureRequest, browser: Chrome) -> None: # make a screenshot with a name of the test, date and time def screenshot(driver: Chrome, node_id: str) -> None: - if not os.path.exists("screenshot"): - os.makedirs("screenshot") + if not os.path.exists(settings.SCREENSHOT_DIRECTORY): + os.makedirs(settings.SCREENSHOT_DIRECTORY) file_name = f'{node_id}_{datetime.today().strftime("%Y-%m-%d_%H.%M")}.png'.replace("/", "_").replace("::", "__") - file_path = os.path.join("screenshot", file_name) + file_path = os.path.join(settings.SCREENSHOT_DIRECTORY, file_name) driver.get_screenshot_as_file(file_path) attach(data=driver.get_screenshot_as_png()) From bb0b5ac3e9e52c7b6e573f87881a2a1a3f9d7c70 Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Tue, 1 Oct 2024 14:17:12 +0200 Subject: [PATCH 040/202] fail fast selenium --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21da0f1780..9fe27f28e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -265,7 +265,7 @@ jobs: -f ./.github/helpers/docker-compose.selenium.yml \ run backend bash -c " waitforit -host=db -port=5432 -timeout=30 - pytest -svvv $extra_options /tests/selenium --cov-report xml:/tests/selenium/output_data/report/coverage.xml --html-report=/tests/selenium/output_data/report/report.html --randomly-seed=42 + pytest -svvv -x $extra_options /tests/selenium --cov-report xml:/tests/selenium/output_data/report/coverage.xml --html-report=/tests/selenium/output_data/report/report.html --randomly-seed=42 " - name: Upload Artifact uses: actions/upload-artifact@v4 From e6c0472e7e8eb902968e4fdd5473fc12b3b47632 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Tue, 1 Oct 2024 14:27:30 +0200 Subject: [PATCH 041/202] fix e2e yesy --- tests/selenium/programme_management/test_programme_management.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index ae26036093..dc39300971 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -661,7 +661,6 @@ def test_create_programme_back_scenarios( @pytest.mark.usefixtures("login") class TestManualCalendar: - @freeze_time("2024-09-09") @pytest.mark.parametrize( "test_data", [ From 1cda51f9f72bfa25b73a7f35d023c618f0f436e5 Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Tue, 1 Oct 2024 14:33:51 +0200 Subject: [PATCH 042/202] remove comented headless --- tests/selenium/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/selenium/conftest.py b/tests/selenium/conftest.py index 573d94211d..ea799d20c8 100644 --- a/tests/selenium/conftest.py +++ b/tests/selenium/conftest.py @@ -188,7 +188,7 @@ def create_session(host: str, username: str, password: str, csrf: str = "") -> o @pytest.fixture def driver() -> Chrome: chrome_options = Options() - # chrome_options.add_argument("--headless") + chrome_options.add_argument("--headless") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-extensions") chrome_options.add_argument("--disable-plugins") From f4313175d5b1066b9bb4918a970e22a0800dd91e Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Tue, 1 Oct 2024 14:59:01 +0200 Subject: [PATCH 043/202] e2e fixes --- tests/selenium/payment_module/test_payment_plans.py | 12 +++++++----- .../people/test_people_periodic_data_update.py | 5 +++-- .../test_periodic_data_templates.py | 6 ++++-- .../test_periodic_data_update_upload.py | 6 ++++-- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/tests/selenium/payment_module/test_payment_plans.py b/tests/selenium/payment_module/test_payment_plans.py index e251d493b0..c01bc99975 100644 --- a/tests/selenium/payment_module/test_payment_plans.py +++ b/tests/selenium/payment_module/test_payment_plans.py @@ -6,6 +6,8 @@ import openpyxl import pytest from dateutil.relativedelta import relativedelta +from sorl.thumbnail.conf import settings + from tests.selenium.page_object.payment_module.new_payment_plan import NewPaymentPlan from tests.selenium.page_object.payment_module.payment_module import PaymentModule from tests.selenium.page_object.payment_module.payment_module_details import PaymentModuleDetails @@ -44,7 +46,7 @@ pytestmark = pytest.mark.django_db(transaction=True) -def find_file(file_name: str, search_in_dir: str = "./report/downloads/", number_of_ties: int = 1) -> str: +def find_file(file_name: str, search_in_dir: str = settings.DOWNLOAD_DIRECTORY, number_of_ties: int = 1) -> str: for _ in range(number_of_ties): for file in os.listdir(search_in_dir): if file_name in file: @@ -122,11 +124,11 @@ def create_targeting(create_test_program: Program) -> None: @pytest.fixture def clear_downloaded_files() -> None: - for file in os.listdir("./report/downloads/"): - os.remove(os.path.join("./report/downloads", file)) + for file in os.listdir(settings.DOWNLOAD_DIRECTORY): + os.remove(os.path.join(settings.DOWNLOAD_DIRECTORY, file)) yield - for file in os.listdir("./report/downloads/"): - os.remove(os.path.join("./report/downloads", file)) + for file in os.listdir(settings.DOWNLOAD_DIRECTORY): + os.remove(os.path.join(settings.DOWNLOAD_DIRECTORY, file)) @pytest.fixture diff --git a/tests/selenium/people/test_people_periodic_data_update.py b/tests/selenium/people/test_people_periodic_data_update.py index 822e39dd12..2956a6017a 100644 --- a/tests/selenium/people/test_people_periodic_data_update.py +++ b/tests/selenium/people/test_people_periodic_data_update.py @@ -4,6 +4,7 @@ import pytest from dateutil.relativedelta import relativedelta +from django.conf import settings from hct_mis_api.apps.account.models import User from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory, create_afghanistan @@ -55,8 +56,8 @@ @pytest.fixture def clear_downloaded_files() -> None: yield - for file in os.listdir("./report/downloads/"): - os.remove(os.path.join("./report/downloads", file)) + for file in os.listdir(settings.DOWNLOAD_DIRECTORY): + os.remove(os.path.join(settings.DOWNLOAD_DIRECTORY, file)) @pytest.fixture diff --git a/tests/selenium/programme_population/test_periodic_data_templates.py b/tests/selenium/programme_population/test_periodic_data_templates.py index 46ef641e61..a276b70c9c 100644 --- a/tests/selenium/programme_population/test_periodic_data_templates.py +++ b/tests/selenium/programme_population/test_periodic_data_templates.py @@ -2,6 +2,8 @@ from time import sleep import pytest +from django.conf import settings + from tests.selenium.page_object.programme_population.periodic_data_update_templates import ( PeriodicDatUpdateTemplates, PeriodicDatUpdateTemplatesDetails, @@ -32,8 +34,8 @@ @pytest.fixture def clear_downloaded_files() -> None: yield - for file in os.listdir("./report/downloads/"): - os.remove(os.path.join("./report/downloads", file)) + for file in os.listdir(settings.DOWNLOAD_DIRECTORY): + os.remove(os.path.join(settings.DOWNLOAD_DIRECTORY, file)) @pytest.fixture diff --git a/tests/selenium/programme_population/test_periodic_data_update_upload.py b/tests/selenium/programme_population/test_periodic_data_update_upload.py index 1f8c39a538..e827e73e0b 100644 --- a/tests/selenium/programme_population/test_periodic_data_update_upload.py +++ b/tests/selenium/programme_population/test_periodic_data_update_upload.py @@ -4,6 +4,8 @@ import openpyxl import pytest +from django.conf import settings + from tests.selenium.page_object.programme_population.periodic_data_update_templates import ( PeriodicDatUpdateTemplates, ) @@ -45,8 +47,8 @@ @pytest.fixture def clear_downloaded_files() -> None: yield - for file in os.listdir("./report/downloads/"): - os.remove(os.path.join("./report/downloads", file)) + for file in os.listdir(settings.DOWNLOAD_DIRECTORY): + os.remove(os.path.join(settings.DOWNLOAD_DIRECTORY, file)) @pytest.fixture From 9d0123e42ba1accf4565c7bb711aa39dbc626c39 Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Tue, 1 Oct 2024 17:40:57 +0200 Subject: [PATCH 044/202] fixed file path --- .../programme_population/test_periodic_data_templates.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/selenium/programme_population/test_periodic_data_templates.py b/tests/selenium/programme_population/test_periodic_data_templates.py index a276b70c9c..e2c3415964 100644 --- a/tests/selenium/programme_population/test_periodic_data_templates.py +++ b/tests/selenium/programme_population/test_periodic_data_templates.py @@ -147,10 +147,9 @@ def test_periodic_data_template_export_and_download( assert status == "EXPORTED" pageIndividuals.getDownloadBtn(periodic_data_update_template.pk).click() periodic_data_update_template.refresh_from_db() - user_path = os.path.expanduser("~") assert ( pageIndividuals.check_file_exists( - os.path.join(user_path, "Downloads", periodic_data_update_template.file.file.name) + os.path.join(settings.DOWNLOAD_DIRECTORY, periodic_data_update_template.file.file.name) ) is True ) From 4839f1f1b3020ab56984933901671a1dbe3e4497 Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Tue, 1 Oct 2024 17:52:30 +0200 Subject: [PATCH 045/202] fixed report path --- .github/helpers/docker-compose.selenium.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/helpers/docker-compose.selenium.yml b/.github/helpers/docker-compose.selenium.yml index 663e12ec39..4882b92ad5 100644 --- a/.github/helpers/docker-compose.selenium.yml +++ b/.github/helpers/docker-compose.selenium.yml @@ -4,7 +4,7 @@ volumes: services: backend: volumes: - - ../tests/report/:/tests/selenium/output_data/report/ + - ./tests/report/:/tests/selenium/output_data/report/ - type: volume source: backend-web-app target: /code/src/hct_mis_api/apps/web From 05b0fd2badded7ba1116cbfddd222011641ef6f1 Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Wed, 2 Oct 2024 09:23:17 +0200 Subject: [PATCH 046/202] run all tests --- .github/workflows/ci.yml | 2 +- .../{local_selenium_env.sh => local_selenium_init.sh} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename development_tools/{local_selenium_env.sh => local_selenium_init.sh} (97%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9fe27f28e3..21da0f1780 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -265,7 +265,7 @@ jobs: -f ./.github/helpers/docker-compose.selenium.yml \ run backend bash -c " waitforit -host=db -port=5432 -timeout=30 - pytest -svvv -x $extra_options /tests/selenium --cov-report xml:/tests/selenium/output_data/report/coverage.xml --html-report=/tests/selenium/output_data/report/report.html --randomly-seed=42 + pytest -svvv $extra_options /tests/selenium --cov-report xml:/tests/selenium/output_data/report/coverage.xml --html-report=/tests/selenium/output_data/report/report.html --randomly-seed=42 " - name: Upload Artifact uses: actions/upload-artifact@v4 diff --git a/development_tools/local_selenium_env.sh b/development_tools/local_selenium_init.sh similarity index 97% rename from development_tools/local_selenium_env.sh rename to development_tools/local_selenium_init.sh index f14ba3c450..930a8b3930 100755 --- a/development_tools/local_selenium_env.sh +++ b/development_tools/local_selenium_init.sh @@ -41,7 +41,7 @@ export CELERY_TASK_ALWAYS_EAGER=true export LIBRARY_PATHS=true export PYTHONPATH=./src:$PYTHONPATH export OUTPUT_DATA_ROOT=$(pwd)/tests/selenium/output_data -export MEDIA_VOLUME=$OUTPUT_DATA_ROOT/data +export DATA_VOLUME=$OUTPUT_DATA_ROOT/data cd src/frontend yarn yarn build-for-backend From 20459e7b419724ad6f9d6bed8902f7269252a4eb Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Wed, 2 Oct 2024 11:34:21 +0200 Subject: [PATCH 047/202] fixes to reports and coverage --- .github/helpers/dev.sh | 2 +- .github/helpers/docker-compose.selenium.yml | 3 ++- .github/helpers/docker-compose.tst.yml | 2 +- .github/workflows/ci.yml | 7 +++---- development_tools/local_selenium_init.sh | 15 +++++++++++---- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/helpers/dev.sh b/.github/helpers/dev.sh index ca25a814cc..ac94420868 100755 --- a/.github/helpers/dev.sh +++ b/.github/helpers/dev.sh @@ -17,7 +17,7 @@ else -n auto \ --reruns 3 \ --reruns-delay 1 \ - --cov-report xml:coverage.xml \ + --cov-report xml:test-coverage/coverage.xml \ --randomly-seed=42 \ --create-db \ /tests/unit/ diff --git a/.github/helpers/docker-compose.selenium.yml b/.github/helpers/docker-compose.selenium.yml index 4882b92ad5..2fcbe8577e 100644 --- a/.github/helpers/docker-compose.selenium.yml +++ b/.github/helpers/docker-compose.selenium.yml @@ -4,7 +4,8 @@ volumes: services: backend: volumes: - - ./tests/report/:/tests/selenium/output_data/report/ + - ../../tests/test-coverage:/code/test-coverage + - ../../tests/report/:/tests/selenium/output_data/report/ - type: volume source: backend-web-app target: /code/src/hct_mis_api/apps/web diff --git a/.github/helpers/docker-compose.tst.yml b/.github/helpers/docker-compose.tst.yml index f9cb7512ab..2e2be2f001 100644 --- a/.github/helpers/docker-compose.tst.yml +++ b/.github/helpers/docker-compose.tst.yml @@ -3,7 +3,7 @@ services: backend: image: ${dev_backend_image} volumes: - - ../../src/test-coverage:/code/backend/test-coverage + - ../../tests/test-coverage:/code/test-coverage - ./dev.sh:/code/dev.sh depends_on: - db diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21da0f1780..8f70e52ea1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -228,7 +228,6 @@ jobs: - name: Unit tests run: | - # a little hack to allow to have 1 compose file running 2 different test suites because --env-file didn't work as docs suggested it should dev_backend_image=${{ vars.DOCKERHUB_ORGANIZATION }}/hope-support-images:core-${{ github.sha }}-dev docker compose \ -f ./.github/helpers/docker-compose.tst.yml \ --profile unit \ @@ -236,7 +235,7 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: - files: ./src/coverage.xml + files: ./tests/test-coverage/coverage.xml flags: unittests token: ${{ secrets.CODECOV_TOKEN }} verbose: true @@ -265,7 +264,7 @@ jobs: -f ./.github/helpers/docker-compose.selenium.yml \ run backend bash -c " waitforit -host=db -port=5432 -timeout=30 - pytest -svvv $extra_options /tests/selenium --cov-report xml:/tests/selenium/output_data/report/coverage.xml --html-report=/tests/selenium/output_data/report/report.html --randomly-seed=42 + pytest -svvv $extra_options /tests/selenium --cov-report xml:test-coverage/coverage.xml --html-report=/tests/selenium/output_data/report/report.html --randomly-seed=42 " - name: Upload Artifact uses: actions/upload-artifact@v4 @@ -280,7 +279,7 @@ jobs: if: always() continue-on-error: true with: - files: ./tests/report/coverage.xml + files: ./test-coverage/coverage.xml flags: e2e token: ${{ secrets.CODECOV_TOKEN }} verbose: true diff --git a/development_tools/local_selenium_init.sh b/development_tools/local_selenium_init.sh index 930a8b3930..b175319558 100755 --- a/development_tools/local_selenium_init.sh +++ b/development_tools/local_selenium_init.sh @@ -1,3 +1,7 @@ +if [ "$0" = "$BASH_SOURCE" ]; then + echo "Please source this script." + exit 1 +fi export SECRET_KEY=SOME_KEY_HERE export DEBUG=true export ENV=dev @@ -39,10 +43,13 @@ export USE_DUMMY_EXCHANGE_RATES=yes export ELASTICSEARCH_HOST=http://localhost:9200 export CELERY_TASK_ALWAYS_EAGER=true export LIBRARY_PATHS=true -export PYTHONPATH=./src:$PYTHONPATH -export OUTPUT_DATA_ROOT=$(pwd)/tests/selenium/output_data +SCRIPT_DIR=$(realpath "$(dirname $0)") +MAIN_DIR=$(realpath $SCRIPT_DIR/..) +echo "SCRIPT_DIR: $SCRIPT_DIR" +export PYTHONPATH=$MAIN_DIR/src:$PYTHONPATH +export OUTPUT_DATA_ROOT=$MAIN_DIR/tests/selenium/output_data export DATA_VOLUME=$OUTPUT_DATA_ROOT/data -cd src/frontend +pushd $MAIN_DIR/src/frontend yarn yarn build-for-backend -cd ../../ +popd From a80d5b1127bc5f5ca2d7ba5a440ce1af95ac416c Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Wed, 2 Oct 2024 11:40:21 +0200 Subject: [PATCH 048/202] add new column in payments table --- .../ProgramDetails/ProgramDetails.tsx | 22 +++++++---------- .../PaymentsTable/PaymentsTableHeadCells.tsx | 7 ++++++ .../PaymentsTable/PaymentsTableRow.tsx | 24 ++++++++++++------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx b/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx index 257ffae326..9838ed3699 100644 --- a/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx +++ b/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx @@ -1,22 +1,18 @@ -import { Box, Grid, Typography } from '@mui/material'; -import * as React from 'react'; -import { useTranslation } from 'react-i18next'; -import styled from 'styled-components'; -import { - ProgrammeChoiceDataQuery, - ProgramPartnerAccess, - ProgramQuery, -} from '@generated/graphql'; -import { MiśTheme } from '../../../theme'; -import { choicesToDict, programStatusToColor } from '@utils/utils'; +import { PartnerAccess } from '@components/programs/constants'; import { ContainerColumnWithBorder } from '@core/ContainerColumnWithBorder'; +import { DividerLine } from '@core/DividerLine'; import { LabelizedField } from '@core/LabelizedField'; import { OverviewContainer } from '@core/OverviewContainer'; import { StatusBox } from '@core/StatusBox'; import { Title } from '@core/Title'; import { UniversalMoment } from '@core/UniversalMoment'; -import { PartnerAccess } from '@components/programs/constants'; -import { DividerLine } from '@core/DividerLine'; +import { ProgrammeChoiceDataQuery, ProgramQuery } from '@generated/graphql'; +import { Box, Grid, Typography } from '@mui/material'; +import { choicesToDict, programStatusToColor } from '@utils/utils'; +import * as React from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; +import { MiśTheme } from '../../../theme'; const NumberOfHouseHolds = styled.div` padding: ${({ theme }) => theme.spacing(8)}; diff --git a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableHeadCells.tsx b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableHeadCells.tsx index a17c8109f3..4392c0f583 100644 --- a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableHeadCells.tsx +++ b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableHeadCells.tsx @@ -59,6 +59,13 @@ export const headCells: HeadCell< id: 'delivered_quantity', numeric: false, }, + { + disablePadding: false, + label: 'Status', + id: 'status', + numeric: false, + disableSort: true, + }, { disablePadding: false, label: 'FSP Auth Code', diff --git a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableRow.tsx b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableRow.tsx index c22b0dc410..b4b93311fa 100644 --- a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableRow.tsx +++ b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableRow.tsx @@ -80,16 +80,10 @@ export function PaymentsTableRow({ }; const renderDeliveredQuantity = (): React.ReactElement => { - const { deliveredQuantity, currency, deliveredQuantityUsd, status } = - payment; - if (status === PaymentStatus.TransactionErroneous) { - return <RoutedBox>UNSUCCESSFUL</RoutedBox>; - } - if (status === PaymentStatus.ManuallyCancelled) { - return <RoutedBox>CANCELLED</RoutedBox>; - } + const { deliveredQuantity, currency, deliveredQuantityUsd } = payment; + if (deliveredQuantity === null) { - return <></>; + return <>-</>; } return ( <> @@ -99,6 +93,17 @@ export function PaymentsTableRow({ ); }; + const renderPaymentStatusBox = (): React.ReactElement => { + const { status } = payment; + if (status === PaymentStatus.TransactionErroneous) { + return <RoutedBox>UNSUCCESSFUL</RoutedBox>; + } + if (status === PaymentStatus.ManuallyCancelled) { + return <RoutedBox>CANCELLED</RoutedBox>; + } + return <></>; + }; + const renderMark = (): React.ReactElement => { const { deliveredQuantity, entitlementQuantity } = payment; @@ -176,6 +181,7 @@ export function PaymentsTableRow({ <TableCell data-cy="delivered-quantity-cell" align="left"> {renderDeliveredQuantity()} </TableCell> + <TableCell>{renderPaymentStatusBox()}</TableCell> {hasPermissions(PERMISSIONS.PM_VIEW_FSP_AUTH_CODE, permissions) && ( <TableCell data-cy="fsp-auth-code-cell" align="left"> {payment.fspAuthCode || '-'} From 49816709e92fc27ef6082ea535754d5348d0c55f Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Wed, 2 Oct 2024 12:44:29 +0200 Subject: [PATCH 049/202] update snap --- .../ProgramDetails.test.tsx.snap | 64 ++++----- .../__snapshots__/PaymentsTable.test.tsx.snap | 122 ++++++++++++------ 2 files changed, 114 insertions(+), 72 deletions(-) diff --git a/src/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap b/src/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap index ac82cf4a5d..b9a8318d22 100644 --- a/src/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap +++ b/src/frontend/src/components/programs/ProgramDetails/__snapshots__/ProgramDetails.test.tsx.snap @@ -6,7 +6,7 @@ exports[`components/ProgramDetails should render 1`] = ` class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 sc-beySPh fvkwGO css-1ps6pg7-MuiPaper-root" > <div - class="sc-gLLuof bNTvKV" + class="sc-fsYfdN hnQuqu" > <h6 class="MuiTypography-root MuiTypography-h6 css-1j53xw6-MuiTypography-root" @@ -15,7 +15,7 @@ exports[`components/ProgramDetails should render 1`] = ` </h6> </div> <div - class="sc-hLQSwg ehkTgD" + class="sc-jTQCzO GcLEc" > <div class="MuiGrid-root MuiGrid-container MuiGrid-spacing-xs-6 css-zow5z4-MuiGrid-root" @@ -25,7 +25,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > status @@ -34,14 +34,14 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-status" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > <div - class="sc-eDLKkx deAQag" + class="sc-gLLuof PuEQG" > <div - class="sc-jTQCzO cQZnmW status-box-container" + class="sc-iBdnpw jiYZmp status-box-container" data-cy="status-container" > FINISHED @@ -56,7 +56,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > START DATE @@ -65,7 +65,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-START DATE" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > <time @@ -82,7 +82,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > END DATE @@ -91,7 +91,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-END DATE" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > <time @@ -108,7 +108,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > Programme Code @@ -125,7 +125,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > Sector @@ -134,7 +134,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-Sector" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > Child Protection @@ -147,7 +147,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > Data Collecting Type @@ -164,7 +164,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > Frequency of Payment @@ -173,7 +173,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-Frequency of Payment" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > Regular @@ -186,7 +186,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > Administrative Areas of implementation @@ -195,7 +195,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-Administrative Areas of implementation" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > Crime book. @@ -208,7 +208,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > Description @@ -217,7 +217,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-Description" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > Yeah worry might newspaper drive her many. @@ -230,7 +230,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > CASH+ @@ -239,7 +239,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-CASH+" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > No @@ -252,7 +252,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > Partner Access @@ -261,7 +261,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-Partner Access" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > Only Selected Partners within the business area @@ -275,7 +275,7 @@ exports[`components/ProgramDetails should render 1`] = ` > <div> <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > Programme size @@ -284,7 +284,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-Programme size" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > <div @@ -298,14 +298,14 @@ exports[`components/ProgramDetails should render 1`] = ` </div> </div> <div - class="sc-iBdnpw fHCMfN" + class="sc-guDLey cGgsPx" > <div - class="sc-fsYfdN fnzeIa" + class="sc-dmyCSP hqweMa" /> </div> <div - class="sc-gLLuof bNTvKV" + class="sc-fsYfdN hnQuqu" > <h6 class="MuiTypography-root MuiTypography-h6 css-1j53xw6-MuiTypography-root" @@ -314,7 +314,7 @@ exports[`components/ProgramDetails should render 1`] = ` </h6> </div> <div - class="sc-hLQSwg ehkTgD" + class="sc-jTQCzO GcLEc" > <div class="MuiGrid-root MuiGrid-container MuiGrid-spacing-xs-6 css-zow5z4-MuiGrid-root" @@ -333,7 +333,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="labelized-field-container-area-access-field" > <span - class="sc-guDLey RwNuC" + class="sc-hLQSwg hqLXmS" color="textSecondary" > Area Access @@ -342,7 +342,7 @@ exports[`components/ProgramDetails should render 1`] = ` data-cy="label-Area Access" > <span - class="sc-dmyCSP bGPBYf" + class="sc-eDLKkx gpSOvu" color="textSecondary" > Business Area diff --git a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/__snapshots__/PaymentsTable.test.tsx.snap b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/__snapshots__/PaymentsTable.test.tsx.snap index fff4879a39..28d3b823c4 100644 --- a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/__snapshots__/PaymentsTable.test.tsx.snap +++ b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/__snapshots__/PaymentsTable.test.tsx.snap @@ -255,6 +255,17 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] </svg> </span> </th> + <th + class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ygcj2i-MuiTableCell-root" + scope="col" + > + <span + class="sc-brSamD gfntwv" + data-cy="table-label" + > + Status + </span> + </th> <th class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ygcj2i-MuiTableCell-root" scope="col" @@ -289,8 +300,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -303,8 +314,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -317,8 +328,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -331,8 +342,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -345,8 +356,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -359,8 +370,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -373,8 +384,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -387,8 +398,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -401,8 +412,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -455,6 +466,11 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" data-cy="delivered-quantity-cell" + > + - + </td> + <td + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" /> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" @@ -511,6 +527,11 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" data-cy="delivered-quantity-cell" + > + - + </td> + <td + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" /> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" @@ -527,8 +548,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] style="height: 70px;" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" /> </tr> </tbody> @@ -894,6 +915,17 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 </svg> </span> </th> + <th + class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ygcj2i-MuiTableCell-root" + scope="col" + > + <span + class="sc-brSamD gfntwv" + data-cy="table-label" + > + Status + </span> + </th> <th class="MuiTableCell-root MuiTableCell-head MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ygcj2i-MuiTableCell-root" scope="col" @@ -928,8 +960,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -942,8 +974,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -956,8 +988,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -970,8 +1002,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -984,8 +1016,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -998,8 +1030,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -1012,8 +1044,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -1026,8 +1058,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -1040,8 +1072,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 data-cy="table-row" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" > <span class="MuiSkeleton-root MuiSkeleton-rectangular MuiSkeleton-pulse css-153n0d3-MuiSkeleton-root" @@ -1094,6 +1126,11 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" data-cy="delivered-quantity-cell" + > + - + </td> + <td + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" /> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" @@ -1150,6 +1187,11 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" data-cy="delivered-quantity-cell" + > + - + </td> + <td + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" /> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" @@ -1166,8 +1208,8 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 style="height: 70px;" > <td - class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr eIzMdV css-1ex1afd-MuiTableCell-root" - colspan="11" + class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium sc-blmEgr iYOZVm css-1ex1afd-MuiTableCell-root" + colspan="12" /> </tr> </tbody> From 80058fa8ecbf00d366d6fcee19812ad4c9c214c5 Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Wed, 2 Oct 2024 13:51:19 +0200 Subject: [PATCH 050/202] display status box --- .../PaymentsTable/PaymentsTableRow.tsx | 48 ++++------------ .../__snapshots__/PaymentsTable.test.tsx.snap | 56 +++++++++++++++++-- 2 files changed, 61 insertions(+), 43 deletions(-) diff --git a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableRow.tsx b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableRow.tsx index b4b93311fa..394547cca6 100644 --- a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableRow.tsx +++ b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/PaymentsTableRow.tsx @@ -9,35 +9,14 @@ import { ClickableTableRow } from '@components/core/Table/ClickableTableRow'; import { WarningTooltip } from '@components/core/WarningTooltip'; import { formatCurrencyWithSymbol, - opacityToHex, + paymentStatusDisplayMap, + paymentStatusToColor, renderSomethingOrDash, } from '@utils/utils'; -import { AllPaymentsForTableQuery, PaymentStatus } from '@generated/graphql'; +import { AllPaymentsForTableQuery } from '@generated/graphql'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { hasPermissions, PERMISSIONS } from '../../../../config/permissions'; - -export const StyledLink = styled.div` - color: #000; - text-decoration: underline; - cursor: pointer; - display: flex; - align-content: center; -`; - -const RoutedBox = styled.div` - color: ${({ theme }) => theme.hctPalette.red}; - background-color: ${({ theme }) => - `${theme.hctPalette.red}${opacityToHex(0.15)}`}; - border-radius: 16px; - font-family: Roboto; - font-size: 10px; - font-weight: 500; - letter-spacing: 1.2px; - line-height: 16px; - padding: ${({ theme }) => theme.spacing(1)}; - text-align: center; - margin-right: 20px; -`; +import { StatusBox } from '@components/core/StatusBox'; const OrangeError = styled(ErrorOutlineRoundedIcon)` color: ${({ theme }) => theme.hctPalette.orange}; @@ -93,17 +72,6 @@ export function PaymentsTableRow({ ); }; - const renderPaymentStatusBox = (): React.ReactElement => { - const { status } = payment; - if (status === PaymentStatus.TransactionErroneous) { - return <RoutedBox>UNSUCCESSFUL</RoutedBox>; - } - if (status === PaymentStatus.ManuallyCancelled) { - return <RoutedBox>CANCELLED</RoutedBox>; - } - return <></>; - }; - const renderMark = (): React.ReactElement => { const { deliveredQuantity, entitlementQuantity } = payment; @@ -181,7 +149,13 @@ export function PaymentsTableRow({ <TableCell data-cy="delivered-quantity-cell" align="left"> {renderDeliveredQuantity()} </TableCell> - <TableCell>{renderPaymentStatusBox()}</TableCell> + <TableCell> + <StatusBox + status={payment.status} + statusToColor={paymentStatusToColor} + statusNameMapping={paymentStatusDisplayMap} + /> + </TableCell> {hasPermissions(PERMISSIONS.PM_VIEW_FSP_AUTH_CODE, permissions) && ( <TableCell data-cy="fsp-auth-code-cell" align="left"> {payment.fspAuthCode || '-'} diff --git a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/__snapshots__/PaymentsTable.test.tsx.snap b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/__snapshots__/PaymentsTable.test.tsx.snap index 28d3b823c4..17be88292b 100644 --- a/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/__snapshots__/PaymentsTable.test.tsx.snap +++ b/src/frontend/src/containers/tables/paymentmodule/PaymentsTable/__snapshots__/PaymentsTable.test.tsx.snap @@ -9,7 +9,7 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 css-1ps6pg7-MuiPaper-root" > <div - class="sc-iCKXBC iyaLVj MuiBox-root css-u331zs" + class="sc-cbDaos fmVUtP MuiBox-root css-u331zs" > <h6 class="MuiTypography-root MuiTypography-h6 css-1j53xw6-MuiTypography-root" @@ -471,7 +471,18 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] </td> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" - /> + > + <div + class="sc-gjLLEI cQdyAk" + > + <div + class="sc-eAKtBH lfKnlm status-box-container" + data-cy="status-container" + > + PENDING + </div> + </div> + </td> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" data-cy="fsp-auth-code-cell" @@ -532,7 +543,18 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render loading 1`] </td> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" - /> + > + <div + class="sc-gjLLEI cQdyAk" + > + <div + class="sc-eAKtBH lfKnlm status-box-container" + data-cy="status-container" + > + PENDING + </div> + </div> + </td> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" data-cy="fsp-auth-code-cell" @@ -669,7 +691,7 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 css-1ps6pg7-MuiPaper-root" > <div - class="sc-iCKXBC iyaLVj MuiBox-root css-u331zs" + class="sc-cbDaos fmVUtP MuiBox-root css-u331zs" > <h6 class="MuiTypography-root MuiTypography-h6 css-1j53xw6-MuiTypography-root" @@ -1131,7 +1153,18 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 </td> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" - /> + > + <div + class="sc-gjLLEI cQdyAk" + > + <div + class="sc-eAKtBH lfKnlm status-box-container" + data-cy="status-container" + > + PENDING + </div> + </div> + </td> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" data-cy="fsp-auth-code-cell" @@ -1192,7 +1225,18 @@ exports[`containers/tables/paymentmodule/PaymentsTable should render with data 1 </td> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" - /> + > + <div + class="sc-gjLLEI cQdyAk" + > + <div + class="sc-eAKtBH lfKnlm status-box-container" + data-cy="status-container" + > + PENDING + </div> + </div> + </td> <td class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignLeft MuiTableCell-sizeMedium css-1ex1afd-MuiTableCell-root" data-cy="fsp-auth-code-cell" From 4dee570cadfe4d2f129e694d15c3f1ad1bf3754a Mon Sep 17 00:00:00 2001 From: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:33:29 +0200 Subject: [PATCH 051/202] =?UTF-8?q?2212815=5FNeeds=5Fadjudication=5FDelays?= =?UTF-8?q?=5Fin=5Fcreating=5Fduplicate=5Ftickets=5Fafter=E2=80=A6=20(#427?= =?UTF-8?q?6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apps/registration_datahub/tasks/rdi_merge.py | 16 +++++++--------- .../apps/registration_datahub/test_rdi_merge.py | 2 -- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/hct_mis_api/apps/registration_datahub/tasks/rdi_merge.py b/src/hct_mis_api/apps/registration_datahub/tasks/rdi_merge.py index 3815bec08f..e454cc6562 100644 --- a/src/hct_mis_api/apps/registration_datahub/tasks/rdi_merge.py +++ b/src/hct_mis_api/apps/registration_datahub/tasks/rdi_merge.py @@ -323,6 +323,13 @@ def execute(self, registration_data_import_id: str) -> None: CheckAgainstSanctionListPreMergeTask.execute(registration_data_import=obj_hct) logger.info(f"RDI:{registration_data_import_id} Checked against sanction list") + # synchronously deduplicate documents + deduplicate_documents() + # synchronously deduplicate biometrics + if obj_hct.program.biometric_deduplication_enabled: + create_grievance_tickets_for_dedup_engine_results(obj_hct.id) + update_rdis_deduplication_engine_statistics(obj_hct.program.id) + obj_hct.update_needs_adjudication_tickets_statistic() obj_hct.status = RegistrationDataImport.MERGED obj_hct.save() @@ -364,15 +371,6 @@ def execute(self, registration_data_import_id: str) -> None: populate_index(Household.objects.filter(registration_data_import=obj_hct), HouseholdDocument) logger.info(f"RDI:{registration_data_import_id} Saved registration data import") - transaction.on_commit(lambda: deduplicate_documents.delay()) - if obj_hct.program.biometric_deduplication_enabled: - transaction.on_commit( - lambda: create_grievance_tickets_for_dedup_engine_results.delay(obj_hct.id) - ) - transaction.on_commit( - lambda: update_rdis_deduplication_engine_statistics.delay(obj_hct.program.id) - ) - rdi_merged.send(sender=obj_hct.__class__, instance=obj_hct) log_create( RegistrationDataImport.ACTIVITY_LOG_MAPPING, diff --git a/tests/unit/apps/registration_datahub/test_rdi_merge.py b/tests/unit/apps/registration_datahub/test_rdi_merge.py index 379f2be498..cb9dce774d 100644 --- a/tests/unit/apps/registration_datahub/test_rdi_merge.py +++ b/tests/unit/apps/registration_datahub/test_rdi_merge.py @@ -590,8 +590,6 @@ def test_merging_external_collector(self) -> None: ) role = PendingIndividualRoleInHousehold(individual=external_collector, household=household, role=ROLE_ALTERNATE) role.save() - self.rdi.program.biometric_deduplication_enabled = True - self.rdi.program.save() with capture_on_commit_callbacks(execute=True): RdiMergeTask().execute(self.rdi.pk) From 522579b8b2aeff90d367b83bf725060d38485767 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 04:40:48 +0200 Subject: [PATCH 052/202] Program cycle requred for TP, adjust create TP from list in admin --- src/hct_mis_api/apps/household/forms.py | 7 ++++++- src/hct_mis_api/apps/payment/fixtures.py | 1 + .../apps/targeting/celery_tasks.py | 1 + src/hct_mis_api/apps/targeting/fixtures.py | 3 ++- .../targeting/migrations/0048_migration.py | 20 +++++++++++++++++++ src/hct_mis_api/apps/targeting/models.py | 2 +- 6 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 src/hct_mis_api/apps/targeting/migrations/0048_migration.py diff --git a/src/hct_mis_api/apps/household/forms.py b/src/hct_mis_api/apps/household/forms.py index 4b582b5bc9..30a3b19e78 100644 --- a/src/hct_mis_api/apps/household/forms.py +++ b/src/hct_mis_api/apps/household/forms.py @@ -15,7 +15,7 @@ PendingIndividual, XlsxUpdateFile, ) -from hct_mis_api.apps.program.models import Program +from hct_mis_api.apps.program.models import Program, ProgramCycle from hct_mis_api.apps.registration_data.models import RegistrationDataImport from hct_mis_api.apps.targeting.models import TargetingCriteria, TargetPopulation @@ -189,6 +189,10 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: assert self.program is not None read_only = kwargs.pop("read_only", False) super().__init__(*args, **kwargs) + self.fields["program_cycle"] = forms.ModelChoiceField( + queryset=ProgramCycle.objects.filter(program=self.program), + label="Programme Cycle", + ) if read_only: self.fields["name"].widget = HiddenInput() @@ -196,6 +200,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: self.fields["target_field"].widget = HiddenInput() self.fields["separator"].widget = HiddenInput() self.fields["targeting_criteria"].widget = HiddenInput() + self.fields["program_cycle"].widget = HiddenInput() def clean_criteria(self) -> Optional[List]: try: diff --git a/src/hct_mis_api/apps/payment/fixtures.py b/src/hct_mis_api/apps/payment/fixtures.py index 5d8a3095b7..bb5878a813 100644 --- a/src/hct_mis_api/apps/payment/fixtures.py +++ b/src/hct_mis_api/apps/payment/fixtures.py @@ -959,6 +959,7 @@ def generate_payment_plan() -> None: business_area=afghanistan, program=program, created_by=root, + program_cycle=program_cycle, )[0] full_rebuild(target_population) target_population.save() diff --git a/src/hct_mis_api/apps/targeting/celery_tasks.py b/src/hct_mis_api/apps/targeting/celery_tasks.py index 12131a0fb6..65d8abd1d5 100644 --- a/src/hct_mis_api/apps/targeting/celery_tasks.py +++ b/src/hct_mis_api/apps/targeting/celery_tasks.py @@ -139,6 +139,7 @@ def create_tp_from_list(form_data: Dict[str, str], user_id: str, program_pk: str name=form.cleaned_data["name"], business_area=program.business_area, program=program, + program_cycle=form.cleaned_data["program_cycle"], ) tp.households.set(population) refresh_stats(tp) diff --git a/src/hct_mis_api/apps/targeting/fixtures.py b/src/hct_mis_api/apps/targeting/fixtures.py index a3f063a7e8..2953abb0e4 100644 --- a/src/hct_mis_api/apps/targeting/fixtures.py +++ b/src/hct_mis_api/apps/targeting/fixtures.py @@ -13,7 +13,7 @@ from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.household.fixtures import HouseholdFactory from hct_mis_api.apps.household.models import RESIDENCE_STATUS_CHOICE -from hct_mis_api.apps.program.fixtures import get_program_with_dct_type_and_name +from hct_mis_api.apps.program.fixtures import get_program_with_dct_type_and_name, ProgramCycleFactory from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.targeting.models import ( HouseholdSelection, @@ -94,6 +94,7 @@ class Meta: lambda t: Program.objects.filter(status=Program.ACTIVE).first() or get_program_with_dct_type_and_name() ) business_area = factory.LazyAttribute(lambda t: BusinessArea.objects.first()) + program_cycle = factory.LazyAttribute(lambda t: t.program.cycles.first() or ProgramCycleFactory(program=t.program)) @factory.post_generation def households(self, create: bool, extracted: Iterable, **kwargs: Any) -> None: diff --git a/src/hct_mis_api/apps/targeting/migrations/0048_migration.py b/src/hct_mis_api/apps/targeting/migrations/0048_migration.py new file mode 100644 index 0000000000..fca6eed297 --- /dev/null +++ b/src/hct_mis_api/apps/targeting/migrations/0048_migration.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.25 on 2024-10-03 01:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('program', '0053_migration'), + ('targeting', '0047_migration'), + ] + + operations = [ + migrations.AlterField( + model_name='targetpopulation', + name='program_cycle', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='target_populations', to='program.programcycle'), + ), + ] diff --git a/src/hct_mis_api/apps/targeting/models.py b/src/hct_mis_api/apps/targeting/models.py index fb41545f1b..18accfa09d 100644 --- a/src/hct_mis_api/apps/targeting/models.py +++ b/src/hct_mis_api/apps/targeting/models.py @@ -180,7 +180,7 @@ class TargetPopulation(SoftDeletableModel, TimeStampedUUIDModel, ConcurrencyMode on_delete=models.SET_NULL, ) program_cycle = models.ForeignKey( - "program.ProgramCycle", on_delete=models.CASCADE, related_name="target_populations", null=True, blank=True + "program.ProgramCycle", on_delete=models.CASCADE, related_name="target_populations" ) targeting_criteria = models.OneToOneField( "TargetingCriteria", From e434edd397fcf560958af858fb5056dc32efd339 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 04:55:57 +0200 Subject: [PATCH 053/202] linter --- src/hct_mis_api/apps/targeting/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hct_mis_api/apps/targeting/fixtures.py b/src/hct_mis_api/apps/targeting/fixtures.py index 2953abb0e4..57d4d62962 100644 --- a/src/hct_mis_api/apps/targeting/fixtures.py +++ b/src/hct_mis_api/apps/targeting/fixtures.py @@ -13,7 +13,7 @@ from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.household.fixtures import HouseholdFactory from hct_mis_api.apps.household.models import RESIDENCE_STATUS_CHOICE -from hct_mis_api.apps.program.fixtures import get_program_with_dct_type_and_name, ProgramCycleFactory +from hct_mis_api.apps.program.fixtures import ProgramCycleFactory, get_program_with_dct_type_and_name from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.targeting.models import ( HouseholdSelection, From b9ffb29e9ab71db539d3d14a69a127f0b7ec997b Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 06:05:42 +0200 Subject: [PATCH 054/202] fix tests creating TPs without cycles --- src/hct_mis_api/apps/targeting/fixtures.py | 5 +- .../test_pull_from_datahub.py | 1 + .../test_data_send_tp_to_datahub.py | 2 + ...t_external_collector_send_tp_to_datahub.py | 2 + .../mis_datahub/test_send_tp_to_datahub.py | 5 + .../payment/test_payment_plan_services.py | 18 -- .../test_copy_target_population_mutation.py | 14 +- ...tatus_change_target_population_mutation.py | 18 +- ...test_target_population_households_query.py | 4 + .../unit/apps/targeting/test_target_query.py | 6 + .../test_update_target_population_mutation.py | 5 +- .../test_program_cycle_data_migration.py | 275 ------------------ 12 files changed, 57 insertions(+), 298 deletions(-) delete mode 100644 tests/unit/one_time_scripts/test_program_cycle_data_migration.py diff --git a/src/hct_mis_api/apps/targeting/fixtures.py b/src/hct_mis_api/apps/targeting/fixtures.py index 57d4d62962..91a13cb578 100644 --- a/src/hct_mis_api/apps/targeting/fixtures.py +++ b/src/hct_mis_api/apps/targeting/fixtures.py @@ -13,7 +13,10 @@ from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.household.fixtures import HouseholdFactory from hct_mis_api.apps.household.models import RESIDENCE_STATUS_CHOICE -from hct_mis_api.apps.program.fixtures import ProgramCycleFactory, get_program_with_dct_type_and_name +from hct_mis_api.apps.program.fixtures import ( + ProgramCycleFactory, + get_program_with_dct_type_and_name, +) from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.targeting.models import ( HouseholdSelection, diff --git a/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py b/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py index f820e0301d..e954b4c0cf 100644 --- a/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py +++ b/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py @@ -75,6 +75,7 @@ def _setup_in_app_data(cls) -> None: status=TargetPopulation.STATUS_PROCESSING, program=cls.program, business_area=cls.business_area, + program_cycle=cls.program.cycles.first() ) program = ProgramFactory( diff --git a/tests/unit/apps/mis_datahub/test_data_send_tp_to_datahub.py b/tests/unit/apps/mis_datahub/test_data_send_tp_to_datahub.py index 88e310adc0..4d9e2e18e5 100644 --- a/tests/unit/apps/mis_datahub/test_data_send_tp_to_datahub.py +++ b/tests/unit/apps/mis_datahub/test_data_send_tp_to_datahub.py @@ -88,6 +88,7 @@ def setUpTestData(cls) -> None: ca_hash_id=uuid.uuid4(), ca_id="TEST", ) + cls.program_cycle = cls.program.cycles.first() rdi = RegistrationDataImportFactory(program=cls.program) cls.create_first_household(admin_area2, rdi) @@ -98,6 +99,7 @@ def setUpTestData(cls) -> None: program=cls.program, business_area=business_area_with_data_sharing, status=TargetPopulation.STATUS_PROCESSING, + program_cycle=cls.program_cycle, ) cls.target_population.households.set([cls.household]) cls.target_population: TargetPopulation = refresh_stats(cls.target_population) diff --git a/tests/unit/apps/mis_datahub/test_external_collector_send_tp_to_datahub.py b/tests/unit/apps/mis_datahub/test_external_collector_send_tp_to_datahub.py index 48d5705826..584f3d57d3 100644 --- a/tests/unit/apps/mis_datahub/test_external_collector_send_tp_to_datahub.py +++ b/tests/unit/apps/mis_datahub/test_external_collector_send_tp_to_datahub.py @@ -90,6 +90,7 @@ def setUpTestData(cls) -> None: program=cls.program_individual_data_needed_true, business_area=business_area_with_data_sharing, status=TargetPopulation.STATUS_PROCESSING, + program_cycle=cls.program_individual_data_needed_true.cycles.first(), ) cls.target_population_with_individuals.households.set([cls.household, cls.household_second]) cls.target_population_with_individuals = refresh_stats(cls.target_population_with_individuals) @@ -101,6 +102,7 @@ def setUpTestData(cls) -> None: program=cls.program_individual_data_needed_false, business_area=business_area_with_data_sharing, status=TargetPopulation.STATUS_PROCESSING, + program_cycle=cls.program_individual_data_needed_false.cycles.first(), ) cls.target_population_without_individuals.households.set([cls.household, cls.household_second]) cls.target_population_without_individuals = refresh_stats(cls.target_population_without_individuals) diff --git a/tests/unit/apps/mis_datahub/test_send_tp_to_datahub.py b/tests/unit/apps/mis_datahub/test_send_tp_to_datahub.py index b37c356fdb..66c2c96e9a 100644 --- a/tests/unit/apps/mis_datahub/test_send_tp_to_datahub.py +++ b/tests/unit/apps/mis_datahub/test_send_tp_to_datahub.py @@ -204,6 +204,7 @@ def setUpTestData(cls) -> None: program=cls.program_individual_data_needed_true, business_area=business_area_with_data_sharing, status=TargetPopulation.STATUS_PROCESSING, + program_cycle=cls.program_individual_data_needed_true.cycles.first(), ) cls.target_population_first.households.set([cls.household]) cls.target_population_first = refresh_stats(cls.target_population_first) @@ -215,6 +216,7 @@ def setUpTestData(cls) -> None: program=cls.program_individual_data_needed_false, business_area=business_area_with_data_sharing, status=TargetPopulation.STATUS_PROCESSING, + program_cycle=cls.program_individual_data_needed_false.cycles.first(), ) cls.target_population_second.households.set([cls.household]) cls.target_population_second = refresh_stats(cls.target_population_second) @@ -226,6 +228,7 @@ def setUpTestData(cls) -> None: program=cls.program_third, business_area=business_area_with_data_sharing, status=TargetPopulation.STATUS_PROCESSING, + program_cycle=cls.program_third.cycles.first(), ) cls.target_population_third.households.set([cls.household_second]) cls.target_population_third = refresh_stats(cls.target_population_third) @@ -292,6 +295,7 @@ def test_send_two_times_household_with_different(self) -> None: program=program_individual_data_needed_false, business_area=business_area_with_data_sharing, status=TargetPopulation.STATUS_PROCESSING, + program_cycle=program_individual_data_needed_false.cycles.first(), ) target_population_first.households.set([household]) target_population_first = refresh_stats(target_population_first) @@ -301,6 +305,7 @@ def test_send_two_times_household_with_different(self) -> None: program=program_individual_data_needed_true, business_area=business_area_with_data_sharing, status=TargetPopulation.STATUS_PROCESSING, + program_cycle=program_individual_data_needed_true.cycles.first(), ) target_population_second.households.set([household]) target_population_second = refresh_stats(target_population_second) diff --git a/tests/unit/apps/payment/test_payment_plan_services.py b/tests/unit/apps/payment/test_payment_plan_services.py index 4887a78472..e3dbbc32b6 100644 --- a/tests/unit/apps/payment/test_payment_plan_services.py +++ b/tests/unit/apps/payment/test_payment_plan_services.py @@ -600,21 +600,3 @@ def test_create_with_program_cycle_validation_error(self) -> None: PaymentPlanService.create(input_data=input_data, user=self.user) cycle.refresh_from_db() assert cycle.status == ProgramCycle.ACTIVE - - def test_create_pp_validation_errors_if_tp_without_cycle(self) -> None: - self.business_area.is_payment_plan_applicable = True - self.business_area.save() - targeting_without_cycle = TargetPopulationFactory( - program=ProgramFactory(), status=TargetPopulation.STATUS_READY_FOR_PAYMENT_MODULE - ) - input_data = dict( - business_area_slug=self.business_area.slug, - targeting_id=self.id_to_base64(targeting_without_cycle.id, "TargetingNode"), - dispersion_start_date=parse_date("2020-09-10"), - dispersion_end_date=parse_date("2020-09-11"), - currency="USD", - ) - - self.assertIsNone(targeting_without_cycle.program_cycle) - with self.assertRaisesMessage(GraphQLError, "Target Population should have assigned Programme Cycle"): - PaymentPlanService.create(input_data=input_data, user=self.user) 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 b2894ead74..f8c9638aac 100644 --- a/tests/unit/apps/targeting/test_copy_target_population_mutation.py +++ b/tests/unit/apps/targeting/test_copy_target_population_mutation.py @@ -76,7 +76,11 @@ def setUpTestData(cls) -> None: cls.household = household cls.update_partner_access_to_program(partner, cls.program) tp = TargetPopulation( - name="Original Target Population", status="LOCKED", business_area=cls.business_area, program=cls.program + name="Original Target Population", + status="LOCKED", + business_area=cls.business_area, + program=cls.program, + program_cycle=cls.cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -86,7 +90,11 @@ def setUpTestData(cls) -> None: tp.households.add(cls.household) cls.target_population = tp cls.empty_target_population_1 = TargetPopulation( - name="emptyTargetPopulation1", status="LOCKED", business_area=cls.business_area, program=cls.program + name="emptyTargetPopulation1", + status="LOCKED", + business_area=cls.business_area, + program=cls.program, + program_cycle=cls.cycle, ) cls.empty_target_population_1.save() @@ -95,6 +103,7 @@ def setUpTestData(cls) -> None: status="LOCKED", business_area=cls.business_area, program=cls.program, + program_cycle=cls.cycle, ) targeting_criteria_hh_ids = TargetingCriteria(household_ids=[cls.household.unicef_id]) targeting_criteria_hh_ids.save() @@ -106,6 +115,7 @@ def setUpTestData(cls) -> None: status="LOCKED", business_area=cls.business_area, program=cls.program, + program_cycle=cls.cycle, ) targeting_criteria_hh_ids = TargetingCriteria(individual_ids=[individual.unicef_id]) targeting_criteria_hh_ids.save() diff --git a/tests/unit/apps/targeting/test_status_change_target_population_mutation.py b/tests/unit/apps/targeting/test_status_change_target_population_mutation.py index c956353b29..fe3503b017 100644 --- a/tests/unit/apps/targeting/test_status_change_target_population_mutation.py +++ b/tests/unit/apps/targeting/test_status_change_target_population_mutation.py @@ -65,6 +65,7 @@ def setUpTestData(cls) -> None: cls.households.append(cls.household_size_1) cls.households.append(cls.household_size_2) cls.program = ProgramFactory(status=Program.ACTIVE, business_area=cls.business_area) + cls.program_cycle = cls.program.cycles.first() cls.update_partner_access_to_program(partner, cls.program) tp = TargetPopulation( @@ -72,6 +73,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_OPEN, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -86,6 +88,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -100,6 +103,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -183,6 +187,7 @@ def setUpTestData(cls) -> None: cls.households.append(cls.household_size_1) cls.households.append(cls.household_size_2) cls.program = ProgramFactory(status=Program.ACTIVE, business_area=cls.business_area) + cls.program_cycle = cls.program.cycles.first() cls.update_partner_access_to_program(partner, cls.program) tp = TargetPopulation( @@ -190,6 +195,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_OPEN, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -203,6 +209,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -217,6 +224,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -313,6 +321,7 @@ def setUpTestData(cls) -> None: cls.households.append(cls.household_size_1) cls.households.append(cls.household_size_2) cls.program = ProgramFactory(status=Program.ACTIVE, business_area=cls.business_area) + cls.program_cycle = cls.program.cycles.first() cls.update_partner_access_to_program(partner, cls.program) tp = TargetPopulation( @@ -320,6 +329,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_OPEN, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -332,6 +342,8 @@ def setUpTestData(cls) -> None: name="Approved Target Population with final filters", status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, + program=cls.program, + program_cycle=cls.program_cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -344,7 +356,11 @@ def setUpTestData(cls) -> None: cls.target_population_approved_with_final_rule = tp tp = TargetPopulation( - name="Approved Target Population", status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area + name="Approved Target Population", + status=TargetPopulation.STATUS_LOCKED, + business_area=cls.business_area, + program=cls.program, + program_cycle=cls.program_cycle, ) tp.targeting_criteria = cls.get_targeting_criteria_for_rule( diff --git a/tests/unit/apps/targeting/test_target_population_households_query.py b/tests/unit/apps/targeting/test_target_population_households_query.py index 16d9fd7add..c2115e4b1c 100644 --- a/tests/unit/apps/targeting/test_target_population_households_query.py +++ b/tests/unit/apps/targeting/test_target_population_households_query.py @@ -66,6 +66,7 @@ def setUpTestData(cls) -> None: cls.partner = PartnerFactory(name="TestPartner") cls.user = UserFactory(partner=cls.partner) cls.program = get_program_with_dct_type_and_name() + cls.program_cycle = cls.program.cycles.first() targeting_criteria = cls.get_targeting_criteria_for_rule( {"field_name": "size", "arguments": [2], "comparison_method": "EQUALS"} ) @@ -75,6 +76,7 @@ def setUpTestData(cls) -> None: targeting_criteria=targeting_criteria, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) cls.target_population_size_2.save() targeting_criteria = cls.get_targeting_criteria_for_rule( @@ -86,6 +88,7 @@ def setUpTestData(cls) -> None: targeting_criteria=targeting_criteria, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) cls.target_population_residence_status.save() @@ -99,6 +102,7 @@ def setUpTestData(cls) -> None: status="LOCKED", business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) cls.target_population_size_1_approved.save() HouseholdSelection.objects.create( diff --git a/tests/unit/apps/targeting/test_target_query.py b/tests/unit/apps/targeting/test_target_query.py index 32d6fde118..2204eefbc2 100644 --- a/tests/unit/apps/targeting/test_target_query.py +++ b/tests/unit/apps/targeting/test_target_query.py @@ -145,6 +145,7 @@ def setUpTestData(cls) -> None: targeting_criteria=targeting_criteria, business_area=cls.business_area, program=cls.program, + program_cycle=cls.cycle_2, ) cls.target_population_size_2.save() cls.target_population_size_2 = full_rebuild(cls.target_population_size_2) @@ -174,6 +175,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + program_cycle=cls.cycle_2, ) cls.target_population_size_1_approved.save() cls.target_population_size_1_approved = full_rebuild(cls.target_population_size_1_approved) @@ -220,6 +222,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + program_cycle=cls.cycle_2, ) cls.target_population_with_pdu_filter.save() cls.target_population_with_pdu_filter = full_rebuild(cls.target_population_with_pdu_filter) @@ -254,6 +257,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + program_cycle=cls.cycle_2, ) cls.target_population_with_individual_filter.save() cls.target_population_with_individual_filter = full_rebuild(cls.target_population_with_individual_filter) @@ -266,6 +270,7 @@ def setUpTestData(cls) -> None: business_area=cls.business_area, data_collecting_type__type=DataCollectingType.Type.SOCIAL, ) + cls.cycle_sw = cls.program_sw.cycles.first() pdu_data_string_sw = PeriodicFieldDataFactory( subtype=PeriodicFieldData.STRING, number_of_rounds=2, @@ -292,6 +297,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program_sw, + program_cycle=cls.cycle_sw, ) cls.target_population_with_pdu_filter_for_sw.save() cls.target_population_with_pdu_filter_for_sw = full_rebuild(cls.target_population_with_pdu_filter_for_sw) diff --git a/tests/unit/apps/targeting/test_update_target_population_mutation.py b/tests/unit/apps/targeting/test_update_target_population_mutation.py index 6b6571ee34..6620da653d 100644 --- a/tests/unit/apps/targeting/test_update_target_population_mutation.py +++ b/tests/unit/apps/targeting/test_update_target_population_mutation.py @@ -168,6 +168,7 @@ def setUpTestData(cls) -> None: create_household({"size": 3, "residence_status": "HOST", "business_area": cls.business_area}) create_household({"size": 3, "residence_status": "HOST", "business_area": cls.business_area}) cls.program = ProgramFactory(status=Program.ACTIVE, business_area=cls.business_area) + cls.program_cycle = cls.program.cycles.first() cls.update_partner_access_to_program(partner, cls.program) cls.draft_target_population = TargetPopulation( name="draft_target_population", @@ -177,6 +178,7 @@ def setUpTestData(cls) -> None: created_by=cls.user, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) cls.draft_target_population.save() cls.approved_target_population = TargetPopulation( @@ -188,11 +190,11 @@ def setUpTestData(cls) -> None: created_by=cls.user, business_area=cls.business_area, program=cls.program, + program_cycle=cls.program_cycle, ) cls.approved_target_population.save() cls.approved_target_population.households.set(Household.objects.all()) cls.target_populations = [cls.draft_target_population, cls.approved_target_population] - cls.program_cycle = cls.program.cycles.first() @staticmethod def get_targeting_criteria_for_rule(rule_filter: Dict) -> TargetingCriteria: @@ -292,6 +294,7 @@ def test_fail_update_for_incorrect_status(self) -> None: business_area=self.business_area, program=self.program, status=TargetPopulation.STATUS_PROCESSING, + program_cycle=self.program_cycle, ) target_population_with_incorrect_status.save() diff --git a/tests/unit/one_time_scripts/test_program_cycle_data_migration.py b/tests/unit/one_time_scripts/test_program_cycle_data_migration.py deleted file mode 100644 index 0949b58fc6..0000000000 --- a/tests/unit/one_time_scripts/test_program_cycle_data_migration.py +++ /dev/null @@ -1,275 +0,0 @@ -from django.test.testcases 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.payment.fixtures import PaymentFactory, PaymentPlanFactory -from hct_mis_api.apps.payment.models import PaymentPlan -from hct_mis_api.apps.program.fixtures import ProgramCycleFactory, ProgramFactory -from hct_mis_api.apps.program.models import Program, ProgramCycle -from hct_mis_api.apps.targeting.fixtures import TargetPopulationFactory -from hct_mis_api.apps.targeting.models import TargetPopulation -from hct_mis_api.one_time_scripts.program_cycle_data_migration import ( - program_cycle_data_migration, -) - - -class TestProgramCycleDataMigration(TestCase): - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - ba = create_afghanistan() - start_date = "2022-10-10" - end_date = "2023-10-10" - program_finished = ProgramFactory( - name="Finished 001", - business_area=ba, - start_date="2022-10-10", - end_date="2022-10-29", - status=Program.FINISHED, - cycle__title="Already Created Cycle for program_finished", - cycle__status=ProgramCycle.DRAFT, - cycle__start_date="2022-10-11", - cycle__end_date="2022-10-12", - ) - program_finished2 = ProgramFactory( - name="Finished 002", - business_area=ba, - start_date="2022-11-10", - end_date="2022-11-30", - status=Program.FINISHED, - cycle__title="Cycle_program_finished2", - cycle__status=ProgramCycle.DRAFT, - cycle__start_date="2022-10-11", - cycle__end_date="2022-10-12", - ) - # remove default program cycle for program_finished2 - ProgramCycle.objects.filter(title="Cycle_program_finished2").delete() - tp_1 = TargetPopulationFactory(program=program_finished, program_cycle=None) - tp_2 = TargetPopulationFactory(program=program_finished2, program_cycle=None) - PaymentPlanFactory( - program=program_finished, - target_population=tp_1, - program_cycle=None, - start_date=start_date, - end_date=end_date, - ) - PaymentPlanFactory( - program=program_finished2, - target_population=tp_2, - program_cycle=None, - start_date=start_date, - end_date=end_date, - ) - - # active programs - program_active_001 = ProgramFactory( - name="Active 001", - business_area=ba, - start_date="2023-01-01", - end_date="2022-01-30", - status=Program.ACTIVE, - cycle__title="Cycle for program_active_001", - cycle__status=ProgramCycle.DRAFT, - cycle__start_date="2023-01-01", - cycle__end_date="2023-01-30", - ) - program_active_002 = ProgramFactory( - name="Active 002", - business_area=ba, - start_date="2023-02-01", - end_date="2023-02-25", - status=Program.ACTIVE, - cycle__title="Cycle for program_active_002", - cycle__status=ProgramCycle.DRAFT, - cycle__start_date="2023-02-01", - cycle__end_date="2023-02-25", - ) - ProgramCycle.objects.filter(title="Cycle for program_active_002").delete() - cls.tp_3 = TargetPopulationFactory(program=program_active_001, program_cycle=None) - cls.tp_4 = TargetPopulationFactory(program=program_active_002, program_cycle=None) - ProgramCycleFactory( - program=program_active_002, - title="Cycle 01", - start_date="2023-02-10", - end_date=None, - ) - household_1, inds = create_household_and_individuals( - household_data={ - "business_area": ba, - "program": program_finished, - }, - individuals_data=[ - { - "business_area": ba, - "program": program_finished, - }, - ], - ) - household_2, inds = create_household_and_individuals( - household_data={ - "business_area": ba, - "program": program_finished, - }, - individuals_data=[ - { - "business_area": ba, - "program": program_finished, - }, - ], - ) - household_3, inds = create_household_and_individuals( - household_data={ - "business_area": ba, - "program": program_finished, - }, - individuals_data=[ - { - "business_area": ba, - "program": program_finished, - }, - ], - ) - household_4, inds = create_household_and_individuals( - household_data={ - "business_area": ba, - "program": program_finished, - }, - individuals_data=[ - { - "business_area": ba, - "program": program_finished, - }, - ], - ) - household_5, inds = create_household_and_individuals( - household_data={ - "business_area": ba, - "program": program_finished, - }, - individuals_data=[ - { - "business_area": ba, - "program": program_finished, - }, - ], - ) - household_6, inds = create_household_and_individuals( - household_data={ - "business_area": ba, - "program": program_finished, - }, - individuals_data=[ - { - "business_area": ba, - "program": program_finished, - }, - ], - ) - - cls.pp_1 = PaymentPlanFactory( - name="Payment Plan pp1", - program=program_active_001, - target_population=cls.tp_3, - program_cycle=None, - start_date=start_date, - end_date=end_date, - ) - cls.pp_2 = PaymentPlanFactory( - name="Payment Plan pp2", - program=program_active_001, - target_population=cls.tp_3, - program_cycle=None, - start_date=start_date, - end_date=end_date, - ) - PaymentFactory(household=household_1, parent=cls.pp_1, status="Distribution Successful") - PaymentFactory(household=household_2, parent=cls.pp_2, status="Distribution Successful") - - cls.pp_3 = PaymentPlanFactory( - name="Payment Plan pp3", - program=program_active_002, - target_population=cls.tp_4, - program_cycle=None, - start_date=start_date, - end_date=end_date, - ) - cls.pp_4 = PaymentPlanFactory( - name="Payment Plan pp4", - program=program_active_002, - target_population=cls.tp_4, - program_cycle=None, - start_date=start_date, - end_date=end_date, - ) - cls.pp_5 = PaymentPlanFactory( - name="Payment Plan pp5", - program=program_active_002, - target_population=cls.tp_4, - program_cycle=None, - start_date=start_date, - end_date=end_date, - ) - - # cycle 1 = Cycle 01 - PaymentFactory(household=household_3, parent=cls.pp_3, status="Distribution Successful") - PaymentFactory(household=household_4, parent=cls.pp_3, status="Distribution Successful") - - # cycle 2 = new created - PaymentFactory(household=household_4, parent=cls.pp_4, status="Distribution Successful") - PaymentFactory(household=household_5, parent=cls.pp_4, status="Distribution Successful") - - # cycle 3 = new created - PaymentFactory(household=household_5, parent=cls.pp_5, status="Distribution Successful") - PaymentFactory(household=household_6, parent=cls.pp_5, status="Distribution Successful") - PaymentFactory(household=household_3, parent=cls.pp_5, status="Distribution Successful") - - def test_program_cycle_data_migration(self) -> None: - # check cycle for program_active_002 - self.assertEqual(ProgramCycle.objects.filter(program=self.pp_3.program).count(), 1) - self.assertEqual(ProgramCycle.objects.filter(program=self.pp_3.program).first().title, "Cycle 01") - - # run script - program_cycle_data_migration() - - program_finished = Program.objects.get(name="Finished 001") - program_finished2 = Program.objects.get(name="Finished 002") - cycle_for_program_finished = program_finished.cycles.first() - self.assertEqual(program_finished.start_date, cycle_for_program_finished.start_date) - self.assertEqual(program_finished.end_date, cycle_for_program_finished.end_date) - self.assertEqual(cycle_for_program_finished.status, ProgramCycle.FINISHED) - self.assertEqual(TargetPopulation.objects.filter(program_cycle=cycle_for_program_finished).count(), 1) - self.assertEqual(PaymentPlan.objects.filter(program_cycle=cycle_for_program_finished).count(), 1) - - cycle_for_program_finished2 = program_finished2.cycles.first() - self.assertEqual(program_finished2.start_date, cycle_for_program_finished2.start_date) - self.assertEqual(program_finished2.end_date, cycle_for_program_finished2.end_date) - self.assertEqual(cycle_for_program_finished2.status, ProgramCycle.FINISHED) - self.assertEqual(TargetPopulation.objects.filter(program_cycle=cycle_for_program_finished2).count(), 1) - self.assertEqual(PaymentPlan.objects.filter(program_cycle=cycle_for_program_finished2).count(), 1) - - # check with active program - self.pp_1.refresh_from_db() - self.pp_2.refresh_from_db() - self.tp_3.refresh_from_db() - - self.assertEqual(self.pp_1.program_cycle.status, ProgramCycle.ACTIVE) - # new default name starts with "Cycle {PaymentPlan.start_date} ({random 4 digits})" - self.assertTrue(self.pp_1.program_cycle.title.startswith("Cycle 2022-10-10 (")) - self.assertTrue(self.tp_3.program_cycle.title.startswith("Cycle 2022-10-10 (")) - - self.pp_3.refresh_from_db() - self.pp_4.refresh_from_db() - self.pp_5.refresh_from_db() - self.tp_4.refresh_from_db() - - self.assertEqual(self.pp_3.program_cycle.status, ProgramCycle.ACTIVE) - self.assertTrue(self.pp_3.program_cycle.title.startswith("Cycle 2022-10-10 (")) - self.assertEqual(self.pp_4.program_cycle.status, ProgramCycle.ACTIVE) - self.assertEqual(self.pp_5.program_cycle.status, ProgramCycle.ACTIVE) - self.assertEqual(self.tp_4.program_cycle.status, ProgramCycle.ACTIVE) - - program_active_002 = self.pp_3.program - values_cycles = ProgramCycle.objects.filter(program=program_active_002).values( - "title", "start_date", "end_date" - ) - self.assertEqual(values_cycles.count(), 3) From 372b465b95c3cf96d4bee3c3dddf1340f7596977 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 06:14:24 +0200 Subject: [PATCH 055/202] skip forms --- tests/.coveragerc | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/.coveragerc b/tests/.coveragerc index 8e5e354932..96ca8cc7c1 100644 --- a/tests/.coveragerc +++ b/tests/.coveragerc @@ -11,6 +11,7 @@ omit = */admin/*.py */admin.py **/fixtures.py + **/forms.py hct_mis_api/one_time_scripts/* hct_mis_api/libs/* hct_mis_api/settings/* From 63dd69b4dfd59e889a1175f57a08f75f36e0f8ac Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Thu, 3 Oct 2024 10:01:39 +0200 Subject: [PATCH 056/202] fixed test --- .../programme_population/test_periodic_data_templates.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/selenium/programme_population/test_periodic_data_templates.py b/tests/selenium/programme_population/test_periodic_data_templates.py index e2c3415964..a435a34dac 100644 --- a/tests/selenium/programme_population/test_periodic_data_templates.py +++ b/tests/selenium/programme_population/test_periodic_data_templates.py @@ -292,10 +292,9 @@ def test_periodic_data_template_create_and_download( pageIndividuals.getDownloadBtn(periodic_data_update_template.pk).click() periodic_data_update_template.refresh_from_db() - user_path = os.path.expanduser("~") assert ( pageIndividuals.check_file_exists( - os.path.join(user_path, "Downloads", periodic_data_update_template.file.file.name) + os.path.join(settings.DOWNLOAD_DIRECTORY, periodic_data_update_template.file.file.name) ) is True ) From 01679655753a5ebd021aaade01d94073cbe704b9 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 10:19:10 +0200 Subject: [PATCH 057/202] bring back nighlt runs for tests marked before --- .../selenium/programme_management/test_programme_management.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index dc39300971..fb79de1f6d 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -395,6 +395,7 @@ def test_create_programme_cancel_scenario( # ToDo: Check Unicef partner! and delete classes +@pytest.mark.night @pytest.mark.usefixtures("login") class TestBusinessAreas: @pytest.mark.parametrize( @@ -522,6 +523,7 @@ def test_copy_programme( assert "New Programme" in pageProgrammeDetails.getHeaderTitle().text +@pytest.mark.night @pytest.mark.usefixtures("login") class TestAdminAreas: @pytest.mark.parametrize( @@ -659,6 +661,7 @@ def test_create_programme_back_scenarios( assert "UNHCR" in pageProgrammeDetails.getLabelPartnerName().text +@pytest.mark.night @pytest.mark.usefixtures("login") class TestManualCalendar: @pytest.mark.parametrize( From fd877368bfcad24b0cc06cfacb39e85add99d50d Mon Sep 17 00:00:00 2001 From: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:24:40 +0200 Subject: [PATCH 058/202] 2216761_Adapt_Dedup_integration (#4280) * 2216761_Adapt_Dedup_integration * add migration --------- Co-authored-by: marekbiczysko <marek.biczysko@tivix.com> --- .../apps/account/migrations/0078_migration.py | 19 ++ .../needs_adjudication_ticket_services.py | 6 +- src/hct_mis_api/apps/program/schema.py | 6 +- .../migrations/0040_migration.py | 18 + .../apps/registration_data/models.py | 4 +- .../apis/deduplication_engine.py | 15 +- .../services/biometric_deduplication.py | 48 ++- .../apps/grievance/test_services_utils.py | 106 ++---- .../test_biometric_deduplication_service.py | 308 +++++------------- .../test_deduplication_engine_api.py | 10 +- 10 files changed, 209 insertions(+), 331 deletions(-) create mode 100644 src/hct_mis_api/apps/account/migrations/0078_migration.py create mode 100644 src/hct_mis_api/apps/registration_data/migrations/0040_migration.py diff --git a/src/hct_mis_api/apps/account/migrations/0078_migration.py b/src/hct_mis_api/apps/account/migrations/0078_migration.py new file mode 100644 index 0000000000..c901dc6148 --- /dev/null +++ b/src/hct_mis_api/apps/account/migrations/0078_migration.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.25 on 2024-10-02 13:50 + +from django.db import migrations, models +import hct_mis_api.apps.account.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0077_migration'), + ] + + operations = [ + migrations.AlterField( + model_name='role', + name='permissions', + field=hct_mis_api.apps.account.fields.ChoiceArrayField(base_field=models.CharField(choices=[('RDI_VIEW_LIST', 'RDI VIEW LIST'), ('RDI_VIEW_DETAILS', 'RDI VIEW DETAILS'), ('RDI_IMPORT_DATA', 'RDI IMPORT DATA'), ('RDI_RERUN_DEDUPE', 'RDI RERUN DEDUPE'), ('RDI_MERGE_IMPORT', 'RDI MERGE IMPORT'), ('RDI_REFUSE_IMPORT', 'RDI REFUSE IMPORT'), ('POPULATION_VIEW_HOUSEHOLDS_LIST', 'POPULATION VIEW HOUSEHOLDS LIST'), ('POPULATION_VIEW_HOUSEHOLDS_DETAILS', 'POPULATION VIEW HOUSEHOLDS DETAILS'), ('POPULATION_VIEW_INDIVIDUALS_LIST', 'POPULATION VIEW INDIVIDUALS LIST'), ('POPULATION_VIEW_INDIVIDUALS_DETAILS', 'POPULATION VIEW INDIVIDUALS DETAILS'), ('POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION', 'POPULATION VIEW INDIVIDUAL DELIVERY MECHANISMS SECTION'), ('PROGRAMME_VIEW_LIST_AND_DETAILS', 'PROGRAMME VIEW LIST AND DETAILS'), ('PROGRAMME_MANAGEMENT_VIEW', 'PROGRAMME MANAGEMENT VIEW'), ('PROGRAMME_VIEW_PAYMENT_RECORD_DETAILS', 'PROGRAMME VIEW PAYMENT RECORD DETAILS'), ('PROGRAMME_CREATE', 'PROGRAMME CREATE'), ('PROGRAMME_UPDATE', 'PROGRAMME UPDATE'), ('PROGRAMME_REMOVE', 'PROGRAMME REMOVE'), ('PROGRAMME_ACTIVATE', 'PROGRAMME ACTIVATE'), ('PROGRAMME_FINISH', 'PROGRAMME FINISH'), ('PROGRAMME_DUPLICATE', 'PROGRAMME DUPLICATE'), ('TARGETING_VIEW_LIST', 'TARGETING VIEW LIST'), ('TARGETING_VIEW_DETAILS', 'TARGETING VIEW DETAILS'), ('TARGETING_CREATE', 'TARGETING CREATE'), ('TARGETING_UPDATE', 'TARGETING UPDATE'), ('TARGETING_DUPLICATE', 'TARGETING DUPLICATE'), ('TARGETING_REMOVE', 'TARGETING REMOVE'), ('TARGETING_LOCK', 'TARGETING LOCK'), ('TARGETING_UNLOCK', 'TARGETING UNLOCK'), ('TARGETING_SEND', 'TARGETING SEND'), ('PAYMENT_VIEW_LIST_MANAGERIAL', 'PAYMENT VIEW LIST MANAGERIAL'), ('PAYMENT_VIEW_LIST_MANAGERIAL_RELEASED', 'PAYMENT VIEW LIST MANAGERIAL RELEASED'), ('PAYMENT_VERIFICATION_VIEW_LIST', 'PAYMENT VERIFICATION VIEW LIST'), ('PAYMENT_VERIFICATION_VIEW_DETAILS', 'PAYMENT VERIFICATION VIEW DETAILS'), ('PAYMENT_VERIFICATION_CREATE', 'PAYMENT VERIFICATION CREATE'), ('PAYMENT_VERIFICATION_UPDATE', 'PAYMENT VERIFICATION UPDATE'), ('PAYMENT_VERIFICATION_ACTIVATE', 'PAYMENT VERIFICATION ACTIVATE'), ('PAYMENT_VERIFICATION_DISCARD', 'PAYMENT VERIFICATION DISCARD'), ('PAYMENT_VERIFICATION_FINISH', 'PAYMENT VERIFICATION FINISH'), ('PAYMENT_VERIFICATION_EXPORT', 'PAYMENT VERIFICATION EXPORT'), ('PAYMENT_VERIFICATION_IMPORT', 'PAYMENT VERIFICATION IMPORT'), ('PAYMENT_VERIFICATION_VERIFY', 'PAYMENT VERIFICATION VERIFY'), ('PAYMENT_VERIFICATION_VIEW_PAYMENT_RECORD_DETAILS', 'PAYMENT VERIFICATION VIEW PAYMENT RECORD DETAILS'), ('PAYMENT_VERIFICATION_DELETE', 'PAYMENT VERIFICATION DELETE'), ('PAYMENT_VERIFICATION_INVALID', 'PAYMENT VERIFICATION INVALID'), ('PAYMENT_VERIFICATION_MARK_AS_FAILED', 'PAYMENT VERIFICATION MARK AS FAILED'), ('PM_VIEW_LIST', 'PM VIEW LIST'), ('PM_CREATE', 'PM CREATE'), ('PM_VIEW_DETAILS', 'PM VIEW DETAILS'), ('PM_IMPORT_XLSX_WITH_ENTITLEMENTS', 'PM IMPORT XLSX WITH ENTITLEMENTS'), ('PM_APPLY_RULE_ENGINE_FORMULA_WITH_ENTITLEMENTS', 'PM APPLY RULE ENGINE FORMULA WITH ENTITLEMENTS'), ('PM_SPLIT', 'PM SPLIT'), ('PM_LOCK_AND_UNLOCK', 'PM LOCK AND UNLOCK'), ('PM_LOCK_AND_UNLOCK_FSP', 'PM LOCK AND UNLOCK FSP'), ('PM_SEND_FOR_APPROVAL', 'PM SEND FOR APPROVAL'), ('PM_EXCLUDE_BENEFICIARIES_FROM_FOLLOW_UP_PP', 'PM EXCLUDE BENEFICIARIES FROM FOLLOW UP PP'), ('PM_ACCEPTANCE_PROCESS_APPROVE', 'PM ACCEPTANCE PROCESS APPROVE'), ('PM_ACCEPTANCE_PROCESS_AUTHORIZE', 'PM ACCEPTANCE PROCESS AUTHORIZE'), ('PM_ACCEPTANCE_PROCESS_FINANCIAL_REVIEW', 'PM ACCEPTANCE PROCESS FINANCIAL REVIEW'), ('PM_IMPORT_XLSX_WITH_RECONCILIATION', 'PM IMPORT XLSX WITH RECONCILIATION'), ('PM_EXPORT_XLSX_FOR_FSP', 'PM EXPORT XLSX FOR FSP'), ('PM_DOWNLOAD_XLSX_FOR_FSP', 'PM DOWNLOAD XLSX FOR FSP'), ('PM_MARK_PAYMENT_AS_FAILED', 'PM MARK PAYMENT AS FAILED'), ('PM_EXPORT_PDF_SUMMARY', 'PM EXPORT PDF SUMMARY'), ('PM_SEND_TO_PAYMENT_GATEWAY', 'PM SEND TO PAYMENT GATEWAY'), ('PM_VIEW_FSP_AUTH_CODE', 'PM VIEW FSP AUTH CODE'), ('PM_ADMIN_FINANCIAL_SERVICE_PROVIDER_UPDATE', 'PM ADMIN FINANCIAL SERVICE PROVIDER UPDATE'), ('PM_PROGRAMME_CYCLE_VIEW_LIST', 'PM PROGRAMME CYCLE VIEW LIST'), ('PM_PROGRAMME_CYCLE_VIEW_DETAILS', 'PM PROGRAMME CYCLE VIEW DETAILS'), ('PM_PROGRAMME_CYCLE_CREATE', 'PM PROGRAMME CYCLE CREATE'), ('PM_PROGRAMME_CYCLE_UPDATE', 'PM PROGRAMME CYCLE UPDATE'), ('PM_PROGRAMME_CYCLE_DELETE', 'PM PROGRAMME CYCLE DELETE'), ('USER_MANAGEMENT_VIEW_LIST', 'USER MANAGEMENT VIEW LIST'), ('DASHBOARD_VIEW_COUNTRY', 'DASHBOARD VIEW COUNTRY'), ('DASHBOARD_EXPORT', 'DASHBOARD EXPORT'), ('GRIEVANCES_VIEW_LIST_EXCLUDING_SENSITIVE', 'GRIEVANCES VIEW LIST EXCLUDING SENSITIVE'), ('GRIEVANCES_VIEW_LIST_EXCLUDING_SENSITIVE_AS_CREATOR', 'GRIEVANCES VIEW LIST EXCLUDING SENSITIVE AS CREATOR'), ('GRIEVANCES_VIEW_LIST_EXCLUDING_SENSITIVE_AS_OWNER', 'GRIEVANCES VIEW LIST EXCLUDING SENSITIVE AS OWNER'), ('GRIEVANCES_VIEW_LIST_SENSITIVE', 'GRIEVANCES VIEW LIST SENSITIVE'), ('GRIEVANCES_VIEW_LIST_SENSITIVE_AS_CREATOR', 'GRIEVANCES VIEW LIST SENSITIVE AS CREATOR'), ('GRIEVANCES_VIEW_LIST_SENSITIVE_AS_OWNER', 'GRIEVANCES VIEW LIST SENSITIVE AS OWNER'), ('GRIEVANCES_VIEW_DETAILS_EXCLUDING_SENSITIVE', 'GRIEVANCES VIEW DETAILS EXCLUDING SENSITIVE'), ('GRIEVANCES_VIEW_DETAILS_EXCLUDING_SENSITIVE_AS_CREATOR', 'GRIEVANCES VIEW DETAILS EXCLUDING SENSITIVE AS CREATOR'), ('GRIEVANCES_VIEW_DETAILS_EXCLUDING_SENSITIVE_AS_OWNER', 'GRIEVANCES VIEW DETAILS EXCLUDING SENSITIVE AS OWNER'), ('GRIEVANCES_VIEW_DETAILS_SENSITIVE', 'GRIEVANCES VIEW DETAILS SENSITIVE'), ('GRIEVANCES_VIEW_DETAILS_SENSITIVE_AS_CREATOR', 'GRIEVANCES VIEW DETAILS SENSITIVE AS CREATOR'), ('GRIEVANCES_VIEW_DETAILS_SENSITIVE_AS_OWNER', 'GRIEVANCES VIEW DETAILS SENSITIVE AS OWNER'), ('GRIEVANCES_VIEW_HOUSEHOLD_DETAILS', 'GRIEVANCES VIEW HOUSEHOLD DETAILS'), ('GRIEVANCES_VIEW_HOUSEHOLD_DETAILS_AS_CREATOR', 'GRIEVANCES VIEW HOUSEHOLD DETAILS AS CREATOR'), ('GRIEVANCES_VIEW_HOUSEHOLD_DETAILS_AS_OWNER', 'GRIEVANCES VIEW HOUSEHOLD DETAILS AS OWNER'), ('GRIEVANCES_VIEW_INDIVIDUALS_DETAILS', 'GRIEVANCES VIEW INDIVIDUALS DETAILS'), ('GRIEVANCES_VIEW_INDIVIDUALS_DETAILS_AS_CREATOR', 'GRIEVANCES VIEW INDIVIDUALS DETAILS AS CREATOR'), ('GRIEVANCES_VIEW_INDIVIDUALS_DETAILS_AS_OWNER', 'GRIEVANCES VIEW INDIVIDUALS DETAILS AS OWNER'), ('GRIEVANCES_CREATE', 'GRIEVANCES CREATE'), ('GRIEVANCES_UPDATE', 'GRIEVANCES UPDATE'), ('GRIEVANCES_UPDATE_AS_CREATOR', 'GRIEVANCES UPDATE AS CREATOR'), ('GRIEVANCES_UPDATE_AS_OWNER', 'GRIEVANCES UPDATE AS OWNER'), ('GRIEVANCES_UPDATE_REQUESTED_DATA_CHANGE', 'GRIEVANCES UPDATE REQUESTED DATA CHANGE'), ('GRIEVANCES_UPDATE_REQUESTED_DATA_CHANGE_AS_CREATOR', 'GRIEVANCES UPDATE REQUESTED DATA CHANGE AS CREATOR'), ('GRIEVANCES_UPDATE_REQUESTED_DATA_CHANGE_AS_OWNER', 'GRIEVANCES UPDATE REQUESTED DATA CHANGE AS OWNER'), ('GRIEVANCES_ADD_NOTE', 'GRIEVANCES ADD NOTE'), ('GRIEVANCES_ADD_NOTE_AS_CREATOR', 'GRIEVANCES ADD NOTE AS CREATOR'), ('GRIEVANCES_ADD_NOTE_AS_OWNER', 'GRIEVANCES ADD NOTE AS OWNER'), ('GRIEVANCES_SET_IN_PROGRESS', 'GRIEVANCES SET IN PROGRESS'), ('GRIEVANCES_SET_IN_PROGRESS_AS_CREATOR', 'GRIEVANCES SET IN PROGRESS AS CREATOR'), ('GRIEVANCES_SET_IN_PROGRESS_AS_OWNER', 'GRIEVANCES SET IN PROGRESS AS OWNER'), ('GRIEVANCES_SET_ON_HOLD', 'GRIEVANCES SET ON HOLD'), ('GRIEVANCES_SET_ON_HOLD_AS_CREATOR', 'GRIEVANCES SET ON HOLD AS CREATOR'), ('GRIEVANCES_SET_ON_HOLD_AS_OWNER', 'GRIEVANCES SET ON HOLD AS OWNER'), ('GRIEVANCES_SEND_FOR_APPROVAL', 'GRIEVANCES SEND FOR APPROVAL'), ('GRIEVANCES_SEND_FOR_APPROVAL_AS_CREATOR', 'GRIEVANCES SEND FOR APPROVAL AS CREATOR'), ('GRIEVANCES_SEND_FOR_APPROVAL_AS_OWNER', 'GRIEVANCES SEND FOR APPROVAL AS OWNER'), ('GRIEVANCES_SEND_BACK', 'GRIEVANCES SEND BACK'), ('GRIEVANCES_SEND_BACK_AS_CREATOR', 'GRIEVANCES SEND BACK AS CREATOR'), ('GRIEVANCES_SEND_BACK_AS_OWNER', 'GRIEVANCES SEND BACK AS OWNER'), ('GRIEVANCES_APPROVE_DATA_CHANGE', 'GRIEVANCES APPROVE DATA CHANGE'), ('GRIEVANCES_APPROVE_DATA_CHANGE_AS_CREATOR', 'GRIEVANCES APPROVE DATA CHANGE AS CREATOR'), ('GRIEVANCES_APPROVE_DATA_CHANGE_AS_OWNER', 'GRIEVANCES APPROVE DATA CHANGE AS OWNER'), ('GRIEVANCES_CLOSE_TICKET_EXCLUDING_FEEDBACK', 'GRIEVANCES CLOSE TICKET EXCLUDING FEEDBACK'), ('GRIEVANCES_CLOSE_TICKET_EXCLUDING_FEEDBACK_AS_CREATOR', 'GRIEVANCES CLOSE TICKET EXCLUDING FEEDBACK AS CREATOR'), ('GRIEVANCES_CLOSE_TICKET_EXCLUDING_FEEDBACK_AS_OWNER', 'GRIEVANCES CLOSE TICKET EXCLUDING FEEDBACK AS OWNER'), ('GRIEVANCES_CLOSE_TICKET_FEEDBACK', 'GRIEVANCES CLOSE TICKET FEEDBACK'), ('GRIEVANCES_CLOSE_TICKET_FEEDBACK_AS_CREATOR', 'GRIEVANCES CLOSE TICKET FEEDBACK AS CREATOR'), ('GRIEVANCES_CLOSE_TICKET_FEEDBACK_AS_OWNER', 'GRIEVANCES CLOSE TICKET FEEDBACK AS OWNER'), ('GRIEVANCES_APPROVE_FLAG_AND_DEDUPE', 'GRIEVANCES APPROVE FLAG AND DEDUPE'), ('GRIEVANCES_APPROVE_FLAG_AND_DEDUPE_AS_CREATOR', 'GRIEVANCES APPROVE FLAG AND DEDUPE AS CREATOR'), ('GRIEVANCES_APPROVE_FLAG_AND_DEDUPE_AS_OWNER', 'GRIEVANCES APPROVE FLAG AND DEDUPE AS OWNER'), ('GRIEVANCES_APPROVE_PAYMENT_VERIFICATION', 'GRIEVANCES APPROVE PAYMENT VERIFICATION'), ('GRIEVANCES_APPROVE_PAYMENT_VERIFICATION_AS_CREATOR', 'GRIEVANCES APPROVE PAYMENT VERIFICATION AS CREATOR'), ('GRIEVANCES_APPROVE_PAYMENT_VERIFICATION_AS_OWNER', 'GRIEVANCES APPROVE PAYMENT VERIFICATION AS OWNER'), ('GRIEVANCE_ASSIGN', 'GRIEVANCE ASSIGN'), ('GRIEVANCE_DOCUMENTS_UPLOAD', 'GRIEVANCE DOCUMENTS UPLOAD'), ('GRIEVANCES_CROSS_AREA_FILTER', 'GRIEVANCES CROSS AREA FILTER'), ('GRIEVANCES_VIEW_BIOMETRIC_RESULTS', 'GRIEVANCES VIEW BIOMETRIC RESULTS'), ('GRIEVANCES_FEEDBACK_VIEW_CREATE', 'GRIEVANCES FEEDBACK VIEW CREATE'), ('GRIEVANCES_FEEDBACK_VIEW_LIST', 'GRIEVANCES FEEDBACK VIEW LIST'), ('GRIEVANCES_FEEDBACK_VIEW_DETAILS', 'GRIEVANCES FEEDBACK VIEW DETAILS'), ('GRIEVANCES_FEEDBACK_VIEW_UPDATE', 'GRIEVANCES FEEDBACK VIEW UPDATE'), ('GRIEVANCES_FEEDBACK_MESSAGE_VIEW_CREATE', 'GRIEVANCES FEEDBACK MESSAGE VIEW CREATE'), ('REPORTING_EXPORT', 'REPORTING EXPORT'), ('PDU_VIEW_LIST_AND_DETAILS', 'PDU VIEW LIST AND DETAILS'), ('PDU_TEMPLATE_CREATE', 'PDU TEMPLATE CREATE'), ('PDU_TEMPLATE_DOWNLOAD', 'PDU TEMPLATE DOWNLOAD'), ('PDU_UPLOAD', 'PDU UPLOAD'), ('ALL_VIEW_PII_DATA_ON_LISTS', 'ALL VIEW PII DATA ON LISTS'), ('ACTIVITY_LOG_VIEW', 'ACTIVITY LOG VIEW'), ('ACTIVITY_LOG_DOWNLOAD', 'ACTIVITY LOG DOWNLOAD'), ('UPLOAD_STORAGE_FILE', 'UPLOAD STORAGE FILE'), ('DOWNLOAD_STORAGE_FILE', 'DOWNLOAD STORAGE FILE'), ('ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_LIST', 'ACCOUNTABILITY COMMUNICATION MESSAGE VIEW LIST'), ('ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_DETAILS', 'ACCOUNTABILITY COMMUNICATION MESSAGE VIEW DETAILS'), ('ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_CREATE', 'ACCOUNTABILITY COMMUNICATION MESSAGE VIEW CREATE'), ('ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_DETAILS_AS_CREATOR', 'ACCOUNTABILITY COMMUNICATION MESSAGE VIEW DETAILS AS CREATOR'), ('ACCOUNTABILITY_SURVEY_VIEW_CREATE', 'ACCOUNTABILITY SURVEY VIEW CREATE'), ('ACCOUNTABILITY_SURVEY_VIEW_LIST', 'ACCOUNTABILITY SURVEY VIEW LIST'), ('ACCOUNTABILITY_SURVEY_VIEW_DETAILS', 'ACCOUNTABILITY SURVEY VIEW DETAILS'), ('GEO_VIEW_LIST', 'GEO VIEW LIST'), ('CAN_ADD_BUSINESS_AREA_TO_PARTNER', 'CAN ADD BUSINESS AREA TO PARTNER')], max_length=255), blank=True, null=True, size=None), + ), + ] diff --git a/src/hct_mis_api/apps/grievance/services/needs_adjudication_ticket_services.py b/src/hct_mis_api/apps/grievance/services/needs_adjudication_ticket_services.py index 1047b5a7df..f0365519b0 100644 --- a/src/hct_mis_api/apps/grievance/services/needs_adjudication_ticket_services.py +++ b/src/hct_mis_api/apps/grievance/services/needs_adjudication_ticket_services.py @@ -108,11 +108,13 @@ def close_needs_adjudication_new_ticket(ticket_details: TicketNeedsAdjudicationD BiometricDeduplicationService, ) - ids = [str(individual.id) for individual in distinct_individuals] + photos = sorted([str(individual.photo.name) for individual in distinct_individuals]) service = BiometricDeduplicationService() try: service.report_false_positive_duplicate( - ids[0], ids[1], ticket_details.ticket.registration_data_import.program.deduplication_set_id + photos[0], + photos[1], + str(ticket_details.ticket.registration_data_import.program.deduplication_set_id), ) except service.api.API_EXCEPTION_CLASS: logger.exception("Failed to report false positive duplicate to Deduplication Engine") diff --git a/src/hct_mis_api/apps/program/schema.py b/src/hct_mis_api/apps/program/schema.py index f9944f43bb..2127c6070d 100644 --- a/src/hct_mis_api/apps/program/schema.py +++ b/src/hct_mis_api/apps/program/schema.py @@ -270,7 +270,11 @@ def resolve_is_deduplication_disabled(self, info: Any, **kwargs: Any) -> bool: program = Program.objects.only("id").get(id=decode_id_string(encoded_program_id)) # deduplication engine in progress is_still_processing = RegistrationDataImport.objects.filter( - program=program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + program=program, + deduplication_engine_status__in=[ + RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, + RegistrationDataImport.DEDUP_ENGINE_PROCESSING, + ], ).exists() # all rdis are deduplicated all_rdis_deduplicated = ( diff --git a/src/hct_mis_api/apps/registration_data/migrations/0040_migration.py b/src/hct_mis_api/apps/registration_data/migrations/0040_migration.py new file mode 100644 index 0000000000..b71ff3f288 --- /dev/null +++ b/src/hct_mis_api/apps/registration_data/migrations/0040_migration.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.25 on 2024-10-02 13:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('registration_data', '0039_migration'), + ] + + operations = [ + migrations.AlterField( + model_name='registrationdataimport', + name='deduplication_engine_status', + field=models.CharField(blank=True, choices=[('PENDING', 'Pending'), ('UPLOADED', 'Uploaded'), ('IN_PROGRESS', 'Started'), ('PROCESSING', 'Processing'), ('FINISHED', 'Finished'), ('ERROR', 'Error'), ('UPLOAD_ERROR', 'Upload Error')], default=None, max_length=255, null=True), + ), + ] diff --git a/src/hct_mis_api/apps/registration_data/models.py b/src/hct_mis_api/apps/registration_data/models.py index bfffd4c4c6..f103784a65 100644 --- a/src/hct_mis_api/apps/registration_data/models.py +++ b/src/hct_mis_api/apps/registration_data/models.py @@ -119,6 +119,7 @@ class RegistrationDataImport(TimeStampedUUIDModel, ConcurrencyModel, AdminUrlMix DEDUP_ENGINE_PENDING = "PENDING" DEDUP_ENGINE_UPLOADED = "UPLOADED" DEDUP_ENGINE_IN_PROGRESS = "IN_PROGRESS" + DEDUP_ENGINE_PROCESSING = "PROCESSING" DEDUP_ENGINE_FINISHED = "FINISHED" DEDUP_ENGINE_UPLOAD_ERROR = "UPLOAD_ERROR" DEDUP_ENGINE_ERROR = "ERROR" @@ -126,7 +127,8 @@ class RegistrationDataImport(TimeStampedUUIDModel, ConcurrencyModel, AdminUrlMix DEDUP_ENGINE_STATUS_CHOICE = ( (DEDUP_ENGINE_PENDING, _("Pending")), (DEDUP_ENGINE_UPLOADED, _("Uploaded")), - (DEDUP_ENGINE_IN_PROGRESS, _("In Progress")), + (DEDUP_ENGINE_IN_PROGRESS, _("Started")), + (DEDUP_ENGINE_PROCESSING, _("Processing")), (DEDUP_ENGINE_FINISHED, _("Finished")), (DEDUP_ENGINE_ERROR, _("Error")), (DEDUP_ENGINE_UPLOAD_ERROR, _("Upload Error")), diff --git a/src/hct_mis_api/apps/registration_datahub/apis/deduplication_engine.py b/src/hct_mis_api/apps/registration_datahub/apis/deduplication_engine.py index 99f023b549..8fb5df3078 100644 --- a/src/hct_mis_api/apps/registration_datahub/apis/deduplication_engine.py +++ b/src/hct_mis_api/apps/registration_datahub/apis/deduplication_engine.py @@ -35,9 +35,9 @@ class DeduplicationImage: @dataclasses.dataclass -class IgnoredKeysPair: - first_reference_pk: str # individual.id - second_reference_pk: str # individual.id +class IgnoredFilenamesPair: + first: str # individual.photo.name + second: str # individual.photo.name class DeduplicationEngineAPI(BaseAPI): @@ -64,7 +64,8 @@ class Endpoints: BULK_DELETE_IMAGES = "deduplication_sets/{deduplication_set_pk}/images_bulk/clear/" # DELETE - Delete all images for a deduplication set GET_DUPLICATES = "deduplication_sets/{deduplication_set_pk}/duplicates/" # GET - List view - IGNORED_KEYS = "deduplication_sets/{deduplication_set_pk}/ignored_keys/" # POST/GET + IGNORED_KEYS = "deduplication_sets/{deduplication_set_pk}/ignored/reference_pks/" # POST/GET + IGNORED_FILENAMES = "deduplication_sets/{deduplication_set_pk}/ignored/filenames/" # POST/GET def delete_deduplication_set(self, deduplication_set_id: str) -> dict: response_data, _ = self._delete(self.Endpoints.DELETE_DEDUPLICATION_SET.format(pk=deduplication_set_id)) @@ -101,8 +102,10 @@ def process_deduplication(self, deduplication_set_id: str) -> Tuple[dict, int]: ) return response_data, status - def report_false_positive_duplicate(self, false_positive_pair: IgnoredKeysPair, deduplication_set_id: str) -> None: + def report_false_positive_duplicate( + self, false_positive_pair: IgnoredFilenamesPair, deduplication_set_id: str + ) -> None: self._post( - self.Endpoints.IGNORED_KEYS.format(deduplication_set_pk=deduplication_set_id), + self.Endpoints.IGNORED_FILENAMES.format(deduplication_set_pk=deduplication_set_id), dataclasses.asdict(false_positive_pair), ) diff --git a/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py b/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py index 194edaaac1..19de1dc88f 100644 --- a/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py +++ b/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py @@ -18,7 +18,7 @@ DeduplicationSet, DeduplicationSetConfig, DeduplicationSetData, - IgnoredKeysPair, + IgnoredFilenamesPair, SimilarityPair, ) @@ -150,21 +150,36 @@ def delete_deduplication_set(self, program: Program) -> None: program.deduplication_set_id = None program.save(update_fields=["deduplication_set_id"]) - @classmethod - def mark_rdis_as_pending(cls, program: Program) -> None: + def store_similarity_pairs(self, deduplication_set_id: str, similarity_pairs: List[SimilarityPair]) -> None: + DeduplicationEngineSimilarityPair.remove_pairs(deduplication_set_id) + DeduplicationEngineSimilarityPair.bulk_add_pairs(deduplication_set_id, similarity_pairs) + + @staticmethod + def mark_rdis_as_pending(program: Program) -> None: RegistrationDataImport.objects.filter(program=program, deduplication_engine_status__isnull=True).update( deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING ) - def store_similarity_pairs(self, deduplication_set_id: str, similarity_pairs: List[SimilarityPair]) -> None: - DeduplicationEngineSimilarityPair.remove_pairs(deduplication_set_id) - DeduplicationEngineSimilarityPair.bulk_add_pairs(deduplication_set_id, similarity_pairs) + @staticmethod + def mark_rdis_as_deduplicated(deduplication_set_id: str) -> None: + program = Program.objects.get(deduplication_set_id=deduplication_set_id) + RegistrationDataImport.objects.filter( + program=program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PROCESSING + ).update(deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED) - def mark_rdis_as_deduplicated(self, deduplication_set_id: str) -> None: + @staticmethod + def mark_rdis_as_deduplication_error(deduplication_set_id: str) -> None: + program = Program.objects.get(deduplication_set_id=deduplication_set_id) + RegistrationDataImport.objects.filter( + program=program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PROCESSING + ).update(deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_ERROR) + + @staticmethod + def mark_rdis_as_processing(deduplication_set_id: str) -> None: program = Program.objects.get(deduplication_set_id=deduplication_set_id) RegistrationDataImport.objects.filter( program=program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS - ).update(deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_FINISHED) + ).update(deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PROCESSING) def store_rdis_deduplication_statistics(self, deduplication_set_id: str) -> None: program = Program.objects.get(deduplication_set_id=deduplication_set_id) @@ -194,13 +209,6 @@ def update_rdis_deduplication_statistics(self, program_id: str) -> None: ) rdi.save(update_fields=["dedup_engine_batch_duplicates", "dedup_engine_golden_record_duplicates"]) - @classmethod - def mark_rdis_as_deduplication_error(cls, deduplication_set_id: str) -> None: - program = Program.objects.get(deduplication_set_id=deduplication_set_id) - RegistrationDataImport.objects.filter( - program=program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS - ).update(deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_ERROR) - def get_duplicates_for_rdi_against_batch( self, rdi: RegistrationDataImport ) -> QuerySet[DeduplicationEngineSimilarityPair]: @@ -309,7 +317,11 @@ def fetch_biometric_deduplication_results_and_process(self, deduplication_set_id self.store_similarity_pairs(deduplication_set_id, similarity_pairs) self.store_rdis_deduplication_statistics(deduplication_set_id) self.mark_rdis_as_deduplicated(deduplication_set_id) - else: + + elif deduplication_set_data.state == "Processing": + self.mark_rdis_as_processing(deduplication_set_id) + + elif deduplication_set_data.state == "Error": self.mark_rdis_as_deduplication_error(deduplication_set_id) logger.error( f"Failed to process deduplication set {deduplication_set_id}," @@ -317,7 +329,7 @@ def fetch_biometric_deduplication_results_and_process(self, deduplication_set_id ) def report_false_positive_duplicate( - self, individual1_id: str, individual2_id: str, deduplication_set_id: str + self, individual1_photo: str, individual2_photo: str, deduplication_set_id: str ) -> None: - false_positive_pair = IgnoredKeysPair(first_reference_pk=individual1_id, second_reference_pk=individual2_id) + false_positive_pair = IgnoredFilenamesPair(first=individual1_photo, second=individual2_photo) self.api.report_false_positive_duplicate(false_positive_pair, deduplication_set_id) diff --git a/tests/unit/apps/grievance/test_services_utils.py b/tests/unit/apps/grievance/test_services_utils.py index bbb712dc8d..3ca9cd72fd 100644 --- a/tests/unit/apps/grievance/test_services_utils.py +++ b/tests/unit/apps/grievance/test_services_utils.py @@ -3,10 +3,12 @@ from unittest.mock import MagicMock, patch from django.core.exceptions import PermissionDenied, ValidationError +from django.core.files.base import ContentFile from django.test import TestCase import pytest +from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory from hct_mis_api.apps.registration_data.models import DeduplicationEngineSimilarityPair from hct_mis_api.apps.account.fixtures import ( BusinessAreaFactory, @@ -132,10 +134,7 @@ def test_handle_update_payment_channel(self) -> None: def test_verify_flex_fields(self) -> None: with pytest.raises(ValueError) as e: verify_flex_fields({"key": "value"}, "associated_with") - assert ( - str(e.value) - == "associated_with argument must be one of ['household', 'individual']" - ) + assert str(e.value) == "associated_with argument must be one of ['household', 'individual']" with pytest.raises(ValueError) as e: verify_flex_fields({"key": "value"}, "individuals") @@ -151,23 +150,14 @@ def test_handle_role(self) -> None: self.assertEqual(IndividualRoleInHousehold.objects.all().count(), 0) with pytest.raises(ValidationError) as e: - IndividualRoleInHouseholdFactory( - household=household, individual=individuals[0], role=ROLE_PRIMARY - ) + IndividualRoleInHouseholdFactory(household=household, individual=individuals[0], role=ROLE_PRIMARY) handle_role(ROLE_PRIMARY, household, individuals[0]) - assert ( - str(e.value) - == "Ticket cannot be closed, primary collector role has to be reassigned" - ) + assert str(e.value) == "Ticket cannot be closed, primary collector role has to be reassigned" # just remove exists roles - IndividualRoleInHousehold.objects.filter(household=household).update( - role=ROLE_ALTERNATE - ) + IndividualRoleInHousehold.objects.filter(household=household).update(role=ROLE_ALTERNATE) handle_role("OTHER_ROLE_XD", household, individuals[0]) - self.assertEqual( - IndividualRoleInHousehold.objects.filter(household=household).count(), 0 - ) + self.assertEqual(IndividualRoleInHousehold.objects.filter(household=household).count(), 0) # create new role handle_role(ROLE_ALTERNATE, household, individuals[0]) @@ -208,10 +198,7 @@ def test_handle_add_document(self) -> None: document_type.unique_for_individual = True document_type.save() handle_add_document(document_data, individual) - assert ( - str(e.value) - == "Document of type tax already exists for this individual" - ) + assert str(e.value) == "Document of type tax already exists for this individual" Document.objects.all().delete() self.assertEqual(Document.objects.all().count(), 0) @@ -222,13 +209,9 @@ def test_handle_add_document(self) -> None: def test_validate_individual_for_need_adjudication(self) -> None: area_type_level_1 = AreaTypeFactory(name="Province", area_level=1) - area_type_level_2 = AreaTypeFactory( - name="District", area_level=2, parent=area_type_level_1 - ) + area_type_level_2 = AreaTypeFactory(name="District", area_level=2, parent=area_type_level_1) ghazni = AreaFactory(name="Ghazni", area_type=area_type_level_1, p_code="area1") - doshi = AreaFactory( - name="Doshi", area_type=area_type_level_2, p_code="area2", parent=ghazni - ) + doshi = AreaFactory(name="Doshi", area_type=area_type_level_2, p_code="area2", parent=ghazni) business_area = BusinessAreaFactory(slug="afghanistan") program = ProgramFactory(business_area=business_area) grievance = GrievanceTicketFactory( @@ -283,13 +266,8 @@ def test_validate_individual_for_need_adjudication(self) -> None: partner_unicef = PartnerFactory() with pytest.raises(PermissionDenied) as e: - validate_individual_for_need_adjudication( - partner, individuals_1[0], ticket_details - ) - assert ( - str(e.value) - == "Permission Denied: User does not have access to select individual" - ) + validate_individual_for_need_adjudication(partner, individuals_1[0], ticket_details) + assert str(e.value) == "Permission Denied: User does not have access to select individual" with pytest.raises(ValidationError) as e: _, individuals = create_household( @@ -308,9 +286,7 @@ def test_validate_individual_for_need_adjudication(self) -> None: ) individuals[0].unicef_id = "IND-333" individuals[0].save() - validate_individual_for_need_adjudication( - partner_unicef, individuals[0], ticket_details - ) + validate_individual_for_need_adjudication(partner_unicef, individuals[0], ticket_details) assert ( str(e.value) == "The selected individual IND-333 is not valid, must be one of those attached to the ticket" @@ -320,24 +296,15 @@ def test_validate_individual_for_need_adjudication(self) -> None: with pytest.raises(ValidationError) as e: individuals[0].withdraw() - validate_individual_for_need_adjudication( - partner_unicef, individuals[0], ticket_details - ) - assert ( - str(e.value) - == "The selected individual IND-333 is not valid, must be not withdrawn" - ) + validate_individual_for_need_adjudication(partner_unicef, individuals[0], ticket_details) + assert str(e.value) == "The selected individual IND-333 is not valid, must be not withdrawn" individuals[0].unwithdraw() - validate_individual_for_need_adjudication( - partner_unicef, individuals[0], ticket_details - ) + validate_individual_for_need_adjudication(partner_unicef, individuals[0], ticket_details) ticket_details.selected_distinct.remove(individuals[0]) individuals[0].unwithdraw() - validate_individual_for_need_adjudication( - partner_unicef, individuals[0], ticket_details - ) + validate_individual_for_need_adjudication(partner_unicef, individuals[0], ticket_details) def test_validate_all_individuals_before_close_needs_adjudication(self) -> None: BusinessAreaFactory(slug="afghanistan") @@ -370,10 +337,7 @@ def test_validate_all_individuals_before_close_needs_adjudication(self) -> None: with pytest.raises(ValidationError) as e: validate_all_individuals_before_close_needs_adjudication(ticket_details) - assert ( - str(e.value) - == "Close ticket is not possible when all Individuals are flagged as duplicates" - ) + assert str(e.value) == "Close ticket is not possible when all Individuals are flagged as duplicates" with pytest.raises(ValidationError) as e: validate_all_individuals_before_close_needs_adjudication(ticket_details) @@ -386,10 +350,7 @@ def test_validate_all_individuals_before_close_needs_adjudication(self) -> None: ticket_details.selected_distinct.add(individuals_2[0]) ticket_details.save() validate_all_individuals_before_close_needs_adjudication(ticket_details) - assert ( - str(e.value) - == "Close ticket is possible when all active Individuals are flagged" - ) + assert str(e.value) == "Close ticket is possible when all active Individuals are flagged" ticket_details.selected_individuals.add(individuals_1[0]) validate_all_individuals_before_close_needs_adjudication(ticket_details) @@ -495,10 +456,7 @@ def test_close_needs_adjudication_ticket_service_when_just_duplicates(self) -> N with pytest.raises(ValidationError) as e: close_needs_adjudication_ticket_service(grievance, user) - assert ( - str(e.value) - == "Close ticket is not possible when all Individuals are flagged as duplicates" - ) + assert str(e.value) == "Close ticket is not possible when all Individuals are flagged as duplicates" gr = GrievanceTicketFactory( category=GrievanceTicket.CATEGORY_NEEDS_ADJUDICATION, @@ -529,14 +487,14 @@ def test_close_needs_adjudication_ticket_service_when_just_duplicates(self) -> N @patch( "hct_mis_api.apps.registration_datahub.services.biometric_deduplication.BiometricDeduplicationService.report_false_positive_duplicate" ) - def test_close_needs_adjudication_ticket_service_for_biometrics( - self, report_false_positive_duplicate_mock - ) -> None: + def test_close_needs_adjudication_ticket_service_for_biometrics(self, report_false_positive_duplicate_mock) -> None: user = UserFactory() ba = BusinessAreaFactory(slug="afghanistan") - program = ProgramFactory(business_area=ba) - program.deduplication_set_id = uuid.uuid4() - program.save() + deduplication_set_id = uuid.uuid4() + program = ProgramFactory(business_area=ba, deduplication_set_id=deduplication_set_id) + rdi = RegistrationDataImportFactory( + program=program, + ) hh1, individuals_1 = create_household( {"size": 2, "business_area": ba, "program": program}, @@ -557,6 +515,10 @@ def test_close_needs_adjudication_ticket_service_for_biometrics( }, ) ind_1, ind_2 = sorted([individuals_1[1], individuals_2[1]], key=lambda x: x.id) + ind_1.photo = ContentFile(b"...", name="1.png") + ind_2.photo = ContentFile(b"...", name="2.png") + ind_1.save() + ind_2.save() ticket, ticket_details = create_grievance_ticket_with_details( main_individual=ind_1, @@ -573,6 +535,8 @@ def test_close_needs_adjudication_ticket_service_for_biometrics( similarity_score=90.55, ), ) + ticket.registration_data_import = rdi + ticket.save() ticket_details.selected_distinct.set([ind_1]) ticket_details.selected_individuals.set([ind_2]) @@ -587,7 +551,7 @@ def test_close_needs_adjudication_ticket_service_for_biometrics( close_needs_adjudication_ticket_service(ticket, user) report_false_positive_duplicate_mock.assert_called_once_with( - str(ind_1.id), - str(ind_2.id), - ticket_details.ticket.registration_data_import.program.deduplication_set_id, + str(ind_1.photo.name), + str(ind_2.photo.name), + str(deduplication_set_id), ) diff --git a/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py b/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py index 1bd2e2ae48..fd5691a46c 100644 --- a/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py +++ b/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py @@ -25,7 +25,7 @@ DeduplicationSetData, SimilarityPair, DeduplicationSetConfig, - IgnoredKeysPair, + IgnoredFilenamesPair, ) from hct_mis_api.apps.registration_datahub.services.biometric_deduplication import ( BiometricDeduplicationService, @@ -56,9 +56,7 @@ def setUpTestData(cls) -> None: @patch( "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.create_deduplication_set" ) - def test_create_deduplication_set( - self, mock_create_deduplication_set: mock.Mock - ) -> None: + def test_create_deduplication_set(self, mock_create_deduplication_set: mock.Mock) -> None: service = BiometricDeduplicationService() new_uuid = str(uuid.uuid4()) @@ -73,18 +71,13 @@ def test_create_deduplication_set( reference_pk=str(self.program.id), notification_url=f"https://{settings.DOMAIN_NAME}/api/rest/{self.program.business_area.slug}/programs/{str(self.program.id)}/registration-data/webhookdeduplication/", config=DeduplicationSetConfig( - face_distance_threshold=self.program.business_area.biometric_deduplication_threshold - / 100 + face_distance_threshold=self.program.business_area.biometric_deduplication_threshold / 100 ), ) ) - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.get_duplicates" - ) - def test_get_deduplication_set_results( - self, mock_get_duplicates: mock.Mock - ) -> None: + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.get_duplicates") + def test_get_deduplication_set_results(self, mock_get_duplicates: mock.Mock) -> None: service = BiometricDeduplicationService() deduplication_set_id = str(uuid.uuid4()) @@ -106,12 +99,8 @@ def test_get_deduplication_set(self, mock_get_deduplication_set: mock.Mock) -> N mock_get_deduplication_set.assert_called_once_with(deduplication_set_id) - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images" - ) - def test_upload_individuals_success( - self, mock_bulk_upload_images: mock.Mock - ) -> None: + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images") + def test_upload_individuals_success(self, mock_bulk_upload_images: mock.Mock) -> None: self.program.deduplication_set_id = uuid.uuid4() self.program.save() @@ -119,45 +108,27 @@ def test_upload_individuals_success( program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING, ) - individual = IndividualFactory( - registration_data_import=rdi, photo="some_photo.jpg" - ) + individual = IndividualFactory(registration_data_import=rdi, photo="some_photo.jpg") service = BiometricDeduplicationService() service.upload_individuals(str(self.program.deduplication_set_id), rdi) mock_bulk_upload_images.assert_called_once_with( str(self.program.deduplication_set_id), - [ - DeduplicationImage( - reference_pk=str(individual.id), filename="some_photo.jpg" - ) - ], + [DeduplicationImage(reference_pk=str(individual.id), filename="some_photo.jpg")], ) rdi.refresh_from_db() - assert ( - rdi.deduplication_engine_status - == RegistrationDataImport.DEDUP_ENGINE_UPLOADED - ) + assert rdi.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_UPLOADED rdi.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_ERROR rdi.save() service.upload_individuals(str(self.program.deduplication_set_id), rdi) rdi.refresh_from_db() - assert ( - rdi.deduplication_engine_status - == RegistrationDataImport.DEDUP_ENGINE_UPLOADED - ) + assert rdi.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_UPLOADED - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images" - ) - def test_upload_individuals_failure( - self, mock_bulk_upload_images: mock.Mock - ) -> None: - mock_bulk_upload_images.side_effect = ( - DeduplicationEngineAPI.DeduplicationEngineAPIException - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images") + def test_upload_individuals_failure(self, mock_bulk_upload_images: mock.Mock) -> None: + mock_bulk_upload_images.side_effect = DeduplicationEngineAPI.DeduplicationEngineAPIException rdi = RegistrationDataImportFactory( program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING, @@ -168,35 +139,25 @@ def test_upload_individuals_failure( service.upload_individuals(str(self.program.deduplication_set_id), rdi) rdi.refresh_from_db() - assert ( - rdi.deduplication_engine_status - == RegistrationDataImport.DEDUP_ENGINE_UPLOAD_ERROR - ) + assert rdi.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_UPLOAD_ERROR def test_upload_individuals_no_individuals(self) -> None: rdi = RegistrationDataImportFactory( program=self.program, deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PENDING, ) - IndividualFactory( - registration_data_import=rdi, photo="some_photo.jpg", withdrawn=True - ) + IndividualFactory(registration_data_import=rdi, photo="some_photo.jpg", withdrawn=True) service = BiometricDeduplicationService() service.upload_individuals(str(self.program.deduplication_set_id), rdi) rdi.refresh_from_db() - assert ( - rdi.deduplication_engine_status - == RegistrationDataImport.DEDUP_ENGINE_FINISHED - ) + assert rdi.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_FINISHED @patch( "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.process_deduplication" ) - def test_process_deduplication_set( - self, mock_process_deduplication: mock.Mock - ) -> None: + def test_process_deduplication_set(self, mock_process_deduplication: mock.Mock) -> None: self.program.deduplication_set_id = uuid.uuid4() self.program.save() rdi = RegistrationDataImportFactory( @@ -206,12 +167,8 @@ def test_process_deduplication_set( service = BiometricDeduplicationService() mock_process_deduplication.return_value = ({}, 200) - service.process_deduplication_set( - str(self.program.deduplication_set_id), RegistrationDataImport.objects.all() - ) - mock_process_deduplication.assert_called_once_with( - str(self.program.deduplication_set_id) - ) + service.process_deduplication_set(str(self.program.deduplication_set_id), RegistrationDataImport.objects.all()) + mock_process_deduplication.assert_called_once_with(str(self.program.deduplication_set_id)) rdi.refresh_from_db() self.assertEqual( rdi.deduplication_engine_status, @@ -219,29 +176,21 @@ def test_process_deduplication_set( ) mock_process_deduplication.return_value = ({}, 409) - with self.assertRaises( - BiometricDeduplicationService.BiometricDeduplicationServiceException - ): + with self.assertRaises(BiometricDeduplicationService.BiometricDeduplicationServiceException): service.process_deduplication_set( str(self.program.deduplication_set_id), RegistrationDataImport.objects.all(), ) mock_process_deduplication.return_value = ({}, 400) - service.process_deduplication_set( - str(self.program.deduplication_set_id), RegistrationDataImport.objects.all() - ) + service.process_deduplication_set(str(self.program.deduplication_set_id), RegistrationDataImport.objects.all()) rdi.refresh_from_db() - self.assertEqual( - rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_ERROR - ) + self.assertEqual(rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_ERROR) @patch( "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.delete_deduplication_set" ) - def test_delete_deduplication_set( - self, mock_delete_deduplication_set: mock.Mock - ) -> None: + def test_delete_deduplication_set(self, mock_delete_deduplication_set: mock.Mock) -> None: service = BiometricDeduplicationService() service.delete_deduplication_set(self.program) @@ -255,9 +204,7 @@ def test_delete_deduplication_set( self.program.refresh_from_db() self.assertIsNone(self.program.deduplication_set_id) - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images" - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.bulk_upload_images") @patch( "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.process_deduplication" ) @@ -291,9 +238,7 @@ def test_upload_and_process_deduplication_set( deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, ) IndividualFactory(registration_data_import=rdi_1, photo="some_photo1.jpg") - PendingIndividualFactory( - registration_data_import=rdi_2, photo="some_photo2.jpg" - ) + PendingIndividualFactory(registration_data_import=rdi_2, photo="some_photo2.jpg") service.create_deduplication_set = mock.MagicMock() with self.assertRaisesMessage( @@ -309,23 +254,15 @@ def test_upload_and_process_deduplication_set( rdi_2.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_PENDING rdi_2.save() - mock_bulk_upload_images.side_effect = ( - DeduplicationEngineAPI.DeduplicationEngineAPIException - ) + mock_bulk_upload_images.side_effect = DeduplicationEngineAPI.DeduplicationEngineAPIException with self.assertRaisesMessage( BiometricDeduplicationService.BiometricDeduplicationServiceException, "Failed to upload images for all RDIs", ): service.upload_and_process_deduplication_set(self.program) assert mock_bulk_upload_images.call_count == 2 - assert ( - rdi_1.deduplication_engine_status - == RegistrationDataImport.DEDUP_ENGINE_ERROR - ) - assert ( - rdi_1.deduplication_engine_status - == RegistrationDataImport.DEDUP_ENGINE_ERROR - ) + assert rdi_1.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_ERROR + assert rdi_1.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_ERROR # Test when all rdi images are uploaded successfully mock_bulk_upload_images.reset_mock() @@ -342,14 +279,8 @@ def test_upload_and_process_deduplication_set( assert mock_process_deduplication.call_count == 1 rdi_1.refresh_from_db() rdi_2.refresh_from_db() - assert ( - rdi_1.deduplication_engine_status - == RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS - ) - assert ( - rdi_2.deduplication_engine_status - == RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS - ) + assert rdi_1.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS + assert rdi_2.deduplication_engine_status == RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS def test_store_results(self) -> None: self.program.deduplication_set_id = uuid.uuid4() @@ -375,9 +306,7 @@ def test_store_results(self) -> None: ), ] - service.store_similarity_pairs( - str(self.program.deduplication_set_id), similarity_pairs - ) + service.store_similarity_pairs(str(self.program.deduplication_set_id), similarity_pairs) assert self.program.deduplication_engine_similarity_pairs.count() == 3 assert self.program.deduplication_engine_similarity_pairs.filter( @@ -400,6 +329,13 @@ def test_mark_rdis_as(self) -> None: deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, ) + service.mark_rdis_as_processing(str(self.program.deduplication_set_id)) + rdi.refresh_from_db() + self.assertEqual( + rdi.deduplication_engine_status, + RegistrationDataImport.DEDUP_ENGINE_PROCESSING, + ) + service.mark_rdis_as_deduplicated(str(self.program.deduplication_set_id)) rdi.refresh_from_db() self.assertEqual( @@ -407,16 +343,12 @@ def test_mark_rdis_as(self) -> None: RegistrationDataImport.DEDUP_ENGINE_FINISHED, ) - rdi.deduplication_engine_status = ( - RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS - ) + rdi.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_PROCESSING rdi.save() service.mark_rdis_as_deduplication_error(str(self.program.deduplication_set_id)) rdi.refresh_from_db() - self.assertEqual( - rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_ERROR - ) + self.assertEqual(rdi.deduplication_engine_status, RegistrationDataImport.DEDUP_ENGINE_ERROR) def test_get_duplicates_for_rdi_against_population(self) -> None: self.program.deduplication_set_id = uuid.uuid4() @@ -459,34 +391,20 @@ def test_get_duplicates_for_rdi_against_population(self) -> None: service = BiometricDeduplicationService() similarity_pairs = [ SimilarityPair(score=0.9, first=ind1.id, second=ind2.id), # within rdi1 - SimilarityPair( - score=0.7, first=ind1.id, second=ind3.id - ), # across rdi1 and rdi2 - SimilarityPair( - score=0.8, first=ind1.id, second=ind5.id - ), # across rdi1 and population - SimilarityPair( - score=0.9, first=ind6.id, second=ind2.id - ), # across rdi1 and population + SimilarityPair(score=0.7, first=ind1.id, second=ind3.id), # across rdi1 and rdi2 + SimilarityPair(score=0.8, first=ind1.id, second=ind5.id), # across rdi1 and population + SimilarityPair(score=0.9, first=ind6.id, second=ind2.id), # across rdi1 and population SimilarityPair(score=0.9, first=ind3.id, second=ind4.id), # within rdi2 - SimilarityPair( - score=0.7, first=ind4.id, second=ind5.id - ), # across rdi2 and population - SimilarityPair( - score=0.8, first=ind5.id, second=ind6.id - ), # within population + SimilarityPair(score=0.7, first=ind4.id, second=ind5.id), # across rdi2 and population + SimilarityPair(score=0.8, first=ind5.id, second=ind6.id), # within population ] - service.store_similarity_pairs( - str(self.program.deduplication_set_id), similarity_pairs - ) + service.store_similarity_pairs(str(self.program.deduplication_set_id), similarity_pairs) duplicates = service.get_duplicates_for_rdi_against_population(rdi1) assert len(duplicates) == 2 assert list( - duplicates.order_by("similarity_score").values( - "individual1", "individual2", "similarity_score" - ) + duplicates.order_by("similarity_score").values("individual1", "individual2", "similarity_score") ) == [ { "individual1": ind1.id, @@ -500,9 +418,7 @@ def test_get_duplicates_for_rdi_against_population(self) -> None: }, ] - duplicate_individuals_count = ( - service.get_duplicate_individuals_for_rdi_against_population_count(rdi1) - ) + duplicate_individuals_count = service.get_duplicate_individuals_for_rdi_against_population_count(rdi1) assert duplicate_individuals_count == 2 def test_get_duplicates_for_merged_rdi_against_population(self) -> None: @@ -545,39 +461,21 @@ def test_get_duplicates_for_merged_rdi_against_population(self) -> None: service = BiometricDeduplicationService() similarity_pairs = [ - SimilarityPair( - score=0.7, first=ind1.id, second=ind2.id - ), # within merged rdi1 - SimilarityPair( - score=0.7, first=ind1.id, second=ind3.id - ), # across merged rdi1 and pending rdi2 - SimilarityPair( - score=0.8, first=ind1.id, second=ind5.id - ), # across merged rdi1 and population - SimilarityPair( - score=0.9, first=ind2.id, second=ind6.id - ), # across merged rdi1 and population - SimilarityPair( - score=0.9, first=ind3.id, second=ind4.id - ), # within pending rdi2 - SimilarityPair( - score=0.7, first=ind4.id, second=ind5.id - ), # across pending rdi2 and population - SimilarityPair( - score=0.8, first=ind5.id, second=ind6.id - ), # within population + SimilarityPair(score=0.7, first=ind1.id, second=ind2.id), # within merged rdi1 + SimilarityPair(score=0.7, first=ind1.id, second=ind3.id), # across merged rdi1 and pending rdi2 + SimilarityPair(score=0.8, first=ind1.id, second=ind5.id), # across merged rdi1 and population + SimilarityPair(score=0.9, first=ind2.id, second=ind6.id), # across merged rdi1 and population + SimilarityPair(score=0.9, first=ind3.id, second=ind4.id), # within pending rdi2 + SimilarityPair(score=0.7, first=ind4.id, second=ind5.id), # across pending rdi2 and population + SimilarityPair(score=0.8, first=ind5.id, second=ind6.id), # within population ] - service.store_similarity_pairs( - str(self.program.deduplication_set_id), similarity_pairs - ) + service.store_similarity_pairs(str(self.program.deduplication_set_id), similarity_pairs) duplicates = service.get_duplicates_for_merged_rdi_against_population(rdi1) assert len(duplicates) == 3 assert list( - duplicates.order_by("similarity_score").values( - "individual1", "individual2", "similarity_score" - ) + duplicates.order_by("similarity_score").values("individual1", "individual2", "similarity_score") ) == [ { "individual1": ind1.id, @@ -637,34 +535,20 @@ def test_get_duplicates_for_rdi_against_batch(self) -> None: service = BiometricDeduplicationService() similarity_pairs = [ SimilarityPair(score=0.9, first=ind1.id, second=ind2.id), # within rdi1 - SimilarityPair( - score=0.7, first=ind1.id, second=ind3.id - ), # across rdi1 and rdi2 - SimilarityPair( - score=0.8, first=ind1.id, second=ind5.id - ), # across rdi1 and population - SimilarityPair( - score=0.9, first=ind2.id, second=ind6.id - ), # across rdi1 and population + SimilarityPair(score=0.7, first=ind1.id, second=ind3.id), # across rdi1 and rdi2 + SimilarityPair(score=0.8, first=ind1.id, second=ind5.id), # across rdi1 and population + SimilarityPair(score=0.9, first=ind2.id, second=ind6.id), # across rdi1 and population SimilarityPair(score=0.9, first=ind3.id, second=ind4.id), # within rdi2 - SimilarityPair( - score=0.7, first=ind4.id, second=ind5.id - ), # across rdi2 and population - SimilarityPair( - score=0.8, first=ind5.id, second=ind6.id - ), # within population + SimilarityPair(score=0.7, first=ind4.id, second=ind5.id), # across rdi2 and population + SimilarityPair(score=0.8, first=ind5.id, second=ind6.id), # within population ] - service.store_similarity_pairs( - str(self.program.deduplication_set_id), similarity_pairs - ) + service.store_similarity_pairs(str(self.program.deduplication_set_id), similarity_pairs) duplicates = service.get_duplicates_for_rdi_against_batch(rdi1) assert len(duplicates) == 1 assert list( - duplicates.order_by("similarity_score").values( - "individual1", "individual2", "similarity_score" - ) + duplicates.order_by("similarity_score").values("individual1", "individual2", "similarity_score") ) == [ { "individual1": ind1.id, @@ -672,9 +556,7 @@ def test_get_duplicates_for_rdi_against_batch(self) -> None: "similarity_score": Decimal("90.00"), }, ] - duplicate_individuals_count = ( - service.get_duplicate_individuals_for_rdi_against_batch_count(rdi1) - ) + duplicate_individuals_count = service.get_duplicate_individuals_for_rdi_against_batch_count(rdi1) assert duplicate_individuals_count == 2 @patch( @@ -693,17 +575,13 @@ def test_create_grievance_tickets_for_duplicates( service.get_duplicates_for_merged_rdi_against_population.return_value = [] service.create_grievance_tickets_for_duplicates(rdi1) - create_needs_adjudication_tickets_for_biometrics_mock.assert_called_once_with( - [], rdi1 - ) + create_needs_adjudication_tickets_for_biometrics_mock.assert_called_once_with([], rdi1) def test_fetch_biometric_deduplication_results_and_process_success(self) -> None: deduplication_set_id = str(uuid.uuid4()) service = BiometricDeduplicationService() - service.get_deduplication_set = mock.Mock( - return_value=DeduplicationSetData(state="Clean") - ) + service.get_deduplication_set = mock.Mock(return_value=DeduplicationSetData(state="Clean")) results_data = [ { @@ -725,9 +603,7 @@ def test_fetch_biometric_deduplication_results_and_process_success(self) -> None service.fetch_biometric_deduplication_results_and_process(deduplication_set_id) service.get_deduplication_set.assert_called_once_with(deduplication_set_id) - service.get_deduplication_set_results.assert_called_once_with( - deduplication_set_id - ) + service.get_deduplication_set_results.assert_called_once_with(deduplication_set_id) service.store_similarity_pairs.assert_called_once_with( deduplication_set_id, [ @@ -744,17 +620,13 @@ def test_fetch_biometric_deduplication_results_and_process_fail(self) -> None: deduplication_set_id = str(uuid.uuid4()) service = BiometricDeduplicationService() - service.get_deduplication_set = mock.Mock( - return_value=DeduplicationSetData(state="Error") - ) + service.get_deduplication_set = mock.Mock(return_value=DeduplicationSetData(state="Error")) service.mark_rdis_as_deduplication_error = mock.Mock() service.fetch_biometric_deduplication_results_and_process(deduplication_set_id) service.get_deduplication_set.assert_called_once_with(deduplication_set_id) - service.mark_rdis_as_deduplication_error.assert_called_once_with( - deduplication_set_id - ) + service.mark_rdis_as_deduplication_error.assert_called_once_with(deduplication_set_id) def test_store_rdis_deduplication_statistics(self) -> None: deduplication_set_id = str(uuid.uuid4()) @@ -768,21 +640,13 @@ def test_store_rdis_deduplication_statistics(self) -> None: deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, ) - service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock( - return_value=8 - ) - service.get_duplicate_individuals_for_rdi_against_population_count = mock.Mock( - return_value=9 - ) + service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock(return_value=8) + service.get_duplicate_individuals_for_rdi_against_population_count = mock.Mock(return_value=9) service.store_rdis_deduplication_statistics(deduplication_set_id) - service.get_duplicate_individuals_for_rdi_against_batch_count.assert_called_once_with( - rdi1 - ) - service.get_duplicate_individuals_for_rdi_against_population_count.assert_called_once_with( - rdi1 - ) + service.get_duplicate_individuals_for_rdi_against_batch_count.assert_called_once_with(rdi1) + service.get_duplicate_individuals_for_rdi_against_population_count.assert_called_once_with(rdi1) rdi1.refresh_from_db() assert rdi1.dedup_engine_batch_duplicates == 8 @@ -801,21 +665,13 @@ def test_update_rdis_deduplication_statistics(self) -> None: status=RegistrationDataImport.IN_REVIEW, ) - service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock( - return_value=8 - ) - service.get_duplicate_individuals_for_rdi_against_population_count = mock.Mock( - return_value=9 - ) + service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock(return_value=8) + service.get_duplicate_individuals_for_rdi_against_population_count = mock.Mock(return_value=9) service.update_rdis_deduplication_statistics(self.program.id) - service.get_duplicate_individuals_for_rdi_against_batch_count.assert_called_once_with( - rdi1 - ) - service.get_duplicate_individuals_for_rdi_against_population_count.assert_called_once_with( - rdi1 - ) + service.get_duplicate_individuals_for_rdi_against_batch_count.assert_called_once_with(rdi1) + service.get_duplicate_individuals_for_rdi_against_population_count.assert_called_once_with(rdi1) rdi1.refresh_from_db() assert rdi1.dedup_engine_batch_duplicates == 8 @@ -824,14 +680,12 @@ def test_update_rdis_deduplication_statistics(self) -> None: @patch( "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI.report_false_positive_duplicate" ) - def test_report_false_positive_duplicate( - self, mock_report_false_positive_duplicate: mock.Mock - ) -> None: + def test_report_false_positive_duplicate(self, mock_report_false_positive_duplicate: mock.Mock) -> None: service = BiometricDeduplicationService() deduplication_set_id = uuid.uuid4() service.report_false_positive_duplicate("123", "456", str(deduplication_set_id)) mock_report_false_positive_duplicate.assert_called_once_with( - IgnoredKeysPair(first_reference_pk="123", second_reference_pk="456"), + IgnoredFilenamesPair(first="123", second="456"), str(deduplication_set_id), ) diff --git a/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py b/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py index db631e2bd5..a3b2ce5bd4 100644 --- a/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py +++ b/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py @@ -13,7 +13,7 @@ DeduplicationImage, DeduplicationSet, DeduplicationSetConfig, - IgnoredKeysPair, + IgnoredFilenamesPair, ) @@ -150,14 +150,14 @@ def test_report_false_positive_duplicate(self, post_mock: mock.Mock) -> None: post_mock.return_value = {}, 200 api.report_false_positive_duplicate( - IgnoredKeysPair(first_reference_pk="123", second_reference_pk="456"), + IgnoredFilenamesPair(first="123", second="456"), deduplication_set_id, ) post_mock.assert_called_once_with( - f"deduplication_sets/{deduplication_set_id}/ignored_keys/", + f"deduplication_sets/{deduplication_set_id}/ignored/filenames/", { - "first_reference_pk": "123", - "second_reference_pk": "456", + "first": "123", + "second": "456", }, ) From f9ee4f3cbd32926cb37df5f2b3a604019413d525 Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <34482854+mmaciekk@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:32:43 +0200 Subject: [PATCH 059/202] remove usd from label (#4281) Co-authored-by: Maciej Szewczyk <maciej.szewczyk@tivix.com> --- .../ProgramCycleDetails/PaymentPlansHeadCells.ts | 6 +++--- .../ProgramCycleDetails/ProgramCycleDetailsHeader.tsx | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlansHeadCells.ts b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlansHeadCells.ts index dca69f159f..215df90a3f 100644 --- a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlansHeadCells.ts +++ b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/PaymentPlansHeadCells.ts @@ -20,19 +20,19 @@ export const headCells = [ }, { disablePadding: false, - label: 'Total Entitled Quantity (USD)', + label: 'Total Entitled Quantity', id: 'totalEntitledQuantity', numeric: true, }, { disablePadding: false, - label: 'Total Undelivered Quantity (USD)', + label: 'Total Undelivered Quantity', id: 'totalUndeliveredQuantity', numeric: true, }, { disablePadding: false, - label: 'Total Delivered Quantity (USD)', + label: 'Total Delivered Quantity', id: 'totalDeliveredQuantity', numeric: true, }, diff --git a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/ProgramCycleDetailsHeader.tsx b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/ProgramCycleDetailsHeader.tsx index 05d5c5a7a4..21ec2d39b0 100644 --- a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/ProgramCycleDetailsHeader.tsx +++ b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/ProgramCycleDetails/ProgramCycleDetailsHeader.tsx @@ -80,6 +80,10 @@ export const ProgramCycleDetailsHeader = ({ title: t('Payment Module'), to: '..', }, + { + title: t('Programme Cycle'), + to: '..', + }, ]; const finishAction = async () => { @@ -153,9 +157,7 @@ export const ProgramCycleDetailsHeader = ({ title={ <Box display="flex" alignItems={'center'}> <Box display="flex" flexDirection="column"> - <Box> - {programCycle.title} - </Box> + <Box>{programCycle.title}</Box> </Box> </Box> } From 2355700d42b3de5f642f620de85840e48b8ae276 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 10:40:56 +0200 Subject: [PATCH 060/202] text --- tests/unit/apps/core/test_edopomoga_tp_creation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/apps/core/test_edopomoga_tp_creation.py b/tests/unit/apps/core/test_edopomoga_tp_creation.py index 5a55c73dd2..5cea47b5c4 100644 --- a/tests/unit/apps/core/test_edopomoga_tp_creation.py +++ b/tests/unit/apps/core/test_edopomoga_tp_creation.py @@ -5,6 +5,8 @@ from django.core.files import File from django.core.management import call_command +import pytest + 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.celery_tasks import create_target_population_task @@ -18,6 +20,7 @@ from hct_mis_api.apps.targeting.models import TargetPopulation +@pytest.mark.skip(reason="Functionality probably can be removed.") class TestEdopomogaCreation(APITestCase): databases = ("default", "cash_assist_datahub_mis") From b747a518ec048c9919296ded1bfe6bcd7783a4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Wo=C5=BAniak?= <17177420+wozniakpl@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:54:58 +0200 Subject: [PATCH 061/202] skip trivy failure --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f70e52ea1..61c6d1cc46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -296,6 +296,7 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Run Trivy vulnerability scanner + continue-on-error: true # due to getting TOOMANYREQUESTS uses: aquasecurity/trivy-action@master with: image-ref: '${{ vars.DOCKERHUB_ORGANIZATION }}/hope-support-images:core-${{ github.sha }}' From 78ef72ae6becefab16bcb8816687fd21c67f874c Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@tivix.com> Date: Thu, 3 Oct 2024 16:45:08 +0200 Subject: [PATCH 062/202] Project structure 3 (#4287) * chenged tests dir * fixed all linter problems * fix precomit * fix compose * fixed tests root dir --- .flake8 | 1 + .github/helpers/dev.sh | 2 +- .github/helpers/docker-compose.selenium.yml | 2 +- .github/workflows/ci.yml | 2 +- .pre-commit-config.yaml | 49 +----- development_tools/compose.yml | 6 +- docker/Dockerfile | 6 +- pyproject.toml | 31 ++-- .../accountability/test_communication.py | 12 +- tests/selenium/accountability/test_surveys.py | 8 +- tests/selenium/conftest.py | 92 ++++++---- .../test_country_dashboard.py | 5 +- tests/selenium/drawer/test_drawer.py | 8 +- tests/selenium/filters/test_filters.py | 2 +- .../grievance/feedback/test_feedback.py | 12 +- .../test_grievance_dashboard.py | 8 +- .../test_grievance_tickets.py | 8 +- .../login_via_admin_panel/test_login.py | 2 +- .../test_managerial_console.py | 4 +- tests/selenium/page_object/404.py | 3 +- .../accountability/communication.py | 3 +- .../accountability/comunication_details.py | 3 +- .../page_object/accountability/surveys.py | 3 +- .../accountability/surveys_details.py | 3 +- .../page_object/admin_panel/admin_panel.py | 3 +- tests/selenium/page_object/base_components.py | 3 +- .../country_dashboard/country_dashboard.py | 3 +- tests/selenium/page_object/filters.py | 3 +- .../grievance/details_feedback_page.py | 3 +- .../grievance/details_grievance_page.py | 3 +- .../page_object/grievance/feedback.py | 3 +- .../grievance/grievance_dashboard.py | 3 +- .../grievance/grievance_tickets.py | 3 +- .../page_object/grievance/new_feedback.py | 3 +- .../page_object/grievance/new_ticket.py | 3 +- .../managerial_console/managerial_console.py | 3 +- .../payment_module/new_payment_plan.py | 3 +- .../payment_module/payment_module.py | 3 +- .../payment_module/payment_module_details.py | 3 +- .../payment_module/program_cycle.py | 3 +- .../payment_verification/payment_record.py | 3 +- .../payment_verification.py | 3 +- .../payment_verification_details.py | 3 +- tests/selenium/page_object/people/people.py | 3 +- .../page_object/program_log/payment_log.py | 3 +- .../programme_details/programme_details.py | 3 +- .../programme_management.py | 3 +- .../programme_population/households.py | 3 +- .../households_details.py | 3 +- .../programme_population/individuals.py | 3 +- .../individuals_details.py | 3 +- .../periodic_data_update_templates.py | 3 +- .../periodic_data_update_uploads.py | 3 +- .../programme_users/programme_users.py | 3 +- .../rdi_details_page.py | 3 +- .../registration_data_import.py | 3 +- .../page_object/targeting/targeting.py | 2 +- .../page_object/targeting/targeting_create.py | 3 +- .../targeting/targeting_details.py | 3 +- .../payment_module/test_payment_plans.py | 19 +- .../payment_module/test_program_cycles.py | 2 +- .../test_payment_verification.py | 12 +- tests/selenium/people/test_people.py | 2 +- .../test_people_periodic_data_update.py | 3 +- .../program_details/test_program_details.py | 10 +- .../selenium/program_log/test_program_log.py | 8 +- .../test_programme_management.py | 11 +- .../programme_population/test_households.py | 5 +- .../programme_population/test_individuals.py | 5 +- .../test_periodic_data_templates.py | 10 +- .../test_periodic_data_update_upload.py | 16 +- .../programme_user/test_programme_user.py | 4 +- .../test_registration_data_import.py | 14 +- tests/selenium/targeting/test_targeting.py | 6 +- tests/unit/api/base.py | 2 +- tests/unit/api/test_auth.py | 4 +- tests/unit/api/test_business_area.py | 2 +- tests/unit/api/test_delegate_people.py | 2 +- tests/unit/api/test_program.py | 2 +- tests/unit/api/test_push_people.py | 2 +- tests/unit/api/test_rdi.py | 2 +- tests/unit/api/test_soft.py | 2 +- tests/unit/api/test_upload.py | 2 +- .../test_pull_from_datahub.py | 4 +- tests/unit/apps/core/test_exchange_rates.py | 6 +- .../test_approve_automatic_tickets.py | 1 - .../test_create_needs_adjudication_tickets.py | 48 ++---- .../apps/grievance/test_services_utils.py | 10 +- .../test_individual_iban_xlsx_update.py | 12 +- .../apps/household/test_individual_query.py | 162 +++++------------- .../household/test_individual_xlsx_update.py | 8 +- tests/unit/apps/payment/test_models1.py | 128 ++++---------- .../test_update_reconciliation_data.py | 4 +- .../test_periodic_data_update_upload_views.py | 4 +- .../program/test_program_cycle_rest_api.py | 2 +- .../services/test_mark_submissions.py | 4 +- .../apps/registration_data/test_models.py | 6 +- .../apps/registration_data/test_rest_api.py | 2 +- .../test_biometric_deduplication_service.py | 4 +- .../test_deduplication_engine_api.py | 48 ++---- .../test_kobo_validators_methods.py | 2 +- .../test_rdi_kobo_create.py | 8 +- .../test_rdi_people_create.py | 4 +- ...istration_data_import_datahub_mutations.py | 4 +- .../test_validate_xlsx_import_task.py | 4 +- tests/unit/apps/targeting/test_admin.py | 2 +- tests/unit/conftest.py | 5 +- 107 files changed, 437 insertions(+), 571 deletions(-) diff --git a/.flake8 b/.flake8 index 5734c6b738..2de1c3d64e 100644 --- a/.flake8 +++ b/.flake8 @@ -21,6 +21,7 @@ exclude = migrations, snapshots, __pypackages__, + frontend, ignore = # black formatting related diff --git a/.github/helpers/dev.sh b/.github/helpers/dev.sh index ac94420868..9a0c457330 100755 --- a/.github/helpers/dev.sh +++ b/.github/helpers/dev.sh @@ -20,7 +20,7 @@ else --cov-report xml:test-coverage/coverage.xml \ --randomly-seed=42 \ --create-db \ - /tests/unit/ + ./tests/unit/ ;; "lint") mkdir -p ./lint-results diff --git a/.github/helpers/docker-compose.selenium.yml b/.github/helpers/docker-compose.selenium.yml index 2fcbe8577e..7ac51dba81 100644 --- a/.github/helpers/docker-compose.selenium.yml +++ b/.github/helpers/docker-compose.selenium.yml @@ -5,7 +5,7 @@ services: backend: volumes: - ../../tests/test-coverage:/code/test-coverage - - ../../tests/report/:/tests/selenium/output_data/report/ + - ../../tests/report/:/code/tests/selenium/output_data/report/ - type: volume source: backend-web-app target: /code/src/hct_mis_api/apps/web diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61c6d1cc46..1f1c2c97f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -264,7 +264,7 @@ jobs: -f ./.github/helpers/docker-compose.selenium.yml \ run backend bash -c " waitforit -host=db -port=5432 -timeout=30 - pytest -svvv $extra_options /tests/selenium --cov-report xml:test-coverage/coverage.xml --html-report=/tests/selenium/output_data/report/report.html --randomly-seed=42 + pytest -svvv $extra_options ./tests/selenium --cov-report xml:test-coverage/coverage.xml --html-report=./tests/selenium/output_data/report/report.html --randomly-seed=42 " - name: Upload Artifact uses: actions/upload-artifact@v4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2efe9bbcc8..73c64dd787 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,42 +1,9 @@ repos: - - repo: https://github.com/PyCQA/isort - rev: 5.13.2 - hooks: - - id: isort - stages: [commit] - - repo: https://github.com/ambv/black - rev: 23.12.1 - hooks: - - id: black - args: [--config=backend/pyproject.toml] - exclude: "migrations|snapshots" - stages: [commit] - - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 - hooks: - - id: flake8 - args: [--config=backend/.flake8] - additional_dependencies: [flake8-bugbear==22.12.6] - stages: [ commit ] - exclude: /deployment/|/migrations/ - # mypy precommit hook - - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.982 - hooks: - - id: mypy - verbose: true - args: [--config-file=backend/pyproject.toml, --follow-imports=skip] - pass_filenames: true - stages: [commit] - additional_dependencies: [ - types-requests==2.28.11.15, - types-redis==4.5.1.4, - types-python-dateutil==2.8.19.10, - types-pytz==2022.7.1.2, - djangorestframework-stubs==1.9.1, - graphene-stubs==0.15, - django-stubs==1.15.0, - django-stubs-ext==0.7.0, - openpyxl-stubs==0.1.25 - ] - exclude: /deployment/|/migrations/ + - repo: local + hooks: + - id: Linters checks + name: Linters checks + entry: docker compose -f development_tools/compose.yml run --no-TTY -i --rm backend sh -c 'black . --check && isort . --check-only && flake8 . && mypy .' + language: system + pass_filenames: false + verbose: true \ No newline at end of file diff --git a/development_tools/compose.yml b/development_tools/compose.yml index 1e9c897272..20899f6bda 100644 --- a/development_tools/compose.yml +++ b/development_tools/compose.yml @@ -25,11 +25,7 @@ services: ports: - "8080:8000" volumes: - - ../src:/code/src/ - - ../manage.py:/code/manage.py - - ../pyproject.toml:/code/pyproject.toml - - ../pdm.lock:/code/pdm.lock - - ../tests:/tests/ +# - ../.:/code/ - backend-data:/data - ../pyproject.toml:/packages/pyproject.toml - ../pdm.lock:/packages/pdm.lock diff --git a/docker/Dockerfile b/docker/Dockerfile index 727d72f63d..e546489360 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -47,7 +47,7 @@ ENV PYTHONPYCACHEPREFIX=/tmp/pycache \ PYTHONPATH=$PYPACKAGES/lib:$PYTHONPATH \ PATH=$PYPACKAGES/bin:$PATH \ XDG_RUNTIME_DIR=/run/user/"${UID}" - +ENV DJANGO_SETTINGS_MODULE=hct_mis_api.config.settings WORKDIR $CODE COPY --from=waitforit /data/waitforit /usr/local/bin/waitforit @@ -82,12 +82,12 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -ENV PYTHONPATH=$CODE/src:/test/:$PYTHONPATH +ENV PYTHONPATH=$CODE/src:$CODE/test/:$PYTHONPATH RUN pdm sync --no-editable --no-self --no-isolation WORKDIR $CODE COPY ./src/ ./src/ -COPY ./tests /tests +COPY ./tests ./tests COPY ./manage.py ./manage.py COPY .flake8 pyproject.toml pdm.lock ./ COPY ./docker/entrypoint.sh /bin/ diff --git a/pyproject.toml b/pyproject.toml index b6ef3ae60f..7d5ef6e541 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ exclude = ''' | migrations | snapshots | __pypackages__ + | frontend )/ ''' # TODO: remove migrations exclude rule once it won't create much conflicts between feature branches and develop @@ -45,18 +46,18 @@ known_first_party = [ "accountability", ] known_django = "django" -sections = ["FUTURE","STDLIB","DJANGO","THIRDPARTY","FIRSTPARTY","LOCALFOLDER"] +sections = ["FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] include_trailing_comma = true -skip = ["migrations", "snapshots", "venv", "__pypackages__"] +skip = ["migrations", "snapshots", "venv", ".venv", "__pypackages__", "frontend"] [tool.mypy] python_version = 3.11 show_error_codes = true exclude = [ - "migrations", - "venv", - "snapshots", - "__pypackages__", + "migrations", + "venv", + "snapshots", + "__pypackages__", ] strict = true @@ -66,12 +67,12 @@ follow_imports = "skip" # TODO: remove one, fix errors, repeat disable_error_code = [ - "var-annotated", # this enforces Django Model fields to have type annotations - "attr-defined", - "misc", # cannot subclass DjangoObjectType - "union-attr", - "type-arg", # this misses type parameters for graphene.ObjectType - "no-any-return", # this enforces adding return None for function that returns None + "var-annotated", # this enforces Django Model fields to have type annotations + "attr-defined", + "misc", # cannot subclass DjangoObjectType + "union-attr", + "type-arg", # this misses type parameters for graphene.ObjectType + "no-any-return", # this enforces adding return None for function that returns None ] [tool.django-stubs] @@ -133,7 +134,7 @@ requires = ["pdm-backend"] build-backend = "pdm.backend" [tool.pdm.build] -includes = ['src/hct_mis_api','src/data'] +includes = ['src/hct_mis_api', 'src/data'] [tool.pdm] distribution = true @@ -143,7 +144,7 @@ name = "hope" version = "3.0.0" description = "HCT MIS is UNICEF's humanitarian cash transfer platform." authors = [ - {name = "Tivix"}, + { name = "Tivix" }, ] dependencies = [ "setuptools==71.1.0", @@ -242,7 +243,7 @@ dependencies = [ ] requires-python = "==3.11.*" readme = "README.md" -license = {text = "None"} +license = { text = "None" } [tool.setuptools] py-modules = ["hct_mis_api"] diff --git a/tests/selenium/accountability/test_communication.py b/tests/selenium/accountability/test_communication.py index 622ecf9458..adda23c38a 100644 --- a/tests/selenium/accountability/test_communication.py +++ b/tests/selenium/accountability/test_communication.py @@ -1,9 +1,4 @@ import pytest -from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name -from tests.selenium.page_object.accountability.communication import AccountabilityCommunication -from tests.selenium.page_object.accountability.comunication_details import ( - AccountabilityCommunicationDetails, -) from hct_mis_api.apps.account.models import User from hct_mis_api.apps.accountability.fixtures import CommunicationMessageFactory @@ -15,6 +10,13 @@ TargetPopulationFactory, ) from hct_mis_api.apps.targeting.models import TargetPopulation +from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name +from tests.selenium.page_object.accountability.communication import ( + AccountabilityCommunication, +) +from tests.selenium.page_object.accountability.comunication_details import ( + AccountabilityCommunicationDetails, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/accountability/test_surveys.py b/tests/selenium/accountability/test_surveys.py index 64f062cb28..b5935f3e21 100644 --- a/tests/selenium/accountability/test_surveys.py +++ b/tests/selenium/accountability/test_surveys.py @@ -1,9 +1,6 @@ from django.db import transaction import pytest -from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name -from tests.selenium.page_object.accountability.surveys import AccountabilitySurveys -from tests.selenium.page_object.accountability.surveys_details import AccountabilitySurveysDetails from hct_mis_api.apps.account.models import User from hct_mis_api.apps.accountability.fixtures import SurveyFactory @@ -16,6 +13,11 @@ TargetingCriteriaFactory, TargetPopulationFactory, ) +from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name +from tests.selenium.page_object.accountability.surveys import AccountabilitySurveys +from tests.selenium.page_object.accountability.surveys_details import ( + AccountabilitySurveysDetails, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/conftest.py b/tests/selenium/conftest.py index ea799d20c8..ab6945a2d4 100644 --- a/tests/selenium/conftest.py +++ b/tests/selenium/conftest.py @@ -6,49 +6,90 @@ from django.core.management import call_command import pytest -from environ import Env from _pytest.fixtures import FixtureRequest from _pytest.nodes import Item from _pytest.runner import CallInfo +from environ import Env from flags.models import FlagState -from tests.selenium.page_object.accountability.communication import AccountabilityCommunication +from pytest_django.live_server_helper import LiveServer +from pytest_html_reporter import attach +from selenium import webdriver +from selenium.webdriver import Chrome +from selenium.webdriver.chrome.options import Options + +from hct_mis_api.apps.account.fixtures import RoleFactory, UserFactory +from hct_mis_api.apps.account.models import Partner, Role, User, UserRole +from hct_mis_api.apps.account.permissions import Permissions +from hct_mis_api.apps.core.models import ( + BusinessArea, + BusinessAreaPartnerThrough, + DataCollectingType, +) +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 tests.selenium.page_object.accountability.communication import ( + AccountabilityCommunication, +) from tests.selenium.page_object.accountability.comunication_details import ( AccountabilityCommunicationDetails, ) from tests.selenium.page_object.accountability.surveys import AccountabilitySurveys -from tests.selenium.page_object.accountability.surveys_details import AccountabilitySurveysDetails +from tests.selenium.page_object.accountability.surveys_details import ( + AccountabilitySurveysDetails, +) from tests.selenium.page_object.admin_panel.admin_panel import AdminPanel -from tests.selenium.page_object.country_dashboard.country_dashboard import CountryDashboard +from tests.selenium.page_object.country_dashboard.country_dashboard import ( + CountryDashboard, +) from tests.selenium.page_object.filters import Filters -from tests.selenium.page_object.grievance.details_feedback_page import FeedbackDetailsPage -from tests.selenium.page_object.grievance.details_grievance_page import GrievanceDetailsPage +from tests.selenium.page_object.grievance.details_feedback_page import ( + FeedbackDetailsPage, +) +from tests.selenium.page_object.grievance.details_grievance_page import ( + GrievanceDetailsPage, +) from tests.selenium.page_object.grievance.feedback import Feedback from tests.selenium.page_object.grievance.grievance_dashboard import GrievanceDashboard from tests.selenium.page_object.grievance.grievance_tickets import GrievanceTickets from tests.selenium.page_object.grievance.new_feedback import NewFeedback from tests.selenium.page_object.grievance.new_ticket import NewTicket -from tests.selenium.page_object.managerial_console.managerial_console import ManagerialConsole +from tests.selenium.page_object.managerial_console.managerial_console import ( + ManagerialConsole, +) from tests.selenium.page_object.payment_module.new_payment_plan import NewPaymentPlan from tests.selenium.page_object.payment_module.payment_module import PaymentModule -from tests.selenium.page_object.payment_module.payment_module_details import PaymentModuleDetails +from tests.selenium.page_object.payment_module.payment_module_details import ( + PaymentModuleDetails, +) from tests.selenium.page_object.payment_module.program_cycle import ( ProgramCycleDetailsPage, ProgramCyclePage, ) from tests.selenium.page_object.payment_verification.payment_record import PaymentRecord -from tests.selenium.page_object.payment_verification.payment_verification import PaymentVerification +from tests.selenium.page_object.payment_verification.payment_verification import ( + PaymentVerification, +) from tests.selenium.page_object.payment_verification.payment_verification_details import ( PaymentVerificationDetails, ) from tests.selenium.page_object.people.people import People from tests.selenium.page_object.people.people_details import PeopleDetails from tests.selenium.page_object.program_log.payment_log import ProgramLog -from tests.selenium.page_object.programme_details.programme_details import ProgrammeDetails -from tests.selenium.page_object.programme_management.programme_management import ProgrammeManagement +from tests.selenium.page_object.programme_details.programme_details import ( + ProgrammeDetails, +) +from tests.selenium.page_object.programme_management.programme_management import ( + ProgrammeManagement, +) from tests.selenium.page_object.programme_population.households import Households -from tests.selenium.page_object.programme_population.households_details import HouseholdsDetails +from tests.selenium.page_object.programme_population.households_details import ( + HouseholdsDetails, +) from tests.selenium.page_object.programme_population.individuals import Individuals -from tests.selenium.page_object.programme_population.individuals_details import IndividualsDetails +from tests.selenium.page_object.programme_population.individuals_details import ( + IndividualsDetails, +) from tests.selenium.page_object.programme_population.periodic_data_update_templates import ( PeriodicDatUpdateTemplates, PeriodicDatUpdateTemplatesDetails, @@ -57,30 +98,15 @@ PeriodicDataUpdateUploads, ) from tests.selenium.page_object.programme_users.programme_users import ProgrammeUsers -from tests.selenium.page_object.registration_data_import.rdi_details_page import RDIDetailsPage +from tests.selenium.page_object.registration_data_import.rdi_details_page import ( + RDIDetailsPage, +) from tests.selenium.page_object.registration_data_import.registration_data_import import ( RegistrationDataImport, ) from tests.selenium.page_object.targeting.targeting import Targeting from tests.selenium.page_object.targeting.targeting_create import TargetingCreate from tests.selenium.page_object.targeting.targeting_details import TargetingDetails -from pytest_django.live_server_helper import LiveServer -from pytest_html_reporter import attach -from selenium import webdriver -from selenium.webdriver import Chrome -from selenium.webdriver.chrome.options import Options - -from hct_mis_api.apps.account.fixtures import RoleFactory, UserFactory -from hct_mis_api.apps.account.models import Partner, Role, User, UserRole -from hct_mis_api.apps.account.permissions import Permissions -from hct_mis_api.apps.core.models import ( - BusinessArea, - BusinessAreaPartnerThrough, - DataCollectingType, -) -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 def pytest_addoption(parser) -> None: # type: ignore @@ -97,8 +123,8 @@ def pytest_configure(config) -> None: # type: ignore settings.SCREENSHOT_DIRECTORY = f"{settings.REPORT_DIRECTORY}/screenshot" if not os.path.exists(settings.SCREENSHOT_DIRECTORY): os.makedirs(settings.SCREENSHOT_DIRECTORY) - print('settings.SCREENSHOT_DIRECTORY',settings.SCREENSHOT_DIRECTORY) - print('*'*70) + print("settings.SCREENSHOT_DIRECTORY", settings.SCREENSHOT_DIRECTORY) + print("*" * 70) for file in os.listdir(settings.SCREENSHOT_DIRECTORY): os.remove(os.path.join(settings.SCREENSHOT_DIRECTORY, file)) diff --git a/tests/selenium/country_dashboard/test_country_dashboard.py b/tests/selenium/country_dashboard/test_country_dashboard.py index b305bcd3d0..1f8f5a3cba 100644 --- a/tests/selenium/country_dashboard/test_country_dashboard.py +++ b/tests/selenium/country_dashboard/test_country_dashboard.py @@ -1,5 +1,8 @@ import pytest -from tests.selenium.page_object.country_dashboard.country_dashboard import CountryDashboard + +from tests.selenium.page_object.country_dashboard.country_dashboard import ( + CountryDashboard, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/drawer/test_drawer.py b/tests/selenium/drawer/test_drawer.py index 25b81ad839..104249977b 100644 --- a/tests/selenium/drawer/test_drawer.py +++ b/tests/selenium/drawer/test_drawer.py @@ -2,13 +2,17 @@ import pytest from dateutil.relativedelta import relativedelta -from tests.selenium.page_object.programme_details.programme_details import ProgrammeDetails -from tests.selenium.page_object.programme_management.programme_management import ProgrammeManagement from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory from hct_mis_api.apps.core.models import BusinessArea, DataCollectingType from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program +from tests.selenium.page_object.programme_details.programme_details import ( + ProgrammeDetails, +) +from tests.selenium.page_object.programme_management.programme_management import ( + ProgrammeManagement, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/filters/test_filters.py b/tests/selenium/filters/test_filters.py index f6e6b03536..f6f5c92ec6 100644 --- a/tests/selenium/filters/test_filters.py +++ b/tests/selenium/filters/test_filters.py @@ -5,7 +5,6 @@ import pytest from dateutil.relativedelta import relativedelta -from tests.selenium.page_object.filters import Filters from hct_mis_api.apps.account.models import User from hct_mis_api.apps.core.models import BusinessArea @@ -27,6 +26,7 @@ TargetPopulationFactory, ) from hct_mis_api.apps.targeting.models import TargetPopulation +from tests.selenium.page_object.filters import Filters from tests.selenium.page_object.grievance.details_grievance_page import ( GrievanceDetailsPage, ) diff --git a/tests/selenium/grievance/feedback/test_feedback.py b/tests/selenium/grievance/feedback/test_feedback.py index 20195c61d3..7f091d980f 100644 --- a/tests/selenium/grievance/feedback/test_feedback.py +++ b/tests/selenium/grievance/feedback/test_feedback.py @@ -4,10 +4,6 @@ from django.core.management import call_command import pytest -from tests.selenium.page_object.grievance.details_feedback_page import FeedbackDetailsPage -from tests.selenium.page_object.grievance.feedback import Feedback -from tests.selenium.page_object.grievance.new_feedback import NewFeedback -from tests.selenium.page_object.programme_details.programme_details import ProgrammeDetails from pytest_django import DjangoDbBlocker from selenium.webdriver import Keys @@ -15,10 +11,18 @@ from hct_mis_api.apps.household.fixtures import create_household_and_individuals from hct_mis_api.apps.household.models import HOST, Household from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name +from tests.selenium.page_object.grievance.details_feedback_page import ( + FeedbackDetailsPage, +) from tests.selenium.page_object.grievance.details_grievance_page import ( GrievanceDetailsPage, ) +from tests.selenium.page_object.grievance.feedback import Feedback +from tests.selenium.page_object.grievance.new_feedback import NewFeedback from tests.selenium.page_object.grievance.new_ticket import NewTicket +from tests.selenium.page_object.programme_details.programme_details import ( + ProgrammeDetails, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/grievance/grievance_dashboard/test_grievance_dashboard.py b/tests/selenium/grievance/grievance_dashboard/test_grievance_dashboard.py index 3c38c22f58..9d15fd4337 100644 --- a/tests/selenium/grievance/grievance_dashboard/test_grievance_dashboard.py +++ b/tests/selenium/grievance/grievance_dashboard/test_grievance_dashboard.py @@ -5,15 +5,17 @@ from django.utils import timezone import pytest -from tests.selenium.page_object.grievance.details_grievance_page import GrievanceDetailsPage -from tests.selenium.page_object.grievance.grievance_dashboard import GrievanceDashboard -from tests.selenium.page_object.grievance.grievance_tickets import GrievanceTickets from hct_mis_api.apps.account.models import User from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.grievance.models import GrievanceTicket from hct_mis_api.apps.program.models import Program from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name +from tests.selenium.page_object.grievance.details_grievance_page import ( + GrievanceDetailsPage, +) +from tests.selenium.page_object.grievance.grievance_dashboard import GrievanceDashboard +from tests.selenium.page_object.grievance.grievance_tickets import GrievanceTickets pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/grievance/grievance_tickets/test_grievance_tickets.py b/tests/selenium/grievance/grievance_tickets/test_grievance_tickets.py index bcc51e2b55..f203fbc3d4 100644 --- a/tests/selenium/grievance/grievance_tickets/test_grievance_tickets.py +++ b/tests/selenium/grievance/grievance_tickets/test_grievance_tickets.py @@ -8,9 +8,6 @@ import pytest from dateutil.relativedelta import relativedelta -from tests.selenium.page_object.grievance.details_grievance_page import GrievanceDetailsPage -from tests.selenium.page_object.grievance.grievance_tickets import GrievanceTickets -from tests.selenium.page_object.grievance.new_ticket import NewTicket from pytest_django import DjangoDbBlocker from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement @@ -38,6 +35,11 @@ from tests.selenium.drawer.test_drawer import get_program_with_dct_type_and_name from tests.selenium.helpers.date_time_format import FormatTime from tests.selenium.page_object.admin_panel.admin_panel import AdminPanel +from tests.selenium.page_object.grievance.details_grievance_page import ( + GrievanceDetailsPage, +) +from tests.selenium.page_object.grievance.grievance_tickets import GrievanceTickets +from tests.selenium.page_object.grievance.new_ticket import NewTicket from tests.selenium.page_object.programme_population.households import Households from tests.selenium.page_object.programme_population.households_details import ( HouseholdsDetails, diff --git a/tests/selenium/login_via_admin_panel/test_login.py b/tests/selenium/login_via_admin_panel/test_login.py index 392f1e3878..6516392d48 100644 --- a/tests/selenium/login_via_admin_panel/test_login.py +++ b/tests/selenium/login_via_admin_panel/test_login.py @@ -1,10 +1,10 @@ import pytest -from tests.selenium.page_object.admin_panel.admin_panel import AdminPanel from selenium.webdriver import Chrome from selenium.webdriver.common.by import By from hct_mis_api.apps.account.fixtures import UserFactory from hct_mis_api.apps.account.models import User +from tests.selenium.page_object.admin_panel.admin_panel import AdminPanel pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/managerial_console/test_managerial_console.py b/tests/selenium/managerial_console/test_managerial_console.py index a8d592cac8..2d3e759560 100644 --- a/tests/selenium/managerial_console/test_managerial_console.py +++ b/tests/selenium/managerial_console/test_managerial_console.py @@ -4,7 +4,6 @@ import pytest from dateutil.relativedelta import relativedelta -from tests.selenium.page_object.managerial_console.managerial_console import ManagerialConsole from selenium.webdriver.common.by import By from hct_mis_api.apps.account.fixtures import UserFactory @@ -20,6 +19,9 @@ TargetPopulationFactory, ) from hct_mis_api.apps.targeting.models import TargetPopulation +from tests.selenium.page_object.managerial_console.managerial_console import ( + ManagerialConsole, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/page_object/404.py b/tests/selenium/page_object/404.py index e3d6568151..7b1737475e 100644 --- a/tests/selenium/page_object/404.py +++ b/tests/selenium/page_object/404.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class ErrorPage(BaseComponents): # Locators diff --git a/tests/selenium/page_object/accountability/communication.py b/tests/selenium/page_object/accountability/communication.py index c406de5076..3cd3a833ee 100644 --- a/tests/selenium/page_object/accountability/communication.py +++ b/tests/selenium/page_object/accountability/communication.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class AccountabilityCommunication(BaseComponents): # Locators diff --git a/tests/selenium/page_object/accountability/comunication_details.py b/tests/selenium/page_object/accountability/comunication_details.py index cbc32e3c3a..9a1dc7610d 100644 --- a/tests/selenium/page_object/accountability/comunication_details.py +++ b/tests/selenium/page_object/accountability/comunication_details.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class AccountabilityCommunicationDetails(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/accountability/surveys.py b/tests/selenium/page_object/accountability/surveys.py index e8447ddc01..4c1c470653 100644 --- a/tests/selenium/page_object/accountability/surveys.py +++ b/tests/selenium/page_object/accountability/surveys.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class AccountabilitySurveys(BaseComponents): # Locators diff --git a/tests/selenium/page_object/accountability/surveys_details.py b/tests/selenium/page_object/accountability/surveys_details.py index e139e3303b..95f4f7c83f 100644 --- a/tests/selenium/page_object/accountability/surveys_details.py +++ b/tests/selenium/page_object/accountability/surveys_details.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class AccountabilitySurveysDetails(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/admin_panel/admin_panel.py b/tests/selenium/page_object/admin_panel/admin_panel.py index 267bd5d5f3..895528d86f 100644 --- a/tests/selenium/page_object/admin_panel/admin_panel.py +++ b/tests/selenium/page_object/admin_panel/admin_panel.py @@ -1,7 +1,8 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class AdminPanel(BaseComponents): login = "id_username" diff --git a/tests/selenium/page_object/base_components.py b/tests/selenium/page_object/base_components.py index 2ad85ba7c0..e9d5b0a876 100644 --- a/tests/selenium/page_object/base_components.py +++ b/tests/selenium/page_object/base_components.py @@ -1,8 +1,9 @@ from time import sleep -from tests.selenium.helpers.helper import Common from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.helpers.helper import Common + class BaseComponents(Common): # Labels diff --git a/tests/selenium/page_object/country_dashboard/country_dashboard.py b/tests/selenium/page_object/country_dashboard/country_dashboard.py index ee7067511c..10a818519b 100644 --- a/tests/selenium/page_object/country_dashboard/country_dashboard.py +++ b/tests/selenium/page_object/country_dashboard/country_dashboard.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class CountryDashboard(BaseComponents): navResourcesReleaseNote = 'a[data-cy="nav-resources-Release Note"]' diff --git a/tests/selenium/page_object/filters.py b/tests/selenium/page_object/filters.py index c2a17efc40..d17a9607c9 100644 --- a/tests/selenium/page_object/filters.py +++ b/tests/selenium/page_object/filters.py @@ -1,9 +1,10 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class Filters(BaseComponents): filtersSearch = 'div[data-cy="filters-search"]' diff --git a/tests/selenium/page_object/grievance/details_feedback_page.py b/tests/selenium/page_object/grievance/details_feedback_page.py index 3800042fdd..188f060023 100644 --- a/tests/selenium/page_object/grievance/details_feedback_page.py +++ b/tests/selenium/page_object/grievance/details_feedback_page.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class FeedbackDetailsPage(BaseComponents): # Locators diff --git a/tests/selenium/page_object/grievance/details_grievance_page.py b/tests/selenium/page_object/grievance/details_grievance_page.py index 7c9c8ca85b..a083204826 100644 --- a/tests/selenium/page_object/grievance/details_grievance_page.py +++ b/tests/selenium/page_object/grievance/details_grievance_page.py @@ -1,9 +1,10 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class GrievanceDetailsPage(BaseComponents): # Locators diff --git a/tests/selenium/page_object/grievance/feedback.py b/tests/selenium/page_object/grievance/feedback.py index 64ff54acc5..c3602b62e4 100644 --- a/tests/selenium/page_object/grievance/feedback.py +++ b/tests/selenium/page_object/grievance/feedback.py @@ -1,8 +1,9 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class Feedback(BaseComponents): # Locators diff --git a/tests/selenium/page_object/grievance/grievance_dashboard.py b/tests/selenium/page_object/grievance/grievance_dashboard.py index d968bc3b7f..4935296e89 100644 --- a/tests/selenium/page_object/grievance/grievance_dashboard.py +++ b/tests/selenium/page_object/grievance/grievance_dashboard.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class GrievanceDashboard(BaseComponents): # Locators diff --git a/tests/selenium/page_object/grievance/grievance_tickets.py b/tests/selenium/page_object/grievance/grievance_tickets.py index 140af1994e..776511cdde 100644 --- a/tests/selenium/page_object/grievance/grievance_tickets.py +++ b/tests/selenium/page_object/grievance/grievance_tickets.py @@ -1,8 +1,9 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class GrievanceTickets(BaseComponents): # Locators diff --git a/tests/selenium/page_object/grievance/new_feedback.py b/tests/selenium/page_object/grievance/new_feedback.py index 2a25af307f..37ab4e7c48 100644 --- a/tests/selenium/page_object/grievance/new_feedback.py +++ b/tests/selenium/page_object/grievance/new_feedback.py @@ -1,8 +1,9 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class NewFeedback(BaseComponents): # Locators diff --git a/tests/selenium/page_object/grievance/new_ticket.py b/tests/selenium/page_object/grievance/new_ticket.py index cd1c87c750..dd0ef168ce 100644 --- a/tests/selenium/page_object/grievance/new_ticket.py +++ b/tests/selenium/page_object/grievance/new_ticket.py @@ -1,9 +1,10 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class NewTicket(BaseComponents): # Locators diff --git a/tests/selenium/page_object/managerial_console/managerial_console.py b/tests/selenium/page_object/managerial_console/managerial_console.py index 273655a87a..f826add927 100644 --- a/tests/selenium/page_object/managerial_console/managerial_console.py +++ b/tests/selenium/page_object/managerial_console/managerial_console.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class ManagerialConsole(BaseComponents): pageHeaderTitle = 'h5[data-cy="page-header-title"]' diff --git a/tests/selenium/page_object/payment_module/new_payment_plan.py b/tests/selenium/page_object/payment_module/new_payment_plan.py index 19d79f7810..e27b34b382 100644 --- a/tests/selenium/page_object/payment_module/new_payment_plan.py +++ b/tests/selenium/page_object/payment_module/new_payment_plan.py @@ -1,7 +1,8 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class NewPaymentPlan(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/payment_module/payment_module.py b/tests/selenium/page_object/payment_module/payment_module.py index 5e8683d346..c2dfad805e 100644 --- a/tests/selenium/page_object/payment_module/payment_module.py +++ b/tests/selenium/page_object/payment_module/payment_module.py @@ -1,8 +1,9 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class PaymentModule(BaseComponents): pageHeaderTitle = 'h5[data-cy="page-header-title"]' diff --git a/tests/selenium/page_object/payment_module/payment_module_details.py b/tests/selenium/page_object/payment_module/payment_module_details.py index eb2794c306..8e7cba74ed 100644 --- a/tests/selenium/page_object/payment_module/payment_module_details.py +++ b/tests/selenium/page_object/payment_module/payment_module_details.py @@ -1,8 +1,9 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class PaymentModuleDetails(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/payment_module/program_cycle.py b/tests/selenium/page_object/payment_module/program_cycle.py index 9c9f938351..a65a281ee5 100644 --- a/tests/selenium/page_object/payment_module/program_cycle.py +++ b/tests/selenium/page_object/payment_module/program_cycle.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class ProgramCyclePage(BaseComponents): mainContent = 'div[data-cy="main-content"]' diff --git a/tests/selenium/page_object/payment_verification/payment_record.py b/tests/selenium/page_object/payment_verification/payment_record.py index 30738956d3..b85c2500ad 100644 --- a/tests/selenium/page_object/payment_verification/payment_record.py +++ b/tests/selenium/page_object/payment_verification/payment_record.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class PaymentRecord(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/payment_verification/payment_verification.py b/tests/selenium/page_object/payment_verification/payment_verification.py index b65f7f585d..dd1b88e2fe 100644 --- a/tests/selenium/page_object/payment_verification/payment_verification.py +++ b/tests/selenium/page_object/payment_verification/payment_verification.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class PaymentVerification(BaseComponents): # Locators diff --git a/tests/selenium/page_object/payment_verification/payment_verification_details.py b/tests/selenium/page_object/payment_verification/payment_verification_details.py index 03ba259f11..a0afa25be2 100644 --- a/tests/selenium/page_object/payment_verification/payment_verification_details.py +++ b/tests/selenium/page_object/payment_verification/payment_verification_details.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class PaymentVerificationDetails(BaseComponents): # Locators diff --git a/tests/selenium/page_object/people/people.py b/tests/selenium/page_object/people/people.py index 355eb26895..2708e9d866 100644 --- a/tests/selenium/page_object/people/people.py +++ b/tests/selenium/page_object/people/people.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class People(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/program_log/payment_log.py b/tests/selenium/page_object/program_log/payment_log.py index 9913ceea2c..3f5953e678 100644 --- a/tests/selenium/page_object/program_log/payment_log.py +++ b/tests/selenium/page_object/program_log/payment_log.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class ProgramLog(BaseComponents): mainActivityLogTable = 'div[data-cy="main-activity-log-table"]' diff --git a/tests/selenium/page_object/programme_details/programme_details.py b/tests/selenium/page_object/programme_details/programme_details.py index c9ebd352d5..f4b5d0ba45 100644 --- a/tests/selenium/page_object/programme_details/programme_details.py +++ b/tests/selenium/page_object/programme_details/programme_details.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class ProgrammeDetails(BaseComponents): headerTitle = 'h5[data-cy="page-header-title"]' diff --git a/tests/selenium/page_object/programme_management/programme_management.py b/tests/selenium/page_object/programme_management/programme_management.py index bc79c4c85b..298236911a 100644 --- a/tests/selenium/page_object/programme_management/programme_management.py +++ b/tests/selenium/page_object/programme_management/programme_management.py @@ -1,10 +1,11 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class ProgrammeManagement(BaseComponents): headerTitle = 'h5[data-cy="page-header-title"]' diff --git a/tests/selenium/page_object/programme_population/households.py b/tests/selenium/page_object/programme_population/households.py index 938ed53932..2c00db3f20 100644 --- a/tests/selenium/page_object/programme_population/households.py +++ b/tests/selenium/page_object/programme_population/households.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class Households(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/programme_population/households_details.py b/tests/selenium/page_object/programme_population/households_details.py index dec9e5ccd3..2eb43fada2 100644 --- a/tests/selenium/page_object/programme_population/households_details.py +++ b/tests/selenium/page_object/programme_population/households_details.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class HouseholdsDetails(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/programme_population/individuals.py b/tests/selenium/page_object/programme_population/individuals.py index c3dcbf8c04..204f78254b 100644 --- a/tests/selenium/page_object/programme_population/individuals.py +++ b/tests/selenium/page_object/programme_population/individuals.py @@ -1,8 +1,9 @@ from typing import Union -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class Individuals(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/programme_population/individuals_details.py b/tests/selenium/page_object/programme_population/individuals_details.py index 44c5e2e69f..86b349b800 100644 --- a/tests/selenium/page_object/programme_population/individuals_details.py +++ b/tests/selenium/page_object/programme_population/individuals_details.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class IndividualsDetails(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/programme_population/periodic_data_update_templates.py b/tests/selenium/page_object/programme_population/periodic_data_update_templates.py index 1aa7f13bf0..bfd700609a 100644 --- a/tests/selenium/page_object/programme_population/periodic_data_update_templates.py +++ b/tests/selenium/page_object/programme_population/periodic_data_update_templates.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class PeriodicDatUpdateTemplates(BaseComponents): navProgramPopulation = 'a[data-cy="nav-Programme Population"]' diff --git a/tests/selenium/page_object/programme_population/periodic_data_update_uploads.py b/tests/selenium/page_object/programme_population/periodic_data_update_uploads.py index 66d646689e..285779f314 100644 --- a/tests/selenium/page_object/programme_population/periodic_data_update_uploads.py +++ b/tests/selenium/page_object/programme_population/periodic_data_update_uploads.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class PeriodicDataUpdateUploads(BaseComponents): navProgramPopulation = 'a[data-cy="nav-Programme Population"]' diff --git a/tests/selenium/page_object/programme_users/programme_users.py b/tests/selenium/page_object/programme_users/programme_users.py index 50da539152..db62c5db26 100644 --- a/tests/selenium/page_object/programme_users/programme_users.py +++ b/tests/selenium/page_object/programme_users/programme_users.py @@ -1,6 +1,7 @@ -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class ProgrammeUsers(BaseComponents): pageHeaderTitle = 'h5[data-cy="page-header-title"]' diff --git a/tests/selenium/page_object/registration_data_import/rdi_details_page.py b/tests/selenium/page_object/registration_data_import/rdi_details_page.py index d29fbc4669..2d8eca8ad3 100644 --- a/tests/selenium/page_object/registration_data_import/rdi_details_page.py +++ b/tests/selenium/page_object/registration_data_import/rdi_details_page.py @@ -1,9 +1,10 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class RDIDetailsPage(BaseComponents): pageHeaderContainer = 'div[data-cy="page-header-container"]' diff --git a/tests/selenium/page_object/registration_data_import/registration_data_import.py b/tests/selenium/page_object/registration_data_import/registration_data_import.py index eb2bed3376..814b3263aa 100644 --- a/tests/selenium/page_object/registration_data_import/registration_data_import.py +++ b/tests/selenium/page_object/registration_data_import/registration_data_import.py @@ -1,8 +1,9 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class RegistrationDataImport(BaseComponents): # Locators diff --git a/tests/selenium/page_object/targeting/targeting.py b/tests/selenium/page_object/targeting/targeting.py index 7212ed41df..4083e76b48 100644 --- a/tests/selenium/page_object/targeting/targeting.py +++ b/tests/selenium/page_object/targeting/targeting.py @@ -1,10 +1,10 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement from hct_mis_api.apps.core.utils import encode_id_base64 +from tests.selenium.page_object.base_components import BaseComponents class Targeting(BaseComponents): diff --git a/tests/selenium/page_object/targeting/targeting_create.py b/tests/selenium/page_object/targeting/targeting_create.py index d2f19681a5..1c9c938ca1 100644 --- a/tests/selenium/page_object/targeting/targeting_create.py +++ b/tests/selenium/page_object/targeting/targeting_create.py @@ -1,8 +1,9 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class TargetingCreate(BaseComponents): # Locators diff --git a/tests/selenium/page_object/targeting/targeting_details.py b/tests/selenium/page_object/targeting/targeting_details.py index 981f7059cc..f1f03bd0d6 100644 --- a/tests/selenium/page_object/targeting/targeting_details.py +++ b/tests/selenium/page_object/targeting/targeting_details.py @@ -1,8 +1,9 @@ from time import sleep -from tests.selenium.page_object.base_components import BaseComponents from selenium.webdriver.remote.webelement import WebElement +from tests.selenium.page_object.base_components import BaseComponents + class TargetingDetails(BaseComponents): # Locators diff --git a/tests/selenium/payment_module/test_payment_plans.py b/tests/selenium/payment_module/test_payment_plans.py index c01bc99975..08693d949b 100644 --- a/tests/selenium/payment_module/test_payment_plans.py +++ b/tests/selenium/payment_module/test_payment_plans.py @@ -6,16 +6,8 @@ import openpyxl import pytest from dateutil.relativedelta import relativedelta -from sorl.thumbnail.conf import settings - -from tests.selenium.page_object.payment_module.new_payment_plan import NewPaymentPlan -from tests.selenium.page_object.payment_module.payment_module import PaymentModule -from tests.selenium.page_object.payment_module.payment_module_details import PaymentModuleDetails -from tests.selenium.page_object.payment_module.program_cycle import ( - ProgramCycleDetailsPage, - ProgramCyclePage, -) from selenium.webdriver.common.by import By +from sorl.thumbnail.conf import settings from hct_mis_api.apps.account.models import User from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory @@ -42,6 +34,15 @@ ) from hct_mis_api.apps.targeting.models import TargetPopulation from tests.selenium.helpers.date_time_format import FormatTime +from tests.selenium.page_object.payment_module.new_payment_plan import NewPaymentPlan +from tests.selenium.page_object.payment_module.payment_module import PaymentModule +from tests.selenium.page_object.payment_module.payment_module_details import ( + PaymentModuleDetails, +) +from tests.selenium.page_object.payment_module.program_cycle import ( + ProgramCycleDetailsPage, + ProgramCyclePage, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/payment_module/test_program_cycles.py b/tests/selenium/payment_module/test_program_cycles.py index 1e59cd666b..dca1769a61 100644 --- a/tests/selenium/payment_module/test_program_cycles.py +++ b/tests/selenium/payment_module/test_program_cycles.py @@ -2,7 +2,6 @@ import pytest from dateutil.relativedelta import relativedelta -from tests.selenium.page_object.payment_module.program_cycle import ProgramCyclePage from selenium.webdriver.common.by import By from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory @@ -10,6 +9,7 @@ from hct_mis_api.apps.payment.fixtures import PaymentPlanFactory from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program, ProgramCycle +from tests.selenium.page_object.payment_module.program_cycle import ProgramCyclePage pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/payment_verification/test_payment_verification.py b/tests/selenium/payment_verification/test_payment_verification.py index 707b1d97ba..086c14fbf1 100644 --- a/tests/selenium/payment_verification/test_payment_verification.py +++ b/tests/selenium/payment_verification/test_payment_verification.py @@ -3,11 +3,6 @@ import pytest from dateutil.relativedelta import relativedelta -from tests.selenium.page_object.payment_verification.payment_record import PaymentRecord -from tests.selenium.page_object.payment_verification.payment_verification import PaymentVerification -from tests.selenium.page_object.payment_verification.payment_verification_details import ( - PaymentVerificationDetails, -) from selenium.webdriver.common.by import By from hct_mis_api.apps.account.models import User @@ -32,6 +27,13 @@ TargetingCriteriaFactory, TargetPopulationFactory, ) +from tests.selenium.page_object.payment_verification.payment_record import PaymentRecord +from tests.selenium.page_object.payment_verification.payment_verification import ( + PaymentVerification, +) +from tests.selenium.page_object.payment_verification.payment_verification_details import ( + PaymentVerificationDetails, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/people/test_people.py b/tests/selenium/people/test_people.py index b2abc03a2e..d3cd69b48b 100644 --- a/tests/selenium/people/test_people.py +++ b/tests/selenium/people/test_people.py @@ -5,7 +5,6 @@ import pytest from dateutil.relativedelta import relativedelta -from tests.selenium.page_object.people.people import People from selenium.webdriver.common.by import By from hct_mis_api.apps.account.models import User @@ -30,6 +29,7 @@ ) from tests.selenium.page_object.grievance.grievance_tickets import GrievanceTickets from tests.selenium.page_object.grievance.new_ticket import NewTicket +from tests.selenium.page_object.people.people import People from tests.selenium.page_object.people.people_details import PeopleDetails pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/people/test_people_periodic_data_update.py b/tests/selenium/people/test_people_periodic_data_update.py index 2956a6017a..c07729e89b 100644 --- a/tests/selenium/people/test_people_periodic_data_update.py +++ b/tests/selenium/people/test_people_periodic_data_update.py @@ -2,9 +2,10 @@ from datetime import datetime from time import sleep +from django.conf import settings + import pytest from dateutil.relativedelta import relativedelta -from django.conf import settings from hct_mis_api.apps.account.models import User from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory, create_afghanistan diff --git a/tests/selenium/program_details/test_program_details.py b/tests/selenium/program_details/test_program_details.py index dd9a30ef44..a87668f885 100644 --- a/tests/selenium/program_details/test_program_details.py +++ b/tests/selenium/program_details/test_program_details.py @@ -7,9 +7,6 @@ import pytest from dateutil.relativedelta import relativedelta -from tests.selenium.helpers.date_time_format import FormatTime -from tests.selenium.page_object.programme_details.programme_details import ProgrammeDetails -from tests.selenium.page_object.programme_management.programme_management import ProgrammeManagement from selenium.webdriver import Keys from hct_mis_api.apps.account.models import User @@ -28,6 +25,13 @@ TargetPopulationFactory, ) from hct_mis_api.apps.targeting.models import TargetPopulation +from tests.selenium.helpers.date_time_format import FormatTime +from tests.selenium.page_object.programme_details.programme_details import ( + ProgrammeDetails, +) +from tests.selenium.page_object.programme_management.programme_management import ( + ProgrammeManagement, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/program_log/test_program_log.py b/tests/selenium/program_log/test_program_log.py index 252908f4e6..8c3ab19df5 100644 --- a/tests/selenium/program_log/test_program_log.py +++ b/tests/selenium/program_log/test_program_log.py @@ -1,12 +1,14 @@ from datetime import datetime import pytest -from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name -from tests.selenium.page_object.program_log.payment_log import ProgramLog -from tests.selenium.page_object.programme_details.programme_details import ProgrammeDetails from hct_mis_api.apps.account.models import User from hct_mis_api.apps.program.models import Program +from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name +from tests.selenium.page_object.program_log.payment_log import ProgramLog +from tests.selenium.page_object.programme_details.programme_details import ( + ProgrammeDetails, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/programme_management/test_programme_management.py b/tests/selenium/programme_management/test_programme_management.py index dc39300971..dd8b2a0289 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -7,10 +7,6 @@ import pytest from dateutil.relativedelta import relativedelta -from freezegun import freeze_time -from tests.selenium.helpers.date_time_format import FormatTime -from tests.selenium.page_object.programme_details.programme_details import ProgrammeDetails -from tests.selenium.page_object.programme_management.programme_management import ProgrammeManagement from selenium import webdriver from selenium.webdriver import Keys from selenium.webdriver.common.by import By @@ -20,6 +16,13 @@ from hct_mis_api.apps.core.models import BusinessArea, BusinessAreaPartnerThrough from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory +from tests.selenium.helpers.date_time_format import FormatTime +from tests.selenium.page_object.programme_details.programme_details import ( + ProgrammeDetails, +) +from tests.selenium.page_object.programme_management.programme_management import ( + ProgrammeManagement, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/programme_population/test_households.py b/tests/selenium/programme_population/test_households.py index 22e0f2dcc9..a8f57e54db 100644 --- a/tests/selenium/programme_population/test_households.py +++ b/tests/selenium/programme_population/test_households.py @@ -2,8 +2,11 @@ from django.core.management import call_command import pytest + from tests.selenium.page_object.programme_population.households import Households -from tests.selenium.page_object.programme_population.households_details import HouseholdsDetails +from tests.selenium.page_object.programme_population.households_details import ( + HouseholdsDetails, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/programme_population/test_individuals.py b/tests/selenium/programme_population/test_individuals.py index b995ef2754..5b3456efd2 100644 --- a/tests/selenium/programme_population/test_individuals.py +++ b/tests/selenium/programme_population/test_individuals.py @@ -3,8 +3,11 @@ import pytest from freezegun import freeze_time + from tests.selenium.page_object.programme_population.individuals import Individuals -from tests.selenium.page_object.programme_population.individuals_details import IndividualsDetails +from tests.selenium.page_object.programme_population.individuals_details import ( + IndividualsDetails, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/programme_population/test_periodic_data_templates.py b/tests/selenium/programme_population/test_periodic_data_templates.py index a435a34dac..3710661764 100644 --- a/tests/selenium/programme_population/test_periodic_data_templates.py +++ b/tests/selenium/programme_population/test_periodic_data_templates.py @@ -1,13 +1,9 @@ import os from time import sleep -import pytest from django.conf import settings -from tests.selenium.page_object.programme_population.periodic_data_update_templates import ( - PeriodicDatUpdateTemplates, - PeriodicDatUpdateTemplatesDetails, -) +import pytest from selenium.webdriver.common.by import By from hct_mis_api.apps.core.fixtures import create_afghanistan @@ -27,6 +23,10 @@ from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory from hct_mis_api.apps.registration_data.models import RegistrationDataImport from tests.selenium.page_object.programme_population.individuals import Individuals +from tests.selenium.page_object.programme_population.periodic_data_update_templates import ( + PeriodicDatUpdateTemplates, + PeriodicDatUpdateTemplatesDetails, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/programme_population/test_periodic_data_update_upload.py b/tests/selenium/programme_population/test_periodic_data_update_upload.py index e827e73e0b..cf62007c2b 100644 --- a/tests/selenium/programme_population/test_periodic_data_update_upload.py +++ b/tests/selenium/programme_population/test_periodic_data_update_upload.py @@ -2,16 +2,10 @@ from tempfile import NamedTemporaryFile, _TemporaryFileWrapper from typing import Any -import openpyxl -import pytest from django.conf import settings -from tests.selenium.page_object.programme_population.periodic_data_update_templates import ( - PeriodicDatUpdateTemplates, -) -from tests.selenium.page_object.programme_population.periodic_data_update_uploads import ( - PeriodicDataUpdateUploads, -) +import openpyxl +import pytest from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory, create_afghanistan from hct_mis_api.apps.core.models import ( @@ -40,6 +34,12 @@ from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory from tests.selenium.page_object.programme_population.individuals import Individuals +from tests.selenium.page_object.programme_population.periodic_data_update_templates import ( + PeriodicDatUpdateTemplates, +) +from tests.selenium.page_object.programme_population.periodic_data_update_uploads import ( + PeriodicDataUpdateUploads, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/programme_user/test_programme_user.py b/tests/selenium/programme_user/test_programme_user.py index 87399502ac..996fd1350f 100644 --- a/tests/selenium/programme_user/test_programme_user.py +++ b/tests/selenium/programme_user/test_programme_user.py @@ -1,9 +1,9 @@ import pytest -from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name -from tests.selenium.page_object.programme_users.programme_users import ProgrammeUsers from hct_mis_api.apps.core.models import DataCollectingType from hct_mis_api.apps.program.models import Program +from tests.selenium.helpers.fixtures import get_program_with_dct_type_and_name +from tests.selenium.page_object.programme_users.programme_users import ProgrammeUsers pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/registration_data_import/test_registration_data_import.py b/tests/selenium/registration_data_import/test_registration_data_import.py index 7925ee11e9..e63c0bf912 100644 --- a/tests/selenium/registration_data_import/test_registration_data_import.py +++ b/tests/selenium/registration_data_import/test_registration_data_import.py @@ -3,17 +3,21 @@ import pytest from elasticsearch_dsl import connections -from tests.selenium.page_object.programme_population.households_details import HouseholdsDetails -from tests.selenium.page_object.registration_data_import.rdi_details_page import RDIDetailsPage -from tests.selenium.page_object.registration_data_import.registration_data_import import ( - RegistrationDataImport, -) from hct_mis_api.apps.account.fixtures import PartnerFactory from hct_mis_api.apps.account.models import Partner from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.geo.models import Area, AreaType, Country from hct_mis_api.apps.utils.elasticsearch_utils import rebuild_search_index +from tests.selenium.page_object.programme_population.households_details import ( + HouseholdsDetails, +) +from tests.selenium.page_object.registration_data_import.rdi_details_page import ( + RDIDetailsPage, +) +from tests.selenium.page_object.registration_data_import.registration_data_import import ( + RegistrationDataImport, +) pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/selenium/targeting/test_targeting.py b/tests/selenium/targeting/test_targeting.py index a76c25b2e7..5687670633 100644 --- a/tests/selenium/targeting/test_targeting.py +++ b/tests/selenium/targeting/test_targeting.py @@ -7,9 +7,6 @@ import pytest from dateutil.relativedelta import relativedelta -from tests.selenium.page_object.targeting.targeting import Targeting -from tests.selenium.page_object.targeting.targeting_create import TargetingCreate -from tests.selenium.page_object.targeting.targeting_details import TargetingDetails from selenium.common import NoSuchElementException from selenium.webdriver import ActionChains, Keys from selenium.webdriver.common.by import By @@ -44,6 +41,9 @@ from hct_mis_api.apps.targeting.fixtures import TargetingCriteriaFactory from hct_mis_api.apps.targeting.models import TargetPopulation from tests.selenium.page_object.filters import Filters +from tests.selenium.page_object.targeting.targeting import Targeting +from tests.selenium.page_object.targeting.targeting_create import TargetingCreate +from tests.selenium.page_object.targeting.targeting_details import TargetingDetails pytestmark = pytest.mark.django_db(transaction=True) diff --git a/tests/unit/api/base.py b/tests/unit/api/base.py index fc9f9af6ea..f13b28ad72 100644 --- a/tests/unit/api/base.py +++ b/tests/unit/api/base.py @@ -4,13 +4,13 @@ from rest_framework.test import APITestCase from hct_mis_api.api.models import APIToken, Grant -from tests.unit.api.factories import APITokenFactory from hct_mis_api.apps.account.fixtures import ( BusinessAreaFactory, RoleFactory, UserFactory, ) from hct_mis_api.apps.core.models import BusinessArea +from tests.unit.api.factories import APITokenFactory class HOPEApiTestCase(APITestCase): diff --git a/tests/unit/api/test_auth.py b/tests/unit/api/test_auth.py index 781ee862d7..c017532516 100644 --- a/tests/unit/api/test_auth.py +++ b/tests/unit/api/test_auth.py @@ -8,13 +8,13 @@ from hct_mis_api.api.auth import HOPEAuthentication, HOPEPermission from hct_mis_api.api.models import APIToken, Grant -from tests.unit.api.base import HOPEApiTestCase -from tests.unit.api.factories import APITokenFactory from hct_mis_api.apps.account.fixtures import ( BusinessAreaFactory, RoleFactory, UserFactory, ) +from tests.unit.api.base import HOPEApiTestCase +from tests.unit.api.factories import APITokenFactory class HOPEPermissionTest(TestCase): diff --git a/tests/unit/api/test_business_area.py b/tests/unit/api/test_business_area.py index 2434e8cf86..5367be30b4 100644 --- a/tests/unit/api/test_business_area.py +++ b/tests/unit/api/test_business_area.py @@ -4,9 +4,9 @@ from rest_framework.reverse import reverse from hct_mis_api.api.models import APIToken, Grant -from tests.unit.api.base import HOPEApiTestCase from hct_mis_api.apps.account.fixtures import BusinessAreaFactory from hct_mis_api.apps.core.models import BusinessArea +from tests.unit.api.base import HOPEApiTestCase @contextlib.contextmanager diff --git a/tests/unit/api/test_delegate_people.py b/tests/unit/api/test_delegate_people.py index d014376d6f..72883f351f 100644 --- a/tests/unit/api/test_delegate_people.py +++ b/tests/unit/api/test_delegate_people.py @@ -7,7 +7,6 @@ from rest_framework.reverse import reverse from hct_mis_api.api.models import Grant -from tests.unit.api.base import HOPEApiTestCase from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory from hct_mis_api.apps.core.models import DataCollectingType from hct_mis_api.apps.core.utils import IDENTIFICATION_TYPE_TO_KEY_MAPPING @@ -22,6 +21,7 @@ from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.models import RegistrationDataImport +from tests.unit.api.base import HOPEApiTestCase class TestDelegatePeople(HOPEApiTestCase): diff --git a/tests/unit/api/test_program.py b/tests/unit/api/test_program.py index dfe43248a9..1f006f50ba 100644 --- a/tests/unit/api/test_program.py +++ b/tests/unit/api/test_program.py @@ -4,11 +4,11 @@ from rest_framework.reverse import reverse from hct_mis_api.api.models import APIToken, Grant -from tests.unit.api.base import HOPEApiTestCase from hct_mis_api.apps.account.fixtures import BusinessAreaFactory from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program +from tests.unit.api.base import HOPEApiTestCase @contextlib.contextmanager diff --git a/tests/unit/api/test_push_people.py b/tests/unit/api/test_push_people.py index 7e1dfb3a63..cbfa9c33f1 100644 --- a/tests/unit/api/test_push_people.py +++ b/tests/unit/api/test_push_people.py @@ -5,7 +5,6 @@ from rest_framework.reverse import reverse from hct_mis_api.api.models import Grant -from tests.unit.api.base import HOPEApiTestCase from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory from hct_mis_api.apps.core.models import DataCollectingType from hct_mis_api.apps.core.utils import IDENTIFICATION_TYPE_TO_KEY_MAPPING @@ -24,6 +23,7 @@ from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.models import RegistrationDataImport +from tests.unit.api.base import HOPEApiTestCase class TestPushPeople(HOPEApiTestCase): diff --git a/tests/unit/api/test_rdi.py b/tests/unit/api/test_rdi.py index 0bbb29c129..c4a8c3bed9 100644 --- a/tests/unit/api/test_rdi.py +++ b/tests/unit/api/test_rdi.py @@ -8,7 +8,6 @@ from rest_framework.reverse import reverse from hct_mis_api.api.models import Grant -from tests.unit.api.base import HOPEApiTestCase from hct_mis_api.apps.core.utils import IDENTIFICATION_TYPE_TO_KEY_MAPPING from hct_mis_api.apps.household.models import ( COLLECT_TYPE_FULL, @@ -25,6 +24,7 @@ ) from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.models import RegistrationDataImport +from tests.unit.api.base import HOPEApiTestCase class CreateRDITests(HOPEApiTestCase): diff --git a/tests/unit/api/test_soft.py b/tests/unit/api/test_soft.py index 069e913e67..c5d4c40a2e 100644 --- a/tests/unit/api/test_soft.py +++ b/tests/unit/api/test_soft.py @@ -8,7 +8,6 @@ from rest_framework import status from hct_mis_api.api.models import Grant -from tests.unit.api.base import HOPEApiTestCase from hct_mis_api.apps.core.utils import IDENTIFICATION_TYPE_TO_KEY_MAPPING from hct_mis_api.apps.household.models import ( COLLECT_TYPE_FULL, @@ -24,6 +23,7 @@ from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.models import RegistrationDataImport +from tests.unit.api.base import HOPEApiTestCase class PushLaxToRDITests(HOPEApiTestCase): diff --git a/tests/unit/api/test_upload.py b/tests/unit/api/test_upload.py index 6af798e0bf..f7db0431b5 100644 --- a/tests/unit/api/test_upload.py +++ b/tests/unit/api/test_upload.py @@ -7,7 +7,6 @@ from rest_framework.reverse import reverse from hct_mis_api.api.models import Grant -from tests.unit.api.base import HOPEApiTestCase from hct_mis_api.apps.core.utils import IDENTIFICATION_TYPE_TO_KEY_MAPPING from hct_mis_api.apps.household.models import ( COLLECT_TYPE_FULL, @@ -25,6 +24,7 @@ from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.models import RegistrationDataImport +from tests.unit.api.base import HOPEApiTestCase class UploadRDITests(HOPEApiTestCase): diff --git a/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py b/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py index f820e0301d..48cafbd0f4 100644 --- a/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py +++ b/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py @@ -28,9 +28,6 @@ ) from hct_mis_api.apps.core.fixtures import create_afghanistan from hct_mis_api.apps.core.models import BusinessArea -from tests.unit.apps.core.test_exchange_rates import ( - EXCHANGE_RATES_WITH_HISTORICAL_DATA, -) from hct_mis_api.apps.household.fixtures import create_household from hct_mis_api.apps.payment.delivery_mechanisms import DeliveryMechanismChoices from hct_mis_api.apps.payment.fixtures import generate_delivery_mechanisms @@ -46,6 +43,7 @@ ) from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.targeting.models import TargetPopulation +from tests.unit.apps.core.test_exchange_rates import EXCHANGE_RATES_WITH_HISTORICAL_DATA class DummyExchangeRates: diff --git a/tests/unit/apps/core/test_exchange_rates.py b/tests/unit/apps/core/test_exchange_rates.py index 524e3c5a3f..6312604c00 100644 --- a/tests/unit/apps/core/test_exchange_rates.py +++ b/tests/unit/apps/core/test_exchange_rates.py @@ -14,9 +14,6 @@ from hct_mis_api.apps.core.exchange_rates import ExchangeRateClientAPI, ExchangeRates from hct_mis_api.apps.core.exchange_rates.api import ExchangeRateClientDummy from hct_mis_api.apps.core.models import BusinessArea -from tests.unit.apps.core.test_files.exchange_rates_api_response import ( - EXCHANGE_RATES_API_RESPONSE, -) from hct_mis_api.apps.household.fixtures import create_household from hct_mis_api.apps.payment.fixtures import ( RealCashPlanFactory, @@ -25,6 +22,9 @@ ServiceProviderFactory, ) from hct_mis_api.apps.payment.models import PaymentRecord +from tests.unit.apps.core.test_files.exchange_rates_api_response import ( + EXCHANGE_RATES_API_RESPONSE, +) EXCHANGE_RATES_WITH_HISTORICAL_DATA = { "ROWSET": { diff --git a/tests/unit/apps/grievance/test_approve_automatic_tickets.py b/tests/unit/apps/grievance/test_approve_automatic_tickets.py index 42966cfcbb..2147327fbb 100644 --- a/tests/unit/apps/grievance/test_approve_automatic_tickets.py +++ b/tests/unit/apps/grievance/test_approve_automatic_tickets.py @@ -22,7 +22,6 @@ from hct_mis_api.apps.grievance.models import GrievanceTicket from hct_mis_api.apps.household.fixtures import HouseholdFactory, IndividualFactory from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.apps.registration_data.models import DeduplicationEngineSimilarityPair from hct_mis_api.apps.sanction_list.models import SanctionListIndividual diff --git a/tests/unit/apps/grievance/test_create_needs_adjudication_tickets.py b/tests/unit/apps/grievance/test_create_needs_adjudication_tickets.py index 6ec9df520f..1e6e1c8e19 100644 --- a/tests/unit/apps/grievance/test_create_needs_adjudication_tickets.py +++ b/tests/unit/apps/grievance/test_create_needs_adjudication_tickets.py @@ -110,9 +110,7 @@ def test_create_needs_adjudication_ticket_with_the_same_ind(self) -> None: "duplicates": [{"hit_id": str(ind_2.pk)}], "possible_duplicates": [{"hit_id": str(ind_2.pk)}], } - ind.deduplication_golden_record_results = ( - deduplication_golden_record_results_data - ) + ind.deduplication_golden_record_results = deduplication_golden_record_results_data ind_2.deduplication_golden_record_results = { "duplicates": [], "possible_duplicates": [], @@ -216,22 +214,18 @@ def setUpTestData(cls) -> None: cls.ind1, cls.ind2 = sorted(individuals, key=lambda x: x.id) cls.ind3, cls.ind4 = sorted([cls.ind1, other_individual], key=lambda x: x.id) - cls.dedup_engine_similarity_pair = ( - DeduplicationEngineSimilarityPair.objects.create( - program=program, - individual1=cls.ind1, - individual2=cls.ind2, - similarity_score=55.55, - ) + cls.dedup_engine_similarity_pair = DeduplicationEngineSimilarityPair.objects.create( + program=program, + individual1=cls.ind1, + individual2=cls.ind2, + similarity_score=55.55, ) - cls.dedup_engine_similarity_pair_2 = ( - DeduplicationEngineSimilarityPair.objects.create( - program=program, - individual1=cls.ind3, - individual2=cls.ind4, - similarity_score=75.25, - ) + cls.dedup_engine_similarity_pair_2 = DeduplicationEngineSimilarityPair.objects.create( + program=program, + individual1=cls.ind3, + individual2=cls.ind4, + similarity_score=75.25, ) def test_create_na_tickets_biometrics(self) -> None: @@ -239,17 +233,13 @@ def test_create_na_tickets_biometrics(self) -> None: self.assertEqual(TicketNeedsAdjudicationDetails.objects.all().count(), 0) self.assertIsNone(self.rdi.deduplication_engine_status) - create_needs_adjudication_tickets_for_biometrics( - DeduplicationEngineSimilarityPair.objects.none(), self.rdi - ) + create_needs_adjudication_tickets_for_biometrics(DeduplicationEngineSimilarityPair.objects.none(), self.rdi) self.assertEqual(GrievanceTicket.objects.all().count(), 0) self.assertEqual(TicketNeedsAdjudicationDetails.objects.all().count(), 0) self.assertEqual(DeduplicationEngineSimilarityPair.objects.all().count(), 2) create_needs_adjudication_tickets_for_biometrics( - DeduplicationEngineSimilarityPair.objects.filter( - pk=self.dedup_engine_similarity_pair.pk - ), + DeduplicationEngineSimilarityPair.objects.filter(pk=self.dedup_engine_similarity_pair.pk), self.rdi, ) @@ -258,9 +248,7 @@ def test_create_na_tickets_biometrics(self) -> None: grievance_ticket = GrievanceTicket.objects.first() na_ticket = TicketNeedsAdjudicationDetails.objects.first() - self.assertEqual( - grievance_ticket.category, GrievanceTicket.CATEGORY_NEEDS_ADJUDICATION - ) + self.assertEqual(grievance_ticket.category, GrievanceTicket.CATEGORY_NEEDS_ADJUDICATION) self.assertEqual( grievance_ticket.issue_type, GrievanceTicket.ISSUE_TYPE_BIOMETRICS_SIMILARITY, @@ -274,18 +262,14 @@ def test_create_na_tickets_biometrics(self) -> None: # different RDI create_needs_adjudication_tickets_for_biometrics( - DeduplicationEngineSimilarityPair.objects.filter( - pk=self.dedup_engine_similarity_pair_2.pk - ), + DeduplicationEngineSimilarityPair.objects.filter(pk=self.dedup_engine_similarity_pair_2.pk), self.rdi, ) self.assertEqual(GrievanceTicket.objects.all().count(), 2) self.assertEqual(TicketNeedsAdjudicationDetails.objects.all().count(), 2) # run one time create_needs_adjudication_tickets_for_biometrics( - DeduplicationEngineSimilarityPair.objects.filter( - pk=self.dedup_engine_similarity_pair_2.pk - ), + DeduplicationEngineSimilarityPair.objects.filter(pk=self.dedup_engine_similarity_pair_2.pk), self.rdi, ) self.assertEqual(GrievanceTicket.objects.all().count(), 2) diff --git a/tests/unit/apps/grievance/test_services_utils.py b/tests/unit/apps/grievance/test_services_utils.py index 3ca9cd72fd..08198b31f6 100644 --- a/tests/unit/apps/grievance/test_services_utils.py +++ b/tests/unit/apps/grievance/test_services_utils.py @@ -8,8 +8,6 @@ import pytest -from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory -from hct_mis_api.apps.registration_data.models import DeduplicationEngineSimilarityPair from hct_mis_api.apps.account.fixtures import ( BusinessAreaFactory, PartnerFactory, @@ -58,6 +56,8 @@ IndividualRoleInHousehold, ) 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 DeduplicationEngineSimilarityPair from hct_mis_api.apps.utils.models import MergeStatusModel @@ -487,7 +487,9 @@ def test_close_needs_adjudication_ticket_service_when_just_duplicates(self) -> N @patch( "hct_mis_api.apps.registration_datahub.services.biometric_deduplication.BiometricDeduplicationService.report_false_positive_duplicate" ) - def test_close_needs_adjudication_ticket_service_for_biometrics(self, report_false_positive_duplicate_mock) -> None: + def test_close_needs_adjudication_ticket_service_for_biometrics( + self, report_false_positive_duplicate_mock: MagicMock + ) -> None: user = UserFactory() ba = BusinessAreaFactory(slug="afghanistan") deduplication_set_id = uuid.uuid4() @@ -535,6 +537,8 @@ def test_close_needs_adjudication_ticket_service_for_biometrics(self, report_fal similarity_score=90.55, ), ) + if not ticket: + raise ValueError("Ticket not created") ticket.registration_data_import = rdi ticket.save() diff --git a/tests/unit/apps/household/test_individual_iban_xlsx_update.py b/tests/unit/apps/household/test_individual_iban_xlsx_update.py index dcaa66d0e2..4d6eac522f 100644 --- a/tests/unit/apps/household/test_individual_iban_xlsx_update.py +++ b/tests/unit/apps/household/test_individual_iban_xlsx_update.py @@ -37,23 +37,17 @@ def valid_file() -> File: def invalid_file_no_match() -> File: - content = Path( - f"{settings.TESTS_ROOT}/apps/household/test_file/iban_update_invalid_no_match.xlsx" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/household/test_file/iban_update_invalid_no_match.xlsx").read_bytes() return File(BytesIO(content), name="iban_update_invalid_no_match.xlsx") def invalid_file_empty_cell() -> File: - content = Path( - f"{settings.TESTS_ROOT}/apps/household/test_file/iban_update_invalid_empty_cell.xlsx" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/household/test_file/iban_update_invalid_empty_cell.xlsx").read_bytes() return File(BytesIO(content), name="iban_update_invalid_empty_cell.xlsx") def invalid_file_bad_columns() -> File: - content = Path( - f"{settings.TESTS_ROOT}/apps/household/test_file/iban_update_invalid_bad_columns.xlsx" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/household/test_file/iban_update_invalid_bad_columns.xlsx").read_bytes() return File(BytesIO(content), name="iban_update_invalid_bad_columns.xlsx") diff --git a/tests/unit/apps/household/test_individual_query.py b/tests/unit/apps/household/test_individual_query.py index 10269047d7..36d82ba38a 100644 --- a/tests/unit/apps/household/test_individual_query.py +++ b/tests/unit/apps/household/test_individual_query.py @@ -5,11 +5,6 @@ from constance.test import override_config from parameterized import parameterized -from hct_mis_api.apps.payment.fixtures import ( - generate_delivery_mechanisms, - DeliveryMechanismDataFactory, -) -from hct_mis_api.apps.payment.models import DeliveryMechanism from hct_mis_api.apps.account.fixtures import ( BusinessAreaFactory, PartnerFactory, @@ -35,6 +30,11 @@ create_household_and_individuals, ) from hct_mis_api.apps.household.models import DocumentType, Individual +from hct_mis_api.apps.payment.fixtures import ( + DeliveryMechanismDataFactory, + generate_delivery_mechanisms, +) +from hct_mis_api.apps.payment.models import DeliveryMechanism from hct_mis_api.apps.periodic_data_update.utils import populate_pdu_with_null_values from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program @@ -118,9 +118,7 @@ def setUpTestData(cls) -> None: data_collecting_type=partial, ) - cls.household_one = HouseholdFactory.build( - business_area=cls.business_area, program=cls.program - ) + cls.household_one = HouseholdFactory.build(business_area=cls.business_area, program=cls.program) cls.household_one.household_collection.save() cls.household_one.registration_data_import.imported_by.save() cls.household_one.registration_data_import.program = cls.program @@ -187,23 +185,17 @@ def setUpTestData(cls) -> None: ] cls.individuals = [ - IndividualFactory( - household=cls.household_one, program=cls.program, **individual - ) + IndividualFactory(household=cls.household_one, program=cls.program, **individual) for index, individual in enumerate(cls.individuals_to_create) ] - cls.individuals_from_hh_one = [ - ind for ind in cls.individuals if ind.household == cls.household_one - ] + cls.individuals_from_hh_one = [ind for ind in cls.individuals if ind.household == cls.household_one] # cls.individuals_from_hh_two = [ind for ind in cls.individuals if ind.household == household_two] cls.household_one.head_of_household = cls.individuals_from_hh_one[0] # household_two.head_of_household = cls.individuals_from_hh_two[1] cls.household_one.save() # individual in program that cls.user does not have access to - cls.household_2 = HouseholdFactory.build( - business_area=cls.business_area, program=cls.program - ) + cls.household_2 = HouseholdFactory.build(business_area=cls.business_area, program=cls.program) cls.household_2.household_collection.save() cls.household_2.registration_data_import.imported_by.save() cls.household_2.registration_data_import.program = cls.program @@ -230,12 +222,8 @@ def setUpTestData(cls) -> None: bank_account_number=11110000222255558888999925, ) - cls.individual_unicef_id_to_search = Individual.objects.get( - full_name="Benjamin Butler" - ).unicef_id - cls.household_unicef_id_to_search = Individual.objects.get( - full_name="Benjamin Butler" - ).household.unicef_id + cls.individual_unicef_id_to_search = Individual.objects.get(full_name="Benjamin Butler").unicef_id + cls.household_unicef_id_to_search = Individual.objects.get(full_name="Benjamin Butler").household.unicef_id DocumentTypeFactory(key="national_id") DocumentTypeFactory(key="national_passport") @@ -295,9 +283,7 @@ def setUpTestData(cls) -> None: area_level=2, ) - cls.area1 = AreaFactory( - name="City Test1", area_type=area_type_level_1, p_code="area1" - ) + cls.area1 = AreaFactory(name="City Test1", area_type=area_type_level_1, p_code="area1") cls.area2 = AreaFactory( name="City Test2", area_type=area_type_level_2, @@ -307,12 +293,8 @@ def setUpTestData(cls) -> None: cls.household_one.set_admin_areas(cls.area2) - cls.update_partner_access_to_program( - cls.partner, cls.program, [cls.household_one.admin_area] - ) - cls.update_partner_access_to_program( - cls.partner, cls.program_draft, [cls.household_one.admin_area] - ) + cls.update_partner_access_to_program(cls.partner, cls.program, [cls.household_one.admin_area]) + cls.update_partner_access_to_program(cls.partner, cls.program_draft, [cls.household_one.admin_area]) # remove after data migration migrate_data_to_representations() @@ -326,9 +308,7 @@ def setUpTestData(cls) -> None: ] ) def test_individual_query_all(self, _: Any, permissions: List[Permissions]) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) self.snapshot_graphql_request( request_string=self.ALL_INDIVIDUALS_QUERY, @@ -347,12 +327,8 @@ def test_individual_query_all(self, _: Any, permissions: List[Permissions]) -> N ("without_permission", []), ] ) - def test_individual_query_single( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_individual_query_single(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) self.snapshot_graphql_request( request_string=self.INDIVIDUAL_QUERY, @@ -363,9 +339,7 @@ def test_individual_query_single( "Business-Area": self.business_area.slug, }, }, - variables={ - "id": self.id_to_base64(self.individuals[0].id, "IndividualNode") - }, + variables={"id": self.id_to_base64(self.individuals[0].id, "IndividualNode")}, ) def test_individual_query_single_different_program_in_header(self) -> None: @@ -385,9 +359,7 @@ def test_individual_query_single_different_program_in_header(self) -> None: "Business-Area": self.business_area.slug, }, }, - variables={ - "id": self.id_to_base64(self.individuals[0].id, "IndividualNode") - }, + variables={"id": self.id_to_base64(self.individuals[0].id, "IndividualNode")}, ) @parameterized.expand( @@ -397,12 +369,8 @@ def test_individual_query_single_different_program_in_header(self) -> None: ] ) @skip("After merging GPF, remove 2nd program") - def test_individual_programme_filter( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program_two - ) + def test_individual_programme_filter(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program_two) self.snapshot_graphql_request( request_string=self.ALL_INDIVIDUALS_BY_PROGRAMME_QUERY, @@ -422,12 +390,8 @@ def test_individual_programme_filter( ("without_permission", []), ] ) - def test_query_individuals_by_search_full_name_filter( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_query_individuals_by_search_full_name_filter(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) # Should be Jenna Franklin self.snapshot_graphql_request( @@ -459,9 +423,7 @@ def test_individual_query_draft(self) -> None: "Business-Area": self.business_area.slug, }, }, - variables={ - "program": self.id_to_base64(self.program_draft.id, "ProgramNode") - }, + variables={"program": self.id_to_base64(self.program_draft.id, "ProgramNode")}, ) @parameterized.expand( @@ -470,12 +432,8 @@ def test_individual_query_draft(self) -> None: ("without_permission", []), ] ) - def test_query_individuals_by_search_phone_no_filter( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_query_individuals_by_search_phone_no_filter(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) # Should be Robin Ford self.snapshot_graphql_request( @@ -496,12 +454,8 @@ def test_query_individuals_by_search_phone_no_filter( ("without_permission", []), ] ) - def test_query_individuals_by_search_national_id_filter( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_query_individuals_by_search_national_id_filter(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) # Should be Benjamin Butler self.snapshot_graphql_request( @@ -525,12 +479,8 @@ def test_query_individuals_by_search_national_id_filter( ("without_permission", []), ] ) - def test_query_individuals_by_search_national_passport_filter( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_query_individuals_by_search_national_passport_filter(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) # Should be Robin Ford self.snapshot_graphql_request( @@ -554,12 +504,8 @@ def test_query_individuals_by_search_national_passport_filter( ("without_permission", []), ] ) - def test_query_individuals_by_search_tax_id_filter( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_query_individuals_by_search_tax_id_filter(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) # Should be Timothy Perry self.snapshot_graphql_request( @@ -583,9 +529,7 @@ def test_query_individuals_by_search_tax_id_filter( def test_query_individuals_by_search_bank_account_number_filter( self, _: Any, permissions: List[Permissions] ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) # Should be James Bond self.snapshot_graphql_request( @@ -606,12 +550,8 @@ def test_query_individuals_by_search_bank_account_number_filter( ("without_permission", []), ] ) - def test_query_individuals_by_search_birth_certificate_filter( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_query_individuals_by_search_birth_certificate_filter(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) # Should be Jenna Franklin self.snapshot_graphql_request( @@ -635,12 +575,8 @@ def test_query_individuals_by_search_birth_certificate_filter( ("without_permission", []), ] ) - def test_query_individuals_by_search_disability_card_filter( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_query_individuals_by_search_disability_card_filter(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) # Should be Peter Parker self.snapshot_graphql_request( @@ -664,12 +600,8 @@ def test_query_individuals_by_search_disability_card_filter( ("without_permission", []), ] ) - def test_query_individuals_by_search_drivers_license_filter( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_query_individuals_by_search_drivers_license_filter(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) # Should be Benjamin Butler self.snapshot_graphql_request( @@ -693,12 +625,8 @@ def test_query_individuals_by_search_drivers_license_filter( ("without_permission", []), ] ) - def test_query_individuals_by_admin2( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_query_individuals_by_admin2(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) self.snapshot_graphql_request( request_string=self.ALL_INDIVIDUALS_QUERY, @@ -954,12 +882,8 @@ def setUpTestData(cls) -> None: ), ] ) - def test_individual_query_delivery_mechanisms_data( - self, _: Any, permissions: List[Permissions] - ) -> None: - self.create_user_role_with_permissions( - self.user, permissions, self.business_area, self.program - ) + def test_individual_query_delivery_mechanisms_data(self, _: Any, permissions: List[Permissions]) -> None: + self.create_user_role_with_permissions(self.user, permissions, self.business_area, self.program) self.snapshot_graphql_request( request_string=self.INDIVIDUAL_QUERY, diff --git a/tests/unit/apps/household/test_individual_xlsx_update.py b/tests/unit/apps/household/test_individual_xlsx_update.py index 390970ab51..452cc7faee 100644 --- a/tests/unit/apps/household/test_individual_xlsx_update.py +++ b/tests/unit/apps/household/test_individual_xlsx_update.py @@ -30,16 +30,12 @@ def valid_file() -> File: def valid_file_complex() -> File: - content = Path( - f"{settings.TESTS_ROOT}/apps/household//test_file/valid_updated_test_file_complex.xlsx" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/household//test_file/valid_updated_test_file_complex.xlsx").read_bytes() return File(BytesIO(content), name="valid_updated_test_file_complex.xlsx") def invalid_file() -> File: - content = Path( - f"{settings.TESTS_ROOT}/apps/household/test_file/invalid_updated_test_file.xlsx" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/household/test_file/invalid_updated_test_file.xlsx").read_bytes() return File(BytesIO(content), name="invalid_updated_test_file.xlsx") diff --git a/tests/unit/apps/payment/test_models1.py b/tests/unit/apps/payment/test_models1.py index b39f426c7b..fcd7a8918f 100644 --- a/tests/unit/apps/payment/test_models1.py +++ b/tests/unit/apps/payment/test_models1.py @@ -154,9 +154,7 @@ def test_can_be_locked(self) -> None: conflicted=False, currency="PLN", ) - self.assertEqual( - pp1.payment_items.filter(payment_plan_hard_conflicted=True).count(), 1 - ) + self.assertEqual(pp1.payment_items.filter(payment_plan_hard_conflicted=True).count(), 1) self.assertEqual(pp1.can_be_locked, False) # create not conflicted payment @@ -243,15 +241,9 @@ def test_manager_annotations_pp_conflicts(self) -> None: program_cycle=program_cycle, ) p1 = PaymentFactory(parent=pp1, conflicted=False, currency="PLN") - p2 = PaymentFactory( - parent=pp2, household=p1.household, conflicted=False, currency="PLN" - ) - p3 = PaymentFactory( - parent=pp3, household=p1.household, conflicted=False, currency="PLN" - ) - p4 = PaymentFactory( - parent=pp4, household=p1.household, conflicted=False, currency="PLN" - ) + p2 = PaymentFactory(parent=pp2, household=p1.household, conflicted=False, currency="PLN") + p3 = PaymentFactory(parent=pp3, household=p1.household, conflicted=False, currency="PLN") + p4 = PaymentFactory(parent=pp4, household=p1.household, conflicted=False, currency="PLN") for obj in [pp1, pp2, pp3, pp4, p1, p2, p3, p4]: obj.refresh_from_db() # update unicef_id from trigger @@ -267,9 +259,7 @@ def test_manager_annotations_pp_conflicts(self) -> None: "payment_id": str(p2.id), "payment_plan_id": str(pp2.id), "payment_plan_status": str(pp2.status), - "payment_plan_start_date": program_cycle.start_date.strftime( - "%Y-%m-%d" - ), + "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), "payment_plan_end_date": program_cycle.end_date.strftime("%Y-%m-%d"), "payment_plan_unicef_id": str(pp2.unicef_id), "payment_unicef_id": str(p2.unicef_id), @@ -277,21 +267,14 @@ def test_manager_annotations_pp_conflicts(self) -> None: ) self.assertEqual(len(p1_data["payment_plan_soft_conflicted_data"]), 2) self.assertCountEqual( - [ - json.loads(conflict_data) - for conflict_data in p1_data["payment_plan_soft_conflicted_data"] - ], + [json.loads(conflict_data) for conflict_data in p1_data["payment_plan_soft_conflicted_data"]], [ { "payment_id": str(p3.id), "payment_plan_id": str(pp3.id), "payment_plan_status": str(pp3.status), - "payment_plan_start_date": program_cycle.start_date.strftime( - "%Y-%m-%d" - ), - "payment_plan_end_date": program_cycle.end_date.strftime( - "%Y-%m-%d" - ), + "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), + "payment_plan_end_date": program_cycle.end_date.strftime("%Y-%m-%d"), "payment_plan_unicef_id": str(pp3.unicef_id), "payment_unicef_id": str(p3.unicef_id), }, @@ -299,12 +282,8 @@ def test_manager_annotations_pp_conflicts(self) -> None: "payment_id": str(p4.id), "payment_plan_id": str(pp4.id), "payment_plan_status": str(pp4.status), - "payment_plan_start_date": program_cycle.start_date.strftime( - "%Y-%m-%d" - ), - "payment_plan_end_date": program_cycle.end_date.strftime( - "%Y-%m-%d" - ), + "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), + "payment_plan_end_date": program_cycle.end_date.strftime("%Y-%m-%d"), "payment_plan_unicef_id": str(pp4.unicef_id), "payment_unicef_id": str(p4.unicef_id), }, @@ -328,9 +307,7 @@ def test_manager_annotations_pp_conflicts(self) -> None: "payment_id": str(p2.id), "payment_plan_id": str(pp2.id), "payment_plan_status": str(pp2.status), - "payment_plan_start_date": program_cycle.start_date.strftime( - "%Y-%m-%d" - ), + "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), "payment_plan_end_date": None, "payment_plan_unicef_id": str(pp2.unicef_id), "payment_unicef_id": str(p2.unicef_id), @@ -338,18 +315,13 @@ def test_manager_annotations_pp_conflicts(self) -> None: ) self.assertEqual(len(payment_data["payment_plan_soft_conflicted_data"]), 2) self.assertCountEqual( - [ - json.loads(conflict_data) - for conflict_data in payment_data["payment_plan_soft_conflicted_data"] - ], + [json.loads(conflict_data) for conflict_data in payment_data["payment_plan_soft_conflicted_data"]], [ { "payment_id": str(p3.id), "payment_plan_id": str(pp3.id), "payment_plan_status": str(pp3.status), - "payment_plan_start_date": program_cycle.start_date.strftime( - "%Y-%m-%d" - ), + "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), "payment_plan_end_date": None, "payment_plan_unicef_id": str(pp3.unicef_id), "payment_unicef_id": str(p3.unicef_id), @@ -358,9 +330,7 @@ def test_manager_annotations_pp_conflicts(self) -> None: "payment_id": str(p4.id), "payment_plan_id": str(pp4.id), "payment_plan_status": str(pp4.status), - "payment_plan_start_date": program_cycle.start_date.strftime( - "%Y-%m-%d" - ), + "payment_plan_start_date": program_cycle.start_date.strftime("%Y-%m-%d"), "payment_plan_end_date": None, "payment_plan_unicef_id": str(pp4.unicef_id), "payment_unicef_id": str(p4.unicef_id), @@ -437,9 +407,7 @@ def test_properties(self) -> None: order=0, ) pp_split1.payments.set([p1, p2]) - self.assertEqual( - pp_split1.financial_service_provider, dm.financial_service_provider - ) + self.assertEqual(pp_split1.financial_service_provider, dm.financial_service_provider) self.assertEqual(pp_split1.chosen_configuration, dm.chosen_configuration) self.assertEqual(pp_split1.delivery_mechanism, dm.delivery_mechanism) @@ -494,12 +462,8 @@ def test_fsp_template_get_column_from_core_field(self) -> None: ) country = CountryFactory() admin_type_1 = AreaTypeFactory(country=country, area_level=1) - admin_type_2 = AreaTypeFactory( - country=country, area_level=2, parent=admin_type_1 - ) - admin_type_3 = AreaTypeFactory( - country=country, area_level=3, parent=admin_type_2 - ) + admin_type_2 = AreaTypeFactory(country=country, area_level=2, parent=admin_type_1) + admin_type_3 = AreaTypeFactory(country=country, area_level=3, parent=admin_type_2) area1 = AreaFactory(parent=None, p_code="AF01", area_type=admin_type_1) area2 = AreaFactory(parent=area1, p_code="AF0101", area_type=admin_type_2) area3 = AreaFactory(parent=area2, p_code="AF010101", area_type=admin_type_3) @@ -508,34 +472,24 @@ def test_fsp_template_get_column_from_core_field(self) -> None: household.admin3 = area3 household.save() - payment = PaymentFactory( - program=ProgramFactory(), household=household, collector=individuals[0] - ) - data_collecting_type = DataCollectingTypeFactory( - type=DataCollectingType.Type.SOCIAL - ) + payment = PaymentFactory(program=ProgramFactory(), household=household, collector=individuals[0]) + data_collecting_type = DataCollectingTypeFactory(type=DataCollectingType.Type.SOCIAL) fsp_xlsx_template = FinancialServiceProviderXlsxTemplate payment.parent.program.data_collecting_type = data_collecting_type payment.parent.program.save() # check invalid filed name - result = fsp_xlsx_template.get_column_from_core_field( - payment, "invalid_people_field_name" - ) + result = fsp_xlsx_template.get_column_from_core_field(payment, "invalid_people_field_name") self.assertIsNone(result) # People program given_name = fsp_xlsx_template.get_column_from_core_field(payment, "given_name") self.assertEqual(given_name, individuals[0].given_name) - ind_unicef_id = fsp_xlsx_template.get_column_from_core_field( - payment, "individual_unicef_id" - ) + ind_unicef_id = fsp_xlsx_template.get_column_from_core_field(payment, "individual_unicef_id") self.assertEqual(ind_unicef_id, individuals[0].unicef_id) # Standard program - payment.parent.program.data_collecting_type.type = ( - DataCollectingType.Type.STANDARD - ) + payment.parent.program.data_collecting_type.type = DataCollectingType.Type.STANDARD payment.parent.program.data_collecting_type.save() # check fields value @@ -549,35 +503,21 @@ def test_fsp_template_get_column_from_core_field(self) -> None: self.assertEqual(admin3, f"{area3.p_code} - {area3.name}") given_name = fsp_xlsx_template.get_column_from_core_field(payment, "given_name") self.assertEqual(given_name, individuals[0].given_name) - ind_unicef_id = fsp_xlsx_template.get_column_from_core_field( - payment, "individual_unicef_id" - ) + ind_unicef_id = fsp_xlsx_template.get_column_from_core_field(payment, "individual_unicef_id") self.assertEqual(ind_unicef_id, individuals[0].unicef_id) - hh_unicef_id = fsp_xlsx_template.get_column_from_core_field( - payment, "household_unicef_id" - ) + hh_unicef_id = fsp_xlsx_template.get_column_from_core_field(payment, "household_unicef_id") self.assertEqual(hh_unicef_id, household.unicef_id) phone_no = fsp_xlsx_template.get_column_from_core_field(payment, "phone_no") self.assertEqual(phone_no, individuals[0].phone_no) - phone_no_alternative = fsp_xlsx_template.get_column_from_core_field( - payment, "phone_no_alternative" - ) + phone_no_alternative = fsp_xlsx_template.get_column_from_core_field(payment, "phone_no_alternative") self.assertEqual(phone_no_alternative, individuals[0].phone_no_alternative) - national_id_no = fsp_xlsx_template.get_column_from_core_field( - payment, "national_id_no" - ) + national_id_no = fsp_xlsx_template.get_column_from_core_field(payment, "national_id_no") self.assertEqual(national_id_no, document.document_number) - wallet_name = fsp_xlsx_template.get_column_from_core_field( - payment, "wallet_name" - ) + wallet_name = fsp_xlsx_template.get_column_from_core_field(payment, "wallet_name") self.assertEqual(wallet_name, individuals[0].wallet_name) - blockchain_name = fsp_xlsx_template.get_column_from_core_field( - payment, "blockchain_name" - ) + blockchain_name = fsp_xlsx_template.get_column_from_core_field(payment, "blockchain_name") self.assertEqual(blockchain_name, individuals[0].blockchain_name) - wallet_address = fsp_xlsx_template.get_column_from_core_field( - payment, "wallet_address" - ) + wallet_address = fsp_xlsx_template.get_column_from_core_field(payment, "wallet_address") self.assertEqual(wallet_address, individuals[0].wallet_address) @@ -615,15 +555,9 @@ def test_model_form_integration(self) -> None: template = form.save() self.assertEqual(template.core_fields, ["age", "residence_status"]) - form = self.FinancialServiceProviderXlsxTemplateForm( - data={"core_fields": ["field1"]} - ) # fake core fields + form = self.FinancialServiceProviderXlsxTemplateForm(data={"core_fields": ["field1"]}) # fake core fields self.assertFalse(form.is_valid()) self.assertEqual( form.errors, - { - "core_fields": [ - "Select a valid choice. field1 is not one of the available choices." - ] - }, + {"core_fields": ["Select a valid choice. field1 is not one of the available choices."]}, ) diff --git a/tests/unit/apps/payment/test_update_reconciliation_data.py b/tests/unit/apps/payment/test_update_reconciliation_data.py index 96c90c8cda..803568c471 100644 --- a/tests/unit/apps/payment/test_update_reconciliation_data.py +++ b/tests/unit/apps/payment/test_update_reconciliation_data.py @@ -22,9 +22,7 @@ def file_without_delivery_dates() -> BytesIO: - content = Path( - f"{settings.TESTS_ROOT}/apps/payment/test_file/import_file_no_delivery_date.xlsx" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/payment/test_file/import_file_no_delivery_date.xlsx").read_bytes() file = BytesIO(content) return file diff --git a/tests/unit/apps/periodic_data_update/test_periodic_data_update_upload_views.py b/tests/unit/apps/periodic_data_update/test_periodic_data_update_upload_views.py index 9944edd76a..6315784760 100644 --- a/tests/unit/apps/periodic_data_update/test_periodic_data_update_upload_views.py +++ b/tests/unit/apps/periodic_data_update/test_periodic_data_update_upload_views.py @@ -31,11 +31,11 @@ from hct_mis_api.apps.periodic_data_update.service.periodic_data_update_export_template_service import ( PeriodicDataUpdateExportTemplateService, ) +from hct_mis_api.apps.periodic_data_update.utils import populate_pdu_with_null_values +from hct_mis_api.apps.program.fixtures import ProgramFactory from tests.unit.apps.periodic_data_update.test_periodic_data_update_import_service import ( add_pdu_data_to_xlsx, ) -from hct_mis_api.apps.periodic_data_update.utils import populate_pdu_with_null_values -from hct_mis_api.apps.program.fixtures import ProgramFactory pytestmark = pytest.mark.django_db diff --git a/tests/unit/apps/program/test_program_cycle_rest_api.py b/tests/unit/apps/program/test_program_cycle_rest_api.py index 2c093798aa..04a53e9109 100644 --- a/tests/unit/apps/program/test_program_cycle_rest_api.py +++ b/tests/unit/apps/program/test_program_cycle_rest_api.py @@ -11,7 +11,6 @@ from rest_framework.exceptions import ValidationError from rest_framework.test import APIClient, APIRequestFactory -from tests.unit.api.base import HOPEApiTestCase from hct_mis_api.apps.account.fixtures import ( BusinessAreaFactory, PartnerFactory, @@ -30,6 +29,7 @@ from hct_mis_api.apps.program.models import Program, ProgramCycle from hct_mis_api.apps.targeting.fixtures import TargetPopulationFactory from hct_mis_api.apps.targeting.models import TargetPopulation +from tests.unit.api.base import HOPEApiTestCase class ProgramCycleAPITestCase(HOPEApiTestCase): diff --git a/tests/unit/apps/registration_data/services/test_mark_submissions.py b/tests/unit/apps/registration_data/services/test_mark_submissions.py index 58390d3206..1a6382e968 100644 --- a/tests/unit/apps/registration_data/services/test_mark_submissions.py +++ b/tests/unit/apps/registration_data/services/test_mark_submissions.py @@ -46,9 +46,7 @@ def _create_submission_with_merged_rdi(cls) -> None: @classmethod def _create_submission(cls, status: str) -> None: - content = Path( - f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/kobo_submissions.json" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/kobo_submissions.json").read_bytes() file = File(BytesIO(content), name="kobo_submissions.json") import_data = ImportData.objects.create( file=file, diff --git a/tests/unit/apps/registration_data/test_models.py b/tests/unit/apps/registration_data/test_models.py index 696822d28e..112c035e48 100644 --- a/tests/unit/apps/registration_data/test_models.py +++ b/tests/unit/apps/registration_data/test_models.py @@ -20,10 +20,7 @@ RegistrationDataImportDatahubFactory, RegistrationDataImportFactory, ) -from hct_mis_api.apps.registration_data.models import ( - DeduplicationEngineSimilarityPair, - RegistrationDataImport, -) +from hct_mis_api.apps.registration_data.models import RegistrationDataImport class TestRegistrationDataModels(TestCase): @@ -144,4 +141,3 @@ def test_linked_rdi(self) -> None: self.rdi_datahub.linked_rdi, self.rdi, ) - diff --git a/tests/unit/apps/registration_data/test_rest_api.py b/tests/unit/apps/registration_data/test_rest_api.py index 8a32e1e31a..806df4d619 100644 --- a/tests/unit/apps/registration_data/test_rest_api.py +++ b/tests/unit/apps/registration_data/test_rest_api.py @@ -6,13 +6,13 @@ from rest_framework import status from rest_framework.test import APIClient, APIRequestFactory -from tests.unit.api.base import HOPEApiTestCase from hct_mis_api.apps.account.fixtures import PartnerFactory, UserFactory from hct_mis_api.apps.account.models import Role, UserRole from hct_mis_api.apps.account.permissions import Permissions from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.api.views import WebhookDeduplicationView +from tests.unit.api.base import HOPEApiTestCase class RegistrationDataImportViewSetTest(HOPEApiTestCase): diff --git a/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py b/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py index fd5691a46c..7840c47531 100644 --- a/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py +++ b/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py @@ -22,10 +22,10 @@ DeduplicationEngineAPI, DeduplicationImage, DeduplicationSet, - DeduplicationSetData, - SimilarityPair, DeduplicationSetConfig, + DeduplicationSetData, IgnoredFilenamesPair, + SimilarityPair, ) from hct_mis_api.apps.registration_datahub.services.biometric_deduplication import ( BiometricDeduplicationService, diff --git a/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py b/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py index a3b2ce5bd4..2227780518 100644 --- a/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py +++ b/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py @@ -30,9 +30,7 @@ def mock_deduplication_engine_env_vars() -> None: class DeduplicationEngineApiTest(TestCase): - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._delete" - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._delete") def test_delete_deduplication_set(self, mock_delete: mock.Mock) -> None: api = DeduplicationEngineAPI() @@ -41,13 +39,9 @@ def test_delete_deduplication_set(self, mock_delete: mock.Mock) -> None: api.delete_deduplication_set(deduplication_set_id) - mock_delete.assert_called_once_with( - f"deduplication_sets/{deduplication_set_id}/" - ) + mock_delete.assert_called_once_with(f"deduplication_sets/{deduplication_set_id}/") - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post" - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post") def test_create_deduplication_set(self, mock_post: mock.Mock) -> None: api = DeduplicationEngineAPI() @@ -60,13 +54,9 @@ def test_create_deduplication_set(self, mock_post: mock.Mock) -> None: api.create_deduplication_set(deduplication_set) print(dataclasses.asdict(deduplication_set)) - mock_post.assert_called_once_with( - "deduplication_sets/", dataclasses.asdict(deduplication_set) - ) + mock_post.assert_called_once_with("deduplication_sets/", dataclasses.asdict(deduplication_set)) - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._get" - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._get") def test_get_deduplication_set(self, get_mock: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -76,9 +66,7 @@ def test_get_deduplication_set(self, get_mock: mock.Mock) -> None: get_mock.assert_called_once_with(f"deduplication_sets/{deduplication_set_id}/") - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post" - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post") def test_bulk_upload_images(self, mock_post: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -98,9 +86,7 @@ def test_bulk_upload_images(self, mock_post: mock.Mock) -> None: [dataclasses.asdict(image) for image in images], ) - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._delete" - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._delete") def test_bulk_delete_images(self, mock_delete: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -108,13 +94,9 @@ def test_bulk_delete_images(self, mock_delete: mock.Mock) -> None: api.bulk_delete_images(deduplication_set_id) - mock_delete.assert_called_once_with( - f"deduplication_sets/{deduplication_set_id}/images_bulk/" - ) + mock_delete.assert_called_once_with(f"deduplication_sets/{deduplication_set_id}/images_bulk/") - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._get" - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._get") def test_get_duplicates(self, get_mock: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -122,13 +104,9 @@ def test_get_duplicates(self, get_mock: mock.Mock) -> None: api.get_duplicates(deduplication_set_id) - get_mock.assert_called_once_with( - f"deduplication_sets/{deduplication_set_id}/duplicates/" - ) + get_mock.assert_called_once_with(f"deduplication_sets/{deduplication_set_id}/duplicates/") - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post" - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post") def test_process_deduplication(self, post_mock: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) @@ -141,9 +119,7 @@ def test_process_deduplication(self, post_mock: mock.Mock) -> None: validate_response=False, ) - @patch( - "hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post" - ) + @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._post") def test_report_false_positive_duplicate(self, post_mock: mock.Mock) -> None: api = DeduplicationEngineAPI() deduplication_set_id = str(uuid.uuid4()) diff --git a/tests/unit/apps/registration_datahub/test_kobo_validators_methods.py b/tests/unit/apps/registration_datahub/test_kobo_validators_methods.py index b81a50b577..f8132715a9 100644 --- a/tests/unit/apps/registration_datahub/test_kobo_validators_methods.py +++ b/tests/unit/apps/registration_datahub/test_kobo_validators_methods.py @@ -762,7 +762,7 @@ def test_validate_everything(self) -> None: {"header": "role_i_c", "message": "Only one person can be a primary collector"}, { "header": "role_i_c", - "message": "The same individual cannot be a primary and alternate collector for the same household." + "message": "The same individual cannot be a primary and alternate collector for the same household.", }, ] self.assertEqual(result, expected) 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 30844c4078..58d6fd5966 100644 --- a/tests/unit/apps/registration_datahub/test_rdi_kobo_create.py +++ b/tests/unit/apps/registration_datahub/test_rdi_kobo_create.py @@ -42,9 +42,7 @@ class TestRdiKoboCreateTask(TestCase): @staticmethod def _return_test_image(*args: Any, **kwargs: Any) -> BytesIO: - return BytesIO( - Path(f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/image.png").read_bytes() - ) + return BytesIO(Path(f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/image.png").read_bytes()) @classmethod def setUpTestData(cls) -> None: @@ -62,9 +60,7 @@ def setUpTestData(cls) -> None: document_types.append(DocumentType(label=label, key=IDENTIFICATION_TYPE_TO_KEY_MAPPING[doc_type])) DocumentType.objects.bulk_create(document_types, ignore_conflicts=True) - content = Path( - f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/kobo_submissions.json" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/kobo_submissions.json").read_bytes() file = File(BytesIO(content), name="kobo_submissions.json") cls.import_data = ImportData.objects.create( file=file, 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 5894aa706a..a35e00684d 100644 --- a/tests/unit/apps/registration_datahub/test_rdi_people_create.py +++ b/tests/unit/apps/registration_datahub/test_rdi_people_create.py @@ -43,9 +43,7 @@ class TestRdiXlsxPeople(TestCase): def setUpTestData(cls) -> None: super().setUpTestData() PartnerFactory(name="UNHCR") - content = Path( - f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/rdi_people_test.xlsx" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/rdi_people_test.xlsx").read_bytes() file = File(BytesIO(content), name="rdi_people_test.xlsx") cls.business_area = create_afghanistan() diff --git a/tests/unit/apps/registration_datahub/test_registration_data_import_datahub_mutations.py b/tests/unit/apps/registration_datahub/test_registration_data_import_datahub_mutations.py index 409ea82881..215379152b 100644 --- a/tests/unit/apps/registration_datahub/test_registration_data_import_datahub_mutations.py +++ b/tests/unit/apps/registration_datahub/test_registration_data_import_datahub_mutations.py @@ -142,9 +142,7 @@ def setUpTestData(cls) -> None: charset=None, ) - xlsx_valid_file_path = ( - f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/new_reg_data_import.xlsx" - ) + xlsx_valid_file_path = f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/new_reg_data_import.xlsx" xlsx_invalid_file_path = f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/rdi_import_3_hh_missing_required_delivery_fields.xlsx" diff --git a/tests/unit/apps/registration_datahub/test_validate_xlsx_import_task.py b/tests/unit/apps/registration_datahub/test_validate_xlsx_import_task.py index 33b933419f..06477d3ad2 100644 --- a/tests/unit/apps/registration_datahub/test_validate_xlsx_import_task.py +++ b/tests/unit/apps/registration_datahub/test_validate_xlsx_import_task.py @@ -37,9 +37,7 @@ def setUpTestData(cls) -> None: "hct_mis_api.apps.registration_datahub.tasks.validate_xlsx_import.UploadXLSXInstanceValidator.validate_everything" ) def test_people(self, validate_everything_mock: Mock) -> None: - content = Path( - f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/rdi_people_test.xlsx" - ).read_bytes() + content = Path(f"{settings.TESTS_ROOT}/apps/registration_datahub/test_file/rdi_people_test.xlsx").read_bytes() file = File(BytesIO(content), name="rdi_people_test.xlsx") import_data = ImportData.objects.create( file=file, diff --git a/tests/unit/apps/targeting/test_admin.py b/tests/unit/apps/targeting/test_admin.py index c50ec08a26..468a1ced01 100644 --- a/tests/unit/apps/targeting/test_admin.py +++ b/tests/unit/apps/targeting/test_admin.py @@ -1,5 +1,5 @@ -from tests.unit.apps.household.test_admin import BaseTest from hct_mis_api.apps.targeting.fixtures import TargetPopulationFactory +from tests.unit.apps.household.test_admin import BaseTest # temporary added test for AutoCompleteFilterTemp have to be removed after fix in AutoCompleteFilter diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index c0fb59b405..6de7143d3f 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -1,4 +1,3 @@ -from .fixtures import * # noqa: ABS101, F403, F401 import logging import os import re @@ -14,6 +13,8 @@ from django_elasticsearch_dsl.test import is_es_online from elasticsearch_dsl import connections +from .fixtures import * # noqa: ABS101, F403, F401 + def pytest_addoption(parser: Parser) -> None: parser.addoption( @@ -52,7 +53,7 @@ def pytest_configure(config: Config) -> None: settings.SECURE_REFERRER_POLICY = "same-origin" settings.CACHE_ENABLED = False - settings.TESTS_ROOT = "/tests/unit" + settings.TESTS_ROOT = "/code/tests/unit" settings.CACHES = { "default": { "BACKEND": "hct_mis_api.apps.core.memcache.LocMemCache", From bbf6217e5f16ce4efa04d7ded04d78531d7f501e Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@tivix.com> Date: Thu, 3 Oct 2024 16:51:14 +0200 Subject: [PATCH 063/202] Update compose.yml uncoment volume --- development_tools/compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/development_tools/compose.yml b/development_tools/compose.yml index 20899f6bda..943c62bc7c 100644 --- a/development_tools/compose.yml +++ b/development_tools/compose.yml @@ -25,7 +25,7 @@ services: ports: - "8080:8000" volumes: -# - ../.:/code/ + - ../.:/code/ - backend-data:/data - ../pyproject.toml:/packages/pyproject.toml - ../pdm.lock:/packages/pdm.lock From 70abd288149d428cc15bcc1b9f0f295441376074 Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Thu, 3 Oct 2024 18:08:57 +0200 Subject: [PATCH 064/202] add open api codegen --- development_tools/.env.example | 1 + 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 | 94 ++ 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 + 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/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/PaginatedBusinessAreaList.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 + .../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 | 19 + .../generated/models/ProgramCycleCreate.ts | 10 + .../generated/models/ProgramCycleList.ts | 21 + .../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 | 1139 +++++++++++++++ src/frontend/package.json | 4 +- src/frontend/yarn.lock | 88 +- 94 files changed, 6585 insertions(+), 5 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/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/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/PaginatedBusinessAreaList.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/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 diff --git a/development_tools/.env.example b/development_tools/.env.example index d37d3410e8..68bac69605 100644 --- a/development_tools/.env.example +++ b/development_tools/.env.example @@ -36,6 +36,7 @@ ROOT_ACCESS_TOKEN=test CELERY_BROKER_URL="redis://redis:6379/0" PROFILING=off USE_DUMMY_EXCHANGE_RATES=no +OPENAPI_URL=127.0.0.1:8080/api/rest/ KOBO_API_URL=https://kobo.humanitarianresponse.info KOBO_KF_URL=https://kobo.humanitarianresponse.info 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<string, any>; + readonly cookies?: Record<string, any>; + readonly headers?: Record<string, any>; + readonly query?: Record<string, any>; + readonly formData?: Record<string, any>; + readonly body?: any; + readonly mediaType?: string; + readonly responseHeader?: string; + readonly errors?: Record<number, string>; +}; 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<T> implements Promise<T> { + #isResolved: boolean; + #isRejected: boolean; + #isCancelled: boolean; + readonly #cancelHandlers: (() => void)[]; + readonly #promise: Promise<T>; + #resolve?: (value: T | PromiseLike<T>) => void; + #reject?: (reason?: any) => void; + + constructor( + executor: ( + resolve: (value: T | PromiseLike<T>) => void, + reject: (reason?: any) => void, + onCancel: OnCancel + ) => void + ) { + this.#isResolved = false; + this.#isRejected = false; + this.#isCancelled = false; + this.#cancelHandlers = []; + this.#promise = new Promise<T>((resolve, reject) => { + this.#resolve = resolve; + this.#reject = reject; + + const onResolve = (value: T | PromiseLike<T>): 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<TResult1 = T, TResult2 = never>( + onFulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, + onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null + ): Promise<TResult1 | TResult2> { + return this.#promise.then(onFulfilled, onRejected); + } + + public catch<TResult = never>( + onRejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null + ): Promise<T | TResult> { + return this.#promise.catch(onRejected); + } + + public finally(onFinally?: (() => void) | null): Promise<T> { + 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<T> = (options: ApiRequestOptions) => Promise<T>; +type Headers = Record<string, string>; + +export type OpenAPIConfig = { + BASE: string; + VERSION: string; + WITH_CREDENTIALS: boolean; + CREDENTIALS: 'include' | 'omit' | 'same-origin'; + TOKEN?: string | Resolver<string> | undefined; + USERNAME?: string | Resolver<string> | undefined; + PASSWORD?: string | Resolver<string> | undefined; + HEADERS?: Headers | Resolver<Headers> | 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 = <T>(value: T | null | undefined): value is Exclude<T, null | undefined> => { + 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, any>): 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<T> = (options: ApiRequestOptions) => Promise<T>; + +export const resolve = async <T>(options: ApiRequestOptions, resolver?: T | Resolver<T>): Promise<T | undefined> => { + if (typeof resolver === 'function') { + return (resolver as Resolver<T>)(options); + } + return resolver; +}; + +export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise<Headers> => { + 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<string, string>); + + 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<Response> => { + 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<any> => { + 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<number, string> = { + 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<T> + * @throws ApiError + */ +export const request = <T>(config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise<T> => { + 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..06bd5313ab --- /dev/null +++ b/src/frontend/generated/index.ts @@ -0,0 +1,94 @@ +/* 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 { 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 { 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 { PaginatedBusinessAreaList } from './models/PaginatedBusinessAreaList'; +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 { 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/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/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<string>; +}; + 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<Delegate>; +}; + 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<Individual>; + 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<string>; + programs?: Array<string>; +}; + 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<Document>; + 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<Area>; +}; + 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<AreaList>; +}; + 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<AreaType>; +}; + 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<BusinessArea>; +}; + 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<PaymentPlan>; +}; + 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<PeriodicDataUpdateTemplateList>; +}; + 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<PeriodicDataUpdateUploadList>; +}; + 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<PeriodicField>; +}; + 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<ProgramCycleList>; +}; + 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<ProgramGlobal>; +}; + 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<RegistrationDataImportList>; +}; + 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<TargetPopulationList>; +}; + 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<FollowUpPaymentPlan>; + 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<string>; + action: ActionEnum; + comment?: string; +}; + 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<string>; +}; + 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..9af73807b8 --- /dev/null +++ b/src/frontend/generated/models/Program.ts @@ -0,0 +1,19 @@ +/* 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; +}; + 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..243e0c92be --- /dev/null +++ b/src/frontend/generated/models/ProgramCycleList.ts @@ -0,0 +1,21 @@ +/* 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; +}; + 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<Document>; + 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<Household>; + 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<any> { + 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<any> { + 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..a0c5c111b7 --- /dev/null +++ b/src/frontend/generated/services/RestService.ts @@ -0,0 +1,1139 @@ +/* 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 { PaginatedBusinessAreaList } from '../models/PaginatedBusinessAreaList'; +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 { 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<Record<string, any>> { + 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<PaginatedAreaListList> { + 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<PaginatedPaymentPlanList> { + 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<PaymentPlanBulkAction> { + 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 + * @returns any No response body + * @throws ApiError + */ + public static restProgramRetrieve( + businessArea: string, + ): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/{business_area}/program/', + path: { + 'business_area': businessArea, + }, + }); + } + /** + * @param businessArea + * @param requestBody + * @returns any No response body + * @throws ApiError + */ + public static restProgramCreateCreate( + businessArea: string, + requestBody: Program, + ): CancelablePromise<any> { + 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<PaginatedProgramCycleListList> { + 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<ProgramCycleCreate> { + 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<ProgramCycleList> { + 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<ProgramCycleUpdate> { + 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<ProgramCycleUpdate> { + 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<void> { + 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<any> { + 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<any> { + 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 + * @returns any No response body + * @throws ApiError + */ + public static restProgramsPaymentPlansSupportingDocumentsUploadCreate( + businessArea: string, + paymentPlanId: string, + programId: string, + ): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/programs/{program_id}/payment-plans/{payment_plan_id}/supporting-documents-upload/', + path: { + 'business_area': businessArea, + 'payment_plan_id': paymentPlanId, + 'program_id': programId, + }, + }); + } + /** + * @param businessArea + * @param fileId + * @param paymentPlanId + * @param programId + * @returns any No response body + * @throws ApiError + */ + public static restProgramsPaymentPlansSupportingDocumentsRetrieve( + businessArea: string, + fileId: string, + paymentPlanId: string, + programId: string, + ): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + 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 void + * @throws ApiError + */ + public static restProgramsPaymentPlansSupportingDocumentsDestroy( + businessArea: string, + fileId: string, + paymentPlanId: string, + programId: string, + ): CancelablePromise<void> { + 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 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<PaginatedPeriodicDataUpdateTemplateListList> { + 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<PeriodicDataUpdateTemplateCreate> { + 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<PeriodicDataUpdateTemplateDetail> { + 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<any> { + 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<any> { + 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<PaginatedPeriodicDataUpdateUploadListList> { + 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<PeriodicDataUpdateUploadDetail> { + 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<PeriodicDataUpdateUpload> { + 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<PaginatedPeriodicFieldList> { + 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<PaginatedRegistrationDataImportListList> { + 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<RegistrationDataImportList> { + 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<any> { + 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<PaginatedTargetPopulationListList> { + 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<RDI> { + 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<RDI> { + 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<RDI> { + 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<any> { + 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<any> { + 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<any> { + 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<any> { + 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<RDI> { + 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<any> { + return __request(OpenAPI, { + method: 'POST', + url: '/api/rest/{business_area}/rdi/upload/', + path: { + 'business_area': businessArea, + }, + body: requestBody, + mediaType: 'application/json', + }); + } + /** + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @returns PaginatedAreaList + * @throws ApiError + */ + public static restAreasList( + limit?: number, + offset?: number, + ): CancelablePromise<PaginatedAreaList> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/areas/', + query: { + 'limit': limit, + 'offset': offset, + }, + }); + } + /** + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @returns PaginatedAreaTypeList + * @throws ApiError + */ + public static restAreatypesList( + limit?: number, + offset?: number, + ): CancelablePromise<PaginatedAreaTypeList> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/areatypes/', + query: { + 'limit': limit, + 'offset': offset, + }, + }); + } + /** + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @returns PaginatedBusinessAreaList + * @throws ApiError + */ + public static restBusinessAreasList( + limit?: number, + offset?: number, + ): CancelablePromise<PaginatedBusinessAreaList> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/business_areas/', + query: { + 'limit': limit, + 'offset': offset, + }, + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restConstanceRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/constance/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsCountryRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/country/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsDatacollectingpolicyRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/datacollectingpolicy/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsDocumentRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/document/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsMaritalstatusRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/maritalstatus/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsObserveddisabilityRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/observeddisability/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsRelationshipRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/relationship/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsResidencestatusRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/residencestatus/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsRoleRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/role/', + }); + } + /** + * @returns any No response body + * @throws ApiError + */ + public static restLookupsSexRetrieve(): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/lookups/sex/', + }); + } + /** + * @param limit Number of results to return per page. + * @param offset The initial index from which to return the results. + * @returns PaginatedProgramGlobalList + * @throws ApiError + */ + public static restProgramsList( + limit?: number, + offset?: number, + ): CancelablePromise<PaginatedProgramGlobalList> { + return __request(OpenAPI, { + method: 'GET', + url: '/api/rest/programs/', + query: { + 'limit': limit, + 'offset': offset, + }, + }); + } +} diff --git a/src/frontend/package.json b/src/frontend/package.json index 31926d3e61..2b583181b0 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -14,7 +14,8 @@ "download-dev-schema": "wget --no-check-certificate -O data/schema.graphql https://dev-hct.unitst.org/api/graphql/schema.graphql", "download-local-schema": "wget --no-check-certificate -O data/schema.graphql http://localhost:3000/api/graphql/schema.graphql", "generate-types": "yarn download-dev-schema && graphql-codegen --config codegen.yml --debug", - "generate-types-local": "yarn download-local-schema && graphql-codegen --config codegen.yml --debug" + "generate-types-local": "yarn download-local-schema && graphql-codegen --config codegen.yml --debug", + "generate-rest-api-types": "npx openapi-typescript-codegen --input http://localhost:8080/api/rest/ --output ./generated" }, "engines": { "node": ">=18.0.0" @@ -60,6 +61,7 @@ "localforage": "^1.10.0", "lodash": "^4.17.21", "moment": "^2.30.1", + "openapi-typescript-codegen": "^0.29.0", "path": "^0.12.7", "prop-types": "^15.7.0", "react": "^18.2.0", diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index 118293b739..2f0a403d0e 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -20,6 +20,15 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" +"@apidevtools/json-schema-ref-parser@^11.5.4": + version "11.7.0" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.0.tgz#228d72018a0e7cbee744b677eaa01a8968f302d9" + integrity sha512-pRrmXMCwnmrkS3MLgAIW5dXRzeTv6GLjkjb4HmxNnvAKXN1Nfzp4KmGADBQvlVUcqi+a5D+hfGDLLnd5NnYxog== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.15" + js-yaml "^4.1.0" + "@apollo/client@^3.0.0-beta.23", "@apollo/client@^3.9.5", "@apollo/client@latest": version "3.9.11" resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.9.11.tgz#737e5c35c21d6f3b78423033ad81837a8a6992e0" @@ -2388,6 +2397,11 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + "@kamilkisiela/fast-url-parser@^1.1.4": version "1.1.4" resolved "https://registry.yarnpkg.com/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz#9d68877a489107411b953c54ea65d0658b515809" @@ -3010,7 +3024,7 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" -"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.9": +"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== @@ -4138,7 +4152,7 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0: +camelcase@^6.2.0, camelcase@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -4437,6 +4451,11 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +commander@^12.0.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + common-tags@1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" @@ -5849,6 +5868,15 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -6029,7 +6057,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -6081,6 +6109,18 @@ graphql@^16.8.1: resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== +handlebars@^4.7.8: + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -7580,6 +7620,15 @@ json5@^2.2.3: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + jsonify@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" @@ -7943,7 +7992,7 @@ minimatch@^4.2.3: dependencies: brace-expansion "^1.1.7" -minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.6: +minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -8041,6 +8090,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -8251,6 +8305,17 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +openapi-typescript-codegen@^0.29.0: + version "0.29.0" + resolved "https://registry.yarnpkg.com/openapi-typescript-codegen/-/openapi-typescript-codegen-0.29.0.tgz#e98a1daa223ccdeb1cc51b2e2dc11bafae6fe746" + integrity sha512-/wC42PkD0LGjDTEULa/XiWQbv4E9NwLjwLjsaJ/62yOsoYhwvmBR31kPttn1DzQ2OlGe5stACcF/EIkZk43M6w== + dependencies: + "@apidevtools/json-schema-ref-parser" "^11.5.4" + camelcase "^6.3.0" + commander "^12.0.0" + fs-extra "^11.2.0" + handlebars "^4.7.8" + optimism@^0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.0.tgz#e7bb38b24715f3fdad8a9a7fc18e999144bbfa63" @@ -10247,6 +10312,11 @@ ua-parser-js@^1.0.35: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f" integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ== +uglify-js@^3.1.4: + version "3.19.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" + integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -10305,6 +10375,11 @@ universalify@^0.2.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + unixify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" @@ -10672,6 +10747,11 @@ word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" From a714960f9495c8fbb652df840861ce9f8ea0265c Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 18:49:14 +0200 Subject: [PATCH 065/202] adjust test to testcase --- tests/unit/apps/targeting/test_target_query.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/unit/apps/targeting/test_target_query.py b/tests/unit/apps/targeting/test_target_query.py index 2204eefbc2..3efdc3eeb7 100644 --- a/tests/unit/apps/targeting/test_target_query.py +++ b/tests/unit/apps/targeting/test_target_query.py @@ -115,6 +115,7 @@ def setUpTestData(cls) -> None: cls.partner = PartnerFactory(name="TestPartner") cls.business_area = BusinessArea.objects.get(slug="afghanistan") cls.program = ProgramFactory(name="test_program", status=Program.ACTIVE) + cls.cycle = cls.program.cycles.first() cls.cycle_2 = ProgramCycleFactory(program=cls.program) _ = create_household( @@ -145,7 +146,7 @@ def setUpTestData(cls) -> None: targeting_criteria=targeting_criteria, business_area=cls.business_area, program=cls.program, - program_cycle=cls.cycle_2, + program_cycle=cls.cycle, ) cls.target_population_size_2.save() cls.target_population_size_2 = full_rebuild(cls.target_population_size_2) @@ -175,7 +176,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, - program_cycle=cls.cycle_2, + program_cycle=cls.cycle, ) cls.target_population_size_1_approved.save() cls.target_population_size_1_approved = full_rebuild(cls.target_population_size_1_approved) @@ -222,7 +223,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, - program_cycle=cls.cycle_2, + program_cycle=cls.cycle, ) cls.target_population_with_pdu_filter.save() cls.target_population_with_pdu_filter = full_rebuild(cls.target_population_with_pdu_filter) @@ -257,7 +258,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, - program_cycle=cls.cycle_2, + program_cycle=cls.cycle, ) cls.target_population_with_individual_filter.save() cls.target_population_with_individual_filter = full_rebuild(cls.target_population_with_individual_filter) From 154e9d8aa6e08f0d0a173828413dca8e3d654505 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 18:56:32 +0200 Subject: [PATCH 066/202] remove edopomoga --- src/hct_mis_api/apps/core/admin.py | 31 +--- src/hct_mis_api/apps/core/celery_tasks.py | 170 ------------------ .../core/templates/core/admin/create_tp.html | 10 -- .../apps/core/test_edopomoga_tp_creation.py | 100 ----------- 4 files changed, 2 insertions(+), 309 deletions(-) delete mode 100644 src/hct_mis_api/apps/core/templates/core/admin/create_tp.html delete mode 100644 tests/unit/apps/core/test_edopomoga_tp_creation.py diff --git a/src/hct_mis_api/apps/core/admin.py b/src/hct_mis_api/apps/core/admin.py index 12272eeab1..9b9343e777 100644 --- a/src/hct_mis_api/apps/core/admin.py +++ b/src/hct_mis_api/apps/core/admin.py @@ -44,11 +44,8 @@ from hct_mis_api.apps.account.models import Role, User from hct_mis_api.apps.administration.widgets import JsonWidget -from hct_mis_api.apps.core.celery_tasks import ( - create_target_population_task, - upload_new_kobo_template_and_update_flex_fields_task, -) -from hct_mis_api.apps.core.forms import DataCollectingTypeForm, ProgramForm +from hct_mis_api.apps.core.celery_tasks import upload_new_kobo_template_and_update_flex_fields_task +from hct_mis_api.apps.core.forms import DataCollectingTypeForm from hct_mis_api.apps.core.models import ( BusinessArea, CountryCodeMap, @@ -692,30 +689,6 @@ def has_view_permission(self, request: HttpRequest, obj: Optional[Any] = None) - def has_add_permission(self, request: HttpRequest) -> bool: return request.user.can_download_storage_files() - @button(label="Create eDopomoga TP") - def create_tp(self, request: HttpRequest, pk: "UUID") -> Union[TemplateResponse, HttpResponsePermanentRedirect]: - storage_obj = StorageFile.objects.get(pk=pk) - context = self.get_common_context( - request, - pk, - ) - if request.method == "GET": - if TargetPopulation.objects.filter(storage_file=storage_obj).exists(): - self.message_user(request, "TargetPopulation for this storageFile have been created", messages.ERROR) - return redirect("..") - - form = ProgramForm(business_area_id=storage_obj.business_area_id) - context["form"] = form - return TemplateResponse(request, "core/admin/create_tp.html", context) - else: - program_id = request.POST.get("program") - tp_name = request.POST.get("name") - - create_target_population_task.delay(storage_obj.pk, program_id, tp_name) - - self.message_user(request, "Creation of TargetPopulation started") - return redirect("..") - @admin.register(MigrationStatus) class MigrationStatusAdmin(admin.ModelAdmin): diff --git a/src/hct_mis_api/apps/core/celery_tasks.py b/src/hct_mis_api/apps/core/celery_tasks.py index 05813521f4..27ea7bea47 100644 --- a/src/hct_mis_api/apps/core/celery_tasks.py +++ b/src/hct_mis_api/apps/core/celery_tasks.py @@ -100,173 +100,3 @@ def upload_new_kobo_template_and_update_flex_fields_task(self: Any, xlsx_kobo_te except Exception as e: logger.exception(e) raise self.retry(exc=e) - - -@app.task(bind=True, default_retry_delay=60, max_retries=3) -@log_start_and_end -@sentry_tags -def create_target_population_task(self: Any, storage_id: str, program_id: str, tp_name: str) -> None: - storage_obj = StorageFile.objects.get(id=storage_id) - file_path = None - program = Program.objects.get(id=program_id) - set_sentry_business_area_tag(program.business_area.name) - - try: - with transaction.atomic(): - registration_data_import = RegistrationDataImport.objects.create( - name=f"{storage_obj.file.name}_{program.name}", - number_of_individuals=0, - number_of_households=0, - business_area=program.business_area, - data_source=RegistrationDataImport.EDOPOMOGA, - program=program, - ) - if program.biometric_deduplication_enabled: - registration_data_import.deduplication_engine_status = RegistrationDataImport.DEDUP_ENGINE_PENDING - - business_area = storage_obj.business_area - country = business_area.countries.first() - - passport_type = DocumentType.objects.get( - key=IDENTIFICATION_TYPE_TO_KEY_MAPPING[IDENTIFICATION_TYPE_NATIONAL_PASSPORT] - ) - tax_type = DocumentType.objects.get(key=IDENTIFICATION_TYPE_TO_KEY_MAPPING[IDENTIFICATION_TYPE_TAX_ID]) - - first_registration_date = timezone.now() - last_registration_date = first_registration_date - - families = {} - individuals, documents, bank_infos = [], [], [] - - storage_obj.status = StorageFile.STATUS_PROCESSING - storage_obj.save(update_fields=["status"]) - rows_count = 0 - - # TODO fix to use Azure storage override AzureStorageFile open method - with storage_obj.file.open("rb") as original_file, tempfile.NamedTemporaryFile(delete=False) as tmp: - tmp.write(original_file.read()) - file_path = tmp.name - - with open(file_path, encoding="cp1251") as file: - reader = csv.DictReader(file, delimiter=";") - for row in reader: - rows_count += 1 - family_id = row["ID_FAM"] - iban = row["IBAN"] - tax_id = row["N_ID"] - passport_id = row["PASSPORT"] - size = row["FAM_NUM"] - - individual_data = { - "given_name": row.get("NAME", ""), - "middle_name": row.get("PATRONYMIC", ""), - "family_name": row.get("SURNAME", ""), - "full_name": f'{row.get("NAME", "")} {row.get("PATRONYMIC", "")} {row.get("SURNAME", "")}', - "birth_date": datetime.strptime(row["BDATE"], "%d.%m.%Y").date(), - "phone_no": row.get("PHONЕ", ""), - "business_area": business_area, - "first_registration_date": first_registration_date, - "last_registration_date": last_registration_date, - "sex": MALE, - "relationship": HEAD, - "rdi_merge_status": MergeStatusModel.MERGED, - "flex_fields": populate_pdu_with_null_values(program), - "registration_data_import": registration_data_import, - } - if family_id in families: - individual = Individual(**individual_data, household_id=families.get(family_id)) - individuals.append(individual) - else: - individual = Individual.objects.create(**individual_data) - individual.refresh_from_db() - - household = Household.objects.create( - head_of_household=individual, - business_area=business_area, - first_registration_date=first_registration_date, - last_registration_date=last_registration_date, - registration_data_import=registration_data_import, - size=size, - family_id=family_id, - storage_obj=storage_obj, - collect_individual_data=COLLECT_TYPE_SIZE_ONLY, - country=country, - rdi_merge_status=MergeStatusModel.MERGED, - ) - - individual.household = household - individual.save(update_fields=("household",)) - - IndividualRoleInHousehold.objects.create( - role=ROLE_PRIMARY, - individual=individual, - household=household, - rdi_merge_status=MergeStatusModel.MERGED, - ) - - families[family_id] = household.id - - passport = Document( - document_number=passport_id, - type=passport_type, - individual=individual, - status=Document.STATUS_INVALID, - country=country, - rdi_merge_status=MergeStatusModel.MERGED, - ) - - tax = Document( - document_number=tax_id, - type=tax_type, - individual=individual, - status=Document.STATUS_INVALID, - country=country, - rdi_merge_status=MergeStatusModel.MERGED, - ) - - bank_account_info = BankAccountInfo( - bank_account_number=iban, individual=individual, rdi_merge_status=MergeStatusModel.MERGED - ) - - documents.append(passport) - documents.append(tax) - bank_infos.append(bank_account_info) - - if rows_count % 1000 == 0: - Individual.objects.bulk_create(individuals) - Document.objects.bulk_create(documents) - BankAccountInfo.objects.bulk_create(bank_infos) - individuals = [] - documents = [] - bank_infos = [] - - Individual.objects.bulk_create(individuals) - Document.objects.bulk_create(documents) - BankAccountInfo.objects.bulk_create(bank_infos) - - households = Household.objects.filter(family_id__in=list(families.keys())) - households.update(withdrawn=True, withdrawn_date=timezone.now()) - Individual.objects.filter(household__in=households).update(withdrawn=True, withdrawn_date=timezone.now()) - - target_population = TargetPopulation.objects.create( - name=tp_name, - created_by=storage_obj.created_by, - program=program, - status=TargetPopulation.STATUS_LOCKED, - build_status=TargetPopulation.BUILD_STATUS_OK, - business_area=business_area, - storage_file=storage_obj, - ) - target_population.households.set(households) - refresh_stats(target_population) - target_population.save() - - storage_obj.status = StorageFile.STATUS_FINISHED - storage_obj.save(update_fields=["status"]) - except Exception as e: - storage_obj.status = StorageFile.STATUS_FAILED - storage_obj.save(update_fields=["status"]) - raise self.retry(exc=e) - finally: - if file_path: - os.remove(file_path) diff --git a/src/hct_mis_api/apps/core/templates/core/admin/create_tp.html b/src/hct_mis_api/apps/core/templates/core/admin/create_tp.html deleted file mode 100644 index ee43184ab4..0000000000 --- a/src/hct_mis_api/apps/core/templates/core/admin/create_tp.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "admin_extra_buttons/action_page.html" %} -{% block content %} - <h2>Target Population</h2> - <form method="post"> - {% csrf_token %} - {{ form.as_p }} - <br> - <button type="submit">Create</button> - </form> -{% endblock %} \ No newline at end of file diff --git a/tests/unit/apps/core/test_edopomoga_tp_creation.py b/tests/unit/apps/core/test_edopomoga_tp_creation.py deleted file mode 100644 index 5cea47b5c4..0000000000 --- a/tests/unit/apps/core/test_edopomoga_tp_creation.py +++ /dev/null @@ -1,100 +0,0 @@ -from io import BytesIO -from pathlib import Path - -from django.conf import settings -from django.core.files import File -from django.core.management import call_command - -import pytest - -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.celery_tasks import create_target_population_task -from hct_mis_api.apps.core.fixtures import create_afghanistan -from hct_mis_api.apps.core.models import BusinessArea, StorageFile -from hct_mis_api.apps.geo import models as geo_models -from hct_mis_api.apps.household.models import STATUS_INACTIVE, Household, Individual -from hct_mis_api.apps.mis_datahub import models as dh_mis_models -from hct_mis_api.apps.mis_datahub.tasks.send_tp_to_datahub import SendTPToDatahubTask -from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.apps.targeting.models import TargetPopulation - - -@pytest.mark.skip(reason="Functionality probably can be removed.") -class TestEdopomogaCreation(APITestCase): - databases = ("default", "cash_assist_datahub_mis") - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls.business_area = create_afghanistan() - call_command("loadcountries") - cls.generate_document_types_for_all_countries() - cls.user = UserFactory.create() - cls.business_area.countries.add(geo_models.Country.objects.get(name="Afghanistan")) - cls.program = ProgramFactory( - name="Test program ONE", - business_area=BusinessArea.objects.first(), - ) - content = Path(f"{settings.TESTS_ROOT}/apps/core/test_files/edopomoga_sample.csv") - cls.storage_file = StorageFile.objects.create( - created_by=cls.user, - business_area=cls.business_area, - status=StorageFile.STATUS_NOT_PROCESSED, - file=File(BytesIO(content.read_bytes()), name="edopomoga_sample.csv"), - ) - - def test_edopomoga_tp_creation(self) -> None: - create_target_population_inner = create_target_population_task.__wrapped__ - create_target_population_inner(self.storage_file.id, self.program.id, "test_edopomoga") - - self.assertEqual(Household.objects.count(), 3) - self.assertEqual(Individual.objects.count(), 5) - self.assertEqual(TargetPopulation.objects.count(), 1) - self.assertEqual(Household.objects.filter(withdrawn=True).count(), 3) - self.assertEqual(Individual.objects.filter(withdrawn=True).count(), 5) - - self.storage_file.refresh_from_db() - self.assertEqual(self.storage_file.status, StorageFile.STATUS_FINISHED) - - def test_calculate_household_size(self) -> None: - create_target_population_inner = create_target_population_task.__wrapped__ - create_target_population_inner(self.storage_file.id, self.program.id, "test_edopomoga") - - household1 = Household.objects.get(family_id="1281191") - household2 = Household.objects.get(family_id="1281375") - household3 = Household.objects.get(family_id="1281383") - - self.assertEqual(household1.size, 4) - self.assertEqual(household2.size, 4) - self.assertEqual(household3.size, 4) - - def test_create_collector(self) -> None: - create_target_population_inner = create_target_population_task.__wrapped__ - create_target_population_inner(self.storage_file.id, self.program.id, "test_edopomoga") - - household1 = Household.objects.get(family_id="1281191") - household2 = Household.objects.get(family_id="1281375") - household3 = Household.objects.get(family_id="1281383") - - self.assertEqual(household1.representatives.count(), 1) - self.assertEqual(household2.representatives.count(), 1) - self.assertEqual(household3.representatives.count(), 1) - - def test_edopomoga_tp_send_to_ca_clear_withdrawn(self) -> None: - # set clear_withdrawn flag - self.business_area.custom_fields = {"clear_withdrawn": True} - self.business_area.save() - tp_name = "New eDopomoga test clear_withdrawn data" - create_target_population_inner = create_target_population_task.__wrapped__ - create_target_population_inner(self.storage_file.id, self.program.id, tp_name) - - target_population = TargetPopulation.objects.get(name=tp_name) - - SendTPToDatahubTask().execute(target_population) - - self.assertEqual(Household.objects.filter(withdrawn=True).count(), 3) - self.assertEqual(Individual.objects.filter(withdrawn=True).count(), 5) - - self.assertEqual(dh_mis_models.Household.objects.filter(status=STATUS_INACTIVE).count(), 0) - self.assertEqual(dh_mis_models.Individual.objects.filter(status=STATUS_INACTIVE).count(), 0) From aa6ee62e740cd276c62333e11f8716558e0f5293 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 19:08:33 +0200 Subject: [PATCH 067/202] include src in path --- tests/.coveragerc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/.coveragerc b/tests/.coveragerc index 96ca8cc7c1..370816c587 100644 --- a/tests/.coveragerc +++ b/tests/.coveragerc @@ -11,7 +11,7 @@ omit = */admin/*.py */admin.py **/fixtures.py - **/forms.py + src/hct_mis_api/**/forms.py hct_mis_api/one_time_scripts/* hct_mis_api/libs/* hct_mis_api/settings/* From a3041d118b6468caf1f5fe0e4286c9c7f5959ed4 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Thu, 3 Oct 2024 20:37:30 +0200 Subject: [PATCH 068/202] linter --- src/hct_mis_api/apps/core/admin.py | 5 ++-- src/hct_mis_api/apps/core/celery_tasks.py | 30 ++----------------- .../test_pull_from_datahub.py | 2 +- 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/src/hct_mis_api/apps/core/admin.py b/src/hct_mis_api/apps/core/admin.py index 9b9343e777..91fd4c590e 100644 --- a/src/hct_mis_api/apps/core/admin.py +++ b/src/hct_mis_api/apps/core/admin.py @@ -44,7 +44,9 @@ from hct_mis_api.apps.account.models import Role, User from hct_mis_api.apps.administration.widgets import JsonWidget -from hct_mis_api.apps.core.celery_tasks import upload_new_kobo_template_and_update_flex_fields_task +from hct_mis_api.apps.core.celery_tasks import ( + upload_new_kobo_template_and_update_flex_fields_task, +) from hct_mis_api.apps.core.forms import DataCollectingTypeForm from hct_mis_api.apps.core.models import ( BusinessArea, @@ -63,7 +65,6 @@ from hct_mis_api.apps.household.models import DocumentType from hct_mis_api.apps.payment.forms import AcceptanceProcessThresholdForm from hct_mis_api.apps.payment.models import AcceptanceProcessThreshold -from hct_mis_api.apps.targeting.models import TargetPopulation from hct_mis_api.apps.utils.admin import ( HOPEModelAdminBase, LastSyncDateResetMixin, diff --git a/src/hct_mis_api/apps/core/celery_tasks.py b/src/hct_mis_api/apps/core/celery_tasks.py index 27ea7bea47..59e4112f54 100644 --- a/src/hct_mis_api/apps/core/celery_tasks.py +++ b/src/hct_mis_api/apps/core/celery_tasks.py @@ -1,42 +1,16 @@ -import csv import logging -import os -import tempfile -from datetime import datetime from functools import wraps from typing import Any, Callable from django.db import transaction -from django.utils import timezone from hct_mis_api.apps.core.celery import app -from hct_mis_api.apps.core.models import StorageFile, XLSXKoboTemplate +from hct_mis_api.apps.core.models import XLSXKoboTemplate from hct_mis_api.apps.core.tasks.upload_new_template_and_update_flex_fields import ( KoboRetriableError, ) -from hct_mis_api.apps.core.utils import IDENTIFICATION_TYPE_TO_KEY_MAPPING -from hct_mis_api.apps.household.models import ( - COLLECT_TYPE_SIZE_ONLY, - HEAD, - IDENTIFICATION_TYPE_NATIONAL_PASSPORT, - IDENTIFICATION_TYPE_TAX_ID, - MALE, - ROLE_PRIMARY, - BankAccountInfo, - Document, - DocumentType, - Household, - Individual, - IndividualRoleInHousehold, -) -from hct_mis_api.apps.periodic_data_update.utils import populate_pdu_with_null_values -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 TargetPopulation -from hct_mis_api.apps.targeting.services.targeting_stats_refresher import refresh_stats from hct_mis_api.apps.utils.logs import log_start_and_end -from hct_mis_api.apps.utils.models import MergeStatusModel -from hct_mis_api.apps.utils.sentry import sentry_tags, set_sentry_business_area_tag +from hct_mis_api.apps.utils.sentry import sentry_tags logger = logging.getLogger(__name__) diff --git a/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py b/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py index e954b4c0cf..d3994159d5 100644 --- a/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py +++ b/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py @@ -75,7 +75,7 @@ def _setup_in_app_data(cls) -> None: status=TargetPopulation.STATUS_PROCESSING, program=cls.program, business_area=cls.business_area, - program_cycle=cls.program.cycles.first() + program_cycle=cls.program.cycles.first(), ) program = ProgramFactory( From 50a2d06e70629f9c7f723f7c28c30021a04ecfaf Mon Sep 17 00:00:00 2001 From: Pavlo Mokiichuk <pv.pasha.pv@gmail.com> Date: Thu, 3 Oct 2024 22:25:43 +0200 Subject: [PATCH 069/202] PM: Supporting documents (#4260) * add new model * add new urls * add test & migrations * imports * tests & fixes * add modal for supporting documents * add delete, fix doc layout * add supporting docs to follow up pages * add title validation * textfield size small * regenerate schema * add error for > 10 files, accept other filetypes * add message when deleting doc * display both title and file name * upd e2e test_smoke_details_payment_plan * review * fix urls in tests * migrations * fix download resp * fix * add get_id * supporting docs fixes * mypy * fixes --------- Co-authored-by: Maciej Szewczyk <maciej.szewczyk@tivix.com> --- src/frontend/data/schema.graphql | 21 + src/frontend/src/__generated__/graphql.tsx | 77 +++- .../src/__generated__/introspection-result.ts | 1 + src/frontend/src/api/paymentModuleApi.ts | 51 +++ .../queries/paymentmodule/PaymentPlan.ts | 5 + .../src/components/core/DropzoneField.tsx | 30 +- .../SupportingDocumentsSection.tsx | 395 ++++++++++++++++++ .../SupportingDocumentsSectionActions.ts | 35 ++ .../PeriodicDataUpdatesUploadDialog.tsx | 12 +- .../create/xlsx/CreateImportFromXlsxForm.tsx | 20 +- src/frontend/src/config/permissions.ts | 3 + .../FollowUpPaymentPlanDetailsPage.tsx | 2 + .../PaymentPlanDetailsPage.tsx | 4 +- .../PeopleFollowUpPaymentPlanDetailsPage.tsx | 2 + .../PeoplePaymentPlanDetailsPage.tsx | 2 + src/frontend/src/utils/en.json | 5 + src/hct_mis_api/api/urls.py | 6 +- .../apps/account/api/permissions.py | 12 + .../apps/account/fixtures/data.json | 2 +- src/hct_mis_api/apps/account/permissions.py | 5 + src/hct_mis_api/apps/payment/admin.py | 6 + .../apps/payment/api/serializers.py | 38 +- .../apps/payment/api/urls/__init__.py | 0 .../apps/payment/api/urls/payment_plans.py | 14 + .../payment/api/{urls.py => urls/payments.py} | 0 src/hct_mis_api/apps/payment/api/views.py | 58 ++- .../apps/payment/migrations/0146_migration.py | 30 ++ src/hct_mis_api/apps/payment/models.py | 17 + src/hct_mis_api/apps/payment/schema.py | 11 + .../payment_module/payment_module_details.py | 8 + .../payment_module/test_payment_plans.py | 2 + .../snap_test_all_payment_plan_queries.py | 7 + .../payment/test_all_payment_plan_queries.py | 19 + .../test_payment_plan_supporting_documents.py | 199 +++++++++ 34 files changed, 1067 insertions(+), 32 deletions(-) create mode 100644 src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx create mode 100644 src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSectionActions.ts create mode 100644 src/hct_mis_api/apps/payment/api/urls/__init__.py create mode 100644 src/hct_mis_api/apps/payment/api/urls/payment_plans.py rename src/hct_mis_api/apps/payment/api/{urls.py => urls/payments.py} (100%) create mode 100644 src/hct_mis_api/apps/payment/migrations/0146_migration.py create mode 100644 tests/unit/apps/payment/test_payment_plan_supporting_documents.py diff --git a/src/frontend/data/schema.graphql b/src/frontend/data/schema.graphql index 046f1d9572..c101609d6e 100644 --- a/src/frontend/data/schema.graphql +++ b/src/frontend/data/schema.graphql @@ -3370,6 +3370,7 @@ type PaymentPlanNode implements Node { deliveryMechanisms: [DeliveryMechanismPerPaymentPlanNode] paymentItems(offset: Int, before: String, after: String, first: Int, last: Int): PaymentNodeConnection! 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 @@ -3392,6 +3393,7 @@ type PaymentPlanNode implements Node { unsuccessfulPaymentsCount: Int canSendToPaymentGateway: Boolean canSplit: Boolean + supportingDocuments: [PaymentPlanSupportingDocumentNode] } type PaymentPlanNodeConnection { @@ -3418,6 +3420,25 @@ enum PaymentPlanStatus { FINISHED } +type PaymentPlanSupportingDocumentNode implements Node { + id: ID! + paymentPlan: PaymentPlanNode! + title: String! + file: String! + uploadedAt: DateTime! + createdBy: UserNode +} + +type PaymentPlanSupportingDocumentNodeConnection { + pageInfo: PageInfo! + edges: [PaymentPlanSupportingDocumentNodeEdge]! +} + +type PaymentPlanSupportingDocumentNodeEdge { + node: PaymentPlanSupportingDocumentNode + cursor: String! +} + type PaymentRecordAndPaymentNode { objType: String id: String diff --git a/src/frontend/src/__generated__/graphql.tsx b/src/frontend/src/__generated__/graphql.tsx index 577c7a5e95..b9241a0d54 100644 --- a/src/frontend/src/__generated__/graphql.tsx +++ b/src/frontend/src/__generated__/graphql.tsx @@ -5191,6 +5191,7 @@ export type PaymentPlanNode = Node & { deliveryMechanisms?: Maybe<Array<Maybe<DeliveryMechanismPerPaymentPlanNode>>>; dispersionEndDate?: Maybe<Scalars['Date']['output']>; dispersionStartDate?: Maybe<Scalars['Date']['output']>; + documents: PaymentPlanSupportingDocumentNodeConnection; endDate?: Maybe<Scalars['Date']['output']>; exchangeRate?: Maybe<Scalars['Float']['output']>; excludeHouseholdError: Scalars['String']['output']; @@ -5223,6 +5224,7 @@ export type PaymentPlanNode = Node & { statusDate: Scalars['DateTime']['output']; steficonAppliedDate?: Maybe<Scalars['DateTime']['output']>; steficonRule?: Maybe<RuleCommitNode>; + supportingDocuments?: Maybe<Array<Maybe<PaymentPlanSupportingDocumentNode>>>; targetPopulation: TargetPopulationNode; totalDeliveredQuantity?: Maybe<Scalars['Float']['output']>; totalDeliveredQuantityUsd?: Maybe<Scalars['Float']['output']>; @@ -5253,6 +5255,15 @@ export type PaymentPlanNodeApprovalProcessArgs = { }; +export type PaymentPlanNodeDocumentsArgs = { + after?: InputMaybe<Scalars['String']['input']>; + before?: InputMaybe<Scalars['String']['input']>; + first?: InputMaybe<Scalars['Int']['input']>; + last?: InputMaybe<Scalars['Int']['input']>; + offset?: InputMaybe<Scalars['Int']['input']>; +}; + + export type PaymentPlanNodeFollowUpsArgs = { after?: InputMaybe<Scalars['String']['input']>; before?: InputMaybe<Scalars['String']['input']>; @@ -5306,6 +5317,28 @@ export enum PaymentPlanStatus { Preparing = 'PREPARING' } +export type PaymentPlanSupportingDocumentNode = Node & { + __typename?: 'PaymentPlanSupportingDocumentNode'; + createdBy?: Maybe<UserNode>; + file: Scalars['String']['output']; + id: Scalars['ID']['output']; + paymentPlan: PaymentPlanNode; + title: Scalars['String']['output']; + uploadedAt: Scalars['DateTime']['output']; +}; + +export type PaymentPlanSupportingDocumentNodeConnection = { + __typename?: 'PaymentPlanSupportingDocumentNodeConnection'; + edges: Array<Maybe<PaymentPlanSupportingDocumentNodeEdge>>; + pageInfo: PageInfo; +}; + +export type PaymentPlanSupportingDocumentNodeEdge = { + __typename?: 'PaymentPlanSupportingDocumentNodeEdge'; + cursor: Scalars['String']['output']; + node?: Maybe<PaymentPlanSupportingDocumentNode>; +}; + export type PaymentRecordAndPaymentNode = { __typename?: 'PaymentRecordAndPaymentNode'; caId?: Maybe<Scalars['String']['output']>; @@ -10805,7 +10838,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: PaymentPlanCurrency, 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 }, 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<string | null> | 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 } | 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: PaymentPlanCurrency, 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 }, 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<string | null> | 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 AllCashPlansQueryVariables = Exact<{ program?: InputMaybe<Scalars['ID']['input']>; @@ -19610,6 +19643,11 @@ export const PaymentPlanDocument = gql` unicefId } unsuccessfulPaymentsCount + supportingDocuments { + id + title + file + } } } `; @@ -24694,7 +24732,7 @@ export type DirectiveResolverFn<TResult = {}, TParent = {}, TContext = {}, TArgs /** Mapping of interface types */ export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = { - Node: ( ApprovalProcessNode ) | ( AreaNode ) | ( AreaTypeNode ) | ( BankAccountInfoNode ) | ( BusinessAreaNode ) | ( CashPlanNode ) | ( CommunicationMessageNode ) | ( CommunicationMessageRecipientMapNode ) | ( DataCollectingTypeNode ) | ( DeliveryMechanismDataNode ) | ( DeliveryMechanismNode ) | ( DeliveryMechanismPerPaymentPlanNode ) | ( DocumentNode ) | ( FeedbackMessageNode ) | ( FeedbackNode ) | ( FinancialServiceProviderNode ) | ( FinancialServiceProviderXlsxTemplateNode ) | ( GrievanceDocumentNode ) | ( GrievanceTicketNode ) | ( HouseholdNode ) | ( ImportDataNode ) | ( ImportedDocumentNode ) | ( ImportedHouseholdNode ) | ( ImportedIndividualIdentityNode ) | ( ImportedIndividualNode ) | ( IndividualIdentityNode ) | ( IndividualNode ) | ( KoboImportDataNode ) | ( LogEntryNode ) | ( PaymentHouseholdSnapshotNode ) | ( PaymentNode ) | ( PaymentPlanNode ) | ( PaymentRecordNode ) | ( PaymentVerificationLogEntryNode ) | ( PaymentVerificationNode ) | ( PaymentVerificationPlanNode ) | ( PaymentVerificationSummaryNode ) | ( PeriodicFieldNode ) | ( ProgramCycleNode ) | ( ProgramNode ) | ( RecipientNode ) | ( RegistrationDataImportDatahubNode ) | ( RegistrationDataImportNode ) | ( ReportNode ) | ( RuleCommitNode ) | ( SanctionListIndividualAliasNameNode ) | ( SanctionListIndividualCountriesNode ) | ( SanctionListIndividualDateOfBirthNode ) | ( SanctionListIndividualDocumentNode ) | ( SanctionListIndividualNationalitiesNode ) | ( SanctionListIndividualNode ) | ( ServiceProviderNode ) | ( 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 ) | ( BusinessAreaNode ) | ( CashPlanNode ) | ( CommunicationMessageNode ) | ( CommunicationMessageRecipientMapNode ) | ( DataCollectingTypeNode ) | ( DeliveryMechanismDataNode ) | ( DeliveryMechanismNode ) | ( DeliveryMechanismPerPaymentPlanNode ) | ( DocumentNode ) | ( FeedbackMessageNode ) | ( FeedbackNode ) | ( FinancialServiceProviderNode ) | ( FinancialServiceProviderXlsxTemplateNode ) | ( GrievanceDocumentNode ) | ( GrievanceTicketNode ) | ( HouseholdNode ) | ( ImportDataNode ) | ( ImportedDocumentNode ) | ( ImportedHouseholdNode ) | ( ImportedIndividualIdentityNode ) | ( ImportedIndividualNode ) | ( IndividualIdentityNode ) | ( IndividualNode ) | ( KoboImportDataNode ) | ( LogEntryNode ) | ( PaymentHouseholdSnapshotNode ) | ( PaymentNode ) | ( PaymentPlanNode ) | ( PaymentPlanSupportingDocumentNode ) | ( PaymentRecordNode ) | ( PaymentVerificationLogEntryNode ) | ( PaymentVerificationNode ) | ( PaymentVerificationPlanNode ) | ( PaymentVerificationSummaryNode ) | ( PeriodicFieldNode ) | ( ProgramCycleNode ) | ( ProgramNode ) | ( RecipientNode ) | ( RegistrationDataImportDatahubNode ) | ( RegistrationDataImportNode ) | ( ReportNode ) | ( RuleCommitNode ) | ( SanctionListIndividualAliasNameNode ) | ( SanctionListIndividualCountriesNode ) | ( SanctionListIndividualDateOfBirthNode ) | ( SanctionListIndividualDocumentNode ) | ( SanctionListIndividualNationalitiesNode ) | ( SanctionListIndividualNode ) | ( ServiceProviderNode ) | ( 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 */ @@ -25004,6 +25042,9 @@ export type ResolversTypes = { PaymentPlanNodeConnection: ResolverTypeWrapper<PaymentPlanNodeConnection>; PaymentPlanNodeEdge: ResolverTypeWrapper<PaymentPlanNodeEdge>; PaymentPlanStatus: PaymentPlanStatus; + PaymentPlanSupportingDocumentNode: ResolverTypeWrapper<PaymentPlanSupportingDocumentNode>; + PaymentPlanSupportingDocumentNodeConnection: ResolverTypeWrapper<PaymentPlanSupportingDocumentNodeConnection>; + PaymentPlanSupportingDocumentNodeEdge: ResolverTypeWrapper<PaymentPlanSupportingDocumentNodeEdge>; PaymentRecordAndPaymentNode: ResolverTypeWrapper<PaymentRecordAndPaymentNode>; PaymentRecordDeliveryTypeChoice: PaymentRecordDeliveryTypeChoice; PaymentRecordEntitlementCardStatus: PaymentRecordEntitlementCardStatus; @@ -25509,6 +25550,9 @@ export type ResolversParentTypes = { PaymentPlanNode: PaymentPlanNode; PaymentPlanNodeConnection: PaymentPlanNodeConnection; PaymentPlanNodeEdge: PaymentPlanNodeEdge; + PaymentPlanSupportingDocumentNode: PaymentPlanSupportingDocumentNode; + PaymentPlanSupportingDocumentNodeConnection: PaymentPlanSupportingDocumentNodeConnection; + PaymentPlanSupportingDocumentNodeEdge: PaymentPlanSupportingDocumentNodeEdge; PaymentRecordAndPaymentNode: PaymentRecordAndPaymentNode; PaymentRecordNode: PaymentRecordNode; PaymentRecordNodeConnection: PaymentRecordNodeConnection; @@ -27895,7 +27939,7 @@ export type NeedsAdjudicationApproveMutationResolvers<ContextType = any, ParentT }; export type NodeResolvers<ContextType = any, ParentType extends ResolversParentTypes['Node'] = ResolversParentTypes['Node']> = { - __resolveType: TypeResolveFn<'ApprovalProcessNode' | 'AreaNode' | 'AreaTypeNode' | 'BankAccountInfoNode' | 'BusinessAreaNode' | 'CashPlanNode' | 'CommunicationMessageNode' | 'CommunicationMessageRecipientMapNode' | 'DataCollectingTypeNode' | 'DeliveryMechanismDataNode' | 'DeliveryMechanismNode' | 'DeliveryMechanismPerPaymentPlanNode' | 'DocumentNode' | 'FeedbackMessageNode' | 'FeedbackNode' | 'FinancialServiceProviderNode' | 'FinancialServiceProviderXlsxTemplateNode' | 'GrievanceDocumentNode' | 'GrievanceTicketNode' | 'HouseholdNode' | 'ImportDataNode' | 'ImportedDocumentNode' | 'ImportedHouseholdNode' | 'ImportedIndividualIdentityNode' | 'ImportedIndividualNode' | 'IndividualIdentityNode' | 'IndividualNode' | 'KoboImportDataNode' | 'LogEntryNode' | 'PaymentHouseholdSnapshotNode' | 'PaymentNode' | 'PaymentPlanNode' | 'PaymentRecordNode' | 'PaymentVerificationLogEntryNode' | 'PaymentVerificationNode' | 'PaymentVerificationPlanNode' | 'PaymentVerificationSummaryNode' | 'PeriodicFieldNode' | 'ProgramCycleNode' | 'ProgramNode' | 'RecipientNode' | 'RegistrationDataImportDatahubNode' | 'RegistrationDataImportNode' | 'ReportNode' | 'RuleCommitNode' | 'SanctionListIndividualAliasNameNode' | 'SanctionListIndividualCountriesNode' | 'SanctionListIndividualDateOfBirthNode' | 'SanctionListIndividualDocumentNode' | 'SanctionListIndividualNationalitiesNode' | 'SanctionListIndividualNode' | 'ServiceProviderNode' | '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' | 'BusinessAreaNode' | 'CashPlanNode' | 'CommunicationMessageNode' | 'CommunicationMessageRecipientMapNode' | 'DataCollectingTypeNode' | 'DeliveryMechanismDataNode' | 'DeliveryMechanismNode' | 'DeliveryMechanismPerPaymentPlanNode' | 'DocumentNode' | 'FeedbackMessageNode' | 'FeedbackNode' | 'FinancialServiceProviderNode' | 'FinancialServiceProviderXlsxTemplateNode' | 'GrievanceDocumentNode' | 'GrievanceTicketNode' | 'HouseholdNode' | 'ImportDataNode' | 'ImportedDocumentNode' | 'ImportedHouseholdNode' | 'ImportedIndividualIdentityNode' | 'ImportedIndividualNode' | 'IndividualIdentityNode' | 'IndividualNode' | 'KoboImportDataNode' | 'LogEntryNode' | 'PaymentHouseholdSnapshotNode' | 'PaymentNode' | 'PaymentPlanNode' | 'PaymentPlanSupportingDocumentNode' | 'PaymentRecordNode' | 'PaymentVerificationLogEntryNode' | 'PaymentVerificationNode' | 'PaymentVerificationPlanNode' | 'PaymentVerificationSummaryNode' | 'PeriodicFieldNode' | 'ProgramCycleNode' | 'ProgramNode' | 'RecipientNode' | 'RegistrationDataImportDatahubNode' | 'RegistrationDataImportNode' | 'ReportNode' | 'RuleCommitNode' | 'SanctionListIndividualAliasNameNode' | 'SanctionListIndividualCountriesNode' | 'SanctionListIndividualDateOfBirthNode' | 'SanctionListIndividualDocumentNode' | 'SanctionListIndividualNationalitiesNode' | 'SanctionListIndividualNode' | 'ServiceProviderNode' | 'SteficonRuleNode' | 'SurveyNode' | 'TargetPopulationNode' | 'TicketAddIndividualDetailsNode' | 'TicketComplaintDetailsNode' | 'TicketDeleteHouseholdDetailsNode' | 'TicketDeleteIndividualDetailsNode' | 'TicketHouseholdDataUpdateDetailsNode' | 'TicketIndividualDataUpdateDetailsNode' | 'TicketNeedsAdjudicationDetailsNode' | 'TicketNegativeFeedbackDetailsNode' | 'TicketNoteNode' | 'TicketPaymentVerificationDetailsNode' | 'TicketPositiveFeedbackDetailsNode' | 'TicketReferralDetailsNode' | 'TicketSensitiveDetailsNode' | 'TicketSystemFlaggingDetailsNode' | 'UserBusinessAreaNode' | 'UserNode' | 'VolumeByDeliveryMechanismNode', ParentType, ContextType>; id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>; }; @@ -28106,6 +28150,7 @@ export type PaymentPlanNodeResolvers<ContextType = any, ParentType extends Resol deliveryMechanisms?: Resolver<Maybe<Array<Maybe<ResolversTypes['DeliveryMechanismPerPaymentPlanNode']>>>, ParentType, ContextType>; dispersionEndDate?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>; dispersionStartDate?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>; + documents?: Resolver<ResolversTypes['PaymentPlanSupportingDocumentNodeConnection'], ParentType, ContextType, Partial<PaymentPlanNodeDocumentsArgs>>; endDate?: Resolver<Maybe<ResolversTypes['Date']>, ParentType, ContextType>; exchangeRate?: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>; excludeHouseholdError?: Resolver<ResolversTypes['String'], ParentType, ContextType>; @@ -28138,6 +28183,7 @@ export type PaymentPlanNodeResolvers<ContextType = any, ParentType extends Resol statusDate?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>; steficonAppliedDate?: Resolver<Maybe<ResolversTypes['DateTime']>, ParentType, ContextType>; steficonRule?: Resolver<Maybe<ResolversTypes['RuleCommitNode']>, ParentType, ContextType>; + supportingDocuments?: Resolver<Maybe<Array<Maybe<ResolversTypes['PaymentPlanSupportingDocumentNode']>>>, ParentType, ContextType>; targetPopulation?: Resolver<ResolversTypes['TargetPopulationNode'], ParentType, ContextType>; totalDeliveredQuantity?: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>; totalDeliveredQuantityUsd?: Resolver<Maybe<ResolversTypes['Float']>, ParentType, ContextType>; @@ -28173,6 +28219,28 @@ export type PaymentPlanNodeEdgeResolvers<ContextType = any, ParentType extends R __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; }; +export type PaymentPlanSupportingDocumentNodeResolvers<ContextType = any, ParentType extends ResolversParentTypes['PaymentPlanSupportingDocumentNode'] = ResolversParentTypes['PaymentPlanSupportingDocumentNode']> = { + createdBy?: Resolver<Maybe<ResolversTypes['UserNode']>, ParentType, ContextType>; + file?: Resolver<ResolversTypes['String'], ParentType, ContextType>; + id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>; + paymentPlan?: Resolver<ResolversTypes['PaymentPlanNode'], ParentType, ContextType>; + title?: Resolver<ResolversTypes['String'], ParentType, ContextType>; + uploadedAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; +}; + +export type PaymentPlanSupportingDocumentNodeConnectionResolvers<ContextType = any, ParentType extends ResolversParentTypes['PaymentPlanSupportingDocumentNodeConnection'] = ResolversParentTypes['PaymentPlanSupportingDocumentNodeConnection']> = { + edges?: Resolver<Array<Maybe<ResolversTypes['PaymentPlanSupportingDocumentNodeEdge']>>, ParentType, ContextType>; + pageInfo?: Resolver<ResolversTypes['PageInfo'], ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; +}; + +export type PaymentPlanSupportingDocumentNodeEdgeResolvers<ContextType = any, ParentType extends ResolversParentTypes['PaymentPlanSupportingDocumentNodeEdge'] = ResolversParentTypes['PaymentPlanSupportingDocumentNodeEdge']> = { + cursor?: Resolver<ResolversTypes['String'], ParentType, ContextType>; + node?: Resolver<Maybe<ResolversTypes['PaymentPlanSupportingDocumentNode']>, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; +}; + export type PaymentRecordAndPaymentNodeResolvers<ContextType = any, ParentType extends ResolversParentTypes['PaymentRecordAndPaymentNode'] = ResolversParentTypes['PaymentRecordAndPaymentNode']> = { caId?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>; currency?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>; @@ -30234,6 +30302,9 @@ export type Resolvers<ContextType = any> = { PaymentPlanNode?: PaymentPlanNodeResolvers<ContextType>; PaymentPlanNodeConnection?: PaymentPlanNodeConnectionResolvers<ContextType>; PaymentPlanNodeEdge?: PaymentPlanNodeEdgeResolvers<ContextType>; + PaymentPlanSupportingDocumentNode?: PaymentPlanSupportingDocumentNodeResolvers<ContextType>; + PaymentPlanSupportingDocumentNodeConnection?: PaymentPlanSupportingDocumentNodeConnectionResolvers<ContextType>; + PaymentPlanSupportingDocumentNodeEdge?: PaymentPlanSupportingDocumentNodeEdgeResolvers<ContextType>; PaymentRecordAndPaymentNode?: PaymentRecordAndPaymentNodeResolvers<ContextType>; PaymentRecordNode?: PaymentRecordNodeResolvers<ContextType>; PaymentRecordNodeConnection?: PaymentRecordNodeConnectionResolvers<ContextType>; diff --git a/src/frontend/src/__generated__/introspection-result.ts b/src/frontend/src/__generated__/introspection-result.ts index b48965a375..be91b923e8 100644 --- a/src/frontend/src/__generated__/introspection-result.ts +++ b/src/frontend/src/__generated__/introspection-result.ts @@ -39,6 +39,7 @@ "PaymentHouseholdSnapshotNode", "PaymentNode", "PaymentPlanNode", + "PaymentPlanSupportingDocumentNode", "PaymentRecordNode", "PaymentVerificationLogEntryNode", "PaymentVerificationNode", diff --git a/src/frontend/src/api/paymentModuleApi.ts b/src/frontend/src/api/paymentModuleApi.ts index 106b14384a..52f2ea9331 100644 --- a/src/frontend/src/api/paymentModuleApi.ts +++ b/src/frontend/src/api/paymentModuleApi.ts @@ -38,3 +38,54 @@ export const bulkActionPaymentPlansManagerial = async ( ); return response.data; }; + +export const deleteSupportingDocument = async ( + businessArea: string, + programId: string, + paymentPlanId: string, + fileId: string, +) => { + try { + await api.delete( + `${businessArea}/programs/${programId}/payment-plans/${paymentPlanId}/supporting-documents/${fileId}/`, + ); + return { success: true }; + } catch (error: any) { + const errorMessage = error?.message || 'An unknown error occurred'; + throw new Error(`Failed to delete supporting document: ${errorMessage}`); + } +}; + +export const uploadSupportingDocument = async ( + businessArea: string, + programId: string, + paymentPlanId: string, + file: File, + title: string, +) => { + const formData = new FormData(); + formData.append('file', file); + formData.append('title', title); + + try { + const response = await api.post( + `${businessArea}/programs/${programId}/payment-plans/${paymentPlanId}/supporting-documents/`, + formData, + ); + return response.data; // Return the response data + } catch (error) { + throw new Error(`Failed to upload supporting document: ${error.message}`); + } +}; + +export const fetchSupportingDocument = async ( + businessAreaSlug: string, + programId: string, + paymentPlanId: string, + fileId: string, +): Promise<any> => { + const response = await api.get( + `${businessAreaSlug}/programs/${programId}/payment-plans/${paymentPlanId}/supporting-documents/${fileId}/download/`, + ); + return response; +}; diff --git a/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts b/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts index de2483cf53..a0590581b6 100644 --- a/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts +++ b/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts @@ -262,6 +262,11 @@ export const PAYMENT_PLAN_QUERY = gql` unicefId } unsuccessfulPaymentsCount + supportingDocuments { + id + title + file + } } } `; diff --git a/src/frontend/src/components/core/DropzoneField.tsx b/src/frontend/src/components/core/DropzoneField.tsx index d7eaa1c03b..96bd070f15 100644 --- a/src/frontend/src/components/core/DropzoneField.tsx +++ b/src/frontend/src/components/core/DropzoneField.tsx @@ -10,6 +10,13 @@ interface DropzoneContainerProps { disabled: boolean; } +interface DropzoneFieldProps { + onChange: (acceptedFiles: File[]) => void; + loading: boolean; + dontShowFilename: boolean; + accepts?: { [key: string]: string[] }; +} + const DropzoneContainer = styled.div<DropzoneContainerProps>` width: 500px; height: 100px; @@ -32,11 +39,12 @@ export const DropzoneField = ({ onChange, loading, dontShowFilename, -}: { - onChange: (acceptedFiles: File[]) => void; - loading: boolean; - dontShowFilename: boolean; -}): React.ReactElement => { + accepts = { + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': [ + '.xlsx', + ], + }, +}: DropzoneFieldProps): React.ReactElement => { const { t } = useTranslation(); const onDrop = useCallback((acceptedFiles: File[]) => { onChange(acceptedFiles); @@ -44,24 +52,22 @@ export const DropzoneField = ({ const { getRootProps, getInputProps, acceptedFiles } = useDropzone({ disabled: loading, - accept: { - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': [ - '.xlsx', - ], - }, + accept: accepts, onDrop, }); + const acceptedFilename = acceptedFiles.length > 0 ? acceptedFiles[0].name : null; + return ( <Box display="flex" justifyContent="center" p={5}> <DropzoneContainer {...getRootProps()} disabled={loading}> <LoadingComponent isLoading={loading} absolute /> <input {...getInputProps()} - accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + accept={Object.values(accepts).flat().join(',')} data-cy="file-input" - />{' '} + /> {dontShowFilename || !acceptedFilename ? t('UPLOAD FILE') : acceptedFilename} diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx new file mode 100644 index 0000000000..70f2afcbac --- /dev/null +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx @@ -0,0 +1,395 @@ +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import DownloadIcon from '@mui/icons-material/Download'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { PaperContainer } from '@components/targeting/PaperContainer'; +import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; +import { GreyText } from '@components/core/GreyText'; +import { LoadingButton } from '@components/core/LoadingButton'; +import { + Typography, + IconButton, + Collapse, + Dialog, + DialogTitle, + TextField, + DialogActions, + Button, + Box, + Grid, + DialogContent, + FormHelperText, +} from '@mui/material'; +import { useState } from 'react'; +import { useMutation } from '@tanstack/react-query'; +import { usePermissions } from '@hooks/usePermissions'; +import { useTranslation } from 'react-i18next'; +import { + PaymentPlanQuery, + PaymentPlanStatus, + usePaymentPlanLazyQuery, +} from '@generated/graphql'; +import { DropzoneField } from '@components/core/DropzoneField'; +import { DialogTitleWrapper } from '@containers/dialogs/DialogTitleWrapper'; +import { + deleteSupportingDocument, + uploadSupportingDocument, +} from '@api/paymentModuleApi'; +import { useSnackbar } from '@hooks/useSnackBar'; +import { useBaseUrl } from '@hooks/useBaseUrl'; +import { useConfirmation } from '@components/core/ConfirmationDialog'; +import { GreyBox } from '@components/core/GreyBox'; +import { BlueText } from '@components/grievances/LookUps/LookUpStyles'; +import { useDownloadSupportingDocument } from './SupportingDocumentsSectionActions'; + +interface SupportingDocumentsSectionProps { + initialOpen?: boolean; + paymentPlan: PaymentPlanQuery['paymentPlan']; +} + +export const SupportingDocumentsSection = ({ + initialOpen = false, + paymentPlan, +}: SupportingDocumentsSectionProps): React.ReactElement => { + const permissions = usePermissions(); + const confirm = useConfirmation(); + const { t } = useTranslation(); + const { showMessage } = useSnackbar(); + + const { mutate: downloadSupportingDocument } = + useDownloadSupportingDocument(); + + const { businessArea, programId } = useBaseUrl(); + const [documents, setDocuments] = useState( + paymentPlan?.supportingDocuments || [], + ); + const [fileToImport, setFileToImport] = useState<File | null>(null); + const [title, setTitle] = useState(''); + const [isExpanded, setIsExpanded] = useState(initialOpen); + const [isLoading, setIsLoading] = useState(false); + const [openImport, setOpenImport] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const [titleError, setTitleError] = useState(''); + + const canUploadFile = + hasPermissions(PERMISSIONS.PM_UPLOAD_SUPPORTING_DOCUMENT, permissions) && + (paymentPlan.status === PaymentPlanStatus.Locked || + paymentPlan.status === PaymentPlanStatus.Open); + + const canRemoveFile = + hasPermissions(PERMISSIONS.PM_DELETE_SUPPORTING_DOCUMENT, permissions) && + (paymentPlan.status === PaymentPlanStatus.Locked || + paymentPlan.status === PaymentPlanStatus.Open); + + const canDownloadFile = hasPermissions( + PERMISSIONS.PM_DOWNLOAD_SUPPORTING_DOCUMENT, + permissions, + ); + + const useUploadSupportingDocument = () => { + return useMutation({ + mutationFn: ({ + _businessArea, + _programId, + paymentPlanId, + file, + _title, + }: { + _businessArea: string; + _programId: string; + paymentPlanId: string; + file: File; + _title: string; + }) => + uploadSupportingDocument( + _businessArea, + _programId, + paymentPlanId, + file, + _title, + ), + onSuccess: (doc) => { + setFileToImport(null); + setTitle(''); + setIsLoading(false); + setErrorMessage(''); + showMessage(t('File uploaded successfully')); + setOpenImport(false); + setDocuments([ + ...documents, + { id: doc.id, title: doc.title, file: doc.file }, + ]); + }, + onError: (err: Error) => { + setErrorMessage(err.message); + setIsLoading(false); + }, + }); + }; + + const { mutate } = useUploadSupportingDocument(); + + const handleUpload = (): void => { + const maxFiles = 10; + const currentFileCount = documents.length; + + if (currentFileCount >= maxFiles) { + setErrorMessage(t('You cannot upload more than 10 files.')); + return; + } + if (!fileToImport || !title) { + setErrorMessage(t('Please select a file and enter a title.')); + if (!title) { + setTitleError(t('Title is required.')); + } + return; + } + if (fileToImport.size > 10 * 1024 * 1024) { + setErrorMessage(t('File size must be ≤ 10MB.')); + return; + } + const validExtensions = ['xlsx', 'pdf', 'jpg', 'jpeg', 'png']; + const fileExtension = fileToImport.name.split('.').pop()?.toLowerCase(); + if (!fileExtension || !validExtensions.includes(fileExtension)) { + setErrorMessage(t('Unsupported file type.')); + return; + } + setIsLoading(true); + mutate({ + _businessArea: businessArea, + _programId: programId, + paymentPlanId: paymentPlan.id, + file: fileToImport, + _title: title, + }); + }; + + const handleRemove = async ( + _businessArea: string, + _programId: string, + paymentPlanId: string, + fileId: string, + ) => { + try { + await deleteSupportingDocument( + _businessArea, + _programId, + paymentPlanId, + fileId, + ); + setDocuments(documents.filter((doc) => doc.id !== fileId)); + showMessage(t('File deleted successfully.')); + } catch (err) { + setErrorMessage( + t(`Failed to delete supporting document: ${err.message}`), + ); + } + }; + + const confirmationModalTitle = t('Deleting Supporting Document'); + const confirmationText = t( + 'Are you sure you want to delete this file? This action cannot be reversed.', + ); + + const handleSupportingDocumentDownloadClick = (fileId: string) => { + downloadSupportingDocument({ + businessAreaSlug: businessArea, + programId, + paymentPlanId: paymentPlan.id, + fileId: fileId.toString(), + }); + }; + + const handleExpandClick = () => { + setIsExpanded(!isExpanded); + }; + + const handleUploadClick = () => { + setOpenImport(true); + }; + + return ( + <PaperContainer> + <Box display="flex" justifyContent="space-between" alignItems="center"> + <Typography variant="h6" data-cy="supporting-documents-title"> + {t('Supporting Documents')} + </Typography> + <Box display="flex"> + {canUploadFile && ( + <Box mr={1}> + <Button + variant="contained" + color="primary" + onClick={handleUploadClick} + data-cy="upload-file-button" + > + {t('Upload File')} + </Button> + </Box> + )} + <Box> + {documents.length > 0 && ( + <IconButton onClick={handleExpandClick} data-cy="expand-button"> + {isExpanded ? <ExpandLessIcon /> : <ExpandMoreIcon />} + </IconButton> + )} + </Box> + </Box> + </Box> + + {documents.length === 0 && ( + <GreyText data-cy="supporting-documents-empty"> + {t('No documents uploaded')} + </GreyText> + )} + + <Collapse in={isExpanded}> + <Box mt={2}> + <Grid container spacing={3}> + {documents.map((doc) => ( + <Grid key={doc.id} item xs={3}> + <GreyBox p={3} key={doc.id} data-cy="document-item"> + <Box + display="flex" + justifyContent="space-between" + alignItems="center" + > + <Box display="flex" flexDirection="column"> + <BlueText>{doc.title}</BlueText> + <BlueText>{doc.file}</BlueText> + </Box> + <Box> + {canDownloadFile && ( + <IconButton + onClick={() => + handleSupportingDocumentDownloadClick(doc.id) + } + data-cy="download-button" + > + <DownloadIcon /> + </IconButton> + )} + {canRemoveFile && ( + <IconButton + onClick={() => + confirm({ + title: confirmationModalTitle, + content: confirmationText, + type: 'error', + }).then(() => + handleRemove( + businessArea, + programId, + paymentPlan.id, + doc.id, + ), + ) + } + > + <DeleteIcon /> + </IconButton> + )} + </Box> + </Box> + </GreyBox> + </Grid> + ))} + </Grid> + </Box> + </Collapse> + {canUploadFile && ( + <Dialog + open={openImport} + onClose={() => setOpenImport(false)} + scroll="paper" + aria-labelledby="form-dialog-title" + > + <DialogTitleWrapper data-cy="dialog-import"> + <DialogTitle>{t('Select File to Upload')}</DialogTitle> + </DialogTitleWrapper> + <DialogContent> + <Box pb={2}> + <GreyText> + {t( + 'The system accepts the following file extensions: XLSX, PDF, images (jpg, jpeg, png). File size must be ≤ 10MB.', + )} + </GreyText> + </Box> + <> + <DropzoneField + dontShowFilename={false} + accepts={{ + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': + ['.xlsx'], + 'application/pdf': ['.pdf'], + 'image/jpeg': ['.jpeg', '.jpg'], + 'image/png': ['.png'], + }} + loading={isLoading} + onChange={(files) => { + if (files.length === 0) { + return; + } + const file = files[0]; + const fileSizeMB = file.size / (1024 * 1024); + if (fileSizeMB > 200) { + setErrorMessage( + `File size is too big. It should be under 200MB, File size is ${fileSizeMB}MB`, + ); + return; + } + + setFileToImport(file); + }} + data-cy="dropzone-field" + /> + <FormHelperText error>{errorMessage}</FormHelperText> + <Grid container> + <Grid item xs={12}> + <TextField + size="small" + label={t('Title')} + value={title} + onChange={(e) => { + setTitle(e.target.value); + setTitleError(''); + }} + fullWidth + margin="normal" + data-cy="title-input" + error={!!titleError} + helperText={titleError} + /> + </Grid> + </Grid> + </> + </DialogContent> + <DialogActions> + <Button + data-cy="close-button" + onClick={() => { + setOpenImport(false); + setFileToImport(null); + setErrorMessage(''); + setTitle(''); + }} + > + {t('CANCEL')} + </Button> + <LoadingButton + loading={isLoading} + disabled={!fileToImport} + type="submit" + color="primary" + variant="contained" + onClick={handleUpload} + data-cy="button-import-submit" + > + {t('Upload')} + </LoadingButton> + </DialogActions> + </Dialog> + )} + </PaperContainer> + ); +}; diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSectionActions.ts b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSectionActions.ts new file mode 100644 index 0000000000..b1d360393f --- /dev/null +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSectionActions.ts @@ -0,0 +1,35 @@ +import { + fetchSupportingDocument, + uploadSupportingDocument, +} from '@api/paymentModuleApi'; +import { useMutation } from '@tanstack/react-query'; +import { t } from 'i18next'; +import { title } from 'process'; + +export const useDownloadSupportingDocument = () => { + return useMutation({ + mutationFn: ({ + businessAreaSlug: mutationBusinessAreaSlug, + programId: mutationProgramId, + paymentPlanId: mutationPaymentPlanId, + fileId: mutationFileId, + }: { + businessAreaSlug: string; + programId: string; + paymentPlanId: string; + fileId: string; + }) => { + return fetchSupportingDocument( + mutationBusinessAreaSlug, + mutationProgramId, + mutationPaymentPlanId, + mutationFileId, + ); + }, + onSuccess: (data) => { + if (data.url) { + window.open(data.url); + } + }, + }); +}; diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUploadDialog.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUploadDialog.tsx index 374c00b4b7..3ba4438eb8 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUploadDialog.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUploadDialog.tsx @@ -15,6 +15,7 @@ import { useBaseUrl } from '@hooks/useBaseUrl'; import { usePermissions } from '@hooks/usePermissions'; import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; import { ButtonTooltip } from '@components/core/ButtonTooltip'; +import { GreyText } from '@components/core/GreyText'; const Error = styled.div` color: ${({ theme }) => theme.palette.error.dark}; @@ -96,7 +97,16 @@ export const PeriodDataUpdatesUploadDialog = (): React.ReactElement => { aria-labelledby="form-dialog-title" > <DialogTitleWrapper data-cy="dialog-import"> - <DialogTitle>{t('Periodic Data Updates')}</DialogTitle> + <DialogTitle> + <Box display="flex" flexDirection="column"> + {t('Select Files to Upload')} + <GreyText> + {t( + 'The system accepts the following file extensions: XLSX, PDF, images (jpg, jpeg, png). File size must be \\u2264 10MB.', + )} + </GreyText> + </Box> + </DialogTitle> <> <DropzoneField dontShowFilename={false} diff --git a/src/frontend/src/components/rdi/create/xlsx/CreateImportFromXlsxForm.tsx b/src/frontend/src/components/rdi/create/xlsx/CreateImportFromXlsxForm.tsx index b9c33253f3..b223b1d565 100644 --- a/src/frontend/src/components/rdi/create/xlsx/CreateImportFromXlsxForm.tsx +++ b/src/frontend/src/components/rdi/create/xlsx/CreateImportFromXlsxForm.tsx @@ -1,19 +1,19 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { Box, CircularProgress } from '@mui/material'; -import { Field, FormikProvider, useFormik } from 'formik'; -import { useEffect } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useNavigate } from 'react-router-dom'; -import styled from 'styled-components'; -import * as Yup from 'yup'; import { ImportDataStatus, useCreateRegistrationXlsxImportMutation, } from '@generated/graphql'; import { useBaseUrl } from '@hooks/useBaseUrl'; import { useSnackbar } from '@hooks/useSnackBar'; -import { FormikTextField } from '@shared/Formik/FormikTextField'; +import { Box, CircularProgress } from '@mui/material'; import { FormikCheckboxField } from '@shared/Formik/FormikCheckboxField'; +import { FormikTextField } from '@shared/Formik/FormikTextField'; +import { Field, FormikProvider, useFormik } from 'formik'; +import { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router-dom'; +import styled from 'styled-components'; +import * as Yup from 'yup'; import { ScreenBeneficiaryField } from '../ScreenBeneficiaryField'; import { DropzoneField } from './DropzoneField'; import { XlsxImportDataRepresentation } from './XlsxImportDataRepresentation'; @@ -135,7 +135,9 @@ export function CreateImportFromXlsxForm({ <Field name="allowDeliveryMechanismsValidationErrors" fullWidth - label={t('Ignore Delivery Mechanisms Validation Errors and Create Grievance Tickets')} + label={t( + 'Ignore Delivery Mechanisms Validation Errors and Create Grievance Tickets', + )} variant="outlined" component={FormikCheckboxField} /> diff --git a/src/frontend/src/config/permissions.ts b/src/frontend/src/config/permissions.ts index 74b632b6e7..cb79a14011 100644 --- a/src/frontend/src/config/permissions.ts +++ b/src/frontend/src/config/permissions.ts @@ -93,6 +93,9 @@ export const PERMISSIONS = { 'PM_EXCLUDE_BENEFICIARIES_FROM_FOLLOW_UP_PP', PM_EXPORT_PDF_SUMMARY: 'PM_EXPORT_PDF_SUMMARY', PM_VIEW_FSP_AUTH_CODE: 'PM_VIEW_FSP_AUTH_CODE', + PM_UPLOAD_SUPPORTING_DOCUMENT: 'PM_UPLOAD_SUPPORTING_DOCUMENT', + PM_DELETE_SUPPORTING_DOCUMENT: 'PM_DELETE_SUPPORTING_DOCUMENT', + PM_DOWNLOAD_SUPPORTING_DOCUMENT: 'PM_DOWNLOAD_SUPPORTING_DOCUMENT', PAYMENT_VIEW_LIST_MANAGERIAL: 'PAYMENT_VIEW_LIST_MANAGERIAL', PAYMENT_VIEW_LIST_MANAGERIAL_RELEASED: 'PAYMENT_VIEW_LIST_MANAGERIAL_RELEASED', diff --git a/src/frontend/src/containers/pages/paymentmodule/FollowUpPaymentPlanDetailsPage.tsx b/src/frontend/src/containers/pages/paymentmodule/FollowUpPaymentPlanDetailsPage.tsx index e5148971d2..87f4bdde43 100644 --- a/src/frontend/src/containers/pages/paymentmodule/FollowUpPaymentPlanDetailsPage.tsx +++ b/src/frontend/src/containers/pages/paymentmodule/FollowUpPaymentPlanDetailsPage.tsx @@ -18,6 +18,7 @@ import { PaymentsTable } from '../../tables/paymentmodule/PaymentsTable'; import { UniversalActivityLogTable } from '../../tables/UniversalActivityLogTable'; import { ExcludeSection } from '@components/paymentmodule/PaymentPlanDetails/ExcludeSection'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { SupportingDocumentsSection } from '@components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection'; export function FollowUpPaymentPlanDetailsPage(): React.ReactElement { const { paymentPlanId } = useParams(); @@ -76,6 +77,7 @@ export function FollowUpPaymentPlanDetailsPage(): React.ReactElement { <FspSection baseUrl={baseUrl} paymentPlan={paymentPlan} /> )} <ExcludeSection paymentPlan={paymentPlan} /> + <SupportingDocumentsSection paymentPlan={paymentPlan} /> <PaymentPlanDetailsResults paymentPlan={paymentPlan} /> <PaymentsTable businessArea={businessArea} 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 73e4a5b081..ab622ec153 100644 --- a/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsPage.tsx +++ b/src/frontend/src/containers/pages/paymentmodule/ProgramCycle/PaymentPlanDetails/PaymentPlanDetailsPage.tsx @@ -22,6 +22,7 @@ import { PaymentPlanDetailsResults } from '@components/paymentmodule/PaymentPlan import { PaymentsTable } from '@containers/tables/paymentmodule/PaymentsTable'; import { ReconciliationSummary } from '@components/paymentmodule/PaymentPlanDetails/ReconciliationSummary'; import { UniversalActivityLogTable } from '@containers/tables/UniversalActivityLogTable'; +import { SupportingDocumentsSection } from '@components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection'; export const PaymentPlanDetailsPage = (): React.ReactElement => { const { paymentPlanId } = useParams(); @@ -32,7 +33,7 @@ export const PaymentPlanDetailsPage = (): React.ReactElement => { variables: { id: paymentPlanId, }, - fetchPolicy: 'cache-and-network', + fetchPolicy: 'network-only', }); const status = data?.paymentPlan?.status; @@ -88,6 +89,7 @@ export const PaymentPlanDetailsPage = (): React.ReactElement => { <FspSection baseUrl={baseUrl} paymentPlan={paymentPlan} /> )} <ExcludeSection paymentPlan={paymentPlan} /> + <SupportingDocumentsSection paymentPlan={paymentPlan} /> <PaymentPlanDetailsResults paymentPlan={paymentPlan} /> <PaymentsTable businessArea={businessArea} diff --git a/src/frontend/src/containers/pages/paymentmodulepeople/PeopleFollowUpPaymentPlanDetailsPage.tsx b/src/frontend/src/containers/pages/paymentmodulepeople/PeopleFollowUpPaymentPlanDetailsPage.tsx index 8d0b46f78c..3ecbc4de4c 100644 --- a/src/frontend/src/containers/pages/paymentmodulepeople/PeopleFollowUpPaymentPlanDetailsPage.tsx +++ b/src/frontend/src/containers/pages/paymentmodulepeople/PeopleFollowUpPaymentPlanDetailsPage.tsx @@ -18,6 +18,7 @@ import { PaymentsTable } from '../../tables/paymentmodule/PaymentsTable'; import { UniversalActivityLogTable } from '../../tables/UniversalActivityLogTable'; import { ExcludeSection } from '@components/paymentmodule/PaymentPlanDetails/ExcludeSection'; import { useBaseUrl } from '@hooks/useBaseUrl'; +import { SupportingDocumentsSection } from '@components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection'; export const PeopleFollowUpPaymentPlanDetailsPage = (): React.ReactElement => { const { paymentPlanId } = useParams(); @@ -76,6 +77,7 @@ export const PeopleFollowUpPaymentPlanDetailsPage = (): React.ReactElement => { <FspSection baseUrl={baseUrl} paymentPlan={paymentPlan} /> )} <ExcludeSection paymentPlan={paymentPlan} /> + <SupportingDocumentsSection paymentPlan={paymentPlan} /> <PaymentPlanDetailsResults paymentPlan={paymentPlan} /> <PaymentsTable businessArea={businessArea} diff --git a/src/frontend/src/containers/pages/paymentmodulepeople/PeoplePaymentPlanDetailsPage.tsx b/src/frontend/src/containers/pages/paymentmodulepeople/PeoplePaymentPlanDetailsPage.tsx index 8fc81f12fa..460a897ec3 100644 --- a/src/frontend/src/containers/pages/paymentmodulepeople/PeoplePaymentPlanDetailsPage.tsx +++ b/src/frontend/src/containers/pages/paymentmodulepeople/PeoplePaymentPlanDetailsPage.tsx @@ -23,6 +23,7 @@ import { UniversalActivityLogTable } from '../../tables/UniversalActivityLogTabl import { PeoplePaymentPlanDetailsResults } from '@components/paymentmodulepeople/PaymentPlanDetails/PeoplePaymentPlanDetailsResults'; import { PeoplePaymentsTable } from '@containers/tables/paymentmodulePeople/PeoplePaymentsTable'; import { ExcludeSection } from '@components/paymentmodulepeople/PaymentPlanDetails/ExcludeSection'; +import { SupportingDocumentsSection } from '@components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection'; export const PeoplePaymentPlanDetailsPage = (): React.ReactElement => { const { paymentPlanId } = useParams(); @@ -90,6 +91,7 @@ export const PeoplePaymentPlanDetailsPage = (): React.ReactElement => { <FspSection baseUrl={baseUrl} paymentPlan={paymentPlan} /> )} <ExcludeSection paymentPlan={paymentPlan} /> + <SupportingDocumentsSection paymentPlan={paymentPlan} /> <PeoplePaymentPlanDetailsResults paymentPlan={paymentPlan} /> <PeoplePaymentsTable businessArea={businessArea} diff --git a/src/frontend/src/utils/en.json b/src/frontend/src/utils/en.json index fc3a7bd5f9..75d4ee7696 100644 --- a/src/frontend/src/utils/en.json +++ b/src/frontend/src/utils/en.json @@ -901,6 +901,11 @@ "Algorithm similarity score:": "Algorithm similarity score:", "Face images matching suggests:": "Face images matching suggests:", "Programme Partners updated.": "Programme Partners updated.", + "Select File to Upload": "Select File to Upload", + "The system accepts the following file extensions: XLSX, PDF, images (jpg, jpeg, png). File size must be ≤ 10MB.": "The system accepts the following file extensions: XLSX, PDF, images (jpg, jpeg, png). File size must be ≤ 10MB.", + "Deleting Supporting Document": "Deleting Supporting Document", + "You cannot upload more than 10 files.": "You cannot upload more than 10 files.", + "File deleted successfully.": "File deleted successfully.", "FX Rate Applied": "FX Rate Applied", "If displayed exchange rate differs from Vision, please contact your designated focal point for resolutionIf displayed exchange rate differs from Vision, please contact your designated focal point for resolution": "If displayed exchange rate differs from Vision, please contact your designated focal point for resolution" } diff --git a/src/hct_mis_api/api/urls.py b/src/hct_mis_api/api/urls.py index 25e01d571a..0871c71f28 100644 --- a/src/hct_mis_api/api/urls.py +++ b/src/hct_mis_api/api/urls.py @@ -42,7 +42,7 @@ "<slug:business_area>/", include( [ - path("payments/", include("hct_mis_api.apps.payment.api.urls", namespace="payments")), + path("payments/", include("hct_mis_api.apps.payment.api.urls.payments", namespace="payments")), path("program/", endpoints.rdi.ProgramViewSet.as_view({"get": "list"}), name="program-list"), path( "program/create/", @@ -70,6 +70,10 @@ "programs/<str:program_id>/", include( [ + path( + "payment-plans/<str:payment_plan_id>/", + include("hct_mis_api.apps.payment.api.urls.payment_plans", namespace="payment-plan"), + ), path( "periodic-data-update/", include( diff --git a/src/hct_mis_api/apps/account/api/permissions.py b/src/hct_mis_api/apps/account/api/permissions.py index 1d950867a3..f421deebd2 100644 --- a/src/hct_mis_api/apps/account/api/permissions.py +++ b/src/hct_mis_api/apps/account/api/permissions.py @@ -81,3 +81,15 @@ class ProgramCycleUpdatePermission(BaseRestPermission): class ProgramCycleDeletePermission(BaseRestPermission): PERMISSIONS = [Permissions.PM_PROGRAMME_CYCLE_DELETE] + + +class PaymentPlanSupportingDocumentUploadPermission(BaseRestPermission): + PERMISSIONS = [Permissions.PM_UPLOAD_SUPPORTING_DOCUMENT] + + +class PaymentPlanSupportingDocumentDownloadPermission(BaseRestPermission): + PERMISSIONS = [Permissions.PM_DOWNLOAD_SUPPORTING_DOCUMENT] + + +class PaymentPlanSupportingDocumentDeletePermission(BaseRestPermission): + PERMISSIONS = [Permissions.PM_DELETE_SUPPORTING_DOCUMENT] diff --git a/src/hct_mis_api/apps/account/fixtures/data.json b/src/hct_mis_api/apps/account/fixtures/data.json index 7dee5ae230..e5c1bb55b3 100644 --- a/src/hct_mis_api/apps/account/fixtures/data.json +++ b/src/hct_mis_api/apps/account/fixtures/data.json @@ -333,7 +333,7 @@ "updated_at": "2022-03-30 09:05:24.480-00:00", "name": "Role with all permissions", "subsystem": "HOPE", - "permissions": "[\"RDI_VIEW_LIST\", \"RDI_VIEW_DETAILS\", \"RDI_IMPORT_DATA\", \"RDI_RERUN_DEDUPE\", \"RDI_MERGE_IMPORT\", \"RDI_REFUSE_IMPORT\", \"POPULATION_VIEW_HOUSEHOLDS_LIST\", \"POPULATION_VIEW_HOUSEHOLDS_DETAILS\", \"POPULATION_VIEW_INDIVIDUALS_LIST\", \"POPULATION_VIEW_INDIVIDUALS_DETAILS\", \"PROGRAMME_VIEW_LIST_AND_DETAILS\", \"PROGRAMME_MANAGEMENT_VIEW\", \"PROGRAMME_DUPLICATE\", \"PROGRAMME_VIEW_PAYMENT_RECORD_DETAILS\", \"PROGRAMME_CREATE\", \"PROGRAMME_UPDATE\", \"PROGRAMME_REMOVE\", \"PROGRAMME_ACTIVATE\", \"PROGRAMME_FINISH\", \"TARGETING_VIEW_LIST\", \"TARGETING_VIEW_DETAILS\", \"TARGETING_CREATE\", \"TARGETING_UPDATE\", \"TARGETING_DUPLICATE\", \"TARGETING_REMOVE\", \"TARGETING_LOCK\", \"TARGETING_UNLOCK\", \"TARGETING_SEND\", \"PAYMENT_VERIFICATION_VIEW_LIST\", \"PAYMENT_VERIFICATION_VIEW_DETAILS\", \"PAYMENT_VERIFICATION_CREATE\", \"PAYMENT_VERIFICATION_UPDATE\", \"PAYMENT_VERIFICATION_ACTIVATE\", \"PAYMENT_VERIFICATION_DISCARD\", \"PAYMENT_VERIFICATION_FINISH\", \"PAYMENT_VERIFICATION_EXPORT\", \"PAYMENT_VERIFICATION_IMPORT\", \"PAYMENT_VERIFICATION_VERIFY\", \"PAYMENT_VERIFICATION_VIEW_PAYMENT_RECORD_DETAILS\", \"PAYMENT_VERIFICATION_DELETE\", \"PAYMENT_VERIFICATION_MARK_AS_FAILED\", \"USER_MANAGEMENT_VIEW_LIST\", \"DASHBOARD_VIEW_COUNTRY\", \"DASHBOARD_EXPORT\", \"GRIEVANCES_VIEW_LIST_EXCLUDING_SENSITIVE\", \"GRIEVANCES_VIEW_LIST_EXCLUDING_SENSITIVE_AS_CREATOR\", \"GRIEVANCES_VIEW_LIST_EXCLUDING_SENSITIVE_AS_OWNER\", \"GRIEVANCES_VIEW_LIST_SENSITIVE\", \"GRIEVANCES_VIEW_LIST_SENSITIVE_AS_CREATOR\", \"GRIEVANCES_VIEW_LIST_SENSITIVE_AS_OWNER\", \"GRIEVANCES_VIEW_DETAILS_EXCLUDING_SENSITIVE\", \"GRIEVANCES_VIEW_DETAILS_EXCLUDING_SENSITIVE_AS_CREATOR\", \"GRIEVANCES_VIEW_DETAILS_EXCLUDING_SENSITIVE_AS_OWNER\", \"GRIEVANCES_VIEW_DETAILS_SENSITIVE\", \"GRIEVANCES_VIEW_DETAILS_SENSITIVE_AS_CREATOR\", \"GRIEVANCES_VIEW_DETAILS_SENSITIVE_AS_OWNER\", \"GRIEVANCES_VIEW_HOUSEHOLD_DETAILS\", \"GRIEVANCES_VIEW_HOUSEHOLD_DETAILS_AS_CREATOR\", \"GRIEVANCES_VIEW_HOUSEHOLD_DETAILS_AS_OWNER\", \"GRIEVANCES_VIEW_INDIVIDUALS_DETAILS\", \"GRIEVANCES_VIEW_INDIVIDUALS_DETAILS_AS_CREATOR\", \"GRIEVANCES_VIEW_INDIVIDUALS_DETAILS_AS_OWNER\", \"GRIEVANCES_CREATE\", \"GRIEVANCES_UPDATE\", \"GRIEVANCES_UPDATE_AS_CREATOR\", \"GRIEVANCES_UPDATE_AS_OWNER\", \"GRIEVANCES_UPDATE_REQUESTED_DATA_CHANGE\", \"GRIEVANCES_UPDATE_REQUESTED_DATA_CHANGE_AS_CREATOR\", \"GRIEVANCES_UPDATE_REQUESTED_DATA_CHANGE_AS_OWNER\", \"GRIEVANCES_ADD_NOTE\", \"GRIEVANCES_ADD_NOTE_AS_CREATOR\", \"GRIEVANCES_ADD_NOTE_AS_OWNER\", \"GRIEVANCES_SET_IN_PROGRESS\", \"GRIEVANCES_SET_IN_PROGRESS_AS_CREATOR\", \"GRIEVANCES_SET_IN_PROGRESS_AS_OWNER\", \"GRIEVANCES_SET_ON_HOLD\", \"GRIEVANCES_SET_ON_HOLD_AS_CREATOR\", \"GRIEVANCES_SET_ON_HOLD_AS_OWNER\", \"GRIEVANCES_SEND_FOR_APPROVAL\", \"GRIEVANCES_SEND_FOR_APPROVAL_AS_CREATOR\", \"GRIEVANCES_SEND_FOR_APPROVAL_AS_OWNER\", \"GRIEVANCES_SEND_BACK\", \"GRIEVANCES_SEND_BACK_AS_CREATOR\", \"GRIEVANCES_SEND_BACK_AS_OWNER\", \"GRIEVANCES_APPROVE_DATA_CHANGE\", \"GRIEVANCES_APPROVE_DATA_CHANGE_AS_CREATOR\", \"GRIEVANCES_APPROVE_DATA_CHANGE_AS_OWNER\", \"GRIEVANCES_CLOSE_TICKET_EXCLUDING_FEEDBACK\", \"GRIEVANCES_CLOSE_TICKET_EXCLUDING_FEEDBACK_AS_CREATOR\", \"GRIEVANCES_CLOSE_TICKET_EXCLUDING_FEEDBACK_AS_OWNER\", \"GRIEVANCES_CLOSE_TICKET_FEEDBACK\", \"GRIEVANCES_CLOSE_TICKET_FEEDBACK_AS_CREATOR\", \"GRIEVANCES_CLOSE_TICKET_FEEDBACK_AS_OWNER\", \"GRIEVANCES_APPROVE_FLAG_AND_DEDUPE\", \"GRIEVANCES_APPROVE_FLAG_AND_DEDUPE_AS_CREATOR\", \"GRIEVANCES_APPROVE_FLAG_AND_DEDUPE_AS_OWNER\", \"GRIEVANCE_ASSIGN\", \"REPORTING_EXPORT\", \"ALL_VIEW_PII_DATA_ON_LISTS\", \"ACTIVITY_LOG_VIEW\", \"ACTIVITY_LOG_DOWNLOAD\", \"PM_CREATE\", \"PM_VIEW_DETAILS\", \"PM_VIEW_LIST\", \"PM_EXPORT_XLSX_FOR_FSP\", \"PM_DOWNLOAD_XLSX_FOR_FSP\", \"PM_SENDING_PAYMENT_PLAN_TO_FSP\", \"PM_MARK_PAYMENT_AS_FAILED\", \"PM_EXPORT_PDF_SUMMARY\", \"PAYMENT_VERIFICATION_INVALID\", \"GRIEVANCES_APPROVE_PAYMENT_VERIFICATION\", \"GRIEVANCES_APPROVE_PAYMENT_VERIFICATION_AS_CREATOR\", \"GRIEVANCES_APPROVE_PAYMENT_VERIFICATION_AS_OWNER\", \"GRIEVANCE_DOCUMENTS_UPLOAD\", \"PM_IMPORT_XLSX_WITH_ENTITLEMENTS\", \"PM_APPLY_RULE_ENGINE_FORMULA_WITH_ENTITLEMENTS\", \"PM_ADMIN_FINANCIAL_SERVICE_PROVIDER_UPDATE\", \"PM_LOCK_AND_UNLOCK\", \"PM_LOCK_AND_UNLOCK_FSP\", \"PM_EXCLUDE_BENEFICIARIES_FROM_FOLLOW_UP_PP\", \"PM_SEND_FOR_APPROVAL\", \"PM_ACCEPTANCE_PROCESS_APPROVE\", \"PM_ACCEPTANCE_PROCESS_AUTHORIZE\", \"PM_ACCEPTANCE_PROCESS_FINANCIAL_REVIEW\", \"PM_IMPORT_XLSX_WITH_RECONCILIATION\", \"ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_LIST\", \"ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_DETAILS\", \"ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_CREATE\", \"ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_DETAILS_AS_CREATOR\", \"GRIEVANCES_FEEDBACK_VIEW_CREATE\", \"GRIEVANCES_FEEDBACK_VIEW_LIST\", \"GRIEVANCES_FEEDBACK_VIEW_DETAILS\", \"GRIEVANCES_FEEDBACK_VIEW_UPDATE\", \"ACCOUNTABILITY_SURVEY_VIEW_CREATE\", \"ACCOUNTABILITY_SURVEY_VIEW_LIST\", \"ACCOUNTABILITY_SURVEY_VIEW_DETAILS\", \"GRIEVANCES_FEEDBACK_MESSAGE_VIEW_CREATE\", \"CAN_ADD_BUSINESS_AREA_TO_PARTNER\", \"GRIEVANCES_CROSS_AREA_FILTER\", \"PAYMENT_VIEW_LIST_MANAGERIAL\", \"PAYMENT_VIEW_LIST_MANAGERIAL_AS_CREATOR\", \"PAYMENT_VIEW_LIST_MANAGERIAL_AS_OWNER\", \"PAYMENT_VIEW_LIST_MANAGERIAL_AS_APPROVER\", \"PAYMENT_VIEW_LIST_MANAGERIAL_AS_AUTHORIZER\", \"PAYMENT_VIEW_LIST_MANAGERIAL\", \"PAYMENT_VIEW_LIST_MANAGERIAL_RELEASED\", \"PDU_VIEW_LIST_AND_DETAILS\", \"PDU_TEMPLATE_CREATE\", \"PDU_TEMPLATE_DOWNLOAD\", \"PDU_UPLOAD\", \"GEO_VIEW_LIST\", \"PM_PROGRAMME_CYCLE_VIEW_LIST\", \"PM_PROGRAMME_CYCLE_VIEW_DETAILS\", \"PM_PROGRAMME_CYCLE_CREATE\", \"PM_PROGRAMME_CYCLE_UPDATE\", \"PM_PROGRAMME_CYCLE_DELETE\"]" + "permissions": "[\"RDI_VIEW_LIST\", \"RDI_VIEW_DETAILS\", \"RDI_IMPORT_DATA\", \"RDI_RERUN_DEDUPE\", \"RDI_MERGE_IMPORT\", \"RDI_REFUSE_IMPORT\", \"POPULATION_VIEW_HOUSEHOLDS_LIST\", \"POPULATION_VIEW_HOUSEHOLDS_DETAILS\", \"POPULATION_VIEW_INDIVIDUALS_LIST\", \"POPULATION_VIEW_INDIVIDUALS_DETAILS\", \"PROGRAMME_VIEW_LIST_AND_DETAILS\", \"PROGRAMME_MANAGEMENT_VIEW\", \"PROGRAMME_DUPLICATE\", \"PROGRAMME_VIEW_PAYMENT_RECORD_DETAILS\", \"PROGRAMME_CREATE\", \"PROGRAMME_UPDATE\", \"PROGRAMME_REMOVE\", \"PROGRAMME_ACTIVATE\", \"PROGRAMME_FINISH\", \"TARGETING_VIEW_LIST\", \"TARGETING_VIEW_DETAILS\", \"TARGETING_CREATE\", \"TARGETING_UPDATE\", \"TARGETING_DUPLICATE\", \"TARGETING_REMOVE\", \"TARGETING_LOCK\", \"TARGETING_UNLOCK\", \"TARGETING_SEND\", \"PAYMENT_VERIFICATION_VIEW_LIST\", \"PAYMENT_VERIFICATION_VIEW_DETAILS\", \"PAYMENT_VERIFICATION_CREATE\", \"PAYMENT_VERIFICATION_UPDATE\", \"PAYMENT_VERIFICATION_ACTIVATE\", \"PAYMENT_VERIFICATION_DISCARD\", \"PAYMENT_VERIFICATION_FINISH\", \"PAYMENT_VERIFICATION_EXPORT\", \"PAYMENT_VERIFICATION_IMPORT\", \"PAYMENT_VERIFICATION_VERIFY\", \"PAYMENT_VERIFICATION_VIEW_PAYMENT_RECORD_DETAILS\", \"PAYMENT_VERIFICATION_DELETE\", \"PAYMENT_VERIFICATION_MARK_AS_FAILED\", \"USER_MANAGEMENT_VIEW_LIST\", \"DASHBOARD_VIEW_COUNTRY\", \"DASHBOARD_EXPORT\", \"GRIEVANCES_VIEW_LIST_EXCLUDING_SENSITIVE\", \"GRIEVANCES_VIEW_LIST_EXCLUDING_SENSITIVE_AS_CREATOR\", \"GRIEVANCES_VIEW_LIST_EXCLUDING_SENSITIVE_AS_OWNER\", \"GRIEVANCES_VIEW_LIST_SENSITIVE\", \"GRIEVANCES_VIEW_LIST_SENSITIVE_AS_CREATOR\", \"GRIEVANCES_VIEW_LIST_SENSITIVE_AS_OWNER\", \"GRIEVANCES_VIEW_DETAILS_EXCLUDING_SENSITIVE\", \"GRIEVANCES_VIEW_DETAILS_EXCLUDING_SENSITIVE_AS_CREATOR\", \"GRIEVANCES_VIEW_DETAILS_EXCLUDING_SENSITIVE_AS_OWNER\", \"GRIEVANCES_VIEW_DETAILS_SENSITIVE\", \"GRIEVANCES_VIEW_DETAILS_SENSITIVE_AS_CREATOR\", \"GRIEVANCES_VIEW_DETAILS_SENSITIVE_AS_OWNER\", \"GRIEVANCES_VIEW_HOUSEHOLD_DETAILS\", \"GRIEVANCES_VIEW_HOUSEHOLD_DETAILS_AS_CREATOR\", \"GRIEVANCES_VIEW_HOUSEHOLD_DETAILS_AS_OWNER\", \"GRIEVANCES_VIEW_INDIVIDUALS_DETAILS\", \"GRIEVANCES_VIEW_INDIVIDUALS_DETAILS_AS_CREATOR\", \"GRIEVANCES_VIEW_INDIVIDUALS_DETAILS_AS_OWNER\", \"GRIEVANCES_CREATE\", \"GRIEVANCES_UPDATE\", \"GRIEVANCES_UPDATE_AS_CREATOR\", \"GRIEVANCES_UPDATE_AS_OWNER\", \"GRIEVANCES_UPDATE_REQUESTED_DATA_CHANGE\", \"GRIEVANCES_UPDATE_REQUESTED_DATA_CHANGE_AS_CREATOR\", \"GRIEVANCES_UPDATE_REQUESTED_DATA_CHANGE_AS_OWNER\", \"GRIEVANCES_ADD_NOTE\", \"GRIEVANCES_ADD_NOTE_AS_CREATOR\", \"GRIEVANCES_ADD_NOTE_AS_OWNER\", \"GRIEVANCES_SET_IN_PROGRESS\", \"GRIEVANCES_SET_IN_PROGRESS_AS_CREATOR\", \"GRIEVANCES_SET_IN_PROGRESS_AS_OWNER\", \"GRIEVANCES_SET_ON_HOLD\", \"GRIEVANCES_SET_ON_HOLD_AS_CREATOR\", \"GRIEVANCES_SET_ON_HOLD_AS_OWNER\", \"GRIEVANCES_SEND_FOR_APPROVAL\", \"GRIEVANCES_SEND_FOR_APPROVAL_AS_CREATOR\", \"GRIEVANCES_SEND_FOR_APPROVAL_AS_OWNER\", \"GRIEVANCES_SEND_BACK\", \"GRIEVANCES_SEND_BACK_AS_CREATOR\", \"GRIEVANCES_SEND_BACK_AS_OWNER\", \"GRIEVANCES_APPROVE_DATA_CHANGE\", \"GRIEVANCES_APPROVE_DATA_CHANGE_AS_CREATOR\", \"GRIEVANCES_APPROVE_DATA_CHANGE_AS_OWNER\", \"GRIEVANCES_CLOSE_TICKET_EXCLUDING_FEEDBACK\", \"GRIEVANCES_CLOSE_TICKET_EXCLUDING_FEEDBACK_AS_CREATOR\", \"GRIEVANCES_CLOSE_TICKET_EXCLUDING_FEEDBACK_AS_OWNER\", \"GRIEVANCES_CLOSE_TICKET_FEEDBACK\", \"GRIEVANCES_CLOSE_TICKET_FEEDBACK_AS_CREATOR\", \"GRIEVANCES_CLOSE_TICKET_FEEDBACK_AS_OWNER\", \"GRIEVANCES_APPROVE_FLAG_AND_DEDUPE\", \"GRIEVANCES_APPROVE_FLAG_AND_DEDUPE_AS_CREATOR\", \"GRIEVANCES_APPROVE_FLAG_AND_DEDUPE_AS_OWNER\", \"GRIEVANCE_ASSIGN\", \"REPORTING_EXPORT\", \"ALL_VIEW_PII_DATA_ON_LISTS\", \"ACTIVITY_LOG_VIEW\", \"ACTIVITY_LOG_DOWNLOAD\", \"PM_CREATE\", \"PM_VIEW_DETAILS\", \"PM_VIEW_LIST\", \"PM_EXPORT_XLSX_FOR_FSP\", \"PM_DOWNLOAD_XLSX_FOR_FSP\", \"PM_SENDING_PAYMENT_PLAN_TO_FSP\", \"PM_MARK_PAYMENT_AS_FAILED\", \"PM_EXPORT_PDF_SUMMARY\", \"PAYMENT_VERIFICATION_INVALID\", \"GRIEVANCES_APPROVE_PAYMENT_VERIFICATION\", \"GRIEVANCES_APPROVE_PAYMENT_VERIFICATION_AS_CREATOR\", \"GRIEVANCES_APPROVE_PAYMENT_VERIFICATION_AS_OWNER\", \"GRIEVANCE_DOCUMENTS_UPLOAD\", \"PM_IMPORT_XLSX_WITH_ENTITLEMENTS\", \"PM_APPLY_RULE_ENGINE_FORMULA_WITH_ENTITLEMENTS\", \"PM_ADMIN_FINANCIAL_SERVICE_PROVIDER_UPDATE\", \"PM_LOCK_AND_UNLOCK\", \"PM_LOCK_AND_UNLOCK_FSP\", \"PM_EXCLUDE_BENEFICIARIES_FROM_FOLLOW_UP_PP\", \"PM_SEND_FOR_APPROVAL\", \"PM_ACCEPTANCE_PROCESS_APPROVE\", \"PM_ACCEPTANCE_PROCESS_AUTHORIZE\", \"PM_ACCEPTANCE_PROCESS_FINANCIAL_REVIEW\", \"PM_IMPORT_XLSX_WITH_RECONCILIATION\", \"ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_LIST\", \"ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_DETAILS\", \"ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_CREATE\", \"ACCOUNTABILITY_COMMUNICATION_MESSAGE_VIEW_DETAILS_AS_CREATOR\", \"GRIEVANCES_FEEDBACK_VIEW_CREATE\", \"GRIEVANCES_FEEDBACK_VIEW_LIST\", \"GRIEVANCES_FEEDBACK_VIEW_DETAILS\", \"GRIEVANCES_FEEDBACK_VIEW_UPDATE\", \"ACCOUNTABILITY_SURVEY_VIEW_CREATE\", \"ACCOUNTABILITY_SURVEY_VIEW_LIST\", \"ACCOUNTABILITY_SURVEY_VIEW_DETAILS\", \"GRIEVANCES_FEEDBACK_MESSAGE_VIEW_CREATE\", \"CAN_ADD_BUSINESS_AREA_TO_PARTNER\", \"GRIEVANCES_CROSS_AREA_FILTER\", \"PAYMENT_VIEW_LIST_MANAGERIAL\", \"PAYMENT_VIEW_LIST_MANAGERIAL_AS_CREATOR\", \"PAYMENT_VIEW_LIST_MANAGERIAL_AS_OWNER\", \"PAYMENT_VIEW_LIST_MANAGERIAL_AS_APPROVER\", \"PAYMENT_VIEW_LIST_MANAGERIAL_AS_AUTHORIZER\", \"PAYMENT_VIEW_LIST_MANAGERIAL\", \"PAYMENT_VIEW_LIST_MANAGERIAL_RELEASED\", \"PDU_VIEW_LIST_AND_DETAILS\", \"PDU_TEMPLATE_CREATE\", \"PDU_TEMPLATE_DOWNLOAD\", \"PDU_UPLOAD\", \"GEO_VIEW_LIST\", \"PM_PROGRAMME_CYCLE_VIEW_LIST\", \"PM_PROGRAMME_CYCLE_VIEW_DETAILS\", \"PM_PROGRAMME_CYCLE_CREATE\", \"PM_PROGRAMME_CYCLE_UPDATE\", \"PM_PROGRAMME_CYCLE_DELETE\", \"PM_UPLOAD_SUPPORTING_DOCUMENT\", \"PM_DOWNLOAD_SUPPORTING_DOCUMENT\", \"PM_DELETE_SUPPORTING_DOCUMENT\"]" } }, { diff --git a/src/hct_mis_api/apps/account/permissions.py b/src/hct_mis_api/apps/account/permissions.py index 0c97a2038d..3a5b197a25 100644 --- a/src/hct_mis_api/apps/account/permissions.py +++ b/src/hct_mis_api/apps/account/permissions.py @@ -110,6 +110,11 @@ def _generate_next_value_( # type: ignore # https://github.com/python/mypy/issu PM_SEND_TO_PAYMENT_GATEWAY = auto() PM_VIEW_FSP_AUTH_CODE = auto() + # PaymentPlanSupportingDocument + PM_DOWNLOAD_SUPPORTING_DOCUMENT = auto() + PM_UPLOAD_SUPPORTING_DOCUMENT = auto() + PM_DELETE_SUPPORTING_DOCUMENT = auto() + # Payment Module Admin PM_ADMIN_FINANCIAL_SERVICE_PROVIDER_UPDATE = auto() diff --git a/src/hct_mis_api/apps/payment/admin.py b/src/hct_mis_api/apps/payment/admin.py index 743e8882d0..5f80eaadd0 100644 --- a/src/hct_mis_api/apps/payment/admin.py +++ b/src/hct_mis_api/apps/payment/admin.py @@ -31,6 +31,7 @@ Payment, PaymentHouseholdSnapshot, PaymentPlan, + PaymentPlanSupportingDocument, PaymentRecord, PaymentVerification, PaymentVerificationPlan, @@ -516,3 +517,8 @@ class DeliveryMechanismDataAdmin(HOPEModelAdminBase): @admin.register(DeliveryMechanism) class DeliveryMechanismAdmin(HOPEModelAdminBase): list_display = ("code", "name", "is_active", "transfer_type") + + +@admin.register(PaymentPlanSupportingDocument) +class PaymentPlanSupportingDocumentAdmin(HOPEModelAdminBase): + list_display = ("title", "created_by", "uploaded_at") diff --git a/src/hct_mis_api/apps/payment/api/serializers.py b/src/hct_mis_api/apps/payment/api/serializers.py index a792a925b0..e3fb5bb536 100644 --- a/src/hct_mis_api/apps/payment/api/serializers.py +++ b/src/hct_mis_api/apps/payment/api/serializers.py @@ -1,9 +1,10 @@ -from typing import Optional +import base64 +from typing import Any, Dict, Optional from rest_framework import serializers from hct_mis_api.apps.account.api.fields import Base64ModelField -from hct_mis_api.apps.payment.models import PaymentPlan +from hct_mis_api.apps.payment.models import PaymentPlan, PaymentPlanSupportingDocument class FollowUpPaymentPlanSerializer(serializers.ModelSerializer): @@ -56,3 +57,36 @@ class PaymentPlanBulkActionSerializer(serializers.Serializer): ids = serializers.ListField(child=serializers.CharField()) action = serializers.ChoiceField(PaymentPlan.Action.choices) comment = serializers.CharField(required=False, allow_blank=True) + + +class PaymentPlanSupportingDocumentSerializer(serializers.ModelSerializer): + id = serializers.SerializerMethodField() + + class Meta: + model = PaymentPlanSupportingDocument + fields = ["id", "title", "file", "uploaded_at", "created_by"] + + def get_id(self, obj: PaymentPlanSupportingDocument) -> str: + return base64.b64encode(f"PaymentPlanSupportingDocumentNode:{str(obj.id)}".encode()).decode() + + def validate_file(self, file: Any) -> Any: + if file.size > PaymentPlanSupportingDocument.FILE_SIZE_LIMIT: + raise serializers.ValidationError("File size must be ≤ 10MB.") + + allowed_extensions = ["pdf", "xlsx", "jpg", "jpeg", "png"] + extension = file.name.split(".")[-1].lower() + if extension not in allowed_extensions: + raise serializers.ValidationError("Unsupported file type.") + + return file + + def validate(self, data: Dict) -> Dict: + payment_plan = self.context["payment_plan"] + if payment_plan.status not in [PaymentPlan.Status.OPEN, PaymentPlan.Status.LOCKED]: + raise serializers.ValidationError("Payment plan must be within status OPEN or LOCKED.") + + if payment_plan.documents.count() >= PaymentPlanSupportingDocument.FILE_LIMIT: + raise serializers.ValidationError( + f"Payment plan already has the maximum of {PaymentPlanSupportingDocument.FILE_LIMIT} supporting documents." + ) + return data diff --git a/src/hct_mis_api/apps/payment/api/urls/__init__.py b/src/hct_mis_api/apps/payment/api/urls/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/hct_mis_api/apps/payment/api/urls/payment_plans.py b/src/hct_mis_api/apps/payment/api/urls/payment_plans.py new file mode 100644 index 0000000000..f77fa12c79 --- /dev/null +++ b/src/hct_mis_api/apps/payment/api/urls/payment_plans.py @@ -0,0 +1,14 @@ +from django.urls import include, path + +from rest_framework.routers import DefaultRouter + +from hct_mis_api.apps.payment.api.views import PaymentPlanSupportingDocumentViewSet + +app_name = "payment_plan" + +router = DefaultRouter() +router.register("supporting-documents", PaymentPlanSupportingDocumentViewSet, basename="supporting_documents") + +urlpatterns = [ + path("", include(router.urls)), +] diff --git a/src/hct_mis_api/apps/payment/api/urls.py b/src/hct_mis_api/apps/payment/api/urls/payments.py similarity index 100% rename from src/hct_mis_api/apps/payment/api/urls.py rename to src/hct_mis_api/apps/payment/api/urls/payments.py diff --git a/src/hct_mis_api/apps/payment/api/views.py b/src/hct_mis_api/apps/payment/api/views.py index c1f7f7a689..fcac590819 100644 --- a/src/hct_mis_api/apps/payment/api/views.py +++ b/src/hct_mis_api/apps/payment/api/views.py @@ -18,13 +18,20 @@ from hct_mis_api.api.caches import etag_decorator from hct_mis_api.apps.account.api.permissions import ( + PaymentPlanSupportingDocumentDeletePermission, + PaymentPlanSupportingDocumentDownloadPermission, + PaymentPlanSupportingDocumentUploadPermission, PaymentViewListManagerialPermission, PMViewListPermission, ) from hct_mis_api.apps.account.permissions import Permissions 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.api.mixins import BusinessAreaMixin, BusinessAreaProgramMixin +from hct_mis_api.apps.core.api.mixins import ( + ActionMixin, + BusinessAreaMixin, + BusinessAreaProgramMixin, +) from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.core.utils import decode_id_string from hct_mis_api.apps.payment.api.caches import PaymentPlanKeyConstructor @@ -32,8 +39,9 @@ from hct_mis_api.apps.payment.api.serializers import ( PaymentPlanBulkActionSerializer, PaymentPlanSerializer, + PaymentPlanSupportingDocumentSerializer, ) -from hct_mis_api.apps.payment.models import PaymentPlan +from hct_mis_api.apps.payment.models import PaymentPlan, PaymentPlanSupportingDocument from hct_mis_api.apps.payment.services.payment_plan_services import PaymentPlanService logger = logging.getLogger(__name__) @@ -163,3 +171,49 @@ def _get_action_permission(self, action_name: str) -> Optional[str]: PaymentPlan.Action.REVIEW.name: Permissions.PM_ACCEPTANCE_PROCESS_FINANCIAL_REVIEW.name, } return action_to_permissions_map.get(action_name) + + +class PaymentPlanSupportingDocumentViewSet( + ActionMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin, GenericViewSet +): + serializer_class = PaymentPlanSupportingDocumentSerializer + lookup_field = "file_id" + + serializer_classes_by_action = { + "create": PaymentPlanSupportingDocumentSerializer, + "delete": PaymentPlanSupportingDocumentSerializer, + } + permission_classes_by_action = { + "create": [PaymentPlanSupportingDocumentUploadPermission], + "delete": [PaymentPlanSupportingDocumentDeletePermission], + "download": [PaymentPlanSupportingDocumentDownloadPermission], + } + + def get_queryset(self) -> QuerySet: + payment_plan_id = decode_id_string(self.kwargs.get("payment_plan_id")) + return PaymentPlanSupportingDocument.objects.filter(payment_plan_id=payment_plan_id) + + def get_object(self) -> PaymentPlanSupportingDocument: + payment_plan = get_object_or_404(PaymentPlan, id=decode_id_string(self.kwargs.get("payment_plan_id"))) + return get_object_or_404( + PaymentPlanSupportingDocument, id=decode_id_string(self.kwargs.get("file_id")), payment_plan=payment_plan + ) + + def create(self, request: Request, *args: Any, **kwargs: Any) -> Response: + payment_plan = get_object_or_404(PaymentPlan, id=decode_id_string(kwargs.get("payment_plan_id"))) + serializer = self.get_serializer(data=request.data, context={"payment_plan": payment_plan}) + if serializer.is_valid(): + serializer.save(payment_plan=payment_plan) + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + def destroy(self, request: Request, *args: Any, **kwargs: Any) -> Response: + self.permission_classes = [PaymentPlanSupportingDocumentDeletePermission] + document = self.get_object() + document.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + + @action(detail=True, methods=["get"]) + def download(self, request: Request, *args: Any, **kwargs: Any) -> Response: + document = self.get_object() + return Response({"url": document.file.url}, status=status.HTTP_200_OK) diff --git a/src/hct_mis_api/apps/payment/migrations/0146_migration.py b/src/hct_mis_api/apps/payment/migrations/0146_migration.py new file mode 100644 index 0000000000..8744eafa89 --- /dev/null +++ b/src/hct_mis_api/apps/payment/migrations/0146_migration.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2.25 on 2024-09-30 09:00 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('payment', '0145_migration'), + ] + + operations = [ + migrations.CreateModel( + name='PaymentPlanSupportingDocument', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255)), + ('file', models.FileField(upload_to='')), + ('uploaded_at', models.DateTimeField(auto_now_add=True)), + ('created_by', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('payment_plan', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='payment.paymentplan')), + ], + options={ + 'ordering': ['uploaded_at'], + }, + ), + ] diff --git a/src/hct_mis_api/apps/payment/models.py b/src/hct_mis_api/apps/payment/models.py index cd2cbbb0c0..b99367a57a 100644 --- a/src/hct_mis_api/apps/payment/models.py +++ b/src/hct_mis_api/apps/payment/models.py @@ -2538,3 +2538,20 @@ def get_delivery_mechanisms_to_xlsx_fields_mapping(cls) -> Dict[str, List[str]]: required_fields_map[dm.code].extend([f"{field}_i_c" for field in dm.required_fields]) return required_fields_map + + +class PaymentPlanSupportingDocument(models.Model): + FILE_LIMIT = 10 # max 10 files per Payment Plan + FILE_SIZE_LIMIT = 10 * 1024 * 1024 # 10 MB + + payment_plan = models.ForeignKey(PaymentPlan, on_delete=models.CASCADE, related_name="documents") + title = models.CharField(max_length=255) + file = models.FileField() + uploaded_at = models.DateTimeField(auto_now_add=True) + created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="+") + + class Meta: + ordering = ["uploaded_at"] + + def __str__(self) -> str: + return self.title diff --git a/src/hct_mis_api/apps/payment/schema.py b/src/hct_mis_api/apps/payment/schema.py index 549db24615..556220dbda 100644 --- a/src/hct_mis_api/apps/payment/schema.py +++ b/src/hct_mis_api/apps/payment/schema.py @@ -94,6 +94,7 @@ PaymentHouseholdSnapshot, PaymentPlan, PaymentPlanSplit, + PaymentPlanSupportingDocument, PaymentRecord, PaymentVerification, PaymentVerificationPlan, @@ -551,6 +552,12 @@ class ReconciliationSummaryNode(graphene.ObjectType): reconciled = graphene.Int() +class PaymentPlanSupportingDocumentNode(DjangoObjectType): + class Meta: + model = PaymentPlanSupportingDocument + interfaces = (relay.Node,) + + class PaymentPlanNode(BaseNodePermissionMixin, AdminUrlNodeMixin, DjangoObjectType): permission_classes = (hopePermissionClass(Permissions.PM_VIEW_DETAILS),) dispersion_start_date = graphene.Date() @@ -586,6 +593,7 @@ class PaymentPlanNode(BaseNodePermissionMixin, AdminUrlNodeMixin, DjangoObjectTy name = graphene.String() can_send_to_payment_gateway = graphene.Boolean() can_split = graphene.Boolean() + supporting_documents = graphene.List(PaymentPlanSupportingDocumentNode) class Meta: model = PaymentPlan @@ -718,6 +726,9 @@ def resolve_can_split(self, info: Any) -> bool: return True + def resolve_supporting_documents(self, info: Any) -> "QuerySet": + return self.documents.all() + class PaymentVerificationNode(BaseNodePermissionMixin, AdminUrlNodeMixin, DjangoObjectType): permission_classes = (hopePermissionClass(Permissions.PAYMENT_VERIFICATION_VIEW_PAYMENT_RECORD_DETAILS),) diff --git a/tests/selenium/page_object/payment_module/payment_module_details.py b/tests/selenium/page_object/payment_module/payment_module_details.py index 8e7cba74ed..aff1b421d2 100644 --- a/tests/selenium/page_object/payment_module/payment_module_details.py +++ b/tests/selenium/page_object/payment_module/payment_module_details.py @@ -22,6 +22,8 @@ class PaymentModuleDetails(BaseComponents): labelRelatedFollowUpPaymentPlans = 'div[data-cy="label-Related Follow-Up Payment Plans"]' buttonSetUpFsp = 'a[data-cy="button-set-up-fsp"]' buttonCreateExclusions = 'button[data-cy="button-create-exclusions"]' + supportingDocumentsTitle = 'h6[data-cy="supporting-documents-title"]' + supportingDocumentsEmpty = 'div[data-cy="supporting-documents-empty"]' inputExclusionreason = 'textarea[data-cy="input-exclusionReason"]' buttonApplyExclusions = 'button[data-cy="button-apply-exclusions"]' labelFemaleChildren = 'div[data-cy="label-Female Children"]' @@ -221,3 +223,9 @@ def checkStatus(self, status: str) -> None: break sleep(1) assert status in self.getStatusContainer().text + + def getSupportingDocumentsTitle(self) -> WebElement: + return self.wait_for(self.supportingDocumentsTitle) + + def getSupportingDocumentsEmpty(self) -> WebElement: + return self.wait_for(self.supportingDocumentsEmpty) diff --git a/tests/selenium/payment_module/test_payment_plans.py b/tests/selenium/payment_module/test_payment_plans.py index 08693d949b..9e3a97e2be 100644 --- a/tests/selenium/payment_module/test_payment_plans.py +++ b/tests/selenium/payment_module/test_payment_plans.py @@ -236,6 +236,8 @@ def test_smoke_details_payment_plan( assert "-" in pagePaymentModuleDetails.getLabelRelatedFollowUpPaymentPlans().text assert "SET UP FSP" in pagePaymentModuleDetails.getButtonSetUpFsp().text assert "CREATE" in pagePaymentModuleDetails.getButtonCreateExclusions().text + assert "Supporting Documents" in pagePaymentModuleDetails.getSupportingDocumentsTitle().text + assert "No documents uploaded" in pagePaymentModuleDetails.getSupportingDocumentsEmpty().text assert "0" in pagePaymentModuleDetails.getLabelFemaleChildren().text assert "0" in pagePaymentModuleDetails.getLabelFemaleAdults().text assert "0" in pagePaymentModuleDetails.getLabelMaleChildren().text 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 982af189c4..7022088f32 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 @@ -46,6 +46,11 @@ 'startDate': '2020-09-10' }, 'status': 'OPEN', + 'supportingDocuments': [ + { + 'title': 'Test File 123' + } + ], 'totalDeliveredQuantity': 50.0, 'totalDeliveredQuantityUsd': 100.0, 'totalEntitledQuantity': 100.0, @@ -87,6 +92,8 @@ 'startDate': '2020-09-10' }, 'status': 'LOCKED', + 'supportingDocuments': [ + ], 'totalDeliveredQuantity': 50.0, 'totalDeliveredQuantityUsd': 100.0, 'totalEntitledQuantity': 100.0, 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 4ce8f39d6d..a680775e29 100644 --- a/tests/unit/apps/payment/test_all_payment_plan_queries.py +++ b/tests/unit/apps/payment/test_all_payment_plan_queries.py @@ -1,6 +1,8 @@ from datetime import datetime +from io import BytesIO from unittest.mock import patch +from django.core.files.uploadedfile import InMemoryUploadedFile from django.utils import timezone from dateutil.relativedelta import relativedelta @@ -25,6 +27,7 @@ ApprovalProcess, PaymentHouseholdSnapshot, PaymentPlan, + PaymentPlanSupportingDocument, ) from hct_mis_api.apps.program.fixtures import ProgramCycleFactory @@ -108,6 +111,9 @@ class TestPaymentPlanQueries(APITestCase): totalUndeliveredQuantity totalUndeliveredQuantityUsd unicefId + supportingDocuments{ + title + } excludedHouseholds{ id } @@ -321,6 +327,19 @@ def setUpTestData(cls) -> None: finance_release_number_required=cls.pp.finance_release_number_required, ) + PaymentPlanSupportingDocument.objects.create( + payment_plan=cls.pp, + title="Test File 123", + file=InMemoryUploadedFile( + name="Test123.jpg", + file=BytesIO(b"abc"), + charset=None, + field_name="0", + size=10, + content_type="image/jpeg", + ), + ) + with patch("hct_mis_api.apps.payment.models.PaymentPlan.get_exchange_rate", return_value=2.0): cls.pp.update_population_count_fields() cls.pp.update_money_fields() diff --git a/tests/unit/apps/payment/test_payment_plan_supporting_documents.py b/tests/unit/apps/payment/test_payment_plan_supporting_documents.py new file mode 100644 index 0000000000..1ac9c08ce0 --- /dev/null +++ b/tests/unit/apps/payment/test_payment_plan_supporting_documents.py @@ -0,0 +1,199 @@ +import base64 +from io import BytesIO + +from django.core.files.uploadedfile import InMemoryUploadedFile, SimpleUploadedFile +from django.test import TestCase +from django.urls import reverse + +from rest_framework import status +from rest_framework.response import Response +from rest_framework.test import APIClient + +from hct_mis_api.apps.account.fixtures import UserFactory +from hct_mis_api.apps.account.models import Role, UserRole +from hct_mis_api.apps.account.permissions import Permissions +from hct_mis_api.apps.core.fixtures import create_afghanistan +from hct_mis_api.apps.payment.api.serializers import ( + PaymentPlanSupportingDocumentSerializer, +) +from hct_mis_api.apps.payment.fixtures import PaymentPlanFactory +from hct_mis_api.apps.payment.models import PaymentPlan, PaymentPlanSupportingDocument + + +class PaymentPlanSupportingDocumentSerializerTests(TestCase): + def setUp(cls) -> None: + create_afghanistan() + cls.payment_plan = PaymentPlanFactory( + status=PaymentPlan.Status.OPEN, + ) + cls.context = {"payment_plan": cls.payment_plan} + cls.file = SimpleUploadedFile("test.pdf", b"123", content_type="application/pdf") + + def test_validate_file_size_success(self) -> None: + document_data = {"file": self.file, "title": "test"} + serializer = PaymentPlanSupportingDocumentSerializer(data=document_data, context=self.context) + serializer.is_valid() + self.assertEqual(serializer.errors, {}) + + def test_validate_file_size_failure(self) -> None: + # just mock file size over limit + self.file.size = PaymentPlanSupportingDocument.FILE_SIZE_LIMIT + 1 + document_data = {"file": self.file, "title": "test"} + serializer = PaymentPlanSupportingDocumentSerializer(data=document_data, context=self.context) + self.assertFalse(serializer.is_valid()) + self.assertIn("file", serializer.errors) + self.assertEqual(serializer.errors["file"][0], "File size must be ≤ 10MB.") + + def test_validate_file_extension_success(self) -> None: + valid_file = SimpleUploadedFile("test.jpg", b"abc", content_type="image/jpeg") + document_data = {"file": valid_file, "title": "test"} + serializer = PaymentPlanSupportingDocumentSerializer(data=document_data, context=self.context) + self.assertTrue(serializer.is_valid()) + + def test_validate_file_extension_failure(self) -> None: + invalid_file = SimpleUploadedFile("test.exe", b"abc", content_type="application/octet-stream") + document_data = {"file": invalid_file, "title": "test"} + serializer = PaymentPlanSupportingDocumentSerializer(data=document_data, context=self.context) + self.assertFalse(serializer.is_valid()) + self.assertIn("file", serializer.errors) + self.assertEqual(serializer.errors["file"][0], "Unsupported file type.") + + def test_validate_payment_plan_status_failure(self) -> None: + self.payment_plan.status = PaymentPlan.Status.FINISHED + self.payment_plan.save(update_fields=["status"]) + serializer = PaymentPlanSupportingDocumentSerializer( + data={"file": self.file, "title": "test"}, context=self.context + ) + self.assertFalse(serializer.is_valid()) + self.assertIn("non_field_errors", serializer.errors) + self.assertEqual(serializer.errors["non_field_errors"][0], "Payment plan must be within status OPEN or LOCKED.") + + def test_validate_file_limit_failure(self) -> None: + # create 10 documents + for _ in range(11): + PaymentPlanSupportingDocument.objects.create( + payment_plan=self.payment_plan, + title="Test 1", + file=InMemoryUploadedFile( + name="Test123.jpg", + file=BytesIO(b"abc"), + charset=None, + field_name="0", + size=10, + content_type="image/jpeg", + ), + ) + + serializer = PaymentPlanSupportingDocumentSerializer( + data={"file": self.file, "title": "test"}, context=self.context + ) + self.assertFalse(serializer.is_valid()) + self.assertIn("non_field_errors", serializer.errors) + self.assertEqual( + serializer.errors["non_field_errors"][0], + f"Payment plan already has the maximum of {PaymentPlanSupportingDocument.FILE_LIMIT} supporting documents.", + ) + + +class PaymentPlanSupportingDocumentUploadViewTests(TestCase): + def setUp(cls) -> None: + cls.business_area = create_afghanistan() + cls.client = APIClient() + cls.user = UserFactory(username="Hope_USER", password="GoodJod") + role, created = Role.objects.update_or_create( + name="TestName", defaults={"permissions": [Permissions.PM_UPLOAD_SUPPORTING_DOCUMENT.value]} + ) + user_role, _ = UserRole.objects.get_or_create(user=cls.user, role=role, business_area=cls.business_area) + cls.payment_plan = PaymentPlanFactory( + status=PaymentPlan.Status.OPEN, + ) + program_id_base64 = base64.b64encode(f"ProgramNode:{str(cls.payment_plan.program.id)}".encode()).decode() + payment_plan_id_base64 = base64.b64encode(f"PaymentPlanNode:{str(cls.payment_plan.id)}".encode()).decode() + + cls.url = reverse( + "api:payment-plan:supporting_documents-list", + kwargs={ + "business_area": "afghanistan", + "program_id": program_id_base64, + "payment_plan_id": payment_plan_id_base64, + }, + ) + cls.file = SimpleUploadedFile("test.pdf", b"abc", content_type="application/pdf") + + def test_post_successful_upload(self) -> None: + self.client.force_authenticate(user=self.user) + self.assertEqual(PaymentPlanSupportingDocument.objects.count(), 0) + data = {"file": self.file, "title": "Test Document"} + response = self.client.post(self.url, data, format="multipart") + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertIn("id", response.data) + self.assertEqual(PaymentPlanSupportingDocument.objects.count(), 1) + + def test_post_invalid_upload(self) -> None: + self.client.force_authenticate(user=self.user) + invalid_file = SimpleUploadedFile("test.exe", b"bbb", content_type="application/octet-stream") + data = {"file": invalid_file, "title": "Test Document"} + response = self.client.post(self.url, data, format="multipart") + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertIn("file", response.data) + + +class PaymentPlanSupportingDocumentViewTests(TestCase): + def setUp(cls) -> None: + cls.business_area = create_afghanistan() + cls.client = APIClient() + cls.user = UserFactory(username="Hope_USER", password="GoodJod") + role, created = Role.objects.update_or_create( + name="TestName", + defaults={ + "permissions": [ + Permissions.PM_DELETE_SUPPORTING_DOCUMENT.value, + Permissions.PM_DOWNLOAD_SUPPORTING_DOCUMENT.value, + ] + }, + ) + user_role, _ = UserRole.objects.get_or_create(user=cls.user, role=role, business_area=cls.business_area) + cls.payment_plan = PaymentPlanFactory( + status=PaymentPlan.Status.OPEN, + ) + cls.document = PaymentPlanSupportingDocument.objects.create( + payment_plan=cls.payment_plan, title="Test Document333", file=SimpleUploadedFile("test.pdf", b"aaa") + ) + cls.program_id_base64 = base64.b64encode(f"ProgramNode:{str(cls.payment_plan.program.id)}".encode()).decode() + cls.payment_plan_id_base64 = base64.b64encode(f"PaymentPlanNode:{str(cls.payment_plan.id)}".encode()).decode() + cls.supporting_document_id_base64 = base64.b64encode( + f"PaymentPlanSupportingDocumentNode:{str(cls.document.id)}".encode() + ).decode() + + cls.url = reverse( + "api:payment-plan:supporting_documents-detail", + kwargs={ + "business_area": "afghanistan", + "program_id": cls.program_id_base64, + "payment_plan_id": cls.payment_plan_id_base64, + "file_id": cls.supporting_document_id_base64, + }, + ) + + def test_delete_document_success(self) -> None: + self.client.force_authenticate(user=self.user) + response = self.client.delete(self.url) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def test_get_document_success(self) -> None: + url = reverse( + "api:payment-plan:supporting_documents-download", + kwargs={ + "business_area": "afghanistan", + "program_id": self.program_id_base64, + "payment_plan_id": self.payment_plan_id_base64, + "file_id": self.supporting_document_id_base64, + }, + ) + + self.client.force_authenticate(user=self.user) + response = self.client.get(url) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertIsInstance(response, Response) From 4f664e967906053b7a316a38befa3eeb00e4d652 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Fri, 4 Oct 2024 00:52:00 +0200 Subject: [PATCH 070/202] fix test admin --- tests/unit/apps/account/test_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/apps/account/test_admin.py b/tests/unit/apps/account/test_admin.py index 8088a9917e..de60521556 100644 --- a/tests/unit/apps/account/test_admin.py +++ b/tests/unit/apps/account/test_admin.py @@ -5,7 +5,7 @@ from django_webtest import WebTest -from hct_mis_api.apps.account.fixtures import UserFactory +from hct_mis_api.apps.account.fixtures import RoleFactory, UserFactory from hct_mis_api.apps.account.models import User @@ -13,6 +13,7 @@ class RoleTest(WebTest): @classmethod def setUpTestData(cls) -> None: super().setUpTestData() + RoleFactory() cls.superuser: User = UserFactory(is_superuser=True, is_staff=True) def test_role_perm_matrix(self) -> None: @@ -24,7 +25,6 @@ def test_role_sync(self) -> None: url = reverse("admin:account_role_dumpdata_qs") res = self.app.get(url, user=self.superuser) assert res.status_code == 200 - print(res.json) jres = json.loads(unquote(res.json["data"])) models = set([item["model"] for item in jres]) assert len(models) == 1 From 77ec188e7e8656a03998929d9694baf08551cae0 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Fri, 4 Oct 2024 02:49:35 +0200 Subject: [PATCH 071/202] fix coveragerc --- tests/.coveragerc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/.coveragerc b/tests/.coveragerc index 370816c587..7942061b4e 100644 --- a/tests/.coveragerc +++ b/tests/.coveragerc @@ -11,7 +11,6 @@ omit = */admin/*.py */admin.py **/fixtures.py - src/hct_mis_api/**/forms.py hct_mis_api/one_time_scripts/* hct_mis_api/libs/* hct_mis_api/settings/* @@ -19,6 +18,7 @@ omit = hct_mis_api/conftest.py hct_mis_api/config/settings.py hct_mis_api/apps/core/management/commands/* + hct_mis_api/apps/household/forms.py [report] # Regexes for lines to exclude from consideration From 4ba14c0f4bb8703b38432ee4526fe08840ea7a65 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Fri, 4 Oct 2024 03:20:39 +0200 Subject: [PATCH 072/202] add ignore to codecov files --- codecov.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codecov.yml b/codecov.yml index 02b73140fc..8a09aba375 100644 --- a/codecov.yml +++ b/codecov.yml @@ -9,3 +9,5 @@ coverage: patch: default: target: 95% + ignore: + - "**/forms.py" From fc309cc2a0f3f44f8fa7d43eb910a69d0e609c10 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Fri, 4 Oct 2024 03:51:43 +0200 Subject: [PATCH 073/202] move ignored paths to codecov.yml --- codecov.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/codecov.yml b/codecov.yml index 8a09aba375..72f96c556e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -11,3 +11,17 @@ coverage: target: 95% ignore: - "**/forms.py" + - "*/selenium_tests/**" + - "*/tests/**" + - "*/migrations/*" + - "*/apps.py" + - "*/admin/*.py" + - "*/admin.py" + - "**/fixtures.py" + - "hct_mis_api/one_time_scripts/*" + - "hct_mis_api/libs/*" + - "hct_mis_api/settings/*" + - "hct_mis_api/settings.py" + - "hct_mis_api/conftest.py" + - "hct_mis_api/config/settings.py" + - "hct_mis_api/apps/core/management/commands/*" From 50c78fe504c4c9e5a647f8fa7b51400d3795a87d Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Fri, 4 Oct 2024 10:11:48 +0200 Subject: [PATCH 074/202] add example type in pp query --- src/frontend/{generated => restgenerated}/core/ApiError.ts | 0 .../{generated => restgenerated}/core/ApiRequestOptions.ts | 0 src/frontend/{generated => restgenerated}/core/ApiResult.ts | 0 .../{generated => restgenerated}/core/CancelablePromise.ts | 0 src/frontend/{generated => restgenerated}/core/OpenAPI.ts | 0 src/frontend/{generated => restgenerated}/core/request.ts | 0 src/frontend/{generated => restgenerated}/index.ts | 0 src/frontend/{generated => restgenerated}/models/ActionEnum.ts | 0 src/frontend/{generated => restgenerated}/models/Admin1Enum.ts | 0 src/frontend/{generated => restgenerated}/models/Admin2Enum.ts | 0 src/frontend/{generated => restgenerated}/models/Admin3Enum.ts | 0 src/frontend/{generated => restgenerated}/models/Admin4Enum.ts | 0 src/frontend/{generated => restgenerated}/models/Area.ts | 0 src/frontend/{generated => restgenerated}/models/AreaList.ts | 0 src/frontend/{generated => restgenerated}/models/AreaType.ts | 0 src/frontend/{generated => restgenerated}/models/BlankEnum.ts | 0 .../{generated => restgenerated}/models/BusinessArea.ts | 0 .../models/CollectIndividualDataEnum.ts | 0 .../{generated => restgenerated}/models/CollectTypeEnum.ts | 0 .../{generated => restgenerated}/models/CommsDisabilityEnum.ts | 0 .../{generated => restgenerated}/models/ConsentSharingEnum.ts | 0 .../{generated => restgenerated}/models/CountryEnum.ts | 0 .../{generated => restgenerated}/models/CountryOriginEnum.ts | 0 .../{generated => restgenerated}/models/CurrencyEnum.ts | 0 .../models/DeduplicationGoldenRecordStatusEnum.ts | 0 src/frontend/{generated => restgenerated}/models/Delegate.ts | 0 .../{generated => restgenerated}/models/DelegatePeople.ts | 0 .../{generated => restgenerated}/models/DisabilityEnum.ts | 0 src/frontend/{generated => restgenerated}/models/Document.ts | 0 .../{generated => restgenerated}/models/DocumentStatusEnum.ts | 0 .../{generated => restgenerated}/models/DocumentTypeEnum.ts | 0 .../{generated => restgenerated}/models/FollowUpPaymentPlan.ts | 0 .../models/FrequencyOfPaymentsEnum.ts | 0 .../models/HearingDisabilityEnum.ts | 0 src/frontend/{generated => restgenerated}/models/Household.ts | 0 src/frontend/{generated => restgenerated}/models/Individual.ts | 0 .../models/MemoryDisabilityEnum.ts | 0 src/frontend/{generated => restgenerated}/models/NullEnum.ts | 0 .../{generated => restgenerated}/models/OrgEnumeratorEnum.ts | 0 .../{generated => restgenerated}/models/PaginatedAreaList.ts | 0 .../models/PaginatedAreaListList.ts | 0 .../models/PaginatedAreaTypeList.ts | 0 .../models/PaginatedBusinessAreaList.ts | 0 .../models/PaginatedPaymentPlanList.ts | 0 .../models/PaginatedPeriodicDataUpdateTemplateListList.ts | 0 .../models/PaginatedPeriodicDataUpdateUploadListList.ts | 0 .../models/PaginatedPeriodicFieldList.ts | 0 .../models/PaginatedProgramCycleListList.ts | 0 .../models/PaginatedProgramGlobalList.ts | 0 .../models/PaginatedRegistrationDataImportListList.ts | 0 .../models/PaginatedTargetPopulationListList.ts | 0 .../models/PatchedProgramCycleUpdate.ts | 0 src/frontend/{generated => restgenerated}/models/PatchedRDI.ts | 0 .../{generated => restgenerated}/models/PaymentPlan.ts | 0 .../models/PaymentPlanBulkAction.ts | 0 .../models/PeriodicDataUpdateTemplateCreate.ts | 0 .../models/PeriodicDataUpdateTemplateDetail.ts | 0 .../models/PeriodicDataUpdateTemplateList.ts | 0 .../models/PeriodicDataUpdateUpload.ts | 0 .../models/PeriodicDataUpdateUploadDetail.ts | 0 .../models/PeriodicDataUpdateUploadList.ts | 0 .../{generated => restgenerated}/models/PeriodicField.ts | 0 .../{generated => restgenerated}/models/PeriodicFieldData.ts | 0 .../models/PhysicalDisabilityEnum.ts | 0 .../models/PreferredLanguageEnum.ts | 0 src/frontend/{generated => restgenerated}/models/Program.ts | 0 .../{generated => restgenerated}/models/ProgramCycleCreate.ts | 0 .../{generated => restgenerated}/models/ProgramCycleList.ts | 0 .../{generated => restgenerated}/models/ProgramCycleUpdate.ts | 0 .../{generated => restgenerated}/models/ProgramGlobal.ts | 0 .../models/ProgramGlobalStatusEnum.ts | 0 src/frontend/{generated => restgenerated}/models/PushPeople.ts | 0 .../{generated => restgenerated}/models/PushPeopleTypeEnum.ts | 0 src/frontend/{generated => restgenerated}/models/RDI.ts | 0 src/frontend/{generated => restgenerated}/models/RDINested.ts | 0 .../{generated => restgenerated}/models/RdiMergeStatusEnum.ts | 0 .../models/RegistrationDataImportList.ts | 0 .../models/RegistrationMethodEnum.ts | 0 .../{generated => restgenerated}/models/RelationshipEnum.ts | 0 .../{generated => restgenerated}/models/ResidenceStatusEnum.ts | 0 src/frontend/{generated => restgenerated}/models/ScopeEnum.ts | 0 src/frontend/{generated => restgenerated}/models/SectorEnum.ts | 0 .../models/SeeingDisabilityEnum.ts | 0 .../models/SelfcareDisabilityEnum.ts | 0 src/frontend/{generated => restgenerated}/models/SexEnum.ts | 0 .../{generated => restgenerated}/models/SubtypeEnum.ts | 0 .../models/TargetPopulationList.ts | 0 .../{generated => restgenerated}/models/WorkStatusEnum.ts | 0 .../services/FieldsAttributesService.ts | 0 .../{generated => restgenerated}/services/HhStatusService.ts | 0 .../{generated => restgenerated}/services/RestService.ts | 0 src/frontend/src/api/paymentModuleApi.ts | 3 ++- src/frontend/tsconfig.json | 3 ++- 93 files changed, 4 insertions(+), 2 deletions(-) rename src/frontend/{generated => restgenerated}/core/ApiError.ts (100%) rename src/frontend/{generated => restgenerated}/core/ApiRequestOptions.ts (100%) rename src/frontend/{generated => restgenerated}/core/ApiResult.ts (100%) rename src/frontend/{generated => restgenerated}/core/CancelablePromise.ts (100%) rename src/frontend/{generated => restgenerated}/core/OpenAPI.ts (100%) rename src/frontend/{generated => restgenerated}/core/request.ts (100%) rename src/frontend/{generated => restgenerated}/index.ts (100%) rename src/frontend/{generated => restgenerated}/models/ActionEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/Admin1Enum.ts (100%) rename src/frontend/{generated => restgenerated}/models/Admin2Enum.ts (100%) rename src/frontend/{generated => restgenerated}/models/Admin3Enum.ts (100%) rename src/frontend/{generated => restgenerated}/models/Admin4Enum.ts (100%) rename src/frontend/{generated => restgenerated}/models/Area.ts (100%) rename src/frontend/{generated => restgenerated}/models/AreaList.ts (100%) rename src/frontend/{generated => restgenerated}/models/AreaType.ts (100%) rename src/frontend/{generated => restgenerated}/models/BlankEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/BusinessArea.ts (100%) rename src/frontend/{generated => restgenerated}/models/CollectIndividualDataEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/CollectTypeEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/CommsDisabilityEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/ConsentSharingEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/CountryEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/CountryOriginEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/CurrencyEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/DeduplicationGoldenRecordStatusEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/Delegate.ts (100%) rename src/frontend/{generated => restgenerated}/models/DelegatePeople.ts (100%) rename src/frontend/{generated => restgenerated}/models/DisabilityEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/Document.ts (100%) rename src/frontend/{generated => restgenerated}/models/DocumentStatusEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/DocumentTypeEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/FollowUpPaymentPlan.ts (100%) rename src/frontend/{generated => restgenerated}/models/FrequencyOfPaymentsEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/HearingDisabilityEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/Household.ts (100%) rename src/frontend/{generated => restgenerated}/models/Individual.ts (100%) rename src/frontend/{generated => restgenerated}/models/MemoryDisabilityEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/NullEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/OrgEnumeratorEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedAreaList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedAreaListList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedAreaTypeList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedBusinessAreaList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedPaymentPlanList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedPeriodicDataUpdateTemplateListList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedPeriodicDataUpdateUploadListList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedPeriodicFieldList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedProgramCycleListList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedProgramGlobalList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedRegistrationDataImportListList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaginatedTargetPopulationListList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PatchedProgramCycleUpdate.ts (100%) rename src/frontend/{generated => restgenerated}/models/PatchedRDI.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaymentPlan.ts (100%) rename src/frontend/{generated => restgenerated}/models/PaymentPlanBulkAction.ts (100%) rename src/frontend/{generated => restgenerated}/models/PeriodicDataUpdateTemplateCreate.ts (100%) rename src/frontend/{generated => restgenerated}/models/PeriodicDataUpdateTemplateDetail.ts (100%) rename src/frontend/{generated => restgenerated}/models/PeriodicDataUpdateTemplateList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PeriodicDataUpdateUpload.ts (100%) rename src/frontend/{generated => restgenerated}/models/PeriodicDataUpdateUploadDetail.ts (100%) rename src/frontend/{generated => restgenerated}/models/PeriodicDataUpdateUploadList.ts (100%) rename src/frontend/{generated => restgenerated}/models/PeriodicField.ts (100%) rename src/frontend/{generated => restgenerated}/models/PeriodicFieldData.ts (100%) rename src/frontend/{generated => restgenerated}/models/PhysicalDisabilityEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/PreferredLanguageEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/Program.ts (100%) rename src/frontend/{generated => restgenerated}/models/ProgramCycleCreate.ts (100%) rename src/frontend/{generated => restgenerated}/models/ProgramCycleList.ts (100%) rename src/frontend/{generated => restgenerated}/models/ProgramCycleUpdate.ts (100%) rename src/frontend/{generated => restgenerated}/models/ProgramGlobal.ts (100%) rename src/frontend/{generated => restgenerated}/models/ProgramGlobalStatusEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/PushPeople.ts (100%) rename src/frontend/{generated => restgenerated}/models/PushPeopleTypeEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/RDI.ts (100%) rename src/frontend/{generated => restgenerated}/models/RDINested.ts (100%) rename src/frontend/{generated => restgenerated}/models/RdiMergeStatusEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/RegistrationDataImportList.ts (100%) rename src/frontend/{generated => restgenerated}/models/RegistrationMethodEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/RelationshipEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/ResidenceStatusEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/ScopeEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/SectorEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/SeeingDisabilityEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/SelfcareDisabilityEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/SexEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/SubtypeEnum.ts (100%) rename src/frontend/{generated => restgenerated}/models/TargetPopulationList.ts (100%) rename src/frontend/{generated => restgenerated}/models/WorkStatusEnum.ts (100%) rename src/frontend/{generated => restgenerated}/services/FieldsAttributesService.ts (100%) rename src/frontend/{generated => restgenerated}/services/HhStatusService.ts (100%) rename src/frontend/{generated => restgenerated}/services/RestService.ts (100%) diff --git a/src/frontend/generated/core/ApiError.ts b/src/frontend/restgenerated/core/ApiError.ts similarity index 100% rename from src/frontend/generated/core/ApiError.ts rename to src/frontend/restgenerated/core/ApiError.ts diff --git a/src/frontend/generated/core/ApiRequestOptions.ts b/src/frontend/restgenerated/core/ApiRequestOptions.ts similarity index 100% rename from src/frontend/generated/core/ApiRequestOptions.ts rename to src/frontend/restgenerated/core/ApiRequestOptions.ts diff --git a/src/frontend/generated/core/ApiResult.ts b/src/frontend/restgenerated/core/ApiResult.ts similarity index 100% rename from src/frontend/generated/core/ApiResult.ts rename to src/frontend/restgenerated/core/ApiResult.ts diff --git a/src/frontend/generated/core/CancelablePromise.ts b/src/frontend/restgenerated/core/CancelablePromise.ts similarity index 100% rename from src/frontend/generated/core/CancelablePromise.ts rename to src/frontend/restgenerated/core/CancelablePromise.ts diff --git a/src/frontend/generated/core/OpenAPI.ts b/src/frontend/restgenerated/core/OpenAPI.ts similarity index 100% rename from src/frontend/generated/core/OpenAPI.ts rename to src/frontend/restgenerated/core/OpenAPI.ts diff --git a/src/frontend/generated/core/request.ts b/src/frontend/restgenerated/core/request.ts similarity index 100% rename from src/frontend/generated/core/request.ts rename to src/frontend/restgenerated/core/request.ts diff --git a/src/frontend/generated/index.ts b/src/frontend/restgenerated/index.ts similarity index 100% rename from src/frontend/generated/index.ts rename to src/frontend/restgenerated/index.ts diff --git a/src/frontend/generated/models/ActionEnum.ts b/src/frontend/restgenerated/models/ActionEnum.ts similarity index 100% rename from src/frontend/generated/models/ActionEnum.ts rename to src/frontend/restgenerated/models/ActionEnum.ts diff --git a/src/frontend/generated/models/Admin1Enum.ts b/src/frontend/restgenerated/models/Admin1Enum.ts similarity index 100% rename from src/frontend/generated/models/Admin1Enum.ts rename to src/frontend/restgenerated/models/Admin1Enum.ts diff --git a/src/frontend/generated/models/Admin2Enum.ts b/src/frontend/restgenerated/models/Admin2Enum.ts similarity index 100% rename from src/frontend/generated/models/Admin2Enum.ts rename to src/frontend/restgenerated/models/Admin2Enum.ts diff --git a/src/frontend/generated/models/Admin3Enum.ts b/src/frontend/restgenerated/models/Admin3Enum.ts similarity index 100% rename from src/frontend/generated/models/Admin3Enum.ts rename to src/frontend/restgenerated/models/Admin3Enum.ts diff --git a/src/frontend/generated/models/Admin4Enum.ts b/src/frontend/restgenerated/models/Admin4Enum.ts similarity index 100% rename from src/frontend/generated/models/Admin4Enum.ts rename to src/frontend/restgenerated/models/Admin4Enum.ts diff --git a/src/frontend/generated/models/Area.ts b/src/frontend/restgenerated/models/Area.ts similarity index 100% rename from src/frontend/generated/models/Area.ts rename to src/frontend/restgenerated/models/Area.ts diff --git a/src/frontend/generated/models/AreaList.ts b/src/frontend/restgenerated/models/AreaList.ts similarity index 100% rename from src/frontend/generated/models/AreaList.ts rename to src/frontend/restgenerated/models/AreaList.ts diff --git a/src/frontend/generated/models/AreaType.ts b/src/frontend/restgenerated/models/AreaType.ts similarity index 100% rename from src/frontend/generated/models/AreaType.ts rename to src/frontend/restgenerated/models/AreaType.ts diff --git a/src/frontend/generated/models/BlankEnum.ts b/src/frontend/restgenerated/models/BlankEnum.ts similarity index 100% rename from src/frontend/generated/models/BlankEnum.ts rename to src/frontend/restgenerated/models/BlankEnum.ts diff --git a/src/frontend/generated/models/BusinessArea.ts b/src/frontend/restgenerated/models/BusinessArea.ts similarity index 100% rename from src/frontend/generated/models/BusinessArea.ts rename to src/frontend/restgenerated/models/BusinessArea.ts diff --git a/src/frontend/generated/models/CollectIndividualDataEnum.ts b/src/frontend/restgenerated/models/CollectIndividualDataEnum.ts similarity index 100% rename from src/frontend/generated/models/CollectIndividualDataEnum.ts rename to src/frontend/restgenerated/models/CollectIndividualDataEnum.ts diff --git a/src/frontend/generated/models/CollectTypeEnum.ts b/src/frontend/restgenerated/models/CollectTypeEnum.ts similarity index 100% rename from src/frontend/generated/models/CollectTypeEnum.ts rename to src/frontend/restgenerated/models/CollectTypeEnum.ts diff --git a/src/frontend/generated/models/CommsDisabilityEnum.ts b/src/frontend/restgenerated/models/CommsDisabilityEnum.ts similarity index 100% rename from src/frontend/generated/models/CommsDisabilityEnum.ts rename to src/frontend/restgenerated/models/CommsDisabilityEnum.ts diff --git a/src/frontend/generated/models/ConsentSharingEnum.ts b/src/frontend/restgenerated/models/ConsentSharingEnum.ts similarity index 100% rename from src/frontend/generated/models/ConsentSharingEnum.ts rename to src/frontend/restgenerated/models/ConsentSharingEnum.ts diff --git a/src/frontend/generated/models/CountryEnum.ts b/src/frontend/restgenerated/models/CountryEnum.ts similarity index 100% rename from src/frontend/generated/models/CountryEnum.ts rename to src/frontend/restgenerated/models/CountryEnum.ts diff --git a/src/frontend/generated/models/CountryOriginEnum.ts b/src/frontend/restgenerated/models/CountryOriginEnum.ts similarity index 100% rename from src/frontend/generated/models/CountryOriginEnum.ts rename to src/frontend/restgenerated/models/CountryOriginEnum.ts diff --git a/src/frontend/generated/models/CurrencyEnum.ts b/src/frontend/restgenerated/models/CurrencyEnum.ts similarity index 100% rename from src/frontend/generated/models/CurrencyEnum.ts rename to src/frontend/restgenerated/models/CurrencyEnum.ts diff --git a/src/frontend/generated/models/DeduplicationGoldenRecordStatusEnum.ts b/src/frontend/restgenerated/models/DeduplicationGoldenRecordStatusEnum.ts similarity index 100% rename from src/frontend/generated/models/DeduplicationGoldenRecordStatusEnum.ts rename to src/frontend/restgenerated/models/DeduplicationGoldenRecordStatusEnum.ts diff --git a/src/frontend/generated/models/Delegate.ts b/src/frontend/restgenerated/models/Delegate.ts similarity index 100% rename from src/frontend/generated/models/Delegate.ts rename to src/frontend/restgenerated/models/Delegate.ts diff --git a/src/frontend/generated/models/DelegatePeople.ts b/src/frontend/restgenerated/models/DelegatePeople.ts similarity index 100% rename from src/frontend/generated/models/DelegatePeople.ts rename to src/frontend/restgenerated/models/DelegatePeople.ts diff --git a/src/frontend/generated/models/DisabilityEnum.ts b/src/frontend/restgenerated/models/DisabilityEnum.ts similarity index 100% rename from src/frontend/generated/models/DisabilityEnum.ts rename to src/frontend/restgenerated/models/DisabilityEnum.ts diff --git a/src/frontend/generated/models/Document.ts b/src/frontend/restgenerated/models/Document.ts similarity index 100% rename from src/frontend/generated/models/Document.ts rename to src/frontend/restgenerated/models/Document.ts diff --git a/src/frontend/generated/models/DocumentStatusEnum.ts b/src/frontend/restgenerated/models/DocumentStatusEnum.ts similarity index 100% rename from src/frontend/generated/models/DocumentStatusEnum.ts rename to src/frontend/restgenerated/models/DocumentStatusEnum.ts diff --git a/src/frontend/generated/models/DocumentTypeEnum.ts b/src/frontend/restgenerated/models/DocumentTypeEnum.ts similarity index 100% rename from src/frontend/generated/models/DocumentTypeEnum.ts rename to src/frontend/restgenerated/models/DocumentTypeEnum.ts diff --git a/src/frontend/generated/models/FollowUpPaymentPlan.ts b/src/frontend/restgenerated/models/FollowUpPaymentPlan.ts similarity index 100% rename from src/frontend/generated/models/FollowUpPaymentPlan.ts rename to src/frontend/restgenerated/models/FollowUpPaymentPlan.ts diff --git a/src/frontend/generated/models/FrequencyOfPaymentsEnum.ts b/src/frontend/restgenerated/models/FrequencyOfPaymentsEnum.ts similarity index 100% rename from src/frontend/generated/models/FrequencyOfPaymentsEnum.ts rename to src/frontend/restgenerated/models/FrequencyOfPaymentsEnum.ts diff --git a/src/frontend/generated/models/HearingDisabilityEnum.ts b/src/frontend/restgenerated/models/HearingDisabilityEnum.ts similarity index 100% rename from src/frontend/generated/models/HearingDisabilityEnum.ts rename to src/frontend/restgenerated/models/HearingDisabilityEnum.ts diff --git a/src/frontend/generated/models/Household.ts b/src/frontend/restgenerated/models/Household.ts similarity index 100% rename from src/frontend/generated/models/Household.ts rename to src/frontend/restgenerated/models/Household.ts diff --git a/src/frontend/generated/models/Individual.ts b/src/frontend/restgenerated/models/Individual.ts similarity index 100% rename from src/frontend/generated/models/Individual.ts rename to src/frontend/restgenerated/models/Individual.ts diff --git a/src/frontend/generated/models/MemoryDisabilityEnum.ts b/src/frontend/restgenerated/models/MemoryDisabilityEnum.ts similarity index 100% rename from src/frontend/generated/models/MemoryDisabilityEnum.ts rename to src/frontend/restgenerated/models/MemoryDisabilityEnum.ts diff --git a/src/frontend/generated/models/NullEnum.ts b/src/frontend/restgenerated/models/NullEnum.ts similarity index 100% rename from src/frontend/generated/models/NullEnum.ts rename to src/frontend/restgenerated/models/NullEnum.ts diff --git a/src/frontend/generated/models/OrgEnumeratorEnum.ts b/src/frontend/restgenerated/models/OrgEnumeratorEnum.ts similarity index 100% rename from src/frontend/generated/models/OrgEnumeratorEnum.ts rename to src/frontend/restgenerated/models/OrgEnumeratorEnum.ts diff --git a/src/frontend/generated/models/PaginatedAreaList.ts b/src/frontend/restgenerated/models/PaginatedAreaList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedAreaList.ts rename to src/frontend/restgenerated/models/PaginatedAreaList.ts diff --git a/src/frontend/generated/models/PaginatedAreaListList.ts b/src/frontend/restgenerated/models/PaginatedAreaListList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedAreaListList.ts rename to src/frontend/restgenerated/models/PaginatedAreaListList.ts diff --git a/src/frontend/generated/models/PaginatedAreaTypeList.ts b/src/frontend/restgenerated/models/PaginatedAreaTypeList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedAreaTypeList.ts rename to src/frontend/restgenerated/models/PaginatedAreaTypeList.ts diff --git a/src/frontend/generated/models/PaginatedBusinessAreaList.ts b/src/frontend/restgenerated/models/PaginatedBusinessAreaList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedBusinessAreaList.ts rename to src/frontend/restgenerated/models/PaginatedBusinessAreaList.ts diff --git a/src/frontend/generated/models/PaginatedPaymentPlanList.ts b/src/frontend/restgenerated/models/PaginatedPaymentPlanList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedPaymentPlanList.ts rename to src/frontend/restgenerated/models/PaginatedPaymentPlanList.ts diff --git a/src/frontend/generated/models/PaginatedPeriodicDataUpdateTemplateListList.ts b/src/frontend/restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedPeriodicDataUpdateTemplateListList.ts rename to src/frontend/restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList.ts diff --git a/src/frontend/generated/models/PaginatedPeriodicDataUpdateUploadListList.ts b/src/frontend/restgenerated/models/PaginatedPeriodicDataUpdateUploadListList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedPeriodicDataUpdateUploadListList.ts rename to src/frontend/restgenerated/models/PaginatedPeriodicDataUpdateUploadListList.ts diff --git a/src/frontend/generated/models/PaginatedPeriodicFieldList.ts b/src/frontend/restgenerated/models/PaginatedPeriodicFieldList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedPeriodicFieldList.ts rename to src/frontend/restgenerated/models/PaginatedPeriodicFieldList.ts diff --git a/src/frontend/generated/models/PaginatedProgramCycleListList.ts b/src/frontend/restgenerated/models/PaginatedProgramCycleListList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedProgramCycleListList.ts rename to src/frontend/restgenerated/models/PaginatedProgramCycleListList.ts diff --git a/src/frontend/generated/models/PaginatedProgramGlobalList.ts b/src/frontend/restgenerated/models/PaginatedProgramGlobalList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedProgramGlobalList.ts rename to src/frontend/restgenerated/models/PaginatedProgramGlobalList.ts diff --git a/src/frontend/generated/models/PaginatedRegistrationDataImportListList.ts b/src/frontend/restgenerated/models/PaginatedRegistrationDataImportListList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedRegistrationDataImportListList.ts rename to src/frontend/restgenerated/models/PaginatedRegistrationDataImportListList.ts diff --git a/src/frontend/generated/models/PaginatedTargetPopulationListList.ts b/src/frontend/restgenerated/models/PaginatedTargetPopulationListList.ts similarity index 100% rename from src/frontend/generated/models/PaginatedTargetPopulationListList.ts rename to src/frontend/restgenerated/models/PaginatedTargetPopulationListList.ts diff --git a/src/frontend/generated/models/PatchedProgramCycleUpdate.ts b/src/frontend/restgenerated/models/PatchedProgramCycleUpdate.ts similarity index 100% rename from src/frontend/generated/models/PatchedProgramCycleUpdate.ts rename to src/frontend/restgenerated/models/PatchedProgramCycleUpdate.ts diff --git a/src/frontend/generated/models/PatchedRDI.ts b/src/frontend/restgenerated/models/PatchedRDI.ts similarity index 100% rename from src/frontend/generated/models/PatchedRDI.ts rename to src/frontend/restgenerated/models/PatchedRDI.ts diff --git a/src/frontend/generated/models/PaymentPlan.ts b/src/frontend/restgenerated/models/PaymentPlan.ts similarity index 100% rename from src/frontend/generated/models/PaymentPlan.ts rename to src/frontend/restgenerated/models/PaymentPlan.ts diff --git a/src/frontend/generated/models/PaymentPlanBulkAction.ts b/src/frontend/restgenerated/models/PaymentPlanBulkAction.ts similarity index 100% rename from src/frontend/generated/models/PaymentPlanBulkAction.ts rename to src/frontend/restgenerated/models/PaymentPlanBulkAction.ts diff --git a/src/frontend/generated/models/PeriodicDataUpdateTemplateCreate.ts b/src/frontend/restgenerated/models/PeriodicDataUpdateTemplateCreate.ts similarity index 100% rename from src/frontend/generated/models/PeriodicDataUpdateTemplateCreate.ts rename to src/frontend/restgenerated/models/PeriodicDataUpdateTemplateCreate.ts diff --git a/src/frontend/generated/models/PeriodicDataUpdateTemplateDetail.ts b/src/frontend/restgenerated/models/PeriodicDataUpdateTemplateDetail.ts similarity index 100% rename from src/frontend/generated/models/PeriodicDataUpdateTemplateDetail.ts rename to src/frontend/restgenerated/models/PeriodicDataUpdateTemplateDetail.ts diff --git a/src/frontend/generated/models/PeriodicDataUpdateTemplateList.ts b/src/frontend/restgenerated/models/PeriodicDataUpdateTemplateList.ts similarity index 100% rename from src/frontend/generated/models/PeriodicDataUpdateTemplateList.ts rename to src/frontend/restgenerated/models/PeriodicDataUpdateTemplateList.ts diff --git a/src/frontend/generated/models/PeriodicDataUpdateUpload.ts b/src/frontend/restgenerated/models/PeriodicDataUpdateUpload.ts similarity index 100% rename from src/frontend/generated/models/PeriodicDataUpdateUpload.ts rename to src/frontend/restgenerated/models/PeriodicDataUpdateUpload.ts diff --git a/src/frontend/generated/models/PeriodicDataUpdateUploadDetail.ts b/src/frontend/restgenerated/models/PeriodicDataUpdateUploadDetail.ts similarity index 100% rename from src/frontend/generated/models/PeriodicDataUpdateUploadDetail.ts rename to src/frontend/restgenerated/models/PeriodicDataUpdateUploadDetail.ts diff --git a/src/frontend/generated/models/PeriodicDataUpdateUploadList.ts b/src/frontend/restgenerated/models/PeriodicDataUpdateUploadList.ts similarity index 100% rename from src/frontend/generated/models/PeriodicDataUpdateUploadList.ts rename to src/frontend/restgenerated/models/PeriodicDataUpdateUploadList.ts diff --git a/src/frontend/generated/models/PeriodicField.ts b/src/frontend/restgenerated/models/PeriodicField.ts similarity index 100% rename from src/frontend/generated/models/PeriodicField.ts rename to src/frontend/restgenerated/models/PeriodicField.ts diff --git a/src/frontend/generated/models/PeriodicFieldData.ts b/src/frontend/restgenerated/models/PeriodicFieldData.ts similarity index 100% rename from src/frontend/generated/models/PeriodicFieldData.ts rename to src/frontend/restgenerated/models/PeriodicFieldData.ts diff --git a/src/frontend/generated/models/PhysicalDisabilityEnum.ts b/src/frontend/restgenerated/models/PhysicalDisabilityEnum.ts similarity index 100% rename from src/frontend/generated/models/PhysicalDisabilityEnum.ts rename to src/frontend/restgenerated/models/PhysicalDisabilityEnum.ts diff --git a/src/frontend/generated/models/PreferredLanguageEnum.ts b/src/frontend/restgenerated/models/PreferredLanguageEnum.ts similarity index 100% rename from src/frontend/generated/models/PreferredLanguageEnum.ts rename to src/frontend/restgenerated/models/PreferredLanguageEnum.ts diff --git a/src/frontend/generated/models/Program.ts b/src/frontend/restgenerated/models/Program.ts similarity index 100% rename from src/frontend/generated/models/Program.ts rename to src/frontend/restgenerated/models/Program.ts diff --git a/src/frontend/generated/models/ProgramCycleCreate.ts b/src/frontend/restgenerated/models/ProgramCycleCreate.ts similarity index 100% rename from src/frontend/generated/models/ProgramCycleCreate.ts rename to src/frontend/restgenerated/models/ProgramCycleCreate.ts diff --git a/src/frontend/generated/models/ProgramCycleList.ts b/src/frontend/restgenerated/models/ProgramCycleList.ts similarity index 100% rename from src/frontend/generated/models/ProgramCycleList.ts rename to src/frontend/restgenerated/models/ProgramCycleList.ts diff --git a/src/frontend/generated/models/ProgramCycleUpdate.ts b/src/frontend/restgenerated/models/ProgramCycleUpdate.ts similarity index 100% rename from src/frontend/generated/models/ProgramCycleUpdate.ts rename to src/frontend/restgenerated/models/ProgramCycleUpdate.ts diff --git a/src/frontend/generated/models/ProgramGlobal.ts b/src/frontend/restgenerated/models/ProgramGlobal.ts similarity index 100% rename from src/frontend/generated/models/ProgramGlobal.ts rename to src/frontend/restgenerated/models/ProgramGlobal.ts diff --git a/src/frontend/generated/models/ProgramGlobalStatusEnum.ts b/src/frontend/restgenerated/models/ProgramGlobalStatusEnum.ts similarity index 100% rename from src/frontend/generated/models/ProgramGlobalStatusEnum.ts rename to src/frontend/restgenerated/models/ProgramGlobalStatusEnum.ts diff --git a/src/frontend/generated/models/PushPeople.ts b/src/frontend/restgenerated/models/PushPeople.ts similarity index 100% rename from src/frontend/generated/models/PushPeople.ts rename to src/frontend/restgenerated/models/PushPeople.ts diff --git a/src/frontend/generated/models/PushPeopleTypeEnum.ts b/src/frontend/restgenerated/models/PushPeopleTypeEnum.ts similarity index 100% rename from src/frontend/generated/models/PushPeopleTypeEnum.ts rename to src/frontend/restgenerated/models/PushPeopleTypeEnum.ts diff --git a/src/frontend/generated/models/RDI.ts b/src/frontend/restgenerated/models/RDI.ts similarity index 100% rename from src/frontend/generated/models/RDI.ts rename to src/frontend/restgenerated/models/RDI.ts diff --git a/src/frontend/generated/models/RDINested.ts b/src/frontend/restgenerated/models/RDINested.ts similarity index 100% rename from src/frontend/generated/models/RDINested.ts rename to src/frontend/restgenerated/models/RDINested.ts diff --git a/src/frontend/generated/models/RdiMergeStatusEnum.ts b/src/frontend/restgenerated/models/RdiMergeStatusEnum.ts similarity index 100% rename from src/frontend/generated/models/RdiMergeStatusEnum.ts rename to src/frontend/restgenerated/models/RdiMergeStatusEnum.ts diff --git a/src/frontend/generated/models/RegistrationDataImportList.ts b/src/frontend/restgenerated/models/RegistrationDataImportList.ts similarity index 100% rename from src/frontend/generated/models/RegistrationDataImportList.ts rename to src/frontend/restgenerated/models/RegistrationDataImportList.ts diff --git a/src/frontend/generated/models/RegistrationMethodEnum.ts b/src/frontend/restgenerated/models/RegistrationMethodEnum.ts similarity index 100% rename from src/frontend/generated/models/RegistrationMethodEnum.ts rename to src/frontend/restgenerated/models/RegistrationMethodEnum.ts diff --git a/src/frontend/generated/models/RelationshipEnum.ts b/src/frontend/restgenerated/models/RelationshipEnum.ts similarity index 100% rename from src/frontend/generated/models/RelationshipEnum.ts rename to src/frontend/restgenerated/models/RelationshipEnum.ts diff --git a/src/frontend/generated/models/ResidenceStatusEnum.ts b/src/frontend/restgenerated/models/ResidenceStatusEnum.ts similarity index 100% rename from src/frontend/generated/models/ResidenceStatusEnum.ts rename to src/frontend/restgenerated/models/ResidenceStatusEnum.ts diff --git a/src/frontend/generated/models/ScopeEnum.ts b/src/frontend/restgenerated/models/ScopeEnum.ts similarity index 100% rename from src/frontend/generated/models/ScopeEnum.ts rename to src/frontend/restgenerated/models/ScopeEnum.ts diff --git a/src/frontend/generated/models/SectorEnum.ts b/src/frontend/restgenerated/models/SectorEnum.ts similarity index 100% rename from src/frontend/generated/models/SectorEnum.ts rename to src/frontend/restgenerated/models/SectorEnum.ts diff --git a/src/frontend/generated/models/SeeingDisabilityEnum.ts b/src/frontend/restgenerated/models/SeeingDisabilityEnum.ts similarity index 100% rename from src/frontend/generated/models/SeeingDisabilityEnum.ts rename to src/frontend/restgenerated/models/SeeingDisabilityEnum.ts diff --git a/src/frontend/generated/models/SelfcareDisabilityEnum.ts b/src/frontend/restgenerated/models/SelfcareDisabilityEnum.ts similarity index 100% rename from src/frontend/generated/models/SelfcareDisabilityEnum.ts rename to src/frontend/restgenerated/models/SelfcareDisabilityEnum.ts diff --git a/src/frontend/generated/models/SexEnum.ts b/src/frontend/restgenerated/models/SexEnum.ts similarity index 100% rename from src/frontend/generated/models/SexEnum.ts rename to src/frontend/restgenerated/models/SexEnum.ts diff --git a/src/frontend/generated/models/SubtypeEnum.ts b/src/frontend/restgenerated/models/SubtypeEnum.ts similarity index 100% rename from src/frontend/generated/models/SubtypeEnum.ts rename to src/frontend/restgenerated/models/SubtypeEnum.ts diff --git a/src/frontend/generated/models/TargetPopulationList.ts b/src/frontend/restgenerated/models/TargetPopulationList.ts similarity index 100% rename from src/frontend/generated/models/TargetPopulationList.ts rename to src/frontend/restgenerated/models/TargetPopulationList.ts diff --git a/src/frontend/generated/models/WorkStatusEnum.ts b/src/frontend/restgenerated/models/WorkStatusEnum.ts similarity index 100% rename from src/frontend/generated/models/WorkStatusEnum.ts rename to src/frontend/restgenerated/models/WorkStatusEnum.ts diff --git a/src/frontend/generated/services/FieldsAttributesService.ts b/src/frontend/restgenerated/services/FieldsAttributesService.ts similarity index 100% rename from src/frontend/generated/services/FieldsAttributesService.ts rename to src/frontend/restgenerated/services/FieldsAttributesService.ts diff --git a/src/frontend/generated/services/HhStatusService.ts b/src/frontend/restgenerated/services/HhStatusService.ts similarity index 100% rename from src/frontend/generated/services/HhStatusService.ts rename to src/frontend/restgenerated/services/HhStatusService.ts diff --git a/src/frontend/generated/services/RestService.ts b/src/frontend/restgenerated/services/RestService.ts similarity index 100% rename from src/frontend/generated/services/RestService.ts rename to src/frontend/restgenerated/services/RestService.ts diff --git a/src/frontend/src/api/paymentModuleApi.ts b/src/frontend/src/api/paymentModuleApi.ts index 106b14384a..82d25fceb7 100644 --- a/src/frontend/src/api/paymentModuleApi.ts +++ b/src/frontend/src/api/paymentModuleApi.ts @@ -1,9 +1,10 @@ import { api } from './api'; +import { PaymentPlan } from '@restgenerated/models/PaymentPlan'; export const fetchPaymentPlansManagerial = async ( businessAreaSlug, params = {}, -) => { +): Promise<PaymentPlan[]> => { const paramsWithNoLimit = { ...params, limit: 10000, diff --git a/src/frontend/tsconfig.json b/src/frontend/tsconfig.json index 84696a5a73..75bf6186bf 100644 --- a/src/frontend/tsconfig.json +++ b/src/frontend/tsconfig.json @@ -30,7 +30,8 @@ "@shared/*": ["src/shared/*"], "@utils/*": ["src/utils/*"], "@hooks/*": ["src/hooks/*"], - "@generated/*": ["src/__generated__/*"] + "@generated/*": ["src/__generated__/*"], + "@restgenerated/*": ["../frontend/restgenerated/*"] } }, "include": [ From 4442bc0507732cb3e15b09ffcb29e28c0caa2e1b Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Fri, 4 Oct 2024 16:35:46 +0200 Subject: [PATCH 075/202] add types for rest queries --- src/frontend/src/api/paymentModuleApi.ts | 11 ++++- src/frontend/src/api/periodicDataUpdateApi.ts | 24 ++++++----- src/frontend/src/api/rdiApi.ts | 3 +- src/frontend/src/api/sharedApi.ts | 3 +- src/frontend/src/api/targetPopulationApi.ts | 4 +- ...riodicDataUpdatesTemplateDetailsDialog.tsx | 40 +++++++++---------- .../PeriodicDataUpdatesTemplatesList.tsx | 18 +++------ .../PeriodicDataUpdatesUpdatesList.tsx | 13 ++---- .../ProgramDetails/ProgramDetails.tsx | 6 +-- .../ManagerialConsolePage.tsx | 12 +++--- 10 files changed, 63 insertions(+), 71 deletions(-) diff --git a/src/frontend/src/api/paymentModuleApi.ts b/src/frontend/src/api/paymentModuleApi.ts index 82d25fceb7..56d8d065d7 100644 --- a/src/frontend/src/api/paymentModuleApi.ts +++ b/src/frontend/src/api/paymentModuleApi.ts @@ -17,12 +17,19 @@ export const fetchPaymentPlansManagerial = async ( return response; }; -export const bulkActionPaymentPlansManagerial = async ( +interface BulkActionPaymentPlansManagerialProps { + businessAreaSlug: string; + ids: string[]; + action: string; + comment: string; +} + +export const bulkActionPaymentPlansManagerial = async ({ businessAreaSlug, ids, action, comment, -) => { +}: BulkActionPaymentPlansManagerialProps) => { const payload: { ids: typeof ids; action: typeof action; comment?: string } = { ids, diff --git a/src/frontend/src/api/periodicDataUpdateApi.ts b/src/frontend/src/api/periodicDataUpdateApi.ts index 29653296e9..0048553b7e 100644 --- a/src/frontend/src/api/periodicDataUpdateApi.ts +++ b/src/frontend/src/api/periodicDataUpdateApi.ts @@ -1,10 +1,15 @@ import { api } from './api'; +import { PaginatedPeriodicDataUpdateTemplateListList } from '@restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList'; +import { PeriodicDataUpdateUploadList } from '@restgenerated/models/PeriodicDataUpdateUploadList'; +import { PeriodicDataUpdateTemplateDetail } from '@restgenerated/models/PeriodicDataUpdateTemplateDetail'; +import { PeriodicDataUpdateUploadDetail } from '@restgenerated/models/PeriodicDataUpdateUploadDetail'; +import { PaginatedPeriodicFieldList } from '@restgenerated/models/PaginatedPeriodicFieldList'; export const fetchPeriodicDataUpdateTemplates = async ( - businessAreaSlug, - programId, + businessAreaSlug: string, + programId: string, params = {}, -) => { +): Promise<PaginatedPeriodicDataUpdateTemplateListList> => { const response = await api.get( `${businessAreaSlug}/programs/${programId}/periodic-data-update/periodic-data-update-templates/`, params, @@ -16,7 +21,7 @@ export const fetchPeriodicDataUpdateUpdates = async ( businessAreaSlug, programId, params = {}, -) => { +): Promise<PeriodicDataUpdateUploadList> => { const response = await api.get( `${businessAreaSlug}/programs/${programId}/periodic-data-update/periodic-data-update-uploads/`, params, @@ -28,7 +33,7 @@ export const fetchPeriodicDataUpdateTemplateDetails = async ( businessAreaSlug, programId, templateId, -) => { +): Promise<PeriodicDataUpdateTemplateDetail> => { const response = await api.get( `${businessAreaSlug}/programs/${programId}/periodic-data-update/periodic-data-update-templates/${templateId}/`, ); @@ -82,7 +87,7 @@ export const fetchPeriodicDataUpdateUploadDetails = async ( businessAreaSlug, programId, uploadId, -) => { +): Promise<PeriodicDataUpdateUploadDetail> => { const response = await api.get( `${businessAreaSlug}/programs/${programId}/periodic-data-update/periodic-data-update-uploads/${uploadId}/`, ); @@ -92,9 +97,8 @@ export const fetchPeriodicDataUpdateUploadDetails = async ( export const createPeriodicDataUpdateTemplate = async ( businessAreaSlug: string, programId: string, - //TODO MS: Add types - roundsData: any, - filters: any, + roundsData: Record<string, any>, + filters: Record<string, any> | null, ) => { const payload = { rounds_data: roundsData, @@ -112,7 +116,7 @@ export const fetchPeriodicFields = async ( businessAreaSlug: string, programId: string, params = {}, -): Promise<any> => { +): Promise<PaginatedPeriodicFieldList> => { const response = await api.get( `${businessAreaSlug}/programs/${programId}/periodic-data-update/periodic-fields/`, params, diff --git a/src/frontend/src/api/rdiApi.ts b/src/frontend/src/api/rdiApi.ts index b34eab26ac..e996d45eaf 100644 --- a/src/frontend/src/api/rdiApi.ts +++ b/src/frontend/src/api/rdiApi.ts @@ -1,10 +1,11 @@ import { api } from './api'; +import { RegistrationDataImportList } from '@restgenerated/models/RegistrationDataImportList'; export const fetchRegistrationDataImports = async ( businessAreaSlug: string, programId: string, params = {}, -): Promise<any> => { +): Promise<RegistrationDataImportList> => { const response = await api.get( `${businessAreaSlug}/programs/${programId}/registration-data/registration-data-imports/`, params, diff --git a/src/frontend/src/api/sharedApi.ts b/src/frontend/src/api/sharedApi.ts index c5f9b04938..0830591ec2 100644 --- a/src/frontend/src/api/sharedApi.ts +++ b/src/frontend/src/api/sharedApi.ts @@ -1,9 +1,10 @@ import { api } from './api'; +import { PaginatedAreaList } from '@restgenerated/models/PaginatedAreaList'; export const fetchAreas = async ( businessArea: string, queryParams: string, -): Promise<any> => { +): Promise<PaginatedAreaList> => { const response = await api.get(`${businessArea}/geo/areas/?${queryParams}`); return response; }; diff --git a/src/frontend/src/api/targetPopulationApi.ts b/src/frontend/src/api/targetPopulationApi.ts index 08796b491a..6cde51e809 100644 --- a/src/frontend/src/api/targetPopulationApi.ts +++ b/src/frontend/src/api/targetPopulationApi.ts @@ -1,11 +1,11 @@ import { api } from './api'; - +import { TargetPopulationList } from '@restgenerated/models/TargetPopulationList'; export const fetchTargetPopulations = async ( businessAreaSlug: string, programId: string, params = {}, -): Promise<any> => { +): Promise<TargetPopulationList> => { const response = await api.get( `${businessAreaSlug}/programs/${programId}/targeting/target-populations/`, params, diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx index 4a56a8b328..615f8eeec3 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx @@ -1,32 +1,32 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { Template } from './PeriodicDataUpdatesTemplatesList'; import { + fetchPeriodicDataUpdateTemplateDetails, + fetchPeriodicFields, +} from '@api/periodicDataUpdateApi'; +import { LabelizedField } from '@components/core/LabelizedField'; +import { LoadingComponent } from '@components/core/LoadingComponent'; +import { useArrayToDict } from '@hooks/useArrayToDict'; +import { useBaseUrl } from '@hooks/useBaseUrl'; +import { + Button, Dialog, - DialogTitle, + DialogActions, DialogContent, + DialogTitle, Table, + TableBody, + TableCell, TableHead, TableRow, - TableCell, - TableBody, - DialogActions, - Button, } from '@mui/material'; -import { LabelizedField } from '@components/core/LabelizedField'; -import { - fetchPeriodicDataUpdateTemplateDetails, - fetchPeriodicFields, -} from '@api/periodicDataUpdateApi'; -import { useBaseUrl } from '@hooks/useBaseUrl'; +import { PeriodicDataUpdateTemplateList } from '@restgenerated/models/PeriodicDataUpdateTemplateList'; import { useQuery } from '@tanstack/react-query'; -import { LoadingComponent } from '@components/core/LoadingComponent'; -import { useArrayToDict } from '@hooks/useArrayToDict'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; interface PeriodicDataUpdatesTemplateDetailsDialogProps { open: boolean; onClose: () => void; - template: Template; + template: PeriodicDataUpdateTemplateList; } export const PeriodicDataUpdatesTemplateDetailsDialog: React.FC< @@ -53,11 +53,7 @@ export const PeriodicDataUpdatesTemplateDetailsDialog: React.FC< queryKey: ['periodicFields', businessArea, programId], queryFn: () => fetchPeriodicFields(businessArea, programId), }); - const pduDataDict = useArrayToDict( - periodicFieldsData?.results, - 'name', - '*', - ); + const pduDataDict = useArrayToDict(periodicFieldsData?.results, 'name', '*'); if (isLoading || periodicFieldsLoading || !pduDataDict) return <LoadingComponent />; return ( diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx index 545a929c34..6c565e16be 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx @@ -22,17 +22,9 @@ import { useTranslation } from 'react-i18next'; import { ButtonTooltip } from '@components/core/ButtonTooltip'; import { usePermissions } from '@hooks/usePermissions'; import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; +import { PeriodicDataUpdateTemplateList } from '@restgenerated/models/PeriodicDataUpdateTemplateList'; -export interface Template { - id: number; - number_of_records: number; - created_at: string; - created_by: string; - status: string; - can_export: boolean; -} - -const templatesHeadCells: HeadCell<Template>[] = [ +const templatesHeadCells: HeadCell<PeriodicDataUpdateTemplateList>[] = [ { id: 'id', numeric: false, @@ -124,7 +116,7 @@ export const PeriodicDataUpdatesTemplatesList = (): ReactElement => { }); }; - const handleDialogOpen = (template: Template) => { + const handleDialogOpen = (template: PeriodicDataUpdateTemplateList) => { setSelectedTemplateId(template.id); setIsDialogOpen(true); }; @@ -170,7 +162,9 @@ export const PeriodicDataUpdatesTemplatesList = (): ReactElement => { permissions, ); - const renderTemplateRow = (row: Template): ReactElement => ( + const renderTemplateRow = ( + row: PeriodicDataUpdateTemplateList, + ): ReactElement => ( <ClickableTableRow key={row.id} data-cy={`template-row-${row.id}`}> <TableCell data-cy={`template-id-${row.id}`}>{row.id}</TableCell> <TableCell data-cy={`template-records-${row.id}`} align="right"> diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx index f0ae3f82b3..54616f7ff0 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx @@ -11,16 +11,9 @@ import { fetchPeriodicDataUpdateUpdates } from '@api/periodicDataUpdateApi'; import { useBaseUrl } from '@hooks/useBaseUrl'; import VisibilityIcon from '@mui/icons-material/Visibility'; import { PeriodicDataUpdatesUploadDetailsDialog } from '@components/periodicDataUpdates/PeriodicDataUpdatesUploadDetailsDialog'; +import { PeriodicDataUpdateUploadList } from '@restgenerated/models/PeriodicDataUpdateUploadList'; -interface Update { - id: string; - template: string; - created_at: string; - created_by: string; - status: string; -} - -const updatesHeadCells: HeadCell<Update>[] = [ +const updatesHeadCells: HeadCell<PeriodicDataUpdateUploadList>[] = [ { id: 'id', numeric: false, @@ -107,7 +100,7 @@ export const PeriodicDataUpdatesUpdatesList = (): ReactElement => { setSelectedUploadId(null); }; - const renderUpdateRow = (row: Update): ReactElement => ( + const renderUpdateRow = (row: PeriodicDataUpdateUploadList): ReactElement => ( <ClickableTableRow key={row.id} data-cy={`update-row-${row.id}`}> <TableCell data-cy={`update-id-${row.id}`}>{row.id}</TableCell> <TableCell data-cy={`update-template-${row.id}`}> diff --git a/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx b/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx index 257ffae326..f252491c4e 100644 --- a/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx +++ b/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx @@ -2,11 +2,7 @@ import { Box, Grid, Typography } from '@mui/material'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; -import { - ProgrammeChoiceDataQuery, - ProgramPartnerAccess, - ProgramQuery, -} from '@generated/graphql'; +import { ProgrammeChoiceDataQuery, ProgramQuery } from '@generated/graphql'; import { MiśTheme } from '../../../theme'; import { choicesToDict, programStatusToColor } from '@utils/utils'; import { ContainerColumnWithBorder } from '@core/ContainerColumnWithBorder'; diff --git a/src/frontend/src/containers/pages/managerialConsole/ManagerialConsolePage.tsx b/src/frontend/src/containers/pages/managerialConsole/ManagerialConsolePage.tsx index 383f1ed657..b6ab96c031 100644 --- a/src/frontend/src/containers/pages/managerialConsole/ManagerialConsolePage.tsx +++ b/src/frontend/src/containers/pages/managerialConsole/ManagerialConsolePage.tsx @@ -100,12 +100,12 @@ export const ManagerialConsolePage: React.FC = () => { const bulkAction = useMutation({ mutationFn: (params: { ids: string[]; action: string; comment: string }) => - bulkActionPaymentPlansManagerial( - businessArea, - params.ids, - params.action, - params.comment, - ), + bulkActionPaymentPlansManagerial({ + businessAreaSlug: businessArea, + ids: params.ids, + action: params.action, + comment: params.comment, + }), onSuccess: () => { refetchInApproval(); refetchInAuthorization(); From 14a3721582457afd6f3635d779116bdf14990d5c Mon Sep 17 00:00:00 2001 From: Jan Romaniak <jan.romaniak@kellton.com> Date: Fri, 4 Oct 2024 16:45:06 +0200 Subject: [PATCH 076/202] fixed retrieving version --- .gitignore | 1 + src/hct_mis_api/__init__.py | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 705c1ce84e..b2df655629 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,4 @@ cov.json .vscode/ output_data/ .coverage +compose.dist.yml diff --git a/src/hct_mis_api/__init__.py b/src/hct_mis_api/__init__.py index 645577a8da..0f94565279 100644 --- a/src/hct_mis_api/__init__.py +++ b/src/hct_mis_api/__init__.py @@ -1,7 +1,18 @@ +import importlib.metadata +import os + +from django.conf import settings + import tomli def get_full_version() -> str: - with open("pyproject.toml", mode="rb") as fp: - config = tomli.load(fp) - return config["project"]["version"] + try: + # works in dist image + version = importlib.metadata.version("hope") + return version + except importlib.metadata.PackageNotFoundError: + # works in local and dev image + with open(os.path.join(settings.PROJECT_ROOT, "../../pyproject.toml"), mode="rb") as fp: + config = tomli.load(fp) + return config["project"]["version"] From dcf5c9e29cac69908a9a884fd952e7e05eb8ee14 Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Fri, 4 Oct 2024 17:19:28 +0200 Subject: [PATCH 077/202] adjust selenium payment plan test --- tests/selenium/payment_module/test_payment_plans.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/selenium/payment_module/test_payment_plans.py b/tests/selenium/payment_module/test_payment_plans.py index 9e3a97e2be..6bdf285915 100644 --- a/tests/selenium/payment_module/test_payment_plans.py +++ b/tests/selenium/payment_module/test_payment_plans.py @@ -255,8 +255,9 @@ def test_smoke_details_payment_plan( assert "FSP" in pagePaymentModuleDetails.getTableLabel()[6].text assert "Entitlement" in pagePaymentModuleDetails.getTableLabel()[7].text assert "Delivered Quantity" in pagePaymentModuleDetails.getTableLabel()[8].text - assert "FSP Auth Code" in pagePaymentModuleDetails.getTableLabel()[9].text - assert "Reconciliation" in pagePaymentModuleDetails.getTableLabel()[10].text + assert "Status" in pagePaymentModuleDetails.getTableLabel()[9].text + assert "FSP Auth Code" in pagePaymentModuleDetails.getTableLabel()[10].text + assert "Reconciliation" in pagePaymentModuleDetails.getTableLabel()[11].text @pytest.mark.skip(reason="Test fails in CI") def test_payment_plan_happy_path( From e9dc18d65801c306fa7deab299025bc7faf3fb15 Mon Sep 17 00:00:00 2001 From: Domenico <dom.dinicola@gmail.com> Date: Fri, 4 Oct 2024 18:54:58 +0200 Subject: [PATCH 078/202] add vision app (#4274) * contrib refactoring * contrib refactoring --------- Co-authored-by: Pavlo Mokiichuk <pv.pasha.pv@gmail.com> --- .gitignore | 4 + .../apps/core/management/commands/initdemo.py | 9 +- src/hct_mis_api/apps/core/tasks_schedules.py | 4 +- src/hct_mis_api/apps/household/models.py | 4 +- src/hct_mis_api/apps/program/apps.py | 1 + src/hct_mis_api/config/settings.py | 3 +- .../{aurora => contrib}/__init__.py | 0 .../migrations => contrib/aurora}/__init__.py | 0 src/hct_mis_api/{ => contrib}/aurora/admin.py | 12 +-- src/hct_mis_api/{ => contrib}/aurora/api.py | 2 +- src/hct_mis_api/{ => contrib}/aurora/apps.py | 12 +-- .../{ => contrib}/aurora/celery_tasks.py | 4 +- .../{ => contrib}/aurora/fixtures.py | 2 +- .../{ => contrib}/aurora/fixtures/data.json | 0 src/hct_mis_api/{ => contrib}/aurora/forms.py | 2 +- .../aurora/migrations/0001_migration.py | 0 .../aurora/migrations/0002_migration.py | 0 .../aurora/migrations}/__init__.py | 0 .../{ => contrib}/aurora/models.py | 2 +- src/hct_mis_api/{ => contrib}/aurora/rdi.py | 0 .../contrib/aurora/services/__init__.py | 0 .../base_flex_registration_service.py | 6 +- ...zech_republic_flex_registration_service.py | 2 +- .../aurora/services/extract_record.py | 2 +- .../services/flex_registration_service.py | 0 .../services/generic_registration_service.py | 2 +- .../sri_lanka_flex_registration_service.py | 2 +- .../ukraine_flex_registration_service.py | 2 +- .../aurora/static/aurora/datatable.js | 0 .../aurora/static/aurora/datatable.scss | 0 .../templates/admin/aurora/app_index.html | 0 .../templates/admin/aurora/record/fetch.html | 0 .../admin/aurora/registration/analyze.html | 0 .../aurora/templates/dataset_list.html | 0 src/hct_mis_api/{ => contrib}/aurora/urls.py | 2 +- src/hct_mis_api/{ => contrib}/aurora/utils.py | 7 +- src/hct_mis_api/{ => contrib}/aurora/views.py | 4 +- src/hct_mis_api/contrib/vision/__init__.py | 0 src/hct_mis_api/contrib/vision/admin.py | 30 ++++++ src/hct_mis_api/contrib/vision/apps.py | 6 ++ src/hct_mis_api/contrib/vision/fixtures.py | 25 +++++ .../vision/migrations/0001_migration.py | 79 ++++++++++++++++ .../contrib/vision/migrations/__init__.py | 0 src/hct_mis_api/contrib/vision/models.py | 94 +++++++++++++++++++ src/hct_mis_api/urls.py | 2 +- tests/unit/apps/account/test_admin.py | 53 ++++++----- .../registration_datahub/test_celery_tasks.py | 18 ++-- .../test_clean_old_record_files_task.py | 4 +- ...est_czech_republic_registration_service.py | 6 +- .../test_extract_records.py | 4 +- .../test_generic_registration_service.py | 6 +- .../test_sri_lanka_registration_service.py | 6 +- .../test_ukrainian_registration_service.py | 6 +- 53 files changed, 336 insertions(+), 93 deletions(-) rename src/hct_mis_api/{aurora => contrib}/__init__.py (100%) rename src/hct_mis_api/{aurora/migrations => contrib/aurora}/__init__.py (100%) rename src/hct_mis_api/{ => contrib}/aurora/admin.py (97%) rename src/hct_mis_api/{ => contrib}/aurora/api.py (98%) rename src/hct_mis_api/{ => contrib}/aurora/apps.py (60%) rename src/hct_mis_api/{ => contrib}/aurora/celery_tasks.py (97%) rename src/hct_mis_api/{ => contrib}/aurora/fixtures.py (95%) rename src/hct_mis_api/{ => contrib}/aurora/fixtures/data.json (100%) rename src/hct_mis_api/{ => contrib}/aurora/forms.py (92%) rename src/hct_mis_api/{ => contrib}/aurora/migrations/0001_migration.py (100%) rename src/hct_mis_api/{ => contrib}/aurora/migrations/0002_migration.py (100%) rename src/hct_mis_api/{aurora/services => contrib/aurora/migrations}/__init__.py (100%) rename src/hct_mis_api/{ => contrib}/aurora/models.py (98%) rename src/hct_mis_api/{ => contrib}/aurora/rdi.py (100%) create mode 100644 src/hct_mis_api/contrib/aurora/services/__init__.py rename src/hct_mis_api/{ => contrib}/aurora/services/base_flex_registration_service.py (97%) rename src/hct_mis_api/{ => contrib}/aurora/services/czech_republic_flex_registration_service.py (99%) rename src/hct_mis_api/{ => contrib}/aurora/services/extract_record.py (98%) rename src/hct_mis_api/{ => contrib}/aurora/services/flex_registration_service.py (100%) rename src/hct_mis_api/{ => contrib}/aurora/services/generic_registration_service.py (99%) rename src/hct_mis_api/{ => contrib}/aurora/services/sri_lanka_flex_registration_service.py (99%) rename src/hct_mis_api/{ => contrib}/aurora/services/ukraine_flex_registration_service.py (99%) rename src/hct_mis_api/{ => contrib}/aurora/static/aurora/datatable.js (100%) rename src/hct_mis_api/{ => contrib}/aurora/static/aurora/datatable.scss (100%) rename src/hct_mis_api/{ => contrib}/aurora/templates/admin/aurora/app_index.html (100%) rename src/hct_mis_api/{ => contrib}/aurora/templates/admin/aurora/record/fetch.html (100%) rename src/hct_mis_api/{ => contrib}/aurora/templates/admin/aurora/registration/analyze.html (100%) rename src/hct_mis_api/{ => contrib}/aurora/templates/dataset_list.html (100%) rename src/hct_mis_api/{ => contrib}/aurora/urls.py (70%) rename src/hct_mis_api/{ => contrib}/aurora/utils.py (96%) rename src/hct_mis_api/{ => contrib}/aurora/views.py (95%) create mode 100644 src/hct_mis_api/contrib/vision/__init__.py create mode 100644 src/hct_mis_api/contrib/vision/admin.py create mode 100644 src/hct_mis_api/contrib/vision/apps.py create mode 100644 src/hct_mis_api/contrib/vision/fixtures.py create mode 100644 src/hct_mis_api/contrib/vision/migrations/0001_migration.py create mode 100644 src/hct_mis_api/contrib/vision/migrations/__init__.py create mode 100644 src/hct_mis_api/contrib/vision/models.py diff --git a/.gitignore b/.gitignore index b2df655629..691473bbb2 100644 --- a/.gitignore +++ b/.gitignore @@ -133,4 +133,8 @@ cov.json .vscode/ output_data/ .coverage + +archive/ +output.json +pytest_html_report.html compose.dist.yml diff --git a/src/hct_mis_api/apps/core/management/commands/initdemo.py b/src/hct_mis_api/apps/core/management/commands/initdemo.py index 6d72be2bf0..4dc6a92d53 100644 --- a/src/hct_mis_api/apps/core/management/commands/initdemo.py +++ b/src/hct_mis_api/apps/core/management/commands/initdemo.py @@ -65,7 +65,7 @@ def handle(self, *args: Any, **options: Any) -> None: call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/household/fixtures/data.json") call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/accountability/fixtures/data.json") call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/steficon/fixtures/data.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/aurora/fixtures/data.json") + call_command("loaddata", f"{settings.PROJECT_ROOT}/contrib/aurora/fixtures/data.json") try: call_command("search_index", "--rebuild", "-f") @@ -86,14 +86,9 @@ def handle(self, *args: Any, **options: Any) -> None: "jan.romaniak@kellton.com", "jakub.krasnowski@kellton.com", "pavlo.mokiichuk@kellton.com", - "kamil.swiechowski@kellton.com", - "karolina.sliwinska@kellton.com", "katarzyna.lanecka@kellton.com", - "konrad.marzec@kellton.com", "maciej.szewczyk@kellton.com", "marek.biczysko@kellton.com", - "patryk.dabrowski@kellton.com", - "zuzanna.okrutna@kellton.com", "gerba@unicef.org", "ddinicola@unicef.org", "sapostolico@unicef.org", @@ -103,10 +98,8 @@ def handle(self, *args: Any, **options: Any) -> None: "asrugano@unicef.org", "gkeriri@unicef.org", "jbassette@unicef.org", - "jyablonski@unicef.org", "nmkuzi@unicef.org", "dhassooneh@unicef.org", - "swaheed@unicef.org", ] tester_list = [ "khaddad@unicef.org", diff --git a/src/hct_mis_api/apps/core/tasks_schedules.py b/src/hct_mis_api/apps/core/tasks_schedules.py index 3a819d914e..5e737ff684 100644 --- a/src/hct_mis_api/apps/core/tasks_schedules.py +++ b/src/hct_mis_api/apps/core/tasks_schedules.py @@ -30,7 +30,7 @@ # "schedule": crontab(hour="*/24"), # }, "extract_records_task": { - "task": "hct_mis_api.aurora.celery_tasks.extract_records_task", + "task": "hct_mis_api.contrib.aurora.celery_tasks.extract_records_task", "schedule": crontab(hour="*/24"), }, "remove_old_cash_plan_payment_verification_xls": { @@ -42,7 +42,7 @@ "schedule": crontab(minute="*/15"), }, "clean_old_record_files_task": { - "task": "hct_mis_api.aurora.celery_tasks.clean_old_record_files_task", + "task": "hct_mis_api.contrib.aurora.celery_tasks.clean_old_record_files_task", "schedule": crontab(month_of_year="2-12/2"), }, "periodic_sync_payment_gateway_fsp": { diff --git a/src/hct_mis_api/apps/household/models.py b/src/hct_mis_api/apps/household/models.py index 202fcaa529..052e5499ef 100644 --- a/src/hct_mis_api/apps/household/models.py +++ b/src/hct_mis_api/apps/household/models.py @@ -53,7 +53,7 @@ ) if TYPE_CHECKING: - from hct_mis_api.aurora.models import Record + from hct_mis_api.contrib.aurora.models import Record BLANK = "" IDP = "IDP" @@ -658,7 +658,7 @@ def alternate_collector(self) -> Optional["Individual"]: @property def flex_registrations_record(self) -> Optional["Record"]: - from hct_mis_api.aurora.models import Record + from hct_mis_api.contrib.aurora.models import Record return Record.objects.filter(id=self.flex_registrations_record_id).first() diff --git a/src/hct_mis_api/apps/program/apps.py b/src/hct_mis_api/apps/program/apps.py index 42fa42f6a0..b6bb76699d 100644 --- a/src/hct_mis_api/apps/program/apps.py +++ b/src/hct_mis_api/apps/program/apps.py @@ -3,6 +3,7 @@ class ProgramConfig(AppConfig): name = "hct_mis_api.apps.program" + verbose_name = "Programme" def ready(self) -> None: from hct_mis_api.apps.grievance import signals as grievance_signals diff --git a/src/hct_mis_api/config/settings.py b/src/hct_mis_api/config/settings.py index 8558b88c54..435bc933d0 100644 --- a/src/hct_mis_api/config/settings.py +++ b/src/hct_mis_api/config/settings.py @@ -233,10 +233,11 @@ "hct_mis_api.apps.steficon.apps.SteficonConfig", "hct_mis_api.apps.reporting.apps.ReportingConfig", "hct_mis_api.apps.activity_log.apps.ActivityLogConfig", - "hct_mis_api.aurora.apps.Config", "hct_mis_api.apps.accountability.apps.AccountabilityConfig", "hct_mis_api.apps.web.apps.WebConfig", "hct_mis_api.apps.periodic_data_update.apps.PeriodicDataUpdateConfig", + "hct_mis_api.contrib.aurora.apps.Config", + "hct_mis_api.contrib.vision.apps.Config", ] DJANGO_APPS = [ diff --git a/src/hct_mis_api/aurora/__init__.py b/src/hct_mis_api/contrib/__init__.py similarity index 100% rename from src/hct_mis_api/aurora/__init__.py rename to src/hct_mis_api/contrib/__init__.py diff --git a/src/hct_mis_api/aurora/migrations/__init__.py b/src/hct_mis_api/contrib/aurora/__init__.py similarity index 100% rename from src/hct_mis_api/aurora/migrations/__init__.py rename to src/hct_mis_api/contrib/aurora/__init__.py diff --git a/src/hct_mis_api/aurora/admin.py b/src/hct_mis_api/contrib/aurora/admin.py similarity index 97% rename from src/hct_mis_api/aurora/admin.py rename to src/hct_mis_api/contrib/aurora/admin.py index 02b626c6a7..885ceb3016 100644 --- a/src/hct_mis_api/aurora/admin.py +++ b/src/hct_mis_api/contrib/aurora/admin.py @@ -31,14 +31,14 @@ from hct_mis_api.apps.registration_data.models import RegistrationDataImport from hct_mis_api.apps.utils.admin import HOPEModelAdminBase from hct_mis_api.apps.utils.security import is_root -from hct_mis_api.aurora import models -from hct_mis_api.aurora.celery_tasks import fresh_extract_records_task -from hct_mis_api.aurora.models import Record, Registration -from hct_mis_api.aurora.services.extract_record import extract -from hct_mis_api.aurora.services.flex_registration_service import ( +from hct_mis_api.contrib.aurora import models +from hct_mis_api.contrib.aurora.celery_tasks import fresh_extract_records_task +from hct_mis_api.contrib.aurora.models import Record, Registration +from hct_mis_api.contrib.aurora.services.extract_record import extract +from hct_mis_api.contrib.aurora.services.flex_registration_service import ( create_task_for_processing_records, ) -from hct_mis_api.aurora.utils import fetch_records, get_metadata +from hct_mis_api.contrib.aurora.utils import fetch_records, get_metadata class StatusFilter(ChoicesFieldComboFilter): diff --git a/src/hct_mis_api/aurora/api.py b/src/hct_mis_api/contrib/aurora/api.py similarity index 98% rename from src/hct_mis_api/aurora/api.py rename to src/hct_mis_api/contrib/aurora/api.py index 02dd379db1..2996bc6ebd 100644 --- a/src/hct_mis_api/aurora/api.py +++ b/src/hct_mis_api/contrib/aurora/api.py @@ -1,7 +1,7 @@ # from rest_framework import serializers # from rest_framework.generics import ListAPIView # -# from hct_mis_api.aurora.models import AuroraRegistration +# from hct_mis_api.contrib.aurora.models import AuroraRegistration # # # class RegistrationDetailSerializer(serializers.ModelSerializer): diff --git a/src/hct_mis_api/aurora/apps.py b/src/hct_mis_api/contrib/aurora/apps.py similarity index 60% rename from src/hct_mis_api/aurora/apps.py rename to src/hct_mis_api/contrib/aurora/apps.py index c2ba04a2b4..ec9e28dac6 100644 --- a/src/hct_mis_api/aurora/apps.py +++ b/src/hct_mis_api/contrib/aurora/apps.py @@ -2,20 +2,20 @@ class Config(AppConfig): - name = "hct_mis_api.aurora" + name = "hct_mis_api.contrib.aurora" def ready(self) -> None: - from hct_mis_api.aurora.rdi import registry - from hct_mis_api.aurora.services.czech_republic_flex_registration_service import ( + from hct_mis_api.contrib.aurora.rdi import registry + from hct_mis_api.contrib.aurora.services.czech_republic_flex_registration_service import ( CzechRepublicFlexRegistration, ) - from hct_mis_api.aurora.services.generic_registration_service import ( + from hct_mis_api.contrib.aurora.services.generic_registration_service import ( GenericRegistrationService, ) - from hct_mis_api.aurora.services.sri_lanka_flex_registration_service import ( + from hct_mis_api.contrib.aurora.services.sri_lanka_flex_registration_service import ( SriLankaRegistrationService, ) - from hct_mis_api.aurora.services.ukraine_flex_registration_service import ( + from hct_mis_api.contrib.aurora.services.ukraine_flex_registration_service import ( Registration2024, UkraineBaseRegistrationService, UkraineRegistrationService, diff --git a/src/hct_mis_api/aurora/celery_tasks.py b/src/hct_mis_api/contrib/aurora/celery_tasks.py similarity index 97% rename from src/hct_mis_api/aurora/celery_tasks.py rename to src/hct_mis_api/contrib/aurora/celery_tasks.py index fd7de580c9..ad4a81188d 100644 --- a/src/hct_mis_api/aurora/celery_tasks.py +++ b/src/hct_mis_api/contrib/aurora/celery_tasks.py @@ -13,8 +13,8 @@ ) from hct_mis_api.apps.utils.logs import log_start_and_end from hct_mis_api.apps.utils.sentry import sentry_tags -from hct_mis_api.aurora.models import Record, Registration -from hct_mis_api.aurora.services.extract_record import extract +from hct_mis_api.contrib.aurora.models import Record, Registration +from hct_mis_api.contrib.aurora.services.extract_record import extract if TYPE_CHECKING: from uuid import UUID diff --git a/src/hct_mis_api/aurora/fixtures.py b/src/hct_mis_api/contrib/aurora/fixtures.py similarity index 95% rename from src/hct_mis_api/aurora/fixtures.py rename to src/hct_mis_api/contrib/aurora/fixtures.py index dccf7cd80d..16024f3c5f 100644 --- a/src/hct_mis_api/aurora/fixtures.py +++ b/src/hct_mis_api/contrib/aurora/fixtures.py @@ -6,7 +6,7 @@ from hct_mis_api.apps.account.fixtures import BusinessAreaFactory from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.aurora.models import Organization, Project, Registration +from hct_mis_api.contrib.aurora.models import Organization, Project, Registration faker = Faker() diff --git a/src/hct_mis_api/aurora/fixtures/data.json b/src/hct_mis_api/contrib/aurora/fixtures/data.json similarity index 100% rename from src/hct_mis_api/aurora/fixtures/data.json rename to src/hct_mis_api/contrib/aurora/fixtures/data.json diff --git a/src/hct_mis_api/aurora/forms.py b/src/hct_mis_api/contrib/aurora/forms.py similarity index 92% rename from src/hct_mis_api/aurora/forms.py rename to src/hct_mis_api/contrib/aurora/forms.py index a56a63d2d4..67f57d24a3 100644 --- a/src/hct_mis_api/aurora/forms.py +++ b/src/hct_mis_api/contrib/aurora/forms.py @@ -3,7 +3,7 @@ from django import forms from django.core.exceptions import ValidationError -from hct_mis_api.aurora.models import Registration +from hct_mis_api.contrib.aurora.models import Registration class ProjectForm(forms.ModelForm): diff --git a/src/hct_mis_api/aurora/migrations/0001_migration.py b/src/hct_mis_api/contrib/aurora/migrations/0001_migration.py similarity index 100% rename from src/hct_mis_api/aurora/migrations/0001_migration.py rename to src/hct_mis_api/contrib/aurora/migrations/0001_migration.py diff --git a/src/hct_mis_api/aurora/migrations/0002_migration.py b/src/hct_mis_api/contrib/aurora/migrations/0002_migration.py similarity index 100% rename from src/hct_mis_api/aurora/migrations/0002_migration.py rename to src/hct_mis_api/contrib/aurora/migrations/0002_migration.py diff --git a/src/hct_mis_api/aurora/services/__init__.py b/src/hct_mis_api/contrib/aurora/migrations/__init__.py similarity index 100% rename from src/hct_mis_api/aurora/services/__init__.py rename to src/hct_mis_api/contrib/aurora/migrations/__init__.py diff --git a/src/hct_mis_api/aurora/models.py b/src/hct_mis_api/contrib/aurora/models.py similarity index 98% rename from src/hct_mis_api/aurora/models.py rename to src/hct_mis_api/contrib/aurora/models.py index 99854595e7..2f8e0f9e55 100644 --- a/src/hct_mis_api/aurora/models.py +++ b/src/hct_mis_api/contrib/aurora/models.py @@ -7,7 +7,7 @@ from strategy_field.fields import StrategyField from hct_mis_api.apps.registration_datahub.utils import combine_collections -from hct_mis_api.aurora.rdi import registry +from hct_mis_api.contrib.aurora.rdi import registry class AuroraModel(models.Model): diff --git a/src/hct_mis_api/aurora/rdi.py b/src/hct_mis_api/contrib/aurora/rdi.py similarity index 100% rename from src/hct_mis_api/aurora/rdi.py rename to src/hct_mis_api/contrib/aurora/rdi.py diff --git a/src/hct_mis_api/contrib/aurora/services/__init__.py b/src/hct_mis_api/contrib/aurora/services/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/hct_mis_api/aurora/services/base_flex_registration_service.py b/src/hct_mis_api/contrib/aurora/services/base_flex_registration_service.py similarity index 97% rename from src/hct_mis_api/aurora/services/base_flex_registration_service.py rename to src/hct_mis_api/contrib/aurora/services/base_flex_registration_service.py index ed2fd14baf..6d36b80e7f 100644 --- a/src/hct_mis_api/aurora/services/base_flex_registration_service.py +++ b/src/hct_mis_api/contrib/aurora/services/base_flex_registration_service.py @@ -14,9 +14,9 @@ from hct_mis_api.apps.household.models import PendingHousehold, PendingIndividual from hct_mis_api.apps.registration_data.models import ImportData, RegistrationDataImport from hct_mis_api.apps.registration_datahub.celery_tasks import rdi_deduplication_task -from hct_mis_api.aurora.celery_tasks import process_flex_records_task -from hct_mis_api.aurora.models import Record, Registration -from hct_mis_api.aurora.rdi import AuroraProcessor +from hct_mis_api.contrib.aurora.celery_tasks import process_flex_records_task +from hct_mis_api.contrib.aurora.models import Record, Registration +from hct_mis_api.contrib.aurora.rdi import AuroraProcessor if TYPE_CHECKING: from uuid import UUID diff --git a/src/hct_mis_api/aurora/services/czech_republic_flex_registration_service.py b/src/hct_mis_api/contrib/aurora/services/czech_republic_flex_registration_service.py similarity index 99% rename from src/hct_mis_api/aurora/services/czech_republic_flex_registration_service.py rename to src/hct_mis_api/contrib/aurora/services/czech_republic_flex_registration_service.py index dba2abe990..70e391a9bc 100644 --- a/src/hct_mis_api/aurora/services/czech_republic_flex_registration_service.py +++ b/src/hct_mis_api/contrib/aurora/services/czech_republic_flex_registration_service.py @@ -33,7 +33,7 @@ PendingIndividualRoleInHousehold, ) from hct_mis_api.apps.registration_data.models import RegistrationDataImport -from hct_mis_api.aurora.services.base_flex_registration_service import ( +from hct_mis_api.contrib.aurora.services.base_flex_registration_service import ( BaseRegistrationService, ) diff --git a/src/hct_mis_api/aurora/services/extract_record.py b/src/hct_mis_api/contrib/aurora/services/extract_record.py similarity index 98% rename from src/hct_mis_api/aurora/services/extract_record.py rename to src/hct_mis_api/contrib/aurora/services/extract_record.py index ad71a1814e..b75b95cf4b 100644 --- a/src/hct_mis_api/aurora/services/extract_record.py +++ b/src/hct_mis_api/contrib/aurora/services/extract_record.py @@ -2,7 +2,7 @@ from typing import Any, Iterable from hct_mis_api.apps.registration_datahub.templatetags.smart_register import is_image -from hct_mis_api.aurora.models import Record +from hct_mis_api.contrib.aurora.models import Record logger = logging.getLogger(__name__) diff --git a/src/hct_mis_api/aurora/services/flex_registration_service.py b/src/hct_mis_api/contrib/aurora/services/flex_registration_service.py similarity index 100% rename from src/hct_mis_api/aurora/services/flex_registration_service.py rename to src/hct_mis_api/contrib/aurora/services/flex_registration_service.py diff --git a/src/hct_mis_api/aurora/services/generic_registration_service.py b/src/hct_mis_api/contrib/aurora/services/generic_registration_service.py similarity index 99% rename from src/hct_mis_api/aurora/services/generic_registration_service.py rename to src/hct_mis_api/contrib/aurora/services/generic_registration_service.py index 52736f51c9..d414cda68f 100644 --- a/src/hct_mis_api/aurora/services/generic_registration_service.py +++ b/src/hct_mis_api/contrib/aurora/services/generic_registration_service.py @@ -22,7 +22,7 @@ PendingIndividualRoleInHousehold, ) from hct_mis_api.apps.registration_data.models import RegistrationDataImport -from hct_mis_api.aurora.services.base_flex_registration_service import ( +from hct_mis_api.contrib.aurora.services.base_flex_registration_service import ( BaseRegistrationService, ) diff --git a/src/hct_mis_api/aurora/services/sri_lanka_flex_registration_service.py b/src/hct_mis_api/contrib/aurora/services/sri_lanka_flex_registration_service.py similarity index 99% rename from src/hct_mis_api/aurora/services/sri_lanka_flex_registration_service.py rename to src/hct_mis_api/contrib/aurora/services/sri_lanka_flex_registration_service.py index d8ca36e7e3..b847796fff 100644 --- a/src/hct_mis_api/aurora/services/sri_lanka_flex_registration_service.py +++ b/src/hct_mis_api/contrib/aurora/services/sri_lanka_flex_registration_service.py @@ -23,7 +23,7 @@ from hct_mis_api.apps.periodic_data_update.utils import populate_pdu_with_null_values from hct_mis_api.apps.registration_data.models import RegistrationDataImport from hct_mis_api.apps.utils.age_at_registration import calculate_age_at_registration -from hct_mis_api.aurora.services.base_flex_registration_service import ( +from hct_mis_api.contrib.aurora.services.base_flex_registration_service import ( BaseRegistrationService, ) diff --git a/src/hct_mis_api/aurora/services/ukraine_flex_registration_service.py b/src/hct_mis_api/contrib/aurora/services/ukraine_flex_registration_service.py similarity index 99% rename from src/hct_mis_api/aurora/services/ukraine_flex_registration_service.py rename to src/hct_mis_api/contrib/aurora/services/ukraine_flex_registration_service.py index d24f98160a..01cd922083 100644 --- a/src/hct_mis_api/aurora/services/ukraine_flex_registration_service.py +++ b/src/hct_mis_api/contrib/aurora/services/ukraine_flex_registration_service.py @@ -37,7 +37,7 @@ PendingIndividualRoleInHousehold, ) from hct_mis_api.apps.registration_data.models import RegistrationDataImport -from hct_mis_api.aurora.services.base_flex_registration_service import ( +from hct_mis_api.contrib.aurora.services.base_flex_registration_service import ( BaseRegistrationService, ) diff --git a/src/hct_mis_api/aurora/static/aurora/datatable.js b/src/hct_mis_api/contrib/aurora/static/aurora/datatable.js similarity index 100% rename from src/hct_mis_api/aurora/static/aurora/datatable.js rename to src/hct_mis_api/contrib/aurora/static/aurora/datatable.js diff --git a/src/hct_mis_api/aurora/static/aurora/datatable.scss b/src/hct_mis_api/contrib/aurora/static/aurora/datatable.scss similarity index 100% rename from src/hct_mis_api/aurora/static/aurora/datatable.scss rename to src/hct_mis_api/contrib/aurora/static/aurora/datatable.scss diff --git a/src/hct_mis_api/aurora/templates/admin/aurora/app_index.html b/src/hct_mis_api/contrib/aurora/templates/admin/aurora/app_index.html similarity index 100% rename from src/hct_mis_api/aurora/templates/admin/aurora/app_index.html rename to src/hct_mis_api/contrib/aurora/templates/admin/aurora/app_index.html diff --git a/src/hct_mis_api/aurora/templates/admin/aurora/record/fetch.html b/src/hct_mis_api/contrib/aurora/templates/admin/aurora/record/fetch.html similarity index 100% rename from src/hct_mis_api/aurora/templates/admin/aurora/record/fetch.html rename to src/hct_mis_api/contrib/aurora/templates/admin/aurora/record/fetch.html diff --git a/src/hct_mis_api/aurora/templates/admin/aurora/registration/analyze.html b/src/hct_mis_api/contrib/aurora/templates/admin/aurora/registration/analyze.html similarity index 100% rename from src/hct_mis_api/aurora/templates/admin/aurora/registration/analyze.html rename to src/hct_mis_api/contrib/aurora/templates/admin/aurora/registration/analyze.html diff --git a/src/hct_mis_api/aurora/templates/dataset_list.html b/src/hct_mis_api/contrib/aurora/templates/dataset_list.html similarity index 100% rename from src/hct_mis_api/aurora/templates/dataset_list.html rename to src/hct_mis_api/contrib/aurora/templates/dataset_list.html diff --git a/src/hct_mis_api/aurora/urls.py b/src/hct_mis_api/contrib/aurora/urls.py similarity index 70% rename from src/hct_mis_api/aurora/urls.py rename to src/hct_mis_api/contrib/aurora/urls.py index 71f73d3a94..1f299a26f2 100644 --- a/src/hct_mis_api/aurora/urls.py +++ b/src/hct_mis_api/contrib/aurora/urls.py @@ -1,6 +1,6 @@ from django.urls import path -from hct_mis_api.aurora.views import FetchDataView, RegistrationDataView +from hct_mis_api.contrib.aurora.views import FetchDataView, RegistrationDataView app_name = "aurora" diff --git a/src/hct_mis_api/aurora/utils.py b/src/hct_mis_api/contrib/aurora/utils.py similarity index 96% rename from src/hct_mis_api/aurora/utils.py rename to src/hct_mis_api/contrib/aurora/utils.py index 554101450c..c14e39ab7f 100644 --- a/src/hct_mis_api/aurora/utils.py +++ b/src/hct_mis_api/contrib/aurora/utils.py @@ -11,7 +11,12 @@ from coreapi import codecs from coreapi.exceptions import NoCodecAvailable -from hct_mis_api.aurora.models import Organization, Project, Record, Registration +from hct_mis_api.contrib.aurora.models import ( + Organization, + Project, + Record, + Registration, +) logger = logging.getLogger(__name__) diff --git a/src/hct_mis_api/aurora/views.py b/src/hct_mis_api/contrib/aurora/views.py similarity index 95% rename from src/hct_mis_api/aurora/views.py rename to src/hct_mis_api/contrib/aurora/views.py index 32f003aa3a..e578de9400 100644 --- a/src/hct_mis_api/aurora/views.py +++ b/src/hct_mis_api/contrib/aurora/views.py @@ -11,8 +11,8 @@ from admin_extra_buttons.utils import HttpResponseRedirectToReferrer from sentry_sdk import set_tag -from hct_mis_api.aurora.models import Registration -from hct_mis_api.aurora.utils import fetch_metadata +from hct_mis_api.contrib.aurora.models import Registration +from hct_mis_api.contrib.aurora.utils import fetch_metadata class FetchDataView(ProcessFormView): diff --git a/src/hct_mis_api/contrib/vision/__init__.py b/src/hct_mis_api/contrib/vision/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/hct_mis_api/contrib/vision/admin.py b/src/hct_mis_api/contrib/vision/admin.py new file mode 100644 index 0000000000..435a9f4480 --- /dev/null +++ b/src/hct_mis_api/contrib/vision/admin.py @@ -0,0 +1,30 @@ +from django.contrib import admin + +from adminfilters.filters import ValueFilter + +from hct_mis_api.apps.utils.admin import HOPEModelAdminBase +from hct_mis_api.contrib.vision.models import DownPayment, FundsCommitment + + +@admin.register(FundsCommitment) +class FundsCommitmentAdmin(HOPEModelAdminBase): + list_display = ( + "rec_serial_number", + "business_area", + "funds_commitment_item", + "funds_commitment_number", + "posting_date", + ) + list_filter = ( + "business_area", + "posting_date", + ("business_area", ValueFilter), + ) + search_fields = ("rec_serial_number", "vendor_id", "wbs_element", "funds_commitment_number") + + +@admin.register(DownPayment) +class DownPaymentAdmin(HOPEModelAdminBase): + list_display = ("rec_serial_number", "down_payment_reference", "business_area", "consumed_fc_number") + + list_filter = (("business_area", ValueFilter),) diff --git a/src/hct_mis_api/contrib/vision/apps.py b/src/hct_mis_api/contrib/vision/apps.py new file mode 100644 index 0000000000..83f681e656 --- /dev/null +++ b/src/hct_mis_api/contrib/vision/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class Config(AppConfig): + name = "hct_mis_api.contrib.vision" + verbose_name = "Vision" diff --git a/src/hct_mis_api/contrib/vision/fixtures.py b/src/hct_mis_api/contrib/vision/fixtures.py new file mode 100644 index 0000000000..534a3c7f85 --- /dev/null +++ b/src/hct_mis_api/contrib/vision/fixtures.py @@ -0,0 +1,25 @@ +import factory.fuzzy +from factory.django import DjangoModelFactory +from pytz import utc + +from hct_mis_api.apps.core.models import BusinessArea +from hct_mis_api.contrib.vision.models import FundsCommitment + + +class FundsCommitmentFactory(DjangoModelFactory): + class Meta: + model = FundsCommitment + + rec_serial_number = factory.fuzzy.FuzzyInteger(1000, 99999999) + business_area = factory.LazyAttribute(lambda o: BusinessArea.objects.first().cash_assist_code) + document_type = "DO" + currency_code = factory.Faker("currency_code") + + total_open_amount_local = factory.fuzzy.FuzzyDecimal(100.0, 10000.0) + total_open_amount_usd = factory.fuzzy.FuzzyDecimal(100.0, 10000.0) + update_date = factory.Faker( + "date_time_this_decade", + before_now=True, + after_now=False, + tzinfo=utc, + ) diff --git a/src/hct_mis_api/contrib/vision/migrations/0001_migration.py b/src/hct_mis_api/contrib/vision/migrations/0001_migration.py new file mode 100644 index 0000000000..368119e567 --- /dev/null +++ b/src/hct_mis_api/contrib/vision/migrations/0001_migration.py @@ -0,0 +1,79 @@ +# Generated by Django 3.2.25 on 2024-10-01 11:58 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('core', '0087_migration'), + ] + + operations = [ + migrations.CreateModel( + name='DownPayment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('rec_serial_number', models.CharField(blank=True, max_length=10, null=True)), + ('business_area', models.CharField(max_length=4)), + ('down_payment_reference', models.CharField(max_length=20)), + ('document_type', models.CharField(max_length=10)), + ('consumed_fc_number', models.CharField(max_length=10)), + ('total_down_payment_amount_local', models.DecimalField(decimal_places=2, max_digits=15)), + ('total_down_payment_amount_usd', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), + ('currency_code', models.CharField(blank=True, max_length=5, null=True)), + ('posting_date', models.DateField(blank=True, null=True)), + ('doc_year', models.IntegerField(blank=True, null=True)), + ('doc_number', models.CharField(blank=True, max_length=10, null=True)), + ('doc_item_number', models.CharField(max_length=3, null=True)), + ('create_date', models.DateTimeField(auto_now_add=True, null=True)), + ('created_by', models.CharField(blank=True, default='', max_length=20, null=True)), + ('update_date', models.DateTimeField(blank=True, null=True)), + ('updated_by', models.CharField(blank=True, default='', max_length=20, null=True)), + ('business_office_code', models.CharField(blank=True, max_length=4, null=True)), + ('business_area_obj', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.businessarea')), + ], + ), + migrations.CreateModel( + name='FundsCommitment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('rec_serial_number', models.CharField(max_length=10, unique=True)), + ('business_area', models.CharField(blank=True, max_length=4, null=True)), + ('funds_commitment_number', models.CharField(blank=True, max_length=10, null=True)), + ('document_type', models.CharField(blank=True, max_length=2, null=True)), + ('document_text', models.CharField(blank=True, max_length=50, null=True)), + ('currency_code', models.CharField(blank=True, max_length=5, null=True)), + ('gl_account', models.CharField(blank=True, max_length=10, null=True)), + ('commitment_amount_local', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), + ('commitment_amount_usd', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), + ('total_open_amount_local', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), + ('total_open_amount_usd', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), + ('vendor_id', models.CharField(blank=True, max_length=10, null=True)), + ('posting_date', models.DateField(blank=True, null=True)), + ('vision_approval', models.CharField(blank=True, max_length=1, null=True)), + ('document_reference', models.CharField(max_length=16, null=True)), + ('fc_status', models.CharField(blank=True, max_length=1, null=True)), + ('create_date', models.DateTimeField(auto_now_add=True, null=True)), + ('created_by', models.CharField(blank=True, default='', max_length=20, null=True)), + ('update_date', models.DateTimeField(blank=True, null=True)), + ('updated_by', models.CharField(blank=True, default='', max_length=20, null=True)), + ('grant_number', models.CharField(blank=True, default='', max_length=10, null=True)), + ('sponsor', models.CharField(blank=True, default='', max_length=10, null=True)), + ('sponsor_name', models.CharField(blank=True, default='', max_length=100, null=True)), + ('wbs_element', models.CharField(blank=True, default='', max_length=24, null=True)), + ('fund', models.CharField(blank=True, default='', max_length=10, null=True)), + ('funds_center', models.CharField(blank=True, default='', max_length=16, null=True)), + ('funds_commitment_item', models.CharField(blank=True, default='', max_length=3, null=True)), + ('percentage', models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True)), + ('business_office_code', models.CharField(blank=True, max_length=4, null=True)), + ('business_area_obj', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.businessarea')), + ], + options={ + 'unique_together': {('funds_commitment_number', 'funds_commitment_item')}, + }, + ), + ] diff --git a/src/hct_mis_api/contrib/vision/migrations/__init__.py b/src/hct_mis_api/contrib/vision/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/hct_mis_api/contrib/vision/models.py b/src/hct_mis_api/contrib/vision/models.py new file mode 100644 index 0000000000..69a44bb794 --- /dev/null +++ b/src/hct_mis_api/contrib/vision/models.py @@ -0,0 +1,94 @@ +from django.db import models + +from hct_mis_api.apps.core.models import BusinessArea + + +class FundsCommitment(models.Model): + rec_serial_number = models.CharField(max_length=10, unique=True) + business_area = models.CharField(max_length=4, blank=True, null=True) + funds_commitment_number = models.CharField(max_length=10, blank=True, null=True) + document_type = models.CharField(max_length=2, blank=True, null=True) + document_text = models.CharField(max_length=50, blank=True, null=True) + currency_code = models.CharField(max_length=5, blank=True, null=True) + gl_account = models.CharField(null=True, blank=True, max_length=10) + commitment_amount_local = models.DecimalField( + decimal_places=2, + max_digits=15, + blank=True, + null=True, + ) + commitment_amount_usd = models.DecimalField( + decimal_places=2, + max_digits=15, + blank=True, + null=True, + ) + total_open_amount_local = models.DecimalField( + decimal_places=2, + max_digits=15, + blank=True, + null=True, + ) + total_open_amount_usd = models.DecimalField( + decimal_places=2, + max_digits=15, + blank=True, + null=True, + ) + vendor_id = models.CharField(max_length=10, blank=True, null=True) + posting_date = models.DateField(blank=True, null=True) + vision_approval = models.CharField(max_length=1, blank=True, null=True) + document_reference = models.CharField(max_length=16, null=True) + fc_status = models.CharField(max_length=1, blank=True, null=True) + create_date = models.DateTimeField(null=True, blank=True, auto_now_add=True) + created_by = models.CharField(max_length=20, null=True, blank=True, default="") + update_date = models.DateTimeField(null=True, blank=True) + updated_by = models.CharField(max_length=20, blank=True, null=True, default="") + + grant_number = models.CharField(max_length=10, null=True, blank=True, default="") + sponsor = models.CharField(max_length=10, null=True, blank=True, default="") + sponsor_name = models.CharField(max_length=100, null=True, blank=True, default="") + wbs_element = models.CharField(max_length=24, null=True, blank=True, default="") + fund = models.CharField(max_length=10, null=True, blank=True, default="") + funds_center = models.CharField(max_length=16, null=True, blank=True, default="") + funds_commitment_item = models.CharField(max_length=3, null=True, blank=True, default="") + percentage = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True) + + business_office_code = models.CharField(max_length=4, blank=True, null=True) + business_area_obj = models.ForeignKey(BusinessArea, on_delete=models.SET_NULL, null=True, blank=True) + + def __str__(self) -> str: + return self.funds_commitment_number + + class Meta: + unique_together = ("funds_commitment_number", "funds_commitment_item") + + +class DownPayment(models.Model): + rec_serial_number = models.CharField(max_length=10, blank=True, null=True) + business_area = models.CharField(max_length=4) + down_payment_reference = models.CharField(max_length=20) + document_type = models.CharField(max_length=10) + consumed_fc_number = models.CharField(max_length=10) + total_down_payment_amount_local = models.DecimalField( + decimal_places=2, + max_digits=15, + ) + total_down_payment_amount_usd = models.DecimalField( + decimal_places=2, + max_digits=15, + blank=True, + null=True, + ) + currency_code = models.CharField(max_length=5, blank=True, null=True) + posting_date = models.DateField(blank=True, null=True) + doc_year = models.IntegerField(blank=True, null=True) + doc_number = models.CharField(max_length=10, blank=True, null=True) + doc_item_number = models.CharField(max_length=3, null=True) + create_date = models.DateTimeField(null=True, blank=True, auto_now_add=True) + created_by = models.CharField(max_length=20, blank=True, null=True, default="") + update_date = models.DateTimeField(blank=True, null=True) + updated_by = models.CharField(max_length=20, blank=True, null=True, default="") + + business_area_obj = models.ForeignKey(BusinessArea, on_delete=models.SET_NULL, null=True, blank=True) + business_office_code = models.CharField(max_length=4, blank=True, null=True) diff --git a/src/hct_mis_api/urls.py b/src/hct_mis_api/urls.py index c4896a4c42..22a56b66ac 100644 --- a/src/hct_mis_api/urls.py +++ b/src/hct_mis_api/urls.py @@ -101,7 +101,7 @@ path(f"{settings.ADMIN_PANEL_URL}/", admin.site.urls), path("hh-status", hct_mis_api.apps.household.views.HouseholdStatusView.as_view()), path("upload-file/", UploadFile.as_view(), name="upload-file"), - path("aurora/", include("hct_mis_api.aurora.urls", namespace="aurora")), + path("aurora/", include("hct_mis_api.contrib.aurora.urls", namespace="aurora")), ] if settings.PROFILING: diff --git a/tests/unit/apps/account/test_admin.py b/tests/unit/apps/account/test_admin.py index de60521556..abd919d946 100644 --- a/tests/unit/apps/account/test_admin.py +++ b/tests/unit/apps/account/test_admin.py @@ -3,29 +3,34 @@ from django.urls import reverse -from django_webtest import WebTest +import pytest +from django_webtest import DjangoTestApp from hct_mis_api.apps.account.fixtures import RoleFactory, UserFactory -from hct_mis_api.apps.account.models import User - - -class RoleTest(WebTest): - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - RoleFactory() - cls.superuser: User = UserFactory(is_superuser=True, is_staff=True) - - def test_role_perm_matrix(self) -> None: - url = reverse("admin:account_role_matrix") - res = self.app.get(url, user=self.superuser) - assert res.status_code == 200 - - def test_role_sync(self) -> None: - url = reverse("admin:account_role_dumpdata_qs") - res = self.app.get(url, user=self.superuser) - assert res.status_code == 200 - jres = json.loads(unquote(res.json["data"])) - models = set([item["model"] for item in jres]) - assert len(models) == 1 - assert models == {"account.role"} +from hct_mis_api.apps.account.models import Role, User + + +@pytest.fixture() +def superuser(request: pytest.FixtureRequest) -> User: + return UserFactory(is_superuser=True, is_staff=True) + + +@pytest.fixture() +def role(request: pytest.FixtureRequest) -> Role: + return RoleFactory(name="Role") + + +def test_role_perm_matrix(django_app: DjangoTestApp, superuser: pytest.FixtureRequest) -> None: + url = reverse("admin:account_role_matrix") + res = django_app.get(url, user=superuser) + assert res.status_code == 200 + + +def test_role_sync(django_app: DjangoTestApp, superuser: User, role: Role) -> None: + url = reverse("admin:account_role_dumpdata_qs") + res = django_app.get(url, user=superuser) + assert res.status_code == 200 + jres = json.loads(unquote(res.json["data"])) + models = set([item["model"] for item in jres]) + assert len(models) == 1 + assert models == {"account.role"} diff --git a/tests/unit/apps/registration_datahub/test_celery_tasks.py b/tests/unit/apps/registration_datahub/test_celery_tasks.py index dd516ca871..53d63bd82f 100644 --- a/tests/unit/apps/registration_datahub/test_celery_tasks.py +++ b/tests/unit/apps/registration_datahub/test_celery_tasks.py @@ -67,26 +67,26 @@ PullKoboSubmissions, ) from hct_mis_api.apps.utils.models import MergeStatusModel -from hct_mis_api.aurora.celery_tasks import ( +from hct_mis_api.contrib.aurora.celery_tasks import ( automate_rdi_creation_task, process_flex_records_task, ) -from hct_mis_api.aurora.fixtures import ( +from hct_mis_api.contrib.aurora.fixtures import ( OrganizationFactory, ProjectFactory, RegistrationFactory, ) -from hct_mis_api.aurora.models import Record -from hct_mis_api.aurora.services.base_flex_registration_service import ( +from hct_mis_api.contrib.aurora.models import Record +from hct_mis_api.contrib.aurora.services.base_flex_registration_service import ( BaseRegistrationService, ) -from hct_mis_api.aurora.services.flex_registration_service import ( +from hct_mis_api.contrib.aurora.services.flex_registration_service import ( create_task_for_processing_records, ) -from hct_mis_api.aurora.services.sri_lanka_flex_registration_service import ( +from hct_mis_api.contrib.aurora.services.sri_lanka_flex_registration_service import ( SriLankaRegistrationService, ) -from hct_mis_api.aurora.services.ukraine_flex_registration_service import ( +from hct_mis_api.contrib.aurora.services.ukraine_flex_registration_service import ( UkraineBaseRegistrationService, UkraineRegistrationService, ) @@ -487,7 +487,7 @@ def create_imported_document_types() -> None: def create_ukraine_business_area() -> None: - from hct_mis_api.aurora.models import Registration + from hct_mis_api.contrib.aurora.models import Registration slug = "ukraine" BusinessArea.objects.create( @@ -553,7 +553,7 @@ def do_nothing_cache(*_args: Any, **_kwargs: Any) -> Generator: @patch( - "hct_mis_api.aurora.services.base_flex_registration_service.BaseRegistrationService.validate_data_collection_type" + "hct_mis_api.contrib.aurora.services.base_flex_registration_service.BaseRegistrationService.validate_data_collection_type" ) class TestAutomatingRDICreationTask(TestCase): databases = { diff --git a/tests/unit/apps/registration_datahub/test_clean_old_record_files_task.py b/tests/unit/apps/registration_datahub/test_clean_old_record_files_task.py index c6adc5baaa..ea212a3691 100644 --- a/tests/unit/apps/registration_datahub/test_clean_old_record_files_task.py +++ b/tests/unit/apps/registration_datahub/test_clean_old_record_files_task.py @@ -3,7 +3,7 @@ from django.utils import timezone from hct_mis_api.apps.core.base_test_case import APITestCase -from hct_mis_api.aurora.models import Record +from hct_mis_api.contrib.aurora.models import Record class TestClearRecordFilesTask(APITestCase): @@ -48,7 +48,7 @@ def setUpTestData(cls) -> None: ) def test_clean_old_record_files_task(self) -> None: - from hct_mis_api.aurora.celery_tasks import clean_old_record_files_task + from hct_mis_api.contrib.aurora.celery_tasks import clean_old_record_files_task clean_old_record_files_task() diff --git a/tests/unit/apps/registration_datahub/test_czech_republic_registration_service.py b/tests/unit/apps/registration_datahub/test_czech_republic_registration_service.py index 1a5c312e9d..40f3d07a53 100644 --- a/tests/unit/apps/registration_datahub/test_czech_republic_registration_service.py +++ b/tests/unit/apps/registration_datahub/test_czech_republic_registration_service.py @@ -24,13 +24,13 @@ PendingIndividualRoleInHousehold, ) from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.aurora.fixtures import ( +from hct_mis_api.contrib.aurora.fixtures import ( OrganizationFactory, ProjectFactory, RegistrationFactory, ) -from hct_mis_api.aurora.models import Record -from hct_mis_api.aurora.services.czech_republic_flex_registration_service import ( +from hct_mis_api.contrib.aurora.models import Record +from hct_mis_api.contrib.aurora.services.czech_republic_flex_registration_service import ( CzechRepublicFlexRegistration, ) diff --git a/tests/unit/apps/registration_datahub/test_extract_records.py b/tests/unit/apps/registration_datahub/test_extract_records.py index a995c97509..3fe906d263 100644 --- a/tests/unit/apps/registration_datahub/test_extract_records.py +++ b/tests/unit/apps/registration_datahub/test_extract_records.py @@ -6,8 +6,8 @@ from django.test import TestCase from django.utils import timezone -from hct_mis_api.aurora.celery_tasks import extract_records_task -from hct_mis_api.aurora.models import Record +from hct_mis_api.contrib.aurora.celery_tasks import extract_records_task +from hct_mis_api.contrib.aurora.models import Record class TestExtractRecords(TestCase): diff --git a/tests/unit/apps/registration_datahub/test_generic_registration_service.py b/tests/unit/apps/registration_datahub/test_generic_registration_service.py index bfd6bc9420..cd9bd3127d 100644 --- a/tests/unit/apps/registration_datahub/test_generic_registration_service.py +++ b/tests/unit/apps/registration_datahub/test_generic_registration_service.py @@ -22,13 +22,13 @@ PendingIndividualRoleInHousehold, ) from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.aurora.fixtures import ( +from hct_mis_api.contrib.aurora.fixtures import ( OrganizationFactory, ProjectFactory, RegistrationFactory, ) -from hct_mis_api.aurora.models import Record -from hct_mis_api.aurora.services.generic_registration_service import ( +from hct_mis_api.contrib.aurora.models import Record +from hct_mis_api.contrib.aurora.services.generic_registration_service import ( GenericRegistrationService, ) diff --git a/tests/unit/apps/registration_datahub/test_sri_lanka_registration_service.py b/tests/unit/apps/registration_datahub/test_sri_lanka_registration_service.py index 0a16ca5616..88fc8a4503 100644 --- a/tests/unit/apps/registration_datahub/test_sri_lanka_registration_service.py +++ b/tests/unit/apps/registration_datahub/test_sri_lanka_registration_service.py @@ -20,13 +20,13 @@ PendingIndividualRoleInHousehold, ) from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.aurora.fixtures import ( +from hct_mis_api.contrib.aurora.fixtures import ( OrganizationFactory, ProjectFactory, RegistrationFactory, ) -from hct_mis_api.aurora.models import Record -from hct_mis_api.aurora.services.sri_lanka_flex_registration_service import ( +from hct_mis_api.contrib.aurora.models import Record +from hct_mis_api.contrib.aurora.services.sri_lanka_flex_registration_service import ( SriLankaRegistrationService, ) diff --git a/tests/unit/apps/registration_datahub/test_ukrainian_registration_service.py b/tests/unit/apps/registration_datahub/test_ukrainian_registration_service.py index 4e2ca80110..b630d669e7 100644 --- a/tests/unit/apps/registration_datahub/test_ukrainian_registration_service.py +++ b/tests/unit/apps/registration_datahub/test_ukrainian_registration_service.py @@ -18,13 +18,13 @@ PendingIndividual, ) from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.aurora.fixtures import ( +from hct_mis_api.contrib.aurora.fixtures import ( OrganizationFactory, ProjectFactory, RegistrationFactory, ) -from hct_mis_api.aurora.models import Record -from hct_mis_api.aurora.services.ukraine_flex_registration_service import ( +from hct_mis_api.contrib.aurora.models import Record +from hct_mis_api.contrib.aurora.services.ukraine_flex_registration_service import ( Registration2024, UkraineBaseRegistrationService, ) From d53bfef7c939d5d6fc386916f0886959e76e0e14 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Sun, 6 Oct 2024 22:44:47 +0200 Subject: [PATCH 079/202] change text of xlsx individual update for invalid phone no --- .../services/individual_xlsx_update.py | 2 ++ .../household/test_individual_xlsx_update.py | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/hct_mis_api/apps/household/services/individual_xlsx_update.py b/src/hct_mis_api/apps/household/services/individual_xlsx_update.py index 89fdf4c388..e2d6d1d622 100644 --- a/src/hct_mis_api/apps/household/services/individual_xlsx_update.py +++ b/src/hct_mis_api/apps/household/services/individual_xlsx_update.py @@ -144,6 +144,8 @@ def _update_single_individual(self, row: Row, individual: Individual) -> Individ field.required = False if not form.is_valid(): + if "phone_no" in form.errors: + form.errors["phone_no"] = [f"Invalid phone number for individual {individual.unicef_id}."] raise ValidationError(form.errors) # TODO: add 'program_id' arg or None? individual.program_id log_create(Individual.ACTIVITY_LOG_MAPPING, "business_area", None, None, old_individual, individual) diff --git a/tests/unit/apps/household/test_individual_xlsx_update.py b/tests/unit/apps/household/test_individual_xlsx_update.py index 452cc7faee..cdc9f2561c 100644 --- a/tests/unit/apps/household/test_individual_xlsx_update.py +++ b/tests/unit/apps/household/test_individual_xlsx_update.py @@ -3,6 +3,7 @@ from pathlib import Path from django.conf import settings +from django.core.exceptions import ValidationError from django.core.files import File from hct_mis_api.apps.core.base_test_case import APITestCase @@ -39,6 +40,13 @@ def invalid_file() -> File: return File(BytesIO(content), name="invalid_updated_test_file.xlsx") +def invalid_phone_no_file() -> File: + content = Path( + f"{settings.TESTS_ROOT}/apps/household/test_file/invalid_updated_test_file_wrong_phone_no.xlsx" + ).read_bytes() + return File(BytesIO(content), name="invalid_updated_phone_no_test_file.xlsx") + + class TestIndividualXlsxUpdate(APITestCase): databases = "__all__" @@ -67,6 +75,12 @@ def setUpTestData(cls) -> None: xlsx_match_columns=["individual__full_name"], ) + cls.xlsx_update_invalid_phone_no_file = XlsxUpdateFile.objects.create( + file=invalid_phone_no_file(), + business_area=cls.business_area, + xlsx_match_columns=["individual__given_name"], + ) + household_data = { "registration_data_import": registration_data_import, "business_area": cls.business_area, @@ -192,3 +206,12 @@ def test_complex_update_individual(self) -> None: self.assertEqual(self.individuals[1].birth_date, datetime.date(1965, 8, 6)) self.assertEqual(self.individuals[2].birth_date, datetime.date(1965, 8, 7)) self.assertEqual(self.individuals[3].birth_date, datetime.date(1985, 8, 12)) + + def test_raise_error_when_invalid_phone_number(self) -> None: + with self.assertRaises(ValidationError) as context: + IndividualXlsxUpdate(self.xlsx_update_invalid_phone_no_file).update_individuals() + + self.assertEqual( + str({"phone_no": [f"Invalid phone number for individual {self.individuals[0]}."]}), + str(context.exception) + ) From 629a4914da0fe18eff1aaa889895fe9587ca39c9 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Sun, 6 Oct 2024 22:47:10 +0200 Subject: [PATCH 080/202] include file --- ...invalid_updated_test_file_wrong_phone_no.xlsx | Bin 0 -> 5706 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/unit/apps/household/test_file/invalid_updated_test_file_wrong_phone_no.xlsx diff --git a/tests/unit/apps/household/test_file/invalid_updated_test_file_wrong_phone_no.xlsx b/tests/unit/apps/household/test_file/invalid_updated_test_file_wrong_phone_no.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..c4658974b9cd525740400157482ee822e2cf1138 GIT binary patch literal 5706 zcmaJ_1z1yk`yNt?#3%vjZWx_{O1H8B(m8U}=tdYR4wVu~X^>6<0qG$~2uQ~O5kwsz zA?bhL>vz4M_`d$nuAQ^<oa?@?J@@(jp1Pxji$?_j04@WDYE_L3G7QS+uy=Bo0D!9i zDu5*%3iA>c`p+2$0K`7HcvL*;bnXTbM`J>WR)g~EBSln`Ec$YrL|X{AVVojyg6uC$ z4-Uj4Yw$JcU}_pae(W&T&y85?SD|LWu)U(PhAzi}qiLz*qrI2y<0rliL0tptaciB$ z<(k~<s$^`NOe}LakptA62_N2(6S70sn4Z++Z<06qgj_vMdOn8_W;Dw`T9!1)TBJOG zNP^QV%}yX7Qc0O6`JmryIXWm=Rj#(bb~q=l?2&}owb^rv*p^|_V3r_K+7C}?h9kzF zw^rKex7d4s%TXQdX&RF6q~G~|TRgP+^$OwaB;lP4y5O<Cr*6UePLBfskYjbRbGN+% zclYoTwsnU?h5TJ%|E7*BQpW{x6R3<iCrWQ^bYsCY-cJQzyQE=Nr8GBYokdn8M;8{# zRr^#6snQlW5itMRBou77YF;(la+gzPFUixZN?|eQ1-ArgFP&11!aJ9}`+2l0q8J80 z+Q5cb1ZlV@>x<@L#NdbGL_;m4XLRmPkwXM{Tt0$xEa@6aM;9N1s*jPbKTo%jnO`U3 zCciPfYxKR~Y0d4i-HymM8PvnKw+fR#?Nxgn-Mu<BI5-#DE{ar<<G@$i!M*7GAu?Gw z0qgt}_6z*gd20`kKlz?D{FbN-1jJho%3)huED8kjo;6dAFqIuXfz5``kPMi7nxiKV z1|)Wan8!(5!(vHn;D{`FNCQ>)vxM%#q^3>%><^<fUnKOyQKU~$xY}7}=}P&Kcl_UF z44V7q@5P6%Gpy#JOp2{J&i2B~kv<{%)nd6g;Am!^4x752juO_hqqMK%{C0Z#z4RBo zAz0IWuz~d?0Q)M*u-^E?g#Fy%E;jD&E`M_7q28cdBnbHGBY$}tcag#MyLf991|JqB z9cYC8sbTDMS2maYl$0lI_2lXjXVT$e4UsRcV%N;^^)iU@THd?BL-2E$-M1JH_RBJZ zr9*<kC|-TFA?E|}K8#APk#IDa|4z<0BFkK^sjVa&GIjR>;Zn9>3r9{Jj2oAfZ|8-h zV}gg#NunVGE*~R_O>MXN8$S4*G$|uX1h|};%N=5txK^LTx2&vqbx}0kKA0nmm@qV8 z;YJ`vy-j0&s7@Z&IBhra`)N>V7w-dVaozE#J!6tgD3kcJkz0reT7;^*L;43pA=0f$ z;fq4y44E=5VTCHi#)9iVh58u|ewFM02@P+tp#g}%1Q696n)sS%@;>&u5@j~Ghl6<4 zs14I_Y*Gw3aRaN{Tz*cN)00pymh;GP3*&hw=V@tT=S_p;T+qwuz2{=v?Lo~!5k77A z+E%QLk&iHxyy;i$UW*kwR15?#2vUn!KgyC^AFD;pZ`v&^`j(_Yk4nBl2_}-X8(fLk zT*3Ee87a=W<r-Zv9+V6HvG=PWj=Pe!-};P&lqg_cF)%9|nkPiZvO{n~S_NW%4bO?D zPneJ84(YvKCwI@&Ck3NpWa!#$7L(N0#QF!0Lj;X=L4mugpL)qIN+lF<n6ics02~_t z0F*za@^*x}LWTc2i~MO_eKB(d&q)K%7cJ2{hwnXMF$kv(JjWub?tM{2#!0y}{4?92 zg2waLRUIj(NJdTG`nL3_^y()}7|j=I{4OczvzWPLV<`xVW7TUeM5<QsO*brKdtv_k z#QDhAIZQg>TQ<Xsr$rN&#MAoi-1;>?^ds4oO1O5-Q^BOJce(WQa(R4*4IfqIazcmw z#h*#@YP>X85YFBWzNVs~K2qnAqhCqH86ldNTP&&zcQVZ^@OG=R)l8|^E7J|NgDA54 zsGxde#cyD&o2Dwy*?Zl*`Vc-!)ALN}%yTjUm<d49@yo&^UQbS|pc`M&*VK-i-b)GX z?R(xY4s!`1xY-3%9c%iMSlszexJK{}>w!<0+4pQ3?`^>|i!fMmu|s}kXjSLQjaMIq zi+q+$b8iz91WU;<H!}v0E_0vl=<1G)a528Z=@mdvMT*@U(I$x#J#YfHn>8+zym1mK z_RoHyB(FJ;rY(SD#YM9*An21e9*YP+QJx_&&`>WQ@z!f#gli8-0iFj_Ez4J@bDe(l z%dOe0U$D(gVcIiD1~nS`C0MABo2J*;wIVdZAhJntQa?-Ty6np=7S%@OqH_(%E2IXg zy_u~wx7MVb(}#4t+tY+T8L^K&3@SUFH*uepDAu`ZV0_g}CZE33Z6paW=>$IvP*b!Y z5)9X?Unl5(AE8zuBk0~$TOGSdF+cDU<T~&O)G`?=Oa&R=jR&tGO}wa*1Q|=*9*!`M zEPw26qYF0Tw&)wk&rZrZceZ(DTH8MS#TY~i<L5Y<N3uwUKV8q&+s$$%Z;ZU52<ZiM zv|VFP@RuCo^IK!y4eVDIDv;$C70MRXnIu4EoG0R1o}i%w%koV$ixX#2Z5~Xzf)ClQ zm*Gn2kk1lvMpg)u`9=pe&IpFB=3*3G#w4RpF;^HuK~&QAb<A5`;0U9^d4K<&RTh8e zr*Goo1<!B_^psJKZ4H*tG|-75BfFqP(~|DhyAvt&8{g_Bq$@r-d|??M+rqR8Is_4B z-nia|tc%34Bus3b-4woCyie+&*=B3dcyy_81Wn>O5`R5isT63K*YSpoDO<LhZMtPO z->v%0aFmDa))M)>{j249T{apfaGQy^<1akLF>9_`3m=vxXAayr#ib+;1=3>%@dV6) zrH0DDx8RTx!>~@Z9#L9|p=Kks!%Aq0#VXp2xT102V54&|>`?j6oK2WVx2KjtL<vHN zb2}t3Rm7Gy(6$FYP7$BZ7K@0<4Rh;0Zx~UjePGb$z1wR69V{T;>is^HjN&?QcdC!L zTdZtG>%(GpofU2RG`tHjL!9o4^F1E+{t&oqwj?ZP!*X0-4FX+~wDvzn!8`SO8=R16 zY94g!WOj_tq1DM?cU}&f?{gc8ta*=9Z;wZzU4TG2YRt`C`4hR>Ov?=JNTplWZHuL; zJcYA~s1dqiRfrvUj6mMw%GX&cN)?W%Lpf)n=vf9PoGAl5zH`3EX5fWJNs5VZTywF+ zlTVMgLY@`Oq^)RFig5`v7w25J+|<k7<Q!e!L*vg31bSO7_*73#et5*3Ek?t}3S0J{ zDZ^y)neOijF*<wjkr}R(*{zXyeKRV0{y>}|7rp0~Z=J@*DWZu?c+8G^%!*8)NBX14 z*HA#USJCVWa6B2&9`X*fdN{)^B(W!lqqOKPB#T<Kw5XFb9PyWJtptstl8IoTTS3*M zQN%xg2~@2aniv^gT1^wLee%V1b9p5jZEbE{9)l+}<<o})Ror^fc^n<SLuiEGK)Fq@ zJVp<=_k6Lu1%Eq|-Oq3VFIh0dcaUuH^0xNUW#x`jRe;jlAD{`VMkd~fnZUKn_FIqH zpH`?Lu?Cq71{O7tX(Gc>R^7hO6N?1}e$%Df4HRD#LMNFzoD(e@`m1JF*AW9jYnQn& zScX^qYd7F{NrxzM?L~GqP(UI>=l=|fQUR9hpR>*tKb4-Rj|^n|09;HI7QJG|p4faE zk4+Y||0_Pc9HCHeFX6u~f6NvSAxq#)5HR$x3bnlq){_1jV<^KAG(4$Wv3Solhh0ET zl|}qY%o(QD7`QX*xpto;!@c9oH%Z6)0CEjRIIC_T*cQj^Ph6&4SUW4!cG9}vZ;KI7 zh&3r>)*-c`TjBT0OS5ou4e4sufb&G{7?4nZg=(gCt?0WqzY$G;9i{n<f^V>!<(>yg zR5k73TCJo-{{fQ_*agU|!SeuFMpe7J5(}eeRBM>-BC9^!SFO{Ym20?#yS}y~y@Jb^ z!qW#G6t~mD1jB2I7$X3Rob~qN4ni2hLZ*ckmzKS1c`l<bG=k&&#`jsZh|(R2wi5Ua zc+U9NpTc#nJb$%_JJw<6?B)IR=-d4b!-h1Lre>9sdBv`W1tNwikz=-HHTZoU5sXxq zx#?`_Mdj$r)FIXzBbvsK-FdSeVXdR+`5Xov1#6oFy<7XAx^HN08O#2tJ|wd|b-;LM z9aN6fOpQX%gg(yHeeK=zmjrr4gC2zY9f6O85!9Xq27?>1w3F~p!slSCgwHoW#&$_q zKvwmU`Q{AD!5<xidOn7cCr!mp(u!~@OS4Fq(Q!nz6-ezN*emHgbq@Cu$h~e49bhk4 zZx0>rZQpG+GYX*z<mc}uLbYra$2@Q}&{>VF<$1}^FUe<>`<hH7Nt;)${QGF3&chpJ z4}ER~E+mtUGG5vLvXMO=D6MBtaf_RA*~gu@{E*I!#xX9Dq)kMt%5c`1wW8l_nqH?x zf6~xT1|s$Svy4jCqQqtbdrHyS-ks$A&G_?}4tl7b@bl^WDREX)qpxF3rzN9C7nnM^ zSV@*H9XqzieZOkkW7)sbU3Z=N6#wGVjn=n~8^)G{D%iT2>gUq+a<qm+?ZDn}CpQPL zKNUGqk3@94Zvs_LGHn{DsRm|MYc89($Fn#Sl~izMPv`qPWM$Q`5-!^_5QACMBX)lr zhJO2U&c5dy(L-DM?ucugXoHr%pz&Rmu-;Zsn;-w6fHTLd1dmvDjY6%8bxzMhxyR^E zS_NOAy!#43JPEAbImiHZ@mF7_{|eyPD?-)2!%Hm4f_fV==tojY=3};6dasmlF~7d< z`TmqPtRjhQ+VaO_v=GGvRoZY}n)eOix&Eq=CaR^Q1TA>oTVKDXGJ##6_66#W>7kC7 zlTsHoQPJImxMH_6hy(y2`L8D40Wjzv#h5fQch3M(g<z_xPurgb`+UKJm>0l;N{bp& z4xE>73((QiacdrIeI&cgL8MuxYZ8$>-F}$8>g7HZg-FbJk}py6OoJRf<{C~O3$fRx zA`cBR4{qsC+|jwWB^Z;&8Lmpe^69d0LuETqzgUaMDTJRN*q5YbpzoSm#^O^LZr^~P zl9>>>j7(0x!VqI)Af#!Y52-{*wtm${ktx<*rf1ED35msSO969v`Z-87a1*=D;*rq! zHdfLtMMs@xGzrX~uNe{>M=WXOkEzfZr3fBMvLSiV&jd=)6ALD6CwQwLpKXob4XGQ4 zc)c;sn+EmQAYRq5A8}HaLKC}58ch<unK$%J)2~$LnM6`tt-Ja5)W<aa$yMHG-e&W0 z8@I>4ILdF~%v&Ak6ucS2aq(;KuHYq=S(^n=@u&Ju)Uv%VZ4h|NVO#$w0<JZ~cC5ap z3q|a|%PNa(k(cI`tejcS^q|>5WKz0zRYJbXpcQs=KJK3{r3cdnj5WZx)^#=Rf1}Yl zkXv#=W}H@w#QSQ#h<DF1f?je)&q5u{)f3$f*X=(CTP~z79LLq&P97yZh3G4lEqJkl z*c|C?m}wf9=wx$J2R)K9szNU*WLa?KRPiJoZZeoh4{vs7-w7-KY_n%Qng1g<Fj%mR zJ7hBH1>{EC8%ohu&}6};f17UmRKK60o#hnUDU8}HK{6m?Hs13s!t!ybl0@cA(N^K` z3nv?kH;2BP7C@5R0D`v}g?toJD)tm;d9ryZP7G5R;~}wi#OqLGP`SHf4lBMAtqb!m zu~Yt6+4c07gDJ<kCIuC2o98DzQeik3*XsOy{I_K6-hD~%vqv#ia(DBFx_Mhd0z9Bz z=6~A06)A&m*hXIE@GPEZj!(q<t^swTic^}P{wCp5|0lF7F)+%mB|iqDvPYCRt2L78 za)KQCbVZ*@=EXaMQu$?&F`F#NoA&XZ#tExAy=uD@dfa6By}FSPJu2!~UwZ3yP}tN_ z;5C%KV-ZQ>XD{hi0|RyHBm~3dNeD(b!0+yR$9=oLV<#S&>BdLXCPlk=!p5ZLe&Vy} z%mA+7&%PVuv{J``qAP)s-P7{bH{Uffj<;*=OEsJ8cZ*4hUg^1ib!k?UhA}qPx~spt zM}LC_y=tAe9Pns+2ZWZrJf-27dN}mnp*!=A4_S;cDWly&5b>qf(%npl*N{f?*aFlD z@xt!#EvXb&H+c0d0NRM>k*7J=+H~9{h4LU6%e;Q5f&YoZZe7*s!N;YWvu1O%=-fcr zF&?}=Pidw}3$^6-#q9#{HrV#H(4HG#t$}?T#5ttS#|l-NYnHv!#3}K*J*)Gq$xnDC z=b%}3zA~bfW;8zA)A#$W=rbSxx-fpP&eSGqlXr{JeqWDE!b&3R74rtVv3)wuB?WA_ z`S+<E)_mdkWrp`V@ZwnOR}?LFrTk;a^*j9HVCYvkH8v>z4gc?$=y&wRLCmk{RV@0q zvCQw_i^GXu!Nhoff&Vb7_?_os=l&~?6t+ae{)ylF_}>XGR&>7-SY7?wi+|L2zq4FS zuD`O-lm5l>k5v0R!^KqeD}ylEPlmq})9=uWk>OY99612+FLC0I7CttJ002bTPdN5n K)l>fG?*9OB!<U5s literal 0 HcmV?d00001 From 8840882e6016d1e9967e14f28e68102c47cf139f Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Mon, 7 Oct 2024 00:18:28 +0200 Subject: [PATCH 081/202] linter --- tests/unit/apps/household/test_individual_xlsx_update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/apps/household/test_individual_xlsx_update.py b/tests/unit/apps/household/test_individual_xlsx_update.py index cdc9f2561c..be12933fab 100644 --- a/tests/unit/apps/household/test_individual_xlsx_update.py +++ b/tests/unit/apps/household/test_individual_xlsx_update.py @@ -213,5 +213,5 @@ def test_raise_error_when_invalid_phone_number(self) -> None: self.assertEqual( str({"phone_no": [f"Invalid phone number for individual {self.individuals[0]}."]}), - str(context.exception) + str(context.exception), ) From 52696360afb62e439b84558317853c339d27abaa Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Mon, 7 Oct 2024 11:35:09 +0200 Subject: [PATCH 082/202] fix restgenerated import --- src/frontend/src/api/paymentModuleApi.ts | 2 +- src/frontend/src/api/periodicDataUpdateApi.ts | 10 +++++----- src/frontend/src/api/rdiApi.ts | 2 +- src/frontend/src/api/sharedApi.ts | 2 +- src/frontend/src/api/targetPopulationApi.ts | 2 +- .../PeriodicDataUpdatesTemplateDetailsDialog.tsx | 2 +- .../PeriodicDataUpdatesTemplatesList.tsx | 2 +- .../PeriodicDataUpdatesUpdatesList.tsx | 2 +- src/frontend/tsconfig.json | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/frontend/src/api/paymentModuleApi.ts b/src/frontend/src/api/paymentModuleApi.ts index 4dd3166d31..9378371140 100644 --- a/src/frontend/src/api/paymentModuleApi.ts +++ b/src/frontend/src/api/paymentModuleApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { PaymentPlan } from '@restgenerated/models/PaymentPlan'; +import { PaymentPlan } from 'restgenerated/models/PaymentPlan'; export const fetchPaymentPlansManagerial = async ( businessAreaSlug, diff --git a/src/frontend/src/api/periodicDataUpdateApi.ts b/src/frontend/src/api/periodicDataUpdateApi.ts index 0048553b7e..d38d465c4b 100644 --- a/src/frontend/src/api/periodicDataUpdateApi.ts +++ b/src/frontend/src/api/periodicDataUpdateApi.ts @@ -1,9 +1,9 @@ import { api } from './api'; -import { PaginatedPeriodicDataUpdateTemplateListList } from '@restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList'; -import { PeriodicDataUpdateUploadList } from '@restgenerated/models/PeriodicDataUpdateUploadList'; -import { PeriodicDataUpdateTemplateDetail } from '@restgenerated/models/PeriodicDataUpdateTemplateDetail'; -import { PeriodicDataUpdateUploadDetail } from '@restgenerated/models/PeriodicDataUpdateUploadDetail'; -import { PaginatedPeriodicFieldList } from '@restgenerated/models/PaginatedPeriodicFieldList'; +import { PaginatedPeriodicDataUpdateTemplateListList } from 'restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList'; +import { PeriodicDataUpdateUploadList } from 'restgenerated/models/PeriodicDataUpdateUploadList'; +import { PeriodicDataUpdateTemplateDetail } from 'restgenerated/models/PeriodicDataUpdateTemplateDetail'; +import { PeriodicDataUpdateUploadDetail } from 'restgenerated/models/PeriodicDataUpdateUploadDetail'; +import { PaginatedPeriodicFieldList } from 'restgenerated/models/PaginatedPeriodicFieldList'; export const fetchPeriodicDataUpdateTemplates = async ( businessAreaSlug: string, diff --git a/src/frontend/src/api/rdiApi.ts b/src/frontend/src/api/rdiApi.ts index e996d45eaf..c213709e54 100644 --- a/src/frontend/src/api/rdiApi.ts +++ b/src/frontend/src/api/rdiApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { RegistrationDataImportList } from '@restgenerated/models/RegistrationDataImportList'; +import { RegistrationDataImportList } from 'restgenerated/models/RegistrationDataImportList'; export const fetchRegistrationDataImports = async ( businessAreaSlug: string, diff --git a/src/frontend/src/api/sharedApi.ts b/src/frontend/src/api/sharedApi.ts index 0830591ec2..254ea1b5a2 100644 --- a/src/frontend/src/api/sharedApi.ts +++ b/src/frontend/src/api/sharedApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { PaginatedAreaList } from '@restgenerated/models/PaginatedAreaList'; +import { PaginatedAreaList } from 'restgenerated/models/PaginatedAreaList'; export const fetchAreas = async ( businessArea: string, diff --git a/src/frontend/src/api/targetPopulationApi.ts b/src/frontend/src/api/targetPopulationApi.ts index 6cde51e809..eb4757981c 100644 --- a/src/frontend/src/api/targetPopulationApi.ts +++ b/src/frontend/src/api/targetPopulationApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { TargetPopulationList } from '@restgenerated/models/TargetPopulationList'; +import { TargetPopulationList } from 'restgenerated/models/TargetPopulationList'; export const fetchTargetPopulations = async ( businessAreaSlug: string, diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx index 615f8eeec3..dd9bb0eb5e 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx @@ -18,7 +18,7 @@ import { TableHead, TableRow, } from '@mui/material'; -import { PeriodicDataUpdateTemplateList } from '@restgenerated/models/PeriodicDataUpdateTemplateList'; +import { PeriodicDataUpdateTemplateList } from 'restgenerated/models/PeriodicDataUpdateTemplateList'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx index 6c565e16be..0b355658f5 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx @@ -22,7 +22,7 @@ import { useTranslation } from 'react-i18next'; import { ButtonTooltip } from '@components/core/ButtonTooltip'; import { usePermissions } from '@hooks/usePermissions'; import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; -import { PeriodicDataUpdateTemplateList } from '@restgenerated/models/PeriodicDataUpdateTemplateList'; +import { PeriodicDataUpdateTemplateList } from 'restgenerated/models/PeriodicDataUpdateTemplateList'; const templatesHeadCells: HeadCell<PeriodicDataUpdateTemplateList>[] = [ { diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx index 54616f7ff0..2547a1802f 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx @@ -11,7 +11,7 @@ import { fetchPeriodicDataUpdateUpdates } from '@api/periodicDataUpdateApi'; import { useBaseUrl } from '@hooks/useBaseUrl'; import VisibilityIcon from '@mui/icons-material/Visibility'; import { PeriodicDataUpdatesUploadDetailsDialog } from '@components/periodicDataUpdates/PeriodicDataUpdatesUploadDetailsDialog'; -import { PeriodicDataUpdateUploadList } from '@restgenerated/models/PeriodicDataUpdateUploadList'; +import { PeriodicDataUpdateUploadList } from 'restgenerated/models/PeriodicDataUpdateUploadList'; const updatesHeadCells: HeadCell<PeriodicDataUpdateUploadList>[] = [ { diff --git a/src/frontend/tsconfig.json b/src/frontend/tsconfig.json index 75bf6186bf..ca28961451 100644 --- a/src/frontend/tsconfig.json +++ b/src/frontend/tsconfig.json @@ -31,7 +31,7 @@ "@utils/*": ["src/utils/*"], "@hooks/*": ["src/hooks/*"], "@generated/*": ["src/__generated__/*"], - "@restgenerated/*": ["../frontend/restgenerated/*"] + "restgenerated/*": ["../frontend/restgenerated/*"] } }, "include": [ From 25ec3f27e5d7cfa5ae9d3c07c8d5700e2ed719be Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Mon, 7 Oct 2024 12:45:32 +0200 Subject: [PATCH 083/202] remove unused imports --- .../SupportingDocumentsSection.tsx | 6 +----- .../SupportingDocumentsSectionActions.ts | 7 +------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx index 70f2afcbac..4221c5d931 100644 --- a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx @@ -24,11 +24,7 @@ import { useState } from 'react'; import { useMutation } from '@tanstack/react-query'; import { usePermissions } from '@hooks/usePermissions'; import { useTranslation } from 'react-i18next'; -import { - PaymentPlanQuery, - PaymentPlanStatus, - usePaymentPlanLazyQuery, -} from '@generated/graphql'; +import { PaymentPlanQuery, PaymentPlanStatus } from '@generated/graphql'; import { DropzoneField } from '@components/core/DropzoneField'; import { DialogTitleWrapper } from '@containers/dialogs/DialogTitleWrapper'; import { diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSectionActions.ts b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSectionActions.ts index b1d360393f..7ace3e3b1c 100644 --- a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSectionActions.ts +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSectionActions.ts @@ -1,10 +1,5 @@ -import { - fetchSupportingDocument, - uploadSupportingDocument, -} from '@api/paymentModuleApi'; +import { fetchSupportingDocument } from '@api/paymentModuleApi'; import { useMutation } from '@tanstack/react-query'; -import { t } from 'i18next'; -import { title } from 'process'; export const useDownloadSupportingDocument = () => { return useMutation({ From 534ec6685efd63fb0a9b8bf983f39d36dbb45d4a Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Mon, 7 Oct 2024 14:23:29 +0200 Subject: [PATCH 084/202] change imports --- src/frontend/src/api/paymentModuleApi.ts | 3 +-- src/frontend/src/api/periodicDataUpdateApi.ts | 10 +++++----- src/frontend/src/api/rdiApi.ts | 2 +- src/frontend/src/api/sharedApi.ts | 2 +- src/frontend/src/api/targetPopulationApi.ts | 2 +- .../PeriodicDataUpdatesTemplateDetailsDialog.tsx | 2 +- .../PeriodicDataUpdatesTemplatesList.tsx | 2 +- .../PeriodicDataUpdatesUpdatesList.tsx | 2 +- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/frontend/src/api/paymentModuleApi.ts b/src/frontend/src/api/paymentModuleApi.ts index 9378371140..1052b126aa 100644 --- a/src/frontend/src/api/paymentModuleApi.ts +++ b/src/frontend/src/api/paymentModuleApi.ts @@ -1,6 +1,5 @@ import { api } from './api'; -import { PaymentPlan } from 'restgenerated/models/PaymentPlan'; - +import { PaymentPlan } from '../../../frontend/restgenerated/models/PaymentPlan'; export const fetchPaymentPlansManagerial = async ( businessAreaSlug, params = {}, diff --git a/src/frontend/src/api/periodicDataUpdateApi.ts b/src/frontend/src/api/periodicDataUpdateApi.ts index d38d465c4b..c67e8d6eb6 100644 --- a/src/frontend/src/api/periodicDataUpdateApi.ts +++ b/src/frontend/src/api/periodicDataUpdateApi.ts @@ -1,9 +1,9 @@ import { api } from './api'; -import { PaginatedPeriodicDataUpdateTemplateListList } from 'restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList'; -import { PeriodicDataUpdateUploadList } from 'restgenerated/models/PeriodicDataUpdateUploadList'; -import { PeriodicDataUpdateTemplateDetail } from 'restgenerated/models/PeriodicDataUpdateTemplateDetail'; -import { PeriodicDataUpdateUploadDetail } from 'restgenerated/models/PeriodicDataUpdateUploadDetail'; -import { PaginatedPeriodicFieldList } from 'restgenerated/models/PaginatedPeriodicFieldList'; +import { PaginatedPeriodicDataUpdateTemplateListList } from '../../../frontend/restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList'; +import { PeriodicDataUpdateUploadList } from '../../../frontend/restgenerated/models/PeriodicDataUpdateUploadList'; +import { PeriodicDataUpdateTemplateDetail } from '../../../frontend/restgenerated/models/PeriodicDataUpdateTemplateDetail'; +import { PeriodicDataUpdateUploadDetail } from '../../../frontend/restgenerated/models/PeriodicDataUpdateUploadDetail'; +import { PaginatedPeriodicFieldList } from '../../../frontend/restgenerated/models/PaginatedPeriodicFieldList'; export const fetchPeriodicDataUpdateTemplates = async ( businessAreaSlug: string, diff --git a/src/frontend/src/api/rdiApi.ts b/src/frontend/src/api/rdiApi.ts index c213709e54..632a07ce5e 100644 --- a/src/frontend/src/api/rdiApi.ts +++ b/src/frontend/src/api/rdiApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { RegistrationDataImportList } from 'restgenerated/models/RegistrationDataImportList'; +import { RegistrationDataImportList } from '../../../frontend/restgenerated/models/RegistrationDataImportList'; export const fetchRegistrationDataImports = async ( businessAreaSlug: string, diff --git a/src/frontend/src/api/sharedApi.ts b/src/frontend/src/api/sharedApi.ts index 254ea1b5a2..a6c0b5d356 100644 --- a/src/frontend/src/api/sharedApi.ts +++ b/src/frontend/src/api/sharedApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { PaginatedAreaList } from 'restgenerated/models/PaginatedAreaList'; +import { PaginatedAreaList } from '../../../frontend/restgenerated/models/PaginatedAreaList'; export const fetchAreas = async ( businessArea: string, diff --git a/src/frontend/src/api/targetPopulationApi.ts b/src/frontend/src/api/targetPopulationApi.ts index eb4757981c..8aebcb0438 100644 --- a/src/frontend/src/api/targetPopulationApi.ts +++ b/src/frontend/src/api/targetPopulationApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { TargetPopulationList } from 'restgenerated/models/TargetPopulationList'; +import { TargetPopulationList } from '../../../frontend/restgenerated/models/TargetPopulationList'; export const fetchTargetPopulations = async ( businessAreaSlug: string, diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx index dd9bb0eb5e..263e16bf4f 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx @@ -18,7 +18,7 @@ import { TableHead, TableRow, } from '@mui/material'; -import { PeriodicDataUpdateTemplateList } from 'restgenerated/models/PeriodicDataUpdateTemplateList'; +import { PeriodicDataUpdateTemplateList } from '../../../frontend/restgenerated/models/PeriodicDataUpdateTemplateList'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx index 0b355658f5..117931fb2f 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx @@ -22,7 +22,7 @@ import { useTranslation } from 'react-i18next'; import { ButtonTooltip } from '@components/core/ButtonTooltip'; import { usePermissions } from '@hooks/usePermissions'; import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; -import { PeriodicDataUpdateTemplateList } from 'restgenerated/models/PeriodicDataUpdateTemplateList'; +import { PeriodicDataUpdateTemplateList } from '../../../frontend/restgenerated/models/PeriodicDataUpdateTemplateList'; const templatesHeadCells: HeadCell<PeriodicDataUpdateTemplateList>[] = [ { diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx index 2547a1802f..0155e96cc9 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx @@ -11,7 +11,7 @@ import { fetchPeriodicDataUpdateUpdates } from '@api/periodicDataUpdateApi'; import { useBaseUrl } from '@hooks/useBaseUrl'; import VisibilityIcon from '@mui/icons-material/Visibility'; import { PeriodicDataUpdatesUploadDetailsDialog } from '@components/periodicDataUpdates/PeriodicDataUpdatesUploadDetailsDialog'; -import { PeriodicDataUpdateUploadList } from 'restgenerated/models/PeriodicDataUpdateUploadList'; +import { PeriodicDataUpdateUploadList } from '../../../frontend/restgenerated/models/PeriodicDataUpdateUploadList'; const updatesHeadCells: HeadCell<PeriodicDataUpdateUploadList>[] = [ { From 5b905242d5386bca0a6d38fcb39fb7a6a621fd5d Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Mon, 7 Oct 2024 14:30:08 +0200 Subject: [PATCH 085/202] Revert "change imports" This reverts commit 534ec6685efd63fb0a9b8bf983f39d36dbb45d4a. --- src/frontend/src/api/paymentModuleApi.ts | 3 ++- src/frontend/src/api/periodicDataUpdateApi.ts | 10 +++++----- src/frontend/src/api/rdiApi.ts | 2 +- src/frontend/src/api/sharedApi.ts | 2 +- src/frontend/src/api/targetPopulationApi.ts | 2 +- .../PeriodicDataUpdatesTemplateDetailsDialog.tsx | 2 +- .../PeriodicDataUpdatesTemplatesList.tsx | 2 +- .../PeriodicDataUpdatesUpdatesList.tsx | 2 +- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/frontend/src/api/paymentModuleApi.ts b/src/frontend/src/api/paymentModuleApi.ts index 1052b126aa..9378371140 100644 --- a/src/frontend/src/api/paymentModuleApi.ts +++ b/src/frontend/src/api/paymentModuleApi.ts @@ -1,5 +1,6 @@ import { api } from './api'; -import { PaymentPlan } from '../../../frontend/restgenerated/models/PaymentPlan'; +import { PaymentPlan } from 'restgenerated/models/PaymentPlan'; + export const fetchPaymentPlansManagerial = async ( businessAreaSlug, params = {}, diff --git a/src/frontend/src/api/periodicDataUpdateApi.ts b/src/frontend/src/api/periodicDataUpdateApi.ts index c67e8d6eb6..d38d465c4b 100644 --- a/src/frontend/src/api/periodicDataUpdateApi.ts +++ b/src/frontend/src/api/periodicDataUpdateApi.ts @@ -1,9 +1,9 @@ import { api } from './api'; -import { PaginatedPeriodicDataUpdateTemplateListList } from '../../../frontend/restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList'; -import { PeriodicDataUpdateUploadList } from '../../../frontend/restgenerated/models/PeriodicDataUpdateUploadList'; -import { PeriodicDataUpdateTemplateDetail } from '../../../frontend/restgenerated/models/PeriodicDataUpdateTemplateDetail'; -import { PeriodicDataUpdateUploadDetail } from '../../../frontend/restgenerated/models/PeriodicDataUpdateUploadDetail'; -import { PaginatedPeriodicFieldList } from '../../../frontend/restgenerated/models/PaginatedPeriodicFieldList'; +import { PaginatedPeriodicDataUpdateTemplateListList } from 'restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList'; +import { PeriodicDataUpdateUploadList } from 'restgenerated/models/PeriodicDataUpdateUploadList'; +import { PeriodicDataUpdateTemplateDetail } from 'restgenerated/models/PeriodicDataUpdateTemplateDetail'; +import { PeriodicDataUpdateUploadDetail } from 'restgenerated/models/PeriodicDataUpdateUploadDetail'; +import { PaginatedPeriodicFieldList } from 'restgenerated/models/PaginatedPeriodicFieldList'; export const fetchPeriodicDataUpdateTemplates = async ( businessAreaSlug: string, diff --git a/src/frontend/src/api/rdiApi.ts b/src/frontend/src/api/rdiApi.ts index 632a07ce5e..c213709e54 100644 --- a/src/frontend/src/api/rdiApi.ts +++ b/src/frontend/src/api/rdiApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { RegistrationDataImportList } from '../../../frontend/restgenerated/models/RegistrationDataImportList'; +import { RegistrationDataImportList } from 'restgenerated/models/RegistrationDataImportList'; export const fetchRegistrationDataImports = async ( businessAreaSlug: string, diff --git a/src/frontend/src/api/sharedApi.ts b/src/frontend/src/api/sharedApi.ts index a6c0b5d356..254ea1b5a2 100644 --- a/src/frontend/src/api/sharedApi.ts +++ b/src/frontend/src/api/sharedApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { PaginatedAreaList } from '../../../frontend/restgenerated/models/PaginatedAreaList'; +import { PaginatedAreaList } from 'restgenerated/models/PaginatedAreaList'; export const fetchAreas = async ( businessArea: string, diff --git a/src/frontend/src/api/targetPopulationApi.ts b/src/frontend/src/api/targetPopulationApi.ts index 8aebcb0438..eb4757981c 100644 --- a/src/frontend/src/api/targetPopulationApi.ts +++ b/src/frontend/src/api/targetPopulationApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { TargetPopulationList } from '../../../frontend/restgenerated/models/TargetPopulationList'; +import { TargetPopulationList } from 'restgenerated/models/TargetPopulationList'; export const fetchTargetPopulations = async ( businessAreaSlug: string, diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx index 263e16bf4f..dd9bb0eb5e 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx @@ -18,7 +18,7 @@ import { TableHead, TableRow, } from '@mui/material'; -import { PeriodicDataUpdateTemplateList } from '../../../frontend/restgenerated/models/PeriodicDataUpdateTemplateList'; +import { PeriodicDataUpdateTemplateList } from 'restgenerated/models/PeriodicDataUpdateTemplateList'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx index 117931fb2f..0b355658f5 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx @@ -22,7 +22,7 @@ import { useTranslation } from 'react-i18next'; import { ButtonTooltip } from '@components/core/ButtonTooltip'; import { usePermissions } from '@hooks/usePermissions'; import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; -import { PeriodicDataUpdateTemplateList } from '../../../frontend/restgenerated/models/PeriodicDataUpdateTemplateList'; +import { PeriodicDataUpdateTemplateList } from 'restgenerated/models/PeriodicDataUpdateTemplateList'; const templatesHeadCells: HeadCell<PeriodicDataUpdateTemplateList>[] = [ { diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx index 0155e96cc9..2547a1802f 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx @@ -11,7 +11,7 @@ import { fetchPeriodicDataUpdateUpdates } from '@api/periodicDataUpdateApi'; import { useBaseUrl } from '@hooks/useBaseUrl'; import VisibilityIcon from '@mui/icons-material/Visibility'; import { PeriodicDataUpdatesUploadDetailsDialog } from '@components/periodicDataUpdates/PeriodicDataUpdatesUploadDetailsDialog'; -import { PeriodicDataUpdateUploadList } from '../../../frontend/restgenerated/models/PeriodicDataUpdateUploadList'; +import { PeriodicDataUpdateUploadList } from 'restgenerated/models/PeriodicDataUpdateUploadList'; const updatesHeadCells: HeadCell<PeriodicDataUpdateUploadList>[] = [ { From 7573abc10b390e627a0f377f6604fc46f4774346 Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Mon, 7 Oct 2024 14:46:14 +0200 Subject: [PATCH 086/202] move folder to src --- src/frontend/src/api/paymentModuleApi.ts | 2 +- src/frontend/src/api/periodicDataUpdateApi.ts | 10 +++++----- src/frontend/src/api/rdiApi.ts | 2 +- src/frontend/src/api/sharedApi.ts | 2 +- src/frontend/src/api/targetPopulationApi.ts | 2 +- .../PeriodicDataUpdatesTemplateDetailsDialog.tsx | 2 +- .../PeriodicDataUpdatesTemplatesList.tsx | 2 +- .../PeriodicDataUpdatesUpdatesList.tsx | 2 +- src/frontend/{ => src}/restgenerated/core/ApiError.ts | 0 .../{ => src}/restgenerated/core/ApiRequestOptions.ts | 0 src/frontend/{ => src}/restgenerated/core/ApiResult.ts | 0 .../{ => src}/restgenerated/core/CancelablePromise.ts | 0 src/frontend/{ => src}/restgenerated/core/OpenAPI.ts | 0 src/frontend/{ => src}/restgenerated/core/request.ts | 0 src/frontend/{ => src}/restgenerated/index.ts | 0 .../{ => src}/restgenerated/models/ActionEnum.ts | 0 .../{ => src}/restgenerated/models/Admin1Enum.ts | 0 .../{ => src}/restgenerated/models/Admin2Enum.ts | 0 .../{ => src}/restgenerated/models/Admin3Enum.ts | 0 .../{ => src}/restgenerated/models/Admin4Enum.ts | 0 src/frontend/{ => src}/restgenerated/models/Area.ts | 0 .../{ => src}/restgenerated/models/AreaList.ts | 0 .../{ => src}/restgenerated/models/AreaType.ts | 0 .../{ => src}/restgenerated/models/BlankEnum.ts | 0 .../{ => src}/restgenerated/models/BusinessArea.ts | 0 .../restgenerated/models/CollectIndividualDataEnum.ts | 0 .../{ => src}/restgenerated/models/CollectTypeEnum.ts | 0 .../restgenerated/models/CommsDisabilityEnum.ts | 0 .../restgenerated/models/ConsentSharingEnum.ts | 0 .../{ => src}/restgenerated/models/CountryEnum.ts | 0 .../restgenerated/models/CountryOriginEnum.ts | 0 .../{ => src}/restgenerated/models/CurrencyEnum.ts | 0 .../models/DeduplicationGoldenRecordStatusEnum.ts | 0 .../{ => src}/restgenerated/models/Delegate.ts | 0 .../{ => src}/restgenerated/models/DelegatePeople.ts | 0 .../{ => src}/restgenerated/models/DisabilityEnum.ts | 0 .../{ => src}/restgenerated/models/Document.ts | 0 .../restgenerated/models/DocumentStatusEnum.ts | 0 .../{ => src}/restgenerated/models/DocumentTypeEnum.ts | 0 .../restgenerated/models/FollowUpPaymentPlan.ts | 0 .../restgenerated/models/FrequencyOfPaymentsEnum.ts | 0 .../restgenerated/models/HearingDisabilityEnum.ts | 0 .../{ => src}/restgenerated/models/Household.ts | 0 .../{ => src}/restgenerated/models/Individual.ts | 0 .../restgenerated/models/MemoryDisabilityEnum.ts | 0 .../{ => src}/restgenerated/models/NullEnum.ts | 0 .../restgenerated/models/OrgEnumeratorEnum.ts | 0 .../restgenerated/models/PaginatedAreaList.ts | 0 .../restgenerated/models/PaginatedAreaListList.ts | 0 .../restgenerated/models/PaginatedAreaTypeList.ts | 0 .../restgenerated/models/PaginatedBusinessAreaList.ts | 0 .../restgenerated/models/PaginatedPaymentPlanList.ts | 0 .../PaginatedPeriodicDataUpdateTemplateListList.ts | 0 .../PaginatedPeriodicDataUpdateUploadListList.ts | 0 .../restgenerated/models/PaginatedPeriodicFieldList.ts | 0 .../models/PaginatedProgramCycleListList.ts | 0 .../restgenerated/models/PaginatedProgramGlobalList.ts | 0 .../models/PaginatedRegistrationDataImportListList.ts | 0 .../models/PaginatedTargetPopulationListList.ts | 0 .../restgenerated/models/PatchedProgramCycleUpdate.ts | 0 .../{ => src}/restgenerated/models/PatchedRDI.ts | 0 .../{ => src}/restgenerated/models/PaymentPlan.ts | 0 .../restgenerated/models/PaymentPlanBulkAction.ts | 0 .../models/PeriodicDataUpdateTemplateCreate.ts | 0 .../models/PeriodicDataUpdateTemplateDetail.ts | 0 .../models/PeriodicDataUpdateTemplateList.ts | 0 .../restgenerated/models/PeriodicDataUpdateUpload.ts | 0 .../models/PeriodicDataUpdateUploadDetail.ts | 0 .../models/PeriodicDataUpdateUploadList.ts | 0 .../{ => src}/restgenerated/models/PeriodicField.ts | 0 .../restgenerated/models/PeriodicFieldData.ts | 0 .../restgenerated/models/PhysicalDisabilityEnum.ts | 0 .../restgenerated/models/PreferredLanguageEnum.ts | 0 src/frontend/{ => src}/restgenerated/models/Program.ts | 0 .../restgenerated/models/ProgramCycleCreate.ts | 0 .../{ => src}/restgenerated/models/ProgramCycleList.ts | 0 .../restgenerated/models/ProgramCycleUpdate.ts | 0 .../{ => src}/restgenerated/models/ProgramGlobal.ts | 0 .../restgenerated/models/ProgramGlobalStatusEnum.ts | 0 .../{ => src}/restgenerated/models/PushPeople.ts | 0 .../restgenerated/models/PushPeopleTypeEnum.ts | 0 src/frontend/{ => src}/restgenerated/models/RDI.ts | 0 .../{ => src}/restgenerated/models/RDINested.ts | 0 .../restgenerated/models/RdiMergeStatusEnum.ts | 0 .../restgenerated/models/RegistrationDataImportList.ts | 0 .../restgenerated/models/RegistrationMethodEnum.ts | 0 .../{ => src}/restgenerated/models/RelationshipEnum.ts | 0 .../restgenerated/models/ResidenceStatusEnum.ts | 0 .../{ => src}/restgenerated/models/ScopeEnum.ts | 0 .../{ => src}/restgenerated/models/SectorEnum.ts | 0 .../restgenerated/models/SeeingDisabilityEnum.ts | 0 .../restgenerated/models/SelfcareDisabilityEnum.ts | 0 src/frontend/{ => src}/restgenerated/models/SexEnum.ts | 0 .../{ => src}/restgenerated/models/SubtypeEnum.ts | 0 .../restgenerated/models/TargetPopulationList.ts | 0 .../{ => src}/restgenerated/models/WorkStatusEnum.ts | 0 .../restgenerated/services/FieldsAttributesService.ts | 0 .../restgenerated/services/HhStatusService.ts | 0 .../{ => src}/restgenerated/services/RestService.ts | 0 src/frontend/tsconfig.json | 2 +- 100 files changed, 13 insertions(+), 13 deletions(-) rename src/frontend/{ => src}/restgenerated/core/ApiError.ts (100%) rename src/frontend/{ => src}/restgenerated/core/ApiRequestOptions.ts (100%) rename src/frontend/{ => src}/restgenerated/core/ApiResult.ts (100%) rename src/frontend/{ => src}/restgenerated/core/CancelablePromise.ts (100%) rename src/frontend/{ => src}/restgenerated/core/OpenAPI.ts (100%) rename src/frontend/{ => src}/restgenerated/core/request.ts (100%) rename src/frontend/{ => src}/restgenerated/index.ts (100%) rename src/frontend/{ => src}/restgenerated/models/ActionEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Admin1Enum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Admin2Enum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Admin3Enum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Admin4Enum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Area.ts (100%) rename src/frontend/{ => src}/restgenerated/models/AreaList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/AreaType.ts (100%) rename src/frontend/{ => src}/restgenerated/models/BlankEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/BusinessArea.ts (100%) rename src/frontend/{ => src}/restgenerated/models/CollectIndividualDataEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/CollectTypeEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/CommsDisabilityEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/ConsentSharingEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/CountryEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/CountryOriginEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/CurrencyEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/DeduplicationGoldenRecordStatusEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Delegate.ts (100%) rename src/frontend/{ => src}/restgenerated/models/DelegatePeople.ts (100%) rename src/frontend/{ => src}/restgenerated/models/DisabilityEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Document.ts (100%) rename src/frontend/{ => src}/restgenerated/models/DocumentStatusEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/DocumentTypeEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/FollowUpPaymentPlan.ts (100%) rename src/frontend/{ => src}/restgenerated/models/FrequencyOfPaymentsEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/HearingDisabilityEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Household.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Individual.ts (100%) rename src/frontend/{ => src}/restgenerated/models/MemoryDisabilityEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/NullEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/OrgEnumeratorEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedAreaList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedAreaListList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedAreaTypeList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedBusinessAreaList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedPaymentPlanList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedPeriodicDataUpdateUploadListList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedPeriodicFieldList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedProgramCycleListList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedProgramGlobalList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedRegistrationDataImportListList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaginatedTargetPopulationListList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PatchedProgramCycleUpdate.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PatchedRDI.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaymentPlan.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PaymentPlanBulkAction.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PeriodicDataUpdateTemplateCreate.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PeriodicDataUpdateTemplateDetail.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PeriodicDataUpdateTemplateList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PeriodicDataUpdateUpload.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PeriodicDataUpdateUploadDetail.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PeriodicDataUpdateUploadList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PeriodicField.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PeriodicFieldData.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PhysicalDisabilityEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PreferredLanguageEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/Program.ts (100%) rename src/frontend/{ => src}/restgenerated/models/ProgramCycleCreate.ts (100%) rename src/frontend/{ => src}/restgenerated/models/ProgramCycleList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/ProgramCycleUpdate.ts (100%) rename src/frontend/{ => src}/restgenerated/models/ProgramGlobal.ts (100%) rename src/frontend/{ => src}/restgenerated/models/ProgramGlobalStatusEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PushPeople.ts (100%) rename src/frontend/{ => src}/restgenerated/models/PushPeopleTypeEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/RDI.ts (100%) rename src/frontend/{ => src}/restgenerated/models/RDINested.ts (100%) rename src/frontend/{ => src}/restgenerated/models/RdiMergeStatusEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/RegistrationDataImportList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/RegistrationMethodEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/RelationshipEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/ResidenceStatusEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/ScopeEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/SectorEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/SeeingDisabilityEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/SelfcareDisabilityEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/SexEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/SubtypeEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/models/TargetPopulationList.ts (100%) rename src/frontend/{ => src}/restgenerated/models/WorkStatusEnum.ts (100%) rename src/frontend/{ => src}/restgenerated/services/FieldsAttributesService.ts (100%) rename src/frontend/{ => src}/restgenerated/services/HhStatusService.ts (100%) rename src/frontend/{ => src}/restgenerated/services/RestService.ts (100%) diff --git a/src/frontend/src/api/paymentModuleApi.ts b/src/frontend/src/api/paymentModuleApi.ts index 9378371140..4dd3166d31 100644 --- a/src/frontend/src/api/paymentModuleApi.ts +++ b/src/frontend/src/api/paymentModuleApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { PaymentPlan } from 'restgenerated/models/PaymentPlan'; +import { PaymentPlan } from '@restgenerated/models/PaymentPlan'; export const fetchPaymentPlansManagerial = async ( businessAreaSlug, diff --git a/src/frontend/src/api/periodicDataUpdateApi.ts b/src/frontend/src/api/periodicDataUpdateApi.ts index d38d465c4b..0048553b7e 100644 --- a/src/frontend/src/api/periodicDataUpdateApi.ts +++ b/src/frontend/src/api/periodicDataUpdateApi.ts @@ -1,9 +1,9 @@ import { api } from './api'; -import { PaginatedPeriodicDataUpdateTemplateListList } from 'restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList'; -import { PeriodicDataUpdateUploadList } from 'restgenerated/models/PeriodicDataUpdateUploadList'; -import { PeriodicDataUpdateTemplateDetail } from 'restgenerated/models/PeriodicDataUpdateTemplateDetail'; -import { PeriodicDataUpdateUploadDetail } from 'restgenerated/models/PeriodicDataUpdateUploadDetail'; -import { PaginatedPeriodicFieldList } from 'restgenerated/models/PaginatedPeriodicFieldList'; +import { PaginatedPeriodicDataUpdateTemplateListList } from '@restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList'; +import { PeriodicDataUpdateUploadList } from '@restgenerated/models/PeriodicDataUpdateUploadList'; +import { PeriodicDataUpdateTemplateDetail } from '@restgenerated/models/PeriodicDataUpdateTemplateDetail'; +import { PeriodicDataUpdateUploadDetail } from '@restgenerated/models/PeriodicDataUpdateUploadDetail'; +import { PaginatedPeriodicFieldList } from '@restgenerated/models/PaginatedPeriodicFieldList'; export const fetchPeriodicDataUpdateTemplates = async ( businessAreaSlug: string, diff --git a/src/frontend/src/api/rdiApi.ts b/src/frontend/src/api/rdiApi.ts index c213709e54..e996d45eaf 100644 --- a/src/frontend/src/api/rdiApi.ts +++ b/src/frontend/src/api/rdiApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { RegistrationDataImportList } from 'restgenerated/models/RegistrationDataImportList'; +import { RegistrationDataImportList } from '@restgenerated/models/RegistrationDataImportList'; export const fetchRegistrationDataImports = async ( businessAreaSlug: string, diff --git a/src/frontend/src/api/sharedApi.ts b/src/frontend/src/api/sharedApi.ts index 254ea1b5a2..0830591ec2 100644 --- a/src/frontend/src/api/sharedApi.ts +++ b/src/frontend/src/api/sharedApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { PaginatedAreaList } from 'restgenerated/models/PaginatedAreaList'; +import { PaginatedAreaList } from '@restgenerated/models/PaginatedAreaList'; export const fetchAreas = async ( businessArea: string, diff --git a/src/frontend/src/api/targetPopulationApi.ts b/src/frontend/src/api/targetPopulationApi.ts index eb4757981c..6cde51e809 100644 --- a/src/frontend/src/api/targetPopulationApi.ts +++ b/src/frontend/src/api/targetPopulationApi.ts @@ -1,5 +1,5 @@ import { api } from './api'; -import { TargetPopulationList } from 'restgenerated/models/TargetPopulationList'; +import { TargetPopulationList } from '@restgenerated/models/TargetPopulationList'; export const fetchTargetPopulations = async ( businessAreaSlug: string, diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx index dd9bb0eb5e..615f8eeec3 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplateDetailsDialog.tsx @@ -18,7 +18,7 @@ import { TableHead, TableRow, } from '@mui/material'; -import { PeriodicDataUpdateTemplateList } from 'restgenerated/models/PeriodicDataUpdateTemplateList'; +import { PeriodicDataUpdateTemplateList } from '@restgenerated/models/PeriodicDataUpdateTemplateList'; import { useQuery } from '@tanstack/react-query'; import React from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx index 0b355658f5..6c565e16be 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesTemplatesList.tsx @@ -22,7 +22,7 @@ import { useTranslation } from 'react-i18next'; import { ButtonTooltip } from '@components/core/ButtonTooltip'; import { usePermissions } from '@hooks/usePermissions'; import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; -import { PeriodicDataUpdateTemplateList } from 'restgenerated/models/PeriodicDataUpdateTemplateList'; +import { PeriodicDataUpdateTemplateList } from '@restgenerated/models/PeriodicDataUpdateTemplateList'; const templatesHeadCells: HeadCell<PeriodicDataUpdateTemplateList>[] = [ { diff --git a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx index 2547a1802f..54616f7ff0 100644 --- a/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx +++ b/src/frontend/src/components/periodicDataUpdates/PeriodicDataUpdatesUpdatesList.tsx @@ -11,7 +11,7 @@ import { fetchPeriodicDataUpdateUpdates } from '@api/periodicDataUpdateApi'; import { useBaseUrl } from '@hooks/useBaseUrl'; import VisibilityIcon from '@mui/icons-material/Visibility'; import { PeriodicDataUpdatesUploadDetailsDialog } from '@components/periodicDataUpdates/PeriodicDataUpdatesUploadDetailsDialog'; -import { PeriodicDataUpdateUploadList } from 'restgenerated/models/PeriodicDataUpdateUploadList'; +import { PeriodicDataUpdateUploadList } from '@restgenerated/models/PeriodicDataUpdateUploadList'; const updatesHeadCells: HeadCell<PeriodicDataUpdateUploadList>[] = [ { diff --git a/src/frontend/restgenerated/core/ApiError.ts b/src/frontend/src/restgenerated/core/ApiError.ts similarity index 100% rename from src/frontend/restgenerated/core/ApiError.ts rename to src/frontend/src/restgenerated/core/ApiError.ts diff --git a/src/frontend/restgenerated/core/ApiRequestOptions.ts b/src/frontend/src/restgenerated/core/ApiRequestOptions.ts similarity index 100% rename from src/frontend/restgenerated/core/ApiRequestOptions.ts rename to src/frontend/src/restgenerated/core/ApiRequestOptions.ts diff --git a/src/frontend/restgenerated/core/ApiResult.ts b/src/frontend/src/restgenerated/core/ApiResult.ts similarity index 100% rename from src/frontend/restgenerated/core/ApiResult.ts rename to src/frontend/src/restgenerated/core/ApiResult.ts diff --git a/src/frontend/restgenerated/core/CancelablePromise.ts b/src/frontend/src/restgenerated/core/CancelablePromise.ts similarity index 100% rename from src/frontend/restgenerated/core/CancelablePromise.ts rename to src/frontend/src/restgenerated/core/CancelablePromise.ts diff --git a/src/frontend/restgenerated/core/OpenAPI.ts b/src/frontend/src/restgenerated/core/OpenAPI.ts similarity index 100% rename from src/frontend/restgenerated/core/OpenAPI.ts rename to src/frontend/src/restgenerated/core/OpenAPI.ts diff --git a/src/frontend/restgenerated/core/request.ts b/src/frontend/src/restgenerated/core/request.ts similarity index 100% rename from src/frontend/restgenerated/core/request.ts rename to src/frontend/src/restgenerated/core/request.ts diff --git a/src/frontend/restgenerated/index.ts b/src/frontend/src/restgenerated/index.ts similarity index 100% rename from src/frontend/restgenerated/index.ts rename to src/frontend/src/restgenerated/index.ts diff --git a/src/frontend/restgenerated/models/ActionEnum.ts b/src/frontend/src/restgenerated/models/ActionEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/ActionEnum.ts rename to src/frontend/src/restgenerated/models/ActionEnum.ts diff --git a/src/frontend/restgenerated/models/Admin1Enum.ts b/src/frontend/src/restgenerated/models/Admin1Enum.ts similarity index 100% rename from src/frontend/restgenerated/models/Admin1Enum.ts rename to src/frontend/src/restgenerated/models/Admin1Enum.ts diff --git a/src/frontend/restgenerated/models/Admin2Enum.ts b/src/frontend/src/restgenerated/models/Admin2Enum.ts similarity index 100% rename from src/frontend/restgenerated/models/Admin2Enum.ts rename to src/frontend/src/restgenerated/models/Admin2Enum.ts diff --git a/src/frontend/restgenerated/models/Admin3Enum.ts b/src/frontend/src/restgenerated/models/Admin3Enum.ts similarity index 100% rename from src/frontend/restgenerated/models/Admin3Enum.ts rename to src/frontend/src/restgenerated/models/Admin3Enum.ts diff --git a/src/frontend/restgenerated/models/Admin4Enum.ts b/src/frontend/src/restgenerated/models/Admin4Enum.ts similarity index 100% rename from src/frontend/restgenerated/models/Admin4Enum.ts rename to src/frontend/src/restgenerated/models/Admin4Enum.ts diff --git a/src/frontend/restgenerated/models/Area.ts b/src/frontend/src/restgenerated/models/Area.ts similarity index 100% rename from src/frontend/restgenerated/models/Area.ts rename to src/frontend/src/restgenerated/models/Area.ts diff --git a/src/frontend/restgenerated/models/AreaList.ts b/src/frontend/src/restgenerated/models/AreaList.ts similarity index 100% rename from src/frontend/restgenerated/models/AreaList.ts rename to src/frontend/src/restgenerated/models/AreaList.ts diff --git a/src/frontend/restgenerated/models/AreaType.ts b/src/frontend/src/restgenerated/models/AreaType.ts similarity index 100% rename from src/frontend/restgenerated/models/AreaType.ts rename to src/frontend/src/restgenerated/models/AreaType.ts diff --git a/src/frontend/restgenerated/models/BlankEnum.ts b/src/frontend/src/restgenerated/models/BlankEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/BlankEnum.ts rename to src/frontend/src/restgenerated/models/BlankEnum.ts diff --git a/src/frontend/restgenerated/models/BusinessArea.ts b/src/frontend/src/restgenerated/models/BusinessArea.ts similarity index 100% rename from src/frontend/restgenerated/models/BusinessArea.ts rename to src/frontend/src/restgenerated/models/BusinessArea.ts diff --git a/src/frontend/restgenerated/models/CollectIndividualDataEnum.ts b/src/frontend/src/restgenerated/models/CollectIndividualDataEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/CollectIndividualDataEnum.ts rename to src/frontend/src/restgenerated/models/CollectIndividualDataEnum.ts diff --git a/src/frontend/restgenerated/models/CollectTypeEnum.ts b/src/frontend/src/restgenerated/models/CollectTypeEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/CollectTypeEnum.ts rename to src/frontend/src/restgenerated/models/CollectTypeEnum.ts diff --git a/src/frontend/restgenerated/models/CommsDisabilityEnum.ts b/src/frontend/src/restgenerated/models/CommsDisabilityEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/CommsDisabilityEnum.ts rename to src/frontend/src/restgenerated/models/CommsDisabilityEnum.ts diff --git a/src/frontend/restgenerated/models/ConsentSharingEnum.ts b/src/frontend/src/restgenerated/models/ConsentSharingEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/ConsentSharingEnum.ts rename to src/frontend/src/restgenerated/models/ConsentSharingEnum.ts diff --git a/src/frontend/restgenerated/models/CountryEnum.ts b/src/frontend/src/restgenerated/models/CountryEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/CountryEnum.ts rename to src/frontend/src/restgenerated/models/CountryEnum.ts diff --git a/src/frontend/restgenerated/models/CountryOriginEnum.ts b/src/frontend/src/restgenerated/models/CountryOriginEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/CountryOriginEnum.ts rename to src/frontend/src/restgenerated/models/CountryOriginEnum.ts diff --git a/src/frontend/restgenerated/models/CurrencyEnum.ts b/src/frontend/src/restgenerated/models/CurrencyEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/CurrencyEnum.ts rename to src/frontend/src/restgenerated/models/CurrencyEnum.ts diff --git a/src/frontend/restgenerated/models/DeduplicationGoldenRecordStatusEnum.ts b/src/frontend/src/restgenerated/models/DeduplicationGoldenRecordStatusEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/DeduplicationGoldenRecordStatusEnum.ts rename to src/frontend/src/restgenerated/models/DeduplicationGoldenRecordStatusEnum.ts diff --git a/src/frontend/restgenerated/models/Delegate.ts b/src/frontend/src/restgenerated/models/Delegate.ts similarity index 100% rename from src/frontend/restgenerated/models/Delegate.ts rename to src/frontend/src/restgenerated/models/Delegate.ts diff --git a/src/frontend/restgenerated/models/DelegatePeople.ts b/src/frontend/src/restgenerated/models/DelegatePeople.ts similarity index 100% rename from src/frontend/restgenerated/models/DelegatePeople.ts rename to src/frontend/src/restgenerated/models/DelegatePeople.ts diff --git a/src/frontend/restgenerated/models/DisabilityEnum.ts b/src/frontend/src/restgenerated/models/DisabilityEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/DisabilityEnum.ts rename to src/frontend/src/restgenerated/models/DisabilityEnum.ts diff --git a/src/frontend/restgenerated/models/Document.ts b/src/frontend/src/restgenerated/models/Document.ts similarity index 100% rename from src/frontend/restgenerated/models/Document.ts rename to src/frontend/src/restgenerated/models/Document.ts diff --git a/src/frontend/restgenerated/models/DocumentStatusEnum.ts b/src/frontend/src/restgenerated/models/DocumentStatusEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/DocumentStatusEnum.ts rename to src/frontend/src/restgenerated/models/DocumentStatusEnum.ts diff --git a/src/frontend/restgenerated/models/DocumentTypeEnum.ts b/src/frontend/src/restgenerated/models/DocumentTypeEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/DocumentTypeEnum.ts rename to src/frontend/src/restgenerated/models/DocumentTypeEnum.ts diff --git a/src/frontend/restgenerated/models/FollowUpPaymentPlan.ts b/src/frontend/src/restgenerated/models/FollowUpPaymentPlan.ts similarity index 100% rename from src/frontend/restgenerated/models/FollowUpPaymentPlan.ts rename to src/frontend/src/restgenerated/models/FollowUpPaymentPlan.ts diff --git a/src/frontend/restgenerated/models/FrequencyOfPaymentsEnum.ts b/src/frontend/src/restgenerated/models/FrequencyOfPaymentsEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/FrequencyOfPaymentsEnum.ts rename to src/frontend/src/restgenerated/models/FrequencyOfPaymentsEnum.ts diff --git a/src/frontend/restgenerated/models/HearingDisabilityEnum.ts b/src/frontend/src/restgenerated/models/HearingDisabilityEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/HearingDisabilityEnum.ts rename to src/frontend/src/restgenerated/models/HearingDisabilityEnum.ts diff --git a/src/frontend/restgenerated/models/Household.ts b/src/frontend/src/restgenerated/models/Household.ts similarity index 100% rename from src/frontend/restgenerated/models/Household.ts rename to src/frontend/src/restgenerated/models/Household.ts diff --git a/src/frontend/restgenerated/models/Individual.ts b/src/frontend/src/restgenerated/models/Individual.ts similarity index 100% rename from src/frontend/restgenerated/models/Individual.ts rename to src/frontend/src/restgenerated/models/Individual.ts diff --git a/src/frontend/restgenerated/models/MemoryDisabilityEnum.ts b/src/frontend/src/restgenerated/models/MemoryDisabilityEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/MemoryDisabilityEnum.ts rename to src/frontend/src/restgenerated/models/MemoryDisabilityEnum.ts diff --git a/src/frontend/restgenerated/models/NullEnum.ts b/src/frontend/src/restgenerated/models/NullEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/NullEnum.ts rename to src/frontend/src/restgenerated/models/NullEnum.ts diff --git a/src/frontend/restgenerated/models/OrgEnumeratorEnum.ts b/src/frontend/src/restgenerated/models/OrgEnumeratorEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/OrgEnumeratorEnum.ts rename to src/frontend/src/restgenerated/models/OrgEnumeratorEnum.ts diff --git a/src/frontend/restgenerated/models/PaginatedAreaList.ts b/src/frontend/src/restgenerated/models/PaginatedAreaList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedAreaList.ts rename to src/frontend/src/restgenerated/models/PaginatedAreaList.ts diff --git a/src/frontend/restgenerated/models/PaginatedAreaListList.ts b/src/frontend/src/restgenerated/models/PaginatedAreaListList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedAreaListList.ts rename to src/frontend/src/restgenerated/models/PaginatedAreaListList.ts diff --git a/src/frontend/restgenerated/models/PaginatedAreaTypeList.ts b/src/frontend/src/restgenerated/models/PaginatedAreaTypeList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedAreaTypeList.ts rename to src/frontend/src/restgenerated/models/PaginatedAreaTypeList.ts diff --git a/src/frontend/restgenerated/models/PaginatedBusinessAreaList.ts b/src/frontend/src/restgenerated/models/PaginatedBusinessAreaList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedBusinessAreaList.ts rename to src/frontend/src/restgenerated/models/PaginatedBusinessAreaList.ts diff --git a/src/frontend/restgenerated/models/PaginatedPaymentPlanList.ts b/src/frontend/src/restgenerated/models/PaginatedPaymentPlanList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedPaymentPlanList.ts rename to src/frontend/src/restgenerated/models/PaginatedPaymentPlanList.ts diff --git a/src/frontend/restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList.ts b/src/frontend/src/restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList.ts rename to src/frontend/src/restgenerated/models/PaginatedPeriodicDataUpdateTemplateListList.ts diff --git a/src/frontend/restgenerated/models/PaginatedPeriodicDataUpdateUploadListList.ts b/src/frontend/src/restgenerated/models/PaginatedPeriodicDataUpdateUploadListList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedPeriodicDataUpdateUploadListList.ts rename to src/frontend/src/restgenerated/models/PaginatedPeriodicDataUpdateUploadListList.ts diff --git a/src/frontend/restgenerated/models/PaginatedPeriodicFieldList.ts b/src/frontend/src/restgenerated/models/PaginatedPeriodicFieldList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedPeriodicFieldList.ts rename to src/frontend/src/restgenerated/models/PaginatedPeriodicFieldList.ts diff --git a/src/frontend/restgenerated/models/PaginatedProgramCycleListList.ts b/src/frontend/src/restgenerated/models/PaginatedProgramCycleListList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedProgramCycleListList.ts rename to src/frontend/src/restgenerated/models/PaginatedProgramCycleListList.ts diff --git a/src/frontend/restgenerated/models/PaginatedProgramGlobalList.ts b/src/frontend/src/restgenerated/models/PaginatedProgramGlobalList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedProgramGlobalList.ts rename to src/frontend/src/restgenerated/models/PaginatedProgramGlobalList.ts diff --git a/src/frontend/restgenerated/models/PaginatedRegistrationDataImportListList.ts b/src/frontend/src/restgenerated/models/PaginatedRegistrationDataImportListList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedRegistrationDataImportListList.ts rename to src/frontend/src/restgenerated/models/PaginatedRegistrationDataImportListList.ts diff --git a/src/frontend/restgenerated/models/PaginatedTargetPopulationListList.ts b/src/frontend/src/restgenerated/models/PaginatedTargetPopulationListList.ts similarity index 100% rename from src/frontend/restgenerated/models/PaginatedTargetPopulationListList.ts rename to src/frontend/src/restgenerated/models/PaginatedTargetPopulationListList.ts diff --git a/src/frontend/restgenerated/models/PatchedProgramCycleUpdate.ts b/src/frontend/src/restgenerated/models/PatchedProgramCycleUpdate.ts similarity index 100% rename from src/frontend/restgenerated/models/PatchedProgramCycleUpdate.ts rename to src/frontend/src/restgenerated/models/PatchedProgramCycleUpdate.ts diff --git a/src/frontend/restgenerated/models/PatchedRDI.ts b/src/frontend/src/restgenerated/models/PatchedRDI.ts similarity index 100% rename from src/frontend/restgenerated/models/PatchedRDI.ts rename to src/frontend/src/restgenerated/models/PatchedRDI.ts diff --git a/src/frontend/restgenerated/models/PaymentPlan.ts b/src/frontend/src/restgenerated/models/PaymentPlan.ts similarity index 100% rename from src/frontend/restgenerated/models/PaymentPlan.ts rename to src/frontend/src/restgenerated/models/PaymentPlan.ts diff --git a/src/frontend/restgenerated/models/PaymentPlanBulkAction.ts b/src/frontend/src/restgenerated/models/PaymentPlanBulkAction.ts similarity index 100% rename from src/frontend/restgenerated/models/PaymentPlanBulkAction.ts rename to src/frontend/src/restgenerated/models/PaymentPlanBulkAction.ts diff --git a/src/frontend/restgenerated/models/PeriodicDataUpdateTemplateCreate.ts b/src/frontend/src/restgenerated/models/PeriodicDataUpdateTemplateCreate.ts similarity index 100% rename from src/frontend/restgenerated/models/PeriodicDataUpdateTemplateCreate.ts rename to src/frontend/src/restgenerated/models/PeriodicDataUpdateTemplateCreate.ts diff --git a/src/frontend/restgenerated/models/PeriodicDataUpdateTemplateDetail.ts b/src/frontend/src/restgenerated/models/PeriodicDataUpdateTemplateDetail.ts similarity index 100% rename from src/frontend/restgenerated/models/PeriodicDataUpdateTemplateDetail.ts rename to src/frontend/src/restgenerated/models/PeriodicDataUpdateTemplateDetail.ts diff --git a/src/frontend/restgenerated/models/PeriodicDataUpdateTemplateList.ts b/src/frontend/src/restgenerated/models/PeriodicDataUpdateTemplateList.ts similarity index 100% rename from src/frontend/restgenerated/models/PeriodicDataUpdateTemplateList.ts rename to src/frontend/src/restgenerated/models/PeriodicDataUpdateTemplateList.ts diff --git a/src/frontend/restgenerated/models/PeriodicDataUpdateUpload.ts b/src/frontend/src/restgenerated/models/PeriodicDataUpdateUpload.ts similarity index 100% rename from src/frontend/restgenerated/models/PeriodicDataUpdateUpload.ts rename to src/frontend/src/restgenerated/models/PeriodicDataUpdateUpload.ts diff --git a/src/frontend/restgenerated/models/PeriodicDataUpdateUploadDetail.ts b/src/frontend/src/restgenerated/models/PeriodicDataUpdateUploadDetail.ts similarity index 100% rename from src/frontend/restgenerated/models/PeriodicDataUpdateUploadDetail.ts rename to src/frontend/src/restgenerated/models/PeriodicDataUpdateUploadDetail.ts diff --git a/src/frontend/restgenerated/models/PeriodicDataUpdateUploadList.ts b/src/frontend/src/restgenerated/models/PeriodicDataUpdateUploadList.ts similarity index 100% rename from src/frontend/restgenerated/models/PeriodicDataUpdateUploadList.ts rename to src/frontend/src/restgenerated/models/PeriodicDataUpdateUploadList.ts diff --git a/src/frontend/restgenerated/models/PeriodicField.ts b/src/frontend/src/restgenerated/models/PeriodicField.ts similarity index 100% rename from src/frontend/restgenerated/models/PeriodicField.ts rename to src/frontend/src/restgenerated/models/PeriodicField.ts diff --git a/src/frontend/restgenerated/models/PeriodicFieldData.ts b/src/frontend/src/restgenerated/models/PeriodicFieldData.ts similarity index 100% rename from src/frontend/restgenerated/models/PeriodicFieldData.ts rename to src/frontend/src/restgenerated/models/PeriodicFieldData.ts diff --git a/src/frontend/restgenerated/models/PhysicalDisabilityEnum.ts b/src/frontend/src/restgenerated/models/PhysicalDisabilityEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/PhysicalDisabilityEnum.ts rename to src/frontend/src/restgenerated/models/PhysicalDisabilityEnum.ts diff --git a/src/frontend/restgenerated/models/PreferredLanguageEnum.ts b/src/frontend/src/restgenerated/models/PreferredLanguageEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/PreferredLanguageEnum.ts rename to src/frontend/src/restgenerated/models/PreferredLanguageEnum.ts diff --git a/src/frontend/restgenerated/models/Program.ts b/src/frontend/src/restgenerated/models/Program.ts similarity index 100% rename from src/frontend/restgenerated/models/Program.ts rename to src/frontend/src/restgenerated/models/Program.ts diff --git a/src/frontend/restgenerated/models/ProgramCycleCreate.ts b/src/frontend/src/restgenerated/models/ProgramCycleCreate.ts similarity index 100% rename from src/frontend/restgenerated/models/ProgramCycleCreate.ts rename to src/frontend/src/restgenerated/models/ProgramCycleCreate.ts diff --git a/src/frontend/restgenerated/models/ProgramCycleList.ts b/src/frontend/src/restgenerated/models/ProgramCycleList.ts similarity index 100% rename from src/frontend/restgenerated/models/ProgramCycleList.ts rename to src/frontend/src/restgenerated/models/ProgramCycleList.ts diff --git a/src/frontend/restgenerated/models/ProgramCycleUpdate.ts b/src/frontend/src/restgenerated/models/ProgramCycleUpdate.ts similarity index 100% rename from src/frontend/restgenerated/models/ProgramCycleUpdate.ts rename to src/frontend/src/restgenerated/models/ProgramCycleUpdate.ts diff --git a/src/frontend/restgenerated/models/ProgramGlobal.ts b/src/frontend/src/restgenerated/models/ProgramGlobal.ts similarity index 100% rename from src/frontend/restgenerated/models/ProgramGlobal.ts rename to src/frontend/src/restgenerated/models/ProgramGlobal.ts diff --git a/src/frontend/restgenerated/models/ProgramGlobalStatusEnum.ts b/src/frontend/src/restgenerated/models/ProgramGlobalStatusEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/ProgramGlobalStatusEnum.ts rename to src/frontend/src/restgenerated/models/ProgramGlobalStatusEnum.ts diff --git a/src/frontend/restgenerated/models/PushPeople.ts b/src/frontend/src/restgenerated/models/PushPeople.ts similarity index 100% rename from src/frontend/restgenerated/models/PushPeople.ts rename to src/frontend/src/restgenerated/models/PushPeople.ts diff --git a/src/frontend/restgenerated/models/PushPeopleTypeEnum.ts b/src/frontend/src/restgenerated/models/PushPeopleTypeEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/PushPeopleTypeEnum.ts rename to src/frontend/src/restgenerated/models/PushPeopleTypeEnum.ts diff --git a/src/frontend/restgenerated/models/RDI.ts b/src/frontend/src/restgenerated/models/RDI.ts similarity index 100% rename from src/frontend/restgenerated/models/RDI.ts rename to src/frontend/src/restgenerated/models/RDI.ts diff --git a/src/frontend/restgenerated/models/RDINested.ts b/src/frontend/src/restgenerated/models/RDINested.ts similarity index 100% rename from src/frontend/restgenerated/models/RDINested.ts rename to src/frontend/src/restgenerated/models/RDINested.ts diff --git a/src/frontend/restgenerated/models/RdiMergeStatusEnum.ts b/src/frontend/src/restgenerated/models/RdiMergeStatusEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/RdiMergeStatusEnum.ts rename to src/frontend/src/restgenerated/models/RdiMergeStatusEnum.ts diff --git a/src/frontend/restgenerated/models/RegistrationDataImportList.ts b/src/frontend/src/restgenerated/models/RegistrationDataImportList.ts similarity index 100% rename from src/frontend/restgenerated/models/RegistrationDataImportList.ts rename to src/frontend/src/restgenerated/models/RegistrationDataImportList.ts diff --git a/src/frontend/restgenerated/models/RegistrationMethodEnum.ts b/src/frontend/src/restgenerated/models/RegistrationMethodEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/RegistrationMethodEnum.ts rename to src/frontend/src/restgenerated/models/RegistrationMethodEnum.ts diff --git a/src/frontend/restgenerated/models/RelationshipEnum.ts b/src/frontend/src/restgenerated/models/RelationshipEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/RelationshipEnum.ts rename to src/frontend/src/restgenerated/models/RelationshipEnum.ts diff --git a/src/frontend/restgenerated/models/ResidenceStatusEnum.ts b/src/frontend/src/restgenerated/models/ResidenceStatusEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/ResidenceStatusEnum.ts rename to src/frontend/src/restgenerated/models/ResidenceStatusEnum.ts diff --git a/src/frontend/restgenerated/models/ScopeEnum.ts b/src/frontend/src/restgenerated/models/ScopeEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/ScopeEnum.ts rename to src/frontend/src/restgenerated/models/ScopeEnum.ts diff --git a/src/frontend/restgenerated/models/SectorEnum.ts b/src/frontend/src/restgenerated/models/SectorEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/SectorEnum.ts rename to src/frontend/src/restgenerated/models/SectorEnum.ts diff --git a/src/frontend/restgenerated/models/SeeingDisabilityEnum.ts b/src/frontend/src/restgenerated/models/SeeingDisabilityEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/SeeingDisabilityEnum.ts rename to src/frontend/src/restgenerated/models/SeeingDisabilityEnum.ts diff --git a/src/frontend/restgenerated/models/SelfcareDisabilityEnum.ts b/src/frontend/src/restgenerated/models/SelfcareDisabilityEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/SelfcareDisabilityEnum.ts rename to src/frontend/src/restgenerated/models/SelfcareDisabilityEnum.ts diff --git a/src/frontend/restgenerated/models/SexEnum.ts b/src/frontend/src/restgenerated/models/SexEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/SexEnum.ts rename to src/frontend/src/restgenerated/models/SexEnum.ts diff --git a/src/frontend/restgenerated/models/SubtypeEnum.ts b/src/frontend/src/restgenerated/models/SubtypeEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/SubtypeEnum.ts rename to src/frontend/src/restgenerated/models/SubtypeEnum.ts diff --git a/src/frontend/restgenerated/models/TargetPopulationList.ts b/src/frontend/src/restgenerated/models/TargetPopulationList.ts similarity index 100% rename from src/frontend/restgenerated/models/TargetPopulationList.ts rename to src/frontend/src/restgenerated/models/TargetPopulationList.ts diff --git a/src/frontend/restgenerated/models/WorkStatusEnum.ts b/src/frontend/src/restgenerated/models/WorkStatusEnum.ts similarity index 100% rename from src/frontend/restgenerated/models/WorkStatusEnum.ts rename to src/frontend/src/restgenerated/models/WorkStatusEnum.ts diff --git a/src/frontend/restgenerated/services/FieldsAttributesService.ts b/src/frontend/src/restgenerated/services/FieldsAttributesService.ts similarity index 100% rename from src/frontend/restgenerated/services/FieldsAttributesService.ts rename to src/frontend/src/restgenerated/services/FieldsAttributesService.ts diff --git a/src/frontend/restgenerated/services/HhStatusService.ts b/src/frontend/src/restgenerated/services/HhStatusService.ts similarity index 100% rename from src/frontend/restgenerated/services/HhStatusService.ts rename to src/frontend/src/restgenerated/services/HhStatusService.ts diff --git a/src/frontend/restgenerated/services/RestService.ts b/src/frontend/src/restgenerated/services/RestService.ts similarity index 100% rename from src/frontend/restgenerated/services/RestService.ts rename to src/frontend/src/restgenerated/services/RestService.ts diff --git a/src/frontend/tsconfig.json b/src/frontend/tsconfig.json index ca28961451..9fc1147f26 100644 --- a/src/frontend/tsconfig.json +++ b/src/frontend/tsconfig.json @@ -31,7 +31,7 @@ "@utils/*": ["src/utils/*"], "@hooks/*": ["src/hooks/*"], "@generated/*": ["src/__generated__/*"], - "restgenerated/*": ["../frontend/restgenerated/*"] + "@restgenerated/*": ["src/restgenerated/*"] } }, "include": [ From a6d9d756788e2d3a9a55fc5d05b94c0621e9ec8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Wo=C5=BAniak?= <17177420+wozniakpl@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:58:34 +0200 Subject: [PATCH 087/202] Provide superusers and testers via env/arg to `initdemo` (#4295) --- .../apps/core/management/commands/initdemo.py | 239 ++++++++++++------ 1 file changed, 166 insertions(+), 73 deletions(-) diff --git a/src/hct_mis_api/apps/core/management/commands/initdemo.py b/src/hct_mis_api/apps/core/management/commands/initdemo.py index 4dc6a92d53..8ae89b33d5 100644 --- a/src/hct_mis_api/apps/core/management/commands/initdemo.py +++ b/src/hct_mis_api/apps/core/management/commands/initdemo.py @@ -1,7 +1,57 @@ +""" +Django Management Command: initdemo + +This command initializes demo data for the application by performing the following steps: + +1. **Database Setup**: + - Waits for the default database connection to be available. + - Optionally drops existing databases unless the `--skip-drop` flag is used. + - Migrates the databases to apply the latest schema. + - Flushes specified databases to remove existing data. + +2. **Fixture Loading**: + - Loads a series of JSON fixtures into the databases to populate them with initial data. + - Rebuilds the Elasticsearch search index to ensure it's in sync with the loaded data. + +3. **Data Generation**: + - Generates additional data such as delivery mechanisms, payment plans, and reconciled payment plans. + - Updates Financial Service Providers (FSPs) with the latest information. + +4. **User Creation**: + - Creates users based on provided email lists, assigning appropriate roles and permissions. + - Users can be added as staff, superusers, or testers based on input. + +5. **Logging and Error Handling**: + - Logs key actions and errors to assist with debugging and monitoring the initialization process. + +**Usage Examples**: + +- Initialize demo data with default settings: + ```bash + python manage.py initdemo + ``` + +- Initialize demo data without dropping existing databases: + ```bash + python manage.py initdemo --skip-drop + ``` + +- Initialize demo data and add specific staff and tester users: + ```bash + python manage.py initdemo --email-list="admin@example.com,user@example.com" --tester-list="tester1@example.com,tester2@example.com" + ``` + +**Environment Variables**: + +- `INITDEMO_EMAIL_LIST`: Comma-separated list of emails to be added as staff and superusers. +- `INITDEMO_TESTER_LIST`: Comma-separated list of emails to be added as testers. +""" + import logging +import os import time from argparse import ArgumentParser -from typing import Any +from typing import Any, List from django.conf import settings from django.core.management import BaseCommand, call_command @@ -24,6 +74,8 @@ class Command(BaseCommand): + help = "Initialize demo data for the application." + def add_arguments(self, parser: ArgumentParser) -> None: parser.add_argument( "--skip-drop", @@ -31,103 +83,144 @@ def add_arguments(self, parser: ArgumentParser) -> None: default=False, help="Skip migrating - just reload the data", ) + parser.add_argument( + "--email-list", + type=str, + help="Comma-separated list of emails to be added as staff and superusers", + ) + parser.add_argument( + "--tester-list", + type=str, + help="Comma-separated list of emails to be added as testers", + ) def handle(self, *args: Any, **options: Any) -> None: start_time = timezone.now() db_connection = connections["default"] connected = False + self.stdout.write("Waiting for database connection...") while not connected: try: db_connection.cursor() - time.sleep(0.5) + connected = True except OperationalError: connected = False - else: - connected = True + time.sleep(0.5) - if options["skip_drop"] is False: + if not options["skip_drop"]: + self.stdout.write("Dropping existing databases...") call_command("dropalldb") + self.stdout.write("Migrating databases...") call_command("migratealldb") - call_command("flush", "--noinput") - call_command("flush", "--noinput", database="cash_assist_datahub_mis") - call_command("flush", "--noinput", database="cash_assist_datahub_ca") - call_command("flush", "--noinput", database="cash_assist_datahub_erp") - - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/geo/fixtures/data.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/core/fixtures/data.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/account/fixtures/data.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/core/fixtures/businessareapartnerthrough.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/program/fixtures/data.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/registration_data/fixtures/data.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/household/fixtures/documenttype.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/household/fixtures/data.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/accountability/fixtures/data.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/steficon/fixtures/data.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/contrib/aurora/fixtures/data.json") + # Flush databases + databases = [ + "default", + "cash_assist_datahub_mis", + "cash_assist_datahub_ca", + "cash_assist_datahub_erp", + ] + self.stdout.write("Flushing databases...") + for db in databases: + self.stdout.write(f"Flushing database: {db}") + call_command("flush", "--noinput", database=db) + + # Load fixtures + fixtures = [ + "apps/geo/fixtures/data.json", + "apps/core/fixtures/data.json", + "apps/account/fixtures/data.json", + "apps/core/fixtures/businessareapartnerthrough.json", + "apps/program/fixtures/data.json", + "apps/registration_data/fixtures/data.json", + "apps/household/fixtures/documenttype.json", + "apps/household/fixtures/data.json", + "apps/accountability/fixtures/data.json", + "apps/steficon/fixtures/data.json", + "contrib/aurora/fixtures/data.json", + ] + self.stdout.write("Loading fixtures...") + for fixture in fixtures: + self.stdout.write(f"Loading fixture: {fixture}") + call_command("loaddata", f"{settings.PROJECT_ROOT}/{fixture}") try: + self.stdout.write("Rebuilding search index...") call_command("search_index", "--rebuild", "-f") except elasticsearch.exceptions.RequestError as e: - logger.error(e) + logger.error(f"Elasticsearch RequestError: {e}") + # Generate additional data + self.stdout.write("Generating delivery mechanisms...") generate_delivery_mechanisms() + self.stdout.write("Generating payment plan...") generate_payment_plan() + self.stdout.write("Generating real cash plans...") generate_real_cash_plans() + self.stdout.write("Generating reconciled payment plan...") generate_reconciled_payment_plan() + self.stdout.write("Updating FSPs...") update_fsps() - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/core/fixtures/pdu.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/program/fixtures/programpartnerthrough.json") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/grievance/fixtures/data.json") - - email_list = [ - "jan.romaniak@kellton.com", - "jakub.krasnowski@kellton.com", - "pavlo.mokiichuk@kellton.com", - "katarzyna.lanecka@kellton.com", - "maciej.szewczyk@kellton.com", - "marek.biczysko@kellton.com", - "gerba@unicef.org", - "ddinicola@unicef.org", - "sapostolico@unicef.org", - "ntrncic@unicef.org", - "aafaour@unicef.org", - "aboncenne@unicef.org", - "asrugano@unicef.org", - "gkeriri@unicef.org", - "jbassette@unicef.org", - "nmkuzi@unicef.org", - "dhassooneh@unicef.org", - ] - tester_list = [ - "khaddad@unicef.org", - "stoor@unicef.org", - "jhalding@unicef.org", - "ysokadjo@unicef.org", - "nkuma@unicef.org", - "hatify@unicef.org", - "gfranco@unicef.org", - "ilutska@unicef.org", - "okozyrenko@unicef.org", + # Load more fixtures + additional_fixtures = [ + "apps/core/fixtures/pdu.json", + "apps/program/fixtures/programpartnerthrough.json", + "apps/grievance/fixtures/data.json", ] + self.stdout.write("Loading additional fixtures...") + for fixture in additional_fixtures: + self.stdout.write(f"Loading fixture: {fixture}") + call_command("loaddata", f"{settings.PROJECT_ROOT}/{fixture}") + + # Retrieve email lists from environment variables or command-line arguments + email_list_env = os.getenv("INITDEMO_EMAIL_LIST") + tester_list_env = os.getenv("INITDEMO_TESTER_LIST") + + email_list = ( + options["email_list"].split(",") + if options.get("email_list") + else email_list_env.split(",") + if email_list_env + else [] + ) + + tester_list = ( + options["tester_list"].split(",") + if options.get("tester_list") + else tester_list_env.split(",") + if tester_list_env + else [] + ) - role_with_all_perms = Role.objects.get(name="Role with all permissions") - afghanistan = BusinessArea.objects.get(slug="afghanistan") - partner = Partner.objects.get(name="UNICEF") - - for email in email_list + tester_list: - user = User.objects.create_user(email, email, "password", partner=partner) - UserRole.objects.create( - user=user, - role=role_with_all_perms, - business_area=afghanistan, - ) - if email in email_list: - user.is_staff = True - user.is_superuser = True - user.set_unusable_password() - user.save() - - print(f"Done in {timezone.now() - start_time}") + if email_list or tester_list: + role_with_all_perms = Role.objects.get(name="Role with all permissions") + afghanistan = BusinessArea.objects.get(slug="afghanistan") + partner = Partner.objects.get(name="UNICEF") + + combined_email_list: List[str] = [email.strip() for email in email_list + tester_list if email.strip()] + + if combined_email_list: + self.stdout.write("Creating users...") + for email in combined_email_list: + try: + user = User.objects.create_user(email, email, "password", partner=partner) + UserRole.objects.create( + user=user, + role=role_with_all_perms, + business_area=afghanistan, + ) + if email in email_list: + user.is_staff = True + user.is_superuser = True + user.set_unusable_password() + user.save() + self.stdout.write(self.style.SUCCESS(f"Created user: {email}")) + except Exception as e: + logger.error(f"Failed to create user {email}: {e}") + self.stderr.write(self.style.ERROR(f"Failed to create user {email}: {e}")) + else: + self.stdout.write("No email lists provided. Skipping user creation.") + + self.stdout.write(self.style.SUCCESS(f"Done in {timezone.now() - start_time}")) From 24cbaebcb894dce8c8c30337edb9b70aef07247d Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Mon, 7 Oct 2024 15:53:44 +0200 Subject: [PATCH 088/202] fix eslint and tests --- src/frontend/.eslintrc.cjs | 7 ++++++- src/frontend/jest.config.ts | 2 ++ .../PaymentPlansTable/PaymentPlansTable.test.tsx | 7 +++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/frontend/.eslintrc.cjs b/src/frontend/.eslintrc.cjs index 8d21326f74..f14735d24d 100644 --- a/src/frontend/.eslintrc.cjs +++ b/src/frontend/.eslintrc.cjs @@ -35,7 +35,12 @@ module.exports = { 'jest', '@tanstack/query', ], - ignorePatterns: ['**/*.test.tsx', '**/*.test.ts', '**/__generated__/*'], + ignorePatterns: [ + '**/*.test.tsx', + '**/*.test.ts', + '**/__generated__/*', + 'src/restgenerated/', + ], settings: { react: { version: 'detect', diff --git a/src/frontend/jest.config.ts b/src/frontend/jest.config.ts index b2e9a59dec..60c68da1b1 100644 --- a/src/frontend/jest.config.ts +++ b/src/frontend/jest.config.ts @@ -24,6 +24,7 @@ module.exports = { '/script/', '/src/serviceWorker.ts', '.*\\.d\\.ts', + '/src/restgenerated/', ], moduleNameMapper: { '^@components/(.*)$': '<rootDir>/src/components/$1', @@ -33,5 +34,6 @@ module.exports = { '^@utils/(.*)$': '<rootDir>/src/utils/$1', '^@hooks/(.*)$': '<rootDir>/src/hooks/$1', '^@generated/(.*)$': '<rootDir>/src/__generated__/$1', + '^@restgenerated/(.*)$': '<rootDir>/src/restgenerated/$1', }, }; diff --git a/src/frontend/src/containers/tables/paymentmodule/PaymentPlansTable/PaymentPlansTable.test.tsx b/src/frontend/src/containers/tables/paymentmodule/PaymentPlansTable/PaymentPlansTable.test.tsx index 0853ad4bab..803a348146 100644 --- a/src/frontend/src/containers/tables/paymentmodule/PaymentPlansTable/PaymentPlansTable.test.tsx +++ b/src/frontend/src/containers/tables/paymentmodule/PaymentPlansTable/PaymentPlansTable.test.tsx @@ -1,9 +1,8 @@ import { MockedProvider } from '@apollo/react-testing'; import { act } from '@testing-library/react'; -import * as React from 'react'; import wait from 'waait'; import { fakeApolloAllPaymentPlansForTable } from '../../../../../fixtures/payments/fakeApolloAllPaymentPlansForTable'; -import { ApolloLoadingLink, render } from '../../../../testUtils/testUtils'; +import { render } from '../../../../testUtils/testUtils'; import { PaymentPlansTable } from './PaymentPlansTable'; describe('containers/tables/payments/PaymentPlansTable', () => { @@ -30,7 +29,7 @@ describe('containers/tables/payments/PaymentPlansTable', () => { await act(() => wait(0)); // wait for response expect(container).toMatchSnapshot(); - }); + }, 10000); // Increase the timeout to 10 seconds it('should render loading', async () => { const { container } = render( @@ -55,5 +54,5 @@ describe('containers/tables/payments/PaymentPlansTable', () => { await act(() => wait(0)); // wait for response expect(container).toMatchSnapshot(); - }); + }, 10000); // Increase the timeout to 10 seconds }); From fc7943c261a43447f6baac0ae52017e9141908a5 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa <p.kujawa2f@gmail.com> Date: Mon, 7 Oct 2024 16:22:33 +0200 Subject: [PATCH 089/202] add index on document number --- .../household/migrations/0186_migration.py | 31 +++++++++++++++++++ src/hct_mis_api/apps/household/models.py | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 src/hct_mis_api/apps/household/migrations/0186_migration.py diff --git a/src/hct_mis_api/apps/household/migrations/0186_migration.py b/src/hct_mis_api/apps/household/migrations/0186_migration.py new file mode 100644 index 0000000000..9bacf5a816 --- /dev/null +++ b/src/hct_mis_api/apps/household/migrations/0186_migration.py @@ -0,0 +1,31 @@ +# Generated by Django 3.2.25 on 2024-10-07 14:09 +from django.contrib.postgres.operations import AddIndexConcurrently +from django.db import migrations, models + + +class Migration(migrations.Migration): + atomic = False + + dependencies = [ + ('household', '0185_migration'), + ] + + operations = [ + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.AlterField( + model_name='document', + name='document_number', + field=models.CharField(db_index=True), + ), + ], + database_operations=[ + AddIndexConcurrently( + model_name="document", + index=models.Index( + fields=["document_number"], name="household_document_number_idx" + ) + ), + ] + ) + ] diff --git a/src/hct_mis_api/apps/household/models.py b/src/hct_mis_api/apps/household/models.py index 052e5499ef..7ab8ae8220 100644 --- a/src/hct_mis_api/apps/household/models.py +++ b/src/hct_mis_api/apps/household/models.py @@ -716,7 +716,7 @@ class Document(AbstractSyncable, SoftDeletableRepresentationMergeStatusModel, Ti (STATUS_INVALID, _("Invalid")), ) - document_number = models.CharField(max_length=255, blank=True) + document_number = models.CharField(max_length=255, blank=True, db_index=True) photo = models.ImageField(blank=True) individual = models.ForeignKey("Individual", related_name="documents", on_delete=models.CASCADE) type = models.ForeignKey("DocumentType", related_name="documents", on_delete=models.CASCADE) From ffb7a9348d66d94438d3b8713604b8cdc390061b Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <maciej.szewczyk@tivix.com> Date: Mon, 7 Oct 2024 18:02:56 +0200 Subject: [PATCH 090/202] exclude generated folder from ts checking --- src/frontend/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/tsconfig.json b/src/frontend/tsconfig.json index 9fc1147f26..c7ce3b0966 100644 --- a/src/frontend/tsconfig.json +++ b/src/frontend/tsconfig.json @@ -43,6 +43,6 @@ ".eslintrc.cjs", "jest.config" ], - "exclude": ["node_modules", "frontend/jest"], + "exclude": ["node_modules", "frontend/jest", "src/restgenerated"], "references": [{ "path": "./tsconfig.node.json" }] } From 4ad63795f47ab91bcb1f4455d75ea5439cab7566 Mon Sep 17 00:00:00 2001 From: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> Date: Mon, 7 Oct 2024 20:11:14 +0200 Subject: [PATCH 091/202] 216761 adapt dedup integration (#4298) --- .../apps/registration_datahub/celery_tasks.py | 6 +++- .../services/biometric_deduplication.py | 32 +++++++++++-------- .../test_biometric_deduplication_service.py | 16 +++++----- .../registration_datahub/test_celery_tasks.py | 3 ++ 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/hct_mis_api/apps/registration_datahub/celery_tasks.py b/src/hct_mis_api/apps/registration_datahub/celery_tasks.py index b79d90a9f7..0860bc02cb 100644 --- a/src/hct_mis_api/apps/registration_datahub/celery_tasks.py +++ b/src/hct_mis_api/apps/registration_datahub/celery_tasks.py @@ -505,11 +505,15 @@ def create_grievance_tickets_for_dedup_engine_results(self: Any, rdi_id: str) -> @app.task(bind=True, default_retry_delay=60, max_retries=3) @log_start_and_end @sentry_tags -def fetch_biometric_deduplication_results_and_process(self: Any, deduplication_set_id: str) -> None: +def fetch_biometric_deduplication_results_and_process(self: Any, deduplication_set_id: Optional[str]) -> None: from hct_mis_api.apps.registration_datahub.services.biometric_deduplication import ( BiometricDeduplicationService, ) + if not deduplication_set_id: + logger.error("Program.deduplication_set_id is None") + return + program = Program.objects.get(deduplication_set_id=deduplication_set_id) set_sentry_business_area_tag(program.business_area.name) diff --git a/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py b/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py index 19de1dc88f..a21d4ab146 100644 --- a/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py +++ b/src/hct_mis_api/apps/registration_datahub/services/biometric_deduplication.py @@ -38,7 +38,7 @@ def create_deduplication_set(self, program: Program) -> None: notification_url=f"https://{settings.DOMAIN_NAME}/api/rest/{program.business_area.slug}/programs/{str(program.id)}/registration-data/webhookdeduplication/", # notification_url=reverse("registration-data:webhook_deduplication", kwargs={"program_id": str(program.id), "business_area": program.business_area.slug}), # TODO MB why reverse is not working config=DeduplicationSetConfig( - face_distance_threshold=program.business_area.biometric_deduplication_threshold / 100 + face_distance_threshold=1 - (program.business_area.biometric_deduplication_threshold / 100) ), ) response_data = self.api.create_deduplication_set(deduplication_set) @@ -186,7 +186,7 @@ def store_rdis_deduplication_statistics(self, deduplication_set_id: str) -> None rdis = RegistrationDataImport.objects.filter( status=RegistrationDataImport.IN_REVIEW, program=program, - deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PROCESSING, ) for rdi in rdis: rdi.dedup_engine_batch_duplicates = self.get_duplicate_individuals_for_rdi_against_batch_count(rdi) @@ -234,18 +234,24 @@ def get_duplicate_individuals_for_rdi_against_batch_count(self, rdi: Registratio def get_duplicates_for_merged_rdi_against_population( self, rdi: RegistrationDataImport ) -> QuerySet[DeduplicationEngineSimilarityPair]: - """Used in Grievance tickets creation for merged RDI""" - from hct_mis_api.apps.utils.models import MergeStatusModel + """Used in Grievance tickets creation for merging RDI""" + rdi_pending_individuals = PendingIndividual.objects.filter(is_removed=False, registration_data_import=rdi).only( + "id" + ) + other_pending_individuals = PendingIndividual.objects.filter(is_removed=False, program=rdi.program).exclude( + id__in=rdi_pending_individuals + ) - rdi_individuals = rdi.individuals.filter(is_removed=False).only("id") - return DeduplicationEngineSimilarityPair.objects.filter( - Q(individual1__in=rdi_individuals) | Q(individual2__in=rdi_individuals), - Q(individual1__duplicate=False) & Q(individual2__duplicate=False), - Q(individual1__withdrawn=False) & Q(individual2__withdrawn=False), - Q(individual1__rdi_merge_status=MergeStatusModel.MERGED) - & Q(individual2__rdi_merge_status=MergeStatusModel.MERGED), - program=rdi.program, - ).distinct() + return ( + DeduplicationEngineSimilarityPair.objects.filter( + Q(individual1__in=rdi_pending_individuals) | Q(individual2__in=rdi_pending_individuals), + Q(individual1__duplicate=False) & Q(individual2__duplicate=False), + Q(individual1__withdrawn=False) & Q(individual2__withdrawn=False), + program=rdi.program, + ) + .exclude(Q(individual1__in=other_pending_individuals) | Q(individual2__in=other_pending_individuals)) + .distinct() + ) def get_duplicates_for_rdi_against_population( self, rdi: RegistrationDataImport diff --git a/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py b/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py index 7840c47531..9086a23d74 100644 --- a/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py +++ b/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py @@ -71,7 +71,7 @@ def test_create_deduplication_set(self, mock_create_deduplication_set: mock.Mock reference_pk=str(self.program.id), notification_url=f"https://{settings.DOMAIN_NAME}/api/rest/{self.program.business_area.slug}/programs/{str(self.program.id)}/registration-data/webhookdeduplication/", config=DeduplicationSetConfig( - face_distance_threshold=self.program.business_area.biometric_deduplication_threshold / 100 + face_distance_threshold=1 - (self.program.business_area.biometric_deduplication_threshold / 100) ), ) ) @@ -471,7 +471,7 @@ def test_get_duplicates_for_merged_rdi_against_population(self) -> None: ] service.store_similarity_pairs(str(self.program.deduplication_set_id), similarity_pairs) - duplicates = service.get_duplicates_for_merged_rdi_against_population(rdi1) + duplicates = service.get_duplicates_for_merged_rdi_against_population(rdi2) assert len(duplicates) == 3 assert list( @@ -479,17 +479,17 @@ def test_get_duplicates_for_merged_rdi_against_population(self) -> None: ) == [ { "individual1": ind1.id, - "individual2": ind2.id, + "individual2": ind3.id, "similarity_score": Decimal("70.00"), }, { - "individual1": ind1.id, + "individual1": ind4.id, "individual2": ind5.id, - "similarity_score": Decimal("80.00"), + "similarity_score": Decimal("70.00"), }, { - "individual1": ind2.id, - "individual2": ind6.id, + "individual1": ind3.id, + "individual2": ind4.id, "similarity_score": Decimal("90.00"), }, ] @@ -637,7 +637,7 @@ def test_store_rdis_deduplication_statistics(self) -> None: rdi1 = RegistrationDataImportFactory( program=self.program, - deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, + deduplication_engine_status=RegistrationDataImport.DEDUP_ENGINE_PROCESSING, ) service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock(return_value=8) diff --git a/tests/unit/apps/registration_datahub/test_celery_tasks.py b/tests/unit/apps/registration_datahub/test_celery_tasks.py index 53d63bd82f..386b3097bd 100644 --- a/tests/unit/apps/registration_datahub/test_celery_tasks.py +++ b/tests/unit/apps/registration_datahub/test_celery_tasks.py @@ -1122,6 +1122,9 @@ def test_fetch_biometric_deduplication_results_and_process( self, mock_fetch_biometric_deduplication_results_and_process: Mock, ) -> None: + fetch_biometric_deduplication_results_and_process(None) + mock_fetch_biometric_deduplication_results_and_process.assert_not_called() + deduplication_set_id = str(uuid.uuid4()) self.program.deduplication_set_id = deduplication_set_id self.program.save() From 75144a995823d56696ebd747d116852b4152c816 Mon Sep 17 00:00:00 2001 From: Domenico <dom.dinicola@gmail.com> Date: Tue, 8 Oct 2024 10:10:36 +0200 Subject: [PATCH 092/202] [Back merge] prod>STG (#4294) (#4296) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [hotfix] 216567 deduplication key error when deduplicating records (#4271) * 2216567_Deduplication_Key_Error_when_deduplicating_records * black * remove distinct * fix dedup logic, add ut * lint --------- * skip trivy failure * fix document status when copy population (#4291) --------- Co-authored-by: Pavlo Mokiichuk <pv.pasha.pv@gmail.com> Co-authored-by: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> Co-authored-by: marekbiczysko <marek.biczysko@tivix.com> Co-authored-by: Bartosz Woźniak <17177420+wozniakpl@users.noreply.github.com> --- src/hct_mis_api/apps/program/utils.py | 1 + .../registration_datahub/tasks/deduplicate.py | 92 ++++++++++--------- .../test_handling_documents_duplicates.py | 75 +++++++++++++-- ...t_program_population_to_pending_objects.py | 4 + 4 files changed, 121 insertions(+), 51 deletions(-) diff --git a/src/hct_mis_api/apps/program/utils.py b/src/hct_mis_api/apps/program/utils.py index 3db760a013..1dab4ef2a8 100644 --- a/src/hct_mis_api/apps/program/utils.py +++ b/src/hct_mis_api/apps/program/utils.py @@ -265,6 +265,7 @@ def copy_document_per_individual( document.individual = individual_representation document.program_id = individual_representation.program_id document.rdi_merge_status = rdi_merge_status + document.status = Document.STATUS_PENDING documents_list.append(document) return documents_list diff --git a/src/hct_mis_api/apps/registration_datahub/tasks/deduplicate.py b/src/hct_mis_api/apps/registration_datahub/tasks/deduplicate.py index ab66587fb0..9c742bad9d 100644 --- a/src/hct_mis_api/apps/registration_datahub/tasks/deduplicate.py +++ b/src/hct_mis_api/apps/registration_datahub/tasks/deduplicate.py @@ -674,20 +674,26 @@ def deduplicate( new_documents.filter(Q(status=Document.STATUS_PENDING) & Q(type__is_identity_document=True) & program_q) .select_related("individual", "type") .select_for_update(of=("self",)) # no need to lock individuals - .order_by("pk") + .order_by("document_number", "created_at") ) - documents_numbers = [x.document_number for x in documents_to_dedup] - new_document_signatures = [self._generate_signature(d) for d in documents_to_dedup] - new_document_signatures_duplicated_in_batch = [ - d for d in new_document_signatures if new_document_signatures.count(d) > 1 - ] + + documents_numbers = [] + new_document_signatures = [] + # use this dict for skip ticket creation for the same Individual with the same doc number - ind_and_new_document_signatures_duplicated_in_batch_dict = defaultdict(list) + new_document_signatures_in_batch_per_individual_dict = defaultdict(list) for d in documents_to_dedup: - ind_and_new_document_signatures_duplicated_in_batch_dict[str(d.individual_id)].append( - self._generate_signature(d) + new_document_signature = self._generate_signature(d) + documents_numbers.append(d.document_number) + new_document_signatures.append(new_document_signature) + new_document_signatures_in_batch_per_individual_dict[str(d.individual_id)].append( + new_document_signature ) + new_document_signatures_duplicated_in_batch = [ + d for d in new_document_signatures if new_document_signatures.count(d) > 1 + ] + # added order_by because test was failed randomly all_matching_number_documents = ( Document.objects.select_related("individual", "individual__household", "individual__business_area") @@ -708,22 +714,23 @@ def deduplicate( ) ) .order_by("individual_id") + .distinct() ) all_matching_number_documents_dict = {d.signature: d for d in all_matching_number_documents} all_matching_number_documents_signatures = all_matching_number_documents_dict.keys() - already_processed_signatures = [] + + already_processed_signatures = set() ticket_data_dict = {} possible_duplicates_individuals_id_set = set() for new_document in documents_to_dedup: new_document_signature = self._generate_signature(new_document) - # use this dict for skip ticket creation for the same Individual with the same doc number - is_duplicated_document_number_for_individual: bool = ( - ind_and_new_document_signatures_duplicated_in_batch_dict.get( - str(new_document.individual_id), [] - ).count(new_document_signature) - > 1 - ) + + individual_document_duplicates_count = new_document_signatures_in_batch_per_individual_dict.get( + str(new_document.individual_id), [] + ).count(new_document_signature) + + is_duplicated_document_number_for_individual: bool = individual_document_duplicates_count > 1 if new_document_signature in all_matching_number_documents_signatures: new_document.status = Document.STATUS_NEED_INVESTIGATION @@ -731,35 +738,38 @@ def deduplicate( new_document_signature, { "original": all_matching_number_documents_dict[new_document_signature], - "possible_duplicates": [], + "possible_duplicates": set(), }, ) - ticket_data["possible_duplicates"].append(new_document) + ticket_data["possible_duplicates"].add(new_document) ticket_data_dict[new_document_signature] = ticket_data possible_duplicates_individuals_id_set.add(str(new_document.individual_id)) - continue - - if ( - new_document_signature in new_document_signatures_duplicated_in_batch - and new_document_signature in already_processed_signatures - and not is_duplicated_document_number_for_individual - ): - new_document.status = Document.STATUS_NEED_INVESTIGATION - ticket_data_dict[new_document_signature]["possible_duplicates"].append(new_document) - possible_duplicates_individuals_id_set.add(str(new_document.individual_id)) - continue - new_document.status = Document.STATUS_VALID - already_processed_signatures.append(new_document_signature) + elif new_document_signatures_duplicated_in_batch.count(new_document_signature) > 1: + if is_duplicated_document_number_for_individual: + # do not create ticket for the same Individual with the same doc number + new_document.status = Document.STATUS_VALID + new_document_signatures_in_batch_per_individual_dict[str(new_document.individual_id)].remove( + new_document_signature + ) + new_document_signatures_duplicated_in_batch.remove(new_document_signature) + elif new_document_signature not in already_processed_signatures: + # first occurrence of new document is considered valid, duplicated are added in next iterations + new_document.status = Document.STATUS_VALID + ticket_data_dict[new_document_signature] = { + "original": new_document, + "possible_duplicates": set(), + } + already_processed_signatures.add(new_document_signature) + else: + new_document.status = Document.STATUS_NEED_INVESTIGATION + ticket_data_dict[new_document_signature]["possible_duplicates"].add(new_document) + possible_duplicates_individuals_id_set.add(str(new_document.individual_id)) - if ( - new_document_signature in new_document_signatures_duplicated_in_batch - and not is_duplicated_document_number_for_individual - ): - ticket_data_dict[new_document_signature] = { - "original": new_document, - "possible_duplicates": [], - } + else: + # not a duplicate + new_document.status = Document.STATUS_VALID + already_processed_signatures.add(new_document_signature) try: Document.objects.bulk_update(documents_to_dedup, ("status", "updated_at")) @@ -768,7 +778,7 @@ def deduplicate( f"Hard Deduplication Documents bulk update error." f"All matching documents in DB: {all_matching_number_documents_signatures}" f"New documents to dedup: {new_document_signatures}" - f"new_document_signatures_duplicated_in_batch: {new_document_signatures_duplicated_in_batch}" + f"new_document_signatures_duplicated_in_batch: {set(new_document_signatures_duplicated_in_batch)}" ) raise diff --git a/tests/unit/apps/registration_datahub/test_handling_documents_duplicates.py b/tests/unit/apps/registration_datahub/test_handling_documents_duplicates.py index ca264c82e1..4fed624c42 100644 --- a/tests/unit/apps/registration_datahub/test_handling_documents_duplicates.py +++ b/tests/unit/apps/registration_datahub/test_handling_documents_duplicates.py @@ -10,7 +10,10 @@ 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.grievance.models import GrievanceTicket +from hct_mis_api.apps.grievance.models import ( + GrievanceTicket, + TicketNeedsAdjudicationDetails, +) from hct_mis_api.apps.household.fixtures import ( DocumentTypeFactory, create_household_and_individuals, @@ -437,7 +440,7 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: passport = Document.objects.create( country=self.country, # the same country type=self.dt, - document_number="123444444", # the same doc number + document_number="111", # the same doc number individual=self.individuals[2], # the same Individual program=self.program, rdi_merge_status=MergeStatusModel.MERGED, @@ -445,7 +448,7 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: tax_id = Document.objects.create( country=self.country, # the same country type=self.dt_tax_id, - document_number="123444444", # the same doc number + document_number="111", # the same doc number individual=self.individuals[2], # the same Individual program=self.program, rdi_merge_status=MergeStatusModel.MERGED, @@ -453,7 +456,7 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: d1 = Document.objects.create( country=self.country, type=self.dt, - document_number="123321321", + document_number="222", individual=self.individuals[2], program=self.program, rdi_merge_status=MergeStatusModel.MERGED, @@ -462,7 +465,7 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: Document.objects.create( country=self.country, type=self.dt, - document_number="123321321", + document_number="222", individual=self.individuals[1], program=self.program, status=Document.STATUS_VALID, @@ -471,7 +474,7 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: d2 = Document.objects.create( country=self.country, type=self.dt, - document_number="222", + document_number="333", individual=self.individuals[3], program=self.program, rdi_merge_status=MergeStatusModel.MERGED, @@ -479,7 +482,7 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: d3 = Document.objects.create( country=self.country, type=self.dt_tax_id, - document_number="222", + document_number="333", individual=self.individuals[4], program=self.program, rdi_merge_status=MergeStatusModel.MERGED, @@ -487,7 +490,7 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: d4 = Document.objects.create( country=self.country, type=self.dt, - document_number="111", + document_number="444", individual=self.individuals[0], program=self.program, rdi_merge_status=MergeStatusModel.MERGED, @@ -495,7 +498,7 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: d5 = Document.objects.create( country=self.country, type=self.dt_tax_id, - document_number="111", + document_number="444", individual=self.individuals[1], program=self.program, rdi_merge_status=MergeStatusModel.MERGED, @@ -503,7 +506,7 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: d6 = Document.objects.create( country=self.country, type=DocumentTypeFactory(label="other_type", key="other_type"), - document_number="111", + document_number="444", individual=self.individuals[2], program=self.program, rdi_merge_status=MergeStatusModel.MERGED, @@ -522,3 +525,55 @@ def test_ticket_creation_for_the_same_ind_doc_numbers(self) -> None: tax_id.refresh_from_db() self.assertEqual(tax_id.status, Document.STATUS_VALID) + + def test_ticket_creation_for_the_same_ind_and_across_other_inds_doc_numbers(self) -> None: + passport = Document.objects.create( + country=self.country, # the same country + type=self.dt, + document_number="111", # the same doc number + individual=self.individuals[2], # the same Individual + program=self.program, + rdi_merge_status=MergeStatusModel.MERGED, + ) + tax_id = Document.objects.create( + country=self.country, # the same country + type=self.dt_tax_id, + document_number="111", # the same doc number + individual=self.individuals[2], # the same Individual + program=self.program, + rdi_merge_status=MergeStatusModel.MERGED, + ) + d1 = Document.objects.create( + country=self.country, + type=self.dt, + document_number="222", # different doc number + individual=self.individuals[2], # different Individual + program=self.program, + rdi_merge_status=MergeStatusModel.MERGED, + ) + d2 = Document.objects.create( + country=self.country, + type=self.dt, + document_number="111", # the same doc number + individual=self.individuals[3], # different Individual + program=self.program, + rdi_merge_status=MergeStatusModel.MERGED, + ) + self.assertEqual(GrievanceTicket.objects.all().count(), 0) + HardDocumentDeduplication().deduplicate( + self.get_documents_query([passport, tax_id, d1, d2]), + self.registration_data_import, + ) + + self.assertEqual(GrievanceTicket.objects.all().count(), 1) + ticket_details = TicketNeedsAdjudicationDetails.objects.first() + self.assertIsNotNone(ticket_details.golden_records_individual) + self.assertEqual(ticket_details.possible_duplicates.all().count(), 1) + self.assertNotEqual(ticket_details.golden_records_individual, ticket_details.possible_duplicates.first()) + + passport.refresh_from_db() + self.assertEqual(passport.status, Document.STATUS_VALID) + tax_id.refresh_from_db() + self.assertEqual(tax_id.status, Document.STATUS_VALID) + d2.refresh_from_db() + self.assertEqual(d2.status, Document.STATUS_NEED_INVESTIGATION) 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 7bf0a2d2f1..f6202097eb 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 @@ -370,6 +370,10 @@ def test_create_pending_objects_from_objects(self) -> None: pending_document.individual, head_of_household_pending_individual, ) + self.assertEqual( + pending_document.status, + Document.STATUS_PENDING, + ) pending_identity = IndividualIdentity.pending_objects.first() self.assertEqual( From 3bfcd84deef69bc642015fff9b8066aa009950c1 Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <34482854+mmaciekk@users.noreply.github.com> Date: Tue, 8 Oct 2024 10:59:23 +0200 Subject: [PATCH 093/202] [201639]-add delivery mechanisms section (#4270) * add delivery mechanisms section * add permission, dont show divider for last item --------- Co-authored-by: Maciej Szewczyk <maciej.szewczyk@tivix.com> Co-authored-by: Marek Biczysko <34810846+MarekBiczysko@users.noreply.github.com> --- ...idualAdditionalRegistrationInformation.tsx | 32 +++-- ...ionalRegistrationInformation.test.tsx.snap | 116 +++++++++--------- .../IndividualDeliveryMechanisms.tsx | 66 ++++++++++ .../ProgramDetails/ProgramDetails.tsx | 6 +- src/frontend/src/config/permissions.ts | 2 + .../PopulationIndividualsDetailsPage.tsx | 4 + src/frontend/src/utils/en.json | 3 +- 7 files changed, 147 insertions(+), 82 deletions(-) create mode 100644 src/frontend/src/components/population/IndividualDeliveryMechanisms.tsx diff --git a/src/frontend/src/components/population/IndividualAdditionalRegistrationInformation/IndividualAdditionalRegistrationInformation.tsx b/src/frontend/src/components/population/IndividualAdditionalRegistrationInformation/IndividualAdditionalRegistrationInformation.tsx index b1ca29046f..3ef09b2e00 100644 --- a/src/frontend/src/components/population/IndividualAdditionalRegistrationInformation/IndividualAdditionalRegistrationInformation.tsx +++ b/src/frontend/src/components/population/IndividualAdditionalRegistrationInformation/IndividualAdditionalRegistrationInformation.tsx @@ -84,22 +84,20 @@ export const IndividualAdditionalRegistrationInformation = ({ ); }); return ( - <div> - <Overview> - <Title> - <Typography variant="h6"> - {t('Additional Registration information')} - </Typography> - - - {fields.map((field, i) => ( - /* eslint-disable-next-line react/no-array-index-key */ - - {field} - - ))} - - - + + + <Typography variant="h6"> + {t('Additional Registration information')} + </Typography> + + + {fields.map((field, i) => ( + /* eslint-disable-next-line react/no-array-index-key */ + + {field} + + ))} + + ); }; diff --git a/src/frontend/src/components/population/IndividualAdditionalRegistrationInformation/__snapshots__/IndividualAdditionalRegistrationInformation.test.tsx.snap b/src/frontend/src/components/population/IndividualAdditionalRegistrationInformation/__snapshots__/IndividualAdditionalRegistrationInformation.test.tsx.snap index e3daad802a..4581aae885 100644 --- a/src/frontend/src/components/population/IndividualAdditionalRegistrationInformation/__snapshots__/IndividualAdditionalRegistrationInformation.test.tsx.snap +++ b/src/frontend/src/components/population/IndividualAdditionalRegistrationInformation/__snapshots__/IndividualAdditionalRegistrationInformation.test.tsx.snap @@ -2,86 +2,84 @@ exports[`components/population/IndividualAdditionalRegistrationInformation should render 1`] = `
-
+
-
-
- Additional Registration information -
-
+ Additional Registration information + +
+
-
-
+
+ + muac + +
- muac + 334 -
- - 334 - -
-
-
+
+
+
+ + school enrolled + +
- school enrolled + Yes -
- - Yes - -
-
-
+
+
+
+ + school enrolled before + +
- school enrolled before + No -
- - No - -
diff --git a/src/frontend/src/components/population/IndividualDeliveryMechanisms.tsx b/src/frontend/src/components/population/IndividualDeliveryMechanisms.tsx new file mode 100644 index 0000000000..cb0e7c5515 --- /dev/null +++ b/src/frontend/src/components/population/IndividualDeliveryMechanisms.tsx @@ -0,0 +1,66 @@ +import { DividerLine } from '@components/core/DividerLine'; +import { IndividualNode } from '@generated/graphql'; +import { Grid, Paper, Typography } from '@mui/material'; +import { Title } from '@core/Title'; +import { t } from 'i18next'; +import React from 'react'; +import styled from 'styled-components'; +import { LabelizedField } from '@components/core/LabelizedField'; +import { renderSomethingOrDash } from '@utils/utils'; +import { usePermissions } from '@hooks/usePermissions'; +import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; + +interface IndividualDeliveryMechanismsProps { + individual: IndividualNode; +} + +const Overview = styled(Paper)` + padding: ${({ theme }) => theme.spacing(8)} + ${({ theme }) => theme.spacing(11)}; + margin-top: ${({ theme }) => theme.spacing(6)}; + margin-bottom: ${({ theme }) => theme.spacing(4)}; +`; + +export const IndividualDeliveryMechanisms: React.FC< + IndividualDeliveryMechanismsProps +> = ({ individual }) => { + const permissions = usePermissions(); + const canViewDeliveryMechanisms = hasPermissions( + PERMISSIONS.POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION, + permissions, + ); + if (!individual.deliveryMechanismsData.length || !canViewDeliveryMechanisms) { + return null; + } + return ( + + + <Typography variant="h6"> + {t('Individual Delivery Mechanisms')} + </Typography> + + + {individual.deliveryMechanismsData.map((mechanism, index) => { + const tabData = JSON.parse(mechanism.individualTabData); + return ( + + {mechanism.name} + + {Object.entries(tabData).map(([key, value], idx) => ( + + + {renderSomethingOrDash(value)} + + + ))} + + {index < individual.deliveryMechanismsData.length - 1 && ( + + )} + + ); + })} + + + ); +}; diff --git a/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx b/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx index 257ffae326..f252491c4e 100644 --- a/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx +++ b/src/frontend/src/components/programs/ProgramDetails/ProgramDetails.tsx @@ -2,11 +2,7 @@ import { Box, Grid, Typography } from '@mui/material'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; -import { - ProgrammeChoiceDataQuery, - ProgramPartnerAccess, - ProgramQuery, -} from '@generated/graphql'; +import { ProgrammeChoiceDataQuery, ProgramQuery } from '@generated/graphql'; import { MiśTheme } from '../../../theme'; import { choicesToDict, programStatusToColor } from '@utils/utils'; import { ContainerColumnWithBorder } from '@core/ContainerColumnWithBorder'; diff --git a/src/frontend/src/config/permissions.ts b/src/frontend/src/config/permissions.ts index cb79a14011..44bf5a29ae 100644 --- a/src/frontend/src/config/permissions.ts +++ b/src/frontend/src/config/permissions.ts @@ -14,6 +14,8 @@ export const PERMISSIONS = { POPULATION_VIEW_HOUSEHOLDS_DETAILS: 'POPULATION_VIEW_HOUSEHOLDS_DETAILS', POPULATION_VIEW_INDIVIDUALS_LIST: 'POPULATION_VIEW_INDIVIDUALS_LIST', POPULATION_VIEW_INDIVIDUALS_DETAILS: 'POPULATION_VIEW_INDIVIDUALS_DETAILS', + POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION: + 'POPULATION_VIEW_INDIVIDUAL_DELIVERY_MECHANISMS_SECTION', // User Management USER_MANAGEMENT_VIEW_LIST: 'USER_MANAGEMENT_VIEW_LIST', diff --git a/src/frontend/src/containers/pages/population/PopulationIndividualsDetailsPage.tsx b/src/frontend/src/containers/pages/population/PopulationIndividualsDetailsPage.tsx index 3a6990b4fe..8f3283a23f 100644 --- a/src/frontend/src/containers/pages/population/PopulationIndividualsDetailsPage.tsx +++ b/src/frontend/src/containers/pages/population/PopulationIndividualsDetailsPage.tsx @@ -27,6 +27,7 @@ import { AdminButton } from '@core/AdminButton'; import { ProgrammeTimeSeriesFields } from '@components/population/ProgrammeTimeSeriesFields'; import { fetchPeriodicFields } from '@api/periodicDataUpdateApi'; import { useQuery } from '@tanstack/react-query'; +import { IndividualDeliveryMechanisms } from '@components/population/IndividualDeliveryMechanisms'; const Container = styled.div` padding: 20px; @@ -130,6 +131,9 @@ export const PopulationIndividualsDetailsPage = (): React.ReactElement => { choicesData={choicesData} grievancesChoices={grievancesChoices} /> + Date: Tue, 8 Oct 2024 14:07:34 +0200 Subject: [PATCH 094/202] add -x to selenium tests pytest invocation (#4300) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f1c2c97f1..4bf0f5c8ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -264,7 +264,7 @@ jobs: -f ./.github/helpers/docker-compose.selenium.yml \ run backend bash -c " waitforit -host=db -port=5432 -timeout=30 - pytest -svvv $extra_options ./tests/selenium --cov-report xml:test-coverage/coverage.xml --html-report=./tests/selenium/output_data/report/report.html --randomly-seed=42 + pytest -x -svvv $extra_options ./tests/selenium --cov-report xml:test-coverage/coverage.xml --html-report=./tests/selenium/output_data/report/report.html --randomly-seed=42 " - name: Upload Artifact uses: actions/upload-artifact@v4 From 3ab966653833dd3f6d4c28a9c28faf86c1f56691 Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk <34482854+mmaciekk@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:39:45 +0200 Subject: [PATCH 095/202] exclusion fix (#4290) Co-authored-by: Maciej Szewczyk --- .../ExcludeSection/ExcludeSection.tsx | 12 ++++++------ .../ExcludeSection/ExcludeSection.tsx | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/ExcludeSection/ExcludeSection.tsx b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/ExcludeSection/ExcludeSection.tsx index 55e8eecd9f..dd1f9a5cc5 100644 --- a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/ExcludeSection/ExcludeSection.tsx +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/ExcludeSection/ExcludeSection.tsx @@ -44,7 +44,7 @@ export function ExcludeSection({ excludeHouseholdError, } = paymentPlan; - const initialExcludedIds = paymentPlan?.excludedIndividuals?.map( + const initialExcludedIds = paymentPlan?.excludedHouseholds?.map( (el) => el.unicefId, ); const [isExclusionsOpen, setExclusionsOpen] = useState(initialOpen); @@ -67,7 +67,7 @@ export function ExcludeSection({ const getTooltipText = (): string => { if (!hasOpenOrLockedStatus) { return t( - 'Beneficiaries can only be excluded from a Payment Plan in status open or locked', + 'Households can only be excluded from a Payment Plan in status open or locked', ); } if (!hasExcludePermission) { @@ -115,7 +115,7 @@ export function ExcludeSection({ awaitRefetchQueries: true, }); if (!error) { - showMessage(t('Beneficiaries exclusion started')); + showMessage(t('Households exclusion started')); setExclusionsOpen(false); } } catch (e) { @@ -125,7 +125,7 @@ export function ExcludeSection({ const handleApply = (): void => { const idRegex = - /^(\s*IND-\d{2}-\d{4}\.\d{4}\s*)(,\s*IND-\d{2}-\d{4}\.\d{4}\s*)*$/; + /^(\s*HH-\d{2}-\d{4}\.\d{4}\s*)(,\s*HH-\d{2}-\d{4}\.\d{4}\s*)*$/; const ids = idsValue.trim().split(/,\s*|\s+/); const invalidIds: string[] = []; const alreadyExcludedIds: string[] = []; @@ -311,7 +311,7 @@ export function ExcludeSection({ {`${numberOfExcluded} ${ - numberOfExcluded === 1 ? 'Beneficiary' : 'Beneficiaries' + numberOfExcluded === 1 ? 'Household' : 'Households' } excluded`} diff --git a/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/ExcludeSection/ExcludeSection.tsx b/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/ExcludeSection/ExcludeSection.tsx index 2b86536934..f17af321fc 100644 --- a/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/ExcludeSection/ExcludeSection.tsx +++ b/src/frontend/src/components/paymentmodulepeople/PaymentPlanDetails/ExcludeSection/ExcludeSection.tsx @@ -67,7 +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', + 'Beneficiaries can only be excluded from a Payment Plan in status open or locked', ); } if (!hasExcludePermission) { @@ -115,7 +115,7 @@ export function ExcludeSection({ awaitRefetchQueries: true, }); if (!error) { - showMessage(t('Households exclusion started')); + showMessage(t('Beneficiaries exclusion started')); setExclusionsOpen(false); } } catch (e) { @@ -311,7 +311,7 @@ export function ExcludeSection({ {`${numberOfExcluded} ${ - numberOfExcluded === 1 ? 'Household' : 'Households' + numberOfExcluded === 1 ? 'Beneficiary' : 'Beneficiaries' } excluded`} From c318f2cb151d485261f292d9b1078fcc5870e28b Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk Date: Wed, 9 Oct 2024 15:54:37 +0200 Subject: [PATCH 096/202] fix display supporting docs --- .../SupportingDocumentsSection.tsx | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx index 4221c5d931..45b49b39a1 100644 --- a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx @@ -1,5 +1,6 @@ import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import styled from 'styled-components'; import DownloadIcon from '@mui/icons-material/Download'; import DeleteIcon from '@mui/icons-material/Delete'; import { PaperContainer } from '@components/targeting/PaperContainer'; @@ -38,6 +39,16 @@ import { GreyBox } from '@components/core/GreyBox'; import { BlueText } from '@components/grievances/LookUps/LookUpStyles'; import { useDownloadSupportingDocument } from './SupportingDocumentsSectionActions'; +const StyledBox = styled(Box)` + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + background-color: ${({ theme }) => theme.palette.grey[200]}; + padding: 4px; + margin-bottom: 4px; +`; + interface SupportingDocumentsSectionProps { initialOpen?: boolean; paymentPlan: PaymentPlanQuery['paymentPlan']; @@ -251,8 +262,12 @@ export const SupportingDocumentsSection = ({ alignItems="center" > - {doc.title} - {doc.file} + + {doc.title} + + + {doc.file} + {canDownloadFile && ( From 00bcfc3fb1db21b7866e03ed7bac5f0fd0767b1f Mon Sep 17 00:00:00 2001 From: Domenico Date: Thu, 10 Oct 2024 10:26:11 +0200 Subject: [PATCH 097/202] Disconnect Datahubs (#4266) * datahub removal final * remove datahub --- .github/helpers/.env-selenium | 4 - .github/helpers/.env-unit | 4 - .github/helpers/docker-compose.tst.yml | 1 - development_tools/.env.example | 4 - development_tools/compose.yml | 2 - development_tools/local_selenium_init.sh | 4 - .../postgres/init/init-multiple-db.sh | 9 +- pyproject.toml | 3 - .../apps/cash_assist_datahub/__init__.py | 0 .../apps/cash_assist_datahub/admin.py | 336 ---- .../apps/cash_assist_datahub/apps.py | 6 - .../apps/cash_assist_datahub/celery_tasks.py | 35 - .../apps/cash_assist_datahub/fixtures.py | 159 -- .../management/commands/create_sessions.py | 49 - .../0001_migration_squashed_0015_migration.py | 146 -- .../migrations/0016_migration.py | 18 - .../migrations/0017_migration.py | 18 - .../migrations/0018_migration.py | 18 - .../migrations/0019_migration.py | 18 - .../migrations/0020_migration.py | 18 - .../migrations/0021_migration.py | 19 - .../migrations/__init__.py | 0 .../apps/cash_assist_datahub/models.py | 177 --- .../tasks/pull_from_datahub.py | 282 ---- .../payment_record/inspect.html | 33 - .../cash_assist_datahub/session/debug.html | 77 - .../cash_assist_datahub/session/inspect.html | 102 -- src/hct_mis_api/apps/core/dbrouters.py | 2 - .../commands/createadminpermissiongroups.py | 22 +- .../management/commands/generatefixtures.py | 21 - .../core/management/commands/initcypress.py | 4 - .../apps/core/management/commands/initdemo.py | 10 +- src/hct_mis_api/apps/core/tasks_schedules.py | 12 - src/hct_mis_api/apps/erp_datahub/__init__.py | 0 src/hct_mis_api/apps/erp_datahub/admin.py | 265 ---- src/hct_mis_api/apps/erp_datahub/apps.py | 6 - .../apps/erp_datahub/celery_tasks.py | 23 - src/hct_mis_api/apps/erp_datahub/fixtures.py | 27 - .../0001_migration_squashed_0017_migration.py | 70 - .../erp_datahub/migrations/0018_migration.py | 38 - .../erp_datahub/migrations/0019_migration.py | 38 - .../erp_datahub/migrations/0020_migration.py | 18 - .../erp_datahub/migrations/0021_migration.py | 22 - .../apps/erp_datahub/migrations/__init__.py | 0 src/hct_mis_api/apps/erp_datahub/models.py | 96 -- .../apps/erp_datahub/tasks/__init__.py | 0 .../tasks/pull_from_erp_datahub.py | 58 - .../erp_datahub/tasks/sync_to_mis_datahub.py | 64 - .../down_payment/assign_business_office.html | 38 - .../assign_business_office.html | 38 - .../admin/erp_datahub/session/inspect.html | 60 - src/hct_mis_api/apps/erp_datahub/utils.py | 43 - src/hct_mis_api/apps/mis_datahub/__init__.py | 0 src/hct_mis_api/apps/mis_datahub/admin.py | 311 ---- src/hct_mis_api/apps/mis_datahub/apps.py | 6 - .../apps/mis_datahub/celery_tasks.py | 27 - .../0001_migration_squashed_0046_migration.py | 1365 ----------------- .../mis_datahub/migrations/0047_migration.py | 18 - .../mis_datahub/migrations/0048_migration.py | 18 - .../mis_datahub/migrations/0049_migration.py | 22 - .../mis_datahub/migrations/0050_migration.py | 17 - .../mis_datahub/migrations/0051_migration.py | 38 - .../mis_datahub/migrations/0052_migration.py | 38 - .../mis_datahub/migrations/0053_migration.py | 18 - .../mis_datahub/migrations/0054_migration.py | 22 - .../apps/mis_datahub/migrations/__init__.py | 0 src/hct_mis_api/apps/mis_datahub/models.py | 293 ---- .../apps/mis_datahub/tasks/__init__.py | 0 .../mis_datahub/tasks/send_tp_to_datahub.py | 321 ---- .../admin/mis_datahub/session/inspect.html | 59 - src/hct_mis_api/apps/targeting/mutations.py | 4 - src/hct_mis_api/config/fragments/constance.py | 1 - .../config/fragments/smart_admin.py | 9 - src/hct_mis_api/config/settings.py | 42 +- tests/unit/apps/administration/test_admin.py | 7 - .../unit/apps/cash_assist_datahub/__init__.py | 0 .../test_pull_from_datahub.py | 392 ----- tests/unit/apps/erp_datahub/__init__.py | 0 .../erp_datahub/test_pull_from_erp_datahub.py | 158 -- .../erp_datahub/test_sync_to_mis_datahub.py | 185 --- tests/unit/apps/mis_datahub/__init__.py | 0 .../test_data_send_tp_to_datahub.py | 256 ---- ...t_external_collector_send_tp_to_datahub.py | 481 ------ .../mis_datahub/test_send_tp_to_datahub.py | 411 ----- ...t_recalculating_household_cash_received.py | 293 ---- .../registration_datahub/test_celery_tasks.py | 6 - 86 files changed, 4 insertions(+), 7331 deletions(-) delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/__init__.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/admin.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/apps.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/celery_tasks.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/fixtures.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/management/commands/create_sessions.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/migrations/0001_migration_squashed_0015_migration.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/migrations/0016_migration.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/migrations/0017_migration.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/migrations/0018_migration.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/migrations/0019_migration.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/migrations/0020_migration.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/migrations/0021_migration.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/migrations/__init__.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/models.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/tasks/pull_from_datahub.py delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/payment_record/inspect.html delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/session/debug.html delete mode 100644 src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/session/inspect.html delete mode 100644 src/hct_mis_api/apps/erp_datahub/__init__.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/admin.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/apps.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/celery_tasks.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/fixtures.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/migrations/0001_migration_squashed_0017_migration.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/migrations/0018_migration.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/migrations/0019_migration.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/migrations/0020_migration.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/migrations/0021_migration.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/migrations/__init__.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/models.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/tasks/__init__.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/tasks/pull_from_erp_datahub.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/tasks/sync_to_mis_datahub.py delete mode 100644 src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/down_payment/assign_business_office.html delete mode 100644 src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/funds_commitment/assign_business_office.html delete mode 100644 src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/session/inspect.html delete mode 100644 src/hct_mis_api/apps/erp_datahub/utils.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/__init__.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/admin.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/apps.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/celery_tasks.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/0001_migration_squashed_0046_migration.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/0047_migration.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/0048_migration.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/0049_migration.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/0050_migration.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/0051_migration.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/0052_migration.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/0053_migration.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/0054_migration.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/migrations/__init__.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/models.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/tasks/__init__.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/tasks/send_tp_to_datahub.py delete mode 100644 src/hct_mis_api/apps/mis_datahub/templates/admin/mis_datahub/session/inspect.html delete mode 100644 tests/unit/apps/cash_assist_datahub/__init__.py delete mode 100644 tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py delete mode 100644 tests/unit/apps/erp_datahub/__init__.py delete mode 100644 tests/unit/apps/erp_datahub/test_pull_from_erp_datahub.py delete mode 100644 tests/unit/apps/erp_datahub/test_sync_to_mis_datahub.py delete mode 100644 tests/unit/apps/mis_datahub/__init__.py delete mode 100644 tests/unit/apps/mis_datahub/test_data_send_tp_to_datahub.py delete mode 100644 tests/unit/apps/mis_datahub/test_external_collector_send_tp_to_datahub.py delete mode 100644 tests/unit/apps/mis_datahub/test_send_tp_to_datahub.py delete mode 100644 tests/unit/apps/payment/test_recalculating_household_cash_received.py diff --git a/.github/helpers/.env-selenium b/.github/helpers/.env-selenium index 99acb3b9ef..9ab01e554a 100644 --- a/.github/helpers/.env-selenium +++ b/.github/helpers/.env-selenium @@ -7,9 +7,5 @@ CELERY_BROKER_URL=redis://redis:6379/0 CELERY_RESULT_BACKEND=redis://redis:6379/0 CACHE_LOCATION=redis://redis:6379/1 DATABASE_URL=postgis://postgres:postgres@db:5432/postgres -DATABASE_URL_HUB_MIS=postgis://postgres:postgres@db:5432/mis_datahub -DATABASE_URL_HUB_CA=postgis://postgres:postgres@db:5432/ca_datahub -DATABASE_URL_HUB_ERP=postgis://postgres:postgres@db:5432/erp_datahub -DATABASE_URL_HUB_REGISTRATION=postgis://postgres:postgres@db:5432/rdi_datahub 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 0f765bbb08..26694b4fa9 100644 --- a/.github/helpers/.env-unit +++ b/.github/helpers/.env-unit @@ -4,10 +4,6 @@ POSTGRES_DB=postgres POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres DATABASE_URL=postgis://postgres:postgres@db:5432/postgres -DATABASE_URL_HUB_MIS=postgis://postgres:postgres@db:5432/mis_datahub -DATABASE_URL_HUB_CA=postgis://postgres:postgres@db:5432/ca_datahub -DATABASE_URL_HUB_ERP=postgis://postgres:postgres@db:5432/erp_datahub -DATABASE_URL_HUB_REGISTRATION=postgis://postgres:postgres@db:5432/rdi_datahub POSTGRES_SSL_MODE=off EMAIL_HOST=TBD EMAIL_HOST_USER=TBD diff --git a/.github/helpers/docker-compose.tst.yml b/.github/helpers/docker-compose.tst.yml index 2e2be2f001..2bfc760ba3 100644 --- a/.github/helpers/docker-compose.tst.yml +++ b/.github/helpers/docker-compose.tst.yml @@ -17,7 +17,6 @@ services: volumes: - ../../development_tools/postgres/init:/docker-entrypoint-initdb.d environment: - - POSTGRES_MULTIPLE_DATABASES=unicef_hct_mis_cashassist,rdi_datahub,mis_datahub,erp_datahub,ca_datahub - POSTGRES_DB=postgres - POSTGRES_USER=postgres - POSTGRES_PASS=postgres diff --git a/development_tools/.env.example b/development_tools/.env.example index 68bac69605..ef548433db 100644 --- a/development_tools/.env.example +++ b/development_tools/.env.example @@ -10,10 +10,6 @@ POSTGRES_PASS=postgres PGUSER=postgres POSTGRES_HOST_AUTH_METHOD=trust DATABASE_URL=postgis://postgres:postgres@db:5432/postgres -DATABASE_URL_HUB_MIS=postgis://postgres:postgres@db:5432/mis_datahub -DATABASE_URL_HUB_CA=postgis://postgres:postgres@db:5432/ca_datahub -DATABASE_URL_HUB_ERP=postgis://postgres:postgres@db:5432/erp_datahub -DATABASE_URL_HUB_REGISTRATION=postgis://postgres:postgres@db:5432/rdi_datahub POSTGRES_SSL_MODE=off EMAIL_HOST=TBD EMAIL_HOST_USER=TBD diff --git a/development_tools/compose.yml b/development_tools/compose.yml index 943c62bc7c..65a4cf84d4 100644 --- a/development_tools/compose.yml +++ b/development_tools/compose.yml @@ -119,8 +119,6 @@ services: volumes: - db:/var/lib/postgresql/data - ./postgres/init:/docker-entrypoint-initdb.d - environment: - POSTGRES_MULTIPLE_DATABASES: unicef_hct_mis_cashassist,rdi_datahub,mis_datahub,erp_datahub,ca_datahub env_file: - .env ports: diff --git a/development_tools/local_selenium_init.sh b/development_tools/local_selenium_init.sh index b175319558..1e5066f680 100755 --- a/development_tools/local_selenium_init.sh +++ b/development_tools/local_selenium_init.sh @@ -11,10 +11,6 @@ export POSTGRES_PASS=postgres export PGUSER=postgres export POSTGRES_HOST_AUTH_METHOD=trust export DATABASE_URL=postgis://postgres:postgres@localhost:5432/postgres -export DATABASE_URL_HUB_MIS=postgis://postgres:postgres@localhost:5432/mis_datahub -export DATABASE_URL_HUB_CA=postgis://postgres:postgres@localhost:5432/ca_datahub -export DATABASE_URL_HUB_ERP=postgis://postgres:postgres@localhost:5432/erp_datahub -export DATABASE_URL_HUB_REGISTRATION=postgis://postgres:postgres@localhost:5432/rdi_datahub export POSTGRES_SSL_MODE=off export EMAIL_HOST=TBD export EMAIL_HOST_USER=TBD diff --git a/development_tools/postgres/init/init-multiple-db.sh b/development_tools/postgres/init/init-multiple-db.sh index 04aa25a2e4..8b9583eb60 100755 --- a/development_tools/postgres/init/init-multiple-db.sh +++ b/development_tools/postgres/init/init-multiple-db.sh @@ -16,11 +16,4 @@ EOSQL CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder; EOSQL -} -if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then - echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" - for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do - create_user_and_database $db - done - echo "Multiple databases created" -fi \ No newline at end of file +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 7d5ef6e541..394fb9da5f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,9 +39,6 @@ known_first_party = [ "mptt", "django_extensions", "registration_data", - "cash_assist_datahub", - "mis_datahub", - "erp_datahub", "sanction_list", "accountability", ] diff --git a/src/hct_mis_api/apps/cash_assist_datahub/__init__.py b/src/hct_mis_api/apps/cash_assist_datahub/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/apps/cash_assist_datahub/admin.py b/src/hct_mis_api/apps/cash_assist_datahub/admin.py deleted file mode 100644 index 9997ec7d5f..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/admin.py +++ /dev/null @@ -1,336 +0,0 @@ -import logging -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union -from uuid import UUID - -from django.conf import settings -from django.contrib import admin, messages -from django.contrib.admin.templatetags.admin_urls import admin_urlname -from django.contrib.messages import DEFAULT_TAGS -from django.db import transaction -from django.template.response import TemplateResponse -from django.urls import reverse -from django.utils import timezone -from django.utils.safestring import mark_safe - -from admin_extra_buttons.api import button -from admin_extra_buttons.decorators import link -from admin_extra_buttons.mixins import confirm_action -from adminfilters.filters import ChoicesFieldComboFilter, ValueFilter -from smart_admin.mixins import LinkedObjectsMixin - -from hct_mis_api.apps.cash_assist_datahub.models import ( - CashPlan, - PaymentRecord, - Programme, - ServiceProvider, - Session, - TargetPopulation, -) -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.household import models as people -from hct_mis_api.apps.payment import models as payment -from hct_mis_api.apps.program import models as program -from hct_mis_api.apps.targeting import models as targeting -from hct_mis_api.apps.utils.admin import HOPEModelAdminBase -from hct_mis_api.apps.utils.admin import HUBBusinessAreaFilter as BusinessAreaFilter - -if TYPE_CHECKING: - from datetime import timedelta - - from django.http import HttpRequest - - from hct_mis_api.apps.utils.models import AbstractSession - - -logger = logging.getLogger(__name__) - -MINUTE = 60 -HOUR = MINUTE * 60 -DAY = HOUR * 24 - - -class RollbackException(Exception): - pass - - -@admin.register(Session) -class SessionAdmin(HOPEModelAdminBase): - list_display = ("timestamp", "id", "status", "last_modified_date", "business_area", "run_time") - date_hierarchy = "timestamp" - list_filter = ( - BusinessAreaFilter, - ("status", ChoicesFieldComboFilter), - "source", - "last_modified_date", - ) - ordering = ("-timestamp",) - exclude = ("traceback",) - readonly_fields = ("timestamp", "last_modified_date", "sentry_id", "source", "business_area") - - def run_time(self, obj: "AbstractSession") -> Optional["timedelta"]: - if obj.status in (obj.STATUS_PROCESSING, obj.STATUS_LOADING): - elapsed = timezone.now() - obj.timestamp - if elapsed.total_seconds() >= HOUR: - return elapsed - return None - - @button(permission="account.can_debug") - def pull(self, request: "HttpRequest") -> None: - from hct_mis_api.apps.cash_assist_datahub.tasks.pull_from_datahub import ( - PullFromDatahubTask, - ) - - try: - ret = PullFromDatahubTask().execute() - if ret["failures"]: - raise Exception(ret) - else: - self.message_user(request, ret, messages.SUCCESS) - except Exception as e: - msg = f"{e.__class__.__name__}: {str(e)}" - self.message_user(request, msg, messages.ERROR) - - @button() - def simulate_import(self, request: "HttpRequest", pk: "UUID") -> Optional[TemplateResponse]: - context = self.get_common_context(request, pk, title="Test Import") - session: Session = context["original"] - if request.method == "POST": - from hct_mis_api.apps.cash_assist_datahub.tasks.pull_from_datahub import ( - PullFromDatahubTask, - ) - - runner = PullFromDatahubTask() - try: - with transaction.atomic(), transaction.atomic(using="cash_assist_datahub_ca"): - runner.copy_session(session) - raise RollbackException() - except RollbackException: - self.message_user(request, "Test Completed", messages.SUCCESS) - except Exception as e: - msg = f"{e.__class__.__name__}: {str(e)}" - if session.sentry_id: - url = f"{settings.SENTRY_URL}?query={session.sentry_id}" - sentry_url = f'View on Sentry' - msg = mark_safe(f"{msg} - {sentry_url}") - - self.message_user(request, msg, messages.ERROR) - logger.exception(e) - - else: - return confirm_action( - self, - request, - self.simulate_import, - mark_safe( - """

DO NOT CONTINUE IF YOU ARE NOT SURE WHAT YOU ARE DOING

-

Import will only be simulated

- """ - ), - "Successfully executed", - ) - - return None - - @link(html_attrs={"target": "_new"}, permission="account.can_debug") - def view_error_on_sentry(self, button: button) -> Optional[Union[str, bool]]: - if "original" in button.context: - obj = button.context["original"] - if obj.sentry_id: - return f"{settings.SENTRY_URL}?query={obj.sentry_id}" - - button.visible = False - return None - - @button(visible=lambda btn: btn.original.traceback, permission="account.can_debug") - def view_error(self, request: "HttpRequest", pk: "UUID") -> TemplateResponse: - context = self.get_common_context(request, pk) - return TemplateResponse(request, "admin/cash_assist_datahub/session/debug.html", context) - - @button(permission="account.can_inspect") - def inspect(self, request: "HttpRequest", pk: "UUID") -> TemplateResponse: - context: Dict[str, Any] = self.get_common_context(request, pk) - obj: Session = context["original"] - context["title"] = f"Session {obj.pk} - {obj.timestamp} - {obj.status}" - context["data"] = {} - warnings: List[List] = [] - errors = 0 - has_content: bool = False - if settings.SENTRY_URL and obj.sentry_id: - context["sentry_url"] = f"{settings.SENTRY_URL}?query={obj.sentry_id}" - - if obj.status == obj.STATUS_EMPTY: - warnings.append([messages.WARNING, "Session is empty"]) - elif obj.status == obj.STATUS_FAILED: - warnings.append([messages.ERROR, "Session is failed"]) - elif obj.status == obj.STATUS_PROCESSING: - elapsed = timezone.now() - obj.timestamp - if elapsed.total_seconds() >= DAY: - warnings.append([messages.ERROR, f"Session is running more than {elapsed}"]) - elif elapsed.total_seconds() >= HOUR: - warnings.append([messages.WARNING, f"Session is running more than {elapsed}"]) - - for model in (Programme, CashPlan, TargetPopulation, PaymentRecord, ServiceProvider): - count = model.objects.filter(session=pk).count() - has_content = has_content or bool(count) - context["data"][model] = {"count": count, "warnings": [], "errors": [], "meta": model._meta} - - for prj in Programme.objects.filter(session=pk): - if not program.Program.objects.filter(id=prj.mis_id).exists(): - errors += 1 - context["data"][Programme]["errors"].append(f"Program {prj.mis_id} not found in HOPE") - - for tp in TargetPopulation.objects.filter(session=pk): - if not targeting.TargetPopulation.objects.filter(id=tp.mis_id).exists(): - errors += 1 - context["data"][TargetPopulation]["errors"].append(f"TargetPopulation {tp.mis_id} not found in HOPE") - - svs = [] - for sv in ServiceProvider.objects.filter(session=pk): - svs.append(sv.ca_id) - - session_cash_plans = CashPlan.objects.filter(session=pk).values_list("cash_plan_id", flat=True) - hope_cash_plans = payment.CashPlan.objects.filter(business_area__code=obj.business_area).values_list( - "ca_id", flat=True - ) - known_cash_plans = list(session_cash_plans) + list(hope_cash_plans) - - for pr in PaymentRecord.objects.filter(session=pk): - if pr.service_provider_ca_id not in svs: - errors += 1 - context["data"][PaymentRecord]["errors"].append( - f"PaymentRecord uses ServiceProvider {pr.service_provider_ca_id} that is not present in the Session" - ) - if pr.cash_plan_ca_id not in known_cash_plans: - errors += 1 - context["data"][PaymentRecord]["errors"].append( - f"PaymentRecord is part of an unknown CashPlan {pr.cash_plan_ca_id}" - ) - - if not people.Household.objects.filter(id=pr.household_mis_id).exists(): - errors += 1 - context["data"][PaymentRecord]["errors"].append( - f"PaymentRecord {pr.id} refers to unknown Household {pr.household_mis_id}" - ) - - if errors: - warnings.append([messages.ERROR, f"{errors} Errors found"]) - - if (obj.status == obj.STATUS_EMPTY) and has_content: - warnings.append([messages.ERROR, "Session marked as Empty but records found"]) - - area = BusinessArea.objects.filter(code=obj.business_area.strip()).first() - context["area"] = area - if not area: - warnings.append([messages.ERROR, "Invalid Business Area"]) - - context["warnings"] = [(DEFAULT_TAGS[w[0]], w[1]) for w in warnings] - return TemplateResponse(request, "admin/cash_assist_datahub/session/inspect.html", context) - - -@admin.register(CashPlan) -class CashPlanAdmin(HOPEModelAdminBase): - list_display = ("session", "name", "status", "business_area", "cash_plan_id") - list_filter = ( - "status", - ("cash_plan_id", ValueFilter), - ("session__id", ValueFilter), - BusinessAreaFilter, - ) - date_hierarchy = "session__timestamp" - raw_id_fields = ("session",) - - @link() - def payment_records(self, button: button) -> Optional[Union[str, bool]]: - if "original" in button.context: - obj = button.context["original"] - url = reverse("admin:cash_assist_datahub_paymentrecord_changelist") - return f"{url}?cash_plan_ca_id|iexact={obj.cash_plan_id}" - else: - button.visible = False - - return None - - -@admin.register(PaymentRecord) -class PaymentRecordAdmin(LinkedObjectsMixin, HOPEModelAdminBase): - list_display = ("session", "business_area", "status", "full_name", "service_provider_ca_id") - raw_id_fields = ("session",) - date_hierarchy = "session__timestamp" - list_filter = ( - "status", - "delivery_type", - "service_provider_ca_id", - ("ca_id", ValueFilter), - ("cash_plan_ca_id", ValueFilter), - ("session__id", ValueFilter), - BusinessAreaFilter, - ) - - @button(permission="account.can_inspect") - def inspect(self, request: "HttpRequest", pk: "UUID") -> TemplateResponse: - opts = self.model._meta - payment_record: PaymentRecord = PaymentRecord.objects.get(pk=pk) - ctx = { - "opts": opts, - "app_label": "account", - "change": True, - "is_popup": False, - "save_as": False, - "has_delete_permission": False, - "has_add_permission": False, - "has_change_permission": True, - "original": payment_record, - "data": {}, - } - from hct_mis_api.apps.core.models import BusinessArea - from hct_mis_api.apps.household.models import Household, Individual - - # ba = BusinessArea.objects.get(code=payment_record.business_area) - for field_name, model, rk in ( - ("business_area", BusinessArea, "code"), - ("household_mis_id", Household, "pk"), - ("head_of_household_mis_id", Individual, "pk"), - ("target_population_mis_id", TargetPopulation, "pk"), - ): - instance = model.objects.filter(**{rk: getattr(payment_record, field_name)}).first() - details = None - if instance: - details = reverse(admin_urlname(model._meta, "change"), args=[instance.pk]) # type: ignore # str vs SafeString? - ctx["data"][model] = {"instance": instance, "details": details, "meta": model._meta} - - return TemplateResponse(request, "admin/cash_assist_datahub/payment_record/inspect.html", ctx) - - -@admin.register(ServiceProvider) -class ServiceProviderAdmin(HOPEModelAdminBase): - list_display = ("session", "business_area", "full_name", "short_name", "country") - raw_id_fields = ("session",) - date_hierarchy = "session__timestamp" - search_fields = ("full_name",) - list_filter = (("session__id", ValueFilter), BusinessAreaFilter) - - -@admin.register(Programme) -class ProgrammeAdmin(HOPEModelAdminBase): - list_display = ("session", "mis_id", "ca_id", "ca_hash_id") - raw_id_fields = ("session",) - date_hierarchy = "session__timestamp" - list_filter = ( - ("session__id", ValueFilter), - ("ca_hash_id", ValueFilter), - ("mis_id", ValueFilter), - ("ca_id", ValueFilter), - ) - - -@admin.register(TargetPopulation) -class TargetPopulationAdmin(HOPEModelAdminBase): - list_display = ("session", "mis_id", "ca_id", "ca_hash_id") - raw_id_fields = ("session",) - date_hierarchy = "session__timestamp" - list_filter = ( - ("session__id", ValueFilter), - ("ca_hash_id", ValueFilter), - ("mis_id", ValueFilter), - ("ca_id", ValueFilter), - ) diff --git a/src/hct_mis_api/apps/cash_assist_datahub/apps.py b/src/hct_mis_api/apps/cash_assist_datahub/apps.py deleted file mode 100644 index e2a4c8fe7a..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class Config(AppConfig): - name = "hct_mis_api.apps.cash_assist_datahub" - verbose_name = "HUB CA (CA->Hope)" diff --git a/src/hct_mis_api/apps/cash_assist_datahub/celery_tasks.py b/src/hct_mis_api/apps/cash_assist_datahub/celery_tasks.py deleted file mode 100644 index b0d42c5ea8..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/celery_tasks.py +++ /dev/null @@ -1,35 +0,0 @@ -import logging -from typing import Any - -from hct_mis_api.apps.core.celery import app -from hct_mis_api.apps.core.exchange_rates.utils import fix_exchange_rates -from hct_mis_api.apps.utils.logs import log_start_and_end -from hct_mis_api.apps.utils.sentry import sentry_tags - -logger = logging.getLogger(__name__) - - -@app.task(bind=True, queue="priority", default_retry_delay=60, max_retries=3) -@log_start_and_end -@sentry_tags -def pull_from_cashassist_datahub_task(self: Any) -> None: - try: - from hct_mis_api.apps.cash_assist_datahub.tasks.pull_from_datahub import ( - PullFromDatahubTask, - ) - - PullFromDatahubTask().execute() - except Exception as e: - logger.exception(e) - raise self.retry(exc=e) - - -@app.task(bind=True, queue="priority", default_retry_delay=60, max_retries=3) -@log_start_and_end -@sentry_tags -def fix_exchange_rates_task(self: Any) -> None: - try: - fix_exchange_rates() - except Exception as e: - logger.exception(e) - raise self.retry(exc=e) diff --git a/src/hct_mis_api/apps/cash_assist_datahub/fixtures.py b/src/hct_mis_api/apps/cash_assist_datahub/fixtures.py deleted file mode 100644 index 38dbf9af48..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/fixtures.py +++ /dev/null @@ -1,159 +0,0 @@ -from datetime import timedelta -from random import randint - -import factory -from factory import fuzzy -from factory.django import DjangoModelFactory -from pytz import utc - -from hct_mis_api.apps.cash_assist_datahub.models import ( - CashPlan, - PaymentRecord, - Programme, - ServiceProvider, -) -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.household.models import Household -from hct_mis_api.apps.payment import models as payment_models -from hct_mis_api.apps.payment.delivery_mechanisms import DeliveryMechanismChoices -from hct_mis_api.apps.program import models as program_models -from hct_mis_api.apps.targeting.models import TargetPopulation - - -class ServiceProviderFactory(DjangoModelFactory): - class Meta: - model = ServiceProvider - - business_area = factory.LazyAttribute(lambda o: BusinessArea.objects.first().cash_assist_code) - ca_id = factory.Faker("uuid4") - full_name = factory.Faker("company") - short_name = factory.LazyAttribute(lambda o: o.full_name[0:3]) - country = factory.Faker("country_code") - vision_id = factory.Faker("uuid4") - - -class PaymentRecordFactory(DjangoModelFactory): - class Meta: - model = PaymentRecord - - business_area = factory.LazyAttribute(lambda o: BusinessArea.objects.first().cash_assist_code) - status = fuzzy.FuzzyChoice( - payment_models.PaymentRecord.STATUS_CHOICE, - getter=lambda c: c[0], - ) - full_name = factory.Faker("name") - status_date = factory.Faker( - "date_time_this_decade", - before_now=True, - after_now=False, - tzinfo=utc, - ) - ca_id = factory.Faker("uuid4") - ca_hash_id = factory.Faker("uuid4") - household_mis_id = factory.LazyAttribute(lambda o: Household.objects.order_by("?").first().id) - 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_mis_id = factory.LazyAttribute(lambda o: TargetPopulation.objects.order_by("?").first().id) - entitlement_card_number = factory.Faker("ssn") - entitlement_card_status = fuzzy.FuzzyChoice( - payment_models.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, - ) - delivery_type = fuzzy.FuzzyChoice( - DeliveryMechanismChoices.DELIVERY_TYPE_CHOICES, - getter=lambda c: c[0], - ) - currency = factory.Faker("currency_code") - entitlement_quantity = factory.fuzzy.FuzzyDecimal(100.0, 10000.0) - delivered_quantity = 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_ca_id = factory.LazyAttribute(lambda o: ServiceProvider.objects.order_by("?").first().ca_id) - - -class CashPlanFactory(DjangoModelFactory): - class Meta: - model = CashPlan - - business_area = factory.LazyAttribute(lambda o: BusinessArea.objects.first().cash_assist_code) - - program_mis_id = factory.LazyAttribute(lambda o: program_models.Program.objects.order_by("?").first().id) - cash_plan_id = factory.Faker("uuid4") - cash_plan_hash_id = factory.Faker("uuid4") - status_date = factory.Faker( - "date_time_this_decade", - before_now=True, - after_now=False, - tzinfo=utc, - ) - status = 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" - start_date = factory.Faker( - "date_time_this_decade", - before_now=True, - after_now=False, - tzinfo=utc, - ) - end_date = factory.LazyAttribute(lambda o: o.start_date + timedelta(days=randint(60, 1000))) - dispersion_date = factory.LazyAttribute(lambda o: o.start_date + timedelta(days=randint(60, 1000))) - 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 = 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) - 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) - - -class ProgrammeFactory(DjangoModelFactory): - mis_id = None - ca_id = factory.Faker("uuid4") - ca_hash_id = factory.Faker("uuid4") - - class Meta: - model = Programme diff --git a/src/hct_mis_api/apps/cash_assist_datahub/management/commands/create_sessions.py b/src/hct_mis_api/apps/cash_assist_datahub/management/commands/create_sessions.py deleted file mode 100644 index 0a1e977323..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/management/commands/create_sessions.py +++ /dev/null @@ -1,49 +0,0 @@ -from typing import Any -from uuid import UUID - -from django.core.management import BaseCommand - -import hct_mis_api.apps.cash_assist_datahub.fixtures as ca_fixtures -import hct_mis_api.apps.payment.fixtures as payment_fixtures -from hct_mis_api.apps.cash_assist_datahub.models import Session -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.household.fixtures import create_household -from hct_mis_api.apps.payment.fixtures import CashPlanFactory - - -class Command(BaseCommand): - def handle(self, *args: Any, **options: Any) -> None: - business_areas = BusinessArea.objects.all() - some_business_area = business_areas.order_by("?").first() - - some_session = Session.objects.get_or_create( - business_area=some_business_area.cash_assist_code, status=Session.STATUS_READY - ) - - service_provider_ca_id = UUID("00000000-0000-0000-0000-000000000000") - cash_plan_ca_id = UUID("00000000-0000-0000-0000-000000000001") - - payment_fixtures.ServiceProviderFactory.create(ca_id=service_provider_ca_id) - - household, _ = create_household( - { - "size": 1, - "residence_status": "HOST", - "business_area": BusinessArea.objects.get(code=some_session.business_area), - "total_cash_received": None, - "total_cash_received_usd": None, - }, - ) - ca_fixtures.PaymentRecordFactory.create( - session=some_session, - service_provider_ca_id=service_provider_ca_id, - cash_plan_ca_id=cash_plan_ca_id, - household_mis_id=household.id, - ) - payment_fixtures.PaymentRecordFactory.create( - household=household, - delivered_quantity=1000, - delivered_quantity_usd=2000, - ) - - CashPlanFactory.create(ca_id=cash_plan_ca_id) diff --git a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0001_migration_squashed_0015_migration.py b/src/hct_mis_api/apps/cash_assist_datahub/migrations/0001_migration_squashed_0015_migration.py deleted file mode 100644 index 1ffb0904eb..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0001_migration_squashed_0015_migration.py +++ /dev/null @@ -1,146 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-08 20:28 - -from decimal import Decimal -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='Session', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ('source', models.CharField(choices=[('MIS', 'HCT-MIS'), ('CA', 'Cash Assist')], max_length=3)), - ('status', models.CharField(choices=[('NEW', 'New'), ('READY', 'Ready'), ('PROCESSING', 'Processing'), ('COMPLETED', 'Completed'), ('FAILED', 'Failed'), ('EMPTY', 'Empty'), ('IGNORED', 'Ignored'), ('LOADING', 'Loading'), ('ERRORED', 'Errored')], max_length=11)), - ('last_modified_date', models.DateTimeField(auto_now=True)), - ('business_area', models.CharField(default='0060', help_text='Same as the business area set on program, but\n this is set as the same value, and all other\n models this way can get easy access to the business area\n via the session.', max_length=20)), - ('sentry_id', models.CharField(blank=True, default='', max_length=100, null=True)), - ('traceback', models.TextField(blank=True, default='', null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='CashPlan', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('business_area', models.CharField(max_length=20, null=True)), - ('cash_plan_id', models.CharField(max_length=255)), - ('cash_plan_hash_id', models.UUIDField()), - ('status', models.CharField(max_length=255, null=True)), - ('status_date', models.DateTimeField(null=True)), - ('name', models.CharField(max_length=255, null=True)), - ('distribution_level', models.CharField(max_length=255, null=True)), - ('start_date', models.DateTimeField(null=True)), - ('end_date', models.DateTimeField(null=True)), - ('dispersion_date', models.DateTimeField(null=True)), - ('coverage_duration', models.PositiveIntegerField(null=True)), - ('coverage_unit', models.CharField(max_length=255, null=True)), - ('comments', models.CharField(max_length=255, null=True)), - ('program_mis_id', models.UUIDField(null=True)), - ('delivery_type', models.CharField(max_length=255, null=True)), - ('assistance_measurement', models.CharField(max_length=255, null=True)), - ('assistance_through', models.CharField(max_length=255, null=True)), - ('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(null=True)), - ('total_persons_covered', models.IntegerField(null=True)), - ('total_persons_covered_revised', models.IntegerField(null=True)), - ('payment_records_count', models.IntegerField(null=True)), - ('total_entitled_quantity', models.DecimalField(decimal_places=2, max_digits=12, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))])), - ('total_entitled_quantity_revised', models.DecimalField(decimal_places=2, max_digits=12, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))])), - ('total_delivered_quantity', models.DecimalField(decimal_places=2, max_digits=12, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))])), - ('total_undelivered_quantity', models.DecimalField(decimal_places=2, max_digits=12, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))])), - ('session', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cash_assist_datahub.session')), - ], - options={ - 'unique_together': {('session', 'cash_plan_id')}, - }, - ), - migrations.CreateModel( - name='PaymentRecord', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('business_area', models.CharField(max_length=20, null=True)), - ('status', models.CharField(max_length=255, null=True)), - ('status_date', models.DateTimeField(null=True)), - ('ca_id', models.CharField(max_length=255)), - ('ca_hash_id', models.UUIDField()), - ('registration_ca_id', models.CharField(max_length=255, null=True)), - ('household_mis_id', models.UUIDField(null=True)), - ('head_of_household_mis_id', models.UUIDField(null=True)), - ('full_name', models.CharField(max_length=255, null=True)), - ('total_persons_covered', models.IntegerField(null=True)), - ('distribution_modality', models.CharField(max_length=255, null=True)), - ('target_population_mis_id', models.UUIDField(null=True)), - ('target_population_cash_assist_id', models.CharField(max_length=255, null=True)), - ('entitlement_card_number', models.CharField(max_length=255, null=True)), - ('entitlement_card_status', models.CharField(max_length=20, null=True)), - ('entitlement_card_issue_date', models.DateField(null=True)), - ('delivery_type', models.CharField(choices=[('Cardless cash withdrawal', 'Cardless cash withdrawal'), ('Cash', 'Cash'), ('Cash by FSP', 'Cash by FSP'), ('Cheque', 'Cheque'), ('Deposit to Card', 'Deposit to Card'), ('In Kind', 'In Kind'), ('Mobile Money', 'Mobile Money'), ('Other', 'Other'), ('Pre-paid card', 'Pre-paid card'), ('Referral', 'Referral'), ('Transfer', 'Transfer'), ('Transfer to Account', 'Transfer to Account'), ('Voucher', 'Voucher')], default='Cash', max_length=24, null=True)), - ('currency', models.CharField(max_length=4, null=True)), - ('entitlement_quantity', models.DecimalField(decimal_places=2, max_digits=12, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))])), - ('delivered_quantity', models.DecimalField(decimal_places=2, max_digits=12, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.01'))])), - ('delivery_date', models.DateTimeField(null=True)), - ('service_provider_ca_id', models.CharField(max_length=255, null=True)), - ('session', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cash_assist_datahub.session')), - ('transaction_reference_id', models.CharField(max_length=255, null=True)), - ('vision_id', models.CharField(max_length=255, null=True)), - ('cash_plan_ca_id', models.CharField(max_length=255, null=True)), - ], - options={ - 'unique_together': {('session', 'ca_id')}, - }, - ), - migrations.CreateModel( - name='Programme', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('mis_id', models.UUIDField()), - ('ca_id', models.CharField(max_length=255)), - ('ca_hash_id', models.CharField(max_length=255)), - ('session', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cash_assist_datahub.session')), - ], - options={ - 'unique_together': {('session', 'mis_id')}, - }, - ), - migrations.CreateModel( - name='ServiceProvider', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('business_area', models.CharField(max_length=20)), - ('ca_id', models.CharField(max_length=255)), - ('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)), - ('session', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cash_assist_datahub.session')), - ], - options={ - 'unique_together': {('session', 'ca_id')}, - }, - ), - migrations.CreateModel( - name='TargetPopulation', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('mis_id', models.UUIDField()), - ('ca_id', models.CharField(max_length=255)), - ('ca_hash_id', models.UUIDField()), - ('session', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cash_assist_datahub.session')), - ], - options={ - 'unique_together': {('session', 'mis_id')}, - }, - ), - ] diff --git a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0016_migration.py b/src/hct_mis_api/apps/cash_assist_datahub/migrations/0016_migration.py deleted file mode 100644 index 67dcba45ab..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0016_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.19 on 2023-07-27 16:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('cash_assist_datahub', '0001_migration_squashed_0015_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='session', - name='business_area', - field=models.CharField(help_text='Same as the business area set on program, but\n this is set as the same value, and all other\n models this way can get easy access to the business area\n via the session.', max_length=20), - ), - ] diff --git a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0017_migration.py b/src/hct_mis_api/apps/cash_assist_datahub/migrations/0017_migration.py deleted file mode 100644 index 8dc195ee7c..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0017_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.20 on 2023-08-04 11:18 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('cash_assist_datahub', '0016_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='paymentrecord', - name='delivery_type', - field=models.CharField(choices=[('Cardless cash withdrawal', 'Cardless cash withdrawal'), ('Cash', 'Cash'), ('Cash by FSP', 'Cash by FSP'), ('Cheque', 'Cheque'), ('Deposit to Card', 'Deposit to Card'), ('Mobile Money', 'Mobile Money'), ('Pre-paid card', 'Pre-paid card'), ('Referral', 'Referral'), ('Transfer', 'Transfer'), ('Transfer to Account', 'Transfer to Account'), ('Voucher', 'Voucher')], default='Cash', max_length=24, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0018_migration.py b/src/hct_mis_api/apps/cash_assist_datahub/migrations/0018_migration.py deleted file mode 100644 index 6467fd2b8e..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0018_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.25 on 2024-04-23 00:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('cash_assist_datahub', '0017_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='paymentrecord', - name='delivery_type', - field=models.CharField(choices=[('Cardless cash withdrawal', 'Cardless cash withdrawal'), ('Cash', 'Cash'), ('Cash by FSP', 'Cash by FSP'), ('Cheque', 'Cheque'), ('Deposit to Card', 'Deposit to Card'), ('Mobile Money', 'Mobile Money'), ('Pre-paid card', 'Pre-paid card'), ('Referral', 'Referral'), ('Transfer', 'Transfer'), ('Transfer to Account', 'Transfer to Account'), ('Voucher', 'Voucher'), ('Cash over the counter', 'Cash over the counter')], default='Cash', max_length=24, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0019_migration.py b/src/hct_mis_api/apps/cash_assist_datahub/migrations/0019_migration.py deleted file mode 100644 index 7ca94b25ca..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0019_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.25 on 2024-05-07 09:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('cash_assist_datahub', '0018_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='paymentrecord', - name='delivery_type', - field=models.CharField(choices=[('Cardless cash withdrawal', 'Cardless cash withdrawal'), ('Cash', 'Cash'), ('Cash by FSP', 'Cash by FSP'), ('Cheque', 'Cheque'), ('Deposit to Card', 'Deposit to Card'), ('Mobile Money', 'Mobile Money'), ('Pre-paid card', 'Pre-paid card'), ('Referral', 'Referral'), ('Transfer', 'Transfer'), ('Transfer to Account', 'Transfer to Account'), ('Voucher', 'Voucher'), ('Cash over the counter', 'Cash over the counter'), ('ATM Card', 'ATM Card')], default='Cash', max_length=24, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0020_migration.py b/src/hct_mis_api/apps/cash_assist_datahub/migrations/0020_migration.py deleted file mode 100644 index 674d5f512b..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0020_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.25 on 2024-05-08 11:01 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('cash_assist_datahub', '0019_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='paymentrecord', - name='delivery_type', - field=models.CharField(choices=[('Cardless cash withdrawal', 'Cardless cash withdrawal'), ('Cash', 'Cash'), ('Cash by FSP', 'Cash by FSP'), ('Cheque', 'Cheque'), ('Deposit to Card', 'Deposit to Card'), ('Mobile Money', 'Mobile Money'), ('Pre-paid card', 'Pre-paid card'), ('Referral', 'Referral'), ('Transfer', 'Transfer'), ('Transfer to Account', 'Transfer to Account'), ('Voucher', 'Voucher'), ('Cash over the counter', 'Cash over the counter'), ('Transfer to Digital Wallet', 'Transfer to Digital Wallet'), ('ATM Card', 'ATM Card')], default='Cash', max_length=32, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0021_migration.py b/src/hct_mis_api/apps/cash_assist_datahub/migrations/0021_migration.py deleted file mode 100644 index 7ba67aec23..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/migrations/0021_migration.py +++ /dev/null @@ -1,19 +0,0 @@ - -# Generated by Django 3.2.25 on 2024-04-26 13:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('cash_assist_datahub', '0020_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='paymentrecord', - name='delivery_type', - field=models.CharField(choices=[('Cardless cash withdrawal', 'Cardless cash withdrawal'), ('Cash', 'Cash'), ('Cash by FSP', 'Cash by FSP'), ('Cheque', 'Cheque'), ('Deposit to Card', 'Deposit to Card'), ('Mobile Money', 'Mobile Money'), ('Pre-paid card', 'Pre-paid card'), ('Referral', 'Referral'), ('Transfer', 'Transfer'), ('Transfer to Account', 'Transfer to Account'), ('Voucher', 'Voucher'), ('ATM Card', 'ATM Card'), ('Cash over the counter', 'Cash over the counter'), ('Transfer to Digital Wallet', 'Transfer to Digital Wallet')], default='Cash', max_length=32, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/cash_assist_datahub/migrations/__init__.py b/src/hct_mis_api/apps/cash_assist_datahub/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/apps/cash_assist_datahub/models.py b/src/hct_mis_api/apps/cash_assist_datahub/models.py deleted file mode 100644 index d43e8921d2..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/models.py +++ /dev/null @@ -1,177 +0,0 @@ -from decimal import Decimal - -from django.core.validators import MinValueValidator -from django.db import models -from django.utils.translation import gettext_lazy as _ - -from hct_mis_api.apps.payment.delivery_mechanisms import DeliveryMechanismChoices -from hct_mis_api.apps.utils.models import AbstractSession - - -class Session(AbstractSession): - pass - - -class SessionModel(models.Model): - session = models.ForeignKey("Session", on_delete=models.CASCADE) - - class Meta: - abstract = True - - -class TargetPopulation(SessionModel): - mis_id = models.UUIDField() - ca_id = models.CharField( - max_length=255, - ) - ca_hash_id = models.UUIDField() - - class Meta: - unique_together = ("session", "mis_id") - - -class Programme(SessionModel): - mis_id = models.UUIDField() - ca_id = models.CharField( - max_length=255, - ) - ca_hash_id = models.CharField( - max_length=255, - ) - - class Meta: - unique_together = ("session", "mis_id") - - -class CashPlan(SessionModel): - 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"), - ), - ) - business_area = models.CharField(max_length=20, null=True) - cash_plan_id = models.CharField(max_length=255) - cash_plan_hash_id = models.UUIDField() - status = models.CharField( - max_length=255, - null=True, - ) - status_date = models.DateTimeField(null=True) - name = models.CharField(max_length=255, null=True) - distribution_level = models.CharField(max_length=255, null=True) - start_date = models.DateTimeField(null=True) - end_date = models.DateTimeField(null=True) - dispersion_date = models.DateTimeField(null=True) - coverage_duration = models.PositiveIntegerField(null=True) - coverage_unit = models.CharField(max_length=255, null=True) - comments = models.CharField(max_length=255, null=True) - program_mis_id = models.UUIDField(null=True) - delivery_type = models.CharField(max_length=255, null=True) - assistance_measurement = models.CharField(max_length=255, null=True) - assistance_through = models.CharField(max_length=255, null=True) - 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(null=True) - total_persons_covered = models.IntegerField(null=True) - total_persons_covered_revised = models.IntegerField(null=True) - payment_records_count = models.IntegerField(null=True) - total_entitled_quantity = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0.01"))], - null=True, - ) - total_entitled_quantity_revised = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0.01"))], - null=True, - ) - total_delivered_quantity = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0.01"))], - null=True, - ) - total_undelivered_quantity = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0.01"))], - null=True, - ) - - class Meta: - unique_together = ("session", "cash_plan_id") - - -class PaymentRecord(SessionModel): - business_area = models.CharField(max_length=20, null=True) - status = models.CharField(max_length=255, null=True) - status_date = models.DateTimeField(null=True) - ca_id = models.CharField(max_length=255) - ca_hash_id = models.UUIDField() - registration_ca_id = models.CharField(max_length=255, null=True) - household_mis_id = models.UUIDField(null=True) - # head of household - head_of_household_mis_id = models.UUIDField(null=True) - full_name = models.CharField(max_length=255, null=True) - total_persons_covered = models.IntegerField(null=True) - distribution_modality = models.CharField(max_length=255, null=True) - target_population_mis_id = models.UUIDField(null=True) - target_population_cash_assist_id = models.CharField(max_length=255, null=True) - cash_plan_ca_id = models.CharField(max_length=255, null=True) - entitlement_card_number = models.CharField(max_length=255, null=True) - entitlement_card_status = models.CharField(max_length=20, null=True) - entitlement_card_issue_date = models.DateField(null=True) - delivery_type = models.CharField( - choices=DeliveryMechanismChoices.DELIVERY_TYPE_CHOICES, - default=DeliveryMechanismChoices.DELIVERY_TYPE_CASH, - max_length=32, - null=True, - ) - currency = models.CharField(max_length=4, null=True) - entitlement_quantity = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0.01"))], - null=True, - ) - delivered_quantity = models.DecimalField( - decimal_places=2, - max_digits=12, - validators=[MinValueValidator(Decimal("0.01"))], - null=True, - ) - delivery_date = models.DateTimeField(null=True) - service_provider_ca_id = models.CharField(max_length=255, null=True) - transaction_reference_id = models.CharField(max_length=255, null=True) - vision_id = models.CharField(max_length=255, null=True) - - class Meta: - unique_together = ("session", "ca_id") - - -class ServiceProvider(SessionModel): - business_area = models.CharField(max_length=20) - ca_id = models.CharField(max_length=255) - 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) - - class Meta: - unique_together = ("session", "ca_id") diff --git a/src/hct_mis_api/apps/cash_assist_datahub/tasks/pull_from_datahub.py b/src/hct_mis_api/apps/cash_assist_datahub/tasks/pull_from_datahub.py deleted file mode 100644 index 0bbb5ea481..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/tasks/pull_from_datahub.py +++ /dev/null @@ -1,282 +0,0 @@ -import logging -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union - -from django.core.cache import cache -from django.db import transaction -from django.db.models import Count - -from sentry_sdk import configure_scope - -from hct_mis_api.apps.cash_assist_datahub import models as ca_models -from hct_mis_api.apps.cash_assist_datahub.models import Session -from hct_mis_api.apps.core.cache_keys import ( - PROGRAM_TOTAL_NUMBER_OF_HOUSEHOLDS_CACHE_KEY, -) -from hct_mis_api.apps.core.exchange_rates import ExchangeRates -from hct_mis_api.apps.core.models import BusinessArea, CountryCodeMap -from hct_mis_api.apps.core.utils import build_arg_dict, clear_cache_for_dashboard_totals -from hct_mis_api.apps.payment.models import ( - CashPlan, - DeliveryMechanism, - PaymentRecord, - PaymentVerificationSummary, - ServiceProvider, -) -from hct_mis_api.apps.payment.services.handle_total_cash_in_households import ( - handle_total_cash_in_specific_households, -) -from hct_mis_api.apps.payment.utils import get_quantity_in_usd -from hct_mis_api.apps.program.models import Program -from hct_mis_api.apps.targeting.models import TargetPopulation - -if TYPE_CHECKING: - from hct_mis_api.apps.utils.models import AbstractSession - - -logger = logging.getLogger(__name__) - - -class PullFromDatahubTask: - MAPPING_CASH_PLAN_DICT = { - "ca_id": "cash_plan_id", - "ca_hash_id": "cash_plan_hash_id", - "status": "status", - "total_undelivered_quantity": "total_undelivered_quantity", - "total_delivered_quantity": "total_delivered_quantity", - "total_entitled_quantity_revised": "total_entitled_quantity_revised", - "total_entitled_quantity": "total_entitled_quantity", - "total_persons_covered_revised": "total_persons_covered_revised", - "total_persons_covered": "total_persons_covered", - "validation_alerts_count": "validation_alerts_count", - "down_payment": "down_payment", - "funds_commitment": "funds_commitment", - "vision_id": "vision_id", - "assistance_through": "assistance_through", - "assistance_measurement": "assistance_measurement", - "delivery_type": "delivery_type", - "comments": "comments", - "coverage_duration": "coverage_duration", - "coverage_unit": "coverage_unit", - "dispersion_date": "dispersion_date", - "end_date": "end_date", # TODO: what with CashPlan and ProgramCycle ??? - "start_date": "start_date", - "distribution_level": "distribution_level", - "name": "name", - "status_date": "status_date", - "program_id": "program_mis_id", - } - MAPPING_PAYMENT_RECORD_DICT = { - "delivery_date": "delivery_date", - "delivered_quantity": "delivered_quantity", - "entitlement_quantity": "entitlement_quantity", - "currency": "currency", - "delivery_type": "delivery_type", - "entitlement_card_issue_date": "entitlement_card_issue_date", - "entitlement_card_status": "entitlement_card_status", - "entitlement_card_number": "entitlement_card_number", - "target_population_id": "target_population_mis_id", - "distribution_modality": "distribution_modality", - "total_persons_covered": "total_persons_covered", - "full_name": "full_name", - "household_id": "household_mis_id", - "head_of_household_id": "head_of_household_mis_id", - "ca_id": "ca_id", - "ca_hash_id": "ca_hash_id", - "status": "status", - "status_date": "status_date", - "transaction_reference_id": "transaction_reference_id", - "vision_id": "vision_id", - "registration_ca_id": "registration_ca_id", - } - MAPPING_SERVICE_PROVIDER_DICT = { - "ca_id": "ca_id", - "full_name": "full_name", - "short_name": "short_name", - "vision_id": "vision_id", - } - - def __init__(self, exchange_rates_client: Optional[ExchangeRates] = None) -> None: - self.exchange_rates_client = exchange_rates_client or ExchangeRates() - - def execute(self) -> Dict[str, Union[int, List[Any]]]: - grouped_session = Session.objects.values("business_area").annotate(count=Count("business_area")) - ret: Dict[str, List] = { - "skipped_due_failure": [], - "successes": [], - "failures": [], - } - grouped_session_count = 0 - remove_dashboard_cache = False - for group in grouped_session: - grouped_session_count += 1 - business_area = group.get("business_area") - session_queryset = Session.objects.filter(business_area=business_area) - # if any session in this business area fails omit other sessions in this business area - if session_queryset.filter(status=Session.STATUS_FAILED).count() > 0: - ret["skipped_due_failure"].append(business_area) - continue - sessions = session_queryset.filter(status=Session.STATUS_READY).order_by("-last_modified_date") - for session in sessions: - try: - self.copy_session(session) - ret["successes"].append(session.id) - remove_dashboard_cache = True - except Exception as e: - logger.exception(e) - ret["failures"].append(session.id) - if remove_dashboard_cache: - clear_cache_for_dashboard_totals() - return ret | {"grouped_session": grouped_session_count} - - def clear_cache(self, session: "AbstractSession") -> None: - business_area = self.get_business_area_for_cash_assist_code(session.business_area) - cache.delete_pattern(PROGRAM_TOTAL_NUMBER_OF_HOUSEHOLDS_CACHE_KEY.format(business_area.id, "*")) - - def copy_session(self, session: "AbstractSession") -> None: - with configure_scope() as scope: - scope.set_tag("session.ca", str(session.id)) - session.status = session.STATUS_PROCESSING - session.save(update_fields=("status",)) - self.clear_cache(session) - try: - with transaction.atomic(), transaction.atomic(using="cash_assist_datahub_ca"): - self.copy_service_providers(session) - self.copy_programs(session) - self.copy_target_population(session) - self.copy_cash_plans(session) - self.copy_payment_records(session) - session.status = session.STATUS_COMPLETED - session.save(update_fields=("status",)) - except Exception as e: - session.process_exception(e) - session.save( - update_fields=( - "status", - "traceback", - "sentry_id", - ) - ) - raise - - def get_business_area_for_cash_assist_code(self, cash_assist_code: str) -> BusinessArea: - return BusinessArea.objects.get( - code=BusinessArea.cash_assist_to_code_mapping.get(cash_assist_code, cash_assist_code) - ) - - def copy_cash_plans(self, session: "AbstractSession") -> None: - dh_cash_plans = ca_models.CashPlan.objects.filter(session=session) - for dh_cash_plan in dh_cash_plans: - cash_plan_args = build_arg_dict(dh_cash_plan, PullFromDatahubTask.MAPPING_CASH_PLAN_DICT) - self.set_cash_plan_service_provider(cash_plan_args) - cash_plan_args["business_area"] = self.get_business_area_for_cash_assist_code(dh_cash_plan.business_area) - ( - cash_plan, - created, - ) = CashPlan.objects.update_or_create(ca_id=dh_cash_plan.cash_plan_id, defaults=cash_plan_args) - - if created: - PaymentVerificationSummary.objects.create(payment_plan_obj=cash_plan) - - try: - if not cash_plan.exchange_rate: - cash_plan.exchange_rate = cash_plan.get_exchange_rate(self.exchange_rates_client) - cash_plan.save(update_fields=["exchange_rate"]) - for usd_field in CashPlan.usd_fields: - setattr( - cash_plan, - usd_field, - get_quantity_in_usd( - amount=getattr(cash_plan, usd_field.removesuffix("_usd")), - currency=cash_plan.currency, - exchange_rate=cash_plan.exchange_rate, - currency_exchange_date=cash_plan.currency_exchange_date, - ), - ) - cash_plan.save(update_fields=CashPlan.usd_fields) - except Exception as e: - logger.exception(e) - - def set_cash_plan_service_provider(self, cash_plan_args: Dict) -> None: - assistance_through = cash_plan_args.get("assistance_through") - if not assistance_through: - return - service_provider = ServiceProvider.objects.filter(ca_id=assistance_through).first() - if service_provider is None: - return - cash_plan_args["service_provider"] = service_provider - - def copy_payment_records(self, session: "AbstractSession") -> None: - dh_payment_records = ca_models.PaymentRecord.objects.filter(session=session) - household_ids = [] - for dh_payment_record in dh_payment_records: - payment_record_args = build_arg_dict( - dh_payment_record, - PullFromDatahubTask.MAPPING_PAYMENT_RECORD_DICT, - ) - payment_record_args["business_area"] = self.get_business_area_for_cash_assist_code( - dh_payment_record.business_area - ) - payment_record_args["service_provider"] = ServiceProvider.objects.get( - ca_id=dh_payment_record.service_provider_ca_id - ) - payment_record_args["parent"] = CashPlan.objects.get(ca_id=dh_payment_record.cash_plan_ca_id) - payment_record_args["delivery_type"] = DeliveryMechanism.objects.get( - name=payment_record_args["delivery_type"] - ) - ( - payment_record, - created, - ) = PaymentRecord.objects.update_or_create(ca_id=dh_payment_record.ca_id, defaults=payment_record_args) - try: - for usd_field in PaymentRecord.usd_fields: - setattr( - payment_record, - usd_field, - get_quantity_in_usd( - amount=getattr(payment_record, usd_field.removesuffix("_usd")), - currency=payment_record.currency, - exchange_rate=payment_record.parent.exchange_rate, - currency_exchange_date=payment_record.parent.currency_exchange_date, - exchange_rates_client=self.exchange_rates_client, - ), - ) - payment_record.save(update_fields=PaymentRecord.usd_fields) - except Exception as e: - logger.exception(e) - household_ids.append(payment_record.household_id) - if payment_record.household and payment_record.parent and payment_record.parent.program: - payment_record.household.programs.add(payment_record.parent.program) - handle_total_cash_in_specific_households(household_ids) - - def copy_service_providers(self, session: "AbstractSession") -> None: - dh_service_providers = ca_models.ServiceProvider.objects.filter(session=session) - for dh_service_provider in dh_service_providers: - service_provider_args = build_arg_dict( - dh_service_provider, - PullFromDatahubTask.MAPPING_SERVICE_PROVIDER_DICT, - ) - service_provider_args["business_area"] = self.get_business_area_for_cash_assist_code( - dh_service_provider.business_area - ) - service_provider_args["country"] = CountryCodeMap.objects.get_iso3_code(dh_service_provider.country) - ServiceProvider.objects.update_or_create(ca_id=dh_service_provider.ca_id, defaults=service_provider_args) - - def copy_programs(self, session: "AbstractSession") -> None: - dh_programs = ca_models.Programme.objects.filter(session=session) - programs = [] - for dh_program in dh_programs: - program = Program.objects.get(id=dh_program.mis_id) - program.ca_id = dh_program.ca_id - program.ca_hash_id = dh_program.ca_hash_id - programs.append(program) - Program.objects.bulk_update(programs, ["ca_id", "ca_hash_id"]) - - def copy_target_population(self, session: "AbstractSession") -> None: - dh_target_populations = ca_models.TargetPopulation.objects.filter(session=session) - target_populations = [] - for dh_target_population in dh_target_populations: - target_population = TargetPopulation.objects.get(id=dh_target_population.mis_id) - target_population.ca_id = dh_target_population.ca_id - target_population.ca_hash_id = dh_target_population.ca_hash_id - target_populations.append(target_population) - TargetPopulation.objects.bulk_update(target_populations, ["ca_id", "ca_hash_id"]) diff --git a/src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/payment_record/inspect.html b/src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/payment_record/inspect.html deleted file mode 100644 index 427645d70e..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/payment_record/inspect.html +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "admin/change_form.html" %} -{% load i18n admin_urls static admin_modify %} -{% block breadcrumbs %} -
-{% endblock %} - -{% block content %} -

Payment Record {{ original }} - {{ original.status }}

- - {% for model, info in data.items %} - - - - - - {% endfor %} - -
{{ info.meta.verbose_name|title }}{{ info.instance|default_if_none:"NOT FOUND" }} -{# {% url info.meta|admin_urlname:'change' instance.pk as the_url %}#} - visit -
-{% endblock content %} - -{% block submit_buttons_bottom %}{% endblock %} diff --git a/src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/session/debug.html b/src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/session/debug.html deleted file mode 100644 index 2974537cb0..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/session/debug.html +++ /dev/null @@ -1,77 +0,0 @@ -{% extends "admin_extra_buttons/action_page.html" %}{% load i18n admin_urls static admin_modify %} -{% block breadcrumbs %} - -{% endblock %} -{% block content %} - - - - - - -
{{ original.datetime }}{{ original.username }}
- -
- -
- {{ original.traceback|safe }} -
-{% endblock content %} diff --git a/src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/session/inspect.html b/src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/session/inspect.html deleted file mode 100644 index 26a1e11bd0..0000000000 --- a/src/hct_mis_api/apps/cash_assist_datahub/templates/admin/cash_assist_datahub/session/inspect.html +++ /dev/null @@ -1,102 +0,0 @@ -{% extends "admin_extra_buttons/action_page.html" %}{% load i18n admin_urls static admin_modify %} -{% block extrahead %} - {{ block.super }} - {{ media }} - {{ adminform.media }} - - - - - - - - - - - -{% endblock %} -{% block breadcrumbs %} - -{% endblock %} -{% block object-tools-items %} - {% if sentry_url %} -
  • Check errors in Sentry
  • - {% endif %} -{% endblock %} -{% block action-content %} -

    {{ original.business_area }}: {{ area }}

    - {% if warnings %} -

    Warnings

    - - {% for level, warn in warnings %} - - - - - {% endfor %} -
    {{ level|upper }}{{ warn }}
    - {% endif %} -
    - - - - - {# #} - - - - {% for model, info in data.items %} - - - - - - - {% endfor %} -
    Component# Records-# Warnings/Errors -
    {{ info.meta.verbose_name_plural|title }}{{ info.count }} - visit - - {% if info.warnings or info.errors %} - {{ info.warnings|length }}/{{ info.errors|length }} - - {% for err in info.warnings %} - - - - {% endfor %} - {% for err in info.errors %} - - - - {% endfor %} - - {% else %} - {{ info.warnings|length }}/{{ info.errors|length }} - {% endif %} -
    -{% endblock %} - -{% block submit_buttons_bottom %}{% endblock %} diff --git a/src/hct_mis_api/apps/core/dbrouters.py b/src/hct_mis_api/apps/core/dbrouters.py index 46ab37846c..e2ecd74342 100644 --- a/src/hct_mis_api/apps/core/dbrouters.py +++ b/src/hct_mis_api/apps/core/dbrouters.py @@ -9,8 +9,6 @@ class DbRouter: def select_db(model: Optional[Type[Model]]) -> Optional[str]: if model._meta.proxy: model = model._meta.proxy_for_model - # if f"{model._meta.app_label}." == "mis_datahub.AuroraRecord": - # return settings.DATABASE_APPS_MAPPING.get(model._meta.app_label) return settings.DATABASE_APPS_MAPPING.get(model._meta.app_label) def db_for_read(self, model: Optional[Type[Model]], **hints: Any) -> Optional[str]: diff --git a/src/hct_mis_api/apps/core/management/commands/createadminpermissiongroups.py b/src/hct_mis_api/apps/core/management/commands/createadminpermissiongroups.py index 9e7b7a5dbe..0d66cd0001 100644 --- a/src/hct_mis_api/apps/core/management/commands/createadminpermissiongroups.py +++ b/src/hct_mis_api/apps/core/management/commands/createadminpermissiongroups.py @@ -67,27 +67,7 @@ def handle(self, *args: Any, **options: Any) -> Any: "individual", "xlsxupdatefile", ], - "cash_assist_datahub": [ - "cashplan", - "paymentrecord", - "programme", - "serviceprovider", - "session", - "targetpopulation", - ], - "erp_datahub": ["downpayment", "fundscommitment"], - "mis_datahub": [ - "document", - "downpayment", - "fundscommitment", - "household", - "individualroleinhousehold", - "individual", - "program", - "session", - "targetpopulationentry", - "targetpopulation", - ], + "vision": ["downpayment", "fundscommitment"], "payment": ["cashplanpaymentverification", "paymentrecord", "paymentverification", "serviceprovider"], "django_celery_beat": [ "clockedschedule", diff --git a/src/hct_mis_api/apps/core/management/commands/generatefixtures.py b/src/hct_mis_api/apps/core/management/commands/generatefixtures.py index 5b156863f5..00e2c1ae18 100644 --- a/src/hct_mis_api/apps/core/management/commands/generatefixtures.py +++ b/src/hct_mis_api/apps/core/management/commands/generatefixtures.py @@ -7,14 +7,9 @@ from django.core.management import BaseCommand, call_command from django.db import transaction -from django.db.models import Q from hct_mis_api.apps.account.fixtures import UserFactory from hct_mis_api.apps.account.models import UserRole -from hct_mis_api.apps.cash_assist_datahub import ( - fixtures as cash_assist_datahub_fixtures, -) -from hct_mis_api.apps.cash_assist_datahub.models import Programme, Session from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.geo.models import Area from hct_mis_api.apps.grievance.fixtures import ( @@ -36,7 +31,6 @@ PaymentVerificationPlanFactory, ) from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.apps.program.models import Program from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory from hct_mis_api.apps.targeting.fixtures import ( TargetingCriteriaFactory, @@ -201,9 +195,6 @@ def handle(self, *args: Any, **options: Any) -> None: self.stdout.write("Generating fixtures...") if options["flush"]: call_command("flush", "--noinput") - call_command("flush", "--noinput", database="cash_assist_datahub_mis") - call_command("flush", "--noinput", database="cash_assist_datahub_ca") - call_command("flush", "--noinput", database="cash_assist_datahub_erp") call_command( "loaddata", "hct_mis_api/apps/account/fixtures/superuser.json", @@ -245,18 +236,6 @@ def handle(self, *args: Any, **options: Any) -> None: for _ in range(programs_amount): self._generate_program_with_dependencies(options, index) - session = Session(source=Session.SOURCE_CA, status=Session.STATUS_READY) - session.save() - cash_assist_datahub_fixtures.ServiceProviderFactory.create_batch(10, session=session) - cash_assist_datahub_fixtures.CashPlanFactory.create_batch(10, session=session) - cash_assist_datahub_fixtures.PaymentRecordFactory.create_batch(10, session=session) - - for _ in range(programs_amount): - used_ids = list(Programme.objects.values_list("mis_id", flat=True)) - mis_id = Program.objects.filter(~Q(id__in=used_ids)).first().id - programme = cash_assist_datahub_fixtures.ProgrammeFactory(session=session, mis_id=mis_id) - programme.save() - if not options["noreindex"]: rebuild_search_index() diff --git a/src/hct_mis_api/apps/core/management/commands/initcypress.py b/src/hct_mis_api/apps/core/management/commands/initcypress.py index b274dbcea2..0da1be727b 100644 --- a/src/hct_mis_api/apps/core/management/commands/initcypress.py +++ b/src/hct_mis_api/apps/core/management/commands/initcypress.py @@ -27,10 +27,6 @@ def handle(self, *args: Any, **options: Any) -> None: reset_business_area_sequences() call_command("flush", "--noinput") - call_command("flush", "--noinput", database="cash_assist_datahub_mis") - call_command("flush", "--noinput", database="cash_assist_datahub_ca") - call_command("flush", "--noinput", database="cash_assist_datahub_erp") - call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/geo/fixtures/data.json") call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/core/fixtures/data.json") call_command("loaddata", f"{settings.PROJECT_ROOT}/apps/account/fixtures/data.json") diff --git a/src/hct_mis_api/apps/core/management/commands/initdemo.py b/src/hct_mis_api/apps/core/management/commands/initdemo.py index 8ae89b33d5..53400a9d27 100644 --- a/src/hct_mis_api/apps/core/management/commands/initdemo.py +++ b/src/hct_mis_api/apps/core/management/commands/initdemo.py @@ -115,16 +115,8 @@ def handle(self, *args: Any, **options: Any) -> None: call_command("migratealldb") # Flush databases - databases = [ - "default", - "cash_assist_datahub_mis", - "cash_assist_datahub_ca", - "cash_assist_datahub_erp", - ] self.stdout.write("Flushing databases...") - for db in databases: - self.stdout.write(f"Flushing database: {db}") - call_command("flush", "--noinput", database=db) + call_command("flush", "--noinput") # Load fixtures fixtures = [ diff --git a/src/hct_mis_api/apps/core/tasks_schedules.py b/src/hct_mis_api/apps/core/tasks_schedules.py index 5e737ff684..077cdc5ac6 100644 --- a/src/hct_mis_api/apps/core/tasks_schedules.py +++ b/src/hct_mis_api/apps/core/tasks_schedules.py @@ -5,14 +5,6 @@ "task": "hct_mis_api.apps.sanction_list.celery_tasks.sync_sanction_list_task", "schedule": crontab(minute=0, hour=0), }, - "pull_from_cashassist_datahub_task": { - "task": "hct_mis_api.apps.cash_assist_datahub.celery_tasks.pull_from_cashassist_datahub_task", - "schedule": crontab(minute=0, hour="*/1"), - }, - "fix_exchange_rates_task": { - "task": "hct_mis_api.apps.cash_assist_datahub.celery_tasks.fix_exchange_rates_task", - "schedule": crontab(minute=0, hour="*/1"), - }, "get_sync_run_rapid_pro": { "task": "hct_mis_api.apps.payment.celery_tasks.get_sync_run_rapid_pro_task", "schedule": crontab(minute="*/20"), @@ -21,10 +13,6 @@ "task": "hct_mis_api.apps.grievance.celery_tasks.periodic_grievances_notifications", "schedule": crontab(minute="*/20"), }, - "sync_to_mis_datahub": { - "task": "hct_mis_api.apps.erp_datahub.celery_tasks.sync_to_mis_datahub_task", - "schedule": crontab(minute="*/20"), - }, # "recalculate_population_fields_task": { # "task": "hct_mis_api.apps.household.celery_tasks.interval_recalculate_population_fields_task", # "schedule": crontab(hour="*/24"), diff --git a/src/hct_mis_api/apps/erp_datahub/__init__.py b/src/hct_mis_api/apps/erp_datahub/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/apps/erp_datahub/admin.py b/src/hct_mis_api/apps/erp_datahub/admin.py deleted file mode 100644 index 48c188cdf0..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/admin.py +++ /dev/null @@ -1,265 +0,0 @@ -from operator import itemgetter -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, Union - -from django import forms -from django.conf import settings -from django.contrib import admin, messages -from django.contrib.admin import ModelAdmin, SimpleListFilter -from django.contrib.admin.options import IncorrectLookupParameters -from django.core.exceptions import ValidationError -from django.core.validators import MinLengthValidator, RegexValidator -from django.db.transaction import atomic -from django.forms import ModelForm -from django.shortcuts import redirect -from django.template.response import TemplateResponse -from django.utils import timezone -from django.utils.safestring import mark_safe - -from admin_extra_buttons.decorators import button -from admin_extra_buttons.mixins import confirm_action -from adminfilters.filters import ValueFilter - -from hct_mis_api.apps.core.currencies import CURRENCY_CHOICES -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.erp_datahub.models import DownPayment, FundsCommitment -from hct_mis_api.apps.erp_datahub.tasks.sync_to_mis_datahub import SyncToMisDatahubTask -from hct_mis_api.apps.mis_datahub import models as mis_models -from hct_mis_api.apps.utils.admin import HOPEModelAdminBase - -if TYPE_CHECKING: - from uuid import UUID - - from django.db.models.query import QuerySet - from django.http import ( - HttpRequest, - HttpResponsePermanentRedirect, - HttpResponseRedirect, - ) - - -class NumberValidator(RegexValidator): - regex = r"[0-9]{10,}" - - -class FundsCommitmentAddForm(forms.ModelForm): - business_area = forms.ModelChoiceField(queryset=BusinessArea.objects, to_field_name="code") - currency_code = forms.ChoiceField(choices=sorted(CURRENCY_CHOICES[1:], key=itemgetter(1))) - funds_commitment_number = forms.CharField(required=True) - vendor_id = forms.CharField(validators=[NumberValidator(), MinLengthValidator(10)]) - gl_account = forms.CharField(validators=[NumberValidator(), MinLengthValidator(10)]) - business_office_code = forms.ModelChoiceField( - queryset=BusinessArea.objects.filter(is_split=False), to_field_name="code", required=False - ) - - class Meta: - model = FundsCommitment - exclude = ("update_date", "updated_by", "mis_sync_flag", "mis_sync_date", "ca_sync_date", "ca_sync_flag") - - def clean_business_area(self) -> str: - return self.cleaned_data["business_area"].cash_assist_code - - -class DownPaymentAddForm(forms.ModelForm): - business_area = forms.ModelChoiceField(queryset=BusinessArea.objects, to_field_name="code") - business_office_code = forms.ModelChoiceField( - queryset=BusinessArea.objects.filter(is_split=False), to_field_name="code", required=False - ) - - class Meta: - model = DownPayment - exclude = ("update_date", "updated_by", "mis_sync_flag", "mis_sync_date", "ca_sync_date", "ca_sync_flag") - - def clean_business_area(self) -> str: - return self.cleaned_data["business_area"].cash_assist_code - - -class FundsCommitmentAssignBusinessOffice(forms.ModelForm): - business_office_code = forms.ModelChoiceField( - queryset=BusinessArea.objects.filter(is_split=False), to_field_name="code", required=True - ) - - class Meta: - model = FundsCommitment - fields = ("business_office_code",) - - def clean_business_office_code(self) -> str: - return self.cleaned_data["business_office_code"].cash_assist_code - - -def should_show_assign_business_office(request: "HttpRequest", obj: Any) -> bool: - business_area = BusinessArea.objects.get(code=obj.business_area) - return business_area.is_split and obj.business_office_code is None - - -class SplitBusinessAreaFilter(SimpleListFilter): - template = "adminfilters/combobox.html" - title = "Split Business Area" - parameter_name = "split" - - def lookups(self, request: "HttpRequest", model_admin: ModelAdmin) -> List[Tuple[int, str]]: - return [(1, "Yes"), (2, "No")] - - def queryset(self, request: "HttpRequest", queryset: "QuerySet") -> Optional["QuerySet"]: - if not self.value(): - return queryset - from hct_mis_api.apps.core.models import BusinessArea - - split_codes = list(BusinessArea.objects.filter(is_split=True).values_list("code", flat=True)) - try: - if self.value() == "1": - return queryset.filter(business_area__in=split_codes) - else: - return queryset.exclude(business_area__in=split_codes) - except (ValueError, ValidationError) as e: - raise IncorrectLookupParameters(e) - - -@admin.register(FundsCommitment) -class FundsCommitmentAdmin(HOPEModelAdminBase): - list_display = ( - "rec_serial_number", - "business_area", - "funds_commitment_item", - "funds_commitment_number", - "posting_date", - ) - list_filter = ( - SplitBusinessAreaFilter, - "business_area", - "posting_date", - "mis_sync_date", - "ca_sync_date", - ("business_area", ValueFilter), - ) - date_hierarchy = "create_date" - form = FundsCommitmentAddForm - search_fields = ("rec_serial_number", "vendor_id", "wbs_element", "funds_commitment_number") - - @atomic(using="cash_assist_datahub_erp") - @atomic() - @button(permission=should_show_assign_business_office) - def assign_business_office( - self, request: "HttpRequest", pk: "UUID" - ) -> Union["HttpResponsePermanentRedirect", "HttpResponseRedirect", TemplateResponse]: - context = self.get_common_context(request, pk, title="Please assign business office") - obj: FundsCommitment = context["original"] - business_area = BusinessArea.objects.get(code=obj.business_area) - context["business_area"] = business_area - if request.method == "POST": - form = FundsCommitmentAssignBusinessOffice(request.POST, instance=obj) - else: - form = FundsCommitmentAssignBusinessOffice(instance=obj) - form.fields["business_office_code"] = forms.ModelChoiceField( - queryset=BusinessArea.objects.filter(parent=business_area), to_field_name="code" - ) - if request.method == "POST": - if form.is_valid(): - form.save() - obj.refresh_from_db() - messages.success(request, "Business Office assigned, Founds Commitment sent") - mis_funds_commitment = mis_models.FundsCommitment(**SyncToMisDatahubTask.get_model_dict(obj)) - mis_funds_commitment.business_area = obj.business_office_code - mis_funds_commitment.save() - obj.mis_sync_flag = True - obj.mis_sync_date = timezone.now() - obj.save() - return redirect(f"{settings.ADMIN_PANEL_URL}/erp_datahub/fundscommitment/{pk}/") - - context["form"] = form - return TemplateResponse(request, "admin/erp_datahub/funds_commitment/assign_business_office.html", context) - - @button() - def execute_exchange_rate_sync(self, request: "HttpRequest") -> None: - if request.method == "POST": - from hct_mis_api.apps.erp_datahub.tasks.pull_from_erp_datahub import ( - PullFromErpDatahubTask, - ) - - task = PullFromErpDatahubTask() - task.execute() - self.message_user(request, "Exchange rate synced", messages.SUCCESS) - else: - return confirm_action( - self, - request, - self.execute_exchange_rate_sync, - mark_safe( - """

    DO NOT CONTINUE IF YOU ARE NOT SURE WHAT YOU ARE DOING

    -

    Import will only be simulated

    - """ - ), - "Successfully executed", - template="admin_extra_buttons/confirm.html", - ) - - def get_changeform_initial_data(self, request: "HttpRequest") -> Dict: - initial: Dict[str, Any] = super().get_changeform_initial_data(request) - initial["created_by"] = request.user.email - initial["updated_by"] = request.user.email - initial["posting_date"] = timezone.now() - initial["status_date"] = timezone.now() - return initial - - def get_form( - self, request: "HttpRequest", obj: Optional[Any] = None, change: bool = False, **kwargs: Any - ) -> Type["ModelForm[Any]"]: - if not change: - return FundsCommitmentAddForm - return super().get_form(request, obj, change, **kwargs) - - -class DownPaymentAssignBusinessOffice(forms.ModelForm): - business_office_code = forms.ModelChoiceField( - queryset=BusinessArea.objects.filter(is_split=False), to_field_name="code", required=True - ) - - class Meta: - model = DownPayment - fields = ("business_office_code",) - - def clean_business_office_code(self) -> str: - return self.cleaned_data["business_office_code"].cash_assist_code - - -@admin.register(DownPayment) -class DownPaymentAdmin(HOPEModelAdminBase): - list_filter = ( - "mis_sync_date", - "ca_sync_date", - ("business_area", ValueFilter), - ) - form = DownPaymentAddForm - date_hierarchy = "create_date" - - @atomic(using="cash_assist_datahub_erp") - @atomic() - @button(permission=should_show_assign_business_office) - def assign_business_office( - self, request: "HttpRequest", pk: "UUID" - ) -> Union["HttpResponsePermanentRedirect", "HttpResponseRedirect", TemplateResponse]: - context = self.get_common_context(request, pk, title="Please assign business office") - obj: DownPayment = context["original"] - business_area = BusinessArea.objects.get(code=obj.business_area) - context["business_area"] = business_area - if request.method == "POST": - form = DownPaymentAssignBusinessOffice(request.POST, instance=obj) - else: - form = DownPaymentAssignBusinessOffice(instance=obj) - form.fields["business_office_code"] = forms.ModelChoiceField( - queryset=BusinessArea.objects.filter(parent=business_area), to_field_name="code" - ) - if request.method == "POST": - if form.is_valid(): - form.save() - obj.refresh_from_db() - messages.success(request, "Business Office assigned, Founds Commitment sent") - mis_down_payment = mis_models.DownPayment(**SyncToMisDatahubTask.get_model_dict(obj)) - mis_down_payment.business_area = obj.business_office_code - obj.mis_sync_flag = True - obj.mis_sync_date = timezone.now() - obj.save() - mis_down_payment.save() - return redirect(f"{settings.ADMIN_PANEL_URL}/erp_datahub/downpayment/{pk}/") - - context["form"] = form - return TemplateResponse(request, "admin/erp_datahub/funds_commitment/assign_business_office.html", context) diff --git a/src/hct_mis_api/apps/erp_datahub/apps.py b/src/hct_mis_api/apps/erp_datahub/apps.py deleted file mode 100644 index 9f5045171c..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class Config(AppConfig): - name = "hct_mis_api.apps.erp_datahub" - verbose_name = "HUB ERP (Vision->Hope)" diff --git a/src/hct_mis_api/apps/erp_datahub/celery_tasks.py b/src/hct_mis_api/apps/erp_datahub/celery_tasks.py deleted file mode 100644 index a6dc878316..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/celery_tasks.py +++ /dev/null @@ -1,23 +0,0 @@ -import logging -from typing import Any - -from hct_mis_api.apps.core.celery import app -from hct_mis_api.apps.utils.logs import log_start_and_end -from hct_mis_api.apps.utils.sentry import sentry_tags - -logger = logging.getLogger(__name__) - - -@app.task(bind=True, default_retry_delay=60, max_retries=3) -@log_start_and_end -@sentry_tags -def sync_to_mis_datahub_task(self: Any) -> None: - try: - from hct_mis_api.apps.erp_datahub.tasks.sync_to_mis_datahub import ( - SyncToMisDatahubTask, - ) - - SyncToMisDatahubTask().execute() - except Exception as e: - logger.exception(e) - raise self.retry(exc=e) diff --git a/src/hct_mis_api/apps/erp_datahub/fixtures.py b/src/hct_mis_api/apps/erp_datahub/fixtures.py deleted file mode 100644 index 5d0cb0f07e..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/fixtures.py +++ /dev/null @@ -1,27 +0,0 @@ -import factory.fuzzy -from factory.django import DjangoModelFactory -from pytz import utc - -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.erp_datahub.models import FundsCommitment -from hct_mis_api.apps.payment.models import CashPlan - - -class FundsCommitmentFactory(DjangoModelFactory): - class Meta: - model = FundsCommitment - - rec_serial_number = factory.fuzzy.FuzzyInteger(1000, 99999999) - business_area = factory.LazyAttribute(lambda o: BusinessArea.objects.first().cash_assist_code) - funds_commitment_number = factory.LazyAttribute(lambda o: CashPlan.objects.order_by("?").first().funds_commitment) - document_type = "DO" - currency_code = factory.Faker("currency_code") - - total_open_amount_local = factory.fuzzy.FuzzyDecimal(100.0, 10000.0) - total_open_amount_usd = factory.fuzzy.FuzzyDecimal(100.0, 10000.0) - update_date = factory.Faker( - "date_time_this_decade", - before_now=True, - after_now=False, - tzinfo=utc, - ) diff --git a/src/hct_mis_api/apps/erp_datahub/migrations/0001_migration_squashed_0017_migration.py b/src/hct_mis_api/apps/erp_datahub/migrations/0001_migration_squashed_0017_migration.py deleted file mode 100644 index b7a8f44f2a..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/migrations/0001_migration_squashed_0017_migration.py +++ /dev/null @@ -1,70 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-08 20:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='DownPayment', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('rec_serial_number', models.CharField(blank=True, max_length=10, null=True)), - ('business_area', models.CharField(max_length=4)), - ('down_payment_reference', models.CharField(max_length=20)), - ('document_type', models.CharField(max_length=10)), - ('consumed_fc_number', models.CharField(max_length=10)), - ('total_down_payment_amount_local', models.DecimalField(decimal_places=2, max_digits=15)), - ('total_down_payment_amount_usd', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), - ('currency_code', models.CharField(blank=True, max_length=5, null=True)), - ('posting_date', models.DateField(blank=True, null=True)), - ('doc_year', models.IntegerField(blank=True, null=True)), - ('doc_number', models.CharField(blank=True, max_length=10, null=True)), - ('doc_item_number', models.CharField(max_length=3, null=True)), - ('create_date', models.DateTimeField(auto_now_add=True, null=True)), - ('created_by', models.CharField(blank=True, default='', max_length=20, null=True)), - ('update_date', models.DateTimeField(blank=True, null=True)), - ('updated_by', models.CharField(blank=True, default='', max_length=20, null=True)), - ('mis_sync_flag', models.BooleanField(blank=True, default=False, null=True)), - ('mis_sync_date', models.DateTimeField(blank=True, null=True)), - ('ca_sync_flag', models.BooleanField(blank=True, default=False, null=True)), - ('ca_sync_date', models.DateTimeField(blank=True, null=True)), - ('business_office_code', models.CharField(blank=True, max_length=4, null=True)), - ], - ), - migrations.CreateModel( - name='FundsCommitment', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('rec_serial_number', models.CharField(blank=True, max_length=10, null=True)), - ('business_area', models.CharField(blank=True, max_length=4, null=True)), - ('funds_commitment_number', models.CharField(blank=True, max_length=10, null=True)), - ('document_type', models.CharField(blank=True, max_length=2, null=True)), - ('document_text', models.CharField(blank=True, max_length=50, null=True)), - ('currency_code', models.CharField(blank=True, max_length=5, null=True)), - ('gl_account', models.CharField(blank=True, max_length=10, null=True)), - ('commitment_amount_local', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), - ('commitment_amount_usd', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), - ('total_open_amount_local', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), - ('total_open_amount_usd', models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)), - ('vendor_id', models.CharField(blank=True, max_length=10, null=True)), - ('posting_date', models.DateField(blank=True, null=True)), - ('vision_approval', models.CharField(blank=True, max_length=1, null=True)), - ('document_reference', models.CharField(max_length=16, null=True)), - ('fc_status', models.CharField(blank=True, max_length=1, null=True)), - ('create_date', models.DateTimeField(auto_now_add=True, null=True)), - ('created_by', models.CharField(blank=True, default='', max_length=20, null=True)), - ('update_date', models.DateTimeField(blank=True, null=True)), - ('updated_by', models.CharField(blank=True, default='', max_length=20, null=True)), - ('mis_sync_flag', models.BooleanField(blank=True, default=False, null=True)), - ('mis_sync_date', models.DateTimeField(blank=True, null=True)), - ('ca_sync_flag', models.BooleanField(blank=True, default=False, null=True)), - ('ca_sync_date', models.DateTimeField(blank=True, null=True)), - ('business_office_code', models.CharField(blank=True, max_length=4, null=True)), - ], - ), - ] diff --git a/src/hct_mis_api/apps/erp_datahub/migrations/0018_migration.py b/src/hct_mis_api/apps/erp_datahub/migrations/0018_migration.py deleted file mode 100644 index af3fa52405..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/migrations/0018_migration.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.2.25 on 2024-06-21 14:18 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('erp_datahub', '0001_migration_squashed_0017_migration'), - ] - - operations = [ - migrations.AddField( - model_name='fundscommitment', - name='grant', - field=models.CharField(blank=True, default='', max_length=10, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='percentage', - field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='sponsor_code', - field=models.CharField(blank=True, default='', max_length=10, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='sponsor_name', - field=models.CharField(blank=True, default='', max_length=100, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='wbs', - field=models.CharField(blank=True, default='', max_length=24, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/erp_datahub/migrations/0019_migration.py b/src/hct_mis_api/apps/erp_datahub/migrations/0019_migration.py deleted file mode 100644 index e7b722b0b9..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/migrations/0019_migration.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.2.25 on 2024-08-02 19:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('erp_datahub', '0018_migration'), - ] - - operations = [ - migrations.RenameField( - model_name='fundscommitment', - old_name='grant', - new_name='grant_number', - ), - migrations.RenameField( - model_name='fundscommitment', - old_name='sponsor_code', - new_name='sponsor', - ), - migrations.RenameField( - model_name='fundscommitment', - old_name='wbs', - new_name='wbs_element', - ), - migrations.AddField( - model_name='fundscommitment', - name='fund', - field=models.CharField(blank=True, default='', max_length=10, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='funds_center', - field=models.CharField(blank=True, default='', max_length=16, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/erp_datahub/migrations/0020_migration.py b/src/hct_mis_api/apps/erp_datahub/migrations/0020_migration.py deleted file mode 100644 index 8f2bb3f69d..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/migrations/0020_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.25 on 2024-08-04 08:22 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('erp_datahub', '0019_migration'), - ] - - operations = [ - migrations.AddField( - model_name='fundscommitment', - name='funds_commitment_item', - field=models.CharField(blank=True, default='', max_length=3, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/erp_datahub/migrations/0021_migration.py b/src/hct_mis_api/apps/erp_datahub/migrations/0021_migration.py deleted file mode 100644 index 52da3adbb1..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/migrations/0021_migration.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.2.25 on 2024-09-23 10:41 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('erp_datahub', '0020_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='fundscommitment', - name='rec_serial_number', - field=models.CharField(max_length=10, unique=True), - ), - migrations.AlterUniqueTogether( - name='fundscommitment', - unique_together={('funds_commitment_number', 'funds_commitment_item')}, - ), - ] diff --git a/src/hct_mis_api/apps/erp_datahub/migrations/__init__.py b/src/hct_mis_api/apps/erp_datahub/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/apps/erp_datahub/models.py b/src/hct_mis_api/apps/erp_datahub/models.py deleted file mode 100644 index 8b1fbb5a0f..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/models.py +++ /dev/null @@ -1,96 +0,0 @@ -from django.db import models - - -class FundsCommitment(models.Model): - rec_serial_number = models.CharField(max_length=10, unique=True) - business_area = models.CharField(max_length=4, blank=True, null=True) - funds_commitment_number = models.CharField(max_length=10, blank=True, null=True) - document_type = models.CharField(max_length=2, blank=True, null=True) - document_text = models.CharField(max_length=50, blank=True, null=True) - currency_code = models.CharField(max_length=5, blank=True, null=True) - gl_account = models.CharField(null=True, blank=True, max_length=10) - commitment_amount_local = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - commitment_amount_usd = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - total_open_amount_local = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - total_open_amount_usd = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - vendor_id = models.CharField(max_length=10, blank=True, null=True) - posting_date = models.DateField(blank=True, null=True) - vision_approval = models.CharField(max_length=1, blank=True, null=True) - document_reference = models.CharField(max_length=16, null=True) - fc_status = models.CharField(max_length=1, blank=True, null=True) - create_date = models.DateTimeField(null=True, blank=True, auto_now_add=True) - created_by = models.CharField(max_length=20, null=True, blank=True, default="") - update_date = models.DateTimeField(null=True, blank=True) - updated_by = models.CharField(max_length=20, blank=True, null=True, default="") - mis_sync_flag = models.BooleanField(null=True, blank=True, default=False) - mis_sync_date = models.DateTimeField(blank=True, null=True) - ca_sync_flag = models.BooleanField(blank=True, null=True, default=False) - ca_sync_date = models.DateTimeField(blank=True, null=True) - business_office_code = models.CharField(max_length=4, blank=True, null=True) - - grant_number = models.CharField(max_length=10, null=True, blank=True, default="") - sponsor = models.CharField(max_length=10, null=True, blank=True, default="") - sponsor_name = models.CharField(max_length=100, null=True, blank=True, default="") - wbs_element = models.CharField(max_length=24, null=True, blank=True, default="") - fund = models.CharField(max_length=10, null=True, blank=True, default="") - funds_center = models.CharField(max_length=16, null=True, blank=True, default="") - funds_commitment_item = models.CharField(max_length=3, null=True, blank=True, default="") - percentage = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True) - - def __str__(self) -> str: - return self.funds_commitment_number - - class Meta: - unique_together = ("funds_commitment_number", "funds_commitment_item") - - -class DownPayment(models.Model): - rec_serial_number = models.CharField(max_length=10, blank=True, null=True) - business_area = models.CharField(max_length=4) - down_payment_reference = models.CharField(max_length=20) - document_type = models.CharField(max_length=10) - consumed_fc_number = models.CharField(max_length=10) - total_down_payment_amount_local = models.DecimalField( - decimal_places=2, - max_digits=15, - ) - total_down_payment_amount_usd = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - currency_code = models.CharField(max_length=5, blank=True, null=True) - posting_date = models.DateField(blank=True, null=True) - doc_year = models.IntegerField(blank=True, null=True) - doc_number = models.CharField(max_length=10, blank=True, null=True) - doc_item_number = models.CharField(max_length=3, null=True) - create_date = models.DateTimeField(null=True, blank=True, auto_now_add=True) - created_by = models.CharField(max_length=20, blank=True, null=True, default="") - update_date = models.DateTimeField(blank=True, null=True) - updated_by = models.CharField(max_length=20, blank=True, null=True, default="") - mis_sync_flag = models.BooleanField(default=False, blank=True, null=True) - mis_sync_date = models.DateTimeField(blank=True, null=True) - ca_sync_flag = models.BooleanField(default=False, blank=True, null=True) - ca_sync_date = models.DateTimeField(blank=True, null=True) - business_office_code = models.CharField(max_length=4, blank=True, null=True) diff --git a/src/hct_mis_api/apps/erp_datahub/tasks/__init__.py b/src/hct_mis_api/apps/erp_datahub/tasks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/apps/erp_datahub/tasks/pull_from_erp_datahub.py b/src/hct_mis_api/apps/erp_datahub/tasks/pull_from_erp_datahub.py deleted file mode 100644 index e2128765da..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/tasks/pull_from_erp_datahub.py +++ /dev/null @@ -1,58 +0,0 @@ -from django.db.models import Q -from django.db.transaction import atomic - -from hct_mis_api.apps.payment.models import CashPlan, PaymentRecord -from hct_mis_api.apps.payment.utils import get_quantity_in_usd - - -class PullFromErpDatahubTask: - @atomic() - def execute(self) -> None: - self.update_cash_plans() - self.update_payment_records() - - @staticmethod - def update_cash_plans() -> None: - cash_plans_without_exchange_rate = CashPlan.objects.filter(exchange_rate__isnull=True) - - for cash_plan in cash_plans_without_exchange_rate: - cash_plan.exchange_rate = cash_plan.get_exchange_rate() - - for usd_field in CashPlan.usd_fields: - setattr( - cash_plan, - usd_field, - get_quantity_in_usd( - amount=getattr(cash_plan, usd_field.removesuffix("_usd")), - currency=cash_plan.currency, - exchange_rate=cash_plan.exchange_rate, - currency_exchange_date=cash_plan.currency_exchange_date, - ), - ) - - CashPlan.objects.bulk_update(cash_plans_without_exchange_rate, ["exchange_rate"] + CashPlan.usd_fields) - - @staticmethod - def update_payment_records() -> None: - payment_records_to_update = PaymentRecord.objects.filter( - Q(delivered_quantity_usd__isnull=True, delivered_quantity__isnull=False) - | Q(entitlement_quantity_usd__isnull=True, entitlement_quantity__isnull=False), - parent__isnull=False, - parent__exchange_rate__isnull=False, - ) - - for payment_record in payment_records_to_update: - for usd_field in PaymentRecord.usd_fields: - if getattr(payment_record, usd_field) is None: - setattr( - payment_record, - usd_field, - get_quantity_in_usd( - amount=getattr(payment_record, usd_field.removesuffix("_usd")), - currency=payment_record.currency, - exchange_rate=payment_record.parent.exchange_rate, - currency_exchange_date=payment_record.parent.currency_exchange_date, - ), - ) - - PaymentRecord.objects.bulk_update(payment_records_to_update, PaymentRecord.usd_fields) diff --git a/src/hct_mis_api/apps/erp_datahub/tasks/sync_to_mis_datahub.py b/src/hct_mis_api/apps/erp_datahub/tasks/sync_to_mis_datahub.py deleted file mode 100644 index ae003a93f1..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/tasks/sync_to_mis_datahub.py +++ /dev/null @@ -1,64 +0,0 @@ -from typing import Dict - -from django.db.models import Model, Q -from django.db.transaction import atomic -from django.utils import timezone - -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.erp_datahub.models import DownPayment, FundsCommitment -from hct_mis_api.apps.mis_datahub import models as mis_models - - -class SyncToMisDatahubTask: - @staticmethod - def get_model_dict(model: Model) -> Dict: - model_dict = {} - model_dict.update(model.__dict__) - if "_prefetched_objects_cache" in model_dict: - del model_dict["_prefetched_objects_cache"] - del model_dict["_state"] - del model_dict["business_office_code"] - return model_dict - - @atomic(using="cash_assist_datahub_erp") - @atomic() - def execute(self) -> None: - # have to be list because it used in another database - - parent_business_area_codes = list(BusinessArea.objects.filter(is_split=True).values_list("code", flat=True)) - normal_down_payments_to_sent = DownPayment.objects.exclude(business_area__in=parent_business_area_codes).filter( - mis_sync_flag=False - ) - normal_funds_commitments_to_sent = FundsCommitment.objects.exclude( - business_area__in=parent_business_area_codes - ).filter(mis_sync_flag=False) - down_payments_from_split_business_areas = DownPayment.objects.filter( - business_area__in=parent_business_area_codes, mis_sync_flag=False - ).exclude(Q(business_office_code__isnull=True) | Q(business_office_code="")) - funds_commitments_from_split_business_areas = FundsCommitment.objects.filter( - business_area__in=parent_business_area_codes, mis_sync_flag=False - ).exclude(Q(business_office_code__isnull=True) | Q(business_office_code="")) - - mis_down_payments_to_create = [] - funds_commitments_to_create = [] - for down_payment in normal_down_payments_to_sent: - mis_down_payment = mis_models.DownPayment(**SyncToMisDatahubTask.get_model_dict(down_payment)) - mis_down_payments_to_create.append(mis_down_payment) - for funds_commitment in normal_funds_commitments_to_sent: - mis_funds_commitment = mis_models.FundsCommitment(**SyncToMisDatahubTask.get_model_dict(funds_commitment)) - funds_commitments_to_create.append(mis_funds_commitment) - # with changed business areas - for down_payment in down_payments_from_split_business_areas: - mis_down_payment = mis_models.DownPayment(**SyncToMisDatahubTask.get_model_dict(down_payment)) - mis_down_payment.business_area = down_payment.business_office_code - mis_down_payments_to_create.append(mis_down_payment) - for funds_commitment in funds_commitments_from_split_business_areas: - mis_funds_commitment = mis_models.FundsCommitment(**SyncToMisDatahubTask.get_model_dict(funds_commitment)) - mis_funds_commitment.business_area = funds_commitment.business_office_code - funds_commitments_to_create.append(mis_funds_commitment) - mis_models.DownPayment.objects.bulk_create(mis_down_payments_to_create) - mis_models.FundsCommitment.objects.bulk_create(funds_commitments_to_create) - normal_down_payments_to_sent.update(mis_sync_flag=True, mis_sync_date=timezone.now()) - normal_funds_commitments_to_sent.update(mis_sync_flag=True, mis_sync_date=timezone.now()) - down_payments_from_split_business_areas.update(mis_sync_flag=True, mis_sync_date=timezone.now()) - funds_commitments_from_split_business_areas.update(mis_sync_flag=True, mis_sync_date=timezone.now()) diff --git a/src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/down_payment/assign_business_office.html b/src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/down_payment/assign_business_office.html deleted file mode 100644 index 78166c3834..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/down_payment/assign_business_office.html +++ /dev/null @@ -1,38 +0,0 @@ -{% extends "admin_extra_buttons/action_page.html" %} -{% load i18n admin_urls static admin_modify %} -{% block breadcrumbs %} - -{% endblock %} - -{% block content %} - - {% if warnings %} -

    Warnings

    - - {% for level, warn in warnings %} - - - - - {% endfor %} -
    {{ level|upper }}{{ warn }}
    - {% endif %} -

    Down Payment: {{ original }}

    -

    Current business area: {{ business_area }}

    -
    - {% csrf_token %} - {{ form.as_p }} - -
    -{% endblock content %} - -{% block submit_buttons_bottom %}{% endblock %} diff --git a/src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/funds_commitment/assign_business_office.html b/src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/funds_commitment/assign_business_office.html deleted file mode 100644 index 48c77b61be..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/funds_commitment/assign_business_office.html +++ /dev/null @@ -1,38 +0,0 @@ -{% extends "admin_extra_buttons/action_page.html" %} -{% load i18n admin_urls static admin_modify %} -{% block breadcrumbs %} - -{% endblock %} - -{% block content %} - - {% if warnings %} -

    Warnings

    - - {% for level, warn in warnings %} - - - - - {% endfor %} -
    {{ level|upper }}{{ warn }}
    - {% endif %} -

    Founds commitment: {{ original }}

    -

    Current business area: {{ business_area }}

    -
    - {% csrf_token %} - {{ form.as_p }} - -
    -{% endblock content %} - -{% block submit_buttons_bottom %}{% endblock %} diff --git a/src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/session/inspect.html b/src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/session/inspect.html deleted file mode 100644 index a53ec973e9..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/templates/admin/erp_datahub/session/inspect.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "admin_extra_buttons/action_page.html" %} -{% load i18n admin_urls static admin_modify %} -{% block extrahead %} - -{% endblock %} -{% block breadcrumbs %} - -{% endblock %} - -{% block content %} - - {% if warnings %} -

    Warnings

    - - {% for level, warn in warnings %} - - - - - {% endfor %} -
    {{ level|upper }}{{ warn }}
    - {% endif %} - - - - - - - - {% for model, info in data.items %} - - - - - - {% endfor %} -
    Component# Records-
    {{ info.meta.verbose_name_plural|title }}{{ info.count }}visit -
    -{% endblock content %} - -{% block submit_buttons_bottom %}{% endblock %} diff --git a/src/hct_mis_api/apps/erp_datahub/utils.py b/src/hct_mis_api/apps/erp_datahub/utils.py deleted file mode 100644 index 200382c82c..0000000000 --- a/src/hct_mis_api/apps/erp_datahub/utils.py +++ /dev/null @@ -1,43 +0,0 @@ -from decimal import Decimal -from typing import Optional - -from hct_mis_api.apps.core.exchange_rates import ExchangeRates -from hct_mis_api.apps.payment.models import PaymentRecord -from hct_mis_api.apps.program.models import CashPlan - - -def get_exchange_rate_for_cash_plan( - cash_plan: CashPlan, exchange_rates_client: Optional[ExchangeRates] = None -) -> Optional[float]: - if exchange_rates_client is None: - exchange_rates_client = ExchangeRates() - - exchange_rate = exchange_rates_client.get_exchange_rate_for_currency_code( - cash_plan.currency, cash_plan.dispersion_date - ) - - return exchange_rate - - -def get_payment_record_delivered_quantity_in_usd( - payment_record: PaymentRecord, exchange_rates_client: Optional[ExchangeRates] = None -) -> Optional[Decimal]: - if ( - not payment_record.delivered_quantity - or not payment_record.cash_plan - or not payment_record.cash_plan.exchange_rate - ): - return None - - if exchange_rates_client is None: - exchange_rates_client = ExchangeRates() - - exchange_rate = exchange_rates_client.get_exchange_rate_for_currency_code( - payment_record.currency, payment_record.cash_plan.dispersion_date - ) - if exchange_rate is None: - exchange_rate = Decimal(1) - else: - exchange_rate = Decimal(exchange_rate) - - return Decimal(payment_record.delivered_quantity / exchange_rate).quantize(Decimal(".01")) diff --git a/src/hct_mis_api/apps/mis_datahub/__init__.py b/src/hct_mis_api/apps/mis_datahub/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/apps/mis_datahub/admin.py b/src/hct_mis_api/apps/mis_datahub/admin.py deleted file mode 100644 index 0287f7ebc6..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/admin.py +++ /dev/null @@ -1,311 +0,0 @@ -import logging -from typing import TYPE_CHECKING, Any, Optional, Tuple -from uuid import UUID - -from django.contrib import admin, messages -from django.contrib.admin.models import DELETION, LogEntry -from django.contrib.contenttypes.models import ContentType -from django.db.transaction import atomic -from django.http import HttpRequest, HttpResponseRedirect -from django.template.response import TemplateResponse -from django.urls import reverse -from django.utils.safestring import mark_safe - -from admin_extra_buttons.decorators import button, link -from admin_extra_buttons.mixins import confirm_action -from adminfilters.filters import ValueFilter -from smart_admin.mixins import FieldsetMixin as SmartFieldsetMixin - -from hct_mis_api.apps.household import models as households -from hct_mis_api.apps.mis_datahub.models import ( - Document, - DownPayment, - FundsCommitment, - Household, - Individual, - IndividualRoleInHousehold, - Program, - Session, - TargetPopulation, - TargetPopulationEntry, -) -from hct_mis_api.apps.program import models as programs -from hct_mis_api.apps.targeting import models as targeting -from hct_mis_api.apps.utils.admin import HOPEModelAdminBase -from hct_mis_api.apps.utils.admin import HUBBusinessAreaFilter as BusinessAreaFilter -from hct_mis_api.apps.utils.security import is_root - -if TYPE_CHECKING: - from django.db.models.query import QuerySet, _QuerySet - -logger = logging.getLogger(__name__) - - -class HUBAdminMixin(HOPEModelAdminBase): - @button(label="Truncate", css_class="btn-danger", permission=is_root) - def truncate(self, request: HttpRequest) -> None: - if not request.headers.get("x-root-access") == "XMLHttpRequest": - self.message_user(request, "You are not allowed to perform this action", messages.ERROR) - return None - if request.method == "POST": - with atomic(): - LogEntry.objects.log_action( - user_id=request.user.pk, - content_type_id=ContentType.objects.get_for_model(self.model).pk, - object_id=None, # type: ignore # None is not a valid object_id but for quite some time noone raised an issue with that so I guess it's intentional somehow - object_repr=f"TRUNCATE TABLE {self.model._meta.verbose_name}", - action_flag=DELETION, - change_message="truncate table", - ) - from django.db import connections - - conn = connections[self.model.objects.db] - cursor = conn.cursor() - cursor.execute(f"TRUNCATE TABLE '{self.model._meta.db_table}' RESTART IDENTITY CASCADE") - else: - return confirm_action( - self, - request, - self.truncate, - mark_safe( - """ -

    This is a low level system feature

    -

    Continuing irreversibly delete all table content

    -""" - ), - "Successfully executed", - ) - - -@admin.register(Household) -class HouseholdAdmin(HUBAdminMixin): - list_filter = (("session__id", ValueFilter), BusinessAreaFilter) - - raw_id_fields = ("session",) - - @link() - def members_sent_to_the_hub(self, button: button) -> Optional[str]: - if "original" in button.context: - obj = button.context["original"] - url = reverse("admin:mis_datahub_individual_changelist") - # http://localhost:9000/api/admin/mis_datahub/individual/?session=1&household_mis_id|iexact=87eb7e38-088d-42e4-ba1d-0b96bc32605a - return f"{url}?session={obj.pk}&household_mis_id={obj.mis_id}" - else: - button.visible = False - return None - - @button() - def see_hope_record(self, request: HttpRequest, pk: UUID) -> HttpResponseRedirect: - obj = self.get_object(request, str(pk)) - hh = households.Household.objects.get(id=obj.mis_id) - url = reverse("admin:household_individual_change", args=[hh.pk]) - return HttpResponseRedirect(url) - - -@admin.register(Individual) -class IndividualAdmin(HUBAdminMixin): - list_display = ("session", "unicef_id", "mis_id", "household_mis_id", "family_name", "given_name") - list_filter = ( - BusinessAreaFilter, - ("session__id", ValueFilter), - ("unicef_id", ValueFilter), - ("mis_id", ValueFilter), - ("household_mis_id", ValueFilter), - ) - raw_id_fields = ("session",) - - @link() - def household(self, button: button) -> Optional[str]: - if "original" in button.context: - obj = button.context["original"] - url = reverse("admin:mis_datahub_household_changelist") - # http://localhost:9000/api/admin/mis_datahub/individual/?session=1&household_mis_id|iexact=87eb7e38-088d-42e4-ba1d-0b96bc32605a - return f"{url}?session={obj.pk}&household_mis_id={obj.mis_id}" - else: - button.visible = False - return None - - -@admin.register(FundsCommitment) -class FundsCommitmentAdmin(HUBAdminMixin): - list_display = ( - "rec_serial_number", - "business_area", - "funds_commitment_item", - "funds_commitment_number", - "posting_date", - ) - list_filter = ( - BusinessAreaFilter, - "business_area", - "posting_date", - "mis_sync_date", - "ca_sync_date", - ("business_area", ValueFilter), - ) - date_hierarchy = "create_date" - search_fields = ("rec_serial_number", "vendor_id", "wbs_element", "funds_commitment_number") - - -@admin.register(DownPayment) -class DownPaymentAdmin(HUBAdminMixin): - filters = ( - BusinessAreaFilter, - ("rec_serial_number", ValueFilter), - "create_date", - "mis_sync_flag", - "ca_sync_flag", - ) - - -@admin.register(IndividualRoleInHousehold) -class IndividualRoleInHouseholdAdmin(HUBAdminMixin): - list_filter = (("session__id", ValueFilter),) - - -@admin.register(Session) -class SessionAdmin(SmartFieldsetMixin, HUBAdminMixin): - list_display = ("timestamp", "id", "source", "status", "last_modified_date", "business_area") - date_hierarchy = "timestamp" - list_filter = ( - "status", - BusinessAreaFilter, - ) - ordering = ("-timestamp",) - search_fields = ("id",) - - @link() - def target_population(self, button: button) -> Optional[str]: - if "original" in button.context: - obj = button.context["original"] - url = reverse("admin:mis_datahub_targetpopulation_changelist") - return f"{url}?session={obj.pk}" - else: - button.visible = False - return None - - @link() - def individuals(self, button: button) -> Optional[str]: - if "original" in button.context: - obj = button.context["original"] - url = reverse("admin:mis_datahub_individual_changelist") - return f"{url}?session={obj.pk}" - else: - button.visible = False - return None - - @link() - def households(self, button: button) -> Optional[str]: - if "original" in button.context: - obj = button.context["original"] - url = reverse("admin:mis_datahub_household_changelist") - return f"{url}?session={obj.pk}" - else: - button.visible = False - return None - - @button(permission="account.can_inspect") - def inspect(self, request: HttpRequest, pk: UUID) -> TemplateResponse: - context = self.get_common_context(request, pk) - obj = context["original"] - context["title"] = f"Session {obj.pk} - {obj.timestamp} - {obj.status}" - context["data"] = {} - for model in [ - Program, - TargetPopulation, - Household, - Individual, - IndividualRoleInHousehold, - TargetPopulationEntry, - Document, - ]: - context["data"][model] = {"count": model.objects.filter(session=pk).count(), "meta": model._meta} - - return TemplateResponse(request, "admin/mis_datahub/session/inspect.html", context) - - @button() - def reset_sync_date(self, request: HttpRequest, pk: UUID) -> Optional[TemplateResponse]: - if request.method == "POST": - try: - with atomic(): - obj = self.get_object(request, str(pk)) - # Programs - hub_program_ids = list(Program.objects.filter(session=obj.id).values_list("mis_id", flat=True)) - programs.Program.objects.filter(id__in=hub_program_ids).update(last_sync_at=None) - # Documents - hub_document_ids = list(Document.objects.filter(session=obj.id).values_list("mis_id", flat=True)) - households.Document.objects.filter(id__in=hub_document_ids).update(last_sync_at=None) - # HH / Ind - for hub_tp in TargetPopulation.objects.filter(session=obj.id): - tp = targeting.TargetPopulation.objects.get(id=hub_tp.mis_id) - tp.households.update(last_sync_at=None) - households.Individual.objects.filter(household__target_populations=tp).update(last_sync_at=None) - - self.message_user(request, "Done", messages.SUCCESS) - - except Exception as e: - logger.exception(e) - self.message_user(request, str(e), messages.ERROR) - else: - return confirm_action( - self, - request, - self.reset_sync_date, - "Continuing will reset last_sync_date of any" " object linked to this Session.", - "Successfully executed", - ) - return None - - -@admin.register(TargetPopulationEntry) -class TargetPopulationEntryAdmin(HUBAdminMixin): - list_filter = (("session__id", ValueFilter),) - raw_id_fields = ("session",) - - -@admin.register(TargetPopulation) -class TargetPopulationAdmin(HUBAdminMixin): - # list_display = ('name', ) - list_filter = (("session__id", ValueFilter), BusinessAreaFilter) - raw_id_fields = ("session",) - search_fields = ("name",) - - def get_search_results( - self, request: HttpRequest, queryset: "QuerySet", search_term: str - ) -> Tuple["_QuerySet[Any, Any]", bool]: - queryset, use_distinct = super().get_search_results(request, queryset, search_term) - return queryset, use_distinct - - @link() - def individuals(self, button: button) -> Optional[str]: - if "original" in button.context: - obj = button.context["original"] - url = reverse("admin:mis_datahub_individual_changelist") - return f"{url}?session={obj.session.pk}" - else: - button.visible = False - return None - - @link() - def households(self, button: button) -> Optional[str]: - if "original" in button.context: - obj = button.context["original"] - url = reverse("admin:mis_datahub_household_changelist") - return f"{url}?session={obj.session.pk}" - else: - button.visible = False - return None - - -@admin.register(Program) -class ProgramAdmin(HUBAdminMixin): - list_filter = (("session__id", ValueFilter), BusinessAreaFilter) - search_fields = ("name",) - raw_id_fields = ("session",) - - -@admin.register(Document) -class DocumentAdmin(HUBAdminMixin): - list_display = ("type", "number") - list_filter = (("session__id", ValueFilter), BusinessAreaFilter) - raw_id_fields = ("session",) diff --git a/src/hct_mis_api/apps/mis_datahub/apps.py b/src/hct_mis_api/apps/mis_datahub/apps.py deleted file mode 100644 index 09f1cd5423..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class Config(AppConfig): - name = "hct_mis_api.apps.mis_datahub" - verbose_name = "HUB MIS (Hope->CA)" diff --git a/src/hct_mis_api/apps/mis_datahub/celery_tasks.py b/src/hct_mis_api/apps/mis_datahub/celery_tasks.py deleted file mode 100644 index c20dfdde81..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/celery_tasks.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging -from typing import Any, Dict -from uuid import UUID - -from hct_mis_api.apps.core.celery import app -from hct_mis_api.apps.utils.logs import log_start_and_end -from hct_mis_api.apps.utils.sentry import sentry_tags, set_sentry_business_area_tag - -logger = logging.getLogger(__name__) - - -@app.task(bind=True, default_retry_delay=60, max_retries=3) -@log_start_and_end -@sentry_tags -def send_target_population_task(self: Any, target_population_id: UUID) -> Dict: - try: - from hct_mis_api.apps.mis_datahub.tasks.send_tp_to_datahub import ( - SendTPToDatahubTask, - ) - from hct_mis_api.apps.targeting.models import TargetPopulation - - target_population = TargetPopulation.objects.select_related("program").get(id=target_population_id) - set_sentry_business_area_tag(target_population.business_area.name) - return SendTPToDatahubTask().execute(target_population) - except Exception as e: - logger.exception(e) - raise self.retry(exc=e) diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/0001_migration_squashed_0046_migration.py b/src/hct_mis_api/apps/mis_datahub/migrations/0001_migration_squashed_0046_migration.py deleted file mode 100644 index 3943262084..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/migrations/0001_migration_squashed_0046_migration.py +++ /dev/null @@ -1,1365 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-08 20:01 - -from django.db import migrations, models -import django.db.models.deletion -import django_countries.fields -from django.utils import timezone - - -# Functions from the following migrations need manual copying. -# Move them and any dependencies into this file, then update the -# RunPython operations to refer to the local versions: -def mark_as_sent(apps, schema_editor): - try: - DHHousehold = apps.get_model("mis_datahub", "Household") - Household = apps.get_model("household", "Household") - mis_ids = list(DHHousehold.objects.values_list("mis_id", flat=True)) - Household.objects.filter(id__in=mis_ids).update(last_sync_at=timezone.now()) - except: - pass - - -def reverse_mark(apps, schema_editor): - try: - DHHousehold = apps.get_model("mis_datahub", "Household") - Household = apps.get_model("household", "Household") - mis_ids = list(DHHousehold.objects.values_list("mis_id", flat=True)) - Household.objects.filter(id__in=mis_ids).update(last_sync_at=None) - except: - pass - - -def mark_as_sent_program(apps, schema_editor): - try: - DHProgram = apps.get_model("mis_datahub", "Program") - Program = apps.get_model("program", "Program") - mis_ids = list(DHProgram.objects.values_list("mis_id", flat=True)) - Program.objects.filter(id__in=mis_ids).update(last_sync_at=timezone.now()) - except: - pass - - -def reverse_mark_program(apps, schema_editor): - try: - DHProgram = apps.get_model("mis_datahub", "Program") - Program = apps.get_model("program", "Program") - mis_ids = list(DHProgram.objects.values_list("mis_id", flat=True)) - Program.objects.filter(id__in=mis_ids).update(last_sync_at=None) - except: - pass - - -def unmark_sent_tp(apps, schema_editor): - try: - TargetPopulation = apps.get_model("targeting", "TargetPopulation") - Program = apps.get_model("program", "Program") - Household = apps.get_model("household", "Household") - TargetPopulation.objects.filter(status="FINALIZED").update( - sent_to_datahub=False - ) - Household.objects.update(last_sync_at=None) - Program.objects.update(last_sync_at=None) - except: - pass - - -class Migration(migrations.Migration): - dependencies = [] - - operations = [ - migrations.CreateModel( - name="Session", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("timestamp", models.DateTimeField(auto_now_add=True)), - ( - "source", - models.CharField( - choices=[("MIS", "HCT-MIS"), ("CA", "Cash Assist")], - max_length=3, - ), - ), - ( - "status", - models.CharField( - choices=[ - ("NEW", "New"), - ("READY", "Ready"), - ("PROCESSING", "Processing"), - ("COMPLETED", "Completed"), - ("FAILED", "Failed"), - ], - max_length=11, - ), - ), - ("last_modified_date", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="TargetPopulationEntry", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("household_unhcr_id", models.CharField(max_length=255, null=True)), - ("individual_unhcr_id", models.CharField(max_length=255, null=True)), - ("household_mis_id", models.UUIDField(null=True)), - ("individual_mis_id", models.UUIDField(null=True)), - ("target_population_mis_id", models.UUIDField()), - ( - "vulnerability_score", - models.DecimalField( - blank=True, - decimal_places=3, - help_text="Written by a tool such as Corticon.", - max_digits=6, - null=True, - ), - ), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="TargetPopulation", - fields=[ - ("mis_id", models.UUIDField(primary_key=True, serialize=False)), - ("name", models.CharField(max_length=255)), - ( - "population_type", - models.CharField(default="HOUSEHOLD", max_length=20), - ), - ("targeting_criteria", models.TextField()), - ("active_households", models.PositiveIntegerField(default=0)), - ("program_mis_id", models.UUIDField()), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="Program", - fields=[ - ("mis_id", models.UUIDField(primary_key=True, serialize=False)), - ("business_area", models.CharField(max_length=20)), - ("ca_id", models.CharField(max_length=255)), - ("ca_hash_id", models.CharField(max_length=255)), - ("name", models.CharField(max_length=255)), - ( - "scope", - models.CharField( - choices=[ - ("FOR_PARTNERS", "For partners"), - ("UNICEF", "Unicef"), - ], - max_length=50, - ), - ), - ("start_date", models.DateTimeField()), - ("end_date", models.DateTimeField()), - ("description", models.CharField(max_length=255, null=True)), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="Individual", - fields=[ - ("mis_id", models.UUIDField(primary_key=True, serialize=False)), - ("unhcr_id", models.CharField(max_length=255, null=True)), - ("household_mis_id", models.UUIDField()), - ( - "status", - models.CharField( - choices=[("INACTIVE", "Inactive"), ("ACTIVE", "Active")], - max_length=50, - null=True, - ), - ), - ("national_id_number", models.CharField(max_length=255, null=True)), - ("full_name", models.CharField(max_length=255)), - ("family_name", models.CharField(max_length=255, null=True)), - ("given_name", models.CharField(max_length=255, null=True)), - ("middle_name", models.CharField(max_length=255, null=True)), - ( - "sex", - models.CharField( - choices=[("MALE", "Male"), ("FEMALE", "Female")], max_length=255 - ), - ), - ("date_of_birth", models.DateField()), - ("estimated_date_of_birth", models.BooleanField()), - ( - "relationship", - models.CharField( - choices=[ - ( - "NON_BENEFICIARY", - "Not a Family Member. Can only act as a recipient.", - ), - ("HEAD", "Head of household (self)"), - ("SON_DAUGHTER", "Son / Daughter"), - ("WIFE_HUSBAND", "Wife / Husband"), - ("BROTHER_SISTER", "Brother / Sister"), - ("MOTHER_FATHER", "Mother / Father"), - ("AUNT_UNCLE", "Aunt / Uncle"), - ("GRANDMOTHER_GRANDFATHER", "Grandmother / Grandfather"), - ( - "MOTHERINLAW_FATHERINLAW", - "Mother-in-law / Father-in-law", - ), - ("DAUGHTERINLAW_SONINLAW", "Daughter-in-law / Son-in-law"), - ( - "SISTERINLAW_BROTHERINLAW", - "Sister-in-law / Brother-in-law", - ), - ("GRANDDAUGHTER_GRANDSON", "Granddaughter / Grandson"), - ("NEPHEW_NIECE", "Nephew / Niece"), - ("COUSIN", "Cousin"), - ], - max_length=255, - null=True, - ), - ), - ( - "role", - models.CharField( - choices=[ - ("PRIMARY", "Primary collector"), - ("ALTERNATE", "Alternate collector"), - ("NO_ROLE", "None"), - ], - max_length=255, - null=True, - ), - ), - ( - "marital_status", - models.CharField( - choices=[ - ("SINGLE", "SINGLE"), - ("MARRIED", "Married"), - ("WIDOW", "Widow"), - ("DIVORCED", "Divorced"), - ("SEPARATED", "Separated"), - ], - max_length=255, - ), - ), - ("phone_number", models.CharField(max_length=14, null=True)), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="Household", - fields=[ - ("mis_id", models.UUIDField(primary_key=True, serialize=False)), - ("unhcr_id", models.CharField(max_length=255, null=True)), - ( - "status", - models.CharField( - choices=[("ACTIVE", "Active"), ("INACTIVE", "Inactive")], - default="ACTIVE", - max_length=20, - ), - ), - ("household_size", models.PositiveIntegerField()), - ("form_number", models.CharField(max_length=255, null=True)), - ("address", models.CharField(max_length=255, null=True)), - ("admin1", models.CharField(max_length=255, null=True)), - ("admin2", models.CharField(max_length=255, null=True)), - ( - "country", - django_countries.fields.CountryField(max_length=2, null=True), - ), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "abstract": False, - }, - ), - migrations.RunPython(mark_as_sent, reverse_mark), - migrations.RunPython(mark_as_sent_program, reverse_mark_program), - migrations.RemoveField( - model_name="individual", - name="session", - ), - migrations.RemoveField( - model_name="program", - name="session", - ), - migrations.RemoveField( - model_name="targetpopulation", - name="session", - ), - migrations.RemoveField( - model_name="targetpopulationentry", - name="session", - ), - migrations.DeleteModel( - name="Household", - ), - migrations.DeleteModel( - name="Individual", - ), - migrations.DeleteModel( - name="Program", - ), - migrations.DeleteModel( - name="Session", - ), - migrations.DeleteModel( - name="TargetPopulation", - ), - migrations.DeleteModel( - name="TargetPopulationEntry", - ), - migrations.CreateModel( - name="Session", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("timestamp", models.DateTimeField(auto_now_add=True)), - ( - "source", - models.CharField( - choices=[("MIS", "HCT-MIS"), ("CA", "Cash Assist")], - max_length=3, - ), - ), - ( - "status", - models.CharField( - choices=[ - ("NEW", "New"), - ("READY", "Ready"), - ("PROCESSING", "Processing"), - ("COMPLETED", "Completed"), - ("FAILED", "Failed"), - ], - max_length=11, - ), - ), - ("last_modified_date", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="TargetPopulationEntry", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("household_unhcr_id", models.CharField(max_length=255, null=True)), - ("individual_unhcr_id", models.CharField(max_length=255, null=True)), - ("household_mis_id", models.UUIDField(null=True)), - ("individual_mis_id", models.UUIDField(null=True)), - ("target_population_mis_id", models.UUIDField()), - ( - "vulnerability_score", - models.DecimalField( - blank=True, - decimal_places=3, - help_text="Written by a tool such as Corticon.", - max_digits=6, - null=True, - ), - ), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "unique_together": { - ("session", "household_mis_id", "target_population_mis_id") - }, - }, - ), - migrations.CreateModel( - name="TargetPopulation", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("mis_id", models.UUIDField()), - ("name", models.CharField(max_length=255)), - ( - "population_type", - models.CharField(default="HOUSEHOLD", max_length=20), - ), - ("targeting_criteria", models.TextField()), - ("active_households", models.PositiveIntegerField(default=0)), - ("program_mis_id", models.UUIDField()), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "unique_together": {("session", "mis_id")}, - }, - ), - migrations.CreateModel( - name="Program", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("mis_id", models.UUIDField()), - ("business_area", models.CharField(max_length=20)), - ("ca_id", models.CharField(max_length=255)), - ("ca_hash_id", models.CharField(max_length=255)), - ("name", models.CharField(max_length=255)), - ( - "scope", - models.CharField( - choices=[ - ("FOR_PARTNERS", "For partners"), - ("UNICEF", "Unicef"), - ], - max_length=50, - ), - ), - ("start_date", models.DateTimeField()), - ("end_date", models.DateTimeField()), - ("description", models.CharField(max_length=255, null=True)), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "unique_together": {("session", "mis_id")}, - }, - ), - migrations.CreateModel( - name="Individual", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("mis_id", models.UUIDField()), - ("unhcr_id", models.CharField(max_length=255, null=True)), - ("household_mis_id", models.UUIDField()), - ( - "status", - models.CharField( - choices=[("INACTIVE", "Inactive"), ("ACTIVE", "Active")], - max_length=50, - null=True, - ), - ), - ("national_id_number", models.CharField(max_length=255, null=True)), - ("full_name", models.CharField(max_length=255)), - ("family_name", models.CharField(max_length=255, null=True)), - ("given_name", models.CharField(max_length=255, null=True)), - ("middle_name", models.CharField(max_length=255, null=True)), - ( - "sex", - models.CharField( - choices=[("MALE", "Male"), ("FEMALE", "Female")], max_length=255 - ), - ), - ("date_of_birth", models.DateField()), - ("estimated_date_of_birth", models.BooleanField()), - ( - "relationship", - models.CharField( - choices=[ - ( - "NON_BENEFICIARY", - "Not a Family Member. Can only act as a recipient.", - ), - ("HEAD", "Head of household (self)"), - ("SON_DAUGHTER", "Son / Daughter"), - ("WIFE_HUSBAND", "Wife / Husband"), - ("BROTHER_SISTER", "Brother / Sister"), - ("MOTHER_FATHER", "Mother / Father"), - ("AUNT_UNCLE", "Aunt / Uncle"), - ("GRANDMOTHER_GRANDFATHER", "Grandmother / Grandfather"), - ( - "MOTHERINLAW_FATHERINLAW", - "Mother-in-law / Father-in-law", - ), - ("DAUGHTERINLAW_SONINLAW", "Daughter-in-law / Son-in-law"), - ( - "SISTERINLAW_BROTHERINLAW", - "Sister-in-law / Brother-in-law", - ), - ("GRANDDAUGHTER_GRANDSON", "Granddaughter / Grandson"), - ("NEPHEW_NIECE", "Nephew / Niece"), - ("COUSIN", "Cousin"), - ], - max_length=255, - null=True, - ), - ), - ( - "role", - models.CharField( - choices=[ - ("PRIMARY", "Primary collector"), - ("ALTERNATE", "Alternate collector"), - ("NO_ROLE", "None"), - ], - max_length=255, - null=True, - ), - ), - ( - "marital_status", - models.CharField( - choices=[ - ("SINGLE", "SINGLE"), - ("MARRIED", "Married"), - ("WIDOW", "Widow"), - ("DIVORCED", "Divorced"), - ("SEPARATED", "Separated"), - ], - max_length=255, - ), - ), - ("phone_number", models.CharField(max_length=14, null=True)), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "unique_together": {("session", "mis_id")}, - }, - ), - migrations.CreateModel( - name="Household", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("mis_id", models.UUIDField()), - ("unhcr_id", models.CharField(max_length=255, null=True)), - ( - "status", - models.CharField( - choices=[("ACTIVE", "Active"), ("INACTIVE", "Inactive")], - default="ACTIVE", - max_length=20, - ), - ), - ("household_size", models.PositiveIntegerField()), - ("form_number", models.CharField(max_length=255, null=True)), - ("address", models.CharField(max_length=255, null=True)), - ("admin1", models.CharField(max_length=255, null=True)), - ("admin2", models.CharField(max_length=255, null=True)), - ( - "country", - django_countries.fields.CountryField(max_length=2, null=True), - ), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ], - options={ - "unique_together": {("session", "mis_id")}, - }, - ), - migrations.RunPython(unmark_sent_tp, migrations.RunPython.noop), - migrations.AddField( - model_name="household", - name="registration_date", - field=models.DateField(null=True), - ), - migrations.AlterField( - model_name="household", - name="country", - field=models.CharField(max_length=3, null=True), - ), - migrations.AddField( - model_name="program", - name="individual_data_needed", - field=models.BooleanField(default=False), - ), - migrations.RemoveField( - model_name="individual", - name="role", - ), - migrations.AlterField( - model_name="individual", - name="household_mis_id", - field=models.UUIDField(null=True), - ), - migrations.AddField( - model_name="session", - name="business_area", - field=models.CharField( - default="0060", - help_text="Same as the business area set on program, but\n this is set as the same value, and all other\n models this way can get easy access to the business area\n via the session.", - max_length=20, - ), - preserve_default=False, - ), - migrations.AlterField( - model_name="individual", - name="phone_number", - field=models.CharField(max_length=60, null=True), - ), - migrations.AlterField( - model_name="individual", - name="marital_status", - field=models.CharField( - choices=[ - ("SINGLE", "SINGLE"), - ("MARRIED", "Married"), - ("WIDOWED", "Widowed"), - ("DIVORCED", "Divorced"), - ("SEPARATED", "Separated"), - ], - max_length=255, - ), - ), - migrations.AlterField( - model_name="individual", - name="marital_status", - field=models.CharField( - choices=[ - ("SINGLE", "Single"), - ("MARRIED", "Married"), - ("WIDOWED", "Widowed"), - ("DIVORCED", "Divorced"), - ("SEPARATED", "Separated"), - ], - max_length=255, - ), - ), - migrations.RemoveField( - model_name="individual", - name="national_id_number", - ), - migrations.AlterField( - model_name="individual", - name="relationship", - field=models.CharField( - choices=[ - ("UNKNOWN", "Unknown"), - ( - "NON_BENEFICIARY", - "Not a Family Member. Can only act as a recipient.", - ), - ("HEAD", "Head of household (self)"), - ("SON_DAUGHTER", "Son / Daughter"), - ("WIFE_HUSBAND", "Wife / Husband"), - ("BROTHER_SISTER", "Brother / Sister"), - ("MOTHER_FATHER", "Mother / Father"), - ("AUNT_UNCLE", "Aunt / Uncle"), - ("GRANDMOTHER_GRANDFATHER", "Grandmother / Grandfather"), - ("MOTHERINLAW_FATHERINLAW", "Mother-in-law / Father-in-law"), - ("DAUGHTERINLAW_SONINLAW", "Daughter-in-law / Son-in-law"), - ("SISTERINLAW_BROTHERINLAW", "Sister-in-law / Brother-in-law"), - ("GRANDDAUGHTER_GRANDSON", "Granddaughter / Grandson"), - ("NEPHEW_NIECE", "Nephew / Niece"), - ("COUSIN", "Cousin"), - ], - max_length=255, - null=True, - ), - ), - migrations.AddField( - model_name="household", - name="residence_status", - field=models.CharField( - choices=[ - ("", "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"), - ], - max_length=255, - null=True, - ), - ), - migrations.AlterField( - model_name="individual", - name="marital_status", - field=models.CharField( - choices=[ - ("", "None"), - ("SINGLE", "Single"), - ("MARRIED", "Married"), - ("WIDOWED", "Widowed"), - ("DIVORCED", "Divorced"), - ("SEPARATED", "Separated"), - ], - max_length=255, - ), - ), - migrations.AlterField( - model_name="individual", - name="marital_status", - field=models.CharField( - choices=[ - ("", "None"), - ("SINGLE", "Single"), - ("MARRIED", "Married"), - ("WIDOWED", "Widowed"), - ("DIVORCED", "Divorced"), - ("SEPARATED", "Separated"), - ], - max_length=255, - null=True, - ), - ), - migrations.AddField( - model_name="individual", - name="pregnant", - field=models.BooleanField(null=True), - ), - migrations.AlterField( - model_name="session", - name="status", - field=models.CharField( - choices=[ - ("NEW", "New"), - ("READY", "Ready"), - ("PROCESSING", "Processing"), - ("COMPLETED", "Completed"), - ("FAILED", "Failed"), - ("EMPTY", "Empty"), - ], - max_length=11, - ), - ), - migrations.AddField( - model_name="individual", - name="sanction_list_confirmed_match", - field=models.BooleanField(default=False), - ), - migrations.AlterField( - model_name="session", - name="status", - field=models.CharField( - choices=[ - ("NEW", "New"), - ("READY", "Ready"), - ("PROCESSING", "Processing"), - ("COMPLETED", "Completed"), - ("FAILED", "Failed"), - ("EMPTY", "Empty"), - ("IGNORED", "Ignored"), - ], - max_length=11, - ), - ), - migrations.AddField( - model_name="session", - name="sentry_id", - field=models.CharField(blank=True, default="", max_length=100, null=True), - ), - migrations.AddField( - model_name="session", - name="traceback", - field=models.TextField(blank=True, default="", null=True), - ), - migrations.AlterField( - model_name="session", - name="status", - field=models.CharField( - choices=[ - ("NEW", "New"), - ("READY", "Ready"), - ("PROCESSING", "Processing"), - ("COMPLETED", "Completed"), - ("FAILED", "Failed"), - ("EMPTY", "Empty"), - ("IGNORED", "Ignored"), - ("LOADING", "Loading"), - ("ERRORED", "Errored"), - ], - max_length=11, - ), - ), - migrations.AddField( - model_name="household", - name="village", - field=models.CharField(blank=True, max_length=250, null=True), - ), - migrations.AlterField( - model_name="program", - name="ca_hash_id", - field=models.CharField(max_length=255, null=True), - ), - migrations.AlterField( - model_name="program", - name="ca_id", - field=models.CharField(max_length=255, null=True), - ), - migrations.CreateModel( - name="DownPayment", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "rec_serial_number", - models.CharField(blank=True, max_length=10, null=True), - ), - ("business_area", models.CharField(max_length=4)), - ("down_payment_reference", models.CharField(max_length=20)), - ("document_type", models.CharField(max_length=10)), - ("consumed_fc_number", models.CharField(max_length=10)), - ( - "total_down_payment_amount_local", - models.DecimalField(decimal_places=2, max_digits=15), - ), - ( - "total_down_payment_amount_usd", - models.DecimalField( - blank=True, decimal_places=2, max_digits=15, null=True - ), - ), - ( - "currency_code", - models.CharField(blank=True, max_length=5, null=True), - ), - ("posting_date", models.DateField(blank=True, null=True)), - ("doc_year", models.IntegerField(blank=True, null=True)), - ("doc_number", models.CharField(blank=True, max_length=10, null=True)), - ("doc_item_number", models.CharField(max_length=3, null=True)), - ("create_date", models.DateTimeField(auto_now_add=True, null=True)), - ( - "created_by", - models.CharField(blank=True, default="", max_length=20, null=True), - ), - ("update_date", models.DateTimeField(blank=True, null=True)), - ( - "updated_by", - models.CharField(blank=True, default="", max_length=20, null=True), - ), - ( - "mis_sync_flag", - models.BooleanField(blank=True, default=False, null=True), - ), - ("mis_sync_date", models.DateTimeField(blank=True, null=True)), - ( - "ca_sync_flag", - models.BooleanField(blank=True, default=False, null=True), - ), - ("ca_sync_date", models.DateTimeField(blank=True, null=True)), - ], - ), - migrations.CreateModel( - name="FundsCommitment", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "rec_serial_number", - models.CharField(blank=True, max_length=10, null=True), - ), - ( - "business_area", - models.CharField(blank=True, max_length=4, null=True), - ), - ( - "funds_commitment_number", - models.CharField(blank=True, max_length=10, null=True), - ), - ( - "document_type", - models.CharField(blank=True, max_length=2, null=True), - ), - ( - "document_text", - models.CharField(blank=True, max_length=50, null=True), - ), - ( - "currency_code", - models.CharField(blank=True, max_length=5, null=True), - ), - ("gl_account", models.CharField(blank=True, max_length=10, null=True)), - ( - "commitment_amount_local", - models.DecimalField( - blank=True, decimal_places=2, max_digits=15, null=True - ), - ), - ( - "commitment_amount_usd", - models.DecimalField( - blank=True, decimal_places=2, max_digits=15, null=True - ), - ), - ( - "total_open_amount_local", - models.DecimalField( - blank=True, decimal_places=2, max_digits=15, null=True - ), - ), - ( - "total_open_amount_usd", - models.DecimalField( - blank=True, decimal_places=2, max_digits=15, null=True - ), - ), - ("vendor_id", models.CharField(blank=True, max_length=10, null=True)), - ("posting_date", models.DateField(blank=True, null=True)), - ( - "vision_approval", - models.CharField(blank=True, max_length=1, null=True), - ), - ("document_reference", models.CharField(max_length=16, null=True)), - ("fc_status", models.CharField(blank=True, max_length=1, null=True)), - ("create_date", models.DateTimeField(auto_now_add=True, null=True)), - ( - "created_by", - models.CharField(blank=True, default="", max_length=20, null=True), - ), - ("update_date", models.DateTimeField(blank=True, null=True)), - ( - "updated_by", - models.CharField(blank=True, default="", max_length=20, null=True), - ), - ( - "mis_sync_flag", - models.BooleanField(blank=True, default=False, null=True), - ), - ("mis_sync_date", models.DateTimeField(blank=True, null=True)), - ( - "ca_sync_flag", - models.BooleanField(blank=True, default=False, null=True), - ), - ("ca_sync_date", models.DateTimeField(blank=True, null=True)), - ], - ), - migrations.AlterField( - model_name="household", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="individual", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="program", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="session", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="targetpopulation", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="targetpopulationentry", - name="id", - field=models.BigAutoField( - auto_created=True, primary_key=True, serialize=False, verbose_name="ID" - ), - ), - migrations.AlterField( - model_name="individual", - name="marital_status", - field=models.CharField( - choices=[ - ("", "None"), - ("DIVORCED", "Divorced"), - ("MARRIED", "Married"), - ("SEPARATED", "Separated"), - ("SINGLE", "Single"), - ("WIDOWED", "Widowed"), - ], - max_length=255, - null=True, - ), - ), - migrations.AlterField( - model_name="individual", - name="relationship", - field=models.CharField( - choices=[ - ("UNKNOWN", "Unknown"), - ("AUNT_UNCLE", "Aunt / Uncle"), - ("BROTHER_SISTER", "Brother / Sister"), - ("COUSIN", "Cousin"), - ("DAUGHTERINLAW_SONINLAW", "Daughter-in-law / Son-in-law"), - ("GRANDDAUGHTER_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.", - ), - ("SISTERINLAW_BROTHERINLAW", "Sister-in-law / Brother-in-law"), - ("SON_DAUGHTER", "Son / Daughter"), - ("WIFE_HUSBAND", "Wife / Husband"), - ], - max_length=255, - null=True, - ), - ), - migrations.CreateModel( - name="IndividualRoleInHousehold", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("individual_mis_id", models.UUIDField()), - ("household_mis_id", models.UUIDField()), - ( - "role", - models.CharField( - blank=True, - choices=[ - ("NO_ROLE", "None"), - ("ALTERNATE", "Alternate collector"), - ("PRIMARY", "Primary collector"), - ], - max_length=255, - ), - ), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ("active", models.BooleanField(default=True)), - ], - options={ - "unique_together": {("role", "household_mis_id", "session")}, - }, - ), - migrations.AlterField( - model_name="individual", - name="relationship", - field=models.CharField( - choices=[ - ("UNKNOWN", "Unknown"), - ("AUNT_UNCLE", "Aunt / Uncle"), - ("BROTHER_SISTER", "Brother / Sister"), - ("COUSIN", "Cousin"), - ("DAUGHTERINLAW_SONINLAW", "Daughter-in-law / Son-in-law"), - ("GRANDDAUGHTER_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"), - ], - max_length=255, - null=True, - ), - ), - migrations.AlterModelOptions( - name="targetpopulationentry", - options={"verbose_name_plural": "Target Population Entries"}, - ), - migrations.AddField( - model_name="household", - name="unicef_id", - field=models.CharField(blank=True, max_length=255, null=True), - ), - migrations.AddField( - model_name="individual", - name="unicef_id", - field=models.CharField(blank=True, max_length=255, null=True), - ), - migrations.AlterField( - model_name="individual", - name="sex", - field=models.CharField( - choices=[ - ("MALE", "Male"), - ("FEMALE", "Female"), - ("UNKNOWN", "Unknown"), - ], - max_length=255, - ), - ), - migrations.CreateModel( - name="Document", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("mis_id", models.UUIDField()), - ("number", models.CharField(max_length=255, null=True)), - ("individual_mis_id", models.UUIDField(null=True)), - ( - "type", - models.CharField( - choices=[ - ("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", "Other"), - ], - max_length=50, - ), - ), - ( - "session", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="mis_datahub.session", - ), - ), - ("date_of_expiry", models.DateField(default=None, null=True)), - ("photo", models.ImageField(blank=True, default="", upload_to="")), - ( - "status", - models.CharField( - choices=[ - ("VALID", "Valid"), - ("COLLECTED", "Collected"), - ("LOST", "Lost"), - ("UNKNOWN", "Unknown"), - ("CANCELED", "Canceled"), - ("EXPIRED", "Expired"), - ("HOLD", "Hold"), - ("DAMAGED", "Damaged"), - ], - default=None, - max_length=30, - null=True, - ), - ), - ], - options={ - "unique_together": set(), - }, - ), - migrations.AlterField( - model_name="individual", - name="relationship", - field=models.CharField( - choices=[ - ("UNKNOWN", "Unknown"), - ("AUNT_UNCLE", "Aunt / Uncle"), - ("BROTHER_SISTER", "Brother / Sister"), - ("COUSIN", "Cousin"), - ("DAUGHTERINLAW_SONINLAW", "Daughter-in-law / Son-in-law"), - ("GRANDDAUGHTER_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"), - ], - max_length=255, - null=True, - ), - ), - migrations.AlterField( - model_name="individual", - name="relationship", - field=models.CharField( - choices=[ - ("UNKNOWN", "Unknown"), - ("AUNT_UNCLE", "Aunt / Uncle"), - ("BROTHER_SISTER", "Brother / Sister"), - ("COUSIN", "Cousin"), - ("DAUGHTERINLAW_SONINLAW", "Daughter-in-law / Son-in-law"), - ("GRANDDAUGHTER_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"), - ], - max_length=255, - null=True, - ), - ), - ] diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/0047_migration.py b/src/hct_mis_api/apps/mis_datahub/migrations/0047_migration.py deleted file mode 100644 index ac9ecd65e2..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/migrations/0047_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.20 on 2023-09-04 16:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mis_datahub', '0001_migration_squashed_0046_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='individual', - name='relationship', - field=models.CharField(choices=[('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')], max_length=255, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/0048_migration.py b/src/hct_mis_api/apps/mis_datahub/migrations/0048_migration.py deleted file mode 100644 index d2fddb72f7..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/migrations/0048_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.23 on 2024-01-04 07:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mis_datahub', '0047_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='household', - name='residence_status', - field=models.CharField(choices=[('', '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')], max_length=255, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/0049_migration.py b/src/hct_mis_api/apps/mis_datahub/migrations/0049_migration.py deleted file mode 100644 index ad87b2faf5..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/migrations/0049_migration.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.2.23 on 2024-01-31 13:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mis_datahub', '0048_migration'), - ] - - operations = [ - migrations.AddField( - model_name='program', - name='programme_code', - field=models.CharField(blank=True, max_length=4, null=True), - ), - migrations.AlterUniqueTogether( - name='program', - unique_together={('business_area', 'programme_code'), ('session', 'mis_id')}, - ), - ] diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/0050_migration.py b/src/hct_mis_api/apps/mis_datahub/migrations/0050_migration.py deleted file mode 100644 index 3c1eca0fec..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/migrations/0050_migration.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.24 on 2024-03-11 20:42 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('mis_datahub', '0049_migration'), - ] - - operations = [ - migrations.RemoveField( - model_name='program', - name='individual_data_needed', - ), - ] diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/0051_migration.py b/src/hct_mis_api/apps/mis_datahub/migrations/0051_migration.py deleted file mode 100644 index a941c3a29d..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/migrations/0051_migration.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.2.25 on 2024-06-21 17:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mis_datahub', '0050_migration'), - ] - - operations = [ - migrations.AddField( - model_name='fundscommitment', - name='grant', - field=models.CharField(blank=True, default='', max_length=10, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='percentage', - field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='sponsor_code', - field=models.CharField(blank=True, default='', max_length=10, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='sponsor_name', - field=models.CharField(blank=True, default='', max_length=100, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='wbs', - field=models.CharField(blank=True, default='', max_length=24, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/0052_migration.py b/src/hct_mis_api/apps/mis_datahub/migrations/0052_migration.py deleted file mode 100644 index c3308ce79f..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/migrations/0052_migration.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.2.25 on 2024-08-03 08:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mis_datahub', '0051_migration'), - ] - - operations = [ - migrations.RenameField( - model_name='fundscommitment', - old_name='grant', - new_name='grant_number', - ), - migrations.RenameField( - model_name='fundscommitment', - old_name='sponsor_code', - new_name='sponsor', - ), - migrations.RenameField( - model_name='fundscommitment', - old_name='wbs', - new_name='wbs_element', - ), - migrations.AddField( - model_name='fundscommitment', - name='fund', - field=models.CharField(blank=True, default='', max_length=10, null=True), - ), - migrations.AddField( - model_name='fundscommitment', - name='funds_center', - field=models.CharField(blank=True, default='', max_length=16, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/0053_migration.py b/src/hct_mis_api/apps/mis_datahub/migrations/0053_migration.py deleted file mode 100644 index ed1b34f854..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/migrations/0053_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.25 on 2024-08-04 08:22 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mis_datahub', '0052_migration'), - ] - - operations = [ - migrations.AddField( - model_name='fundscommitment', - name='funds_commitment_item', - field=models.CharField(blank=True, default='', max_length=3, null=True), - ), - ] diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/0054_migration.py b/src/hct_mis_api/apps/mis_datahub/migrations/0054_migration.py deleted file mode 100644 index d16bdaf8c5..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/migrations/0054_migration.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.2.25 on 2024-09-23 10:41 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('mis_datahub', '0053_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='fundscommitment', - name='rec_serial_number', - field=models.CharField(max_length=10, unique=True), - ), - migrations.AlterUniqueTogether( - name='fundscommitment', - unique_together={('funds_commitment_number', 'funds_commitment_item')}, - ), - ] diff --git a/src/hct_mis_api/apps/mis_datahub/migrations/__init__.py b/src/hct_mis_api/apps/mis_datahub/migrations/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/apps/mis_datahub/models.py b/src/hct_mis_api/apps/mis_datahub/models.py deleted file mode 100644 index 9f380f2478..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/models.py +++ /dev/null @@ -1,293 +0,0 @@ -from django.db import models -from django.utils.translation import gettext_lazy as _ - -from hct_mis_api.apps.household.models import ( - IDENTIFICATION_TYPE_CHOICE, - INDIVIDUAL_HOUSEHOLD_STATUS, - MARITAL_STATUS_CHOICE, - RELATIONSHIP_CHOICE, - RESIDENCE_STATUS_CHOICE, - ROLE_CHOICE, -) -from hct_mis_api.apps.utils.models import AbstractSession - - -class Session(AbstractSession): - def __str__(self) -> str: - return f"{self.business_area} / {self.timestamp}" - - -class SessionModel(models.Model): - session = models.ForeignKey("Session", on_delete=models.CASCADE) - - class Meta: - abstract = True - - -class Household(SessionModel): - mis_id = models.UUIDField() - unhcr_id = models.CharField(max_length=255, null=True) - unicef_id = models.CharField(blank=True, max_length=255, null=True) - status = models.CharField(max_length=20, choices=INDIVIDUAL_HOUSEHOLD_STATUS, default="ACTIVE") - household_size = models.PositiveIntegerField() - # registration household id - form_number = models.CharField(max_length=255, null=True) - address = models.CharField(max_length=255, null=True) - admin1 = models.CharField(max_length=255, null=True) - admin2 = models.CharField(max_length=255, null=True) - country = models.CharField(null=True, max_length=3) - residence_status = models.CharField(max_length=255, choices=RESIDENCE_STATUS_CHOICE, null=True) - registration_date = models.DateField(null=True) - village = models.CharField(max_length=250, blank=True, null=True) - - class Meta: - unique_together = ("session", "mis_id") - - -class Individual(SessionModel): - INACTIVE = "INACTIVE" - ACTIVE = "ACTIVE" - STATUS_CHOICE = ((INACTIVE, "Inactive"), (ACTIVE, "Active")) - MALE = "MALE" - FEMALE = "FEMALE" - UNKNOWN = "UNKNOWN" - - SEX_CHOICE = ( - (MALE, _("Male")), - (FEMALE, _("Female")), - (UNKNOWN, _("Unknown")), - ) - - mis_id = models.UUIDField() - unhcr_id = models.CharField(max_length=255, null=True) - unicef_id = models.CharField(blank=True, max_length=255, null=True) - household_mis_id = models.UUIDField(null=True) - status = models.CharField(max_length=50, choices=STATUS_CHOICE, null=True) - full_name = models.CharField(max_length=255) - family_name = models.CharField(max_length=255, null=True) - given_name = models.CharField(max_length=255, null=True) - middle_name = models.CharField(max_length=255, null=True) - sex = models.CharField(max_length=255, choices=SEX_CHOICE) - date_of_birth = models.DateField() - estimated_date_of_birth = models.BooleanField() - relationship = models.CharField( - max_length=255, - null=True, - choices=RELATIONSHIP_CHOICE, - ) - marital_status = models.CharField( - max_length=255, - null=True, - choices=MARITAL_STATUS_CHOICE, - ) - phone_number = models.CharField(max_length=60, null=True) - pregnant = models.BooleanField(null=True) - sanction_list_confirmed_match = models.BooleanField(default=False) - - class Meta: - unique_together = ("session", "mis_id") - - def __str__(self) -> str: - return self.family_name or "" - - -class TargetPopulation(SessionModel): - mis_id = models.UUIDField() - name = models.CharField(max_length=255) - population_type = models.CharField(default="HOUSEHOLD", max_length=20) - targeting_criteria = models.TextField() - - active_households = models.PositiveIntegerField(default=0) - program_mis_id = models.UUIDField() - - class Meta: - unique_together = ("session", "mis_id") - - def __str__(self) -> str: - return self.name - - -class IndividualRoleInHousehold(SessionModel): - individual_mis_id = models.UUIDField() - household_mis_id = models.UUIDField() - role = models.CharField( - max_length=255, - blank=True, - choices=ROLE_CHOICE, - ) - active = models.BooleanField(default=True) - - class Meta: - unique_together = ("role", "household_mis_id", "session") - - -class TargetPopulationEntry(SessionModel): - household_unhcr_id = models.CharField(max_length=255, null=True) - individual_unhcr_id = models.CharField(max_length=255, null=True) - household_mis_id = models.UUIDField(null=True) - individual_mis_id = models.UUIDField(null=True) - target_population_mis_id = models.UUIDField() - vulnerability_score = models.DecimalField( - blank=True, - null=True, - decimal_places=3, - max_digits=6, - help_text="Written by a tool such as Corticon.", - ) - - class Meta: - unique_together = ( - "session", - "household_mis_id", - "target_population_mis_id", - ) - verbose_name_plural = "Target Population Entries" - - -class Program(SessionModel): - STATUS_NOT_STARTED = "NOT_STARTED" - STATUS_STARTED = "STARTED" - STATUS_COMPLETE = "COMPLETE" - SCOPE_FOR_PARTNERS = "FOR_PARTNERS" - SCOPE_UNICEF = "UNICEF" - SCOPE_CHOICE = ( - (SCOPE_FOR_PARTNERS, _("For partners")), - (SCOPE_UNICEF, _("Unicef")), - ) - mis_id = models.UUIDField() - business_area = models.CharField( - max_length=20 - ) # TODO: potentially remove in future since base model has it already - ca_id = models.CharField(max_length=255, null=True) - ca_hash_id = models.CharField(max_length=255, null=True) - name = models.CharField(max_length=255) - scope = models.CharField(choices=SCOPE_CHOICE, max_length=50) - start_date = models.DateTimeField() - end_date = models.DateTimeField() - description = models.CharField(max_length=255, null=True) - programme_code = models.CharField(max_length=4, null=True, blank=True) - - class Meta: - unique_together = (("session", "mis_id"), ("business_area", "programme_code")) - - -class Document(SessionModel): - VALID = "VALID" - COLLECTED = "COLLECTED" - LOST = "LOST" - UNKNOWN = "UNKNOWN" - CANCELED = "CANCELED" - EXPIRED = "EXPIRED" - HOLD = "HOLD" - DAMAGED = "DAMAGED" - STATUS_CHOICE = ( - (VALID, _("Valid")), - (COLLECTED, _("Collected")), - (LOST, _("Lost")), - (UNKNOWN, _("Unknown")), - (CANCELED, _("Canceled")), - (EXPIRED, _("Expired")), - (HOLD, _("Hold")), - (DAMAGED, _("Damaged")), - ) - - status = models.CharField(choices=STATUS_CHOICE, null=True, max_length=30, default=None) - date_of_expiry = models.DateField(null=True, default=None) - photo = models.ImageField(blank=True, default="") - mis_id = models.UUIDField() - number = models.CharField(max_length=255, null=True) - individual_mis_id = models.UUIDField(null=True) - type = models.CharField(max_length=50, choices=IDENTIFICATION_TYPE_CHOICE) - - -class FundsCommitment(models.Model): - rec_serial_number = models.CharField(max_length=10, unique=True) - business_area = models.CharField(max_length=4, blank=True, null=True) - funds_commitment_number = models.CharField(max_length=10, blank=True, null=True) - document_type = models.CharField(max_length=2, blank=True, null=True) - document_text = models.CharField(max_length=50, blank=True, null=True) - currency_code = models.CharField(max_length=5, blank=True, null=True) - gl_account = models.CharField(null=True, blank=True, max_length=10) - commitment_amount_local = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - commitment_amount_usd = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - total_open_amount_local = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - total_open_amount_usd = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - vendor_id = models.CharField(max_length=10, blank=True, null=True) - posting_date = models.DateField(blank=True, null=True) - vision_approval = models.CharField(max_length=1, blank=True, null=True) - document_reference = models.CharField(max_length=16, null=True) - fc_status = models.CharField(max_length=1, blank=True, null=True) - create_date = models.DateTimeField(null=True, blank=True, auto_now_add=True) - created_by = models.CharField(max_length=20, null=True, blank=True, default="") - update_date = models.DateTimeField(null=True, blank=True) - updated_by = models.CharField(max_length=20, blank=True, null=True, default="") - mis_sync_flag = models.BooleanField(null=True, blank=True, default=False) - mis_sync_date = models.DateTimeField(blank=True, null=True) - ca_sync_flag = models.BooleanField(blank=True, null=True, default=False) - ca_sync_date = models.DateTimeField(blank=True, null=True) - - grant_number = models.CharField(max_length=10, null=True, blank=True, default="") - sponsor = models.CharField(max_length=10, null=True, blank=True, default="") - sponsor_name = models.CharField(max_length=100, null=True, blank=True, default="") - wbs_element = models.CharField(max_length=24, null=True, blank=True, default="") - fund = models.CharField(max_length=10, null=True, blank=True, default="") - funds_center = models.CharField(max_length=16, null=True, blank=True, default="") - funds_commitment_item = models.CharField(max_length=3, null=True, blank=True, default="") - percentage = models.DecimalField(decimal_places=2, max_digits=5, null=True, blank=True) - - def __str__(self) -> str: - return self.funds_commitment_number if self.funds_commitment_number else "N/A" - - class Meta: - unique_together = ("funds_commitment_number", "funds_commitment_item") - - -class DownPayment(models.Model): - rec_serial_number = models.CharField(max_length=10, blank=True, null=True) - business_area = models.CharField(max_length=4) - down_payment_reference = models.CharField(max_length=20) - document_type = models.CharField(max_length=10) - consumed_fc_number = models.CharField(max_length=10) - total_down_payment_amount_local = models.DecimalField( - decimal_places=2, - max_digits=15, - ) - total_down_payment_amount_usd = models.DecimalField( - decimal_places=2, - max_digits=15, - blank=True, - null=True, - ) - currency_code = models.CharField(max_length=5, blank=True, null=True) - posting_date = models.DateField(blank=True, null=True) - doc_year = models.IntegerField(blank=True, null=True) - doc_number = models.CharField(max_length=10, blank=True, null=True) - doc_item_number = models.CharField(max_length=3, null=True) - create_date = models.DateTimeField(null=True, blank=True, auto_now_add=True) - created_by = models.CharField(max_length=20, blank=True, null=True, default="") - update_date = models.DateTimeField(blank=True, null=True) - updated_by = models.CharField(max_length=20, blank=True, null=True, default="") - mis_sync_flag = models.BooleanField(default=False, blank=True, null=True) - mis_sync_date = models.DateTimeField(blank=True, null=True) - ca_sync_flag = models.BooleanField(default=False, blank=True, null=True) - ca_sync_date = models.DateTimeField(blank=True, null=True) diff --git a/src/hct_mis_api/apps/mis_datahub/tasks/__init__.py b/src/hct_mis_api/apps/mis_datahub/tasks/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/apps/mis_datahub/tasks/send_tp_to_datahub.py b/src/hct_mis_api/apps/mis_datahub/tasks/send_tp_to_datahub.py deleted file mode 100644 index 151f4a6885..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/tasks/send_tp_to_datahub.py +++ /dev/null @@ -1,321 +0,0 @@ -import logging -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple - -from django.db import transaction -from django.db.models import F, Q, QuerySet -from django.utils import timezone - -from hct_mis_api.apps.core.models import CountryCodeMap -from hct_mis_api.apps.core.utils import build_arg_dict, timezone_datetime -from hct_mis_api.apps.household.models import ( - STATUS_ACTIVE, - Document, - Household, - Individual, - IndividualIdentity, - IndividualRoleInHousehold, -) -from hct_mis_api.apps.mis_datahub import models as dh_mis_models -from hct_mis_api.apps.targeting.models import HouseholdSelection, TargetPopulation - -if TYPE_CHECKING: - from hct_mis_api.apps.program.models import Program - - -logger = logging.getLogger(__name__) - - -class SendTPToDatahubTask: - MAPPING_TP_DICT = { - "mis_id": "id", - "name": "name", - "active_households": "total_households_count", - "program_mis_id": "program_id", - "targeting_criteria": "targeting_criteria_string", - } - MAPPING_PROGRAM_DICT = { - "mis_id": "id", - "name": "name", - "business_area": "business_area.cash_assist_code", - "scope": "scope", - "start_date": "start_date", - "end_date": "end_date", - "description": "description", - "ca_id": "ca_id", - "ca_hash_id": "ca_hash_id", - "programme_code": "programme_code", - } - - MAPPING_HOUSEHOLD_DICT = { - "mis_id": "id", - "unicef_id": "unicef_id", - "status": "status", - "household_size": "size", - "address": "address", - "admin1": "admin1.name", - "admin2": "admin2.name", - "residence_status": "residence_status", - "registration_date": "last_registration_date", - "village": "village", - } - MAPPING_INDIVIDUAL_DICT = { - "mis_id": "id", - "unicef_id": "unicef_id", - "status": "cash_assist_status", - "full_name": "full_name", - "family_name": "family_name", - "given_name": "given_name", - "middle_name": "middle_name", - "sex": "sex", - "date_of_birth": "birth_date", - "estimated_date_of_birth": "estimated_birth_date", - "relationship": "relationship", - "marital_status": "marital_status", - "phone_number": "phone_no", - "household_mis_id": "household_id", - "pregnant": "pregnant", - "sanction_list_confirmed_match": "sanction_list_confirmed_match", - } - MAPPING_DOCUMENT_DICT = { - "mis_id": "id", - "number": "document_number", - "individual_mis_id": "individual_id", - "type": "type.key", - } - - def execute(self, target_population: "TargetPopulation") -> Dict: - target_population.status = TargetPopulation.STATUS_SENDING_TO_CASH_ASSIST - target_population.save(update_fields=["status"]) - return self.send_target_population(target_population) - - @transaction.atomic() - @transaction.atomic(using="cash_assist_datahub_mis") - def send_target_population(self, target_population: "TargetPopulation") -> Dict: - households_to_bulk_create = [] - individuals_to_bulk_create = [] - documents_to_bulk_create = [] - tp_entries_to_bulk_create = [] - roles_to_bulk_create = [] - - try: - program = target_population.program - self.dh_session = dh_mis_models.Session( - source=dh_mis_models.Session.SOURCE_MIS, - status=dh_mis_models.Session.STATUS_READY, - business_area=program.business_area.cash_assist_code, - ) - self.dh_session.save() - - ( - documents_to_sync, - households_to_sync, - individuals_to_sync, - roles_to_sync, - target_population_selections, - ) = self._prepare_data_to_send(target_population) - self._send_program(program) - self._send_target_population_object(target_population) - chunk_size = 1000 - - # AB#156327 - is_clear_withdrawn = ( - target_population.business_area.custom_fields.get("clear_withdrawn") is True - and "clear_withdrawn" in target_population.name.split() - ) - - for household in households_to_sync: - dh_household = self._prepare_datahub_object_household(household, is_clear_withdrawn) - households_to_bulk_create.append(dh_household) - if len(households_to_bulk_create) % chunk_size: - dh_mis_models.Household.objects.bulk_create(households_to_bulk_create) - households_to_bulk_create = [] - - for individual in individuals_to_sync: - dh_individual = self._prepare_datahub_object_individual(individual, is_clear_withdrawn) - individuals_to_bulk_create.append(dh_individual) - if len(individuals_to_bulk_create) % chunk_size: - dh_mis_models.Individual.objects.bulk_create(individuals_to_bulk_create) - individuals_to_bulk_create = [] - - for role in roles_to_sync: - dh_role = self._prepare_datahub_object_role(role) - roles_to_bulk_create.append(dh_role) - if len(roles_to_bulk_create) % chunk_size: - dh_mis_models.IndividualRoleInHousehold.objects.bulk_create(roles_to_bulk_create) - roles_to_bulk_create = [] - - for document in documents_to_sync: - dh_document = self._prepare_datahub_object_document(document) - dh_document.type = dh_document.type.upper() - documents_to_bulk_create.append(dh_document) - if len(documents_to_bulk_create) % chunk_size: - dh_mis_models.Document.objects.bulk_create(documents_to_bulk_create) - documents_to_bulk_create = [] - - for selection in target_population_selections: - dh_target_population_selection = self._prepare_datahub_object_target_entry(selection) - tp_entries_to_bulk_create.append(dh_target_population_selection) - if len(tp_entries_to_bulk_create) % chunk_size: - dh_mis_models.TargetPopulationEntry.objects.bulk_create(tp_entries_to_bulk_create) - tp_entries_to_bulk_create = [] - - dh_mis_models.Household.objects.bulk_create(households_to_bulk_create) - dh_mis_models.Individual.objects.bulk_create(individuals_to_bulk_create) - dh_mis_models.IndividualRoleInHousehold.objects.bulk_create(roles_to_bulk_create) - dh_mis_models.Document.objects.bulk_create(documents_to_bulk_create) - dh_mis_models.TargetPopulationEntry.objects.bulk_create(tp_entries_to_bulk_create) - target_population.set_to_ready_for_cash_assist() - target_population.save() - - households_to_sync.update(last_sync_at=timezone.now()) - individuals_to_sync.update(last_sync_at=timezone.now()) - return { - "session": self.dh_session.id, - "program": str(program), - "target_population": str(target_population), - "households_count": len(households_to_bulk_create), - "individuals_count": len(individuals_to_bulk_create), - "roles_count": len(roles_to_bulk_create), - "tp_entries_count": len(tp_entries_to_bulk_create), - } - except Exception as e: - logger.exception(e) - raise - - def _prepare_data_to_send(self, target_population: "TargetPopulation") -> Tuple: - ( - all_targeted_households_ids, - households_to_sync, - individuals_to_sync, - ) = self._get_individuals_and_households(target_population) - self._prepare_unhcr_dict(individuals_to_sync) - documents = self._get_documents(individuals_to_sync) - # household_id__in - to filter also by vulnerability_score score - target_population_selections = ( - HouseholdSelection.objects.filter( - target_population__id=target_population.id, - household_id__in=all_targeted_households_ids, - ) - .select_related("household") - .distinct() - ) - roles_to_sync = IndividualRoleInHousehold.objects.filter( - Q(household__last_sync_at__isnull=True) | Q(household__last_sync_at__lte=F("household__updated_at")), - household_id__in=all_targeted_households_ids, - ).distinct() - return documents, households_to_sync, individuals_to_sync, roles_to_sync, target_population_selections - - def _prepare_unhcr_dict(self, individuals_to_sync: List[Individual]) -> None: - individual_identities = IndividualIdentity.objects.filter( - partner__name="UNHCR", individual__in=individuals_to_sync - ).distinct() - self.unhcr_id_dict = {identity.individual_id: identity.number for identity in individual_identities} - - def _get_individuals_and_households(self, target_population: "TargetPopulation") -> Any: - all_targeted_households_ids = target_population.household_list.values_list("id", flat=True) - # only head of households + collectors (primary_collector,alternate_collector) - # | Q(household__id__in=all_targeted_households_ids) for filter only if individual data needed - all_individuals = Individual.objects.filter( - Q(heading_household__in=all_targeted_households_ids) - | Q(represented_households__in=all_targeted_households_ids) - ).distinct() - all_households = Household.objects.filter( - id__in=all_individuals.values_list("household_id", flat=True) - ).distinct() - households_to_sync = all_households.filter( - Q(last_sync_at__isnull=True) | Q(last_sync_at__lte=F("updated_at")) - ).distinct() - individuals_to_sync = all_individuals.filter( - Q(last_sync_at__isnull=True) | Q(last_sync_at__lte=F("updated_at")) - ).distinct() - return all_targeted_households_ids, households_to_sync, individuals_to_sync - - def _get_documents(self, individuals: List[Individual]) -> QuerySet[Document]: - return Document.objects.filter(individual__in=individuals).distinct() - - def _send_program(self, program: "Program") -> Optional[dh_mis_models.Program]: - if not (program.last_sync_at is None or program.last_sync_at < program.updated_at): - return None - dh_program_args = build_arg_dict(program, SendTPToDatahubTask.MAPPING_PROGRAM_DICT) - - dh_program = dh_mis_models.Program(**dh_program_args) - dh_program.session = self.dh_session - - dh_program.start_date = timezone_datetime(dh_program.start_date) - dh_program.end_date = timezone_datetime(dh_program.end_date) - dh_program.save() - - program.last_sync_at = timezone.now() - program.save(update_fields=["last_sync_at"]) - return dh_program - - def _send_target_population_object(self, target_population: "TargetPopulation") -> dh_mis_models.TargetPopulation: - dh_tp_args = build_arg_dict(target_population, SendTPToDatahubTask.MAPPING_TP_DICT) - dh_target = dh_mis_models.TargetPopulation(**dh_tp_args) - dh_target.session = self.dh_session - dh_target.save() - return dh_target - - def _prepare_datahub_object_household( - self, household: Household, is_clear_withdrawn: bool = False - ) -> dh_mis_models.Household: - dh_household_args = build_arg_dict(household, SendTPToDatahubTask.MAPPING_HOUSEHOLD_DICT) - if household.country: - dh_household_args["country"] = CountryCodeMap.objects.get_code(household.country.iso_code2) - - if is_clear_withdrawn and household.withdrawn: - dh_household_args["status"] = STATUS_ACTIVE - - dh_household = dh_mis_models.Household(**dh_household_args) - dh_household.unhcr_id = self._get_unhcr_household_id(household) - dh_household.session = self.dh_session - return dh_household - - def _prepare_datahub_object_individual( - self, individual: Individual, is_clear_withdrawn: bool = False - ) -> dh_mis_models.Individual: - dh_individual_args = build_arg_dict(individual, SendTPToDatahubTask.MAPPING_INDIVIDUAL_DICT) - - if is_clear_withdrawn and individual.withdrawn and not individual.duplicate: - dh_individual_args["status"] = STATUS_ACTIVE - - dh_individual = dh_mis_models.Individual(**dh_individual_args) - dh_individual.unhcr_id = self._get_unhcr_individual_id(individual) - dh_individual.session = self.dh_session - return dh_individual - - def _prepare_datahub_object_role(self, role: IndividualRoleInHousehold) -> dh_mis_models.IndividualRoleInHousehold: - return dh_mis_models.IndividualRoleInHousehold( - role=role.role, - household_mis_id=role.household.id, - individual_mis_id=role.individual.id, - session=self.dh_session, - ) - - def _prepare_datahub_object_document(self, document: Document) -> dh_mis_models.Document: - dh_document_args = build_arg_dict(document, SendTPToDatahubTask.MAPPING_DOCUMENT_DICT) - dh_document = dh_mis_models.Document( - **dh_document_args, - session=self.dh_session, - ) - return dh_document - - def _prepare_datahub_object_target_entry( - self, target_population_selection: HouseholdSelection - ) -> dh_mis_models.TargetPopulationEntry: - household_unhcr_id = self._get_unhcr_household_id(target_population_selection.household) - return dh_mis_models.TargetPopulationEntry( - target_population_mis_id=target_population_selection.target_population.id, - household_mis_id=target_population_selection.household.id, - household_unhcr_id=household_unhcr_id, - vulnerability_score=target_population_selection.vulnerability_score, - session=self.dh_session, - ) - - def _get_unhcr_individual_id(self, individual: Individual) -> Optional[str]: - return self.unhcr_id_dict.get(individual.id) - - def _get_unhcr_household_id(self, household: Household) -> Optional[str]: - if household.unhcr_id == "": - return None - return household.unhcr_id diff --git a/src/hct_mis_api/apps/mis_datahub/templates/admin/mis_datahub/session/inspect.html b/src/hct_mis_api/apps/mis_datahub/templates/admin/mis_datahub/session/inspect.html deleted file mode 100644 index b8dbc8e935..0000000000 --- a/src/hct_mis_api/apps/mis_datahub/templates/admin/mis_datahub/session/inspect.html +++ /dev/null @@ -1,59 +0,0 @@ -{% extends "admin_extra_buttons/action_page.html" %} -{% load i18n admin_urls static admin_modify %} -{% block extrahead %} - -{% endblock %} -{% block breadcrumbs %} - -{% endblock %} - -{% block content %} - {% if warnings %} -

    Warnings

    - - {% for level, warn in warnings %} - - - - - {% endfor %} -
    {{ level|upper }}{{ warn }}
    - {% endif %} - - - - - - - - {% for model, info in data.items %} - - - - - - {% endfor %} -
    Component# Records-
    {{ info.meta.verbose_name_plural|title }}{{ info.count }}visit -
    -{% endblock content %} - -{% block submit_buttons_bottom %}{% endblock %} diff --git a/src/hct_mis_api/apps/targeting/mutations.py b/src/hct_mis_api/apps/targeting/mutations.py index a0cb004b9a..1a9a2b29a4 100644 --- a/src/hct_mis_api/apps/targeting/mutations.py +++ b/src/hct_mis_api/apps/targeting/mutations.py @@ -24,7 +24,6 @@ ) from hct_mis_api.apps.core.validators import raise_program_status_is from hct_mis_api.apps.household.models import Household, Individual -from hct_mis_api.apps.mis_datahub.celery_tasks import send_target_population_task from hct_mis_api.apps.program.models import Program, ProgramCycle from hct_mis_api.apps.steficon.models import Rule from hct_mis_api.apps.steficon.schema import SteficonRuleNode @@ -440,9 +439,6 @@ def validated_mutate(cls, root: Any, info: Any, **kwargs: Any) -> "FinalizeTarge target_population.finalized_by = user target_population.finalized_at = timezone.now() target_population.save() - transaction.on_commit( - lambda: send_target_population_task.delay(target_population_id=target_population.id) - ) log_create( TargetPopulation.ACTIVITY_LOG_MAPPING, "business_area", diff --git a/src/hct_mis_api/config/fragments/constance.py b/src/hct_mis_api/config/fragments/constance.py index f2d438b7d6..81e790297f 100644 --- a/src/hct_mis_api/config/fragments/constance.py +++ b/src/hct_mis_api/config/fragments/constance.py @@ -148,7 +148,6 @@ ), "QUICK_LINKS": ( """Kobo,https://kf-hope.unitst.org/ -CashAssist,https://cashassist-trn.crm4.dynamics.com/ Sentry,https://excubo.unicef.io/sentry/hct-mis-stg/ elasticsearch,hope-elasticsearch-coordinating-only:9200 Datamart,https://datamart.unicef.io diff --git a/src/hct_mis_api/config/fragments/smart_admin.py b/src/hct_mis_api/config/fragments/smart_admin.py index c2c8151eb3..a19fed948a 100644 --- a/src/hct_mis_api/config/fragments/smart_admin.py +++ b/src/hct_mis_api/config/fragments/smart_admin.py @@ -37,18 +37,9 @@ "core.FlexibleAttribute", "core.FlexibleAttributeGroup", ], - "HUB (Hope->CA)": [ - "mis_datahub", - ], - "HUB (CA->Hope)": [ - "cash_assist_datahub", - ], "HUB (Kobo->Hope)": [ "registration_datahub", ], - "HUB (Vision->Hope)": [ - "erp_datahub", - ], "System": [ "social_django", "constance", diff --git a/src/hct_mis_api/config/settings.py b/src/hct_mis_api/config/settings.py index 435bc933d0..2c2e16ce03 100644 --- a/src/hct_mis_api/config/settings.py +++ b/src/hct_mis_api/config/settings.py @@ -121,10 +121,6 @@ DATABASES = { "default": env.db(), "read_only": RO_CONN, - "cash_assist_datahub_mis": env.db("DATABASE_URL_HUB_MIS"), - "cash_assist_datahub_ca": env.db("DATABASE_URL_HUB_CA"), - "cash_assist_datahub_erp": env.db("DATABASE_URL_HUB_ERP"), - "registration_datahub": env.db("DATABASE_URL_HUB_REGISTRATION"), } DATABASES["default"].update({"CONN_MAX_AGE": 60}) @@ -133,33 +129,9 @@ "sslmode": "verify-full", "sslrootcert": "/certs/psql-cert.crt", } - DATABASES["cash_assist_datahub_mis"]["OPTIONS"] = { - "sslmode": "verify-full", - "sslrootcert": "/certs/psql-cert.crt", - "options": "-c search_path=mis", - } - DATABASES["cash_assist_datahub_ca"]["OPTIONS"] = { - "sslmode": "verify-full", - "sslrootcert": "/certs/psql-cert.crt", - "options": "-c search_path=ca", - } - DATABASES["cash_assist_datahub_erp"]["OPTIONS"] = { - "sslmode": "verify-full", - "sslrootcert": "/certs/psql-cert.crt", - "options": "-c search_path=erp", - } - DATABASES["registration_datahub"]["OPTIONS"] = { - "sslmode": "verify-full", - "sslrootcert": "/certs/psql-cert.crt", - } # If app is not specified here it will use default db -DATABASE_APPS_MAPPING: Dict[str, str] = { - "cash_assist_datahub": "cash_assist_datahub_ca", - "mis_datahub": "cash_assist_datahub_mis", - "erp_datahub": "cash_assist_datahub_erp", - "registration_datahub": "registration_datahub", -} +DATABASE_APPS_MAPPING: Dict[str, str] = {} DATABASE_ROUTERS = ("hct_mis_api.apps.core.dbrouters.DbRouter",) @@ -221,14 +193,10 @@ "hct_mis_api.apps.program.apps.ProgramConfig", "hct_mis_api.apps.changelog.apps.ChangelogConfig", "power_query.apps.Config", - # "hct_mis_api.apps.targeting", "hct_mis_api.apps.targeting.apps.TargetingConfig", "hct_mis_api.apps.utils.apps.UtilsConfig", "hct_mis_api.apps.registration_datahub.apps.Config", "hct_mis_api.apps.registration_data.apps.RegistrationDataConfig", - "hct_mis_api.apps.cash_assist_datahub.apps.Config", - "hct_mis_api.apps.mis_datahub.apps.Config", - "hct_mis_api.apps.erp_datahub.apps.Config", "hct_mis_api.apps.sanction_list.apps.SanctionListConfig", "hct_mis_api.apps.steficon.apps.SteficonConfig", "hct_mis_api.apps.reporting.apps.ReportingConfig", @@ -449,9 +417,6 @@ def masker(key: str, value: Any, config: Dict, request: HttpRequest) -> Any: EXPLORER_CONNECTIONS = { "default": "default", - "HUB MIS": "cash_assist_datahub_mis", - "HUB CA": "cash_assist_datahub_ca", - "HUB ERP": "cash_assist_datahub_erp", } EXPLORER_DEFAULT_CONNECTION = "default" EXPLORER_PERMISSION_VIEW = lambda r: r.user.has_perm("explorer.view_query") @@ -503,11 +468,6 @@ def masker(key: str, value: Any, config: Dict, request: HttpRequest) -> Any: } } -SHELL_PLUS_DONT_LOAD = [ - "mis_datahub.Individual", - "mis_datahub.Household", -] - CYPRESS_TESTING = env("CYPRESS_TESTING", default="no") == "yes" if CYPRESS_TESTING and ENV != "dev": diff --git a/tests/unit/apps/administration/test_admin.py b/tests/unit/apps/administration/test_admin.py index 8a25623f16..2e27669841 100644 --- a/tests/unit/apps/administration/test_admin.py +++ b/tests/unit/apps/administration/test_admin.py @@ -49,13 +49,6 @@ def model_admins() -> Tuple: class TestAdminSite(WebTest): - databases = [ - "default", - "cash_assist_datahub_ca", - "cash_assist_datahub_erp", - "cash_assist_datahub_mis", - ] - @classmethod def setUpTestData(cls) -> None: super().setUpTestData() diff --git a/tests/unit/apps/cash_assist_datahub/__init__.py b/tests/unit/apps/cash_assist_datahub/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py b/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py deleted file mode 100644 index 6a51e4c2e0..0000000000 --- a/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py +++ /dev/null @@ -1,392 +0,0 @@ -import os -import uuid -from datetime import timedelta -from typing import Any -from unittest import mock -from unittest.mock import MagicMock, patch - -from django.conf import settings -from django.core.management import call_command -from django.test import TestCase -from django.utils import timezone - -import requests_mock -from parameterized import parameterized - -from hct_mis_api.apps.cash_assist_datahub.models import CashPlan as DHCashPlan -from hct_mis_api.apps.cash_assist_datahub.models import PaymentRecord as DHPaymentRecord -from hct_mis_api.apps.cash_assist_datahub.models import Programme as DHProgram -from hct_mis_api.apps.cash_assist_datahub.models import ( - ServiceProvider as DHServiceProvider, -) -from hct_mis_api.apps.cash_assist_datahub.models import Session -from hct_mis_api.apps.cash_assist_datahub.models import ( - TargetPopulation as DHTargetPopulation, -) -from hct_mis_api.apps.cash_assist_datahub.tasks.pull_from_datahub import ( - PullFromDatahubTask, -) -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 create_household -from hct_mis_api.apps.payment.delivery_mechanisms import DeliveryMechanismChoices -from hct_mis_api.apps.payment.fixtures import generate_delivery_mechanisms -from hct_mis_api.apps.payment.models import ( - CashPlan, - DeliveryMechanism, - PaymentRecord, - ServiceProvider, -) -from hct_mis_api.apps.program.fixtures import ( - ProgramFactory, - get_program_with_dct_type_and_name, -) -from hct_mis_api.apps.program.models import Program -from hct_mis_api.apps.targeting.models import TargetPopulation -from tests.unit.apps.core.test_exchange_rates import EXCHANGE_RATES_WITH_HISTORICAL_DATA - - -class DummyExchangeRates: - pass - - -@mock.patch.dict(os.environ, {"EXCHANGE_RATES_API_KEY": "TEST_API_KEY"}) -class TestPullDataFromDatahub(TestCase): - databases = "__all__" - program = None - target_population = None - dh_cash_plan1 = None - dh_cash_plan2 = None - household = None - - @classmethod - def _pre_test_commands(cls) -> None: - cls.business_area = create_afghanistan() - cls.program = get_program_with_dct_type_and_name() - call_command("loadcountries") - call_command("loadcountrycodes") - - @classmethod - def _setup_in_app_data(cls) -> None: - target_population = TargetPopulation.objects.create( - name="Test TP", - status=TargetPopulation.STATUS_PROCESSING, - program=cls.program, - business_area=cls.business_area, - program_cycle=cls.program.cycles.first(), - ) - - program = ProgramFactory( - name="Test Program", - status=Program.ACTIVE, - start_date=timezone.now(), - end_date=timezone.now() + timedelta(days=10), - description="Test Program description", - business_area=BusinessArea.objects.first(), - budget=1000, - frequency_of_payments=Program.REGULAR, - sector=Program.CHILD_PROTECTION, - scope=Program.SCOPE_UNICEF, - cash_plus=True, - population_goal=1000, - administrative_areas_of_implementation="Test something", - ) - (household, individuals) = create_household(household_args={"size": 1}) - cls.household = household - cls.target_population = target_population - cls.program = program - generate_delivery_mechanisms() - - @classmethod - def _setup_datahub_data(cls) -> None: - session = Session() - session.business_area = BusinessArea.objects.first().cash_assist_code - session.status = Session.STATUS_READY - session.save() - cls.session = session - dh_target_population = DHTargetPopulation() - dh_target_population.session = session - dh_target_population.ca_id = "123-TP-12345" - dh_target_population.ca_hash_id = uuid.uuid4() - dh_target_population.mis_id = cls.target_population.id - dh_target_population.save() - cls.dh_target_population = dh_target_population - - dh_program = DHProgram() - dh_program.session = session - dh_program.mis_id = cls.program.id - dh_program.ca_id = "123-PRG-12345" - dh_program.ca_hash_id = uuid.uuid4() - dh_program.save() - cls.dh_program = dh_program - - dh_service_provider = DHServiceProvider() - dh_service_provider.session = session - dh_service_provider.business_area = BusinessArea.objects.first().cash_assist_code - dh_service_provider.ca_id = "123-SP-12345" - dh_service_provider.full_name = "SOME TEST BANK" - dh_service_provider.short_name = "STB" - dh_service_provider.country = "POL" - dh_service_provider.vision_id = "random-sp-vision-id" - dh_service_provider.save() - cls.dh_service_provider = dh_service_provider - - dh_cash_plan1 = DHCashPlan() - dh_cash_plan1.session = session - dh_cash_plan1.business_area = BusinessArea.objects.first().cash_assist_code - dh_cash_plan1.cash_plan_id = "123-CSH-12345" - dh_cash_plan1.cash_plan_hash_id = uuid.uuid4() - dh_cash_plan1.status = CashPlan.DISTRIBUTION_COMPLETED - dh_cash_plan1.status_date = timezone.now() - dh_cash_plan1.name = "Test CashAssist CashPlan" - dh_cash_plan1.distribution_level = "Test Distribution Level" - dh_cash_plan1.start_date = timezone.now() - dh_cash_plan1.end_date = timezone.now() + timedelta(days=10) - dh_cash_plan1.dispersion_date = timezone.now() + timedelta(days=2) - dh_cash_plan1.coverage_duration = 4 - dh_cash_plan1.coverage_unit = "days" - dh_cash_plan1.comments = "Test Comment" - dh_cash_plan1.program_mis_id = cls.program.id - dh_cash_plan1.delivery_type = "CARD" - dh_cash_plan1.assistance_measurement = "TEST measurement" - dh_cash_plan1.assistance_through = dh_service_provider.ca_id - dh_cash_plan1.vision_id = "random-csh-vision-id" - dh_cash_plan1.funds_commitment = "123" - dh_cash_plan1.validation_alerts_count = 0 - dh_cash_plan1.total_persons_covered = 1 - dh_cash_plan1.total_persons_covered_revised = 1 - dh_cash_plan1.payment_records_count = 1 - dh_cash_plan1.total_entitled_quantity = 10 - dh_cash_plan1.total_entitled_quantity_revised = 10 - dh_cash_plan1.total_delivered_quantity = 10 - dh_cash_plan1.total_undelivered_quantity = 0 - dh_cash_plan1.save() - cls.dh_cash_plan1 = dh_cash_plan1 - - dh_payment_record = DHPaymentRecord() - dh_payment_record.session = session - dh_payment_record.business_area = BusinessArea.objects.first().cash_assist_code - dh_payment_record.status = PaymentRecord.STATUS_SUCCESS - dh_payment_record.status_date = timezone.now() - dh_payment_record.ca_id = "123-PR-12345" - dh_payment_record.ca_hash_id = uuid.uuid4() - dh_payment_record.cash_plan_ca_id = dh_cash_plan1.cash_plan_id - dh_payment_record.registration_ca_id = "123-RDI-12345" - dh_payment_record.household_mis_id = cls.household.id - dh_payment_record.head_of_household_mis_id = cls.household.head_of_household.id - dh_payment_record.full_name = cls.household.head_of_household.full_name - dh_payment_record.total_persons_covered = 1 - dh_payment_record.distribution_modality = "Test distribution_modality" - dh_payment_record.target_population_mis_id = cls.target_population.id - dh_payment_record.target_population_cash_assist_id = "123-TP-12345" - dh_payment_record.entitlement_card_number = "ASH12345678" - dh_payment_record.entitlement_card_status = PaymentRecord.ENTITLEMENT_CARD_STATUS_ACTIVE - dh_payment_record.entitlement_card_issue_date = timezone.now() - timedelta(days=10) - dh_payment_record.delivery_type = DeliveryMechanismChoices.DELIVERY_TYPE_CASH - dh_payment_record.currency = "USD" - dh_payment_record.entitlement_quantity = 10 - dh_payment_record.delivered_quantity = 10 - dh_payment_record.delivery_date = timezone.now() - timedelta(days=1) - dh_payment_record.service_provider_ca_id = dh_service_provider.ca_id - dh_payment_record.transaction_reference_id = "12345" - dh_payment_record.vision_id = "random-pr-vision-id" - dh_payment_record.save() - cls.dh_payment_record = dh_payment_record - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls._pre_test_commands() - cls._setup_in_app_data() - cls._setup_datahub_data() - - @requests_mock.Mocker() - def test_pull_data(self, mocker: Any) -> None: - mocker.register_uri( - "GET", - "https://uniapis.unicef.org/biapi/v1/exchangerates?history=yes", - json=EXCHANGE_RATES_WITH_HISTORICAL_DATA, - ) - task = PullFromDatahubTask() - task.execute() - session = self.session - session.refresh_from_db() - self.assertEqual(session.status, Session.STATUS_COMPLETED) - # tp - self.target_population.refresh_from_db() - self.assertEqual(self.target_population.ca_id, self.dh_target_population.ca_id) - self.assertEqual(str(self.target_population.ca_hash_id), str(self.dh_target_population.ca_hash_id)) - # program - self.program.refresh_from_db() - self.assertEqual(self.program.ca_id, self.dh_program.ca_id) - self.assertEqual(str(self.program.ca_hash_id), str(self.dh_program.ca_hash_id)) - # service provider - service_provider = ServiceProvider.objects.get(ca_id=self.dh_payment_record.service_provider_ca_id) - self.assertEqual(service_provider.business_area.cash_assist_code, self.dh_service_provider.business_area) - self.assertEqual(service_provider.ca_id, self.dh_service_provider.ca_id) - self.assertEqual(service_provider.full_name, self.dh_service_provider.full_name) - self.assertEqual(service_provider.short_name, self.dh_service_provider.short_name) - self.assertEqual(service_provider.country, self.dh_service_provider.country) - self.assertEqual(service_provider.vision_id, self.dh_service_provider.vision_id) - # cash plan - cash_plan = CashPlan.objects.get(ca_id=self.dh_cash_plan1.cash_plan_id) - self.assertEqual(cash_plan.business_area.cash_assist_code, self.dh_cash_plan1.business_area) - self.assertEqual(cash_plan.ca_id, self.dh_cash_plan1.cash_plan_id) - self.assertEqual(str(cash_plan.ca_hash_id), str(self.dh_cash_plan1.cash_plan_hash_id)) - self.assertEqual(cash_plan.status, self.dh_cash_plan1.status) - self.assertEqual(cash_plan.status_date, self.dh_cash_plan1.status_date) - self.assertEqual(cash_plan.name, self.dh_cash_plan1.name) - self.assertEqual(cash_plan.distribution_level, self.dh_cash_plan1.distribution_level) - self.assertEqual(cash_plan.start_date.date(), self.dh_cash_plan1.start_date.date()) - self.assertEqual(cash_plan.end_date.date(), self.dh_cash_plan1.end_date.date()) - self.assertEqual(cash_plan.dispersion_date, self.dh_cash_plan1.dispersion_date) - self.assertEqual(cash_plan.coverage_duration, self.dh_cash_plan1.coverage_duration) - self.assertEqual(cash_plan.coverage_unit, self.dh_cash_plan1.coverage_unit) - self.assertEqual(cash_plan.comments, self.dh_cash_plan1.comments) - self.assertEqual(str(cash_plan.program_id), str(self.dh_cash_plan1.program_mis_id)) - self.assertEqual(cash_plan.delivery_type, self.dh_cash_plan1.delivery_type) - self.assertEqual(cash_plan.assistance_measurement, self.dh_cash_plan1.assistance_measurement) - self.assertEqual(cash_plan.assistance_through, self.dh_cash_plan1.assistance_through) - self.assertEqual(cash_plan.vision_id, self.dh_cash_plan1.vision_id) - self.assertEqual(cash_plan.funds_commitment, self.dh_cash_plan1.funds_commitment) - self.assertEqual(cash_plan.down_payment, self.dh_cash_plan1.down_payment) - self.assertEqual(cash_plan.validation_alerts_count, self.dh_cash_plan1.validation_alerts_count) - self.assertEqual(cash_plan.total_persons_covered, self.dh_cash_plan1.total_persons_covered) - self.assertEqual(cash_plan.total_persons_covered_revised, self.dh_cash_plan1.total_persons_covered_revised) - self.assertEqual(cash_plan.payment_records_count, self.dh_cash_plan1.payment_records_count) - self.assertEqual(cash_plan.total_entitled_quantity, self.dh_cash_plan1.total_entitled_quantity) - self.assertEqual(cash_plan.total_entitled_quantity_revised, self.dh_cash_plan1.total_entitled_quantity_revised) - self.assertEqual(cash_plan.total_delivered_quantity, self.dh_cash_plan1.total_delivered_quantity) - self.assertEqual(cash_plan.total_undelivered_quantity, self.dh_cash_plan1.total_undelivered_quantity) - self.assertEqual(cash_plan.service_provider.ca_id, self.dh_cash_plan1.assistance_through) - - # payment record - payment_record = PaymentRecord.objects.get(ca_id=self.dh_payment_record.ca_id) - - self.assertEqual(payment_record.business_area.cash_assist_code, self.dh_payment_record.business_area) - self.assertEqual(payment_record.status, self.dh_payment_record.status) - self.assertEqual(payment_record.status_date, self.dh_payment_record.status_date) - self.assertEqual(payment_record.ca_id, self.dh_payment_record.ca_id) - self.assertEqual(str(payment_record.ca_hash_id), str(self.dh_payment_record.ca_hash_id)) - self.assertEqual(str(payment_record.household_id), str(self.dh_payment_record.household_mis_id)) - self.assertEqual(str(payment_record.head_of_household_id), str(self.dh_payment_record.head_of_household_mis_id)) - self.assertEqual(payment_record.full_name, self.dh_payment_record.full_name) - self.assertEqual(payment_record.total_persons_covered, self.dh_payment_record.total_persons_covered) - self.assertEqual(payment_record.distribution_modality, self.dh_payment_record.distribution_modality) - self.assertEqual(str(payment_record.target_population_id), str(self.dh_payment_record.target_population_mis_id)) - self.assertEqual(payment_record.entitlement_card_number, self.dh_payment_record.entitlement_card_number) - self.assertEqual(payment_record.entitlement_card_status, self.dh_payment_record.entitlement_card_status) - self.assertEqual( - payment_record.entitlement_card_issue_date, self.dh_payment_record.entitlement_card_issue_date.date() - ) - self.assertEqual( - payment_record.delivery_type, DeliveryMechanism.objects.get(name=self.dh_payment_record.delivery_type) - ) - self.assertEqual(payment_record.currency, self.dh_payment_record.currency) - self.assertEqual(payment_record.entitlement_quantity, self.dh_payment_record.entitlement_quantity) - self.assertEqual(payment_record.delivered_quantity, self.dh_payment_record.delivered_quantity) - self.assertEqual(payment_record.delivery_date, self.dh_payment_record.delivery_date) - self.assertEqual(payment_record.transaction_reference_id, self.dh_payment_record.transaction_reference_id) - self.assertEqual(payment_record.vision_id, self.dh_payment_record.vision_id) - self.assertEqual(payment_record.service_provider_id, service_provider.id) - self.assertEqual(payment_record.registration_ca_id, self.dh_payment_record.registration_ca_id) - - self.assertIn(self.household, self.program.households.all()) - self.household.refresh_from_db() - self.assertEqual(self.household.total_cash_received, 10) - - -class TestSessionsPullDataFromDatahub(TestCase): - databases = "__all__" - fixtures = (f"{settings.PROJECT_ROOT}/apps/geo/fixtures/data.json",) - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - call_command("loadbusinessareas") - call_command("loadcountrycodes") - - def test_multiple_sessions_same_ba_working(self) -> None: - session1 = Session(status=Session.STATUS_READY, business_area=BusinessArea.objects.first().cash_assist_code) - session1.save() - session2 = Session(status=Session.STATUS_READY, business_area=BusinessArea.objects.first().cash_assist_code) - session2.save() - copy_session_mock = MagicMock() - with patch( - "hct_mis_api.apps.cash_assist_datahub.tasks.pull_from_datahub.PullFromDatahubTask.copy_session", - copy_session_mock, - ): - task = PullFromDatahubTask(DummyExchangeRates()) - task.execute() - self.assertEqual(copy_session_mock.call_count, 2) - session1.delete() - session2.delete() - - def test_multiple_sessions_same_ba_fail(self) -> None: - session1 = Session(status=Session.STATUS_FAILED, business_area=BusinessArea.objects.first().cash_assist_code) - session1.save() - session2 = Session(status=Session.STATUS_READY, business_area=BusinessArea.objects.first().cash_assist_code) - session2.save() - copy_session_mock = MagicMock() - with patch( - "hct_mis_api.apps.cash_assist_datahub.tasks.pull_from_datahub.PullFromDatahubTask.copy_session", - copy_session_mock, - ): - task = PullFromDatahubTask(DummyExchangeRates()) - task.execute() - self.assertEqual(copy_session_mock.call_count, 0) - self.assertEqual(copy_session_mock.call_args_list, []) - session1.delete() - session2.delete() - - def test_multiple_sessions_different_ba_run1(self) -> None: - session1 = Session(status=Session.STATUS_FAILED, business_area=BusinessArea.objects.first().cash_assist_code) - session1.save() - session2 = Session(status=Session.STATUS_READY, business_area=BusinessArea.objects.first().cash_assist_code) - session2.save() - session3 = Session(status=Session.STATUS_READY, business_area=BusinessArea.objects.all()[3].cash_assist_code) - session3.save() - copy_session_mock = MagicMock() - with patch( - "hct_mis_api.apps.cash_assist_datahub.tasks.pull_from_datahub.PullFromDatahubTask.copy_session", - copy_session_mock, - ): - task = PullFromDatahubTask(DummyExchangeRates()) - task.execute() - self.assertEqual(copy_session_mock.call_count, 1) - self.assertEqual(copy_session_mock.call_args_list[0][0][0].id, session3.id) - session1.delete() - session2.delete() - session3.delete() - - @parameterized.expand( - [ - ( - "equal", - "AFG", - "AFG", - ), - ( - "custom_code", - "AUL", - "AUS", - ), - ] - ) - def test_country_mapping(self, _: Any, ca_code: str, expected: str) -> None: - session = Session(status=Session.STATUS_READY, business_area=BusinessArea.objects.first().cash_assist_code) - session.save() - dh_service_provider = DHServiceProvider() - dh_service_provider.session = session - dh_service_provider.business_area = BusinessArea.objects.first().cash_assist_code - dh_service_provider.ca_id = str(uuid.uuid4()) - dh_service_provider.full_name = "SOME TEST BANK" - dh_service_provider.short_name = "STB" - dh_service_provider.country = ca_code - dh_service_provider.vision_id = "random-sp-vision-id" - dh_service_provider.save() - - task = PullFromDatahubTask(DummyExchangeRates()) - task.copy_service_providers(session) - service_provider = ServiceProvider.objects.filter(ca_id=dh_service_provider.ca_id).first() - self.assertEqual(service_provider.country, expected) diff --git a/tests/unit/apps/erp_datahub/__init__.py b/tests/unit/apps/erp_datahub/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/unit/apps/erp_datahub/test_pull_from_erp_datahub.py b/tests/unit/apps/erp_datahub/test_pull_from_erp_datahub.py deleted file mode 100644 index b9d5e10620..0000000000 --- a/tests/unit/apps/erp_datahub/test_pull_from_erp_datahub.py +++ /dev/null @@ -1,158 +0,0 @@ -from decimal import Decimal -from unittest.mock import patch - -from django.test import TestCase - -from hct_mis_api.apps.core.fixtures import create_afghanistan -from hct_mis_api.apps.erp_datahub.fixtures import FundsCommitmentFactory -from hct_mis_api.apps.erp_datahub.tasks.pull_from_erp_datahub import ( - PullFromErpDatahubTask, -) -from hct_mis_api.apps.household.fixtures import create_household -from hct_mis_api.apps.payment.fixtures import CashPlanFactory, PaymentRecordFactory - - -class TestPullDataFromErpDatahub(TestCase): - databases = "__all__" - cash_plan_1 = None - cash_plan_2 = None - payment_record_1 = None - payment_record_2 = None - funds_commitment_1 = None - funds_commitment_2 = None - - @classmethod - def _pre_test_commands(cls) -> None: - cls.business_area = create_afghanistan() - - @classmethod - def _setup_in_app_data(cls) -> None: - (household, _) = create_household(household_args={"size": 1}) - - cls.cash_plan_1 = CashPlanFactory(funds_commitment="123456", exchange_rate=None) - cls.cash_plan_2 = CashPlanFactory(funds_commitment="654321", exchange_rate=None) - cls.cash_plan_3 = CashPlanFactory(funds_commitment="000000", exchange_rate=None) - cls.cash_plan_4 = CashPlanFactory(funds_commitment="111111", exchange_rate=None) - - cls.payment_record_1 = PaymentRecordFactory( - parent=cls.cash_plan_1, - business_area=cls.cash_plan_1.business_area, - entitlement_quantity=1000, - entitlement_quantity_usd=None, - delivered_quantity=1000, - delivered_quantity_usd=None, - household=household, - currency="EUR", - ) - cls.payment_record_2 = PaymentRecordFactory( - parent=cls.cash_plan_2, - business_area=cls.cash_plan_2.business_area, - entitlement_quantity=2000, - entitlement_quantity_usd=None, - delivered_quantity=2000, - delivered_quantity_usd=None, - household=household, - currency="EUR", - ) - cls.payment_record_3 = PaymentRecordFactory( - parent=cls.cash_plan_3, - business_area=cls.cash_plan_3.business_area, - entitlement_quantity=3000, - entitlement_quantity_usd=None, - delivered_quantity=3000, - delivered_quantity_usd=None, - household=household, - currency="EUR", - ) - cls.payment_record_4 = PaymentRecordFactory( - parent=cls.cash_plan_4, - business_area=cls.cash_plan_4.business_area, - entitlement_quantity=1000, - entitlement_quantity_usd=None, - delivered_quantity=1000, - delivered_quantity_usd=None, - household=household, - currency="EUR", - ) - - @classmethod - def _setup_datahub_data(cls) -> None: - cls.funds_commitment_1 = FundsCommitmentFactory( - funds_commitment_number="123456", total_open_amount_local=1000, total_open_amount_usd=2000 - ) - cls.funds_commitment_2 = FundsCommitmentFactory( - funds_commitment_number="654321", total_open_amount_local=1500, total_open_amount_usd=2000 - ) - cls.funds_commitment_4 = FundsCommitmentFactory( - funds_commitment_number="111111", total_open_amount_local=1000, total_open_amount_usd=None - ) - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls._pre_test_commands() - cls._setup_in_app_data() - cls._setup_datahub_data() - - @patch("hct_mis_api.apps.payment.models.CashPlan.get_exchange_rate", new=lambda *args, **kwargs: 2.00) - def test_pull_data(self) -> None: - task = PullFromErpDatahubTask() - task.execute() - self.cash_plan_1.refresh_from_db() - self.assertEqual(self.cash_plan_1.exchange_rate, Decimal(2)) - self.assertEqual( - self.cash_plan_1.total_entitled_quantity_usd, - Decimal(self.cash_plan_1.total_entitled_quantity / Decimal(2)).quantize(Decimal(".01")), - ) - self.assertEqual( - self.cash_plan_1.total_entitled_quantity_revised_usd, - Decimal(self.cash_plan_1.total_entitled_quantity_revised / Decimal(2)).quantize(Decimal(".01")), - ) - self.assertEqual( - self.cash_plan_1.total_delivered_quantity_usd, - Decimal(self.cash_plan_1.total_delivered_quantity / Decimal(2)).quantize(Decimal(".01")), - ) - self.assertEqual( - self.cash_plan_1.total_undelivered_quantity_usd, - Decimal(self.cash_plan_1.total_undelivered_quantity / Decimal(2)).quantize(Decimal(".01")), - ) - self.cash_plan_2.refresh_from_db() - self.assertEqual(self.cash_plan_2.exchange_rate, Decimal(2)) - self.cash_plan_3.refresh_from_db() - self.assertEqual(self.cash_plan_3.exchange_rate, Decimal(2)) - self.payment_record_1.refresh_from_db() - self.assertEqual( - self.payment_record_1.delivered_quantity_usd, Decimal(self.payment_record_1.delivered_quantity / Decimal(2)) - ) - self.assertEqual( - self.payment_record_1.entitlement_quantity_usd, - Decimal(self.payment_record_1.entitlement_quantity / Decimal(2)), - ) - self.payment_record_2.refresh_from_db() - self.assertIsNotNone(self.payment_record_2.delivered_quantity) - self.assertIsNotNone(self.payment_record_2.delivered_quantity_usd) - self.assertEqual( - self.payment_record_2.delivered_quantity_usd, Decimal(self.payment_record_2.delivered_quantity / Decimal(2)) - ) - self.assertEqual( - self.payment_record_2.entitlement_quantity_usd, - Decimal(self.payment_record_2.entitlement_quantity / Decimal(2)), - ) - self.payment_record_3.refresh_from_db() - self.assertEqual( - self.payment_record_3.delivered_quantity_usd, Decimal(self.payment_record_3.delivered_quantity / Decimal(2)) - ) - self.assertEqual( - self.payment_record_3.entitlement_quantity_usd, - Decimal(self.payment_record_3.entitlement_quantity / Decimal(2)), - ) - self.cash_plan_4.refresh_from_db() - self.assertEqual(self.cash_plan_4.exchange_rate, Decimal(2)) - self.payment_record_4.refresh_from_db() - self.assertEqual( - self.payment_record_4.delivered_quantity_usd, Decimal(self.payment_record_4.delivered_quantity / Decimal(2)) - ) - self.assertEqual( - self.payment_record_4.entitlement_quantity_usd, - Decimal(self.payment_record_4.entitlement_quantity / Decimal(2)), - ) diff --git a/tests/unit/apps/erp_datahub/test_sync_to_mis_datahub.py b/tests/unit/apps/erp_datahub/test_sync_to_mis_datahub.py deleted file mode 100644 index 56cdd82b62..0000000000 --- a/tests/unit/apps/erp_datahub/test_sync_to_mis_datahub.py +++ /dev/null @@ -1,185 +0,0 @@ -from django.core.management import call_command -from django.test import TestCase -from django.utils import timezone - -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.erp_datahub.fixtures import FundsCommitmentFactory -from hct_mis_api.apps.erp_datahub.models import DownPayment -from hct_mis_api.apps.erp_datahub.tasks.sync_to_mis_datahub import SyncToMisDatahubTask -from hct_mis_api.apps.mis_datahub import models as mis_models - - -class TestSyncToMisDatahubTask(TestCase): - databases = "__all__" - bosnia_and_herzegovina = None - bosnia = None - herzegovina = None - - funds_commitment_1 = None - funds_commitment_2 = None - - @classmethod - def _prepare_business_areas(cls) -> None: - call_command("loadbusinessareas") - cls.bosnia_and_herzegovina = BusinessArea.objects.get(code="0530") - cls.bosnia = BusinessArea( - code="0531", - name="Bosnia", - long_name="Bosnia", - region_name="ECAR", - slug="bosnia", - parent=cls.bosnia_and_herzegovina, - ) - cls.herzegovina = BusinessArea( - code="0532", - name="Herzegovina", - long_name="Herzegovina", - region_name="ECAR", - slug="herzegovina", - parent=cls.bosnia_and_herzegovina, - ) - cls.bosnia.save() - cls.herzegovina.save() - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls._prepare_business_areas() - - def test_dont_sync_parent(self) -> None: - funds_commitment = FundsCommitmentFactory.create( - business_area=self.bosnia_and_herzegovina.cash_assist_code, - funds_commitment_number="123456", - total_open_amount_local=1000, - total_open_amount_usd=2000, - ) - down_payment = DownPayment.objects.create( - rec_serial_number="1600000009", - business_area=self.bosnia_and_herzegovina.cash_assist_code, - down_payment_reference="2021/3210065763/001", - total_down_payment_amount_local=1000.00, - total_down_payment_amount_usd=1000.00, - currency_code="USD", - posting_date=timezone.now(), - created_by="johniak", - ) - task = SyncToMisDatahubTask() - task.execute() - funds_commitment.refresh_from_db() - down_payment.refresh_from_db() - self.assertEqual(funds_commitment.mis_sync_flag, False) - self.assertEqual(down_payment.mis_sync_flag, False) - self.assertEqual(0, mis_models.FundsCommitment.objects.count()) - self.assertEqual(0, mis_models.DownPayment.objects.count()) - - def test_sync_with_set_new_business_area(self) -> None: - funds_commitment = FundsCommitmentFactory.create( - business_area=self.bosnia_and_herzegovina.cash_assist_code, - funds_commitment_number="123456", - total_open_amount_local=1000, - total_open_amount_usd=2000, - business_office_code=self.bosnia.cash_assist_code, - ) - down_payment = DownPayment.objects.create( - rec_serial_number="1600000009", - business_area=self.bosnia_and_herzegovina.cash_assist_code, - down_payment_reference="2021/3210065763/001", - total_down_payment_amount_local=1000.00, - total_down_payment_amount_usd=1000.00, - currency_code="USD", - posting_date=timezone.now(), - created_by="johniak", - business_office_code=self.herzegovina.cash_assist_code, - ) - task = SyncToMisDatahubTask() - task.execute() - funds_commitment.refresh_from_db() - down_payment.refresh_from_db() - self.assertEqual(funds_commitment.mis_sync_flag, True) - self.assertEqual(down_payment.mis_sync_flag, True) - self.assertEqual(1, mis_models.FundsCommitment.objects.count()) - self.assertEqual(1, mis_models.DownPayment.objects.count()) - - def test_sync_normal(self) -> None: - funds_commitment = FundsCommitmentFactory.create( - business_area=BusinessArea.objects.first().cash_assist_code, - funds_commitment_number="123456", - total_open_amount_local=1000, - total_open_amount_usd=2000, - ) - down_payment = DownPayment.objects.create( - rec_serial_number="1600000009", - business_area=BusinessArea.objects.first().cash_assist_code, - down_payment_reference="2021/3210065763/001", - total_down_payment_amount_local=1000.00, - total_down_payment_amount_usd=1000.00, - currency_code="USD", - posting_date=timezone.now(), - created_by="johniak", - ) - task = SyncToMisDatahubTask() - task.execute() - funds_commitment.refresh_from_db() - down_payment.refresh_from_db() - self.assertEqual(funds_commitment.mis_sync_flag, True) - self.assertEqual(down_payment.mis_sync_flag, True) - self.assertEqual(1, mis_models.FundsCommitment.objects.count()) - self.assertEqual(1, mis_models.DownPayment.objects.count()) - - def test_dont_sync_with_set_new_business_area_already_synced(self) -> None: - funds_commitment = FundsCommitmentFactory.create( - business_area=self.bosnia_and_herzegovina.cash_assist_code, - funds_commitment_number="123456", - total_open_amount_local=1000, - total_open_amount_usd=2000, - business_office_code=self.bosnia.cash_assist_code, - mis_sync_flag=True, - ) - down_payment = DownPayment.objects.create( - rec_serial_number="1600000009", - business_area=self.bosnia_and_herzegovina.cash_assist_code, - down_payment_reference="2021/3210065763/001", - total_down_payment_amount_local=1000.00, - total_down_payment_amount_usd=1000.00, - currency_code="USD", - posting_date=timezone.now(), - created_by="johniak", - business_office_code=self.herzegovina.cash_assist_code, - mis_sync_flag=True, - ) - task = SyncToMisDatahubTask() - task.execute() - funds_commitment.refresh_from_db() - down_payment.refresh_from_db() - self.assertEqual(funds_commitment.mis_sync_flag, True) - self.assertEqual(down_payment.mis_sync_flag, True) - self.assertEqual(0, mis_models.FundsCommitment.objects.count()) - self.assertEqual(0, mis_models.DownPayment.objects.count()) - - def test_dont_sync_normal_already_synced(self) -> None: - funds_commitment = FundsCommitmentFactory.create( - business_area=BusinessArea.objects.first().cash_assist_code, - funds_commitment_number="123456", - total_open_amount_local=1000, - total_open_amount_usd=2000, - mis_sync_flag=True, - ) - down_payment = DownPayment.objects.create( - rec_serial_number="1600000009", - business_area=BusinessArea.objects.first().cash_assist_code, - down_payment_reference="2021/3210065763/001", - total_down_payment_amount_local=1000.00, - total_down_payment_amount_usd=1000.00, - currency_code="USD", - posting_date=timezone.now(), - created_by="johniak", - mis_sync_flag=True, - ) - task = SyncToMisDatahubTask() - task.execute() - funds_commitment.refresh_from_db() - down_payment.refresh_from_db() - self.assertEqual(funds_commitment.mis_sync_flag, True) - self.assertEqual(down_payment.mis_sync_flag, True) - self.assertEqual(0, mis_models.FundsCommitment.objects.count()) - self.assertEqual(0, mis_models.DownPayment.objects.count()) diff --git a/tests/unit/apps/mis_datahub/__init__.py b/tests/unit/apps/mis_datahub/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/unit/apps/mis_datahub/test_data_send_tp_to_datahub.py b/tests/unit/apps/mis_datahub/test_data_send_tp_to_datahub.py deleted file mode 100644 index 4d9e2e18e5..0000000000 --- a/tests/unit/apps/mis_datahub/test_data_send_tp_to_datahub.py +++ /dev/null @@ -1,256 +0,0 @@ -import uuid -from datetime import datetime -from decimal import Decimal -from typing import Any - -from django.core.management import call_command -from django.test import TestCase -from django.utils import timezone - -import hct_mis_api.apps.mis_datahub.models as dh_models -from hct_mis_api.apps.account.models import Partner -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.geo.models import Country -from hct_mis_api.apps.household.fixtures import HouseholdFactory, IndividualFactory -from hct_mis_api.apps.household.models import ( - ROLE_PRIMARY, - UNHCR, - IndividualIdentity, - IndividualRoleInHousehold, -) -from hct_mis_api.apps.mis_datahub.tasks.send_tp_to_datahub import SendTPToDatahubTask -from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory -from hct_mis_api.apps.targeting.models import HouseholdSelection, TargetPopulation -from hct_mis_api.apps.targeting.services.targeting_stats_refresher import refresh_stats -from hct_mis_api.apps.utils.models import MergeStatusModel - - -class TestDataSendTpToDatahub(TestCase): - databases = {"default", "cash_assist_datahub_mis"} - - @staticmethod - def _pre_test_commands() -> None: - create_afghanistan() - call_command("generatedocumenttypes") - call_command("loadcountries") - call_command("loadcountrycodes") - business_area_with_data_sharing = BusinessArea.objects.first() - business_area_with_data_sharing.has_data_sharing_agreement = True - business_area_with_data_sharing.save() - - @staticmethod - def _create_target_population(**kwargs: Any) -> TargetPopulation: - tp_nullable = { - "ca_id": None, - "ca_hash_id": None, - "created_by": None, - "change_date": None, - "changed_by": None, - "finalized_at": None, - "finalized_by": None, - "targeting_criteria": None, - } - - return TargetPopulation.objects.create( - **tp_nullable, - **kwargs, - ) - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls._pre_test_commands() - - business_area_with_data_sharing = BusinessArea.objects.first() - - country = geo_models.Country.objects.get(name="Afghanistan") - state_area_type = AreaTypeFactory( - name="State", - country=country, - area_level=1, - ) - province_area_type = AreaTypeFactory( - name="Province", - country=country, - area_level=2, - ) - admin_area1 = AreaFactory(name="City Test", area_type=state_area_type, p_code="asdfgfhghkjltr1") - admin_area2 = AreaFactory( - name="City Test", area_type=province_area_type, p_code="asdfgfhghkjltr2", parent=admin_area1 - ) - - cls.program = ProgramFactory( - business_area=business_area_with_data_sharing, - ca_hash_id=uuid.uuid4(), - ca_id="TEST", - ) - cls.program_cycle = cls.program.cycles.first() - rdi = RegistrationDataImportFactory(program=cls.program) - - cls.create_first_household(admin_area2, rdi) - - cls.target_population: TargetPopulation = cls._create_target_population( - sent_to_datahub=False, - name="Test TP", - program=cls.program, - business_area=business_area_with_data_sharing, - status=TargetPopulation.STATUS_PROCESSING, - program_cycle=cls.program_cycle, - ) - cls.target_population.households.set([cls.household]) - cls.target_population: TargetPopulation = refresh_stats(cls.target_population) - cls.target_population.save() - HouseholdSelection.objects.update(vulnerability_score=1.23) - - @classmethod - def create_first_household(cls, admin_area: Any, rdi: RegistrationDataImportFactory) -> None: - country = Country.objects.filter(iso_code2="PL").first() - cls.household = HouseholdFactory.build( - size=1, - registration_data_import=rdi, - admin_area=admin_area, - unhcr_id="UNHCR-1337", - country=country, - program=cls.program, - ) - cls.household.household_collection.save() - unhcr, _ = Partner.objects.get_or_create(name=UNHCR, defaults={"is_un": True}) - cls.individual = IndividualFactory( - household=cls.household, relationship="HEAD", registration_data_import=rdi, program=cls.program - ) - IndividualIdentity.objects.create( - partner=unhcr, - number="UN-TEST", - individual=cls.individual, - country=country, - rdi_merge_status=MergeStatusModel.MERGED, - ) - IndividualRoleInHousehold.objects.create( - individual=cls.individual, - household=cls.household, - role=ROLE_PRIMARY, - rdi_merge_status=MergeStatusModel.MERGED, - ) - - cls.household.head_of_household = cls.individual - cls.household.save() - - def test_program_data_is_send_correctly(self) -> None: - self.target_population.refresh_from_db() - task = SendTPToDatahubTask() - task.send_target_population(self.target_population) - expected_program_dict = { - "business_area": "0060", - "ca_hash_id": str(self.program.ca_hash_id), - "ca_id": self.program.ca_id, - "description": self.program.description, - "end_date": timezone.make_aware(datetime.combine(self.program.end_date, datetime.min.time())), - "mis_id": self.program.id, - "name": self.program.name, - "scope": self.program.scope, - "start_date": timezone.make_aware(datetime.combine(self.program.start_date, datetime.min.time())), - "programme_code": self.program.programme_code, - } - dh_program_dict = dh_models.Program.objects.first().__dict__ - dh_program_dict.pop("_state") - dh_program_dict.pop("id") - dh_program_dict.pop("session_id") - self.assertDictEqual(expected_program_dict, dh_program_dict) - - def test_target_population_data_is_send_correctly(self) -> None: - self.target_population.refresh_from_db() - task = SendTPToDatahubTask() - task.send_target_population(self.target_population) - expected_target_population_dict = { - "active_households": 1, - "mis_id": self.target_population.id, - "name": "Test TP", - "population_type": "HOUSEHOLD", - "program_mis_id": self.program.id, - "targeting_criteria": "", - } - dh_target_population_dict = dh_models.TargetPopulation.objects.first().__dict__ - dh_target_population_dict.pop("_state") - dh_target_population_dict.pop("id") - dh_target_population_dict.pop("session_id") - self.assertDictEqual(expected_target_population_dict, dh_target_population_dict) - - def test_target_population_entry_data_is_send_correctly(self) -> None: - self.target_population.refresh_from_db() - task = SendTPToDatahubTask() - task.send_target_population(self.target_population) - expected_target_population_entry_dict = { - "household_mis_id": self.household.id, - "household_unhcr_id": "UNHCR-1337", - "individual_mis_id": None, - "individual_unhcr_id": None, - "target_population_mis_id": self.target_population.id, - "vulnerability_score": Decimal("1.230"), - } - dh_target_population_entry_dict = dh_models.TargetPopulationEntry.objects.first().__dict__ - dh_target_population_entry_dict.pop("_state") - dh_target_population_entry_dict.pop("id") - dh_target_population_entry_dict.pop("session_id") - self.assertDictEqual(expected_target_population_entry_dict, dh_target_population_entry_dict) - - def test_individual_send_correctly(self) -> None: - self.target_population.refresh_from_db() - task = SendTPToDatahubTask() - task.send_target_population(self.target_population) - self.individual.refresh_from_db() - expected_individual_dict = { - "date_of_birth": self.individual.birth_date, - "estimated_date_of_birth": self.individual.estimated_birth_date, - "family_name": self.individual.family_name, - "full_name": self.individual.full_name, - "given_name": self.individual.given_name, - "household_mis_id": self.individual.household.id, - "marital_status": self.individual.marital_status, - "middle_name": self.individual.middle_name, - "mis_id": self.individual.id, - "phone_number": str(self.individual.phone_no), - "pregnant": None, - "relationship": self.individual.relationship, - "sanction_list_confirmed_match": self.individual.sanction_list_confirmed_match, - "sex": self.individual.sex, - "status": self.individual.status, - "unhcr_id": "UN-TEST", - "unicef_id": self.individual.unicef_id, - } - dh_expected_individual_dict = dh_models.Individual.objects.first().__dict__ - dh_expected_individual_dict.pop("_state") - dh_expected_individual_dict.pop("id") - dh_expected_individual_dict.pop("session_id") - self.assertDictEqual(expected_individual_dict, dh_expected_individual_dict) - - def test_household_send_correctly(self) -> None: - task = SendTPToDatahubTask() - self.target_population.refresh_from_db() - self.target_population: TargetPopulation = refresh_stats(self.target_population) - self.target_population.save() - task.send_target_population(self.target_population) - self.household.refresh_from_db() - expected_household_dict = { - "address": self.household.address, - "admin1": self.household.admin1, - "admin2": self.household.admin2, - "country": "POL", - "form_number": None, - "household_size": 1, - "mis_id": self.household.id, - "registration_date": self.household.last_registration_date.date(), - "residence_status": self.household.residence_status, - "status": self.household.status, - "unhcr_id": self.household.unhcr_id, - "unicef_id": self.household.unicef_id, - "village": self.household.village, - } - dh_expected_household_dict = dh_models.Household.objects.first().__dict__ - dh_expected_household_dict.pop("_state") - dh_expected_household_dict.pop("id") - dh_expected_household_dict.pop("session_id") - self.assertDictEqual(expected_household_dict, dh_expected_household_dict) diff --git a/tests/unit/apps/mis_datahub/test_external_collector_send_tp_to_datahub.py b/tests/unit/apps/mis_datahub/test_external_collector_send_tp_to_datahub.py deleted file mode 100644 index 584f3d57d3..0000000000 --- a/tests/unit/apps/mis_datahub/test_external_collector_send_tp_to_datahub.py +++ /dev/null @@ -1,481 +0,0 @@ -from typing import Any - -from django.core.management import call_command -from django.test import TestCase - -import hct_mis_api.apps.mis_datahub.models as dh_models -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 HouseholdFactory, IndividualFactory -from hct_mis_api.apps.household.models import ( - ROLE_ALTERNATE, - ROLE_PRIMARY, - IndividualRoleInHousehold, -) -from hct_mis_api.apps.mis_datahub.tasks.send_tp_to_datahub import SendTPToDatahubTask -from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory -from hct_mis_api.apps.targeting.models import TargetPopulation -from hct_mis_api.apps.targeting.services.targeting_stats_refresher import refresh_stats -from hct_mis_api.apps.utils.models import MergeStatusModel - - -class TestExternalCollectorSendTpToDatahub(TestCase): - databases = {"default", "cash_assist_datahub_mis"} - - @staticmethod - def _pre_test_commands() -> None: - create_afghanistan() - call_command("loadcountries") - call_command("generatedocumenttypes") - call_command("loadcountrycodes") - business_area_with_data_sharing = BusinessArea.objects.first() - business_area_with_data_sharing.has_data_sharing_agreement = True - business_area_with_data_sharing.save() - - @staticmethod - def _create_target_population(**kwargs: Any) -> TargetPopulation: - tp_nullable = { - "ca_id": None, - "ca_hash_id": None, - "created_by": None, - "change_date": None, - "changed_by": None, - "finalized_at": None, - "finalized_by": None, - } - - return TargetPopulation.objects.create( - **tp_nullable, - **kwargs, - ) - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls._pre_test_commands() - - business_area_with_data_sharing = BusinessArea.objects.first() - - country = geo_models.Country.objects.get(name="Afghanistan") - area_type = AreaTypeFactory( - name="State", - country=country, - area_level=1, - ) - admin_area = AreaFactory(name="City Test", area_type=area_type, p_code="asdfgfhghkjltr") - - cls.program_individual_data_needed_true = ProgramFactory( - business_area=business_area_with_data_sharing, - ) - cls.program_individual_data_needed_false = ProgramFactory( - business_area=business_area_with_data_sharing, - ) - - rdi = RegistrationDataImportFactory() - rdi.program.save() - rdi_second = RegistrationDataImportFactory() - rdi_second.program.save() - - cls.create_first_household(admin_area, rdi) - - cls.create_second_household(admin_area, rdi_second) - cls.create_third_household(admin_area, rdi_second) - - cls.target_population_with_individuals = cls._create_target_population( - sent_to_datahub=False, - name="Test TP", - program=cls.program_individual_data_needed_true, - business_area=business_area_with_data_sharing, - status=TargetPopulation.STATUS_PROCESSING, - program_cycle=cls.program_individual_data_needed_true.cycles.first(), - ) - cls.target_population_with_individuals.households.set([cls.household, cls.household_second]) - cls.target_population_with_individuals = refresh_stats(cls.target_population_with_individuals) - cls.target_population_with_individuals.save() - - cls.target_population_without_individuals = cls._create_target_population( - sent_to_datahub=False, - name="Test TP 2", - program=cls.program_individual_data_needed_false, - business_area=business_area_with_data_sharing, - status=TargetPopulation.STATUS_PROCESSING, - program_cycle=cls.program_individual_data_needed_false.cycles.first(), - ) - cls.target_population_without_individuals.households.set([cls.household, cls.household_second]) - cls.target_population_without_individuals = refresh_stats(cls.target_population_without_individuals) - cls.target_population_without_individuals.save() - - @classmethod - def create_first_household(cls, admin_area: Any, rdi: RegistrationDataImportFactory) -> None: - cls.household = HouseholdFactory.build( - size=4, - registration_data_import=rdi, - admin_area=admin_area, - program=rdi.program, - ) - cls.household.household_collection.save() - cls.household1_individual_primary_and_head = IndividualFactory( - household=cls.household, - relationship="HEAD", - registration_data_import=rdi, - program=rdi.program, - ) - IndividualRoleInHousehold.objects.create( - individual=cls.household1_individual_primary_and_head, - household=cls.household, - role=ROLE_PRIMARY, - rdi_merge_status=MergeStatusModel.MERGED, - ) - cls.household1_individual_alternate = IndividualFactory( - household=cls.household, - registration_data_import=rdi, - program=rdi.program, - ) - IndividualRoleInHousehold.objects.create( - individual=cls.household1_individual_alternate, - household=cls.household, - role=ROLE_ALTERNATE, - rdi_merge_status=MergeStatusModel.MERGED, - ) - cls.individual_no_role_first = IndividualFactory( - household=cls.household, - registration_data_import=rdi, - program=rdi.program, - ) - cls.individual_no_role_second = IndividualFactory( - household=cls.household, - registration_data_import=rdi, - program=rdi.program, - ) - cls.household.head_of_household = cls.household1_individual_primary_and_head - cls.household.save() - - @classmethod - def create_second_household(cls, admin_area: Any, rdi_second: RegistrationDataImportFactory) -> None: - cls.household_second = HouseholdFactory.build( - size=1, - registration_data_import=rdi_second, - admin_area=admin_area, - program=rdi_second.program, - ) - cls.household_second.household_collection.save() - cls.external_primary_collector_household = HouseholdFactory.build( - size=1, - registration_data_import=rdi_second, - admin_area=admin_area, - program=rdi_second.program, - ) - cls.external_primary_collector_household.household_collection.save() - cls.second_household_head = IndividualFactory( - household=cls.household_second, - relationship="HEAD", - registration_data_import=rdi_second, - program=rdi_second.program, - ) - cls.external_primary_collector = IndividualFactory( - household=cls.external_primary_collector_household, - registration_data_import=rdi_second, - program=rdi_second.program, - ) - cls.external_primary_collector_household.head_of_household = cls.external_primary_collector - cls.external_primary_collector_household.save() - cls.external_alternate_collector = IndividualFactory( - registration_data_import=rdi_second, - household=cls.external_primary_collector_household, - program=rdi_second.program, - ) - IndividualRoleInHousehold.objects.create( - individual=cls.external_primary_collector, - household=cls.household_second, - role=ROLE_PRIMARY, - rdi_merge_status=MergeStatusModel.MERGED, - ) - IndividualRoleInHousehold.objects.create( - individual=cls.external_alternate_collector, - household=cls.household_second, - role=ROLE_ALTERNATE, - rdi_merge_status=MergeStatusModel.MERGED, - ) - cls.household_second.head_of_household = cls.second_household_head - cls.household_second.save() - - @classmethod - def create_third_household(cls, admin_area: Any, rdi_second: RegistrationDataImportFactory) -> None: - """this is generated only to have additional informaation in DB""" - household_third = HouseholdFactory.build( - size=1, - registration_data_import=rdi_second, - admin_area=admin_area, - program=rdi_second.program, - ) - household_third.household_collection.save() - external_primary_collector_household = HouseholdFactory.build( - size=1, - registration_data_import=rdi_second, - admin_area=admin_area, - program=rdi_second.program, - ) - external_primary_collector_household.household_collection.save() - household_third_head = IndividualFactory( - household=household_third, - relationship="HEAD", - registration_data_import=rdi_second, - program=rdi_second.program, - ) - external_primary_collector = IndividualFactory( - household=external_primary_collector_household, - registration_data_import=rdi_second, - program=rdi_second.program, - ) - external_primary_collector_household.head_of_household = external_primary_collector - external_primary_collector_household.save() - external_alternate_collector = IndividualFactory( - registration_data_import=rdi_second, - household=external_primary_collector_household, - program=rdi_second.program, - ) - IndividualRoleInHousehold.objects.create( - individual=external_primary_collector, - household=household_third, - role=ROLE_PRIMARY, - rdi_merge_status=MergeStatusModel.MERGED, - ) - IndividualRoleInHousehold.objects.create( - individual=external_alternate_collector, - household=household_third, - role=ROLE_ALTERNATE, - rdi_merge_status=MergeStatusModel.MERGED, - ) - household_third.head_of_household = household_third_head - household_third.save() - - def test_send_targeting_with_external_collectors_with_individuals(self) -> None: - task = SendTPToDatahubTask() - task.send_target_population(self.target_population_with_individuals) - - # Check first household in DB - self.assertEqual( - dh_models.Household.objects.filter(mis_id=self.household.id).count(), - 1, - "Household 1 should be in MIS datahub", - ) - - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.household1_individual_primary_and_head.id).count(), - 1, - "Head of household for first household should be in datahub", - ) - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.household1_individual_alternate.id).count(), - 1, - "Alternate collector for first household should be in datahub", - ) - self.assertEqual( - dh_models.Individual.objects.filter( - mis_id__in=[self.individual_no_role_first.id, self.individual_no_role_second.id] - ).count(), - 0, - "All individuals for first household should be in datahub", - ) - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.filter( - individual_mis_id=self.household1_individual_primary_and_head.id, - household_mis_id=self.household.id, - role=ROLE_PRIMARY, - ).count(), - 1, - "Primary collector role for household 1 should exist", - ) - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.filter( - individual_mis_id=self.household1_individual_alternate.id, - household_mis_id=self.household.id, - role=ROLE_ALTERNATE, - ).count(), - 1, - "Alternate collector role for household 1 should exist", - ) - - # Check 2nd household in DB - self.assertEqual( - dh_models.Household.objects.filter(mis_id=self.household_second.id).count(), - 1, - "Household 2 should be in MIS datahub", - ) - self.assertEqual( - dh_models.Household.objects.filter(mis_id=self.external_primary_collector_household.id).count(), - 1, - "Primary collector household should be in MIS datahub", - ) - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.second_household_head.id).count(), - 1, - "Head of household for 2nd household should be in datahub", - ) - - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.external_primary_collector.id).count(), - 1, - "External Primary collector for 2nd household should be in datahub", - ) - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.external_primary_collector.id).count(), - 1, - "External Alternate collector for 2nd household should be in datahub", - ) - - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.filter( - individual_mis_id=self.external_primary_collector.id, - household_mis_id=self.household_second.id, - role=ROLE_PRIMARY, - ).count(), - 1, - "Primary collector role for 2nd household should exist", - ) - - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.filter( - individual_mis_id=self.external_alternate_collector.id, - household_mis_id=self.household_second.id, - role=ROLE_ALTERNATE, - ).count(), - 1, - "Alternate collector role for 2nd household should exist", - ) - - # check for duplcations - - self.assertEqual( - dh_models.Household.objects.count(), - 3, - "Only 3 households should be copied", - ) - - self.assertEqual( - dh_models.Individual.objects.count(), - 5, - "Only 5 individuals should be copied", - ) - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.count(), - 4, - "Only 4 Roles should be copied", - ) - - def test_send_targeting_with_external_collectors_without_individuals(self) -> None: - task = SendTPToDatahubTask() - task.send_target_population(self.target_population_without_individuals) - - # Check first household in DB - self.assertEqual( - dh_models.Household.objects.filter(mis_id=self.household.id).count(), - 1, - "Household 1 should be in MIS datahub", - ) - - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.household1_individual_primary_and_head.id).count(), - 1, - "Head of household for first household should be in datahub", - ) - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.household1_individual_alternate.id).count(), - 1, - "Alternate collector for first household should be in datahub", - ) - self.assertEqual( - dh_models.Individual.objects.filter( - mis_id__in=[self.individual_no_role_first.id, self.individual_no_role_second.id] - ).count(), - 0, - "Individuals without role should not be sent", - ) - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.filter( - individual_mis_id=self.household1_individual_primary_and_head.id, - household_mis_id=self.household.id, - role=ROLE_PRIMARY, - ).count(), - 1, - "Primary collector role for household 1 should exist", - ) - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.filter( - individual_mis_id=self.household1_individual_alternate.id, - household_mis_id=self.household.id, - role=ROLE_ALTERNATE, - ).count(), - 1, - "Alternate collector role for household 1 should exist", - ) - - # Check 2nd household in DB - self.assertEqual( - dh_models.Household.objects.filter(mis_id=self.household_second.id).count(), - 1, - "Household 2 should be in MIS datahub", - ) - self.assertEqual( - dh_models.Household.objects.filter(mis_id=self.external_primary_collector_household.id).count(), - 1, - "Primary collector household should be in MIS datahub", - ) - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.second_household_head.id).count(), - 1, - "Head of household for 2nd household should be in datahub", - ) - - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.external_primary_collector.id).count(), - 1, - "External Primary collector for 2nd household should be in datahub", - ) - self.assertEqual( - dh_models.Individual.objects.filter(mis_id=self.external_primary_collector.id).count(), - 1, - "External Alternate collector for 2nd household should be in datahub", - ) - - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.filter( - individual_mis_id=self.external_primary_collector.id, - household_mis_id=self.household_second.id, - role=ROLE_PRIMARY, - ).count(), - 1, - "Primary collector role for 2nd household should exist", - ) - - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.filter( - individual_mis_id=self.external_alternate_collector.id, - household_mis_id=self.household_second.id, - role=ROLE_ALTERNATE, - ).count(), - 1, - "Alternate collector role for 2nd household should exist", - ) - - # check for duplcations - - self.assertEqual( - dh_models.Household.objects.count(), - 3, - "Only 3 households should be copied", - ) - - self.assertEqual( - dh_models.Individual.objects.count(), - 5, - "Only 5 individuals should be copied", - ) - self.assertEqual( - dh_models.IndividualRoleInHousehold.objects.count(), - 4, - "Only 4 Roles should be copied", - ) diff --git a/tests/unit/apps/mis_datahub/test_send_tp_to_datahub.py b/tests/unit/apps/mis_datahub/test_send_tp_to_datahub.py deleted file mode 100644 index 66c2c96e9a..0000000000 --- a/tests/unit/apps/mis_datahub/test_send_tp_to_datahub.py +++ /dev/null @@ -1,411 +0,0 @@ -from typing import Any - -from django.core.management import call_command -from django.db.utils import IntegrityError -from django.test import TestCase - -from parameterized import parameterized - -import hct_mis_api.apps.mis_datahub.models as dh_models -from hct_mis_api.apps.account.fixtures import PartnerFactory -from hct_mis_api.apps.account.models import Partner -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 ( - HouseholdFactory, - IndividualFactory, - create_household, -) -from hct_mis_api.apps.household.models import ( - ROLE_ALTERNATE, - ROLE_PRIMARY, - Document, - DocumentType, - IndividualIdentity, - IndividualRoleInHousehold, -) -from hct_mis_api.apps.mis_datahub.tasks.send_tp_to_datahub import SendTPToDatahubTask -from hct_mis_api.apps.program.fixtures import ProgramFactory -from hct_mis_api.apps.registration_data.fixtures import RegistrationDataImportFactory -from hct_mis_api.apps.targeting.fixtures import ( - HouseholdSelectionFactory, - TargetingCriteriaFactory, - TargetingCriteriaRuleFactory, - TargetPopulationFactory, -) -from hct_mis_api.apps.targeting.models import TargetPopulation -from hct_mis_api.apps.targeting.services.targeting_stats_refresher import refresh_stats -from hct_mis_api.apps.utils.models import MergeStatusModel - - -class TestSendTpToDatahub(TestCase): - databases = {"default", "cash_assist_datahub_mis"} - - @staticmethod - def _pre_test_commands() -> None: - create_afghanistan() - PartnerFactory(name="UNICEF") - call_command("loadcountries") - call_command("generatedocumenttypes") - call_command("loadcountrycodes") - business_area_with_data_sharing = BusinessArea.objects.first() - business_area_with_data_sharing.has_data_sharing_agreement = True - business_area_with_data_sharing.save() - - @staticmethod - def _create_target_population(**kwargs: Any) -> TargetPopulation: - tp_nullable = { - "ca_id": None, - "ca_hash_id": None, - "created_by": None, - "change_date": None, - "changed_by": None, - "finalized_at": None, - "finalized_by": None, - } - - return TargetPopulation.objects.create( - **tp_nullable, - **kwargs, - ) - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls._pre_test_commands() - - business_area_with_data_sharing = BusinessArea.objects.first() - - country = geo_models.Country.objects.get(name="Afghanistan") - area_type = AreaTypeFactory( - name="State", - country=country, - area_level=1, - ) - admin_area = AreaFactory(name="City Test", area_type=area_type, p_code="asdfgfhghkjltr") - - cls.unhcr, _ = Partner.objects.get_or_create(name="UNHCR", defaults={"is_un": True}) - - cls.program_individual_data_needed_true = ProgramFactory( - business_area=business_area_with_data_sharing, - ) - cls.program_individual_data_needed_false = ProgramFactory( - business_area=business_area_with_data_sharing, - ) - cls.program_third = ProgramFactory( - business_area=business_area_with_data_sharing, - ) - rdi = RegistrationDataImportFactory() - rdi.program.save() - rdi_second = RegistrationDataImportFactory() - rdi_second.program.save() - - cls.household = HouseholdFactory.build( - size=4, - registration_data_import=rdi, - admin_area=admin_area, - program=rdi.program, - ) - cls.household.household_collection.save() - cls.household_second = HouseholdFactory.build( - size=1, registration_data_import=rdi_second, admin_area=admin_area, program=rdi_second.program - ) - cls.household_second.household_collection.save() - cls.second_household_head = IndividualFactory( - household=cls.household_second, - relationship="HEAD", - registration_data_import=rdi_second, - program=rdi_second.program, - ) - unhcr, _ = Partner.objects.get_or_create(name="UNHCR", defaults={"is_un": True}) - IndividualRoleInHousehold.objects.create( - individual=cls.second_household_head, - household=cls.household_second, - role=ROLE_PRIMARY, - rdi_merge_status=MergeStatusModel.MERGED, - ) - cls.household_second.head_of_household = cls.second_household_head - cls.household_second.save() - - cls.individual_primary = IndividualFactory( - household=cls.household, relationship="HEAD", registration_data_import=rdi, program=rdi.program - ) - IndividualRoleInHousehold.objects.create( - individual=cls.individual_primary, - household=cls.household, - role=ROLE_PRIMARY, - rdi_merge_status=MergeStatusModel.MERGED, - ) - Document.objects.create( - document_number="1231231", - photo="", - individual=cls.individual_primary, - type=DocumentType.objects.filter(key="national_id").first(), - program=cls.individual_primary.program, - rdi_merge_status=MergeStatusModel.MERGED, - ) - IndividualIdentity.objects.create( - partner=cls.unhcr, - individual=cls.individual_primary, - number="1111", - country=country, - rdi_merge_status=MergeStatusModel.MERGED, - ) - - cls.individual_alternate = IndividualFactory( - household=cls.household, registration_data_import=rdi, program=rdi.program - ) - IndividualRoleInHousehold.objects.create( - individual=cls.individual_alternate, - household=cls.household, - role=ROLE_ALTERNATE, - rdi_merge_status=MergeStatusModel.MERGED, - ) - IndividualIdentity.objects.create( - individual=cls.individual_alternate, - number="2222", - partner=cls.unhcr, - country=country, - rdi_merge_status=MergeStatusModel.MERGED, - ) - - cls.individual_no_role_first = IndividualFactory( - household=cls.household, - registration_data_import=rdi, - program=rdi.program, - ) - IndividualIdentity.objects.create( - individual=cls.individual_no_role_first, - number="3333", - partner=cls.unhcr, - country=country, - rdi_merge_status=MergeStatusModel.MERGED, - ) - - cls.individual_no_role_second = IndividualFactory( - household=cls.household, registration_data_import=rdi, program=rdi.program - ) - IndividualIdentity.objects.create( - individual=cls.individual_no_role_second, - number="4444", - partner=cls.unhcr, - country=country, - rdi_merge_status=MergeStatusModel.MERGED, - ) - - cls.household.head_of_household = cls.individual_primary - cls.household.save() - - cls.target_population_first = cls._create_target_population( - sent_to_datahub=False, - name="Test TP", - program=cls.program_individual_data_needed_true, - business_area=business_area_with_data_sharing, - status=TargetPopulation.STATUS_PROCESSING, - program_cycle=cls.program_individual_data_needed_true.cycles.first(), - ) - cls.target_population_first.households.set([cls.household]) - cls.target_population_first = refresh_stats(cls.target_population_first) - cls.target_population_first.save() - - cls.target_population_second = cls._create_target_population( - sent_to_datahub=False, - name="Test TP 2", - program=cls.program_individual_data_needed_false, - business_area=business_area_with_data_sharing, - status=TargetPopulation.STATUS_PROCESSING, - program_cycle=cls.program_individual_data_needed_false.cycles.first(), - ) - cls.target_population_second.households.set([cls.household]) - cls.target_population_second = refresh_stats(cls.target_population_second) - cls.target_population_second.save() - - cls.target_population_third = cls._create_target_population( - sent_to_datahub=False, - name="Test TP 3", - program=cls.program_third, - business_area=business_area_with_data_sharing, - status=TargetPopulation.STATUS_PROCESSING, - program_cycle=cls.program_third.cycles.first(), - ) - cls.target_population_third.households.set([cls.household_second]) - cls.target_population_third = refresh_stats(cls.target_population_third) - cls.target_population_third.save() - - def test_individual_data_needed_true(self) -> None: - task = SendTPToDatahubTask() - task.send_target_population(self.target_population_first) - - dh_household = dh_models.Household.objects.all() - dh_individuals = dh_models.Individual.objects.all() - dh_documents = dh_models.Document.objects.all() - dh_roles = dh_models.IndividualRoleInHousehold.objects.all() - - self.assertEqual(dh_household.count(), 1) - self.assertEqual(dh_individuals.count(), 2) - self.assertEqual(dh_documents.count(), 1) - self.assertEqual(dh_roles.count(), 2) - - def test_individual_data_needed_false(self) -> None: - task = SendTPToDatahubTask() - task.send_target_population(self.target_population_second) - - dh_household = dh_models.Household.objects.all() - dh_individuals = dh_models.Individual.objects.all() - dh_documents = dh_models.Document.objects.all() - dh_roles = dh_models.IndividualRoleInHousehold.objects.all() - - self.assertEqual(dh_household.count(), 1) - self.assertEqual(dh_individuals.count(), 2) - self.assertEqual(dh_documents.count(), 1) - self.assertEqual(dh_roles.count(), 2) - - def test_individual_sharing_is_true_and_unhcr_id(self) -> None: - task = SendTPToDatahubTask() - task.send_target_population(self.target_population_third) - - dh_household = dh_models.Household.objects.all() - dh_individuals = dh_models.Individual.objects.all() - dh_documents = dh_models.Document.objects.all() - dh_roles = dh_models.IndividualRoleInHousehold.objects.all() - self.assertEqual(dh_household.count(), 1) - self.assertEqual(dh_household.first().unhcr_id, None) - self.assertEqual(dh_individuals.count(), 1) - self.assertEqual(dh_documents.count(), 0) - self.assertEqual(dh_roles.count(), 1) - - def test_send_two_times_household_with_different(self) -> None: - business_area_with_data_sharing = BusinessArea.objects.first() - - program_individual_data_needed_true = ProgramFactory( - business_area=business_area_with_data_sharing, - ) - program_individual_data_needed_false = ProgramFactory( - business_area=business_area_with_data_sharing, - ) - (household, individuals) = create_household( - {"size": 3, "residence_status": "HOST", "business_area": business_area_with_data_sharing}, - ) - - target_population_first = self._create_target_population( - sent_to_datahub=False, - name="Test TP xD", - program=program_individual_data_needed_false, - business_area=business_area_with_data_sharing, - status=TargetPopulation.STATUS_PROCESSING, - program_cycle=program_individual_data_needed_false.cycles.first(), - ) - target_population_first.households.set([household]) - target_population_first = refresh_stats(target_population_first) - target_population_second = self._create_target_population( - sent_to_datahub=False, - name="Test TP xD 2", - program=program_individual_data_needed_true, - business_area=business_area_with_data_sharing, - status=TargetPopulation.STATUS_PROCESSING, - program_cycle=program_individual_data_needed_true.cycles.first(), - ) - target_population_second.households.set([household]) - target_population_second = refresh_stats(target_population_second) - task = SendTPToDatahubTask() - task.send_target_population(target_population_first) - dh_households_count = dh_models.Household.objects.filter(mis_id=household.id).count() - dh_individuals_count = dh_models.Individual.objects.filter(household_mis_id=household.id).count() - self.assertEqual(dh_households_count, 1) - self.assertEqual(dh_individuals_count, 1) - task.send_target_population(target_population_second) - dh_individuals_count = dh_models.Individual.objects.filter(household_mis_id=household.id).count() - dh_households_count = dh_models.Household.objects.filter(mis_id=household.id).count() - self.assertEqual(dh_households_count, 1) - self.assertEqual(dh_individuals_count, 1) - - @parameterized.expand( - [ - ("equal", "AF", "AFG"), - ("custom_code", "AU", "AUL"), - ] - ) - def test_send_household_country(self, _: Any, iso_code2: str, expected_ca_code: str) -> None: - (household, individuals) = create_household(household_args={"size": 1}) - household.country = geo_models.Country.objects.filter(iso_code2=iso_code2).first() - household.save() - task = SendTPToDatahubTask() - task.dh_session = dh_models.Session() - dh_household = task._prepare_datahub_object_household(household) - self.assertEqual(dh_household.country, expected_ca_code) - - def test_trim_targeting_criteria(self) -> None: - business_area = BusinessArea.objects.first() - - program = ProgramFactory( - business_area=business_area, - ) - - targeting_criteria = TargetingCriteriaFactory() - TargetingCriteriaRuleFactory.create_batch(150, targeting_criteria=targeting_criteria) - target_population = TargetPopulationFactory( - program=program, - status=TargetPopulation.STATUS_PROCESSING, - targeting_criteria=targeting_criteria, - ) - target_population = refresh_stats(target_population) - - task = SendTPToDatahubTask() - task.send_target_population(target_population) - - dh_target_population = dh_models.TargetPopulation.objects.filter(mis_id=target_population.id).first() - - self.assertEqual(len(dh_target_population.targeting_criteria), 390) - self.assertTrue("..." in dh_target_population.targeting_criteria) - - def test_should_not_trim_targeting_criteria(self) -> None: - business_area = BusinessArea.objects.first() - - program = ProgramFactory( - business_area=business_area, - ) - - targeting_criteria = TargetingCriteriaFactory() - TargetingCriteriaRuleFactory.create_batch(50, targeting_criteria=targeting_criteria) - target_population = TargetPopulationFactory( - program=program, - status=TargetPopulation.STATUS_PROCESSING, - targeting_criteria=targeting_criteria, - ) - target_population = refresh_stats(target_population) - - task = SendTPToDatahubTask() - task.send_target_population(target_population) - - dh_target_population = dh_models.TargetPopulation.objects.filter(mis_id=target_population.id).first() - - self.assertEqual(len(dh_target_population.targeting_criteria), 194) - self.assertFalse("..." in dh_target_population.targeting_criteria) - - def test_not_creating_duplicate_households(self) -> None: - business_area = BusinessArea.objects.first() - - program = ProgramFactory( - business_area=business_area, - ) - - targeting_criteria = TargetingCriteriaFactory() - TargetingCriteriaRuleFactory.create_batch(50, targeting_criteria=targeting_criteria) - target_population = TargetPopulationFactory( - program=program, - status=TargetPopulation.STATUS_PROCESSING, - targeting_criteria=targeting_criteria, - ) - - try: - for _ in range(2): - HouseholdSelectionFactory( - household=self.household, - target_population=target_population, - ) - except IntegrityError: - pass - else: - self.fail("Should raise IntegrityError") diff --git a/tests/unit/apps/payment/test_recalculating_household_cash_received.py b/tests/unit/apps/payment/test_recalculating_household_cash_received.py deleted file mode 100644 index dc2acc930e..0000000000 --- a/tests/unit/apps/payment/test_recalculating_household_cash_received.py +++ /dev/null @@ -1,293 +0,0 @@ -import uuid -from typing import Any, Dict -from unittest.mock import MagicMock - -from django.utils.dateparse import parse_date - -import hct_mis_api.apps.cash_assist_datahub.fixtures as ca_fixtures -import hct_mis_api.apps.cash_assist_datahub.models as ca_models -import hct_mis_api.apps.payment.fixtures as payment_fixtures -from hct_mis_api.apps.account.fixtures import UserFactory -from hct_mis_api.apps.account.permissions import Permissions -from hct_mis_api.apps.cash_assist_datahub.tasks.pull_from_datahub import ( - PullFromDatahubTask, -) -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, DataCollectingType -from hct_mis_api.apps.core.utils import decode_id_string -from hct_mis_api.apps.household.fixtures import create_household -from hct_mis_api.apps.payment.fixtures import CashPlanFactory -from hct_mis_api.apps.program.models import ProgramCycle - - -class TestRecalculatingCash(APITestCase): - databases = ( - "default", - "cash_assist_datahub_ca", - ) - - CREATE_PROGRAM_MUTATION = """ - mutation CreateProgram($programData: CreateProgramInput!) { - createProgram(programData: $programData) { - program { - id - name - status - startDate - endDate - budget - description - frequencyOfPayments - sector - cashPlus - populationGoal - administrativeAreasOfImplementation - cycles { - edges { - node { - id - status - } - } - } - } - validationErrors - } - } - """ - - CREATE_TARGET_POPULATION_MUTATION = """ - mutation CreateTargetPopulation($createTargetPopulationInput: CreateTargetPopulationInput!) { - createTargetPopulation(input: $createTargetPopulationInput) { - targetPopulation{ - id - name - status - } - } - } - """ - - UPDATE_PROGRAM_MUTATION = """ - mutation UpdateProgram($programData: UpdateProgramInput) { - updateProgram(programData: $programData) { - program { - status - } - } - } - """ - - LOCK_TARGET_POPULATION_MUTATION = """ - mutation LockTP($id: ID!) { - lockTargetPopulation(id: $id) { - targetPopulation { - __typename - } - __typename - } - } - """ - - FINALIZE_TARGET_POPULATION_MUTATION = """ - mutation FinalizeTP($id: ID!) { - finalizeTargetPopulation(id: $id) { - targetPopulation { - __typename - } - __typename - } - } - """ - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - create_afghanistan() - payment_fixtures.generate_delivery_mechanisms() - cls.user = UserFactory.create() - cls.business_area = BusinessArea.objects.get(slug="afghanistan") - - cls.data_collecting_type = DataCollectingType.objects.create( - code="full", description="Full individual collected", active=True - ) - cls.data_collecting_type.limit_to.add(cls.business_area) - - cls.create_program_mutation_variables = { - "programData": { - "administrativeAreasOfImplementation": "", - "budget": "10000.00", - "businessAreaSlug": "afghanistan", - "cashPlus": True, - "description": "", - "endDate": "2022-07-26", - "frequencyOfPayments": "ONE_OFF", - "name": "newprogram", - "populationGoal": 1, - "sector": "MULTI_PURPOSE", - "startDate": "2022-07-25", - "dataCollectingTypeCode": cls.data_collecting_type.code, - } - } - - cls.create_target_population_mutation_variables = lambda program_id, program_cycle_id: { - "createTargetPopulationInput": { - "programId": program_id, - "name": "asdasd", - "excludedIds": "", - "exclusionReason": "", - "businessAreaSlug": "afghanistan", - "programCycleId": program_cycle_id, - "targetingCriteria": { - "rules": [ - { - "filters": [ - { - "comparisonMethod": "EQUALS", - "fieldName": "consent", - "flexFieldClassification": "NOT_FLEX_FIELD", - "arguments": [True], - } - ], - "individualsFiltersBlocks": [], - } - ] - }, - } - } - - cls.update_program_mutation_variables = lambda program_id: { - "programData": { - "id": program_id, - "status": "ACTIVE", - } - } - - def send_successful_graphql_request(self, **kwargs: Any) -> Dict: - response = self.graphql_request(**kwargs) - self.assertTrue("data" in response) # ensures successful response - return response - - def create_program(self) -> Dict: - return self.send_successful_graphql_request( - request_string=self.CREATE_PROGRAM_MUTATION, - context={"user": self.user}, - variables=self.create_program_mutation_variables, - ) - - def activate_program(self, program_id: str) -> Dict: - return self.send_successful_graphql_request( - request_string=self.UPDATE_PROGRAM_MUTATION, - context={"user": self.user}, - variables=self.update_program_mutation_variables(program_id), - ) - - def create_target_population(self, program_id: str, program_cycle_id: str) -> Dict: - return self.send_successful_graphql_request( - request_string=self.CREATE_TARGET_POPULATION_MUTATION, - context={"user": self.user}, - variables=self.create_target_population_mutation_variables(program_id, program_cycle_id), - ) - - def lock_target_population(self, target_population_id: str) -> Dict: - return self.send_successful_graphql_request( - request_string=self.LOCK_TARGET_POPULATION_MUTATION, - context={"user": self.user}, - variables={"id": target_population_id}, - ) - - def finalize_target_population(self, target_population_id: str) -> Dict: - return self.send_successful_graphql_request( - request_string=self.FINALIZE_TARGET_POPULATION_MUTATION, - context={"user": self.user}, - variables={"id": target_population_id}, - ) - - def test_household_cash_received_update(self) -> None: - self.create_user_role_with_permissions( - self.user, - [ - Permissions.PROGRAMME_CREATE, - Permissions.TARGETING_CREATE, - Permissions.PROGRAMME_ACTIVATE, - Permissions.TARGETING_LOCK, - Permissions.TARGETING_SEND, - ], - self.business_area, - ) - household, _ = create_household( - { - "size": 1, - "residence_status": "HOST", - "business_area": self.business_area, - "total_cash_received": None, - "total_cash_received_usd": None, - }, - ) - self.assertIsNone(household.total_cash_received) - self.assertIsNone(household.total_cash_received_usd) - - session = ca_models.Session.objects.create( - business_area=self.business_area.cash_assist_code, status=ca_models.Session.STATUS_READY - ) - - service_provider_ca_id = uuid.uuid4() - cash_plan_ca_id = uuid.uuid4() - payment_fixtures.ServiceProviderFactory.create(ca_id=service_provider_ca_id) - - program_response = self.create_program() - program_id = program_response["data"]["createProgram"]["program"]["id"] - program_cycle_id = program_response["data"]["createProgram"]["program"]["cycles"]["edges"][0]["node"]["id"] - - ProgramCycle.objects.filter(id=decode_id_string(program_cycle_id)).update(end_date=parse_date("2033-01-01")) - - self.activate_program(program_id) - - target_population_response = self.create_target_population(program_id, program_cycle_id) - target_population_id = target_population_response["data"]["createTargetPopulation"]["targetPopulation"]["id"] - - self.lock_target_population(target_population_id) - - self.finalize_target_population(target_population_id) - - cash_amount_1 = 123 - ca_fixtures.PaymentRecordFactory.create( - session=session, - service_provider_ca_id=service_provider_ca_id, - cash_plan_ca_id=cash_plan_ca_id, - household_mis_id=household.id, - delivered_quantity=cash_amount_1, - currency="PLN", - ) - - CashPlanFactory.create(ca_id=cash_plan_ca_id) - - self.assertIsNone(household.total_cash_received) - self.assertIsNone(household.total_cash_received_usd) - - PullFromDatahubTask(exchange_rates_client=MagicMock()).execute() - - household.refresh_from_db() - previous_value = household.total_cash_received - self.assertIsNotNone(household.total_cash_received) - self.assertIsNotNone(household.total_cash_received_usd) - - session_2 = ca_models.Session.objects.create( - business_area=self.business_area.cash_assist_code, status=ca_models.Session.STATUS_READY - ) - cash_amount_2 = 234 - ca_fixtures.PaymentRecordFactory.create( - session=session_2, - service_provider_ca_id=service_provider_ca_id, - cash_plan_ca_id=cash_plan_ca_id, - household_mis_id=household.id, - delivered_quantity=cash_amount_2, - currency="PLN", - ) - - PullFromDatahubTask(exchange_rates_client=MagicMock()).execute() - - household.refresh_from_db() - self.assertNotEqual(previous_value, household.total_cash_received) - self.assertEqual(household.total_cash_received, cash_amount_1 + cash_amount_2) diff --git a/tests/unit/apps/registration_datahub/test_celery_tasks.py b/tests/unit/apps/registration_datahub/test_celery_tasks.py index 386b3097bd..a128249d67 100644 --- a/tests/unit/apps/registration_datahub/test_celery_tasks.py +++ b/tests/unit/apps/registration_datahub/test_celery_tasks.py @@ -556,12 +556,6 @@ def do_nothing_cache(*_args: Any, **_kwargs: Any) -> Generator: "hct_mis_api.contrib.aurora.services.base_flex_registration_service.BaseRegistrationService.validate_data_collection_type" ) class TestAutomatingRDICreationTask(TestCase): - databases = { - "default", - "cash_assist_datahub_ca", - "cash_assist_datahub_erp", - "cash_assist_datahub_mis", - } fixtures = (f"{settings.PROJECT_ROOT}/apps/geo/fixtures/data.json",) @classmethod From 78016cba92c199feac79d4fcf789871f8e9bc684 Mon Sep 17 00:00:00 2001 From: Pavlo Mokiichuk Date: Thu, 10 Oct 2024 13:19:41 +0200 Subject: [PATCH 098/202] Programme Cycle - Allow deletion of cycle only if no TP / PP linked (#4229) * add new property can_remove_cycle * upd fields list * fix serializer * upd can_remove_cycle * allow delete cycle when program doesnt have tp nor rdi * add can remove flag * can remove only draft cycles * add delete permission * add perm + flag to delete * add more tests for can_remove_cycle field * upd fe types --------- Co-authored-by: Maciej Szewczyk Co-authored-by: Maciej Szewczyk <34482854+mmaciekk@users.noreply.github.com> --- src/frontend/src/api/programCycleApi.ts | 1 + .../SupportingDocumentsSection.tsx | 54 +++++++++---------- .../ProgramCyclesTableProgramDetails.tsx | 18 ++++--- .../apps/program/api/serializers.py | 5 ++ src/hct_mis_api/apps/program/models.py | 9 ++++ tests/unit/apps/program/test_program_cycle.py | 3 ++ .../program/test_program_cycle_rest_api.py | 10 ++++ 7 files changed, 67 insertions(+), 33 deletions(-) diff --git a/src/frontend/src/api/programCycleApi.ts b/src/frontend/src/api/programCycleApi.ts index d067bd2cef..1a88e215a4 100644 --- a/src/frontend/src/api/programCycleApi.ts +++ b/src/frontend/src/api/programCycleApi.ts @@ -40,6 +40,7 @@ export interface ProgramCycle { total_delivered_quantity_usd: number; frequency_of_payments: string; admin_url?: string; + can_remove_cycle: boolean; } export const fetchProgramCycles = async ( diff --git a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx index 4221c5d931..9a35efbf26 100644 --- a/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx +++ b/src/frontend/src/components/paymentmodule/PaymentPlanDetails/SupportingDocumentsSection/SupportingDocumentsSection.tsx @@ -1,41 +1,41 @@ -import ExpandLessIcon from '@mui/icons-material/ExpandLess'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import DownloadIcon from '@mui/icons-material/Download'; -import DeleteIcon from '@mui/icons-material/Delete'; -import { PaperContainer } from '@components/targeting/PaperContainer'; -import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; +import { + deleteSupportingDocument, + uploadSupportingDocument, +} from '@api/paymentModuleApi'; +import { useConfirmation } from '@components/core/ConfirmationDialog'; +import { DropzoneField } from '@components/core/DropzoneField'; +import { GreyBox } from '@components/core/GreyBox'; import { GreyText } from '@components/core/GreyText'; import { LoadingButton } from '@components/core/LoadingButton'; +import { BlueText } from '@components/grievances/LookUps/LookUpStyles'; +import { PaperContainer } from '@components/targeting/PaperContainer'; +import { DialogTitleWrapper } from '@containers/dialogs/DialogTitleWrapper'; +import { PaymentPlanQuery, PaymentPlanStatus } from '@generated/graphql'; +import { useBaseUrl } from '@hooks/useBaseUrl'; +import { usePermissions } from '@hooks/usePermissions'; +import { useSnackbar } from '@hooks/useSnackBar'; +import DeleteIcon from '@mui/icons-material/Delete'; +import DownloadIcon from '@mui/icons-material/Download'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { - Typography, - IconButton, + Box, + Button, Collapse, Dialog, - DialogTitle, - TextField, DialogActions, - Button, - Box, - Grid, DialogContent, + DialogTitle, FormHelperText, + Grid, + IconButton, + TextField, + Typography, } from '@mui/material'; -import { useState } from 'react'; import { useMutation } from '@tanstack/react-query'; -import { usePermissions } from '@hooks/usePermissions'; +import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { PaymentPlanQuery, PaymentPlanStatus } from '@generated/graphql'; -import { DropzoneField } from '@components/core/DropzoneField'; -import { DialogTitleWrapper } from '@containers/dialogs/DialogTitleWrapper'; -import { - deleteSupportingDocument, - uploadSupportingDocument, -} from '@api/paymentModuleApi'; -import { useSnackbar } from '@hooks/useSnackBar'; -import { useBaseUrl } from '@hooks/useBaseUrl'; -import { useConfirmation } from '@components/core/ConfirmationDialog'; -import { GreyBox } from '@components/core/GreyBox'; -import { BlueText } from '@components/grievances/LookUps/LookUpStyles'; +import { hasPermissions, PERMISSIONS } from 'src/config/permissions'; import { useDownloadSupportingDocument } from './SupportingDocumentsSectionActions'; interface SupportingDocumentsSectionProps { diff --git a/src/frontend/src/containers/tables/ProgramCycle/ProgramCyclesTableProgramDetails.tsx b/src/frontend/src/containers/tables/ProgramCycle/ProgramCyclesTableProgramDetails.tsx index 7b7bac0fcc..ecacbfb17a 100644 --- a/src/frontend/src/containers/tables/ProgramCycle/ProgramCyclesTableProgramDetails.tsx +++ b/src/frontend/src/containers/tables/ProgramCycle/ProgramCyclesTableProgramDetails.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'; @@ -34,6 +34,10 @@ export const ProgramCyclesTableProgramDetails = ({ const canCreateProgramCycle = program.status === ProgramStatus.Active && hasPermissions(PERMISSIONS.PM_PROGRAMME_CYCLE_CREATE, permissions); + const canDeleteProgramCycle = hasPermissions( + PERMISSIONS.PM_PROGRAMME_CYCLE_DELETE, + permissions, + ); const { data, error, isLoading } = useQuery({ queryKey: ['programCycles', businessArea, program.id, queryVariables], @@ -46,13 +50,15 @@ export const ProgramCyclesTableProgramDetails = ({ const renderRow = (row: ProgramCycle): ReactElement => { const detailsUrl = `/${baseUrl}/payment-module/program-cycles/${row.id}`; + const canEditProgramCycle = (row.status === 'Draft' || row.status === 'Active') && hasPermissions(PERMISSIONS.PM_PROGRAMME_CYCLE_UPDATE, permissions); - const canDeleteProgramCycle = - row.status === 'Draft' && - data.results.length > 1 && - hasPermissions(PERMISSIONS.PM_PROGRAMME_CYCLE_DELETE, permissions); + + const hasPermissionToDelete = hasPermissions( + PERMISSIONS.PM_PROGRAMME_CYCLE_DELETE, + permissions, + ); return ( @@ -100,7 +106,7 @@ export const ProgramCyclesTableProgramDetails = ({ )} - {canDeleteProgramCycle && ( + {row.can_remove_cycle && hasPermissionToDelete && ( )} diff --git a/src/hct_mis_api/apps/program/api/serializers.py b/src/hct_mis_api/apps/program/api/serializers.py index 44f546da91..f319c3e428 100644 --- a/src/hct_mis_api/apps/program/api/serializers.py +++ b/src/hct_mis_api/apps/program/api/serializers.py @@ -45,6 +45,7 @@ class ProgramCycleListSerializer(EncodedIdSerializerMixin): program_start_date = serializers.DateField(format="%Y-%m-%d") program_end_date = serializers.DateField(format="%Y-%m-%d") admin_url = serializers.SerializerMethodField() + can_remove_cycle = serializers.SerializerMethodField() class Meta: model = ProgramCycle @@ -63,6 +64,7 @@ class Meta: "frequency_of_payments", "created_by", "admin_url", + "can_remove_cycle", ) def get_created_by(self, obj: ProgramCycle) -> str: @@ -74,6 +76,9 @@ def get_admin_url(self, obj: ProgramCycle) -> Optional[str]: user = self.context["request"].user return obj.admin_url if user.is_superuser else None + def get_can_remove_cycle(self, obj: ProgramCycle) -> bool: + return obj.can_remove_cycle + class ProgramCycleCreateSerializer(EncodedIdSerializerMixin): title = serializers.CharField(required=True) diff --git a/src/hct_mis_api/apps/program/models.py b/src/hct_mis_api/apps/program/models.py index d0f29e502f..334309d7cb 100644 --- a/src/hct_mis_api/apps/program/models.py +++ b/src/hct_mis_api/apps/program/models.py @@ -347,6 +347,15 @@ def save(self, *args: Any, **kwargs: Any) -> None: def __str__(self) -> str: return f"{self.title} ({self.status})" + @property + def can_remove_cycle(self) -> bool: + return ( + not self.target_populations.exists() + and not self.payment_plans.exists() + and self.program.cycles.count() > 1 + and self.status == ProgramCycle.DRAFT + ) + @property def total_entitled_quantity_usd(self) -> Decimal: total_entitled = self.payment_plans.aggregate(total_entitled=Sum("total_entitled_quantity_usd"))[ diff --git a/tests/unit/apps/program/test_program_cycle.py b/tests/unit/apps/program/test_program_cycle.py index 905d8249fa..efd17a02b9 100644 --- a/tests/unit/apps/program/test_program_cycle.py +++ b/tests/unit/apps/program/test_program_cycle.py @@ -69,6 +69,7 @@ def test_set_active(self) -> None: self.cycle.set_active() self.cycle.refresh_from_db() self.assertEqual(self.cycle.status, ProgramCycle.ACTIVE) + self.assertFalse(self.cycle.can_remove_cycle) def test_set_draft(self) -> None: with self.assertRaisesMessage(ValidationError, "Program should be within Active status."): @@ -88,6 +89,7 @@ def test_set_draft(self) -> None: self.cycle.set_draft() self.cycle.refresh_from_db() self.assertEqual(self.cycle.status, ProgramCycle.DRAFT) + self.assertTrue(self.cycle.can_remove_cycle) def test_set_finish(self) -> None: with self.assertRaisesMessage(ValidationError, "Program should be within Active status."): @@ -105,6 +107,7 @@ def test_set_finish(self) -> None: self.cycle.set_finish() self.cycle.refresh_from_db() self.assertEqual(self.cycle.status, ProgramCycle.FINISHED) + self.assertFalse(self.cycle.can_remove_cycle) def test_total_entitled_quantity_usd(self) -> None: self.assertEqual(self.cycle.total_entitled_quantity_usd, Decimal("0.0")) diff --git a/tests/unit/apps/program/test_program_cycle_rest_api.py b/tests/unit/apps/program/test_program_cycle_rest_api.py index 04a53e9109..84c0a4e52d 100644 --- a/tests/unit/apps/program/test_program_cycle_rest_api.py +++ b/tests/unit/apps/program/test_program_cycle_rest_api.py @@ -99,6 +99,16 @@ def test_list_program_cycles(self) -> None: cycles = ProgramCycle.objects.filter(program=self.program) self.assertEqual(int(response.data["count"]), cycles.count()) + results = response.data["results"] + first_cycle = results[0] + second_cycle = results[1] + last_cycle = results[2] + # check can_remove_cycle + self.assertEqual(first_cycle["can_remove_cycle"], False) + self.assertEqual(second_cycle["can_remove_cycle"], False) + self.assertEqual(last_cycle["status"], "Draft") + self.assertEqual(last_cycle["can_remove_cycle"], True) + def test_retrieve_program_cycle(self) -> None: self.client.force_authenticate(user=self.user) response = self.client.get(self.cycle_1_detail_url) From 900d85bbe08c5260ddece48922881cc8ec685eec Mon Sep 17 00:00:00 2001 From: Domenico DiNicola Date: Thu, 10 Oct 2024 00:29:11 +0200 Subject: [PATCH 099/202] remove PQ --- .../commands/createadminpermissiongroups.py | 2 - .../apps/core/management/commands/upgrade.py | 4 - .../apps/household/admin/household.py | 2 - .../power_query/migrations/0002_migration.py | 18 - .../static/admin/power_query/code.css | 306 + .../admin/power_query/codemirror/abcdef.css | 32 + .../power_query/codemirror/active-line.js | 72 + .../power_query/codemirror/codemirror.css | 343 + .../power_query/codemirror/codemirror.js | 9847 +++++++++++++++++ .../admin/power_query/codemirror/django.js | 356 + .../admin/power_query/codemirror/foldcode.js | 159 + .../power_query/codemirror/foldgutter.css | 20 + .../power_query/codemirror/foldgutter.js | 163 + .../power_query/codemirror/fullscreen.css | 6 + .../power_query/codemirror/fullscreen.js | 41 + .../power_query/codemirror/indent-fold.js | 48 + .../power_query/codemirror/matchbrackets.js | 160 + .../admin/power_query/codemirror/midnight.css | 39 + .../admin/power_query/codemirror/overlay.js | 90 + .../admin/power_query/codemirror/python.js | 402 + .../admin/power_query/codemirror/xml.js | 417 + .../admin/power_query/codemirror/yaml.js | 120 + .../static/admin/power_query/colorful.css | 74 + .../static/admin/power_query/diff.css | 53 + .../static/admin/power_query/editor.png | Bin 0 -> 16556 bytes .../apps/steficon/static/power_query.css | 1 + .../apps/steficon/static/power_query.css.map | 1 + .../apps/steficon/static/power_query.scss | 34 + src/hct_mis_api/apps/steficon/static/redo.png | Bin 0 -> 6742 bytes src/hct_mis_api/apps/steficon/static/undo.png | Bin 0 -> 6554 bytes src/hct_mis_api/apps/steficon/widget.py | 43 + .../config/fragments/smart_admin.py | 3 - src/hct_mis_api/config/settings.py | 2 - src/hct_mis_api/libs/power_query/__init__.py | 0 src/hct_mis_api/libs/power_query/backends.py | 48 - src/hct_mis_api/libs/power_query/defaults.py | 57 - .../libs/power_query/management/__init__.py | 0 .../management/commands/__init__.py | 0 .../power_query/management/commands/pq.py | 102 - src/hct_mis_api/urls.py | 4 - tests/unit/libs/__init__.py | 0 tests/unit/libs/power_query/__init__.py | 0 tests/unit/libs/power_query/test_auth.py | 60 - tests/unit/libs/power_query/test_backend.py | 55 - tests/unit/libs/power_query/test_params.py | 35 - tests/unit/libs/power_query/test_views.py | 129 - tests/unit/libs/power_query/utils.py | 58 - 47 files changed, 12827 insertions(+), 579 deletions(-) delete mode 100644 src/hct_mis_api/apps/power_query/migrations/0002_migration.py create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/code.css create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/abcdef.css create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/active-line.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/codemirror.css create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/codemirror.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/django.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldcode.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldgutter.css create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldgutter.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/fullscreen.css create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/fullscreen.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/indent-fold.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/matchbrackets.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/midnight.css create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/overlay.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/python.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/xml.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/yaml.js create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/colorful.css create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/diff.css create mode 100644 src/hct_mis_api/apps/steficon/static/admin/power_query/editor.png create mode 100644 src/hct_mis_api/apps/steficon/static/power_query.css create mode 100644 src/hct_mis_api/apps/steficon/static/power_query.css.map create mode 100644 src/hct_mis_api/apps/steficon/static/power_query.scss create mode 100644 src/hct_mis_api/apps/steficon/static/redo.png create mode 100644 src/hct_mis_api/apps/steficon/static/undo.png delete mode 100644 src/hct_mis_api/libs/power_query/__init__.py delete mode 100644 src/hct_mis_api/libs/power_query/backends.py delete mode 100644 src/hct_mis_api/libs/power_query/defaults.py delete mode 100644 src/hct_mis_api/libs/power_query/management/__init__.py delete mode 100644 src/hct_mis_api/libs/power_query/management/commands/__init__.py delete mode 100644 src/hct_mis_api/libs/power_query/management/commands/pq.py delete mode 100644 tests/unit/libs/__init__.py delete mode 100644 tests/unit/libs/power_query/__init__.py delete mode 100644 tests/unit/libs/power_query/test_auth.py delete mode 100644 tests/unit/libs/power_query/test_backend.py delete mode 100644 tests/unit/libs/power_query/test_params.py delete mode 100644 tests/unit/libs/power_query/test_views.py delete mode 100644 tests/unit/libs/power_query/utils.py diff --git a/src/hct_mis_api/apps/core/management/commands/createadminpermissiongroups.py b/src/hct_mis_api/apps/core/management/commands/createadminpermissiongroups.py index 0d66cd0001..ce4d60f4bf 100644 --- a/src/hct_mis_api/apps/core/management/commands/createadminpermissiongroups.py +++ b/src/hct_mis_api/apps/core/management/commands/createadminpermissiongroups.py @@ -76,7 +76,6 @@ def handle(self, *args: Any, **options: Any) -> Any: "periodictask", "solarschedule", ], - "power_query": ["formatter", "query", "report"], # 'dataset' change only "program": ["cashplan", "program"], "social_django": ["association", "nonce", "usersocialauth"], "registration_data": ["registrationdataimport"], @@ -139,7 +138,6 @@ def handle(self, *args: Any, **options: Any) -> Any: "codename": "change_advancedfilter", "action": "change", }, - {"name": "power_query | dataset | Can change dataset", "codename": "change_dataset", "action": "change"}, ] for i in other_custom_groups_map: 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 f672591b07..29bd9c9010 100644 --- a/src/hct_mis_api/apps/core/management/commands/upgrade.py +++ b/src/hct_mis_api/apps/core/management/commands/upgrade.py @@ -11,7 +11,3 @@ def handle(self, *args: Any, **options: Any) -> None: from adminactions.perms import create_extra_permissions create_extra_permissions() - - from hct_mis_api.libs.power_query.defaults import hope_create_defaults - - hope_create_defaults() diff --git a/src/hct_mis_api/apps/household/admin/household.py b/src/hct_mis_api/apps/household/admin/household.py index 66a8edff0c..7b4c7f6f10 100644 --- a/src/hct_mis_api/apps/household/admin/household.py +++ b/src/hct_mis_api/apps/household/admin/household.py @@ -21,7 +21,6 @@ from adminfilters.autocomplete import LinkedAutoCompleteFilter from adminfilters.depot.widget import DepotManager from adminfilters.querystring import QueryStringFilter -from power_query.mixin import PowerQueryMixin from smart_admin.mixins import FieldsetMixin as SmartFieldsetMixin from hct_mis_api.apps.core.models import BusinessArea @@ -201,7 +200,6 @@ class HouseholdAdmin( SoftDeletableAdminMixin, LastSyncDateResetMixin, LinkedObjectsManagerMixin, - PowerQueryMixin, SmartFieldsetMixin, CursorPaginatorAdmin, HouseholdWithDrawnMixin, diff --git a/src/hct_mis_api/apps/power_query/migrations/0002_migration.py b/src/hct_mis_api/apps/power_query/migrations/0002_migration.py deleted file mode 100644 index 05e91bbddd..0000000000 --- a/src/hct_mis_api/apps/power_query/migrations/0002_migration.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.19 on 2023-07-27 16:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('power_query', '0001_migration_squashed_0014_migration'), - ] - - operations = [ - migrations.AlterField( - model_name='dataset', - name='description', - field=models.CharField(max_length=100), - ), - ] diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/code.css b/src/hct_mis_api/apps/steficon/static/admin/power_query/code.css new file mode 100644 index 0000000000..72e3b743bd --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/code.css @@ -0,0 +1,306 @@ +.code { + line-height: 125%; + font-family: monospace, sans-serif; + font-size: 16px; +} + + +td.linenos pre { + color: #000000; + background-color: #f0f0f0; + padding-left: 5px; + padding-right: 5px; +} + +span.linenos { + color: #000000; + background-color: #f0f0f0; + padding-left: 5px; + padding-right: 5px; +} + +td.linenos pre.special { + color: #000000; + background-color: #ffffc0; + padding-left: 5px; + padding-right: 5px; +} + +span.linenos.special { + color: #000000; + background-color: #ffffc0; + padding-left: 5px; + padding-right: 5px; +} + +.hll { + background-color: #ffffcc +} + +.c { + color: #008800; + font-style: italic +} + +/* Comment */ +.err { + color: #a61717; + background-color: #e3d2d2 +} + +/* Error */ +.k { + color: #000080; + font-weight: bold +} + +/* Keyword */ +.ch { + color: #008800; + font-style: italic +} + +/* Comment.Hashbang */ +.cm { + color: #008800; + font-style: italic +} + +/* Comment.Multiline */ +.cp { + color: #008080 +} + +/* Comment.Preproc */ +.cpf { + color: #008800; + font-style: italic +} + +/* Comment.PreprocFile */ +.c1 { + color: #008800; + font-style: italic +} + +/* Comment.Single */ +.cs { + color: #008800; + font-weight: bold +} + +/* Comment.Special */ +.gd { + color: #000000; + background-color: #ffdddd +} + +/* Generic.Deleted */ +.ge { + font-style: italic +} + +/* Generic.Emph */ +.gr { + color: #aa0000 +} + +/* Generic.Error */ +.gh { + color: #999999 +} + +/* Generic.Heading */ +.gi { + color: #000000; + background-color: #ddffdd +} + +/* Generic.Inserted */ +.go { + color: #888888 +} + +/* Generic.Output */ +.gp { + color: #555555 +} + +/* Generic.Prompt */ +.gs { + font-weight: bold +} + +/* Generic.Strong */ +.gu { + color: #aaaaaa +} + +/* Generic.Subheading */ +.gt { + color: #aa0000 +} + +/* Generic.Traceback */ +.kc { + color: #000080; + font-weight: bold +} + +/* Keyword.Constant */ +.kd { + color: #000080; + font-weight: bold +} + +/* Keyword.Declaration */ +.kn { + color: #000080; + font-weight: bold +} + +/* Keyword.Namespace */ +.kp { + color: #000080; + font-weight: bold +} + +/* Keyword.Pseudo */ +.kr { + color: #000080; + font-weight: bold +} + +/* Keyword.Reserved */ +.kt { + color: #000080; + font-weight: bold +} + +/* Keyword.Type */ +.m { + color: #0000FF +} + +/* Literal.Number */ +.s { + color: #0000FF +} + +/* Literal.String */ +.na { + color: #FF0000 +} + +/* Name.Attribute */ +.nt { + color: #000080; + font-weight: bold +} + +/* Name.Tag */ +.ow { + font-weight: bold +} + +/* Operator.Word */ +.w { + color: #bbbbbb +} + +/* Text.Whitespace */ +.mb { + color: #0000FF +} + +/* Literal.Number.Bin */ +.mf { + color: #0000FF +} + +/* Literal.Number.Float */ +.mh { + color: #0000FF +} + +/* Literal.Number.Hex */ +.mi { + color: #0000FF +} + +/* Literal.Number.Integer */ +.mo { + color: #0000FF +} + +/* Literal.Number.Oct */ +.sa { + color: #0000FF +} + +/* Literal.String.Affix */ +.sb { + color: #0000FF +} + +/* Literal.String.Backtick */ +.sc { + color: #800080 +} + +/* Literal.String.Char */ +.dl { + color: #0000FF +} + +/* Literal.String.Delimiter */ +.sd { + color: #0000FF +} + +/* Literal.String.Doc */ +.s2 { + color: #0000FF +} + +/* Literal.String.Double */ +.se { + color: #0000FF +} + +/* Literal.String.Escape */ +.sh { + color: #0000FF +} + +/* Literal.String.Heredoc */ +.si { + color: #0000FF +} + +/* Literal.String.Interpol */ +.sx { + color: #0000FF +} + +/* Literal.String.Other */ +.sr { + color: #0000FF +} + +/* Literal.String.Regex */ +.s1 { + color: #0000FF +} + +/* Literal.String.Single */ +.ss { + color: #0000FF +} + +/* Literal.String.Symbol */ +.il { + color: #0000FF +} + +/* Literal.Number.Integer.Long */ diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/abcdef.css b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/abcdef.css new file mode 100644 index 0000000000..cf93530946 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/abcdef.css @@ -0,0 +1,32 @@ +.cm-s-abcdef.CodeMirror { background: #0f0f0f; color: #defdef; } +.cm-s-abcdef div.CodeMirror-selected { background: #515151; } +.cm-s-abcdef .CodeMirror-line::selection, .cm-s-abcdef .CodeMirror-line > span::selection, .cm-s-abcdef .CodeMirror-line > span > span::selection { background: rgba(56, 56, 56, 0.99); } +.cm-s-abcdef .CodeMirror-line::-moz-selection, .cm-s-abcdef .CodeMirror-line > span::-moz-selection, .cm-s-abcdef .CodeMirror-line > span > span::-moz-selection { background: rgba(56, 56, 56, 0.99); } +.cm-s-abcdef .CodeMirror-gutters { background: #555; border-right: 2px solid #314151; } +.cm-s-abcdef .CodeMirror-guttermarker { color: #222; } +.cm-s-abcdef .CodeMirror-guttermarker-subtle { color: azure; } +.cm-s-abcdef .CodeMirror-linenumber { color: #FFFFFF; } +.cm-s-abcdef .CodeMirror-cursor { border-left: 1px solid #00FF00; } + +.cm-s-abcdef span.cm-keyword { color: darkgoldenrod; font-weight: bold; } +.cm-s-abcdef span.cm-atom { color: #77F; } +.cm-s-abcdef span.cm-number { color: violet; } +.cm-s-abcdef span.cm-def { color: #fffabc; } +.cm-s-abcdef span.cm-variable { color: #abcdef; } +.cm-s-abcdef span.cm-variable-2 { color: #cacbcc; } +.cm-s-abcdef span.cm-variable-3, .cm-s-abcdef span.cm-type { color: #def; } +.cm-s-abcdef span.cm-property { color: #fedcba; } +.cm-s-abcdef span.cm-operator { color: #ff0; } +.cm-s-abcdef span.cm-comment { color: #7a7b7c; font-style: italic;} +.cm-s-abcdef span.cm-string { color: #2b4; } +.cm-s-abcdef span.cm-meta { color: #C9F; } +.cm-s-abcdef span.cm-qualifier { color: #FFF700; } +.cm-s-abcdef span.cm-builtin { color: #30aabc; } +.cm-s-abcdef span.cm-bracket { color: #8a8a8a; } +.cm-s-abcdef span.cm-tag { color: #FFDD44; } +.cm-s-abcdef span.cm-attribute { color: #DDFF00; } +.cm-s-abcdef span.cm-error { color: #FF0000; } +.cm-s-abcdef span.cm-header { color: aquamarine; font-weight: bold; } +.cm-s-abcdef span.cm-link { color: blueviolet; } + +.cm-s-abcdef .CodeMirror-activeline-background { background: #314151; } diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/active-line.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/active-line.js new file mode 100644 index 0000000000..c7b14ce07f --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/active-line.js @@ -0,0 +1,72 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var WRAP_CLASS = "CodeMirror-activeline"; + var BACK_CLASS = "CodeMirror-activeline-background"; + var GUTT_CLASS = "CodeMirror-activeline-gutter"; + + CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { + var prev = old == CodeMirror.Init ? false : old; + if (val == prev) return + if (prev) { + cm.off("beforeSelectionChange", selectionChange); + clearActiveLines(cm); + delete cm.state.activeLines; + } + if (val) { + cm.state.activeLines = []; + updateActiveLines(cm, cm.listSelections()); + cm.on("beforeSelectionChange", selectionChange); + } + }); + + function clearActiveLines(cm) { + for (var i = 0; i < cm.state.activeLines.length; i++) { + cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS); + cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS); + } + } + + function sameArray(a, b) { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; i++) + if (a[i] != b[i]) return false; + return true; + } + + function updateActiveLines(cm, ranges) { + var active = []; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + var option = cm.getOption("styleActiveLine"); + if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) + continue + var line = cm.getLineHandleVisualStart(range.head.line); + if (active[active.length - 1] != line) active.push(line); + } + if (sameArray(cm.state.activeLines, active)) return; + cm.operation(function() { + clearActiveLines(cm); + for (var i = 0; i < active.length; i++) { + cm.addLineClass(active[i], "wrap", WRAP_CLASS); + cm.addLineClass(active[i], "background", BACK_CLASS); + cm.addLineClass(active[i], "gutter", GUTT_CLASS); + } + cm.state.activeLines = active; + }); + } + + function selectionChange(cm, sel) { + updateActiveLines(cm, sel.ranges); + } +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/codemirror.css b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/codemirror.css new file mode 100644 index 0000000000..d85fbc930b --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/codemirror.css @@ -0,0 +1,343 @@ +/* BASICS */ + +.CodeMirror { + /* Set height, width, borders, and global font properties here */ + font-family: monospace; + height: 300px; + color: black; + direction: ltr; +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; + white-space: nowrap; +} +.CodeMirror-linenumbers {} +.CodeMirror-linenumber { + padding: 0 3px 0 5px; + min-width: 20px; + text-align: right; + color: #999; + white-space: nowrap; +} + +.CodeMirror-guttermarker { color: black; } +.CodeMirror-guttermarker-subtle { color: #999; } + +/* CURSOR */ + +.CodeMirror-cursor { + border-left: 1px solid black; + border-right: none; + width: 0; +} +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.cm-fat-cursor .CodeMirror-cursor { + width: auto; + border: 0 !important; + background: #7e7; +} +.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} +.cm-fat-cursor .CodeMirror-line::selection, +.cm-fat-cursor .CodeMirror-line > span::selection, +.cm-fat-cursor .CodeMirror-line > span > span::selection { background: transparent; } +.cm-fat-cursor .CodeMirror-line::-moz-selection, +.cm-fat-cursor .CodeMirror-line > span::-moz-selection, +.cm-fat-cursor .CodeMirror-line > span > span::-moz-selection { background: transparent; } +.cm-fat-cursor { caret-color: transparent; } +@-moz-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@-webkit-keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} +@keyframes blink { + 0% {} + 50% { background-color: transparent; } + 100% {} +} + +/* Can style cursor different in overwrite (non-insert) mode */ +.CodeMirror-overwrite .CodeMirror-cursor {} + +.cm-tab { display: inline-block; text-decoration: inherit; } + +.CodeMirror-rulers { + position: absolute; + left: 0; right: 0; top: -50px; bottom: 0; + overflow: hidden; +} +.CodeMirror-ruler { + border-left: 1px solid #ccc; + top: 0; bottom: 0; + position: absolute; +} + +/* DEFAULT THEME */ + +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-negative {color: #d44;} +.cm-positive {color: #292;} +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-link {text-decoration: underline;} +.cm-strikethrough {text-decoration: line-through;} + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable, +.cm-s-default .cm-punctuation, +.cm-s-default .cm-property, +.cm-s-default .cm-operator {} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #997;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-s-default .cm-error {color: #f00;} +.cm-invalidchar {color: #f00;} + +.CodeMirror-composing { border-bottom: 2px solid; } + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} +.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } +.CodeMirror-activeline-background {background: #e8f2ff;} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + position: relative; + overflow: hidden; + background: white; +} + +.CodeMirror-scroll { + overflow: scroll !important; /* Things will break if this is overridden */ + /* 50px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -50px; margin-right: -50px; + padding-bottom: 50px; + height: 100%; + outline: none; /* Prevent dragging from highlighting the element */ + position: relative; +} +.CodeMirror-sizer { + position: relative; + border-right: 50px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actual scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; + outline: none; +} +.CodeMirror-vscrollbar { + right: 0; top: 0; + overflow-x: hidden; + overflow-y: scroll; +} +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; +} +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; +} +.CodeMirror-gutter-filler { + left: 0; bottom: 0; +} + +.CodeMirror-gutters { + position: absolute; left: 0; top: 0; + min-height: 100%; + z-index: 3; +} +.CodeMirror-gutter { + white-space: normal; + height: 100%; + display: inline-block; + vertical-align: top; + margin-bottom: -50px; +} +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} +.CodeMirror-gutter-background { + position: absolute; + top: 0; bottom: 0; + z-index: 4; +} +.CodeMirror-gutter-elt { + position: absolute; + cursor: default; + z-index: 4; +} +.CodeMirror-gutter-wrapper ::selection { background-color: transparent } +.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } + +.CodeMirror-lines { + cursor: text; + min-height: 1px; /* prevents collapsing before first draw */ +} +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-line-like { + /* Reset some styles that the rest of the page might have set */ + -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; + font-family: inherit; + font-size: inherit; + margin: 0; + white-space: pre; + word-wrap: normal; + line-height: inherit; + color: inherit; + z-index: 2; + position: relative; + overflow: visible; + -webkit-tap-highlight-color: transparent; + -webkit-font-variant-ligatures: contextual; + font-variant-ligatures: contextual; +} +.CodeMirror-wrap pre.CodeMirror-line, +.CodeMirror-wrap pre.CodeMirror-line-like { + word-wrap: break-word; + white-space: pre-wrap; + word-break: normal; +} + +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + padding: 0.1px; /* Force widget margins to stay inside of the container */ +} + +.CodeMirror-widget {} + +.CodeMirror-rtl pre { direction: rtl; } + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + width: 100%; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.CodeMirror-cursor { + position: absolute; + pointer-events: none; +} +.CodeMirror-measure pre { position: static; } + +div.CodeMirror-cursors { + visibility: hidden; + position: relative; + z-index: 3; +} +div.CodeMirror-dragcursors { + visibility: visible; +} + +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { background: #d9d9d9; } +.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } +.CodeMirror-crosshair { cursor: crosshair; } +.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } +.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } + +.cm-searching { + background-color: #ffa; + background-color: rgba(255, 255, 0, .4); +} + +/* Used to force a border model for a node */ +.cm-force-border { padding-right: .1px; } + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack:after { content: ''; } + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { background: none; } diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/codemirror.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/codemirror.js new file mode 100644 index 0000000000..c23b52c44c --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/codemirror.js @@ -0,0 +1,9847 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// This is CodeMirror (https://codemirror.net), a code editor +// implemented in JavaScript on top of the browser's DOM. +// +// You can find some technical background for some of the code below +// at http://marijnhaverbeke.nl/blog/#cm-internals . + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = global || self, global.CodeMirror = factory()); +}(this, (function () { 'use strict'; + + // Kludges for bugs and behavior differences that can't be feature + // detected are enabled based on userAgent etc sniffing. + var userAgent = navigator.userAgent; + var platform = navigator.platform; + + var gecko = /gecko\/\d/i.test(userAgent); + var ie_upto10 = /MSIE \d/.test(userAgent); + var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); + var edge = /Edge\/(\d+)/.exec(userAgent); + var ie = ie_upto10 || ie_11up || edge; + var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); + var webkit = !edge && /WebKit\//.test(userAgent); + var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); + var chrome = !edge && /Chrome\//.test(userAgent); + var presto = /Opera\//.test(userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); + var phantom = /PhantomJS/.test(userAgent); + + var ios = safari && (/Mobile\/\w+/.test(userAgent) || navigator.maxTouchPoints > 2); + var android = /Android/.test(userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); + var mac = ios || /Mac/.test(platform); + var chromeOS = /\bCrOS\b/.test(userAgent); + var windows = /win/i.test(platform); + + var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); + if (presto_version) { presto_version = Number(presto_version[1]); } + if (presto_version && presto_version >= 15) { presto = false; webkit = true; } + // Some browsers use the wrong event properties to signal cmd/ctrl on OS X + var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); + var captureRightClick = gecko || (ie && ie_version >= 9); + + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + + var rmClass = function(node, cls) { + var current = node.className; + var match = classTest(cls).exec(current); + if (match) { + var after = current.slice(match.index + match[0].length); + node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + } + }; + + function removeChildren(e) { + for (var count = e.childNodes.length; count > 0; --count) + { e.removeChild(e.firstChild); } + return e + } + + function removeChildrenAndAdd(parent, e) { + return removeChildren(parent).appendChild(e) + } + + function elt(tag, content, className, style) { + var e = document.createElement(tag); + if (className) { e.className = className; } + if (style) { e.style.cssText = style; } + if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } + else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } + return e + } + // wrapper for elt, which removes the elt from the accessibility tree + function eltP(tag, content, className, style) { + var e = elt(tag, content, className, style); + e.setAttribute("role", "presentation"); + return e + } + + var range; + if (document.createRange) { range = function(node, start, end, endNode) { + var r = document.createRange(); + r.setEnd(endNode || node, end); + r.setStart(node, start); + return r + }; } + else { range = function(node, start, end) { + var r = document.body.createTextRange(); + try { r.moveToElementText(node.parentNode); } + catch(e) { return r } + r.collapse(true); + r.moveEnd("character", end); + r.moveStart("character", start); + return r + }; } + + function contains(parent, child) { + if (child.nodeType == 3) // Android browser always returns false when child is a textnode + { child = child.parentNode; } + if (parent.contains) + { return parent.contains(child) } + do { + if (child.nodeType == 11) { child = child.host; } + if (child == parent) { return true } + } while (child = child.parentNode) + } + + function activeElt() { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + var activeElement; + try { + activeElement = document.activeElement; + } catch(e) { + activeElement = document.body || null; + } + while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) + { activeElement = activeElement.shadowRoot.activeElement; } + return activeElement + } + + function addClass(node, cls) { + var current = node.className; + if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } + } + function joinClasses(a, b) { + var as = a.split(" "); + for (var i = 0; i < as.length; i++) + { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } + return b + } + + var selectInput = function(node) { node.select(); }; + if (ios) // Mobile Safari apparently has a bug where select() is broken. + { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } + else if (ie) // Suppress mysterious IE10 errors + { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args)} + } + + function copyObj(obj, target, overwrite) { + if (!target) { target = {}; } + for (var prop in obj) + { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) + { target[prop] = obj[prop]; } } + return target + } + + // Counts the column offset in a string, taking tabs into account. + // Used mostly to find indentation. + function countColumn(string, end, tabSize, startIndex, startValue) { + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) { end = string.length; } + } + for (var i = startIndex || 0, n = startValue || 0;;) { + var nextTab = string.indexOf("\t", i); + if (nextTab < 0 || nextTab >= end) + { return n + (end - i) } + n += nextTab - i; + n += tabSize - (n % tabSize); + i = nextTab + 1; + } + } + + var Delayed = function() { + this.id = null; + this.f = null; + this.time = 0; + this.handler = bind(this.onTimeout, this); + }; + Delayed.prototype.onTimeout = function (self) { + self.id = 0; + if (self.time <= +new Date) { + self.f(); + } else { + setTimeout(self.handler, self.time - +new Date); + } + }; + Delayed.prototype.set = function (ms, f) { + this.f = f; + var time = +new Date + ms; + if (!this.id || time < this.time) { + clearTimeout(this.id); + this.id = setTimeout(this.handler, ms); + this.time = time; + } + }; + + function indexOf(array, elt) { + for (var i = 0; i < array.length; ++i) + { if (array[i] == elt) { return i } } + return -1 + } + + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerGap = 50; + + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = {toString: function(){return "CodeMirror.Pass"}}; + + // Reused option objects for setSelection & friends + var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; + + // The inverse of countColumn -- find the offset that corresponds to + // a particular column. + function findColumn(string, goal, tabSize) { + for (var pos = 0, col = 0;;) { + var nextTab = string.indexOf("\t", pos); + if (nextTab == -1) { nextTab = string.length; } + var skipped = nextTab - pos; + if (nextTab == string.length || col + skipped >= goal) + { return pos + Math.min(skipped, goal - col) } + col += nextTab - pos; + col += tabSize - (col % tabSize); + pos = nextTab + 1; + if (col >= goal) { return pos } + } + } + + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + { spaceStrs.push(lst(spaceStrs) + " "); } + return spaceStrs[n] + } + + function lst(arr) { return arr[arr.length-1] } + + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } + return out + } + + function insertSorted(array, value, score) { + var pos = 0, priority = score(value); + while (pos < array.length && score(array[pos]) <= priority) { pos++; } + array.splice(pos, 0, value); + } + + function nothing() {} + + function createObj(base, props) { + var inst; + if (Object.create) { + inst = Object.create(base); + } else { + nothing.prototype = base; + inst = new nothing(); + } + if (props) { copyObj(props, inst); } + return inst + } + + var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + function isWordCharBasic(ch) { + return /\w/.test(ch) || ch > "\x80" && + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) + } + function isWordChar(ch, helper) { + if (!helper) { return isWordCharBasic(ch) } + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } + return helper.test(ch) + } + + function isEmpty(obj) { + for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } + return true + } + + // Extending unicode characters. A series of a non-extending char + + // any number of extending chars is treated as a single unit as far + // as editing and measuring is concerned. This is not fully correct, + // since some scripts/fonts/browsers also treat other configurations + // of code points as a group. + var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; + function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + + // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. + function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } + return pos + } + + // Returns the value from the range [`from`; `to`] that satisfies + // `pred` and is closest to `from`. Assumes that at least `to` + // satisfies `pred`. Supports `from` being greater than `to`. + function findFirst(pred, from, to) { + // At any point we are certain `to` satisfies `pred`, don't know + // whether `from` does. + var dir = from > to ? -1 : 1; + for (;;) { + if (from == to) { return from } + var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF); + if (mid == from) { return pred(mid) ? from : to } + if (pred(mid)) { to = mid; } + else { from = mid + dir; } + } + } + + // BIDI HELPERS + + function iterateBidiSections(order, from, to, f) { + if (!order) { return f(from, to, "ltr", 0) } + var found = false; + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from || from == to && part.to == from) { + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i); + found = true; + } + } + if (!found) { f(from, to, "ltr"); } + } + + var bidiOther = null; + function getBidiPartAt(order, ch, sticky) { + var found; + bidiOther = null; + for (var i = 0; i < order.length; ++i) { + var cur = order[i]; + if (cur.from < ch && cur.to > ch) { return i } + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") { found = i; } + else { bidiOther = i; } + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") { found = i; } + else { bidiOther = i; } + } + } + return found != null ? found : bidiOther + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + // Character types for codepoints 0x600 to 0x6f9 + var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; + function charType(code) { + if (code <= 0xf7) { return lowTypes.charAt(code) } + else if (0x590 <= code && code <= 0x5f4) { return "R" } + else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } + else if (0x6ee <= code && code <= 0x8ac) { return "r" } + else if (0x2000 <= code && code <= 0x200b) { return "w" } + else if (code == 0x200c) { return "b" } + else { return "L" } + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + function BidiSpan(level, from, to) { + this.level = level; + this.from = from; this.to = to; + } + + return function(str, direction) { + var outerType = direction == "ltr" ? "L" : "R"; + + if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } + var len = str.length, types = []; + for (var i = 0; i < len; ++i) + { types.push(charType(str.charCodeAt(i))); } + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { + var type = types[i$1]; + if (type == "m") { types[i$1] = prev; } + else { prev = type; } + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { + var type$1 = types[i$2]; + if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } + else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { + var type$2 = types[i$3]; + if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } + else if (type$2 == "," && prev$1 == types[i$3+1] && + (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } + prev$1 = type$2; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i$4 = 0; i$4 < len; ++i$4) { + var type$3 = types[i$4]; + if (type$3 == ",") { types[i$4] = "N"; } + else if (type$3 == "%") { + var end = (void 0); + for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; + for (var j = i$4; j < end; ++j) { types[j] = replace; } + i$4 = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { + var type$4 = types[i$5]; + if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } + else if (isStrong.test(type$4)) { cur$1 = type$4; } + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i$6 = 0; i$6 < len; ++i$6) { + if (isNeutral.test(types[i$6])) { + var end$1 = (void 0); + for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} + var before = (i$6 ? types[i$6-1] : outerType) == "L"; + var after = (end$1 < len ? types[end$1] : outerType) == "L"; + var replace$1 = before == after ? (before ? "L" : "R") : outerType; + for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } + i$6 = end$1 - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = [], m; + for (var i$7 = 0; i$7 < len;) { + if (countsAsLeft.test(types[i$7])) { + var start = i$7; + for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} + order.push(new BidiSpan(0, start, i$7)); + } else { + var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0; + for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} + for (var j$2 = pos; j$2 < i$7;) { + if (countsAsNum.test(types[j$2])) { + if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; } + var nstart = j$2; + for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} + order.splice(at, 0, new BidiSpan(2, nstart, j$2)); + at += isRTL; + pos = j$2; + } else { ++j$2; } + } + if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } + } + } + if (direction == "ltr") { + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift(new BidiSpan(0, 0, m[0].length)); + } + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; + order.push(new BidiSpan(0, len - m[0].length, len)); + } + } + + return direction == "rtl" ? order.reverse() : order + } + })(); + + // Get the bidi ordering for the given line (and cache it). Returns + // false for lines that are fully left-to-right, and an array of + // BidiSpan objects otherwise. + function getOrder(line, direction) { + var order = line.order; + if (order == null) { order = line.order = bidiOrdering(line.text, direction); } + return order + } + + // EVENT HANDLING + + // Lightweight event framework. on/off also work on DOM nodes, + // registering native DOM handlers. + + var noHandlers = []; + + var on = function(emitter, type, f) { + if (emitter.addEventListener) { + emitter.addEventListener(type, f, false); + } else if (emitter.attachEvent) { + emitter.attachEvent("on" + type, f); + } else { + var map = emitter._handlers || (emitter._handlers = {}); + map[type] = (map[type] || noHandlers).concat(f); + } + }; + + function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers + } + + function off(emitter, type, f) { + if (emitter.removeEventListener) { + emitter.removeEventListener(type, f, false); + } else if (emitter.detachEvent) { + emitter.detachEvent("on" + type, f); + } else { + var map = emitter._handlers, arr = map && map[type]; + if (arr) { + var index = indexOf(arr, f); + if (index > -1) + { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } + } + } + } + + function signal(emitter, type /*, values...*/) { + var handlers = getHandlers(emitter, type); + if (!handlers.length) { return } + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } + } + + // The DOM events that CodeMirror handles can be overridden by + // registering a (non-DOM) handler on the editor for the event name, + // and preventDefault-ing the event in that handler. + function signalDOMEvent(cm, e, override) { + if (typeof e == "string") + { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } + signal(cm, override || e.type, cm, e); + return e_defaultPrevented(e) || e.codemirrorIgnore + } + + function signalCursorActivity(cm) { + var arr = cm._handlers && cm._handlers.cursorActivity; + if (!arr) { return } + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) + { set.push(arr[i]); } } + } + + function hasHandler(emitter, type) { + return getHandlers(emitter, type).length > 0 + } + + // Add on and off methods to a constructor's prototype, to make + // registering events on such objects more convenient. + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + + // Due to the fact that we still support jurassic IE versions, some + // compatibility wrappers are needed. + + function e_preventDefault(e) { + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } + } + function e_stopPropagation(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + } + function e_defaultPrevented(e) { + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false + } + function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} + + function e_target(e) {return e.target || e.srcElement} + function e_button(e) { + var b = e.which; + if (b == null) { + if (e.button & 1) { b = 1; } + else if (e.button & 2) { b = 3; } + else if (e.button & 4) { b = 2; } + } + if (mac && e.ctrlKey && b == 1) { b = 3; } + return b + } + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie && ie_version < 9) { return false } + var div = elt('div'); + return "draggable" in div || "dragDrop" in div + }(); + + var zwspSupported; + function zeroWidthElement(measure) { + if (zwspSupported == null) { + var test = elt("span", "\u200b"); + removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + if (measure.firstChild.offsetHeight != 0) + { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } + } + var node = zwspSupported ? elt("span", "\u200b") : + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + node.setAttribute("cm-text", ""); + return node + } + + // Feature-detect IE's crummy client rect reporting for bidi text + var badBidiRects; + function hasBadBidiRects(measure) { + if (badBidiRects != null) { return badBidiRects } + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); + var r0 = range(txt, 0, 1).getBoundingClientRect(); + var r1 = range(txt, 1, 2).getBoundingClientRect(); + removeChildren(measure); + if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) + } + + // See if "".split is the broken IE version, if so, provide an + // alternative way to split lines. + var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { + var pos = 0, result = [], l = string.length; + while (pos <= l) { + var nl = string.indexOf("\n", pos); + if (nl == -1) { nl = string.length; } + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); + var rt = line.indexOf("\r"); + if (rt != -1) { + result.push(line.slice(0, rt)); + pos += rt + 1; + } else { + result.push(line); + pos = nl + 1; + } + } + return result + } : function (string) { return string.split(/\r\n?|\n/); }; + + var hasSelection = window.getSelection ? function (te) { + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } + } : function (te) { + var range; + try {range = te.ownerDocument.selection.createRange();} + catch(e) {} + if (!range || range.parentElement() != te) { return false } + return range.compareEndPoints("StartToEnd", range) != 0 + }; + + var hasCopyEvent = (function () { + var e = elt("div"); + if ("oncopy" in e) { return true } + e.setAttribute("oncopy", "return;"); + return typeof e.oncopy == "function" + })(); + + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) { return badZoomedRects } + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 + } + + // Known modes, by name and by MIME + var modes = {}, mimeModes = {}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + function defineMode(name, mode) { + if (arguments.length > 2) + { mode.dependencies = Array.prototype.slice.call(arguments, 2); } + modes[name] = mode; + } + + function defineMIME(mime, spec) { + mimeModes[mime] = spec; + } + + // Given a MIME type, a {name, ...options} config object, or a name + // string, return a mode config object. + function resolveMode(spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { + spec = mimeModes[spec]; + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + if (typeof found == "string") { found = {name: found}; } + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { + return resolveMode("application/xml") + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return resolveMode("application/json") + } + if (typeof spec == "string") { return {name: spec} } + else { return spec || {name: "null"} } + } + + // Given a mode spec (anything that resolveMode accepts), find and + // initialize an actual mode object. + function getMode(options, spec) { + spec = resolveMode(spec); + var mfactory = modes[spec.name]; + if (!mfactory) { return getMode(options, "text/plain") } + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) { + if (!exts.hasOwnProperty(prop)) { continue } + if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } + modeObj[prop] = exts[prop]; + } + } + modeObj.name = spec.name; + if (spec.helperType) { modeObj.helperType = spec.helperType; } + if (spec.modeProps) { for (var prop$1 in spec.modeProps) + { modeObj[prop$1] = spec.modeProps[prop$1]; } } + + return modeObj + } + + // This can be used to attach properties to mode objects from + // outside the actual mode definition. + var modeExtensions = {}; + function extendMode(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + copyObj(properties, exts); + } + + function copyState(mode, state) { + if (state === true) { return state } + if (mode.copyState) { return mode.copyState(state) } + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) { val = val.concat([]); } + nstate[n] = val; + } + return nstate + } + + // Given a mode and a state (for that mode), find the inner mode and + // state at the position that the state refers to. + function innerMode(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) { break } + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state} + } + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true + } + + // STRING STREAM + + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. + + var StringStream = function(string, tabSize, lineOracle) { + this.pos = this.start = 0; + this.string = string; + this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; + this.lineStart = 0; + this.lineOracle = lineOracle; + }; + + StringStream.prototype.eol = function () {return this.pos >= this.string.length}; + StringStream.prototype.sol = function () {return this.pos == this.lineStart}; + StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; + StringStream.prototype.next = function () { + if (this.pos < this.string.length) + { return this.string.charAt(this.pos++) } + }; + StringStream.prototype.eat = function (match) { + var ch = this.string.charAt(this.pos); + var ok; + if (typeof match == "string") { ok = ch == match; } + else { ok = ch && (match.test ? match.test(ch) : match(ch)); } + if (ok) {++this.pos; return ch} + }; + StringStream.prototype.eatWhile = function (match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start + }; + StringStream.prototype.eatSpace = function () { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; } + return this.pos > start + }; + StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; + StringStream.prototype.skipTo = function (ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true} + }; + StringStream.prototype.backUp = function (n) {this.pos -= n;}; + StringStream.prototype.column = function () { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.indentation = function () { + return countColumn(this.string, null, this.tabSize) - + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) + }; + StringStream.prototype.match = function (pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { + if (consume !== false) { this.pos += pattern.length; } + return true + } + } else { + var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) { return null } + if (match && consume !== false) { this.pos += match[0].length; } + return match + } + }; + StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; + StringStream.prototype.hideFirstChars = function (n, inner) { + this.lineStart += n; + try { return inner() } + finally { this.lineStart -= n; } + }; + StringStream.prototype.lookAhead = function (n) { + var oracle = this.lineOracle; + return oracle && oracle.lookAhead(n) + }; + StringStream.prototype.baseToken = function () { + var oracle = this.lineOracle; + return oracle && oracle.baseToken(this.pos) + }; + + // Find the line object corresponding to the given line number. + function getLine(doc, n) { + n -= doc.first; + if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } + var chunk = doc; + while (!chunk.lines) { + for (var i = 0;; ++i) { + var child = chunk.children[i], sz = child.chunkSize(); + if (n < sz) { chunk = child; break } + n -= sz; + } + } + return chunk.lines[n] + } + + // Get the part of a document between two positions, as an array of + // strings. + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function (line) { + var text = line.text; + if (n == end.line) { text = text.slice(0, end.ch); } + if (n == start.line) { text = text.slice(start.ch); } + out.push(text); + ++n; + }); + return out + } + // Get the lines between from and to, as array of strings. + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value + return out + } + + // Update the height of a line, propagating the height change + // upwards to parent nodes. + function updateLineHeight(line, height) { + var diff = height - line.height; + if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } + } + + // Given a line object, find its line number by walking up through + // its parent links. + function lineNo(line) { + if (line.parent == null) { return null } + var cur = line.parent, no = indexOf(cur.lines, line); + for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (var i = 0;; ++i) { + if (chunk.children[i] == cur) { break } + no += chunk.children[i].chunkSize(); + } + } + return no + cur.first + } + + // Find the line at the given vertical position, using the height + // information in the document tree. + function lineAtHeight(chunk, h) { + var n = chunk.first; + outer: do { + for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { + var child = chunk.children[i$1], ch = child.height; + if (h < ch) { chunk = child; continue outer } + h -= ch; + n += child.chunkSize(); + } + return n + } while (!chunk.lines) + var i = 0; + for (; i < chunk.lines.length; ++i) { + var line = chunk.lines[i], lh = line.height; + if (h < lh) { break } + h -= lh; + } + return n + i + } + + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)) + } + + // A Pos instance represents a position within the text. + function Pos(line, ch, sticky) { + if ( sticky === void 0 ) sticky = null; + + if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } + this.line = line; + this.ch = ch; + this.sticky = sticky; + } + + // Compare two positions, return 0 if they are the same, a negative + // number when a is less, and a positive number otherwise. + function cmp(a, b) { return a.line - b.line || a.ch - b.ch } + + function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + + function copyPos(x) {return Pos(x.line, x.ch)} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b } + + // Most of the external API clips given positions to make sure they + // actually exist within the document. + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} + function clipPos(doc, pos) { + if (pos.line < doc.first) { return Pos(doc.first, 0) } + var last = doc.first + doc.size - 1; + if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } + return clipToLen(pos, getLine(doc, pos.line).text.length) + } + function clipToLen(pos, linelen) { + var ch = pos.ch; + if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } + else if (ch < 0) { return Pos(pos.line, 0) } + else { return pos } + } + function clipPosArray(doc, array) { + var out = []; + for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } + return out + } + + var SavedContext = function(state, lookAhead) { + this.state = state; + this.lookAhead = lookAhead; + }; + + var Context = function(doc, state, line, lookAhead) { + this.state = state; + this.doc = doc; + this.line = line; + this.maxLookAhead = lookAhead || 0; + this.baseTokens = null; + this.baseTokenPos = 1; + }; + + Context.prototype.lookAhead = function (n) { + var line = this.doc.getLine(this.line + n); + if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } + return line + }; + + Context.prototype.baseToken = function (n) { + if (!this.baseTokens) { return null } + while (this.baseTokens[this.baseTokenPos] <= n) + { this.baseTokenPos += 2; } + var type = this.baseTokens[this.baseTokenPos + 1]; + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + }; + + Context.prototype.nextLine = function () { + this.line++; + if (this.maxLookAhead > 0) { this.maxLookAhead--; } + }; + + Context.fromSaved = function (doc, saved, line) { + if (saved instanceof SavedContext) + { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } + else + { return new Context(doc, copyState(doc.mode, saved), line) } + }; + + Context.prototype.save = function (copy) { + var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; + return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state + }; + + + // Compute a style array (an array starting with a mode generation + // -- for invalidation -- followed by pairs of end positions and + // style strings), which is used to highlight the tokens on the + // line. + function highlightLine(cm, line, context, forceToEnd) { + // A styles array always starts with a number identifying the + // mode/overlays that it is based on (for easy invalidation). + var st = [cm.state.modeGen], lineClasses = {}; + // Compute the base array of styles + runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, + lineClasses, forceToEnd); + var state = context.state; + + // Run overlays, adjust style array. + var loop = function ( o ) { + context.baseTokens = st; + var overlay = cm.state.overlays[o], i = 1, at = 0; + context.state = true; + runMode(cm, line.text, overlay.mode, context, function (end, style) { + var start = i; + // Ensure there's a token end at the current position, and that i points at it + while (at < end) { + var i_end = st[i]; + if (i_end > end) + { st.splice(i, 1, end, st[i+1], i_end); } + i += 2; + at = Math.min(end, i_end); + } + if (!style) { return } + if (overlay.opaque) { + st.splice(start, i - start, end, "overlay " + style); + i = start + 2; + } else { + for (; start < i; start += 2) { + var cur = st[start+1]; + st[start+1] = (cur ? cur + " " : "") + "overlay " + style; + } + } + }, lineClasses); + context.state = state; + context.baseTokens = null; + context.baseTokenPos = 1; + }; + + for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); + + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} + } + + function getLineStyles(cm, line, updateFrontier) { + if (!line.styles || line.styles[0] != cm.state.modeGen) { + var context = getContextBefore(cm, lineNo(line)); + var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); + var result = highlightLine(cm, line, context); + if (resetState) { context.state = resetState; } + line.stateAfter = context.save(!resetState); + line.styles = result.styles; + if (result.classes) { line.styleClasses = result.classes; } + else if (line.styleClasses) { line.styleClasses = null; } + if (updateFrontier === cm.doc.highlightFrontier) + { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } + } + return line.styles + } + + function getContextBefore(cm, n, precise) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) { return new Context(doc, true, n) } + var start = findStartLine(cm, n, precise); + var saved = start > doc.first && getLine(doc, start - 1).stateAfter; + var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); + + doc.iter(start, n, function (line) { + processLine(cm, line.text, context); + var pos = context.line; + line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; + context.nextLine(); + }); + if (precise) { doc.modeFrontier = context.line; } + return context + } + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. Used for lines that + // aren't currently visible. + function processLine(cm, text, context, startAt) { + var mode = cm.doc.mode; + var stream = new StringStream(text, cm.options.tabSize, context); + stream.start = stream.pos = startAt || 0; + if (text == "") { callBlankLine(mode, context.state); } + while (!stream.eol()) { + readToken(mode, stream, context.state); + stream.start = stream.pos; + } + } + + function callBlankLine(mode, state) { + if (mode.blankLine) { return mode.blankLine(state) } + if (!mode.innerMode) { return } + var inner = innerMode(mode, state); + if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } + } + + function readToken(mode, stream, state, inner) { + for (var i = 0; i < 10; i++) { + if (inner) { inner[0] = innerMode(mode, state).mode; } + var style = mode.token(stream, state); + if (stream.pos > stream.start) { return style } + } + throw new Error("Mode " + mode.name + " failed to advance stream.") + } + + var Token = function(stream, type, state) { + this.start = stream.start; this.end = stream.pos; + this.string = stream.current(); + this.type = type || null; + this.state = state; + }; + + // Utility for getTokenAt and getLineTokens + function takeToken(cm, pos, precise, asArray) { + var doc = cm.doc, mode = doc.mode, style; + pos = clipPos(doc, pos); + var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); + var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; + if (asArray) { tokens = []; } + while ((asArray || stream.pos < pos.ch) && !stream.eol()) { + stream.start = stream.pos; + style = readToken(mode, stream, context.state); + if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } + } + return asArray ? tokens : new Token(stream, style, context.state) + } + + function extractLineClasses(type, output) { + if (type) { for (;;) { + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); + if (!lineClass) { break } + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (output[prop] == null) + { output[prop] = lineClass[2]; } + else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop])) + { output[prop] += " " + lineClass[2]; } + } } + return type + } + + // Run the given mode's parser over a line, calling f for each token. + function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { + var flattenSpans = mode.flattenSpans; + if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } + var curStart = 0, curStyle = null; + var stream = new StringStream(text, cm.options.tabSize, context), style; + var inner = cm.options.addModeClass && [null]; + if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } + while (!stream.eol()) { + if (stream.pos > cm.options.maxHighlightLength) { + flattenSpans = false; + if (forceToEnd) { processLine(cm, text, context, stream.pos); } + stream.pos = text.length; + style = null; + } else { + style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); + } + if (inner) { + var mName = inner[0].name; + if (mName) { style = "m-" + (style ? mName + " " + style : mName); } + } + if (!flattenSpans || curStyle != style) { + while (curStart < stream.start) { + curStart = Math.min(stream.start, curStart + 5000); + f(curStart, curStyle); + } + curStyle = style; + } + stream.start = stream.pos; + } + while (curStart < stream.pos) { + // Webkit seems to refuse to render text nodes longer than 57444 + // characters, and returns inaccurate measurements in nodes + // starting around 5000 chars. + var pos = Math.min(stream.pos, curStart + 5000); + f(pos, curStyle); + curStart = pos; + } + } + + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(cm, n, precise) { + var minindent, minline, doc = cm.doc; + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + for (var search = n; search > lim; --search) { + if (search <= doc.first) { return doc.first } + var line = getLine(doc, search - 1), after = line.stateAfter; + if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) + { return search } + var indented = countColumn(line.text, null, cm.options.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline + } + + function retreatFrontier(doc, n) { + doc.modeFrontier = Math.min(doc.modeFrontier, n); + if (doc.highlightFrontier < n - 10) { return } + var start = doc.first; + for (var line = n - 1; line > start; line--) { + var saved = getLine(doc, line).stateAfter; + // change is on 3 + // state on line 1 looked ahead 2 -- so saw 3 + // test 1 + 2 < 3 should cover this + if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { + start = line + 1; + break + } + } + doc.highlightFrontier = Math.min(doc.highlightFrontier, start); + } + + // Optimize some code when these features are not used. + var sawReadOnlySpans = false, sawCollapsedSpans = false; + + function seeReadOnlySpans() { + sawReadOnlySpans = true; + } + + function seeCollapsedSpans() { + sawCollapsedSpans = true; + } + + // TEXTMARKER SPANS + + function MarkedSpan(marker, from, to) { + this.marker = marker; + this.from = from; this.to = to; + } + + // Search an array of spans for a span matching the given marker. + function getMarkedSpanFor(spans, marker) { + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { return span } + } } + } + + // Remove a span from an array, returning undefined if no spans are + // left (we don't store arrays for lines without spans). + function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } + return r + } + + // Add a span to a line. + function addMarkedSpan(line, span, op) { + var inThisOp = op && window.WeakSet && (op.markedSpans || (op.markedSpans = new WeakSet)); + if (inThisOp && inThisOp.has(line.markedSpans)) { + line.markedSpans.push(span); + } else { + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; + if (inThisOp) { inThisOp.add(line.markedSpans); } + } + span.marker.attachLine(line); + } + + // Used for the algorithm that adjusts markers for a change in the + // document. These functions cut an array of spans at a given + // character position, returning an array of remaining chunks (or + // undefined if nothing remains). + function markedSpansBefore(old, startCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + } + } } + return nw + } + function markedSpansAfter(old, endCh, isInsert) { + var nw; + if (old) { for (var i = 0; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)); + } + } } + return nw + } + + // Given a change object, compute the new set of marker spans that + // cover the line in which the change took place. Removes spans + // entirely within the change, reconnects spans belonging to the + // same marker that appear on both sides of the change, and cuts off + // spans partially within the change. Returns an array of span + // arrays with one element for each line in (after) the change. + function stretchSpansOverChange(doc, change) { + if (change.full) { return null } + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) { return null } + + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh, isInsert); + var last = markedSpansAfter(oldLast, endCh, isInsert); + + // Next, merge those two ends + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) { span.to = startCh; } + else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } + } + } + } + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i$1 = 0; i$1 < last.length; ++i$1) { + var span$1 = last[i$1]; + if (span$1.to != null) { span$1.to += offset; } + if (span$1.from == null) { + var found$1 = getMarkedSpanFor(first, span$1.marker); + if (!found$1) { + span$1.from = offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } else { + span$1.from += offset; + if (sameLine) { (first || (first = [])).push(span$1); } + } + } + } + // Make sure we didn't create any zero-length spans + if (first) { first = clearEmptySpans(first); } + if (last && last != first) { last = clearEmptySpans(last); } + + var newMarkers = [first]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = change.text.length - 2, gapMarkers; + if (gap > 0 && first) + { for (var i$2 = 0; i$2 < first.length; ++i$2) + { if (first[i$2].to == null) + { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } + for (var i$3 = 0; i$3 < gap; ++i$3) + { newMarkers.push(gapMarkers); } + newMarkers.push(last); + } + return newMarkers + } + + // Remove spans that are empty and don't have a clearWhenEmpty + // option of false. + function clearEmptySpans(spans) { + for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) + { spans.splice(i--, 1); } + } + if (!spans.length) { return null } + return spans + } + + // Used to 'clip' out readOnly ranges when making a change. + function removeReadOnlyRanges(doc, from, to) { + var markers = null; + doc.iter(from.line, to.line + 1, function (line) { + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var mark = line.markedSpans[i].marker; + if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) + { (markers || (markers = [])).push(mark); } + } } + }); + if (!markers) { return null } + var parts = [{from: from, to: to}]; + for (var i = 0; i < markers.length; ++i) { + var mk = markers[i], m = mk.find(0); + for (var j = 0; j < parts.length; ++j) { + var p = parts[j]; + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) + { newParts.push({from: p.from, to: m.from}); } + if (dto > 0 || !mk.inclusiveRight && !dto) + { newParts.push({from: m.to, to: p.to}); } + parts.splice.apply(parts, newParts); + j += newParts.length - 3; + } + } + return parts + } + + // Connect or disconnect spans from a line. + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.detachLine(line); } + line.markedSpans = null; + } + function attachMarkedSpans(line, spans) { + if (!spans) { return } + for (var i = 0; i < spans.length; ++i) + { spans[i].marker.attachLine(line); } + line.markedSpans = spans; + } + + // Helpers used when computing which overlapping collapsed span + // counts as the larger one. + function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } + function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } + + // Returns a number indicating which of two overlapping collapsed + // spans is larger (and thus includes the other). Falls back to + // comparing ids when the spans cover exactly the same range. + function compareCollapsedMarkers(a, b) { + var lenDiff = a.lines.length - b.lines.length; + if (lenDiff != 0) { return lenDiff } + var aPos = a.find(), bPos = b.find(); + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); + if (fromCmp) { return -fromCmp } + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); + if (toCmp) { return toCmp } + return b.id - a.id + } + + // Find out whether a line ends or starts in a collapsed span. If + // so, return the marker for that span. + function collapsedSpanAtSide(line, start) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) + { found = sp.marker; } + } } + return found + } + function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } + function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } + + function collapsedSpanAround(line, ch) { + var sps = sawCollapsedSpans && line.markedSpans, found; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) && + (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } + } } + return found + } + + // Test whether there exists a collapsed span that partially + // overlaps (covers the start or end, but not both) of a new span. + // Such overlap is not allowed. + function conflictingCollapsedRange(doc, lineNo, from, to, marker) { + var line = getLine(doc, lineNo); + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) { + var sp = sps[i]; + if (!sp.marker.collapsed) { continue } + var found = sp.marker.find(0); + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) + { return true } + } } + } + + // A visual line is a line as drawn on the screen. Folding, for + // example, can cause multiple logical lines to appear on the same + // visual line. This finds the start of the visual line that the + // given line is part of (usually that is the line itself). + function visualLine(line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + { line = merged.find(-1, true).line; } + return line + } + + function visualLineEnd(line) { + var merged; + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return line + } + + // Returns an array of logical lines that continue the visual line + // started by the argument, or undefined if there are no such lines. + function visualLineContinued(line) { + var merged, lines; + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line); + } + return lines + } + + // Get the line number of the start of the visual line that the + // given line number is part of. + function visualLineNo(doc, lineN) { + var line = getLine(doc, lineN), vis = visualLine(line); + if (line == vis) { return lineN } + return lineNo(vis) + } + + // Get the line number of the start of the next visual line after + // the given line. + function visualLineEndNo(doc, lineN) { + if (lineN > doc.lastLine()) { return lineN } + var line = getLine(doc, lineN), merged; + if (!lineIsHidden(doc, line)) { return lineN } + while (merged = collapsedSpanAtEnd(line)) + { line = merged.find(1, true).line; } + return lineNo(line) + 1 + } + + // Compute whether a line is hidden. Lines count as hidden when they + // are part of a visual line that starts with another line, or when + // they are entirely covered by collapsed, non-widget span. + function lineIsHidden(doc, line) { + var sps = sawCollapsedSpans && line.markedSpans; + if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { + sp = sps[i]; + if (!sp.marker.collapsed) { continue } + if (sp.from == null) { return true } + if (sp.marker.widgetNode) { continue } + if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) + { return true } + } } + } + function lineIsHiddenInner(doc, line, span) { + if (span.to == null) { + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) + } + if (span.marker.inclusiveRight && span.to == line.text.length) + { return true } + for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { + sp = line.markedSpans[i]; + if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && + (sp.to == null || sp.to != span.from) && + (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && + lineIsHiddenInner(doc, line, sp)) { return true } + } + } + + // Find the height above the given line. + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); + + var h = 0, chunk = lineObj.parent; + for (var i = 0; i < chunk.lines.length; ++i) { + var line = chunk.lines[i]; + if (line == lineObj) { break } + else { h += line.height; } + } + for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (var i$1 = 0; i$1 < p.children.length; ++i$1) { + var cur = p.children[i$1]; + if (cur == chunk) { break } + else { h += cur.height; } + } + } + return h + } + + // Compute the character length of a line, taking into account + // collapsed ranges (see markText) that might hide parts, and join + // other lines onto it. + function lineLength(line) { + if (line.height == 0) { return 0 } + var len = line.text.length, merged, cur = line; + while (merged = collapsedSpanAtStart(cur)) { + var found = merged.find(0, true); + cur = found.from.line; + len += found.from.ch - found.to.ch; + } + cur = line; + while (merged = collapsedSpanAtEnd(cur)) { + var found$1 = merged.find(0, true); + len -= cur.text.length - found$1.from.ch; + cur = found$1.to.line; + len += cur.text.length - found$1.to.ch; + } + return len + } + + // Find the longest line in the document. + function findMaxLine(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(d.maxLine); + d.maxLineChanged = true; + doc.iter(function (line) { + var len = lineLength(line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; + } + }); + } + + // LINE DATA STRUCTURE + + // Line objects. These hold state related to a line, including + // highlighting info (the styles array). + var Line = function(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; + }; + + Line.prototype.lineNo = function () { return lineNo(this) }; + eventMixin(Line); + + // Change the content (text, markers) of a line. Automatically + // invalidates cached information and tries to re-estimate the + // line's height. + function updateLine(line, text, markedSpans, estimateHeight) { + line.text = text; + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + if (line.order != null) { line.order = null; } + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + } + + // Detach a line from the document tree and its markers. + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Convert a style as returned by a mode (either null, or a string + // containing one or more styles) to a CSS style. This is cached, + // and also looks for line-wide styles. + var styleToClassCache = {}, styleToClassCacheWithMode = {}; + function interpretTokenStyle(style, options) { + if (!style || /^\s*$/.test(style)) { return null } + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + return cache[style] || + (cache[style] = style.replace(/\S+/g, "cm-$&")) + } + + // Render the DOM representation of the text of a line. Also builds + // up a 'line map', which points at the DOM nodes that represent + // specific stretches of text, and is used by the measuring code. + // The returned object contains the DOM node, this map, and + // information about line-wide styles that were set by the mode. + function buildLineContent(cm, lineView) { + // The padding-right forces the element to have a 'border', which + // is needed on Webkit to be able to get line-level bounding + // rectangles for it (in measureChar). + var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); + var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, + col: 0, pos: 0, cm: cm, + trailingSpace: false, + splitSpaces: cm.getOption("lineWrapping")}; + lineView.measure = {}; + + // Iterate over the logical lines that make up this visual line. + for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); + builder.pos = 0; + builder.addToken = buildToken; + // Optionally wire in some hacks into the token-rendering + // algorithm, to deal with browser quirks. + if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) + { builder.addToken = buildTokenBadBidi(builder.addToken, order); } + builder.map = []; + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + if (line.styleClasses) { + if (line.styleClasses.bgClass) + { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } + if (line.styleClasses.textClass) + { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } + } + + // Ensure at least a single node is present, for measuring. + if (builder.map.length == 0) + { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } + + // Store the map and a cache object for the current logical line + if (i == 0) { + lineView.measure.map = builder.map; + lineView.measure.cache = {}; + } else { + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); + } + } + + // See issue #2901 + if (webkit) { + var last = builder.content.lastChild; + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + { builder.content.className = "cm-tab-wrap-hack"; } + } + + signal(cm, "renderLine", cm, lineView.line, builder.pre); + if (builder.pre.className) + { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } + + return builder + } + + function defaultSpecialCharPlaceholder(ch) { + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + ch.charCodeAt(0).toString(16); + token.setAttribute("aria-label", token.title); + return token + } + + // Build up the DOM representation for a single token, and add it to + // the line map. Takes care to render special characters separately. + function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { + if (!text) { return } + var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; + var special = builder.cm.state.specialChars, mustWrap = false; + var content; + if (!special.test(text)) { + builder.col += text.length; + content = document.createTextNode(displayText); + builder.map.push(builder.pos, builder.pos + text.length, content); + if (ie && ie_version < 9) { mustWrap = true; } + builder.pos += text.length; + } else { + content = document.createDocumentFragment(); + var pos = 0; + while (true) { + special.lastIndex = pos; + var m = special.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } + else { content.appendChild(txt); } + builder.map.push(builder.pos, builder.pos + skipped, txt); + builder.col += skipped; + builder.pos += skipped; + } + if (!m) { break } + pos += skipped + 1; + var txt$1 = (void 0); + if (m[0] == "\t") { + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; + txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + txt$1.setAttribute("role", "presentation"); + txt$1.setAttribute("cm-text", "\t"); + builder.col += tabWidth; + } else if (m[0] == "\r" || m[0] == "\n") { + txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); + txt$1.setAttribute("cm-text", m[0]); + builder.col += 1; + } else { + txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); + txt$1.setAttribute("cm-text", m[0]); + if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } + else { content.appendChild(txt$1); } + builder.col += 1; + } + builder.map.push(builder.pos, builder.pos + 1, txt$1); + builder.pos++; + } + } + builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; + if (style || startStyle || endStyle || mustWrap || css || attributes) { + var fullStyle = style || ""; + if (startStyle) { fullStyle += startStyle; } + if (endStyle) { fullStyle += endStyle; } + var token = elt("span", [content], fullStyle, css); + if (attributes) { + for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + { token.setAttribute(attr, attributes[attr]); } } + } + return builder.content.appendChild(token) + } + builder.content.appendChild(content); + } + + // Change some spaces to NBSP to prevent the browser from collapsing + // trailing spaces at the end of a line when rendering text (issue #1362). + function splitSpaces(text, trailingBefore) { + if (text.length > 1 && !/ /.test(text)) { return text } + var spaceBefore = trailingBefore, result = ""; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) + { ch = "\u00a0"; } + result += ch; + spaceBefore = ch == " "; + } + return result + } + + // Work around nonsense dimensions being reported for stretches of + // right-to-left text. + function buildTokenBadBidi(inner, order) { + return function (builder, text, style, startStyle, endStyle, css, attributes) { + style = style ? style + " cm-force-border" : "cm-force-border"; + var start = builder.pos, end = start + text.length; + for (;;) { + // Find the part that overlaps with the start of this text + var part = (void 0); + for (var i = 0; i < order.length; i++) { + part = order[i]; + if (part.to > start && part.from <= start) { break } + } + if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) } + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes); + startStyle = null; + text = text.slice(part.to - start); + start = part.to; + } + } + } + + function buildCollapsedSpan(builder, size, marker, ignoreWidget) { + var widget = !ignoreWidget && marker.widgetNode; + if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } + if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { + if (!widget) + { widget = builder.content.appendChild(document.createElement("span")); } + widget.setAttribute("cm-marker", marker.id); + } + if (widget) { + builder.cm.display.input.setUneditable(widget); + builder.content.appendChild(widget); + } + builder.pos += size; + builder.trailingSpace = false; + } + + // Outputs a number of spans to make up a line, taking highlighting + // and marked text into account. + function insertLineContent(line, builder, styles) { + var spans = line.markedSpans, allText = line.text, at = 0; + if (!spans) { + for (var i$1 = 1; i$1 < styles.length; i$1+=2) + { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } + return + } + + var len = allText.length, pos = 0, i = 1, text = "", style, css; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes; + for (;;) { + if (nextChange == pos) { // Update current marker set + spanStyle = spanEndStyle = spanStartStyle = css = ""; + attributes = null; + collapsed = null; nextChange = Infinity; + var foundBookmarks = [], endStyles = (void 0); + for (var j = 0; j < spans.length; ++j) { + var sp = spans[j], m = sp.marker; + if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { + foundBookmarks.push(m); + } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { + if (sp.to != null && sp.to != pos && nextChange > sp.to) { + nextChange = sp.to; + spanEndStyle = ""; + } + if (m.className) { spanStyle += " " + m.className; } + if (m.css) { css = (css ? css + ";" : "") + m.css; } + if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } + if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) { (attributes || (attributes = {})).title = m.title; } + if (m.attributes) { + for (var attr in m.attributes) + { (attributes || (attributes = {}))[attr] = m.attributes[attr]; } + } + if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) + { collapsed = sp; } + } else if (sp.from > pos && nextChange > sp.from) { + nextChange = sp.from; + } + } + if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) + { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } + + if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) + { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } + if (collapsed && (collapsed.from || 0) == pos) { + buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, + collapsed.marker, collapsed.from == null); + if (collapsed.to == null) { return } + if (collapsed.to == pos) { collapsed = false; } + } + } + if (pos >= len) { break } + + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + if (!collapsed) { + var tokenText = end > upto ? text.slice(0, upto - pos) : text; + builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes); + } + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end; + spanStartStyle = ""; + } + text = allText.slice(at, at = styles[i++]); + style = interpretTokenStyle(styles[i++], builder.cm.options); + } + } + } + + + // These objects are used to represent the visible (currently drawn) + // part of the document. A LineView may correspond to multiple + // logical lines, if those are connected by collapsed ranges. + function LineView(doc, line, lineN) { + // The starting line + this.line = line; + // Continuing lines, if any + this.rest = visualLineContinued(line); + // Number of logical lines in this visual line + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; + this.node = this.text = null; + this.hidden = lineIsHidden(doc, line); + } + + // Create a range of LineView objects for the given lines. + function buildViewArray(cm, from, to) { + var array = [], nextPos; + for (var pos = from; pos < to; pos = nextPos) { + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); + nextPos = pos + view.size; + array.push(view); + } + return array + } + + var operationGroup = null; + + function pushOperation(op) { + if (operationGroup) { + operationGroup.ops.push(op); + } else { + op.ownsGroup = operationGroup = { + ops: [op], + delayedCallbacks: [] + }; + } + } + + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + { callbacks[i].call(null); } + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } + } + } while (i < callbacks.length) + } + + function finishOperation(op, endCb) { + var group = op.ownsGroup; + if (!group) { return } + + try { fireCallbacksForOps(group); } + finally { + operationGroup = null; + endCb(group); + } + } + + var orphanDelayedCallbacks = null; + + // Often, we want to signal events at a point where we are in the + // middle of some work, but don't want the handler to start calling + // other methods on the editor, which might be in an inconsistent + // state or simply not expect any other events to happen. + // signalLater looks whether there are any handlers, and schedules + // them to be executed when the last operation ends, or, if no + // operation is active, when a timeout fires. + function signalLater(emitter, type /*, values...*/) { + var arr = getHandlers(emitter, type); + if (!arr.length) { return } + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); + } + var loop = function ( i ) { + list.push(function () { return arr[i].apply(null, args); }); + }; + + for (var i = 0; i < arr.length; ++i) + loop( i ); + } + + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; + for (var i = 0; i < delayed.length; ++i) { delayed[i](); } + } + + // When an aspect of a line changes, a string is added to + // lineView.changes. This updates the relevant part of the line's + // DOM structure. + function updateLineForChanges(cm, lineView, lineN, dims) { + for (var j = 0; j < lineView.changes.length; j++) { + var type = lineView.changes[j]; + if (type == "text") { updateLineText(cm, lineView); } + else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } + else if (type == "class") { updateLineClasses(cm, lineView); } + else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } + } + lineView.changes = null; + } + + // Lines with gutter elements, widgets or a background class need to + // be wrapped, and have the extra elements added to the wrapper div + function ensureLineWrapped(lineView) { + if (lineView.node == lineView.text) { + lineView.node = elt("div", null, null, "position: relative"); + if (lineView.text.parentNode) + { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } + lineView.node.appendChild(lineView.text); + if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } + } + return lineView.node + } + + function updateLineBackground(cm, lineView) { + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; + if (cls) { cls += " CodeMirror-linebackground"; } + if (lineView.background) { + if (cls) { lineView.background.className = cls; } + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + } else if (cls) { + var wrap = ensureLineWrapped(lineView); + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + cm.display.input.setUneditable(lineView.background); + } + } + + // Wrapper around buildLineContent which will reuse the structure + // in display.externalMeasured when possible. + function getLineContent(cm, lineView) { + var ext = cm.display.externalMeasured; + if (ext && ext.line == lineView.line) { + cm.display.externalMeasured = null; + lineView.measure = ext.measure; + return ext.built + } + return buildLineContent(cm, lineView) + } + + // Redraw the line's text. Interacts with the background and text + // classes because the mode may output tokens that influence these + // classes. + function updateLineText(cm, lineView) { + var cls = lineView.text.className; + var built = getLineContent(cm, lineView); + if (lineView.text == lineView.node) { lineView.node = built.pre; } + lineView.text.parentNode.replaceChild(built.pre, lineView.text); + lineView.text = built.pre; + if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { + lineView.bgClass = built.bgClass; + lineView.textClass = built.textClass; + updateLineClasses(cm, lineView); + } else if (cls) { + lineView.text.className = cls; + } + } + + function updateLineClasses(cm, lineView) { + updateLineBackground(cm, lineView); + if (lineView.line.wrapClass) + { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } + else if (lineView.node != lineView.text) + { lineView.node.className = ""; } + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; + lineView.text.className = textClass || ""; + } + + function updateLineGutter(cm, lineView, lineN, dims) { + if (lineView.gutter) { + lineView.node.removeChild(lineView.gutter); + lineView.gutter = null; + } + if (lineView.gutterBackground) { + lineView.node.removeChild(lineView.gutterBackground); + lineView.gutterBackground = null; + } + if (lineView.line.gutterClass) { + var wrap = ensureLineWrapped(lineView); + lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, + ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); + cm.display.input.setUneditable(lineView.gutterBackground); + wrap.insertBefore(lineView.gutterBackground, lineView.text); + } + var markers = lineView.line.gutterMarkers; + if (cm.options.lineNumbers || markers) { + var wrap$1 = ensureLineWrapped(lineView); + var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + gutterWrap.setAttribute("aria-hidden", "true"); + cm.display.input.setUneditable(gutterWrap); + wrap$1.insertBefore(gutterWrap, lineView.text); + if (lineView.line.gutterClass) + { gutterWrap.className += " " + lineView.line.gutterClass; } + if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) + { lineView.lineNumber = gutterWrap.appendChild( + elt("div", lineNumberFor(cm.options, lineN), + "CodeMirror-linenumber CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } + if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) { + var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id]; + if (found) + { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } + } } + } + } + + function updateLineWidgets(cm, lineView, dims) { + if (lineView.alignable) { lineView.alignable = null; } + var isWidget = classTest("CodeMirror-linewidget"); + for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { + next = node.nextSibling; + if (isWidget.test(node.className)) { lineView.node.removeChild(node); } + } + insertLineWidgets(cm, lineView, dims); + } + + // Build a line's DOM representation from scratch + function buildLineElement(cm, lineView, lineN, dims) { + var built = getLineContent(cm, lineView); + lineView.text = lineView.node = built.pre; + if (built.bgClass) { lineView.bgClass = built.bgClass; } + if (built.textClass) { lineView.textClass = built.textClass; } + + updateLineClasses(cm, lineView); + updateLineGutter(cm, lineView, lineN, dims); + insertLineWidgets(cm, lineView, dims); + return lineView.node + } + + // A lineView may contain multiple logical lines (when merged by + // collapsed spans). The widgets for all of them need to be drawn. + function insertLineWidgets(cm, lineView, dims) { + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } + } + + function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { + if (!line.widgets) { return } + var wrap = ensureLineWrapped(lineView); + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : "")); + if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } + positionLineWidget(widget, node, lineView, dims); + cm.display.input.setUneditable(node); + if (allowAbove && widget.above) + { wrap.insertBefore(node, lineView.gutter || lineView.text); } + else + { wrap.appendChild(node); } + signalLater(widget, "redraw"); + } + } + + function positionLineWidget(widget, node, lineView, dims) { + if (widget.noHScroll) { + (lineView.alignable || (lineView.alignable = [])).push(node); + var width = dims.wrapperWidth; + node.style.left = dims.fixedPos + "px"; + if (!widget.coverGutter) { + width -= dims.gutterTotalWidth; + node.style.paddingLeft = dims.gutterTotalWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } + } + } + + function widgetHeight(widget) { + if (widget.height != null) { return widget.height } + var cm = widget.doc.cm; + if (!cm) { return 0 } + if (!contains(document.body, widget.node)) { + var parentStyle = "position: relative;"; + if (widget.coverGutter) + { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } + if (widget.noHScroll) + { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + } + return widget.height = widget.node.parentNode.offsetHeight + } + + // Return true when the given mouse event happened in a widget + function eventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + { return true } + } + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop} + function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} + function paddingH(display) { + if (display.cachedPaddingH) { return display.cachedPaddingH } + var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")); + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; + if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } + return data + } + + function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } + function displayWidth(cm) { + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth + } + function displayHeight(cm) { + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight + } + + // Ensure the lineView.wrapping.heights array is populated. This is + // an array of bottom offsets for the lines that make up a drawn + // line. When lineWrapping is on, there might be more than one + // height. + function ensureLineHeights(cm, lineView, rect) { + var wrapping = cm.options.lineWrapping; + var curWidth = wrapping && displayWidth(cm); + if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { + var heights = lineView.measure.heights = []; + if (wrapping) { + lineView.measure.width = curWidth; + var rects = lineView.text.firstChild.getClientRects(); + for (var i = 0; i < rects.length - 1; i++) { + var cur = rects[i], next = rects[i + 1]; + if (Math.abs(cur.bottom - next.bottom) > 2) + { heights.push((cur.bottom + next.top) / 2 - rect.top); } + } + } + heights.push(rect.bottom - rect.top); + } + } + + // Find a line map (mapping character offsets to text nodes) and a + // measurement cache for the given line number. (A line view might + // contain multiple lines when collapsed ranges are present.) + function mapFromLineView(lineView, line, lineN) { + if (lineView.line == line) + { return {map: lineView.measure.map, cache: lineView.measure.cache} } + if (lineView.rest) { + for (var i = 0; i < lineView.rest.length; i++) + { if (lineView.rest[i] == line) + { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } + for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) + { if (lineNo(lineView.rest[i$1]) > lineN) + { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } + } + } + + // Render a line into the hidden node display.externalMeasured. Used + // when measurement is needed for a line that's not in the viewport. + function updateExternalMeasurement(cm, line) { + line = visualLine(line); + var lineN = lineNo(line); + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); + view.lineN = lineN; + var built = view.built = buildLineContent(cm, view); + view.text = built.pre; + removeChildrenAndAdd(cm.display.lineMeasure, built.pre); + return view + } + + // Get a {top, bottom, left, right} box (in line-local coordinates) + // for a given character. + function measureChar(cm, line, ch, bias) { + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) + } + + // Find a line view that corresponds to the given line number. + function findViewForLine(cm, lineN) { + if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) + { return cm.display.view[findViewIndex(cm, lineN)] } + var ext = cm.display.externalMeasured; + if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) + { return ext } + } + + // Measurement can be split in two steps, the set-up work that + // applies to the whole line, and the measurement of the actual + // character. Functions like coordsChar, that need to do a lot of + // measurements in a row, can thus ensure that the set-up work is + // only done once. + function prepareMeasureForLine(cm, line) { + var lineN = lineNo(line); + var view = findViewForLine(cm, lineN); + if (view && !view.text) { + view = null; + } else if (view && view.changes) { + updateLineForChanges(cm, view, lineN, getDimensions(cm)); + cm.curOp.forceUpdate = true; + } + if (!view) + { view = updateExternalMeasurement(cm, line); } + + var info = mapFromLineView(view, line, lineN); + return { + line: line, view: view, rect: null, + map: info.map, cache: info.cache, before: info.before, + hasHeights: false + } + } + + // Given a prepared measurement object, measures the position of an + // actual character (or fetches it from the cache). + function measureCharPrepared(cm, prepared, ch, bias, varHeight) { + if (prepared.before) { ch = -1; } + var key = ch + (bias || ""), found; + if (prepared.cache.hasOwnProperty(key)) { + found = prepared.cache[key]; + } else { + if (!prepared.rect) + { prepared.rect = prepared.view.text.getBoundingClientRect(); } + if (!prepared.hasHeights) { + ensureLineHeights(cm, prepared.view, prepared.rect); + prepared.hasHeights = true; + } + found = measureCharInner(cm, prepared, ch, bias); + if (!found.bogus) { prepared.cache[key] = found; } + } + return {left: found.left, right: found.right, + top: varHeight ? found.rtop : found.top, + bottom: varHeight ? found.rbottom : found.bottom} + } + + var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; + + function nodeAndOffsetInLineMap(map, ch, bias) { + var node, start, end, collapse, mStart, mEnd; + // First, search the line map for the text node corresponding to, + // or closest to, the target character. + for (var i = 0; i < map.length; i += 3) { + mStart = map[i]; + mEnd = map[i + 1]; + if (ch < mStart) { + start = 0; end = 1; + collapse = "left"; + } else if (ch < mEnd) { + start = ch - mStart; + end = start + 1; + } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { + end = mEnd - mStart; + start = end - 1; + if (ch >= mEnd) { collapse = "right"; } + } + if (start != null) { + node = map[i + 2]; + if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) + { collapse = bias; } + if (bias == "left" && start == 0) + { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { + node = map[(i -= 3) + 2]; + collapse = "left"; + } } + if (bias == "right" && start == mEnd - mStart) + { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { + node = map[(i += 3) + 2]; + collapse = "right"; + } } + break + } + } + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} + } + + function getUsefulRect(rects, bias) { + var rect = nullRect; + if (bias == "left") { for (var i = 0; i < rects.length; i++) { + if ((rect = rects[i]).left != rect.right) { break } + } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { + if ((rect = rects[i$1]).left != rect.right) { break } + } } + return rect + } + + function measureCharInner(cm, prepared, ch, bias) { + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); + var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + + var rect; + if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. + for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } + if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) + { rect = node.parentNode.getBoundingClientRect(); } + else + { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } + if (rect.left || rect.right || start == 0) { break } + end = start; + start = start - 1; + collapse = "right"; + } + if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } + } else { // If it is a widget, simply get the box for the whole widget. + if (start > 0) { collapse = bias = "right"; } + var rects; + if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) + { rect = rects[bias == "right" ? rects.length - 1 : 0]; } + else + { rect = node.getBoundingClientRect(); } + } + if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { + var rSpan = node.parentNode.getClientRects()[0]; + if (rSpan) + { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } + else + { rect = nullRect; } + } + + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; + var mid = (rtop + rbot) / 2; + var heights = prepared.view.measure.heights; + var i = 0; + for (; i < heights.length - 1; i++) + { if (mid < heights[i]) { break } } + var top = i ? heights[i - 1] : 0, bot = heights[i]; + var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, + top: top, bottom: bot}; + if (!rect.left && !rect.right) { result.bogus = true; } + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + + return result + } + + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + { return rect } + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY} + } + + function clearLineMeasurementCacheFor(lineView) { + if (lineView.measure) { + lineView.measure.cache = {}; + lineView.measure.heights = null; + if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) + { lineView.measure.caches[i] = {}; } } + } + } + + function clearLineMeasurementCache(cm) { + cm.display.externalMeasure = null; + removeChildren(cm.display.lineMeasure); + for (var i = 0; i < cm.display.view.length; i++) + { clearLineMeasurementCacheFor(cm.display.view[i]); } + } + + function clearCaches(cm) { + clearLineMeasurementCache(cm); + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; + if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } + cm.display.lineNumChars = null; + } + + function pageScrollX() { + // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 + // which causes page_Offset and bounding client rects to use + // different reference viewports and invalidate our calculations. + if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } + return window.pageXOffset || (document.documentElement || document.body).scrollLeft + } + function pageScrollY() { + if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } + return window.pageYOffset || (document.documentElement || document.body).scrollTop + } + + function widgetTopHeight(lineObj) { + var height = 0; + if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) + { height += widgetHeight(lineObj.widgets[i]); } } } + return height + } + + // Converts a {top, bottom, left, right} box from line-local + // coordinates into another coordinate system. Context may be one of + // "line", "div" (display.lineDiv), "local"./null (editor), "window", + // or "page". + function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets) { + var height = widgetTopHeight(lineObj); + rect.top += height; rect.bottom += height; + } + if (context == "line") { return rect } + if (!context) { context = "local"; } + var yOff = heightAtLine(lineObj); + if (context == "local") { yOff += paddingTop(cm.display); } + else { yOff -= cm.display.viewOffset; } + if (context == "page" || context == "window") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); + rect.left += xOff; rect.right += xOff; + } + rect.top += yOff; rect.bottom += yOff; + return rect + } + + // Coverts a box from "div" coords to another coordinate system. + // Context may be "window", "page", "div", or "local"./null. + function fromCoordSystem(cm, coords, context) { + if (context == "div") { return coords } + var left = coords.left, top = coords.top; + // First move into "page" coordinate system + if (context == "page") { + left -= pageScrollX(); + top -= pageScrollY(); + } else if (context == "local" || !context) { + var localBox = cm.display.sizer.getBoundingClientRect(); + left += localBox.left; + top += localBox.top; + } + + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} + } + + function charCoords(cm, pos, context, lineObj, bias) { + if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) + } + + // Returns a box for a given cursor position, which may have an + // 'other' property containing the position of the secondary cursor + // on a bidi boundary. + // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` + // and after `char - 1` in writing order of `char - 1` + // A cursor Pos(line, char, "after") is on the same visual line as `char` + // and before `char` in writing order of `char` + // Examples (upper-case letters are RTL, lower-case are LTR): + // Pos(0, 1, ...) + // before after + // ab a|b a|b + // aB a|B aB| + // Ab |Ab A|b + // AB B|A B|A + // Every position after the last character on a line is considered to stick + // to the last character on the line. + function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { + lineObj = lineObj || getLine(cm.doc, pos.line); + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + function get(ch, right) { + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); + if (right) { m.left = m.right; } else { m.right = m.left; } + return intoCoordSystem(cm, lineObj, m, context) + } + var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; + if (ch >= lineObj.text.length) { + ch = lineObj.text.length; + sticky = "before"; + } else if (ch <= 0) { + ch = 0; + sticky = "after"; + } + if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } + + function getBidi(ch, partPos, invert) { + var part = order[partPos], right = part.level == 1; + return get(invert ? ch - 1 : ch, right != invert) + } + var partPos = getBidiPartAt(order, ch, sticky); + var other = bidiOther; + var val = getBidi(ch, partPos, sticky == "before"); + if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } + return val + } + + // Used to cheaply estimate the coordinates for a position. Used for + // intermediate scroll updates. + function estimateCoords(cm, pos) { + var left = 0; + pos = clipPos(cm.doc, pos); + if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } + var lineObj = getLine(cm.doc, pos.line); + var top = heightAtLine(lineObj) + paddingTop(cm.display); + return {left: left, right: left, top: top, bottom: top + lineObj.height} + } + + // Positions returned by coordsChar contain some extra information. + // xRel is the relative x position of the input coordinates compared + // to the found position (so xRel > 0 means the coordinates are to + // the right of the character position, for example). When outside + // is true, that means the coordinates lie outside the line's + // vertical range. + function PosWithInfo(line, ch, sticky, outside, xRel) { + var pos = Pos(line, ch, sticky); + pos.xRel = xRel; + if (outside) { pos.outside = outside; } + return pos + } + + // Compute the character position closest to the given coordinates. + // Input must be lineSpace-local ("div" coordinate system). + function coordsChar(cm, x, y) { + var doc = cm.doc; + y += cm.display.viewOffset; + if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) } + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) + { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) } + if (x < 0) { x = 0; } + + var lineObj = getLine(doc, lineN); + for (;;) { + var found = coordsCharInner(cm, lineObj, lineN, x, y); + var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)); + if (!collapsed) { return found } + var rangeEnd = collapsed.find(1); + if (rangeEnd.line == lineN) { return rangeEnd } + lineObj = getLine(doc, lineN = rangeEnd.line); + } + } + + function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + y -= widgetTopHeight(lineObj); + var end = lineObj.text.length; + var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0); + end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end); + return {begin: begin, end: end} + } + + function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } + var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) + } + + // Returns true if the given side of a box is after the given + // coordinates, in top-to-bottom, left-to-right order. + function boxIsAfter(box, x, y, left) { + return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x + } + + function coordsCharInner(cm, lineObj, lineNo, x, y) { + // Move y into line-local coordinate space + y -= heightAtLine(lineObj); + var preparedMeasure = prepareMeasureForLine(cm, lineObj); + // When directly calling `measureCharPrepared`, we have to adjust + // for the widgets at this line. + var widgetHeight = widgetTopHeight(lineObj); + var begin = 0, end = lineObj.text.length, ltr = true; + + var order = getOrder(lineObj, cm.doc.direction); + // If the line isn't plain left-to-right text, first figure out + // which bidi section the coordinates fall into. + if (order) { + var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart) + (cm, lineObj, lineNo, preparedMeasure, order, x, y); + ltr = part.level != 1; + // The awkward -1 offsets are needed because findFirst (called + // on these below) will treat its first bound as inclusive, + // second as exclusive, but we want to actually address the + // characters in the part's range + begin = ltr ? part.from : part.to - 1; + end = ltr ? part.to : part.from - 1; + } + + // A binary search to find the first character whose bounding box + // starts after the coordinates. If we run across any whose box wrap + // the coordinates, store that. + var chAround = null, boxAround = null; + var ch = findFirst(function (ch) { + var box = measureCharPrepared(cm, preparedMeasure, ch); + box.top += widgetHeight; box.bottom += widgetHeight; + if (!boxIsAfter(box, x, y, false)) { return false } + if (box.top <= y && box.left <= x) { + chAround = ch; + boxAround = box; + } + return true + }, begin, end); + + var baseX, sticky, outside = false; + // If a box around the coordinates was found, use that + if (boxAround) { + // Distinguish coordinates nearer to the left or right side of the box + var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr; + ch = chAround + (atStart ? 0 : 1); + sticky = atStart ? "after" : "before"; + baseX = atLeft ? boxAround.left : boxAround.right; + } else { + // (Adjust for extended bound, if necessary.) + if (!ltr && (ch == end || ch == begin)) { ch++; } + // To determine which side to associate with, get the box to the + // left of the character and compare it's vertical position to the + // coordinates + sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" : + (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ? + "after" : "before"; + // Now get accurate coordinates for this place, in order to get a + // base X position + var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure); + baseX = coords.left; + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0; + } + + ch = skipExtendingChars(lineObj.text, ch, 1); + return PosWithInfo(lineNo, ch, sticky, outside, x - baseX) + } + + function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) { + // Bidi parts are sorted left-to-right, and in a non-line-wrapping + // situation, we can take this ordering to correspond to the visual + // ordering. This finds the first part whose end is after the given + // coordinates. + var index = findFirst(function (i) { + var part = order[i], ltr = part.level != 1; + return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"), + "line", lineObj, preparedMeasure), x, y, true) + }, 0, order.length - 1); + var part = order[index]; + // If this isn't the first part, the part's start is also after + // the coordinates, and the coordinates aren't on the same line as + // that start, move one part back. + if (index > 0) { + var ltr = part.level != 1; + var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"), + "line", lineObj, preparedMeasure); + if (boxIsAfter(start, x, y, true) && start.top > y) + { part = order[index - 1]; } + } + return part + } + + function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) { + // In a wrapped line, rtl text on wrapping boundaries can do things + // that don't correspond to the ordering in our `order` array at + // all, so a binary search doesn't work, and we want to return a + // part that only spans one line so that the binary search in + // coordsCharInner is safe. As such, we first find the extent of the + // wrapped line, and then do a flat search in which we discard any + // spans that aren't on the line. + var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y); + var begin = ref.begin; + var end = ref.end; + if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; } + var part = null, closestDist = null; + for (var i = 0; i < order.length; i++) { + var p = order[i]; + if (p.from >= end || p.to <= begin) { continue } + var ltr = p.level != 1; + var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right; + // Weigh against spans ending before this, so that they are only + // picked if nothing ends after + var dist = endX < x ? x - endX + 1e9 : endX - x; + if (!part || closestDist > dist) { + part = p; + closestDist = dist; + } + } + if (!part) { part = order[order.length - 1]; } + // Clip the part to the wrapped line. + if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; } + if (part.to > end) { part = {from: part.from, to: end, level: part.level}; } + return part + } + + var measureText; + // Compute the default text height. + function textHeight(display) { + if (display.cachedTextHeight != null) { return display.cachedTextHeight } + if (measureText == null) { + measureText = elt("pre", null, "CodeMirror-line-like"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) { display.cachedTextHeight = height; } + removeChildren(display.measure); + return height || 1 + } + + // Compute the default character width. + function charWidth(display) { + if (display.cachedCharWidth != null) { return display.cachedCharWidth } + var anchor = elt("span", "xxxxxxxxxx"); + var pre = elt("pre", [anchor], "CodeMirror-line-like"); + removeChildrenAndAdd(display.measure, pre); + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; + if (width > 2) { display.cachedCharWidth = width; } + return width || 10 + } + + // Do a bulk-read of the DOM positions and sizes needed to draw the + // view, so that we don't interleave reading and writing to the DOM. + function getDimensions(cm) { + var d = cm.display, left = {}, width = {}; + var gutterLeft = d.gutters.clientLeft; + for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + var id = cm.display.gutterSpecs[i].className; + left[id] = n.offsetLeft + n.clientLeft + gutterLeft; + width[id] = n.clientWidth; + } + return {fixedPos: compensateForHScroll(d), + gutterTotalWidth: d.gutters.offsetWidth, + gutterLeft: left, + gutterWidth: width, + wrapperWidth: d.wrapper.clientWidth} + } + + // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, + // but using getBoundingClientRect to get a sub-pixel-accurate + // result. + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left + } + + // Returns a function that estimates the height of a line, to use as + // first approximation until the line becomes visible (and is thus + // properly measurable). + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function (line) { + if (lineIsHidden(cm.doc, line)) { return 0 } + + var widgetsHeight = 0; + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { + if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } + } } + + if (wrapping) + { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } + else + { return widgetsHeight + th } + } + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function (line) { + var estHeight = est(line); + if (estHeight != line.height) { updateLineHeight(line, estHeight); } + }); + } + + // Given a mouse event, find the corresponding position. If liberal + // is false, it checks whether a gutter or scrollbar was clicked, + // and returns null if it was. forRect is used by rectangular + // selections, and tries to estimate a character position even for + // coordinates beyond the right of the text. + function posFromMouse(cm, e, liberal, forRect) { + var display = cm.display; + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } + + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e$1) { return null } + var coords = coordsChar(cm, x, y), line; + if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + } + return coords + } + + // Find the view element corresponding to a given line. Return null + // when the line isn't visible. + function findViewIndex(cm, n) { + if (n >= cm.display.viewTo) { return null } + n -= cm.display.viewFrom; + if (n < 0) { return null } + var view = cm.display.view; + for (var i = 0; i < view.length; i++) { + n -= view[i].size; + if (n < 0) { return i } + } + } + + // Updates the display.view data structure for a given change to the + // document. From and to are in pre-change coordinates. Lendiff is + // the amount of lines added or subtracted by the change. This is + // used for changes that span multiple lines, or change the way + // lines are divided into visual lines. regLineChange (below) + // registers single-line changes. + function regChange(cm, from, to, lendiff) { + if (from == null) { from = cm.doc.first; } + if (to == null) { to = cm.doc.first + cm.doc.size; } + if (!lendiff) { lendiff = 0; } + + var display = cm.display; + if (lendiff && to < display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers > from)) + { display.updateLineNumbers = from; } + + cm.curOp.viewChanged = true; + + if (from >= display.viewTo) { // Change after + if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) + { resetView(cm); } + } else if (to <= display.viewFrom) { // Change before + if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { + resetView(cm); + } else { + display.viewFrom += lendiff; + display.viewTo += lendiff; + } + } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap + resetView(cm); + } else if (from <= display.viewFrom) { // Top overlap + var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cut) { + display.view = display.view.slice(cut.index); + display.viewFrom = cut.lineN; + display.viewTo += lendiff; + } else { + resetView(cm); + } + } else if (to >= display.viewTo) { // Bottom overlap + var cut$1 = viewCuttingPoint(cm, from, from, -1); + if (cut$1) { + display.view = display.view.slice(0, cut$1.index); + display.viewTo = cut$1.lineN; + } else { + resetView(cm); + } + } else { // Gap in the middle + var cutTop = viewCuttingPoint(cm, from, from, -1); + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + if (cutTop && cutBot) { + display.view = display.view.slice(0, cutTop.index) + .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) + .concat(display.view.slice(cutBot.index)); + display.viewTo += lendiff; + } else { + resetView(cm); + } + } + + var ext = display.externalMeasured; + if (ext) { + if (to < ext.lineN) + { ext.lineN += lendiff; } + else if (from < ext.lineN + ext.size) + { display.externalMeasured = null; } + } + } + + // Register a change to a single line. Type must be one of "text", + // "gutter", "class", "widget" + function regLineChange(cm, line, type) { + cm.curOp.viewChanged = true; + var display = cm.display, ext = cm.display.externalMeasured; + if (ext && line >= ext.lineN && line < ext.lineN + ext.size) + { display.externalMeasured = null; } + + if (line < display.viewFrom || line >= display.viewTo) { return } + var lineView = display.view[findViewIndex(cm, line)]; + if (lineView.node == null) { return } + var arr = lineView.changes || (lineView.changes = []); + if (indexOf(arr, type) == -1) { arr.push(type); } + } + + // Clear the view. + function resetView(cm) { + cm.display.viewFrom = cm.display.viewTo = cm.doc.first; + cm.display.view = []; + cm.display.viewOffset = 0; + } + + function viewCuttingPoint(cm, oldN, newN, dir) { + var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) + { return {index: index, lineN: newN} } + var n = cm.display.viewFrom; + for (var i = 0; i < index; i++) + { n += view[i].size; } + if (n != oldN) { + if (dir > 0) { + if (index == view.length - 1) { return null } + diff = (n + view[index].size) - oldN; + index++; + } else { + diff = n - oldN; + } + oldN += diff; newN += diff; + } + while (visualLineNo(cm.doc, newN) != newN) { + if (index == (dir < 0 ? 0 : view.length - 1)) { return null } + newN += dir * view[index - (dir < 0 ? 1 : 0)].size; + index += dir; + } + return {index: index, lineN: newN} + } + + // Force the view to cover a given range, adding empty view element + // or clipping off existing ones as needed. + function adjustView(cm, from, to) { + var display = cm.display, view = display.view; + if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { + display.view = buildViewArray(cm, from, to); + display.viewFrom = from; + } else { + if (display.viewFrom > from) + { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } + else if (display.viewFrom < from) + { display.view = display.view.slice(findViewIndex(cm, from)); } + display.viewFrom = from; + if (display.viewTo < to) + { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } + else if (display.viewTo > to) + { display.view = display.view.slice(0, findViewIndex(cm, to)); } + } + display.viewTo = to; + } + + // Count the number of lines in the view whose DOM representation is + // out of date (or nonexistent). + function countDirtyView(cm) { + var view = cm.display.view, dirty = 0; + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } + } + return dirty + } + + function updateSelection(cm) { + cm.display.input.showSelection(cm.display.input.prepareSelection()); + } + + function prepareSelection(cm, primary) { + if ( primary === void 0 ) primary = true; + + var doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); + + var customCursor = cm.options.$customCursor; + if (customCursor) { primary = true; } + for (var i = 0; i < doc.sel.ranges.length; i++) { + if (!primary && i == doc.sel.primIndex) { continue } + var range = doc.sel.ranges[i]; + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue } + var collapsed = range.empty(); + if (customCursor) { + var head = customCursor(cm, range); + if (head) { drawSelectionCursor(cm, head, curFragment); } + } else if (collapsed || cm.options.showCursorWhenSelecting) { + drawSelectionCursor(cm, range.head, curFragment); + } + if (!collapsed) + { drawSelectionRange(cm, range, selFragment); } + } + return result + } + + // Draws a cursor for the given range + function drawSelectionCursor(cm, head, output) { + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + + if (/\bcm-fat-cursor\b/.test(cm.getWrapperElement().className)) { + var charPos = charCoords(cm, head, "div", null, null); + var width = charPos.right - charPos.left; + cursor.style.width = (width > 0 ? width : cm.defaultCharWidth()) + "px"; + } + + if (pos.other) { + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } + } + + function cmpCoords(a, b) { return a.top - b.top || a.left - b.left } + + // Draws the given range as a highlighted selection + function drawSelectionRange(cm, range, output) { + var display = cm.display, doc = cm.doc; + var fragment = document.createDocumentFragment(); + var padding = paddingH(cm.display), leftSide = padding.left; + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var docLTR = doc.direction == "ltr"; + + function add(left, top, width, bottom) { + if (top < 0) { top = 0; } + top = Math.round(top); + bottom = Math.round(bottom); + fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); + } + + function drawForLine(line, fromArg, toArg) { + var lineObj = getLine(doc, line); + var lineLen = lineObj.text.length; + var start, end; + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias) + } + + function wrapX(pos, dir, side) { + var extent = wrappedLineExtentChar(cm, lineObj, null, pos); + var prop = (dir == "ltr") == (side == "after") ? "left" : "right"; + var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1); + return coords(ch, prop)[prop] + } + + var order = getOrder(lineObj, doc.direction); + iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) { + var ltr = dir == "ltr"; + var fromPos = coords(from, ltr ? "left" : "right"); + var toPos = coords(to - 1, ltr ? "right" : "left"); + + var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen; + var first = i == 0, last = !order || i == order.length - 1; + if (toPos.top - fromPos.top <= 3) { // Single line + var openLeft = (docLTR ? openStart : openEnd) && first; + var openRight = (docLTR ? openEnd : openStart) && last; + var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left; + var right = openRight ? rightSide : (ltr ? toPos : fromPos).right; + add(left, fromPos.top, right - left, fromPos.bottom); + } else { // Multiple lines + var topLeft, topRight, botLeft, botRight; + if (ltr) { + topLeft = docLTR && openStart && first ? leftSide : fromPos.left; + topRight = docLTR ? rightSide : wrapX(from, dir, "before"); + botLeft = docLTR ? leftSide : wrapX(to, dir, "after"); + botRight = docLTR && openEnd && last ? rightSide : toPos.right; + } else { + topLeft = !docLTR ? leftSide : wrapX(from, dir, "before"); + topRight = !docLTR && openStart && first ? rightSide : fromPos.right; + botLeft = !docLTR && openEnd && last ? leftSide : toPos.left; + botRight = !docLTR ? rightSide : wrapX(to, dir, "after"); + } + add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom); + if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); } + add(botLeft, toPos.top, botRight - botLeft, toPos.bottom); + } + + if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; } + if (cmpCoords(toPos, start) < 0) { start = toPos; } + if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; } + if (cmpCoords(toPos, end) < 0) { end = toPos; } + }); + return {start: start, end: end} + } + + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); + } else { + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); + var singleVLine = visualLine(fromLine) == visualLine(toLine); + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + if (singleVLine) { + if (leftEnd.top < rightStart.top - 2) { + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + } else { + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + } + } + if (leftEnd.bottom < rightStart.top) + { add(leftSide, leftEnd.bottom, null, rightStart.top); } + } + + output.appendChild(fragment); + } + + // Cursor-blinking + function restartBlink(cm) { + if (!cm.state.focused) { return } + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursorDiv.style.visibility = ""; + if (cm.options.cursorBlinkRate > 0) + { display.blinker = setInterval(function () { + if (!cm.hasFocus()) { onBlur(cm); } + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } + else if (cm.options.cursorBlinkRate < 0) + { display.cursorDiv.style.visibility = "hidden"; } + } + + function ensureFocus(cm) { + if (!cm.hasFocus()) { + cm.display.input.focus(); + if (!cm.state.focused) { onFocus(cm); } + } + } + + function delayBlurEvent(cm) { + cm.state.delayingBlurEvent = true; + setTimeout(function () { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false; + if (cm.state.focused) { onBlur(cm); } + } }, 100); + } + + function onFocus(cm, e) { + if (cm.state.delayingBlurEvent && !cm.state.draggingText) { cm.state.delayingBlurEvent = false; } + + if (cm.options.readOnly == "nocursor") { return } + if (!cm.state.focused) { + signal(cm, "focus", cm, e); + cm.state.focused = true; + addClass(cm.display.wrapper, "CodeMirror-focused"); + // This test prevents this from firing when a context + // menu is closed (since the input reset would kill the + // select-all detection hack) + if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { + cm.display.input.reset(); + if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 + } + cm.display.input.receivedFocus(); + } + restartBlink(cm); + } + function onBlur(cm, e) { + if (cm.state.delayingBlurEvent) { return } + + if (cm.state.focused) { + signal(cm, "blur", cm, e); + cm.state.focused = false; + rmClass(cm.display.wrapper, "CodeMirror-focused"); + } + clearInterval(cm.display.blinker); + setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); + } + + // Read the actual heights of the rendered lines, and update their + // stored heights to match. + function updateHeightsInViewport(cm) { + var display = cm.display; + var prevBottom = display.lineDiv.offsetTop; + var viewTop = Math.max(0, display.scroller.getBoundingClientRect().top); + var oldHeight = display.lineDiv.getBoundingClientRect().top; + var mustScroll = 0; + for (var i = 0; i < display.view.length; i++) { + var cur = display.view[i], wrapping = cm.options.lineWrapping; + var height = (void 0), width = 0; + if (cur.hidden) { continue } + oldHeight += cur.line.height; + if (ie && ie_version < 8) { + var bot = cur.node.offsetTop + cur.node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = cur.node.getBoundingClientRect(); + height = box.bottom - box.top; + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; } + } + var diff = cur.line.height - height; + if (diff > .005 || diff < -.005) { + if (oldHeight < viewTop) { mustScroll -= diff; } + updateLineHeight(cur.line, height); + updateWidgetHeight(cur.line); + if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) + { updateWidgetHeight(cur.rest[j]); } } + } + if (width > cm.display.sizerWidth) { + var chWidth = Math.ceil(width / charWidth(cm.display)); + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth; + cm.display.maxLine = cur.line; + cm.display.maxLineChanged = true; + } + } + } + if (Math.abs(mustScroll) > 2) { display.scroller.scrollTop += mustScroll; } + } + + // Read and store the height of line widgets associated with the + // given line. + function updateWidgetHeight(line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i], parent = w.node.parentNode; + if (parent) { w.height = parent.offsetHeight; } + } } + } + + // Compute the lines that are visible in a given viewport (defaults + // the the current scroll position). viewport may contain top, + // height, and ensure (see op.scrollToPos) properties. + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; + top = Math.floor(top - paddingTop(display)); + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; + + var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + // Ensure is a {from: {line, ch}, to: {line, ch}} object, and + // forces those lines into the viewport (if possible). + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; + if (ensureFrom < from) { + from = ensureFrom; + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + } else if (Math.min(ensureTo, doc.lastLine()) >= to) { + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); + to = ensureTo; + } + } + return {from: from, to: Math.max(to, from + 1)} + } + + // SCROLLING THINGS INTO VIEW + + // If an editor sits on the top or bottom of the window, partially + // scrolled out of view, this ensures that the cursor is visible. + function maybeScrollWindow(cm, rect) { + if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } + + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (rect.top + box.top < 0) { doScroll = true; } + else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } + if (doScroll != null && !phantom) { + var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); + cm.display.lineSpace.appendChild(scrollNode); + scrollNode.scrollIntoView(doScroll); + cm.display.lineSpace.removeChild(scrollNode); + } + } + + // Scroll a given position into view (immediately), verifying that + // it actually became visible (as line heights are accurately + // measured, the position of something may 'drift' during drawing). + function scrollPosIntoView(cm, pos, end, margin) { + if (margin == null) { margin = 0; } + var rect; + if (!cm.options.lineWrapping && pos == end) { + // Set pos and end to the cursor positions around the character pos sticks to + // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch + // If pos == Pos(_, 0, "before"), pos and end are unchanged + end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; + pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; + } + for (var limit = 0; limit < 5; limit++) { + var changed = false; + var coords = cursorCoords(cm, pos); + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + rect = {left: Math.min(coords.left, endCoords.left), + top: Math.min(coords.top, endCoords.top) - margin, + right: Math.max(coords.left, endCoords.left), + bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; + var scrollPos = calculateScrollPos(cm, rect); + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + if (scrollPos.scrollTop != null) { + updateScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } + } + if (!changed) { break } + } + return rect + } + + // Scroll a given set of coordinates into view (immediately). + function scrollIntoView(cm, rect) { + var scrollPos = calculateScrollPos(cm, rect); + if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } + if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } + } + + // Calculate a new scroll position needed to scroll the given + // rectangle into view. Returns an object with scrollTop and + // scrollLeft properties. When these are undefined, the + // vertical/horizontal position does not need to be adjusted. + function calculateScrollPos(cm, rect) { + var display = cm.display, snapMargin = textHeight(cm.display); + if (rect.top < 0) { rect.top = 0; } + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; + var screen = displayHeight(cm), result = {}; + if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } + var docBottom = cm.doc.height + paddingVert(display); + var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; + if (rect.top < screentop) { + result.scrollTop = atTop ? 0 : rect.top; + } else if (rect.bottom > screentop + screen) { + var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); + if (newTop != screentop) { result.scrollTop = newTop; } + } + + var gutterSpace = cm.options.fixedGutter ? 0 : display.gutters.offsetWidth; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - gutterSpace; + var screenw = displayWidth(cm) - display.gutters.offsetWidth; + var tooWide = rect.right - rect.left > screenw; + if (tooWide) { rect.right = rect.left + screenw; } + if (rect.left < 10) + { result.scrollLeft = 0; } + else if (rect.left < screenleft) + { result.scrollLeft = Math.max(0, rect.left + gutterSpace - (tooWide ? 0 : 10)); } + else if (rect.right > screenw + screenleft - 3) + { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } + return result + } + + // Store a relative adjustment to the scroll position in the current + // operation (to be applied when the operation finishes). + function addToScrollTop(cm, top) { + if (top == null) { return } + resolveScrollToPos(cm); + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + } + + // Make sure that at the end of the operation the current cursor is + // shown. + function ensureCursorVisible(cm) { + resolveScrollToPos(cm); + var cur = cm.getCursor(); + cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; + } + + function scrollToCoords(cm, x, y) { + if (x != null || y != null) { resolveScrollToPos(cm); } + if (x != null) { cm.curOp.scrollLeft = x; } + if (y != null) { cm.curOp.scrollTop = y; } + } + + function scrollToRange(cm, range) { + resolveScrollToPos(cm); + cm.curOp.scrollToPos = range; + } + + // When an operation has its scrollToPos property set, and another + // scroll action is applied before the end of the operation, this + // 'simulates' scrolling that position into view in a cheap way, so + // that the effect of intermediate scroll commands is not ignored. + function resolveScrollToPos(cm) { + var range = cm.curOp.scrollToPos; + if (range) { + cm.curOp.scrollToPos = null; + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + scrollToCoordsRange(cm, from, to, range.margin); + } + } + + function scrollToCoordsRange(cm, from, to, margin) { + var sPos = calculateScrollPos(cm, { + left: Math.min(from.left, to.left), + top: Math.min(from.top, to.top) - margin, + right: Math.max(from.right, to.right), + bottom: Math.max(from.bottom, to.bottom) + margin + }); + scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); + } + + // Sync the scrollable area and scrollbars, ensure the viewport + // covers the visible area. + function updateScrollTop(cm, val) { + if (Math.abs(cm.doc.scrollTop - val) < 2) { return } + if (!gecko) { updateDisplaySimple(cm, {top: val}); } + setScrollTop(cm, val, true); + if (gecko) { updateDisplaySimple(cm); } + startWorker(cm, 100); + } + + function setScrollTop(cm, val, forceScroll) { + val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val)); + if (cm.display.scroller.scrollTop == val && !forceScroll) { return } + cm.doc.scrollTop = val; + cm.display.scrollbars.setScrollTop(val); + if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } + } + + // Sync scroller and scrollbar, ensure the gutter elements are + // aligned. + function setScrollLeft(cm, val, isScroller, forceScroll) { + val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth)); + if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } + cm.doc.scrollLeft = val; + alignHorizontally(cm); + if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } + cm.display.scrollbars.setScrollLeft(val); + } + + // SCROLLBARS + + // Prepare DOM reads needed to update the scrollbars. Done in one + // shot to minimize update/measure roundtrips. + function measureForScrollbars(cm) { + var d = cm.display, gutterW = d.gutters.offsetWidth; + var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + return { + clientHeight: d.scroller.clientHeight, + viewHeight: d.wrapper.clientHeight, + scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, + viewWidth: d.wrapper.clientWidth, + barLeft: cm.options.fixedGutter ? gutterW : 0, + docHeight: docH, + scrollHeight: docH + scrollGap(cm) + d.barHeight, + nativeBarWidth: d.nativeBarWidth, + gutterWidth: gutterW + } + } + + var NativeScrollbars = function(place, scroll, cm) { + this.cm = cm; + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); + vert.tabIndex = horiz.tabIndex = -1; + place(vert); place(horiz); + + on(vert, "scroll", function () { + if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } + }); + on(horiz, "scroll", function () { + if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } + }); + + this.checkedZeroWidth = false; + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } + }; + + NativeScrollbars.prototype.update = function (measure) { + var needsH = measure.scrollWidth > measure.clientWidth + 1; + var needsV = measure.scrollHeight > measure.clientHeight + 1; + var sWidth = measure.nativeBarWidth; + + if (needsV) { + this.vert.style.display = "block"; + this.vert.style.bottom = needsH ? sWidth + "px" : "0"; + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + // A bug in IE8 can cause this value to be negative, so guard it. + this.vert.firstChild.style.height = + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + } else { + this.vert.scrollTop = 0; + this.vert.style.display = ""; + this.vert.firstChild.style.height = "0"; + } + + if (needsH) { + this.horiz.style.display = "block"; + this.horiz.style.right = needsV ? sWidth + "px" : "0"; + this.horiz.style.left = measure.barLeft + "px"; + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.firstChild.style.width = + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + } else { + this.horiz.style.display = ""; + this.horiz.firstChild.style.width = "0"; + } + + if (!this.checkedZeroWidth && measure.clientHeight > 0) { + if (sWidth == 0) { this.zeroWidthHack(); } + this.checkedZeroWidth = true; + } + + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} + }; + + NativeScrollbars.prototype.setScrollLeft = function (pos) { + if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } + if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } + }; + + NativeScrollbars.prototype.setScrollTop = function (pos) { + if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } + if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } + }; + + NativeScrollbars.prototype.zeroWidthHack = function () { + var w = mac && !mac_geMountainLion ? "12px" : "18px"; + this.horiz.style.height = this.vert.style.width = w; + this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; + this.disableHoriz = new Delayed; + this.disableVert = new Delayed; + }; + + NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { + bar.style.pointerEvents = "auto"; + function maybeDisable() { + // To find out whether the scrollbar is still visible, we + // check whether the element under the pixel in the bottom + // right corner of the scrollbar box is the scrollbar box + // itself (when the bar is still visible) or its filler child + // (when the bar is hidden). If it is still visible, we keep + // it enabled, if it's hidden, we disable pointer events. + var box = bar.getBoundingClientRect(); + var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) + : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); + if (elt != bar) { bar.style.pointerEvents = "none"; } + else { delay.set(1000, maybeDisable); } + } + delay.set(1000, maybeDisable); + }; + + NativeScrollbars.prototype.clear = function () { + var parent = this.horiz.parentNode; + parent.removeChild(this.horiz); + parent.removeChild(this.vert); + }; + + var NullScrollbars = function () {}; + + NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; + NullScrollbars.prototype.setScrollLeft = function () {}; + NullScrollbars.prototype.setScrollTop = function () {}; + NullScrollbars.prototype.clear = function () {}; + + function updateScrollbars(cm, measure) { + if (!measure) { measure = measureForScrollbars(cm); } + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; + updateScrollbarsInner(cm, measure); + for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + if (startWidth != cm.display.barWidth && cm.options.lineWrapping) + { updateHeightsInViewport(cm); } + updateScrollbarsInner(cm, measureForScrollbars(cm)); + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + } + } + + // Re-synchronize the fake scrollbars with the actual size of the + // content. + function updateScrollbarsInner(cm, measure) { + var d = cm.display; + var sizes = d.scrollbars.update(measure); + + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; + + if (sizes.right && sizes.bottom) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = sizes.bottom + "px"; + d.scrollbarFiller.style.width = sizes.right + "px"; + } else { d.scrollbarFiller.style.display = ""; } + if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { + d.gutterFiller.style.display = "block"; + d.gutterFiller.style.height = sizes.bottom + "px"; + d.gutterFiller.style.width = measure.gutterWidth + "px"; + } else { d.gutterFiller.style.display = ""; } + } + + var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; + + function initScrollbars(cm) { + if (cm.display.scrollbars) { + cm.display.scrollbars.clear(); + if (cm.display.scrollbars.addClass) + { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + // Prevent clicks in the scrollbars from killing focus + on(node, "mousedown", function () { + if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } + }); + node.setAttribute("cm-not-content", "true"); + }, function (pos, axis) { + if (axis == "horizontal") { setScrollLeft(cm, pos); } + else { updateScrollTop(cm, pos); } + }, cm); + if (cm.display.scrollbars.addClass) + { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } + } + + // Operations are used to wrap a series of changes to the editor + // state in such a way that each change won't have to update the + // cursor and display (which would be awkward, slow, and + // error-prone). Instead, display updates are batched and then all + // combined and executed at once. + + var nextOpId = 0; + // Start a new operation. + function startOperation(cm) { + cm.curOp = { + cm: cm, + viewChanged: false, // Flag that indicates that lines might need to be redrawn + startHeight: cm.doc.height, // Used to detect need to update scrollbar + forceUpdate: false, // Used to force a redraw + updateInput: 0, // Whether to reset the input textarea + typing: false, // Whether this reset should be careful to leave existing text (for compositing) + changeObjs: null, // Accumulated changes, for firing change events + cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already + selectionChanged: false, // Whether the selection needs to be redrawn + updateMaxLine: false, // Set when the widest line needs to be determined anew + scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet + scrollToPos: null, // Used to scroll to a specific position + focus: false, + id: ++nextOpId, // Unique ID + markArrays: null // Used by addMarkedSpan + }; + pushOperation(cm.curOp); + } + + // Finish an operation, updating the display and signalling delayed events + function endOperation(cm) { + var op = cm.curOp; + if (op) { finishOperation(op, function (group) { + for (var i = 0; i < group.ops.length; i++) + { group.ops[i].cm.curOp = null; } + endOperations(group); + }); } + } + + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + { endOperation_R1(ops[i]); } + for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) + { endOperation_W1(ops[i$1]); } + for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM + { endOperation_R2(ops[i$2]); } + for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) + { endOperation_W2(ops[i$3]); } + for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM + { endOperation_finish(ops[i$4]); } + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; + maybeClipScrollbars(cm); + if (op.updateMaxLine) { findMaxLine(cm); } + + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) { updateHeightsInViewport(cm); } + + op.barMeasure = measureForScrollbars(cm); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplay_W2 will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; + cm.display.sizerWidth = op.adjustWidthTo; + op.barMeasure.scrollWidth = + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + } + + if (op.updatedDisplay || op.selectionChanged) + { op.preparedSelection = display.input.prepareSelection(); } + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } + cm.display.maxLineChanged = false; + } + + var takeFocus = op.focus && op.focus == activeElt(); + if (op.preparedSelection) + { cm.display.input.showSelection(op.preparedSelection, takeFocus); } + if (op.updatedDisplay || op.startHeight != cm.doc.height) + { updateScrollbars(cm, op.barMeasure); } + if (op.updatedDisplay) + { setDocumentHeight(cm, op.barMeasure); } + + if (op.selectionChanged) { restartBlink(cm); } + + if (cm.state.focused && op.updateInput) + { cm.display.input.reset(op.typing); } + if (takeFocus) { ensureFocus(op.cm); } + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; + + if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } + + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + { display.wheelStartX = display.wheelStartY = null; } + + // Propagate the scroll position to the actual DOM scroller + if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } + + if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } + // If we need to scroll a specific position into view, do so. + if (op.scrollToPos) { + var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); + maybeScrollWindow(cm, rect); + } + + // Fire events for markers that are hidden/unidden by editing or + // undoing + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) { for (var i = 0; i < hidden.length; ++i) + { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } + if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) + { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } + + if (display.wrapper.offsetHeight) + { doc.scrollTop = cm.display.scroller.scrollTop; } + + // Fire change events, and delayed event handlers + if (op.changeObjs) + { signal(cm, "changes", cm, op.changeObjs); } + if (op.update) + { op.update.finish(); } + } + + // Run the given function in an operation + function runInOp(cm, f) { + if (cm.curOp) { return f() } + startOperation(cm); + try { return f() } + finally { endOperation(cm); } + } + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm, f) { + return function() { + if (cm.curOp) { return f.apply(cm, arguments) } + startOperation(cm); + try { return f.apply(cm, arguments) } + finally { endOperation(cm); } + } + } + // Used to add methods to editor and doc instances, wrapping them in + // operations. + function methodOp(f) { + return function() { + if (this.curOp) { return f.apply(this, arguments) } + startOperation(this); + try { return f.apply(this, arguments) } + finally { endOperation(this); } + } + } + function docMethodOp(f) { + return function() { + var cm = this.cm; + if (!cm || cm.curOp) { return f.apply(this, arguments) } + startOperation(cm); + try { return f.apply(this, arguments) } + finally { endOperation(cm); } + } + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.doc.highlightFrontier < cm.display.viewTo) + { cm.state.highlight.set(time, bind(highlightWorker, cm)); } + } + + function highlightWorker(cm) { + var doc = cm.doc; + if (doc.highlightFrontier >= cm.display.viewTo) { return } + var end = +new Date + cm.options.workTime; + var context = getContextBefore(cm, doc.highlightFrontier); + var changedLines = []; + + doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { + if (context.line >= cm.display.viewFrom) { // Visible + var oldStyles = line.styles; + var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; + var highlighted = highlightLine(cm, line, context, true); + if (resetState) { context.state = resetState; } + line.styles = highlighted.styles; + var oldCls = line.styleClasses, newCls = highlighted.classes; + if (newCls) { line.styleClasses = newCls; } + else if (oldCls) { line.styleClasses = null; } + var ischange = !oldStyles || oldStyles.length != line.styles.length || + oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); + for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } + if (ischange) { changedLines.push(context.line); } + line.stateAfter = context.save(); + context.nextLine(); + } else { + if (line.text.length <= cm.options.maxHighlightLength) + { processLine(cm, line.text, context); } + line.stateAfter = context.line % 5 == 0 ? context.save() : null; + context.nextLine(); + } + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true + } + }); + doc.highlightFrontier = context.line; + doc.modeFrontier = Math.max(doc.modeFrontier, context.line); + if (changedLines.length) { runInOp(cm, function () { + for (var i = 0; i < changedLines.length; i++) + { regLineChange(cm, changedLines[i], "text"); } + }); } + } + + // DISPLAY DRAWING + + var DisplayUpdate = function(cm, viewport, force) { + var display = cm.display; + + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.wrapperWidth = display.wrapper.clientWidth; + this.oldDisplayWidth = displayWidth(cm); + this.force = force; + this.dims = getDimensions(cm); + this.events = []; + }; + + DisplayUpdate.prototype.signal = function (emitter, type) { + if (hasHandler(emitter, type)) + { this.events.push(arguments); } + }; + DisplayUpdate.prototype.finish = function () { + for (var i = 0; i < this.events.length; i++) + { signal.apply(null, this.events[i]); } + }; + + function maybeClipScrollbars(cm) { + var display = cm.display; + if (!display.scrollbarsClipped && display.scroller.offsetWidth) { + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; + display.heightForcer.style.height = scrollGap(cm) + "px"; + display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; + display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; + display.scrollbarsClipped = true; + } + } + + function selectionSnapshot(cm) { + if (cm.hasFocus()) { return null } + var active = activeElt(); + if (!active || !contains(cm.display.lineDiv, active)) { return null } + var result = {activeElt: active}; + if (window.getSelection) { + var sel = window.getSelection(); + if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { + result.anchorNode = sel.anchorNode; + result.anchorOffset = sel.anchorOffset; + result.focusNode = sel.focusNode; + result.focusOffset = sel.focusOffset; + } + } + return result + } + + function restoreSelection(snapshot) { + if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } + snapshot.activeElt.focus(); + if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) && + snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { + var sel = window.getSelection(), range = document.createRange(); + range.setEnd(snapshot.anchorNode, snapshot.anchorOffset); + range.collapse(false); + sel.removeAllRanges(); + sel.addRange(range); + sel.extend(snapshot.focusNode, snapshot.focusOffset); + } + } + + // Does the actual updating of the line display. Bails out + // (returning false) when there is nothing to be done and forced is + // false. + function updateDisplayIfNeeded(cm, update) { + var display = cm.display, doc = cm.doc; + + if (update.editorIsHidden) { + resetView(cm); + return false + } + + // Bail out if the visible area is already rendered and nothing changed. + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && + display.renderedView == display.view && countDirtyView(cm) == 0) + { return false } + + if (maybeUpdateLineNumberWidth(cm)) { + resetView(cm); + update.dims = getDimensions(cm); + } + + // Compute a suitable new viewport (from & to) + var end = doc.first + doc.size; + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); + if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } + if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } + if (sawCollapsedSpans) { + from = visualLineNo(cm.doc, from); + to = visualLineEndNo(cm.doc, to); + } + + var different = from != display.viewFrom || to != display.viewTo || + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; + adjustView(cm, from, to); + + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + // Position the mover div to align with the current scroll position + cm.display.mover.style.top = display.viewOffset + "px"; + + var toUpdate = countDirtyView(cm); + if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + { return false } + + // For big changes, we hide the enclosing element during the + // update, since that speeds up the operations on most browsers. + var selSnapshot = selectionSnapshot(cm); + if (toUpdate > 4) { display.lineDiv.style.display = "none"; } + patchDisplay(cm, display.updateLineNumbers, update.dims); + if (toUpdate > 4) { display.lineDiv.style.display = ""; } + display.renderedView = display.view; + // There might have been a widget with a focused element that got + // hidden or updated, if so re-focus it. + restoreSelection(selSnapshot); + + // Prevent selection and cursors from interfering with the scroll + // width and height. + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); + display.gutters.style.height = display.sizer.style.minHeight = 0; + + if (different) { + display.lastWrapHeight = update.wrapperHeight; + display.lastWrapWidth = update.wrapperWidth; + startWorker(cm, 400); + } + + display.updateLineNumbers = null; + + return true + } + + function postUpdateDisplay(cm, update) { + var viewport = update.viewport; + + for (var first = true;; first = false) { + if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) + { break } + } else if (first) { + update.visible = visibleLines(cm.display, cm.doc, viewport); + } + if (!updateDisplayIfNeeded(cm, update)) { break } + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.force = false; + } + + update.signal(cm, "update", cm); + if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + } + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + updateHeightsInViewport(cm); + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + updateScrollbars(cm, barMeasure); + setDocumentHeight(cm, barMeasure); + update.finish(); + } + } + + // Sync the actual display DOM structure with display.view, removing + // nodes for lines that are no longer in view, and creating the ones + // that are not there yet, and updating the ones that are out of + // date. + function patchDisplay(cm, updateNumbersFrom, dims) { + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + // Works around a throw-scroll bug in OS X Webkit + if (webkit && mac && cm.display.currentWheelTarget == node) + { node.style.display = "none"; } + else + { node.parentNode.removeChild(node); } + return next + } + + var view = display.view, lineN = display.viewFrom; + // Loop over the elements in the view, syncing cur (the DOM nodes + // in display.lineDiv) with the view as we go. + for (var i = 0; i < view.length; i++) { + var lineView = view[i]; + if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet + var node = buildLineElement(cm, lineView, lineN, dims); + container.insertBefore(node, cur); + } else { // Already drawn + while (cur != lineView.node) { cur = rm(cur); } + var updateNumber = lineNumbers && updateNumbersFrom != null && + updateNumbersFrom <= lineN && lineView.lineNumber; + if (lineView.changes) { + if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } + updateLineForChanges(cm, lineView, lineN, dims); + } + if (updateNumber) { + removeChildren(lineView.lineNumber); + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + } + cur = lineView.node.nextSibling; + } + lineN += lineView.size; + } + while (cur) { cur = rm(cur); } + } + + function updateGutterSpace(display) { + var width = display.gutters.offsetWidth; + display.sizer.style.marginLeft = width + "px"; + // Send an event to consumers responding to changes in gutter width. + signalLater(display, "gutterChanged", display); + } + + function setDocumentHeight(cm, measure) { + cm.display.sizer.style.minHeight = measure.docHeight + "px"; + cm.display.heightForcer.style.top = measure.docHeight + "px"; + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; + } + + // Re-align line numbers and gutter marks to compensate for + // horizontal scrolling. + function alignHorizontally(cm) { + var display = cm.display, view = display.view; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; + var gutterW = display.gutters.offsetWidth, left = comp + "px"; + for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { + if (cm.options.fixedGutter) { + if (view[i].gutter) + { view[i].gutter.style.left = left; } + if (view[i].gutterBackground) + { view[i].gutterBackground.style.left = left; } + } + var align = view[i].alignable; + if (align) { for (var j = 0; j < align.length; j++) + { align[j].style.left = left; } } + } } + if (cm.options.fixedGutter) + { display.gutters.style.left = (comp + gutterW) + "px"; } + } + + // Used to ensure that the line number gutter is still the right + // size for the current document size. Returns true when an update + // is needed. + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) { return false } + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; + display.lineGutter.style.width = ""; + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; + display.lineNumWidth = display.lineNumInnerWidth + padding; + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + updateGutterSpace(cm.display); + return true + } + return false + } + + function getGutters(gutters, lineNumbers) { + var result = [], sawLineNumbers = false; + for (var i = 0; i < gutters.length; i++) { + var name = gutters[i], style = null; + if (typeof name != "string") { style = name.style; name = name.className; } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) { continue } + else { sawLineNumbers = true; } + } + result.push({className: name, style: style}); + } + if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); } + return result + } + + // Rebuild the gutter elements, ensure the margin to the left of the + // code matches their width. + function renderGutters(display) { + var gutters = display.gutters, specs = display.gutterSpecs; + removeChildren(gutters); + display.lineGutter = null; + for (var i = 0; i < specs.length; ++i) { + var ref = specs[i]; + var className = ref.className; + var style = ref.style; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)); + if (style) { gElt.style.cssText = style; } + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt; + gElt.style.width = (display.lineNumWidth || 1) + "px"; + } + } + gutters.style.display = specs.length ? "" : "none"; + updateGutterSpace(display); + } + + function updateGutters(cm) { + renderGutters(cm.display); + regChange(cm); + alignHorizontally(cm); + } + + // The display handles the DOM integration, both for input reading + // and content drawing. It holds references to DOM nodes and + // display-related state. + + function Display(place, doc, input, options) { + var d = this; + this.input = input; + + // Covers bottom-right square when both scrollbars are present. + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarFiller.setAttribute("cm-not-content", "true"); + // Covers bottom of gutter when coverGutterNextToScrollbar is on + // and h scrollbar is present. + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); + d.gutterFiller.setAttribute("cm-not-content", "true"); + // Will contain the actual code, positioned to cover the viewport. + d.lineDiv = eltP("div", null, "CodeMirror-code"); + // Elements are added to these to represent selection and cursors. + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + // A visibility: hidden element used to find the size of things. + d.measure = elt("div", null, "CodeMirror-measure"); + // When lines outside of the viewport are measured, they are drawn in this. + d.lineMeasure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], + null, "position: relative; outline: none"); + var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); + // Moved around its parent to cover visible view. + d.mover = elt("div", [lines], null, "position: relative"); + // Set to the height of the document, allowing scrolling. + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + d.sizerWidth = null; + // Behavior of elts with overflow: auto and padding is + // inconsistent across browsers. This is used to ensure the + // scrollable area is big enough. + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + // Will contain the gutters, if any. + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; + // Actual scrollable element. + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + + // This attribute is respected by automatic translation systems such as Google Translate, + // and may also be respected by tools used by human translators. + d.wrapper.setAttribute('translate', 'no'); + + // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } + if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } + + if (place) { + if (place.appendChild) { place.appendChild(d.wrapper); } + else { place(d.wrapper); } + } + + // Current rendered range (may be bigger than the view window). + d.viewFrom = d.viewTo = doc.first; + d.reportedViewFrom = d.reportedViewTo = doc.first; + // Information about the rendered lines. + d.view = []; + d.renderedView = null; + // Holds info about a single rendered line when it was rendered + // for measurement, while not in view. + d.externalMeasured = null; + // Empty space (in pixels) above the view + d.viewOffset = 0; + d.lastWrapHeight = d.lastWrapWidth = 0; + d.updateLineNumbers = null; + + d.nativeBarWidth = d.barHeight = d.barWidth = 0; + d.scrollbarsClipped = false; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; + // Set to true when a non-horizontal-scrolling line widget is + // added. As an optimization, line widget aligning is skipped when + // this is false. + d.alignWidgets = false; + + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; + + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + // True when shift is held down. + d.shift = false; + + // Used to track whether anything happened since the context menu + // was opened. + d.selForContextMenu = null; + + d.activeTouch = null; + + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers); + renderGutters(d); + + input.init(d); + } + + // Since the delta values reported on mouse wheel events are + // unstandardized between browsers and even browser versions, and + // generally horribly unpredictable, this code starts by measuring + // the scroll effect that the first few mouse wheel events have, + // and, from that, detects the way it can convert deltas to pixel + // offsets afterwards. + // + // The reason we want to know the amount a wheel event will scroll + // is that it gives us a chance to update the display before the + // actual scrolling happens, reducing flickering. + + var wheelSamples = 0, wheelPixelsPerUnit = null; + // Fill in a browser-detected starting value on browsers where we + // know one. These don't have to be accurate -- the result of them + // being wrong would just be a slight flicker on the first wheel + // scroll (if it is large enough). + if (ie) { wheelPixelsPerUnit = -.53; } + else if (gecko) { wheelPixelsPerUnit = 15; } + else if (chrome) { wheelPixelsPerUnit = -.7; } + else if (safari) { wheelPixelsPerUnit = -1/3; } + + function wheelEventDelta(e) { + var dx = e.wheelDeltaX, dy = e.wheelDeltaY; + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } + else if (dy == null) { dy = e.wheelDelta; } + return {x: dx, y: dy} + } + function wheelEventPixels(e) { + var delta = wheelEventDelta(e); + delta.x *= wheelPixelsPerUnit; + delta.y *= wheelPixelsPerUnit; + return delta + } + + function onScrollWheel(cm, e) { + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + var pixelsPerUnit = wheelPixelsPerUnit; + if (e.deltaMode === 0) { + dx = e.deltaX; + dy = e.deltaY; + pixelsPerUnit = 1; + } + + var display = cm.display, scroll = display.scroller; + // Quit if there's nothing to scroll here + var canScrollX = scroll.scrollWidth > scroll.clientWidth; + var canScrollY = scroll.scrollHeight > scroll.clientHeight; + if (!(dx && canScrollX || dy && canScrollY)) { return } + + // Webkit browsers on OS X abort momentum scrolls when the target + // of the scroll event is removed from the scrollable element. + // This hack (see related code in patchDisplay) makes sure the + // element is kept around. + if (dy && mac && webkit) { + outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (var i = 0; i < view.length; i++) { + if (view[i].node == cur) { + cm.display.currentWheelTarget = cur; + break outer + } + } + } + } + + // On some browsers, horizontal scrolling will cause redraws to + // happen before the gutter has been realigned, causing it to + // wriggle around in a most unseemly way. When we have an + // estimated pixels/delta value, we just handle horizontal + // scrolling entirely here. It'll be slightly off from native, but + // better than glitching out. + if (dx && !gecko && !presto && pixelsPerUnit != null) { + if (dy && canScrollY) + { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * pixelsPerUnit)); } + setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * pixelsPerUnit)); + // Only prevent default scrolling if vertical scrolling is + // actually possible. Otherwise, it causes vertical scroll + // jitter on OSX trackpads when deltaX is small and deltaY + // is large (issue #3579) + if (!dy || (dy && canScrollY)) + { e_preventDefault(e); } + display.wheelStartX = null; // Abort measurement, if in progress + return + } + + // 'Project' the visible viewport to cover the area that is being + // scrolled into view (if we know enough to estimate it). + if (dy && pixelsPerUnit != null) { + var pixels = dy * pixelsPerUnit; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; + if (pixels < 0) { top = Math.max(0, top + pixels - 50); } + else { bot = Math.min(cm.doc.height, bot + pixels + 50); } + updateDisplaySimple(cm, {top: top, bottom: bot}); + } + + if (wheelSamples < 20 && e.deltaMode !== 0) { + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; + setTimeout(function () { + if (display.wheelStartX == null) { return } + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; + if (!sample) { return } + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); + ++wheelSamples; + }, 200); + } else { + display.wheelDX += dx; display.wheelDY += dy; + } + } + } + + // Selection objects are immutable. A new one is created every time + // the selection changes. A selection is one or more non-overlapping + // (and non-touching) ranges, sorted, and an integer that indicates + // which one is the primary selection (the one that's scrolled into + // view, that getCursor returns, etc). + var Selection = function(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + }; + + Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; + + Selection.prototype.equals = function (other) { + if (other == this) { return true } + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } + } + return true + }; + + Selection.prototype.deepCopy = function () { + var out = []; + for (var i = 0; i < this.ranges.length; i++) + { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); } + return new Selection(out, this.primIndex) + }; + + Selection.prototype.somethingSelected = function () { + for (var i = 0; i < this.ranges.length; i++) + { if (!this.ranges[i].empty()) { return true } } + return false + }; + + Selection.prototype.contains = function (pos, end) { + if (!end) { end = pos; } + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + { return i } + } + return -1 + }; + + var Range = function(anchor, head) { + this.anchor = anchor; this.head = head; + }; + + Range.prototype.from = function () { return minPos(this.anchor, this.head) }; + Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; + Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; + + // Take an unsorted, potentially overlapping set of ranges, and + // build a selection out of it. 'Consumes' ranges array (modifying + // it). + function normalizeSelection(cm, ranges, primIndex) { + var mayTouch = cm && cm.options.selectionsMayTouch; + var prim = ranges[primIndex]; + ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); + primIndex = indexOf(ranges, prim); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + var diff = cmp(prev.to(), cur.from()); + if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) { + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; + if (i <= primIndex) { --primIndex; } + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex) + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0) + } + + // Compute the position of the end of a change (its 'to' property + // refers to the pre-change end). + function changeEnd(change) { + if (!change.text) { return change.to } + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) + } + + // Adjust a position to refer to the post-change position of the + // same text, or the end of the change if the change covers it. + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) { return pos } + if (cmp(pos, change.to) <= 0) { return changeEnd(change) } + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } + return Pos(line, ch) + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(doc.cm, out, doc.sel.primIndex) + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + { return Pos(nw.line, pos.ch - old.ch + nw.ch) } + else + { return Pos(nw.line + (pos.line - old.line), pos.ch) } + } + + // Used by replaceSelections to allow moving the selection to the + // start or around the replaced test. Hint may be "start" or "around". + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex) + } + + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + cm.doc.mode = getMode(cm.options, cm.doc.modeOption); + resetModeState(cm); + } + + function resetModeState(cm) { + cm.doc.iter(function (line) { + if (line.stateAfter) { line.stateAfter = null; } + if (line.styles) { line.styles = null; } + }); + cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; + startWorker(cm, 100); + cm.state.modeGen++; + if (cm.curOp) { regChange(cm); } + } + + // DOCUMENT DATA STRUCTURE + + // By default, updates that start and end at the beginning of a line + // are treated specially, in order to make the association of line + // widgets and marker elements with the text behave more intuitive. + function isWholeLineUpdate(doc, change) { + return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) + } + + // Perform a change on the document data structure. + function updateDoc(doc, change, markedSpans, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } + function linesFor(start, end) { + var result = []; + for (var i = start; i < end; ++i) + { result.push(new Line(text[i], spansFor(i), estimateHeight)); } + return result + } + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // Adjust the line structure + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = linesFor(0, text.length - 1); + update(lastLine, lastLine.text, lastSpans); + if (nlines) { doc.remove(from.line, nlines); } + if (added.length) { doc.insert(from.line, added); } + } else if (firstLine == lastLine) { + if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + } else { + var added$1 = linesFor(1, text.length - 1); + added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + doc.insert(from.line + 1, added$1); + } + } else if (text.length == 1) { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); + doc.remove(from.line + 1, nlines); + } else { + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); + var added$2 = linesFor(1, text.length - 1); + if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } + doc.insert(from.line + 1, added$2); + } + + signalLater(doc, "change", doc, change); + } + + // Call f for all linked documents. + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) { continue } + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) { continue } + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } } + } + propagate(doc, null, true); + } + + // Attach a document to an editor. + function attachDoc(cm, doc) { + if (doc.cm) { throw new Error("This document is already in use.") } + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + setDirectionClass(cm); + cm.options.direction = doc.direction; + if (!cm.options.lineWrapping) { findMaxLine(cm); } + cm.options.mode = doc.modeOption; + regChange(cm); + } + + function setDirectionClass(cm) { + (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); + } + + function directionChanged(cm) { + runInOp(cm, function () { + setDirectionClass(cm); + regChange(cm); + }); + } + + function History(prev) { + // Arrays of change events and selections. Doing something adds an + // event to done and clears undo. Undoing moves events from done + // to undone, redoing moves them in the other direction. + this.done = []; this.undone = []; + this.undoDepth = prev ? prev.undoDepth : Infinity; + // Used to track when changes can be merged into a single undo + // event + this.lastModTime = this.lastSelTime = 0; + this.lastOp = this.lastSelOp = null; + this.lastOrigin = this.lastSelOrigin = null; + // Used by the isClean() method + this.generation = this.maxGeneration = prev ? prev.maxGeneration : 1; + } + + // Create a history change event from an updateDoc-style change + // object. + function historyChangeFromChange(doc, change) { + var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); + return histChange + } + + // Pop all selection events off the end of a history array. Stop at + // a change event. + function clearSelectionEvents(array) { + while (array.length) { + var last = lst(array); + if (last.ranges) { array.pop(); } + else { break } + } + } + + // Find the top change event in the history. Pop off selection + // events that are in the way. + function lastChangeEvent(hist, force) { + if (force) { + clearSelectionEvents(hist.done); + return lst(hist.done) + } else if (hist.done.length && !lst(hist.done).ranges) { + return lst(hist.done) + } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { + hist.done.pop(); + return lst(hist.done) + } + } + + // Register a change in the history. Merges changes that are within + // a single operation, or are close together with an origin that + // allows merging (starting with "+") into a single event. + function addChangeToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur; + var last; + + if ((hist.lastOp == opId || + hist.lastOrigin == change.origin && change.origin && + ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) || + change.origin.charAt(0) == "*")) && + (cur = lastChangeEvent(hist, hist.lastOp == opId))) { + // Merge this change into the last event + last = lst(cur.changes); + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); + } else { + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); + } + } else { + // Can not be merged, start a new event. + var before = lst(hist.done); + if (!before || !before.ranges) + { pushSelectionToHistory(doc.sel, hist.done); } + cur = {changes: [historyChangeFromChange(doc, change)], + generation: hist.generation}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) { + hist.done.shift(); + if (!hist.done[0].ranges) { hist.done.shift(); } + } + } + hist.done.push(selAfter); + hist.generation = ++hist.maxGeneration; + hist.lastModTime = hist.lastSelTime = time; + hist.lastOp = hist.lastSelOp = opId; + hist.lastOrigin = hist.lastSelOrigin = change.origin; + + if (!last) { signal(doc, "historyAdded"); } + } + + function selectionEventCanBeMerged(doc, origin, prev, sel) { + var ch = origin.charAt(0); + return ch == "*" || + ch == "+" && + prev.ranges.length == sel.ranges.length && + prev.somethingSelected() == sel.somethingSelected() && + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) + } + + // Called whenever the selection changes, sets the new selection as + // the pending selection in the history, and pushes the old pending + // selection into the 'done' array when it was significantly + // different (in number of selected ranges, emptiness, or time). + function addSelectionToHistory(doc, sel, opId, options) { + var hist = doc.history, origin = options && options.origin; + + // A new event is started when the previous origin does not match + // the current, or the origins don't allow matching. Origins + // starting with * are always merged, those starting with + are + // merged when similar and close together in time. + if (opId == hist.lastSelOp || + (origin && hist.lastSelOrigin == origin && + (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || + selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) + { hist.done[hist.done.length - 1] = sel; } + else + { pushSelectionToHistory(sel, hist.done); } + + hist.lastSelTime = +new Date; + hist.lastSelOrigin = origin; + hist.lastSelOp = opId; + if (options && options.clearRedo !== false) + { clearSelectionEvents(hist.undone); } + } + + function pushSelectionToHistory(sel, dest) { + var top = lst(dest); + if (!(top && top.ranges && top.equals(sel))) + { dest.push(sel); } + } + + // Used to store marked span information in the history. + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { + if (line.markedSpans) + { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } + ++n; + }); + } + + // When un/re-doing restores text containing marked spans, those + // that have been explicitly cleared should not be restored. + function removeClearedSpans(spans) { + if (!spans) { return null } + var out; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } + else if (out) { out.push(spans[i]); } + } + return !out ? spans : out.length ? out : null + } + + // Retrieve and filter the old marked spans stored in a change event. + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) { return null } + var nw = []; + for (var i = 0; i < change.text.length; ++i) + { nw.push(removeClearedSpans(found[i])); } + return nw + } + + // Used for un/re-doing changes from the history. Combines the + // result of computing the existing spans with the set of spans that + // existed in the history (so that deleting around a span and then + // undoing brings back the span). + function mergeOldSpans(doc, change) { + var old = getOldSpans(doc, change); + var stretched = stretchSpansOverChange(doc, change); + if (!old) { return stretched } + if (!stretched) { return old } + + for (var i = 0; i < old.length; ++i) { + var oldCur = old[i], stretchCur = stretched[i]; + if (oldCur && stretchCur) { + spans: for (var j = 0; j < stretchCur.length; ++j) { + var span = stretchCur[j]; + for (var k = 0; k < oldCur.length; ++k) + { if (oldCur[k].marker == span.marker) { continue spans } } + oldCur.push(span); + } + } else if (stretchCur) { + old[i] = stretchCur; + } + } + return old + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup, instantiateSel) { + var copy = []; + for (var i = 0; i < events.length; ++i) { + var event = events[i]; + if (event.ranges) { + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); + continue + } + var changes = event.changes, newChanges = []; + copy.push({changes: newChanges}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m = (void 0); + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } } } + } + } + return copy + } + + // The 'scroll' parameter given to many of these indicated whether + // the new cursor position should be scrolled into view after + // modifying the selection. + + // If shift is held or the extend flag is set, extends a range to + // include a given position (and optionally a second position). + // Otherwise, simply returns the range between the given positions. + // Used for cursor motion and such. + function extendRange(range, head, other, extend) { + if (extend) { + var anchor = range.anchor; + if (other) { + var posBefore = cmp(head, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; + } + } + return new Range(anchor, head) + } else { + return new Range(other || head, head) + } + } + + // Extend the primary selection range, discard the rest. + function extendSelection(doc, head, other, options, extend) { + if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } + setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, heads, options) { + var out = []; + var extend = doc.cm && (doc.cm.display.shift || doc.extend); + for (var i = 0; i < doc.sel.ranges.length; i++) + { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } + var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex); + setSelection(doc, newSel, options); + } + + // Updates a single range in the selection. + function replaceOneSelection(doc, i, range, options) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options); + } + + // Reset the selection to a single range. + function setSimpleSelection(doc, anchor, head, options) { + setSelection(doc, simpleSelection(anchor, head), options); + } + + // Give beforeSelectionChange handlers a change to influence a + // selection update. + function filterSelectionChange(doc, sel, options) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); } + }, + origin: options && options.origin + }; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } + if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) } + else { return sel } + } + + function setSelectionReplaceHistory(doc, sel, options) { + var done = doc.history.done, last = lst(done); + if (last && last.ranges) { + done[done.length - 1] = sel; + setSelectionNoUndo(doc, sel, options); + } else { + setSelection(doc, sel, options); + } + } + + // Set a new selection. + function setSelection(doc, sel, options) { + setSelectionNoUndo(doc, sel, options); + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + } + + function setSelectionNoUndo(doc, sel, options) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + { sel = filterSelectionChange(doc, sel, options); } + + var bias = options && options.bias || + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + + if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor") + { ensureCursorVisible(doc.cm); } + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) { return } + + doc.sel = sel; + + if (doc.cm) { + doc.cm.curOp.updateInput = 1; + doc.cm.curOp.selectionChanged = true; + signalCursorActivity(doc.cm); + } + signalLater(doc, "cursorActivity", doc); + } + + // Verify that the selection does not partially select any atomic + // marked ranges. + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); + } + + // Return a selection that does not partially select any atomic + // ranges. + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); + var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) { out = sel.ranges.slice(0, i); } + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel + } + + function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { + var line = getLine(doc, pos.line); + if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { + var sp = line.markedSpans[i], m = sp.marker; + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft; + var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight; + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) { break } + else {--i; continue} + } + } + if (!m.atomic) { continue } + + if (oldPos) { + var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); + if (dir < 0 ? preventCursorRight : preventCursorLeft) + { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } + if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) + { return skipAtomicInner(doc, near, pos, dir, mayClear) } + } + + var far = m.find(dir < 0 ? -1 : 1); + if (dir < 0 ? preventCursorLeft : preventCursorRight) + { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null + } + } } + return pos + } + + // Ensure a given position is not inside an atomic range. + function skipAtomic(doc, pos, oldPos, bias, mayClear) { + var dir = bias || 1; + var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || + skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + if (!found) { + doc.cantEdit = true; + return Pos(doc.first, 0) + } + return found + } + + function movePos(doc, pos, dir, line) { + if (dir < 0 && pos.ch == 0) { + if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } + else { return null } + } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { + if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } + else { return null } + } else { + return new Pos(pos.line, pos.ch + dir) + } + } + + function selectAll(cm) { + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); + } + + // UPDATING + + // Allow "beforeChange" event handlers to influence a change + function filterChange(doc, change, update) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + cancel: function () { return obj.canceled = true; } + }; + if (update) { obj.update = function (from, to, text, origin) { + if (from) { obj.from = clipPos(doc, from); } + if (to) { obj.to = clipPos(doc, to); } + if (text) { obj.text = text; } + if (origin !== undefined) { obj.origin = origin; } + }; } + signal(doc, "beforeChange", doc, obj); + if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } + + if (obj.canceled) { + if (doc.cm) { doc.cm.curOp.updateInput = 2; } + return null + } + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} + } + + // Apply a change to a document, and add it to the document's + // history, and propagating it to all linked documents. + function makeChange(doc, change, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } + if (doc.cm.state.suppressEdits) { return } + } + + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change, true); + if (!change) { return } + } + + // Possibly split or suppress the update based on the presence + // of read-only spans in its range. + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); + if (split) { + for (var i = split.length - 1; i >= 0; --i) + { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); } + } else { + makeChangeInner(doc, change); + } + } + + function makeChangeInner(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } + var selAfter = computeSelAfterChange(doc, change); + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + var rebased = []; + + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); + }); + } + + // Revert a change stored in a document's history. + function makeChangeFromHistory(doc, type, allowSelectionOnly) { + var suppress = doc.cm && doc.cm.state.suppressEdits; + if (suppress && !allowSelectionOnly) { return } + + var hist = doc.history, event, selAfter = doc.sel; + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + + // Verify that there is a useable event (so that ctrl-z won't + // needlessly clear selection events) + var i = 0; + for (; i < source.length; i++) { + event = source[i]; + if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) + { break } + } + if (i == source.length) { return } + hist.lastOrigin = hist.lastSelOrigin = null; + + for (;;) { + event = source.pop(); + if (event.ranges) { + pushSelectionToHistory(event, dest); + if (allowSelectionOnly && !event.equals(doc.sel)) { + setSelection(doc, event, {clearRedo: false}); + return + } + selAfter = event; + } else if (suppress) { + source.push(event); + return + } else { break } + } + + // Build up a reverse change object to add to the opposite history + // stack (redo when undoing, and vice versa). + var antiChanges = []; + pushSelectionToHistory(selAfter, dest); + dest.push({changes: antiChanges, generation: hist.generation}); + hist.generation = event.generation || ++hist.maxGeneration; + + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + + var loop = function ( i ) { + var change = event.changes[i]; + change.origin = type; + if (filter && !filterChange(doc, change, false)) { + source.length = 0; + return {} + } + + antiChanges.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc, change) : lst(source); + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); + if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } + var rebased = []; + + // Propagate to the linked documents + linkedDocs(doc, function (doc, sharedHist) { + if (!sharedHist && indexOf(rebased, doc.history) == -1) { + rebaseHist(doc.history, change); + rebased.push(doc.history); + } + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); + }); + }; + + for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { + var returned = loop( i$1 ); + + if ( returned ) return returned.v; + } + } + + // Sub-views need their line numbers shifted when text is added + // above or below them in the parent document. + function shiftDoc(doc, distance) { + if (distance == 0) { return } + doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + ); }), doc.sel.primIndex); + if (doc.cm) { + regChange(doc.cm, doc.first, doc.first - distance, distance); + for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + { regLineChange(doc.cm, l, "gutter"); } + } + } + + // More lower-level change function, handling only a single document + // (not linked ones). + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return + } + if (change.from.line > doc.lastLine()) { return } + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), + text: [change.text[0]], origin: change.origin}; + } + + change.removed = getBetween(doc, change.from, change.to); + + if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } + if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } + else { updateDoc(doc, change, spans); } + setSelectionNoUndo(doc, selAfter, sel_dontScroll); + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + { doc.cantEdit = false; } + } + + // Handle the interaction of a change to a document with the editor + // that this document is part of. + function makeChangeSingleDocInEditor(cm, change, spans) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + + var recomputeMaxLength = false, checkWidthStart = from.line; + if (!cm.options.lineWrapping) { + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + doc.iter(checkWidthStart, to.line + 1, function (line) { + if (line == display.maxLine) { + recomputeMaxLength = true; + return true + } + }); + } + + if (doc.sel.contains(change.from, change.to) > -1) + { signalCursorActivity(cm); } + + updateDoc(doc, change, spans, estimateHeight(cm)); + + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function (line) { + var len = lineLength(line); + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; + recomputeMaxLength = false; + } + }); + if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } + } + + retreatFrontier(doc, from.line); + startWorker(cm, 400); + + var lendiff = change.text.length - (to.line - from.line) - 1; + // Remember that these lines changed, for updating the display + if (change.full) + { regChange(cm); } + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + { regLineChange(cm, from.line, "text"); } + else + { regChange(cm, from.line, to.line + 1, lendiff); } + + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + if (changeHandler || changesHandler) { + var obj = { + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }; + if (changeHandler) { signalLater(cm, "change", cm, obj); } + if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } + } + cm.display.selForContextMenu = null; + } + + function replaceRange(doc, code, from, to, origin) { + var assign; + + if (!to) { to = from; } + if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); } + if (typeof code == "string") { code = doc.splitLines(code); } + makeChange(doc, {from: from, to: to, text: code, origin: origin}); + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSelSingle(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + if (sub.ranges) { + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + for (var j = 0; j < sub.ranges.length; j++) { + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + } + continue + } + for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { + var cur = sub.changes[j$1]; + if (to < cur.from.line) { + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); + } else if (from <= cur.to.line) { + ok = false; + break + } + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); + } + + // Utility for applying a change to a line by handle or number, + // returning the number and optionally registering the line as + // changed. + function changeLine(doc, handle, changeType, op) { + var no = handle, line = handle; + if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } + else { no = lineNo(handle); } + if (no == null) { return null } + if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } + return line + } + + // The document is represented as a BTree consisting of leaves, with + // chunk of lines in them, and branches, with up to ten leaves or + // other branch nodes below them. The top node is always a branch + // node, and is the document object itself (meaning it has + // additional methods and properties). + // + // All nodes have parent links. The tree is used both to go from + // line numbers to line objects, and to go from objects to numbers. + // It also indexes by height, and is used to convert between height + // and line object, and to find the total height of the document. + // + // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html + + function LeafChunk(lines) { + this.lines = lines; + this.parent = null; + var height = 0; + for (var i = 0; i < lines.length; ++i) { + lines[i].parent = this; + height += lines[i].height; + } + this.height = height; + } + + LeafChunk.prototype = { + chunkSize: function() { return this.lines.length }, + + // Remove the n lines at offset 'at'. + removeInner: function(at, n) { + for (var i = at, e = at + n; i < e; ++i) { + var line = this.lines[i]; + this.height -= line.height; + cleanUpLine(line); + signalLater(line, "delete"); + } + this.lines.splice(at, n); + }, + + // Helper used to collapse a small branch into a single leaf. + collapse: function(lines) { + lines.push.apply(lines, this.lines); + }, + + // Insert the given array of lines at offset 'at', count them as + // having the given height. + insertInner: function(at, lines, height) { + this.height += height; + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); + for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; } + }, + + // Used to iterate over a part of the tree. + iterN: function(at, n, op) { + for (var e = at + n; at < e; ++at) + { if (op(this.lines[at])) { return true } } + } + }; + + function BranchChunk(children) { + this.children = children; + var size = 0, height = 0; + for (var i = 0; i < children.length; ++i) { + var ch = children[i]; + size += ch.chunkSize(); height += ch.height; + ch.parent = this; + } + this.size = size; + this.height = height; + this.parent = null; + } + + BranchChunk.prototype = { + chunkSize: function() { return this.size }, + + removeInner: function(at, n) { + this.size -= n; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var rm = Math.min(n, sz - at), oldHeight = child.height; + child.removeInner(at, rm); + this.height -= oldHeight - child.height; + if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } + if ((n -= rm) == 0) { break } + at = 0; + } else { at -= sz; } + } + // If the result is smaller than 25 lines, ensure that it is a + // single leaf node. + if (this.size - n < 25 && + (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { + var lines = []; + this.collapse(lines); + this.children = [new LeafChunk(lines)]; + this.children[0].parent = this; + } + }, + + collapse: function(lines) { + for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); } + }, + + insertInner: function(at, lines, height) { + this.size += lines.length; + this.height += height; + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at <= sz) { + child.insertInner(at, lines, height); + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var remaining = child.lines.length % 25 + 25; + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); + child.height -= leaf.height; + this.children.splice(++i, 0, leaf); + leaf.parent = this; + } + child.lines = child.lines.slice(0, remaining); + this.maybeSpill(); + } + break + } + at -= sz; + } + }, + + // When a node has grown, check whether it should be split. + maybeSpill: function() { + if (this.children.length <= 10) { return } + var me = this; + do { + var spilled = me.children.splice(me.children.length - 5, 5); + var sibling = new BranchChunk(spilled); + if (!me.parent) { // Become the parent node + var copy = new BranchChunk(me.children); + copy.parent = me; + me.children = [copy, sibling]; + me = copy; + } else { + me.size -= sibling.size; + me.height -= sibling.height; + var myIndex = indexOf(me.parent.children, me); + me.parent.children.splice(myIndex + 1, 0, sibling); + } + sibling.parent = me.parent; + } while (me.children.length > 10) + me.parent.maybeSpill(); + }, + + iterN: function(at, n, op) { + for (var i = 0; i < this.children.length; ++i) { + var child = this.children[i], sz = child.chunkSize(); + if (at < sz) { + var used = Math.min(n, sz - at); + if (child.iterN(at, used, op)) { return true } + if ((n -= used) == 0) { break } + at = 0; + } else { at -= sz; } + } + } + }; + + // Line widgets are block elements displayed above or below a line. + + var LineWidget = function(doc, node, options) { + if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) + { this[opt] = options[opt]; } } } + this.doc = doc; + this.node = node; + }; + + LineWidget.prototype.clear = function () { + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); + if (no == null || !ws) { return } + for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } } + if (!ws.length) { line.widgets = null; } + var height = widgetHeight(this); + updateLineHeight(line, Math.max(0, line.height - height)); + if (cm) { + runInOp(cm, function () { + adjustScrollWhenAboveVisible(cm, line, -height); + regLineChange(cm, no, "widget"); + }); + signalLater(cm, "lineWidgetCleared", cm, this, no); + } + }; + + LineWidget.prototype.changed = function () { + var this$1 = this; + + var oldH = this.height, cm = this.doc.cm, line = this.line; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) { return } + if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); } + if (cm) { + runInOp(cm, function () { + cm.curOp.forceUpdate = true; + adjustScrollWhenAboveVisible(cm, line, diff); + signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); + }); + } + }; + eventMixin(LineWidget); + + function adjustScrollWhenAboveVisible(cm, line, diff) { + if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) + { addToScrollTop(cm, diff); } + } + + function addLineWidget(doc, handle, node, options) { + var widget = new LineWidget(doc, node, options); + var cm = doc.cm; + if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } + changeLine(doc, handle, "widget", function (line) { + var widgets = line.widgets || (line.widgets = []); + if (widget.insertAt == null) { widgets.push(widget); } + else { widgets.splice(Math.min(widgets.length, Math.max(0, widget.insertAt)), 0, widget); } + widget.line = line; + if (cm && !lineIsHidden(doc, line)) { + var aboveVisible = heightAtLine(line) < doc.scrollTop; + updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) { addToScrollTop(cm, widget.height); } + cm.curOp.forceUpdate = true; + } + return true + }); + if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); } + return widget + } + + // TEXTMARKERS + + // Created with markText and setBookmark methods. A TextMarker is a + // handle that can be used to clear or find a marked position in the + // document. Line objects hold arrays (markedSpans) containing + // {from, to, marker} object pointing to such marker objects, and + // indicating that such a marker is present on that line. Multiple + // lines may point to the same marker when it spans across lines. + // The spans will have null for their from/to properties when the + // marker continues beyond the start/end of the line. Markers have + // links back to the lines they currently touch. + + // Collapsed markers have unique ids, in order to be able to order + // them, which is needed for uniquely determining an outer marker + // when they overlap (they may nest, but not partially overlap). + var nextMarkerId = 0; + + var TextMarker = function(doc, type) { + this.lines = []; + this.type = type; + this.doc = doc; + this.id = ++nextMarkerId; + }; + + // Clear the marker. + TextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + var cm = this.doc.cm, withOp = cm && !cm.curOp; + if (withOp) { startOperation(cm); } + if (hasHandler(this, "clear")) { + var found = this.find(); + if (found) { signalLater(this, "clear", found.from, found.to); } + } + var min = null, max = null; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); } + else if (cm) { + if (span.to != null) { max = lineNo(line); } + if (span.from != null) { min = lineNo(line); } + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + { updateLineHeight(line, textHeight(cm.display)); } + } + if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { + var visual = visualLine(this.lines[i$1]), len = lineLength(visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; + } + } } + + if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } + this.lines.length = 0; + this.explicitlyCleared = true; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) { reCheckSelection(cm.doc); } + } + if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } + if (withOp) { endOperation(cm); } + if (this.parent) { this.parent.clear(); } + }; + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + TextMarker.prototype.find = function (side, lineObj) { + if (side == null && this.type == "bookmark") { side = 1; } + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) { return from } + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) { return to } + } + } + return from && {from: from, to: to} + }; + + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + TextMarker.prototype.changed = function () { + var this$1 = this; + + var pos = this.find(-1, true), widget = this, cm = this.doc.cm; + if (!pos || !cm) { return } + runInOp(cm, function () { + var line = pos.line, lineN = lineNo(pos.line); + var view = findViewForLine(cm, lineN); + if (view) { + clearLineMeasurementCacheFor(view); + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + } + cm.curOp.updateMaxLine = true; + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + var oldHeight = widget.height; + widget.height = null; + var dHeight = widgetHeight(widget) - oldHeight; + if (dHeight) + { updateLineHeight(line, line.height + dHeight); } + } + signalLater(cm, "markerChanged", cm, this$1); + }); + }; + + TextMarker.prototype.attachLine = function (line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } + } + this.lines.push(line); + }; + + TextMarker.prototype.detachLine = function (line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + eventMixin(TextMarker); + + // Create a marker, wire it up to the right lines, and + function markText(doc, from, to, options, type) { + // Shared markers (across linked documents) are handled separately + // (markTextShared will call out to this again, once per + // document). + if (options && options.shared) { return markTextShared(doc, from, to, options, type) } + // Ensure we are in an operation. + if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } + + var marker = new TextMarker(doc, type), diff = cmp(from, to); + if (options) { copyObj(options, marker, false); } + // Don't connect empty markers unless clearWhenEmpty is false + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) + { return marker } + if (marker.replacedWith) { + // Showing up as a widget implies collapsed (widget replaces text) + marker.collapsed = true; + marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } + if (options.insertLeft) { marker.widgetNode.insertLeft = true; } + } + if (marker.collapsed) { + if (conflictingCollapsedRange(doc, from.line, from, to, marker) || + from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) + { throw new Error("Inserting collapsed marker partially overlapping an existing one") } + seeCollapsedSpans(); + } + + if (marker.addToHistory) + { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } + + var curLine = from.line, cm = doc.cm, updateMaxLine; + doc.iter(curLine, to.line + 1, function (line) { + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) + { updateMaxLine = true; } + if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } + addMarkedSpan(line, new MarkedSpan(marker, + curLine == from.line ? from.ch : null, + curLine == to.line ? to.ch : null), doc.cm && doc.cm.curOp); + ++curLine; + }); + // lineIsHidden depends on the presence of the spans, so needs a second pass + if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { + if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } + }); } + + if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } + + if (marker.readOnly) { + seeReadOnlySpans(); + if (doc.history.done.length || doc.history.undone.length) + { doc.clearHistory(); } + } + if (marker.collapsed) { + marker.id = ++nextMarkerId; + marker.atomic = true; + } + if (cm) { + // Sync editor state + if (updateMaxLine) { cm.curOp.updateMaxLine = true; } + if (marker.collapsed) + { regChange(cm, from.line, to.line + 1); } + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) + { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } + if (marker.atomic) { reCheckSelection(cm.doc); } + signalLater(cm, "markerAdded", cm, marker); + } + return marker + } + + // SHARED TEXTMARKERS + + // A shared marker spans multiple linked documents. It is + // implemented as a meta-marker-object controlling multiple normal + // markers. + var SharedTextMarker = function(markers, primary) { + this.markers = markers; + this.primary = primary; + for (var i = 0; i < markers.length; ++i) + { markers[i].parent = this; } + }; + + SharedTextMarker.prototype.clear = function () { + if (this.explicitlyCleared) { return } + this.explicitlyCleared = true; + for (var i = 0; i < this.markers.length; ++i) + { this.markers[i].clear(); } + signalLater(this, "clear"); + }; + + SharedTextMarker.prototype.find = function (side, lineObj) { + return this.primary.find(side, lineObj) + }; + eventMixin(SharedTextMarker); + + function markTextShared(doc, from, to, options, type) { + options = copyObj(options); + options.shared = false; + var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.widgetNode; + linkedDocs(doc, function (doc) { + if (widget) { options.widgetNode = widget.cloneNode(true); } + markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + for (var i = 0; i < doc.linked.length; ++i) + { if (doc.linked[i].isParent) { return } } + primary = lst(markers); + }); + return new SharedTextMarker(markers, primary) + } + + function findSharedMarkers(doc) { + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) + } + + function copySharedMarkers(doc, markers) { + for (var i = 0; i < markers.length; i++) { + var marker = markers[i], pos = marker.find(); + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + if (cmp(mFrom, mTo)) { + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); + marker.markers.push(subMark); + subMark.parent = marker; + } + } + } + + function detachSharedMarkers(markers) { + var loop = function ( i ) { + var marker = markers[i], linked = [marker.primary.doc]; + linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); + for (var j = 0; j < marker.markers.length; j++) { + var subMarker = marker.markers[j]; + if (indexOf(linked, subMarker.doc) == -1) { + subMarker.parent = null; + marker.markers.splice(j--, 1); + } + } + }; + + for (var i = 0; i < markers.length; i++) loop( i ); + } + + var nextDocId = 0; + var Doc = function(text, mode, firstLine, lineSep, direction) { + if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } + if (firstLine == null) { firstLine = 0; } + + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.cleanGeneration = 1; + this.modeFrontier = this.highlightFrontier = firstLine; + var start = Pos(firstLine, 0); + this.sel = simpleSelection(start); + this.history = new History(null); + this.id = ++nextDocId; + this.modeOption = mode; + this.lineSep = lineSep; + this.direction = (direction == "rtl") ? "rtl" : "ltr"; + this.extend = false; + + if (typeof text == "string") { text = this.splitLines(text); } + updateDoc(this, {from: start, to: start, text: text}); + setSelection(this, simpleSelection(start), sel_dontScroll); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, + // Iterate over the document. Supports two forms -- with only one + // argument, it calls that for each line in the document. With + // three, it iterates over the range given by the first two (with + // the second being non-inclusive). + iter: function(from, to, op) { + if (op) { this.iterN(from - this.first, to - from, op); } + else { this.iterN(this.first, this.first + this.size, from); } + }, + + // Non-public interface for adding and removing lines. + insert: function(at, lines) { + var height = 0; + for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + // From here, the methods are part of the public interface. Most + // are also available from CodeMirror (editor) instances. + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) { return lines } + return lines.join(lineSep || this.lineSeparator()) + }, + setValue: docMethodOp(function(code) { + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), + text: this.splitLines(code), origin: "setValue", full: true}, true); + if (this.cm) { scrollToCoords(this.cm, 0, 0); } + setSelection(this, simpleSelection(top), sel_dontScroll); + }), + replaceRange: function(code, from, to, origin) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to, origin); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) { return lines } + if (lineSep === '') { return lines.join('') } + return lines.join(lineSep || this.lineSeparator()) + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + + getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, + getLineNumber: function(line) {return lineNo(line)}, + + getLineHandleVisualStart: function(line) { + if (typeof line == "number") { line = getLine(this, line); } + return visualLine(line) + }, + + lineCount: function() {return this.size}, + firstLine: function() {return this.first}, + lastLine: function() {return this.first + this.size - 1}, + + clipPos: function(pos) {return clipPos(this, pos)}, + + getCursor: function(start) { + var range = this.sel.primary(), pos; + if (start == null || start == "head") { pos = range.head; } + else if (start == "anchor") { pos = range.anchor; } + else if (start == "end" || start == "to" || start === false) { pos = range.to(); } + else { pos = range.from(); } + return pos + }, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, + + setCursor: docMethodOp(function(line, ch, options) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + }), + setSelection: docMethodOp(function(anchor, head, options) { + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + }), + extendSelection: docMethodOp(function(head, other, options) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + }), + extendSelections: docMethodOp(function(heads, options) { + extendSelections(this, clipPosArray(this, heads), options); + }), + extendSelectionsBy: docMethodOp(function(f, options) { + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); + }), + setSelections: docMethodOp(function(ranges, primary, options) { + if (!ranges.length) { return } + var out = []; + for (var i = 0; i < ranges.length; i++) + { out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head || ranges[i].anchor)); } + if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } + setSelection(this, normalizeSelection(this.cm, out, primary), options); + }), + addSelection: docMethodOp(function(anchor, head, options) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options); + }), + + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) { return lines } + else { return lines.join(lineSep || this.lineSeparator()) } + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); } + parts[i] = sel; + } + return parts + }, + replaceSelection: function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + { dup[i] = code; } + this.replaceSelections(dup, collapse, origin || "+input"); + }, + replaceSelections: docMethodOp(function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) + { makeChange(this, changes[i$1]); } + if (newSel) { setSelectionReplaceHistory(this, newSel); } + else if (this.cm) { ensureCursorVisible(this.cm); } + }), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + + setExtending: function(val) {this.extend = val;}, + getExtending: function() {return this.extend}, + + historySize: function() { + var hist = this.history, done = 0, undone = 0; + for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } + for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } + return {undo: done, redo: undone} + }, + clearHistory: function() { + var this$1 = this; + + this.history = new History(this.history); + linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true); + }, + + markClean: function() { + this.cleanGeneration = this.changeGeneration(true); + }, + changeGeneration: function(forceSplit) { + if (forceSplit) + { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } + return this.history.generation + }, + isClean: function (gen) { + return this.history.generation == (gen || this.cleanGeneration) + }, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)} + }, + setHistory: function(histData) { + var hist = this.history = new History(this.history); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + }, + + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", function (line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) { line.gutterMarkers = null; } + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + var this$1 = this; + + this.iter(function (line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this$1, line, "gutter", function () { + line.gutterMarkers[gutterID] = null; + if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } + return true + }); + } + }); + }), + + lineInfo: function(line) { + var n; + if (typeof line == "number") { + if (!isLine(this, line)) { return null } + n = line; + line = getLine(this, line); + if (!line) { return null } + } else { + n = lineNo(line); + if (n == null) { return null } + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + + addLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + if (!line[prop]) { line[prop] = cls; } + else if (classTest(cls).test(line[prop])) { return false } + else { line[prop] += " " + cls; } + return true + }) + }), + removeLineClass: docMethodOp(function(handle, where, cls) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { + var prop = where == "text" ? "textClass" + : where == "background" ? "bgClass" + : where == "gutter" ? "gutterClass" : "wrapClass"; + var cur = line[prop]; + if (!cur) { return false } + else if (cls == null) { line[prop] = null; } + else { + var found = cur.match(classTest(cls)); + if (!found) { return false } + var end = found.index + found[0].length; + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + } + return true + }) + }), + + addLineWidget: docMethodOp(function(handle, node, options) { + return addLineWidget(this, handle, node, options) + }), + removeLineWidget: function(widget) { widget.clear(); }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") + }, + setBookmark: function(pos, options) { + var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + insertLeft: options && options.insertLeft, + clearWhenEmpty: false, shared: options && options.shared, + handleMouseEvents: options && options.handleMouseEvents}; + pos = clipPos(this, pos); + return markText(this, pos, pos, realOpts, "bookmark") + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) { for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + { markers.push(span.marker.parent || span.marker); } + } } + return markers + }, + findMarks: function(from, to, filter) { + from = clipPos(this, from); to = clipPos(this, to); + var found = [], lineNo = from.line; + this.iter(from.line, to.line + 1, function (line) { + var spans = line.markedSpans; + if (spans) { for (var i = 0; i < spans.length; i++) { + var span = spans[i]; + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || + span.from == null && lineNo != from.line || + span.from != null && lineNo == to.line && span.from >= to.ch) && + (!filter || filter(span.marker))) + { found.push(span.marker.parent || span.marker); } + } } + ++lineNo; + }); + return found + }, + getAllMarks: function() { + var markers = []; + this.iter(function (line) { + var sps = line.markedSpans; + if (sps) { for (var i = 0; i < sps.length; ++i) + { if (sps[i].from != null) { markers.push(sps[i].marker); } } } + }); + return markers + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; + this.iter(function (line) { + var sz = line.text.length + sepSize; + if (sz > off) { ch = off; return true } + off -= sz; + ++lineNo; + }); + return clipPos(this, Pos(lineNo, ch)) + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) { return 0 } + var sepSize = this.lineSeparator().length; + this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value + index += line.text.length + sepSize; + }); + return index + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), + this.modeOption, this.first, this.lineSep, this.direction); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = this.sel; + doc.extend = false; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc + }, + + linkedDoc: function(options) { + if (!options) { options = {}; } + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) { from = options.from; } + if (options.to != null && options.to < to) { to = options.to; } + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); + if (options.sharedHist) { copy.history = this.history + ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + copySharedMarkers(copy, findSharedMarkers(this)); + return copy + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) { other = other.doc; } + if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) { continue } + this.linked.splice(i, 1); + other.unlinkDoc(this); + detachSharedMarkers(findSharedMarkers(this)); + break + } } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); + other.history = new History(null); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, + + splitLines: function(str) { + if (this.lineSep) { return str.split(this.lineSep) } + return splitLinesAuto(str) + }, + lineSeparator: function() { return this.lineSep || "\n" }, + + setDirection: docMethodOp(function (dir) { + if (dir != "rtl") { dir = "ltr"; } + if (dir == this.direction) { return } + this.direction = dir; + this.iter(function (line) { return line.order = null; }); + if (this.cm) { directionChanged(this.cm); } + }) + }); + + // Public alias. + Doc.prototype.eachLine = Doc.prototype.iter; + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + + function onDrop(e) { + var cm = this; + clearDragCursor(cm); + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) + { return } + e_preventDefault(e); + if (ie) { lastDrop = +new Date; } + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.isReadOnly()) { return } + // Might be a file drop, in which case we simply extract the text + // and insert it. + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var markAsReadAndPasteIfAllFilesAreRead = function () { + if (++read == n) { + operation(cm, function () { + pos = clipPos(cm.doc, pos); + var change = {from: pos, to: pos, + text: cm.doc.splitLines( + text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())), + origin: "paste"}; + makeChange(cm.doc, change); + setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change)))); + })(); + } + }; + var readTextFromFile = function (file, i) { + if (cm.options.allowDropFileTypes && + indexOf(cm.options.allowDropFileTypes, file.type) == -1) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + var reader = new FileReader; + reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); }; + reader.onload = function () { + var content = reader.result; + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { + markAsReadAndPasteIfAllFilesAreRead(); + return + } + text[i] = content; + markAsReadAndPasteIfAllFilesAreRead(); + }; + reader.readAsText(file); + }; + for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); } + } else { // Normal drop + // Don't do a replace if the drop happened inside of the selected text. + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { + cm.state.draggingText(e); + // Ensure the editor is re-focused + setTimeout(function () { return cm.display.input.focus(); }, 20); + return + } + try { + var text$1 = e.dataTransfer.getData("Text"); + if (text$1) { + var selected; + if (cm.state.draggingText && !cm.state.draggingText.copy) + { selected = cm.listSelections(); } + setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) + { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } + cm.replaceSelection(text$1, "around", "paste"); + cm.display.input.focus(); + } + } + catch(e$1){} + } + } + + function onDragStart(cm, e) { + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } + + e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove"; + + // Use dummy image instead of default browsers image. + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + if (presto) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } + e.dataTransfer.setDragImage(img, 0, 0); + if (presto) { img.parentNode.removeChild(img); } + } + } + + function onDragOver(cm, e) { + var pos = posFromMouse(cm, e); + if (!pos) { return } + var frag = document.createDocumentFragment(); + drawSelectionCursor(cm, pos, frag); + if (!cm.display.dragCursor) { + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + } + removeChildrenAndAdd(cm.display.dragCursor, frag); + } + + function clearDragCursor(cm) { + if (cm.display.dragCursor) { + cm.display.lineSpace.removeChild(cm.display.dragCursor); + cm.display.dragCursor = null; + } + } + + // These must be handled carefully, because naively registering a + // handler for each editor will cause the editors to never be + // garbage collected. + + function forEachCodeMirror(f) { + if (!document.getElementsByClassName) { return } + var byClass = document.getElementsByClassName("CodeMirror"), editors = []; + for (var i = 0; i < byClass.length; i++) { + var cm = byClass[i].CodeMirror; + if (cm) { editors.push(cm); } + } + if (editors.length) { editors[0].operation(function () { + for (var i = 0; i < editors.length; i++) { f(editors[i]); } + }); } + } + + var globalsRegistered = false; + function ensureGlobalHandlers() { + if (globalsRegistered) { return } + registerGlobalHandlers(); + globalsRegistered = true; + } + function registerGlobalHandlers() { + // When the window resizes, we need to refresh active editors. + var resizeTimer; + on(window, "resize", function () { + if (resizeTimer == null) { resizeTimer = setTimeout(function () { + resizeTimer = null; + forEachCodeMirror(onResize); + }, 100); } + }); + // When the window loses focus, we want to show the editor as blurred + on(window, "blur", function () { return forEachCodeMirror(onBlur); }); + } + // Called when the window resizes + function onResize(cm) { + var d = cm.display; + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.scrollbarsClipped = false; + cm.setSize(); + } + + var keyNames = { + 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", + 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", + 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", + 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", + 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", + 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" + }; + + // Number keys + for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } + // Alphabetic keys + for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } + // Function keys + for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } + + var keyMap = {}; + + keyMap.basic = { + "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", + "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", + "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" + }; + // Note that the save and find-related commands aren't defined by + // default. User code or addons can define them. Unknown commands + // are simply ignored. + keyMap.pcDefault = { + "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", + "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", + "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", + "fallthrough": "basic" + }; + // Very basic readline/emacs-style bindings, which are standard on Mac. + keyMap.emacsy = { + "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", + "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", + "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", + "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" + }; + keyMap.macDefault = { + "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", + "fallthrough": ["basic", "emacsy"] + }; + keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; + + // KEYMAP DISPATCH + + function normalizeKeyName(name) { + var parts = name.split(/-(?!$)/); + name = parts[parts.length - 1]; + var alt, ctrl, shift, cmd; + for (var i = 0; i < parts.length - 1; i++) { + var mod = parts[i]; + if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } + else if (/^a(lt)?$/i.test(mod)) { alt = true; } + else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } + else if (/^s(hift)?$/i.test(mod)) { shift = true; } + else { throw new Error("Unrecognized modifier name: " + mod) } + } + if (alt) { name = "Alt-" + name; } + if (ctrl) { name = "Ctrl-" + name; } + if (cmd) { name = "Cmd-" + name; } + if (shift) { name = "Shift-" + name; } + return name + } + + // This is a kludge to keep keymaps mostly working as raw objects + // (backwards compatibility) while at the same time support features + // like normalization and multi-stroke key bindings. It compiles a + // new normalized keymap, and then updates the old object to reflect + // this. + function normalizeKeyMap(keymap) { + var copy = {}; + for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { + var value = keymap[keyname]; + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } + if (value == "...") { delete keymap[keyname]; continue } + + var keys = map(keyname.split(" "), normalizeKeyName); + for (var i = 0; i < keys.length; i++) { + var val = (void 0), name = (void 0); + if (i == keys.length - 1) { + name = keys.join(" "); + val = value; + } else { + name = keys.slice(0, i + 1).join(" "); + val = "..."; + } + var prev = copy[name]; + if (!prev) { copy[name] = val; } + else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } + } + delete keymap[keyname]; + } } + for (var prop in copy) { keymap[prop] = copy[prop]; } + return keymap + } + + function lookupKey(key, map, handle, context) { + map = getKeyMap(map); + var found = map.call ? map.call(key, context) : map[key]; + if (found === false) { return "nothing" } + if (found === "...") { return "multi" } + if (found != null && handle(found)) { return "handled" } + + if (map.fallthrough) { + if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") + { return lookupKey(key, map.fallthrough, handle, context) } + for (var i = 0; i < map.fallthrough.length; i++) { + var result = lookupKey(key, map.fallthrough[i], handle, context); + if (result) { return result } + } + } + } + + // Modifier key presses don't count as 'real' key presses for the + // purpose of keymap fallthrough. + function isModifierKey(value) { + var name = typeof value == "string" ? value : keyNames[value.keyCode]; + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" + } + + function addModifierNames(name, event, noShift) { + var base = name; + if (event.altKey && base != "Alt") { name = "Alt-" + name; } + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; } + if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } + return name + } + + // Look up the name of a key as indicated by an event object. + function keyName(event, noShift) { + if (presto && event.keyCode == 34 && event["char"]) { return false } + var name = keyNames[event.keyCode]; + if (name == null || event.altGraphKey) { return false } + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) { name = event.code; } + return addModifierNames(name, event, noShift) + } + + function getKeyMap(val) { + return typeof val == "string" ? keyMap[val] : val + } + + // Helper for deleting text near the selection(s), used to implement + // backspace, delete, and similar functionality. + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + // Build up a set of ranges to kill first, merging overlapping + // ranges. + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break + } + } + kill.push(toKill); + } + // Next, remove those actual ranges. + runInOp(cm, function () { + for (var i = kill.length - 1; i >= 0; i--) + { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } + ensureCursorVisible(cm); + }); + } + + function moveCharLogically(line, ch, dir) { + var target = skipExtendingChars(line.text, ch + dir, dir); + return target < 0 || target > line.text.length ? null : target + } + + function moveLogically(line, start, dir) { + var ch = moveCharLogically(line, start.ch, dir); + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") + } + + function endOfLine(visually, cm, lineObj, lineNo, dir) { + if (visually) { + if (cm.doc.direction == "rtl") { dir = -dir; } + var order = getOrder(lineObj, cm.doc.direction); + if (order) { + var part = dir < 0 ? lst(order) : order[0]; + var moveInStorageOrder = (dir < 0) == (part.level == 1); + var sticky = moveInStorageOrder ? "after" : "before"; + var ch; + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (part.level > 0 || cm.doc.direction == "rtl") { + var prep = prepareMeasureForLine(cm, lineObj); + ch = dir < 0 ? lineObj.text.length - 1 : 0; + var targetTop = measureCharPrepared(cm, prep, ch).top; + ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); + if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } + } else { ch = dir < 0 ? part.to : part.from; } + return new Pos(lineNo, ch, sticky) + } + } + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") + } + + function moveVisually(cm, line, start, dir) { + var bidi = getOrder(line, cm.doc.direction); + if (!bidi) { return moveLogically(line, start, dir) } + if (start.ch >= line.text.length) { + start.ch = line.text.length; + start.sticky = "before"; + } else if (start.ch <= 0) { + start.ch = 0; + start.sticky = "after"; + } + var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; + if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { + // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, + // nothing interesting happens. + return moveLogically(line, start, dir) + } + + var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; + var prep; + var getWrappedLineExtent = function (ch) { + if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } + prep = prep || prepareMeasureForLine(cm, line); + return wrappedLineExtentChar(cm, line, prep, ch) + }; + var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); + + if (cm.doc.direction == "rtl" || part.level == 1) { + var moveInStorageOrder = (part.level == 1) == (dir < 0); + var ch = mv(start, moveInStorageOrder ? 1 : -1); + if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { + // Case 2: We move within an rtl part or in an rtl editor on the same visual line + var sticky = moveInStorageOrder ? "before" : "after"; + return new Pos(start.line, ch, sticky) + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { + var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after"); }; + + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { + var part = bidi[partPos]; + var moveInStorageOrder = (dir > 0) == (part.level != 1); + var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); + if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } + ch = moveInStorageOrder ? part.from : mv(part.to, -1); + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } + } + }; + + // Case 3a: Look for other bidi parts on the same visual line + var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); + if (res) { return res } + + // Case 3b: Look for other bidi parts on the next visual line + var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); + if (res) { return res } + } + + // Case 4: Nowhere to move + return null + } + + // Commands are parameter-less actions that can be performed on an + // editor, mostly used for keybindings. + var commands = { + selectAll: selectAll, + singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, + killLine: function (cm) { return deleteNearSelection(cm, function (range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + { return {from: range.head, to: Pos(range.head.line + 1, 0)} } + else + { return {from: range.head, to: Pos(range.head.line, len)} } + } else { + return {from: range.from(), to: range.to()} + } + }); }, + deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + }); }); }, + delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ + from: Pos(range.from().line, 0), to: range.from() + }); }); }, + delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()} + }); }, + delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos } + }); }, + undo: function (cm) { return cm.undo(); }, + redo: function (cm) { return cm.redo(); }, + undoSelection: function (cm) { return cm.undoSelection(); }, + redoSelection: function (cm) { return cm.redoSelection(); }, + goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, + goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, + goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, + {origin: "+move", bias: 1} + ); }, + goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, + {origin: "+move", bias: 1} + ); }, + goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, + {origin: "+move", bias: -1} + ); }, + goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move); }, + goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move); }, + goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { + var top = cm.cursorCoords(range.head, "div").top + 5; + var pos = cm.coordsChar({left: 0, top: top}, "div"); + if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } + return pos + }, sel_move); }, + goLineUp: function (cm) { return cm.moveV(-1, "line"); }, + goLineDown: function (cm) { return cm.moveV(1, "line"); }, + goPageUp: function (cm) { return cm.moveV(-1, "page"); }, + goPageDown: function (cm) { return cm.moveV(1, "page"); }, + goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, + goCharRight: function (cm) { return cm.moveH(1, "char"); }, + goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, + goColumnRight: function (cm) { return cm.moveH(1, "column"); }, + goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, + goGroupRight: function (cm) { return cm.moveH(1, "group"); }, + goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, + goWordRight: function (cm) { return cm.moveH(1, "word"); }, + delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); }, + delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, + delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, + delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, + delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, + delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, + indentAuto: function (cm) { return cm.indentSelection("smart"); }, + indentMore: function (cm) { return cm.indentSelection("add"); }, + indentLess: function (cm) { return cm.indentSelection("subtract"); }, + insertTab: function (cm) { return cm.replaceSelection("\t"); }, + insertSoftTab: function (cm) { + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].from(); + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); + spaces.push(spaceStr(tabSize - col % tabSize)); + } + cm.replaceSelections(spaces); + }, + defaultTab: function (cm) { + if (cm.somethingSelected()) { cm.indentSelection("add"); } + else { cm.execCommand("insertTab"); } + }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. + transposeChars: function (cm) { return runInOp(cm, function () { + var ranges = cm.listSelections(), newSel = []; + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) { continue } + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + if (line) { + if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1); + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose"); + } else if (cur.line > cm.doc.first) { + var prev = getLine(cm.doc, cur.line - 1).text; + if (prev) { + cur = new Pos(cur.line, 1); + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); + } + } + } + newSel.push(new Range(cur, cur)); + } + cm.setSelections(newSel); + }); }, + newlineAndIndent: function (cm) { return runInOp(cm, function () { + var sels = cm.listSelections(); + for (var i = sels.length - 1; i >= 0; i--) + { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } + sels = cm.listSelections(); + for (var i$1 = 0; i$1 < sels.length; i$1++) + { cm.indentLine(sels[i$1].from().line, null, true); } + ensureCursorVisible(cm); + }); }, + openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, + toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } + }; + + + function lineStart(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLine(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, visual, lineN, 1) + } + function lineEnd(cm, lineN) { + var line = getLine(cm.doc, lineN); + var visual = visualLineEnd(line); + if (visual != line) { lineN = lineNo(visual); } + return endOfLine(true, cm, line, lineN, -1) + } + function lineStartSmart(cm, pos) { + var start = lineStart(cm, pos.line); + var line = getLine(cm.doc, start.line); + var order = getOrder(line, cm.doc.direction); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(start.ch, line.text.search(/\S/)); + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) + } + return start + } + + // Run a handler that was bound to a key. + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) { return false } + } + // Ensure previous input has been read, so that the handler sees a + // consistent view of the document + cm.display.input.ensurePolled(); + var prevShift = cm.display.shift, done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + if (dropShift) { cm.display.shift = false; } + done = bound(cm) != Pass; + } finally { + cm.display.shift = prevShift; + cm.state.suppressEdits = false; + } + return done + } + + function lookupKeyForEditor(cm, name, handle) { + for (var i = 0; i < cm.state.keyMaps.length; i++) { + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); + if (result) { return result } + } + return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) + || lookupKey(name, cm.options.keyMap, handle, cm) + } + + // Note that, despite the name, this function is also used to check + // for bound mouse clicks. + + var stopSeq = new Delayed; + + function dispatchKey(cm, name, e, handle) { + var seq = cm.state.keySeq; + if (seq) { + if (isModifierKey(name)) { return "handled" } + if (/\'$/.test(name)) + { cm.state.keySeq = null; } + else + { stopSeq.set(50, function () { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null; + cm.display.input.reset(); + } + }); } + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true } + } + return dispatchKeyInner(cm, name, e, handle) + } + + function dispatchKeyInner(cm, name, e, handle) { + var result = lookupKeyForEditor(cm, name, handle); + + if (result == "multi") + { cm.state.keySeq = name; } + if (result == "handled") + { signalLater(cm, "keyHandled", cm, name, e); } + + if (result == "handled" || result == "multi") { + e_preventDefault(e); + restartBlink(cm); + } + + return !!result + } + + // Handle a key from the keydown event. + function handleKeyBinding(cm, e) { + var name = keyName(e, true); + if (!name) { return false } + + if (e.shiftKey && !cm.state.keySeq) { + // First try to resolve full name (including 'Shift-'). Failing + // that, see if there is a cursor-motion command (starting with + // 'go') bound to the keyname without 'Shift-'. + return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) + || dispatchKey(cm, name, e, function (b) { + if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) + { return doHandleBinding(cm, b) } + }) + } else { + return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) + } + } + + // Handle a key from the keypress event + function handleCharBinding(cm, e, ch) { + return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) + } + + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + cm.curOp.focus = activeElt(); + if (signalDOMEvent(cm, e)) { return } + // IE does strange things with escape. + if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } + var code = e.keyCode; + cm.display.shift = code == 16 || e.shiftKey; + var handled = handleKeyBinding(cm, e); + if (presto) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) + { cm.replaceSelection("", null, "cut"); } + } + if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand) + { document.execCommand("cut"); } + + // Turn mouse into crosshair when Alt is held on Mac. + if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) + { showCrossHair(cm); } + } + + function showCrossHair(cm) { + var lineDiv = cm.display.lineDiv; + addClass(lineDiv, "CodeMirror-crosshair"); + + function up(e) { + if (e.keyCode == 18 || !e.altKey) { + rmClass(lineDiv, "CodeMirror-crosshair"); + off(document, "keyup", up); + off(document, "mouseover", up); + } + } + on(document, "keyup", up); + on(document, "mouseover", up); + } + + function onKeyUp(e) { + if (e.keyCode == 16) { this.doc.sel.shift = false; } + signalDOMEvent(this, e); + } + + function onKeyPress(e) { + var cm = this; + if (e.target && e.target != cm.display.input.getField()) { return } + if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } + var keyCode = e.keyCode, charCode = e.charCode; + if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} + if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") { return } + if (handleCharBinding(cm, e, ch)) { return } + cm.display.input.onKeyPress(e); + } + + var DOUBLECLICK_DELAY = 400; + + var PastClick = function(time, pos, button) { + this.time = time; + this.pos = pos; + this.button = button; + }; + + PastClick.prototype.compare = function (time, pos, button) { + return this.time + DOUBLECLICK_DELAY > time && + cmp(pos, this.pos) == 0 && button == this.button + }; + + var lastClick, lastDoubleClick; + function clickRepeat(pos, button) { + var now = +new Date; + if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { + lastClick = lastDoubleClick = null; + return "triple" + } else if (lastClick && lastClick.compare(now, pos, button)) { + lastDoubleClick = new PastClick(now, pos, button); + lastClick = null; + return "double" + } else { + lastClick = new PastClick(now, pos, button); + lastDoubleClick = null; + return "single" + } + } + + // A mouse down can be a single click, double click, triple click, + // start of selection drag, start of text drag, new cursor + // (ctrl-click), rectangle drag (alt-drag), or xwin + // middle-click-paste. Or it might be a click on something we should + // not interfere with, such as a scrollbar or widget. + function onMouseDown(e) { + var cm = this, display = cm.display; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } + display.input.ensurePolled(); + display.shift = e.shiftKey; + + if (eventInWidget(display, e)) { + if (!webkit) { + // Briefly turn off draggability, to allow widgets to do + // normal dragging things. + display.scroller.draggable = false; + setTimeout(function () { return display.scroller.draggable = true; }, 100); + } + return + } + if (clickInGutter(cm, e)) { return } + var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; + window.focus(); + + // #3261: make sure, that we're not starting a second selection + if (button == 1 && cm.state.selectingText) + { cm.state.selectingText(e); } + + if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } + + if (button == 1) { + if (pos) { leftButtonDown(cm, pos, repeat, e); } + else if (e_target(e) == display.scroller) { e_preventDefault(e); } + } else if (button == 2) { + if (pos) { extendSelection(cm.doc, pos); } + setTimeout(function () { return display.input.focus(); }, 20); + } else if (button == 3) { + if (captureRightClick) { cm.display.input.onContextMenu(e); } + else { delayBlurEvent(cm); } + } + } + + function handleMappedButton(cm, button, pos, repeat, event) { + var name = "Click"; + if (repeat == "double") { name = "Double" + name; } + else if (repeat == "triple") { name = "Triple" + name; } + name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; + + return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { + if (typeof bound == "string") { bound = commands[bound]; } + if (!bound) { return false } + var done = false; + try { + if (cm.isReadOnly()) { cm.state.suppressEdits = true; } + done = bound(cm, pos) != Pass; + } finally { + cm.state.suppressEdits = false; + } + return done + }) + } + + function configureMouse(cm, repeat, event) { + var option = cm.getOption("configureMouse"); + var value = option ? option(cm, repeat, event) : {}; + if (value.unit == null) { + var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; + value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; + } + if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } + if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } + if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } + return value + } + + function leftButtonDown(cm, pos, repeat, event) { + if (ie) { setTimeout(bind(ensureFocus, cm), 0); } + else { cm.curOp.focus = activeElt(); } + + var behavior = configureMouse(cm, repeat, event); + + var sel = cm.doc.sel, contained; + if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && + repeat == "single" && (contained = sel.contains(pos)) > -1 && + (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && + (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) + { leftButtonStartDrag(cm, event, pos, behavior); } + else + { leftButtonSelect(cm, event, pos, behavior); } + } + + // Start a text drag. When it ends, see if any dragging actually + // happen, and treat as a click if it didn't. + function leftButtonStartDrag(cm, event, pos, behavior) { + var display = cm.display, moved = false; + var dragEnd = operation(cm, function (e) { + if (webkit) { display.scroller.draggable = false; } + cm.state.draggingText = false; + if (cm.state.delayingBlurEvent) { + if (cm.hasFocus()) { cm.state.delayingBlurEvent = false; } + else { delayBlurEvent(cm); } + } + off(display.wrapper.ownerDocument, "mouseup", dragEnd); + off(display.wrapper.ownerDocument, "mousemove", mouseMove); + off(display.scroller, "dragstart", dragStart); + off(display.scroller, "drop", dragEnd); + if (!moved) { + e_preventDefault(e); + if (!behavior.addNew) + { extendSelection(cm.doc, pos, null, null, behavior.extend); } + // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) + if ((webkit && !safari) || ie && ie_version == 9) + { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); } + else + { display.input.focus(); } + } + }); + var mouseMove = function(e2) { + moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; + }; + var dragStart = function () { return moved = true; }; + // Let the drag handler handle this. + if (webkit) { display.scroller.draggable = true; } + cm.state.draggingText = dragEnd; + dragEnd.copy = !behavior.moveOnDrag; + on(display.wrapper.ownerDocument, "mouseup", dragEnd); + on(display.wrapper.ownerDocument, "mousemove", mouseMove); + on(display.scroller, "dragstart", dragStart); + on(display.scroller, "drop", dragEnd); + + cm.state.delayingBlurEvent = true; + setTimeout(function () { return display.input.focus(); }, 20); + // IE's approach to draggable + if (display.scroller.dragDrop) { display.scroller.dragDrop(); } + } + + function rangeForUnit(cm, pos, unit) { + if (unit == "char") { return new Range(pos, pos) } + if (unit == "word") { return cm.findWordAt(pos) } + if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } + var result = unit(cm, pos); + return new Range(result.from, result.to) + } + + // Normal selection, as opposed to text dragging. + function leftButtonSelect(cm, event, start, behavior) { + if (ie) { delayBlurEvent(cm); } + var display = cm.display, doc = cm.doc; + e_preventDefault(event); + + var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; + if (behavior.addNew && !behavior.extend) { + ourIndex = doc.sel.contains(start); + if (ourIndex > -1) + { ourRange = ranges[ourIndex]; } + else + { ourRange = new Range(start, start); } + } else { + ourRange = doc.sel.primary(); + ourIndex = doc.sel.primIndex; + } + + if (behavior.unit == "rectangle") { + if (!behavior.addNew) { ourRange = new Range(start, start); } + start = posFromMouse(cm, event, true, true); + ourIndex = -1; + } else { + var range = rangeForUnit(cm, start, behavior.unit); + if (behavior.extend) + { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); } + else + { ourRange = range; } + } + + if (!behavior.addNew) { + ourIndex = 0; + setSelection(doc, new Selection([ourRange], 0), sel_mouse); + startSel = doc.sel; + } else if (ourIndex == -1) { + ourIndex = ranges.length; + setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex), + {scroll: false, origin: "*mouse"}); + } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { + setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), + {scroll: false, origin: "*mouse"}); + startSel = doc.sel; + } else { + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); + } + + var lastPos = start; + function extendTo(pos) { + if (cmp(lastPos, pos) == 0) { return } + lastPos = pos; + + if (behavior.unit == "rectangle") { + var ranges = [], tabSize = cm.options.tabSize; + var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); + var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); + if (left == right) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } + else if (text.length > leftPos) + { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } + } + if (!ranges.length) { ranges.push(new Range(start, start)); } + setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), + {origin: "*mouse", scroll: false}); + cm.scrollIntoView(pos); + } else { + var oldRange = ourRange; + var range = rangeForUnit(cm, pos, behavior.unit); + var anchor = oldRange.anchor, head; + if (cmp(range.anchor, anchor) > 0) { + head = range.head; + anchor = minPos(oldRange.from(), range.anchor); + } else { + head = range.anchor; + anchor = maxPos(oldRange.to(), range.head); + } + var ranges$1 = startSel.ranges.slice(0); + ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head)); + setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse); + } + } + + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + + function extend(e) { + var curCount = ++counter; + var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); + if (!cur) { return } + if (cmp(cur, lastPos) != 0) { + cm.curOp.focus = activeElt(); + extendTo(cur); + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) { setTimeout(operation(cm, function () { + if (counter != curCount) { return } + display.scroller.scrollTop += outside; + extend(e); + }), 50); } + } + } + + function done(e) { + cm.state.selectingText = false; + counter = Infinity; + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e); + display.input.focus(); + } + off(display.wrapper.ownerDocument, "mousemove", move); + off(display.wrapper.ownerDocument, "mouseup", up); + doc.history.lastSelOrigin = null; + } + + var move = operation(cm, function (e) { + if (e.buttons === 0 || !e_button(e)) { done(e); } + else { extend(e); } + }); + var up = operation(cm, done); + cm.state.selectingText = up; + on(display.wrapper.ownerDocument, "mousemove", move); + on(display.wrapper.ownerDocument, "mouseup", up); + } + + // Used when mouse-selecting to adjust the anchor to the proper side + // of a bidi jump depending on the visual position of the head. + function bidiSimplify(cm, range) { + var anchor = range.anchor; + var head = range.head; + var anchorLine = getLine(cm.doc, anchor.line); + if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range } + var order = getOrder(anchorLine); + if (!order) { return range } + var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index]; + if (part.from != anchor.ch && part.to != anchor.ch) { return range } + var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1); + if (boundary == 0 || boundary == order.length) { return range } + + // Compute the relative visual position of the head compared to the + // anchor (<0 is to the left, >0 to the right) + var leftSide; + if (head.line != anchor.line) { + leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0; + } else { + var headIndex = getBidiPartAt(order, head.ch, head.sticky); + var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1); + if (headIndex == boundary - 1 || headIndex == boundary) + { leftSide = dir < 0; } + else + { leftSide = dir > 0; } + } + + var usePart = order[boundary + (leftSide ? -1 : 0)]; + var from = leftSide == (usePart.level == 1); + var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before"; + return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head) + } + + + // Determines whether an event happened in the gutter, and fires the + // handlers for the corresponding event. + function gutterEvent(cm, e, type, prevent) { + var mX, mY; + if (e.touches) { + mX = e.touches[0].clientX; + mY = e.touches[0].clientY; + } else { + try { mX = e.clientX; mY = e.clientY; } + catch(e$1) { return false } + } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } + if (prevent) { e_preventDefault(e); } + + var display = cm.display; + var lineBox = display.lineDiv.getBoundingClientRect(); + + if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.display.gutterSpecs.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.display.gutterSpecs[i]; + signal(cm, type, cm, line, gutter.className, e); + return e_defaultPrevented(e) + } + } + } + + function clickInGutter(cm, e) { + return gutterEvent(cm, e, "gutterClick", true) + } + + // CONTEXT MENU HANDLING + + // To make the context menu work, we need to briefly unhide the + // textarea (making it as unobtrusive as possible) to let the + // right-click take effect on it. + function onContextMenu(cm, e) { + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } + if (signalDOMEvent(cm, e, "contextmenu")) { return } + if (!captureRightClick) { cm.display.input.onContextMenu(e); } + } + + function contextMenuInGutter(cm, e) { + if (!hasHandler(cm, "gutterContextMenu")) { return false } + return gutterEvent(cm, e, "gutterContextMenu", false) + } + + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); + } + + var Init = {toString: function(){return "CodeMirror.Init"}}; + + var defaults = {}; + var optionHandlers = {}; + + function defineOptions(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + function option(name, deflt, handle, notOnInit) { + CodeMirror.defaults[name] = deflt; + if (handle) { optionHandlers[name] = + notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } + } + + CodeMirror.defineOption = option; + + // Passed to option handlers when there is no old value. + CodeMirror.Init = Init; + + // These two are, on init, called from the constructor because they + // have to be initialized before the editor can start at all. + option("value", "", function (cm, val) { return cm.setValue(val); }, true); + option("mode", null, function (cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); + + option("indentUnit", 2, loadMode, true); + option("indentWithTabs", false); + option("smartIndent", true); + option("tabSize", 4, function (cm) { + resetModeState(cm); + clearCaches(cm); + regChange(cm); + }, true); + + option("lineSeparator", null, function (cm, val) { + cm.doc.lineSep = val; + if (!val) { return } + var newBreaks = [], lineNo = cm.doc.first; + cm.doc.iter(function (line) { + for (var pos = 0;;) { + var found = line.text.indexOf(val, pos); + if (found == -1) { break } + pos = found + val.length; + newBreaks.push(Pos(lineNo, found)); + } + lineNo++; + }); + for (var i = newBreaks.length - 1; i >= 0; i--) + { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } + }); + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) { + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); + if (old != Init) { cm.refresh(); } + }); + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); + option("electricChars", true); + option("inputStyle", mobile ? "contenteditable" : "textarea", function () { + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true); + option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); + option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true); + option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true); + option("rtlMoveVisually", !windows); + option("wholeLineUpdateBefore", true); + + option("theme", "default", function (cm) { + themeChanged(cm); + updateGutters(cm); + }, true); + option("keyMap", "default", function (cm, val, old) { + var next = getKeyMap(val); + var prev = old != Init && getKeyMap(old); + if (prev && prev.detach) { prev.detach(cm, next); } + if (next.attach) { next.attach(cm, prev || null); } + }); + option("extraKeys", null); + option("configureMouse", null); + + option("lineWrapping", false, wrappingChanged, true); + option("gutters", [], function (cm, val) { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers); + updateGutters(cm); + }, true); + option("fixedGutter", true, function (cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); + option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); + option("scrollbarStyle", "native", function (cm) { + initScrollbars(cm); + updateScrollbars(cm); + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); + }, true); + option("lineNumbers", false, function (cm, val) { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val); + updateGutters(cm); + }, true); + option("firstLineNumber", 1, updateGutters, true); + option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true); + option("showCursorWhenSelecting", false, updateSelection, true); + + option("resetSelectionOnContextMenu", true); + option("lineWiseCopyCut", true); + option("pasteLinesPerSelection", true); + option("selectionsMayTouch", false); + + option("readOnly", false, function (cm, val) { + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + } + cm.display.input.readOnlyChanged(val); + }); + + option("screenReaderLabel", null, function (cm, val) { + val = (val === '') ? null : val; + cm.display.input.screenReaderLabelChanged(val); + }); + + option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); + option("dragDrop", true, dragDropChanged); + option("allowDropFileTypes", null); + + option("cursorBlinkRate", 530); + option("cursorScrollMargin", 0); + option("cursorHeight", 1, updateSelection, true); + option("singleCursorHeightPerLine", true, updateSelection, true); + option("workTime", 100); + option("workDelay", 100); + option("flattenSpans", true, resetModeState, true); + option("addModeClass", false, resetModeState, true); + option("pollInterval", 100); + option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); + option("historyEventDelay", 1250); + option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); + option("maxHighlightLength", 10000, resetModeState, true); + option("moveInputWithCursor", true, function (cm, val) { + if (!val) { cm.display.input.resetPosition(); } + }); + + option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); + option("autofocus", null); + option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); + option("phrases", null); + } + + function dragDropChanged(cm, value, old) { + var wasOn = old && old != Init; + if (!value != !wasOn) { + var funcs = cm.display.dragFunctions; + var toggle = value ? on : off; + toggle(cm.display.scroller, "dragstart", funcs.start); + toggle(cm.display.scroller, "dragenter", funcs.enter); + toggle(cm.display.scroller, "dragover", funcs.over); + toggle(cm.display.scroller, "dragleave", funcs.leave); + toggle(cm.display.scroller, "drop", funcs.drop); + } + } + + function wrappingChanged(cm) { + if (cm.options.lineWrapping) { + addClass(cm.display.wrapper, "CodeMirror-wrap"); + cm.display.sizer.style.minWidth = ""; + cm.display.sizerWidth = null; + } else { + rmClass(cm.display.wrapper, "CodeMirror-wrap"); + findMaxLine(cm); + } + estimateLineHeights(cm); + regChange(cm); + clearCaches(cm); + setTimeout(function () { return updateScrollbars(cm); }, 100); + } + + // A CodeMirror instance represents an editor. This is the object + // that user code is usually dealing with. + + function CodeMirror(place, options) { + var this$1 = this; + + if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) } + + this.options = options = options ? copyObj(options) : {}; + // Determine effective options based on given values and defaults. + copyObj(defaults, options, false); + + var doc = options.value; + if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } + else if (options.mode) { doc.modeOption = options.mode; } + this.doc = doc; + + var input = new CodeMirror.inputStyles[options.inputStyle](this); + var display = this.display = new Display(place, doc, input, options); + display.wrapper.CodeMirror = this; + themeChanged(this); + if (options.lineWrapping) + { this.display.wrapper.className += " CodeMirror-wrap"; } + initScrollbars(this); + + this.state = { + keyMaps: [], // stores maps added by addKeyMap + overlays: [], // highlighting overlays, as added by addOverlay + modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info + overwrite: false, + delayingBlurEvent: false, + focused: false, + suppressEdits: false, // used to disable editing during key handlers when in readOnly mode + pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll + selectingText: false, + draggingText: false, + highlight: new Delayed(), // stores highlight worker timeout + keySeq: null, // Unfinished key sequence + specialChars: null + }; + + if (options.autofocus && !mobile) { display.input.focus(); } + + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } + + registerEventHandlers(this); + ensureGlobalHandlers(); + + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); + + if ((options.autofocus && !mobile) || this.hasFocus()) + { setTimeout(function () { + if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); } + }, 20); } + else + { onBlur(this); } + + for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) + { optionHandlers[opt](this, options[opt], Init); } } + maybeUpdateLineNumberWidth(this); + if (options.finishInit) { options.finishInit(this); } + for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); } + endOperation(this); + // Suppress optimizelegibility in Webkit, since it breaks text + // measuring on line wrapping boundaries. + if (webkit && options.lineWrapping && + getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") + { display.lineDiv.style.textRendering = "auto"; } + } + + // The default configuration options. + CodeMirror.defaults = defaults; + // Functions to run when options are changed. + CodeMirror.optionHandlers = optionHandlers; + + // Attach the necessary event handlers when initializing the editor + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + // Older IE's will not fire a second mousedown for a double click + if (ie && ie_version < 11) + { on(d.scroller, "dblclick", operation(cm, function (e) { + if (signalDOMEvent(cm, e)) { return } + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } + e_preventDefault(e); + var word = cm.findWordAt(pos); + extendSelection(cm.doc, word.anchor, word.head); + })); } + else + { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } + // Some browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for these browsers. + on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); + on(d.input.getField(), "contextmenu", function (e) { + if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); } + }); + + // Used to suppress mouse event handling when a touch happens + var touchFinished, prevTouch = {end: 0}; + function finishTouch() { + if (d.activeTouch) { + touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); + prevTouch = d.activeTouch; + prevTouch.end = +new Date; + } + } + function isMouseLikeTouchEvent(e) { + if (e.touches.length != 1) { return false } + var touch = e.touches[0]; + return touch.radiusX <= 1 && touch.radiusY <= 1 + } + function farAway(touch, other) { + if (other.left == null) { return true } + var dx = other.left - touch.left, dy = other.top - touch.top; + return dx * dx + dy * dy > 20 * 20 + } + on(d.scroller, "touchstart", function (e) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) { + d.input.ensurePolled(); + clearTimeout(touchFinished); + var now = +new Date; + d.activeTouch = {start: now, moved: false, + prev: now - prevTouch.end <= 300 ? prevTouch : null}; + if (e.touches.length == 1) { + d.activeTouch.left = e.touches[0].pageX; + d.activeTouch.top = e.touches[0].pageY; + } + } + }); + on(d.scroller, "touchmove", function () { + if (d.activeTouch) { d.activeTouch.moved = true; } + }); + on(d.scroller, "touchend", function (e) { + var touch = d.activeTouch; + if (touch && !eventInWidget(d, e) && touch.left != null && + !touch.moved && new Date - touch.start < 300) { + var pos = cm.coordsChar(d.activeTouch, "page"), range; + if (!touch.prev || farAway(touch, touch.prev)) // Single tap + { range = new Range(pos, pos); } + else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap + { range = cm.findWordAt(pos); } + else // Triple tap + { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } + cm.setSelection(range.anchor, range.head); + cm.focus(); + e_preventDefault(e); + } + finishTouch(); + }); + on(d.scroller, "touchcancel", finishTouch); + + // Sync scrolling between fake scrollbars and real scrollable + // area, ensure viewport is updated when scrolling. + on(d.scroller, "scroll", function () { + if (d.scroller.clientHeight) { + updateScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } + }); + + // Listen to wheel events in order to try and update the viewport on time. + on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); + on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); + + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + + d.dragFunctions = { + enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, + over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, + start: function (e) { return onDragStart(cm, e); }, + drop: operation(cm, onDrop), + leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} + }; + + var inp = d.input.getField(); + on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); + on(inp, "keydown", operation(cm, onKeyDown)); + on(inp, "keypress", operation(cm, onKeyPress)); + on(inp, "focus", function (e) { return onFocus(cm, e); }); + on(inp, "blur", function (e) { return onBlur(cm, e); }); + } + + var initHooks = []; + CodeMirror.defineInitHook = function (f) { return initHooks.push(f); }; + + // Indent the given line. The how parameter can be "smart", + // "add"/null, "subtract", or "prev". When aggressive is false + // (typically set to true for forced single-line indents), empty + // lines are not indented, and places where the mode returns Pass + // are left alone. + function indentLine(cm, n, how, aggressive) { + var doc = cm.doc, state; + if (how == null) { how = "add"; } + if (how == "smart") { + // Fall back to "prev" when the mode doesn't have an indentation + // method. + if (!doc.mode.indent) { how = "prev"; } + else { state = getContextBefore(cm, n).state; } + } + + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); + if (line.stateAfter) { line.stateAfter = null; } + var curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (!aggressive && !/\S/.test(line.text)) { + indentation = 0; + how = "not"; + } else if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass || indentation > 150) { + if (!aggressive) { return } + how = "prev"; + } + } + if (how == "prev") { + if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } + else { indentation = 0; } + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; + } else if (typeof how == "number") { + indentation = curSpace + how; + } + indentation = Math.max(0, indentation); + + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } + if (pos < indentation) { indentString += spaceStr(indentation - pos); } + + if (indentString != curSpaceString) { + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + line.stateAfter = null; + return true + } else { + // Ensure that, if the cursor was in the whitespace at the start + // of the line, it is moved to the end of that space. + for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { + var range = doc.sel.ranges[i$1]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos$1 = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); + break + } + } + } + } + + // This will be set to a {lineWise: bool, text: [string]} object, so + // that, when pasting, we know what kind of selections the copied + // text was made out of. + var lastCopied = null; + + function setLastCopied(newLastCopied) { + lastCopied = newLastCopied; + } + + function applyTextInput(cm, inserted, deleted, sel, origin) { + var doc = cm.doc; + cm.display.shift = false; + if (!sel) { sel = doc.sel; } + + var recent = +new Date - 200; + var paste = origin == "paste" || cm.state.pasteIncoming > recent; + var textLines = splitLinesAuto(inserted), multiPaste = null; + // When pasting N lines into N selections, insert one line per selection + if (paste && sel.ranges.length > 1) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { + multiPaste = []; + for (var i = 0; i < lastCopied.text.length; i++) + { multiPaste.push(doc.splitLines(lastCopied.text[i])); } + } + } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { + multiPaste = map(textLines, function (l) { return [l]; }); + } + } + + var updateInput = cm.curOp.updateInput; + // Normal behavior is to insert the new text into every selection + for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { + var range = sel.ranges[i$1]; + var from = range.from(), to = range.to(); + if (range.empty()) { + if (deleted && deleted > 0) // Handle deletion + { from = Pos(from.line, from.ch - deleted); } + else if (cm.state.overwrite && !paste) // Handle overwrite + { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } + else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n")) + { from = to = Pos(from.line, 0); } + } + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + } + if (inserted && !paste) + { triggerElectric(cm, inserted); } + + ensureCursorVisible(cm); + if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; } + cm.curOp.typing = true; + cm.state.pasteIncoming = cm.state.cutIncoming = -1; + } + + function handlePaste(e, cm) { + var pasted = e.clipboardData && e.clipboardData.getData("Text"); + if (pasted) { + e.preventDefault(); + if (!cm.isReadOnly() && !cm.options.disableInput) + { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } + return true + } + } + + function triggerElectric(cm, inserted) { + // When an 'electric' character is inserted, immediately trigger a reindent + if (!cm.options.electricChars || !cm.options.smartIndent) { return } + var sel = cm.doc.sel; + + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue } + var mode = cm.getModeAt(range.head); + var indented = false; + if (mode.electricChars) { + for (var j = 0; j < mode.electricChars.length; j++) + { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { + indented = indentLine(cm, range.head.line, "smart"); + break + } } + } else if (mode.electricInput) { + if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) + { indented = indentLine(cm, range.head.line, "smart"); } + } + if (indented) { signalLater(cm, "electricInput", cm, range.head.line); } + } + } + + function copyableRanges(cm) { + var text = [], ranges = []; + for (var i = 0; i < cm.doc.sel.ranges.length; i++) { + var line = cm.doc.sel.ranges[i].head.line; + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; + ranges.push(lineRange); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); + } + return {text: text, ranges: ranges} + } + + function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", autocorrect ? "" : "off"); + field.setAttribute("autocapitalize", autocapitalize ? "" : "off"); + field.setAttribute("spellcheck", !!spellcheck); + } + + function hiddenTextarea() { + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; min-height: 1em; outline: none"); + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The textarea is kept positioned near the cursor to prevent the + // fact that it'll be scrolled into view on input from scrolling + // our fake cursor out of view. On webkit, when wrap=off, paste is + // very slow. So make the area wide instead. + if (webkit) { te.style.width = "1000px"; } + else { te.setAttribute("wrap", "off"); } + // If border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) { te.style.border = "1px solid black"; } + disableBrowserMagic(te); + return div + } + + // The publicly visible API. Note that methodOp(f) means + // 'wrap f in an operation, performed on its `this` parameter'. + + // This is not the complete set of editor methods. Most of the + // methods defined on the Doc type are also injected into + // CodeMirror.prototype, for backwards compatibility and + // convenience. + + function addEditorMethods(CodeMirror) { + var optionHandlers = CodeMirror.optionHandlers; + + var helpers = CodeMirror.helpers = {}; + + CodeMirror.prototype = { + constructor: CodeMirror, + focus: function(){window.focus(); this.display.input.focus();}, + + setOption: function(option, value) { + var options = this.options, old = options[option]; + if (options[option] == value && option != "mode") { return } + options[option] = value; + if (optionHandlers.hasOwnProperty(option)) + { operation(this, optionHandlers[option])(this, value, old); } + signal(this, "optionChange", this, option); + }, + + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, + + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + }, + removeKeyMap: function(map) { + var maps = this.state.keyMaps; + for (var i = 0; i < maps.length; ++i) + { if (maps[i] == map || maps[i].name == map) { + maps.splice(i, 1); + return true + } } + }, + + addOverlay: methodOp(function(spec, options) { + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); + if (mode.startState) { throw new Error("Overlays may not be stateful.") } + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function (overlay) { return overlay.priority; }); + this.state.modeGen++; + regChange(this); + }), + removeOverlay: methodOp(function(spec) { + var overlays = this.state.overlays; + for (var i = 0; i < overlays.length; ++i) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { + overlays.splice(i, 1); + this.state.modeGen++; + regChange(this); + return + } + } + }), + + indentLine: methodOp(function(n, dir, aggressive) { + if (typeof dir != "string" && typeof dir != "number") { + if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } + else { dir = dir ? "add" : "subtract"; } + } + if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } + }), + indentSelection: methodOp(function(how) { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var from = range.from(), to = range.to(); + var start = Math.max(end, from.line); + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) + { indentLine(this, j, how); } + var newRanges = this.doc.sel.ranges; + if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) + { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; + if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); } + } + } + }), + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + getTokenAt: function(pos, precise) { + return takeToken(this, pos, precise) + }, + + getLineTokens: function(line, precise) { + return takeToken(this, Pos(line), precise, true) + }, + + getTokenTypeAt: function(pos) { + pos = clipPos(this.doc, pos); + var styles = getLineStyles(this, getLine(this.doc, pos.line)); + var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + var type; + if (ch == 0) { type = styles[2]; } + else { for (;;) { + var mid = (before + after) >> 1; + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } + else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } + else { type = styles[mid * 2 + 2]; break } + } } + var cut = type ? type.indexOf("overlay ") : -1; + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) + }, + + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) { return mode } + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode + }, + + getHelper: function(pos, type) { + return this.getHelpers(pos, type)[0] + }, + + getHelpers: function(pos, type) { + var found = []; + if (!helpers.hasOwnProperty(type)) { return found } + var help = helpers[type], mode = this.getModeAt(pos); + if (typeof mode[type] == "string") { + if (help[mode[type]]) { found.push(help[mode[type]]); } + } else if (mode[type]) { + for (var i = 0; i < mode[type].length; i++) { + var val = help[mode[type][i]]; + if (val) { found.push(val); } + } + } else if (mode.helperType && help[mode.helperType]) { + found.push(help[mode.helperType]); + } else if (help[mode.name]) { + found.push(help[mode.name]); + } + for (var i$1 = 0; i$1 < help._global.length; i$1++) { + var cur = help._global[i$1]; + if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) + { found.push(cur.val); } + } + return found + }, + + getStateAfter: function(line, precise) { + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); + return getContextBefore(this, line + 1, precise).state + }, + + cursorCoords: function(start, mode) { + var pos, range = this.doc.sel.primary(); + if (start == null) { pos = range.head; } + else if (typeof start == "object") { pos = clipPos(this.doc, start); } + else { pos = start ? range.from() : range.to(); } + return cursorCoords(this, pos, mode || "page") + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.doc, pos), mode || "page") + }, + + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top) + }, + + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset) + }, + heightAtLine: function(line, mode, includeWidgets) { + var end = false, lineObj; + if (typeof line == "number") { + var last = this.doc.first + this.doc.size - 1; + if (line < this.doc.first) { line = this.doc.first; } + else if (line > last) { line = last; end = true; } + lineObj = getLine(this.doc, line); + } else { + lineObj = line; + } + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + + (end ? this.doc.height - heightAtLine(lineObj) : 0) + }, + + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, + + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.doc, pos)); + var top = pos.bottom, left = pos.left; + node.style.position = "absolute"; + node.setAttribute("cm-ignore-events", "true"); + this.display.input.setUneditable(node); + display.sizer.appendChild(node); + if (vert == "over") { + top = pos.top; + } else if (vert == "above" || vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + // Default to positioning above (if specified and possible); otherwise default to positioning below + if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) + { top = pos.top - node.offsetHeight; } + else if (pos.bottom + node.offsetHeight <= vspace) + { top = pos.bottom; } + if (left + node.offsetWidth > hspace) + { left = hspace - node.offsetWidth; } + } + node.style.top = top + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") { left = 0; } + else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } + node.style.left = left + "px"; + } + if (scroll) + { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } + }, + + triggerOnKeyDown: methodOp(onKeyDown), + triggerOnKeyPress: methodOp(onKeyPress), + triggerOnKeyUp: onKeyUp, + triggerOnMouseDown: methodOp(onMouseDown), + + execCommand: function(cmd) { + if (commands.hasOwnProperty(cmd)) + { return commands[cmd].call(null, this) } + }, + + triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) { break } + } + return cur + }, + + moveH: methodOp(function(dir, unit) { + var this$1 = this; + + this.extendSelectionsBy(function (range) { + if (this$1.display.shift || this$1.doc.extend || range.empty()) + { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) } + else + { return dir < 0 ? range.from() : range.to() } + }, sel_move); + }), + + deleteH: methodOp(function(dir, unit) { + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + { doc.replaceSelection("", null, "+delete"); } + else + { deleteNearSelection(this, function (range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }); } + }), + + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; + if (amount < 0) { dir = -1; amount = -amount; } + var cur = clipPos(this.doc, from); + for (var i = 0; i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) { x = coords.left; } + else { coords.left = x; } + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) { break } + } + return cur + }, + + moveV: methodOp(function(dir, unit) { + var this$1 = this; + + var doc = this.doc, goals = []; + var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function (range) { + if (collapse) + { return dir < 0 ? range.from() : range.to() } + var headPos = cursorCoords(this$1, range.head, "div"); + if (range.goalColumn != null) { headPos.left = range.goalColumn; } + goals.push(headPos.left); + var pos = findPosV(this$1, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } + return pos + }, sel_move); + if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) + { doc.sel.ranges[i].goalColumn = goals[i]; } } + }), + + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function (ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } + : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; + while (start > 0 && check(line.charAt(start - 1))) { --start; } + while (end < line.length && check(line.charAt(end))) { ++end; } + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)) + }, + + toggleOverwrite: function(value) { + if (value != null && value == this.state.overwrite) { return } + if (this.state.overwrite = !this.state.overwrite) + { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + else + { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } + + signal(this, "overwriteToggle", this, this.state.overwrite); + }, + hasFocus: function() { return this.display.input.getField() == activeElt() }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, + + scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), + getScrollInfo: function() { + var scroller = this.display.scroller; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, + width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} + }, + + scrollIntoView: methodOp(function(range, margin) { + if (range == null) { + range = {from: this.doc.sel.primary().head, to: null}; + if (margin == null) { margin = this.options.cursorScrollMargin; } + } else if (typeof range == "number") { + range = {from: Pos(range, 0), to: null}; + } else if (range.from == null) { + range = {from: range, to: null}; + } + if (!range.to) { range.to = range.from; } + range.margin = margin || 0; + + if (range.from.line != null) { + scrollToRange(this, range); + } else { + scrollToCoordsRange(this, range.from, range.to, range.margin); + } + }), + + setSize: methodOp(function(width, height) { + var this$1 = this; + + var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; + if (width != null) { this.display.wrapper.style.width = interpret(width); } + if (height != null) { this.display.wrapper.style.height = interpret(height); } + if (this.options.lineWrapping) { clearLineMeasurementCache(this); } + var lineNo = this.display.viewFrom; + this.doc.iter(lineNo, this.display.viewTo, function (line) { + if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) + { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } } + ++lineNo; + }); + this.curOp.forceUpdate = true; + signal(this, "refresh", this); + }), + + operation: function(f){return runInOp(this, f)}, + startOperation: function(){return startOperation(this)}, + endOperation: function(){return endOperation(this)}, + + refresh: methodOp(function() { + var oldHeight = this.display.cachedTextHeight; + regChange(this); + this.curOp.forceUpdate = true; + clearCaches(this); + scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); + updateGutterSpace(this.display); + if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping) + { estimateLineHeights(this); } + signal(this, "refresh", this); + }), + + swapDoc: methodOp(function(doc) { + var old = this.doc; + old.cm = null; + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) { this.state.selectingText(); } + attachDoc(this, doc); + clearCaches(this); + this.display.input.reset(); + scrollToCoords(this, doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; + signalLater(this, "swapDoc", this, old); + return old + }), + + phrase: function(phraseText) { + var phrases = this.options.phrases; + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText + }, + + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + }; + eventMixin(CodeMirror); + + CodeMirror.registerHelper = function(type, name, value) { + if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } + helpers[type][name] = value; + }; + CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { + CodeMirror.registerHelper(type, name, value); + helpers[type]._global.push({pred: predicate, val: value}); + }; + } + + // Used for horizontal relative motion. Dir is -1 or 1 (left or + // right), unit can be "codepoint", "char", "column" (like char, but + // doesn't cross line boundaries), "word" (across next word), or + // "group" (to the start of next group of word or + // non-word-non-whitespace chars). The visually param controls + // whether, in right-to-left text, direction 1 means to move towards + // the next index in the string, or towards the character to the right + // of the current position. The resulting position will have a + // hitSide=true property if it reached the end of the document. + function findPosH(doc, pos, dir, unit, visually) { + var oldPos = pos; + var origDir = dir; + var lineObj = getLine(doc, pos.line); + var lineDir = visually && doc.direction == "rtl" ? -dir : dir; + function findNextLine() { + var l = pos.line + lineDir; + if (l < doc.first || l >= doc.first + doc.size) { return false } + pos = new Pos(l, pos.ch, pos.sticky); + return lineObj = getLine(doc, l) + } + function moveOnce(boundToLine) { + var next; + if (unit == "codepoint") { + var ch = lineObj.text.charCodeAt(pos.ch + (dir > 0 ? 0 : -1)); + if (isNaN(ch)) { + next = null; + } else { + var astral = dir > 0 ? ch >= 0xD800 && ch < 0xDC00 : ch >= 0xDC00 && ch < 0xDFFF; + next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (astral ? 2 : 1))), -dir); + } + } else if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir); + } else { + next = moveLogically(lineObj, pos, dir); + } + if (next == null) { + if (!boundToLine && findNextLine()) + { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); } + else + { return false } + } else { + pos = next; + } + return true + } + + if (unit == "char" || unit == "codepoint") { + moveOnce(); + } else if (unit == "column") { + moveOnce(true); + } else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) { break } + var cur = lineObj.text.charAt(pos.ch) || "\n"; + var type = isWordChar(cur, helper) ? "w" + : group && cur == "\n" ? "n" + : !group || /\s/.test(cur) ? null + : "p"; + if (group && !first && !type) { type = "s"; } + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} + break + } + + if (type) { sawType = type; } + if (dir > 0 && !moveOnce(!first)) { break } + } + } + var result = skipAtomic(doc, pos, oldPos, origDir, true); + if (equalCursorPos(oldPos, result)) { result.hitSide = true; } + return result + } + + // For relative vertical movement. Dir may be -1 or 1. Unit can be + // "page" or "line". The resulting position will have a hitSide=true + // property if it reached the end of the document. + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + var target; + for (;;) { + target = coordsChar(cm, x, y); + if (!target.outside) { break } + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } + y += dir * 5; + } + return target + } + + // CONTENTEDITABLE INPUT STYLE + + var ContentEditableInput = function(cm) { + this.cm = cm; + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; + this.polling = new Delayed(); + this.composing = null; + this.gracePeriod = false; + this.readDOMTimeout = null; + }; + + ContentEditableInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = input.cm; + var div = input.div = display.lineDiv; + div.contentEditable = true; + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize); + + function belongsToInput(e) { + for (var t = e.target; t; t = t.parentNode) { + if (t == div) { return true } + if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break } + } + return false + } + + on(div, "paste", function (e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } + }); + + on(div, "compositionstart", function (e) { + this$1.composing = {data: e.data, done: false}; + }); + on(div, "compositionupdate", function (e) { + if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } + }); + on(div, "compositionend", function (e) { + if (this$1.composing) { + if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } + this$1.composing.done = true; + } + }); + + on(div, "touchstart", function () { return input.forceCompositionEnd(); }); + + on(div, "input", function () { + if (!this$1.composing) { this$1.readFromDOMSoon(); } + }); + + function onCopyCut(e) { + if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.operation(function () { + cm.setSelections(ranges.ranges, 0, sel_dontScroll); + cm.replaceSelection("", null, "cut"); + }); + } + } + if (e.clipboardData) { + e.clipboardData.clearData(); + var content = lastCopied.text.join("\n"); + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } + } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = activeElt(); + selectInput(te); + setTimeout(function () { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + if (hadFocus == div) { input.showPrimarySelection(); } + }, 50); + } + on(div, "copy", onCopyCut); + on(div, "cut", onCopyCut); + }; + + ContentEditableInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.div.setAttribute('aria-label', label); + } else { + this.div.removeAttribute('aria-label'); + } + }; + + ContentEditableInput.prototype.prepareSelection = function () { + var result = prepareSelection(this.cm, false); + result.focus = activeElt() == this.div; + return result + }; + + ContentEditableInput.prototype.showSelection = function (info, takeFocus) { + if (!info || !this.cm.display.view.length) { return } + if (info.focus || takeFocus) { this.showPrimarySelection(); } + this.showMultipleSelections(info); + }; + + ContentEditableInput.prototype.getSelection = function () { + return this.cm.display.wrapper.ownerDocument.getSelection() + }; + + ContentEditableInput.prototype.showPrimarySelection = function () { + var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); + var from = prim.from(), to = prim.to(); + + if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { + sel.removeAllRanges(); + return + } + + var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); + if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && + cmp(minPos(curAnchor, curFocus), from) == 0 && + cmp(maxPos(curAnchor, curFocus), to) == 0) + { return } + + var view = cm.display.view; + var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || + {node: view[0].measure.map[2], offset: 0}; + var end = to.line < cm.display.viewTo && posToDOM(cm, to); + if (!end) { + var measure = view[view.length - 1].measure; + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + } + + if (!start || !end) { + sel.removeAllRanges(); + return + } + + var old = sel.rangeCount && sel.getRangeAt(0), rng; + try { rng = range(start.node, start.offset, end.offset, end.node); } + catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible + if (rng) { + if (!gecko && cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) { + sel.removeAllRanges(); + sel.addRange(rng); + } + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } + if (old && sel.anchorNode == null) { sel.addRange(old); } + else if (gecko) { this.startGracePeriod(); } + } + this.rememberSelection(); + }; + + ContentEditableInput.prototype.startGracePeriod = function () { + var this$1 = this; + + clearTimeout(this.gracePeriod); + this.gracePeriod = setTimeout(function () { + this$1.gracePeriod = false; + if (this$1.selectionChanged()) + { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } + }, 20); + }; + + ContentEditableInput.prototype.showMultipleSelections = function (info) { + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + }; + + ContentEditableInput.prototype.rememberSelection = function () { + var sel = this.getSelection(); + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + }; + + ContentEditableInput.prototype.selectionInEditor = function () { + var sel = this.getSelection(); + if (!sel.rangeCount) { return false } + var node = sel.getRangeAt(0).commonAncestorContainer; + return contains(this.div, node) + }; + + ContentEditableInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor() || activeElt() != this.div) + { this.showSelection(this.prepareSelection(), true); } + this.div.focus(); + } + }; + ContentEditableInput.prototype.blur = function () { this.div.blur(); }; + ContentEditableInput.prototype.getField = function () { return this.div }; + + ContentEditableInput.prototype.supportsTouch = function () { return true }; + + ContentEditableInput.prototype.receivedFocus = function () { + var this$1 = this; + + var input = this; + if (this.selectionInEditor()) + { setTimeout(function () { return this$1.pollSelection(); }, 20); } + else + { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } + + function poll() { + if (input.cm.state.focused) { + input.pollSelection(); + input.polling.set(input.cm.options.pollInterval, poll); + } + } + this.polling.set(this.cm.options.pollInterval, poll); + }; + + ContentEditableInput.prototype.selectionChanged = function () { + var sel = this.getSelection(); + return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset + }; + + ContentEditableInput.prototype.pollSelection = function () { + if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } + var sel = this.getSelection(), cm = this.cm; + // On Android Chrome (version 56, at least), backspacing into an + // uneditable block element will put the cursor in that element, + // and then, because it's not editable, hide the virtual keyboard. + // Because Android doesn't allow us to actually detect backspace + // presses in a sane way, this code checks for when that happens + // and simulates a backspace press in this case. + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { + this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); + this.blur(); + this.focus(); + return + } + if (this.composing) { return } + this.rememberSelection(); + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); + var head = domToPos(cm, sel.focusNode, sel.focusOffset); + if (anchor && head) { runInOp(cm, function () { + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); + if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } + }); } + }; + + ContentEditableInput.prototype.pollContent = function () { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout); + this.readDOMTimeout = null; + } + + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); + var from = sel.from(), to = sel.to(); + if (from.ch == 0 && from.line > cm.firstLine()) + { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + { to = Pos(to.line + 1, 0); } + if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } + + var fromIndex, fromLine, fromNode; + if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { + fromLine = lineNo(display.view[0].line); + fromNode = display.view[0].node; + } else { + fromLine = lineNo(display.view[fromIndex].line); + fromNode = display.view[fromIndex - 1].node.nextSibling; + } + var toIndex = findViewIndex(cm, to.line); + var toLine, toNode; + if (toIndex == display.view.length - 1) { + toLine = display.viewTo - 1; + toNode = display.lineDiv.lastChild; + } else { + toLine = lineNo(display.view[toIndex + 1].line) - 1; + toNode = display.view[toIndex + 1].node.previousSibling; + } + + if (!fromNode) { return false } + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + while (newText.length > 1 && oldText.length > 1) { + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } + else { break } + } + + var cutFront = 0, cutEnd = 0; + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) + { ++cutFront; } + var newBot = lst(newText), oldBot = lst(oldText); + var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + oldBot.length - (oldText.length == 1 ? cutFront : 0)); + while (cutEnd < maxCutEnd && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) + { ++cutEnd; } + // Try to move start of change to start of selection if ambiguous + if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { + while (cutFront && cutFront > from.ch && + newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { + cutFront--; + cutEnd++; + } + } + + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); + + var chFrom = Pos(fromLine, cutFront); + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { + replaceRange(cm.doc, newText, chFrom, chTo, "+input"); + return true + } + }; + + ContentEditableInput.prototype.ensurePolled = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.reset = function () { + this.forceCompositionEnd(); + }; + ContentEditableInput.prototype.forceCompositionEnd = function () { + if (!this.composing) { return } + clearTimeout(this.readDOMTimeout); + this.composing = null; + this.updateFromDOM(); + this.div.blur(); + this.div.focus(); + }; + ContentEditableInput.prototype.readFromDOMSoon = function () { + var this$1 = this; + + if (this.readDOMTimeout != null) { return } + this.readDOMTimeout = setTimeout(function () { + this$1.readDOMTimeout = null; + if (this$1.composing) { + if (this$1.composing.done) { this$1.composing = null; } + else { return } + } + this$1.updateFromDOM(); + }, 80); + }; + + ContentEditableInput.prototype.updateFromDOM = function () { + var this$1 = this; + + if (this.cm.isReadOnly() || !this.pollContent()) + { runInOp(this.cm, function () { return regChange(this$1.cm); }); } + }; + + ContentEditableInput.prototype.setUneditable = function (node) { + node.contentEditable = "false"; + }; + + ContentEditableInput.prototype.onKeyPress = function (e) { + if (e.charCode == 0 || this.composing) { return } + e.preventDefault(); + if (!this.cm.isReadOnly()) + { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } + }; + + ContentEditableInput.prototype.readOnlyChanged = function (val) { + this.div.contentEditable = String(val != "nocursor"); + }; + + ContentEditableInput.prototype.onContextMenu = function () {}; + ContentEditableInput.prototype.resetPosition = function () {}; + + ContentEditableInput.prototype.needsContentAttribute = true; + + function posToDOM(cm, pos) { + var view = findViewForLine(cm, pos.line); + if (!view || view.hidden) { return null } + var line = getLine(cm.doc, pos.line); + var info = mapFromLineView(view, line, pos.line); + + var order = getOrder(line, cm.doc.direction), side = "left"; + if (order) { + var partPos = getBidiPartAt(order, pos.ch); + side = partPos % 2 ? "right" : "left"; + } + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); + result.offset = result.collapse == "right" ? result.end : result.start; + return result + } + + function isInGutter(node) { + for (var scan = node; scan; scan = scan.parentNode) + { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } + return false + } + + function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } + + function domTextBetween(cm, from, to, fromLine, toLine) { + var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false; + function recognizeMarker(id) { return function (marker) { return marker.id == id; } } + function close() { + if (closing) { + text += lineSep; + if (extraLinebreak) { text += lineSep; } + closing = extraLinebreak = false; + } + } + function addText(str) { + if (str) { + close(); + text += str; + } + } + function walk(node) { + if (node.nodeType == 1) { + var cmText = node.getAttribute("cm-text"); + if (cmText) { + addText(cmText); + return + } + var markerID = node.getAttribute("cm-marker"), range; + if (markerID) { + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + if (found.length && (range = found[0].find(0))) + { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); } + return + } + if (node.getAttribute("contenteditable") == "false") { return } + var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName); + if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return } + + if (isBlock) { close(); } + for (var i = 0; i < node.childNodes.length; i++) + { walk(node.childNodes[i]); } + + if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; } + if (isBlock) { closing = true; } + } else if (node.nodeType == 3) { + addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " ")); + } + } + for (;;) { + walk(from); + if (from == to) { break } + from = from.nextSibling; + extraLinebreak = false; + } + return text + } + + function domToPos(cm, node, offset) { + var lineNode; + if (node == cm.display.lineDiv) { + lineNode = cm.display.lineDiv.childNodes[offset]; + if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } + node = null; offset = 0; + } else { + for (lineNode = node;; lineNode = lineNode.parentNode) { + if (!lineNode || lineNode == cm.display.lineDiv) { return null } + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } + } + } + for (var i = 0; i < cm.display.view.length; i++) { + var lineView = cm.display.view[i]; + if (lineView.node == lineNode) + { return locateNodeInLineView(lineView, node, offset) } + } + } + + function locateNodeInLineView(lineView, node, offset) { + var wrapper = lineView.text.firstChild, bad = false; + if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } + if (node == wrapper) { + bad = true; + node = wrapper.childNodes[offset]; + offset = 0; + if (!node) { + var line = lineView.rest ? lst(lineView.rest) : lineView.line; + return badPos(Pos(lineNo(line), line.text.length), bad) + } + } + + var textNode = node.nodeType == 3 ? node : null, topNode = node; + if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { + textNode = node.firstChild; + if (offset) { offset = textNode.nodeValue.length; } + } + while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } + var measure = lineView.measure, maps = measure.maps; + + function find(textNode, topNode, offset) { + for (var i = -1; i < (maps ? maps.length : 0); i++) { + var map = i < 0 ? measure.map : maps[i]; + for (var j = 0; j < map.length; j += 3) { + var curNode = map[j + 2]; + if (curNode == textNode || curNode == topNode) { + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); + var ch = map[j] + offset; + if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; } + return Pos(line, ch) + } + } + } + } + var found = find(textNode, topNode, offset); + if (found) { return badPos(found, bad) } + + // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems + for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + found = find(after, after.firstChild, 0); + if (found) + { return badPos(Pos(found.line, found.ch - dist), bad) } + else + { dist += after.textContent.length; } + } + for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { + found = find(before, before.firstChild, -1); + if (found) + { return badPos(Pos(found.line, found.ch + dist$1), bad) } + else + { dist$1 += before.textContent.length; } + } + } + + // TEXTAREA INPUT STYLE + + var TextareaInput = function(cm) { + this.cm = cm; + // See input.poll and input.reset + this.prevInput = ""; + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false; + // Self-resetting timeout for the poller + this.polling = new Delayed(); + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false; + this.composing = null; + }; + + TextareaInput.prototype.init = function (display) { + var this$1 = this; + + var input = this, cm = this.cm; + this.createField(display); + var te = this.textarea; + + display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild); + + // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) + if (ios) { te.style.width = "0px"; } + + on(te, "input", function () { + if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } + input.poll(); + }); + + on(te, "paste", function (e) { + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } + + cm.state.pasteIncoming = +new Date; + input.fastPoll(); + }); + + function prepareCopyCut(e) { + if (signalDOMEvent(cm, e)) { return } + if (cm.somethingSelected()) { + setLastCopied({lineWise: false, text: cm.getSelections()}); + } else if (!cm.options.lineWiseCopyCut) { + return + } else { + var ranges = copyableRanges(cm); + setLastCopied({lineWise: true, text: ranges.text}); + if (e.type == "cut") { + cm.setSelections(ranges.ranges, null, sel_dontScroll); + } else { + input.prevInput = ""; + te.value = ranges.text.join("\n"); + selectInput(te); + } + } + if (e.type == "cut") { cm.state.cutIncoming = +new Date; } + } + on(te, "cut", prepareCopyCut); + on(te, "copy", prepareCopyCut); + + on(display.scroller, "paste", function (e) { + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } + if (!te.dispatchEvent) { + cm.state.pasteIncoming = +new Date; + input.focus(); + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + var event = new Event("paste"); + event.clipboardData = e.clipboardData; + te.dispatchEvent(event); + }); + + // Prevent normal selection in the editor (we handle our own) + on(display.lineSpace, "selectstart", function (e) { + if (!eventInWidget(display, e)) { e_preventDefault(e); } + }); + + on(te, "compositionstart", function () { + var start = cm.getCursor("from"); + if (input.composing) { input.composing.range.clear(); } + input.composing = { + start: start, + range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) + }; + }); + on(te, "compositionend", function () { + if (input.composing) { + input.poll(); + input.composing.range.clear(); + input.composing = null; + } + }); + }; + + TextareaInput.prototype.createField = function (_display) { + // Wraps and hides input textarea + this.wrapper = hiddenTextarea(); + // The semihidden textarea that is focused when the editor is + // focused, and receives input. + this.textarea = this.wrapper.firstChild; + }; + + TextareaInput.prototype.screenReaderLabelChanged = function (label) { + // Label for screenreaders, accessibility + if(label) { + this.textarea.setAttribute('aria-label', label); + } else { + this.textarea.removeAttribute('aria-label'); + } + }; + + TextareaInput.prototype.prepareSelection = function () { + // Redraw the selection and/or cursor + var cm = this.cm, display = cm.display, doc = cm.doc; + var result = prepareSelection(cm); + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); + } + + return result + }; + + TextareaInput.prototype.showSelection = function (drawn) { + var cm = this.cm, display = cm.display; + removeChildrenAndAdd(display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + this.wrapper.style.top = drawn.teTop + "px"; + this.wrapper.style.left = drawn.teLeft + "px"; + } + }; + + // Reset the input to correspond to the selection (or to be empty, + // when not typing and nothing is selected) + TextareaInput.prototype.reset = function (typing) { + if (this.contextMenuPending || this.composing) { return } + var cm = this.cm; + if (cm.somethingSelected()) { + this.prevInput = ""; + var content = cm.getSelection(); + this.textarea.value = content; + if (cm.state.focused) { selectInput(this.textarea); } + if (ie && ie_version >= 9) { this.hasSelection = content; } + } else if (!typing) { + this.prevInput = this.textarea.value = ""; + if (ie && ie_version >= 9) { this.hasSelection = null; } + } + }; + + TextareaInput.prototype.getField = function () { return this.textarea }; + + TextareaInput.prototype.supportsTouch = function () { return false }; + + TextareaInput.prototype.focus = function () { + if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { + try { this.textarea.focus(); } + catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM + } + }; + + TextareaInput.prototype.blur = function () { this.textarea.blur(); }; + + TextareaInput.prototype.resetPosition = function () { + this.wrapper.style.top = this.wrapper.style.left = 0; + }; + + TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; + + // Poll for input changes, using the normal rate of polling. This + // runs as long as the editor is focused. + TextareaInput.prototype.slowPoll = function () { + var this$1 = this; + + if (this.pollingFast) { return } + this.polling.set(this.cm.options.pollInterval, function () { + this$1.poll(); + if (this$1.cm.state.focused) { this$1.slowPoll(); } + }); + }; + + // When an event has just come in that is likely to add or change + // something in the input textarea, we poll faster, to ensure that + // the change appears on the screen quickly. + TextareaInput.prototype.fastPoll = function () { + var missed = false, input = this; + input.pollingFast = true; + function p() { + var changed = input.poll(); + if (!changed && !missed) {missed = true; input.polling.set(60, p);} + else {input.pollingFast = false; input.slowPoll();} + } + input.polling.set(20, p); + }; + + // Read input from the textarea, and update the document to match. + // When something is selected, it is present in the textarea, and + // selected (unless it is huge, in which case a placeholder is + // used). When nothing is selected, the cursor sits after previously + // seen text (can be empty), which is stored in prevInput (we must + // not reset the textarea when typing, because that breaks IME). + TextareaInput.prototype.poll = function () { + var this$1 = this; + + var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + // Since this is called a *lot*, try to bail out as cheaply as + // possible when it is clear that nothing happened. hasSelection + // will be the case when there is a lot of text in the textarea, + // in which case reading its value would be expensive. + if (this.contextMenuPending || !cm.state.focused || + (hasSelection(input) && !prevInput && !this.composing) || + cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) + { return false } + + var text = input.value; + // If nothing changed, bail. + if (text == prevInput && !cm.somethingSelected()) { return false } + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && this.hasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { + cm.display.input.reset(); + return false + } + + if (cm.doc.sel == cm.display.selForContextMenu) { + var first = text.charCodeAt(0); + if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } + } + // Find the part of the input that is actually new + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } + + runInOp(cm, function () { + applyTextInput(cm, text.slice(same), prevInput.length - same, + null, this$1.composing ? "*compose" : null); + + // Don't leave long text in the textarea, since it makes further polling slow + if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } + else { this$1.prevInput = text; } + + if (this$1.composing) { + this$1.composing.range.clear(); + this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), + {className: "CodeMirror-composing"}); + } + }); + return true + }; + + TextareaInput.prototype.ensurePolled = function () { + if (this.pollingFast && this.poll()) { this.pollingFast = false; } + }; + + TextareaInput.prototype.onKeyPress = function () { + if (ie && ie_version >= 9) { this.hasSelection = null; } + this.fastPoll(); + }; + + TextareaInput.prototype.onContextMenu = function (e) { + var input = this, cm = input.cm, display = cm.display, te = input.textarea; + if (input.contextMenuPending) { input.contextMenuPending(); } + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || presto) { return } // Opera is difficult. + + // Reset the current text selection only if the click is done outside of the selection + // and 'resetSelectionOnContextMenu' option is true. + var reset = cm.options.resetSelectionOnContextMenu; + if (reset && cm.doc.sel.contains(pos) == -1) + { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } + + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect(); + input.wrapper.style.cssText = "position: static"; + te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + var oldScrollY; + if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) + display.input.focus(); + if (webkit) { window.scrollTo(null, oldScrollY); } + display.input.reset(); + // Adds "Select all" to context menu in FF + if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } + input.contextMenuPending = rehide; + display.selForContextMenu = cm.doc.sel; + clearTimeout(display.detectingSelectAll); + + // Select-all will be greyed out if there's nothing to select, so + // this adds a zero-width space so that we can later check whether + // it got selected. + function prepareSelectAllHack() { + if (te.selectionStart != null) { + var selected = cm.somethingSelected(); + var extval = "\u200b" + (selected ? te.value : ""); + te.value = "\u21da"; // Used to catch context-menu undo + te.value = extval; + input.prevInput = selected ? "" : "\u200b"; + te.selectionStart = 1; te.selectionEnd = extval.length; + // Re-set this, in case some other handler touched the + // selection in the meantime. + display.selForContextMenu = cm.doc.sel; + } + } + function rehide() { + if (input.contextMenuPending != rehide) { return } + input.contextMenuPending = false; + input.wrapper.style.cssText = oldWrapperCSS; + te.style.cssText = oldCSS; + if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } + + // Try to detect the user choosing select-all + if (te.selectionStart != null) { + if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } + var i = 0, poll = function () { + if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && + te.selectionEnd > 0 && input.prevInput == "\u200b") { + operation(cm, selectAll)(cm); + } else if (i++ < 10) { + display.detectingSelectAll = setTimeout(poll, 500); + } else { + display.selForContextMenu = null; + display.input.reset(); + } + }; + display.detectingSelectAll = setTimeout(poll, 200); + } + } + + if (ie && ie_version >= 9) { prepareSelectAllHack(); } + if (captureRightClick) { + e_stop(e); + var mouseup = function () { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); + }; + on(window, "mouseup", mouseup); + } else { + setTimeout(rehide, 50); + } + }; + + TextareaInput.prototype.readOnlyChanged = function (val) { + if (!val) { this.reset(); } + this.textarea.disabled = val == "nocursor"; + this.textarea.readOnly = !!val; + }; + + TextareaInput.prototype.setUneditable = function () {}; + + TextareaInput.prototype.needsContentAttribute = false; + + function fromTextArea(textarea, options) { + options = options ? copyObj(options) : {}; + options.value = textarea.value; + if (!options.tabindex && textarea.tabIndex) + { options.tabindex = textarea.tabIndex; } + if (!options.placeholder && textarea.placeholder) + { options.placeholder = textarea.placeholder; } + // Set autofocus to true if this textarea is focused, or if it has + // autofocus and no other element is focused. + if (options.autofocus == null) { + var hasFocus = activeElt(); + options.autofocus = hasFocus == textarea || + textarea.getAttribute("autofocus") != null && hasFocus == document.body; + } + + function save() {textarea.value = cm.getValue();} + + var realSubmit; + if (textarea.form) { + on(textarea.form, "submit", save); + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form; + realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function () { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } + } + + options.finishInit = function (cm) { + cm.save = save; + cm.getTextArea = function () { return textarea; }; + cm.toTextArea = function () { + cm.toTextArea = isNaN; // Prevent this from being ran twice + save(); + textarea.parentNode.removeChild(cm.getWrapperElement()); + textarea.style.display = ""; + if (textarea.form) { + off(textarea.form, "submit", save); + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") + { textarea.form.submit = realSubmit; } + } + }; + }; + + textarea.style.display = "none"; + var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, + options); + return cm + } + + function addLegacyProps(CodeMirror) { + CodeMirror.off = off; + CodeMirror.on = on; + CodeMirror.wheelEventPixels = wheelEventPixels; + CodeMirror.Doc = Doc; + CodeMirror.splitLines = splitLinesAuto; + CodeMirror.countColumn = countColumn; + CodeMirror.findColumn = findColumn; + CodeMirror.isWordChar = isWordCharBasic; + CodeMirror.Pass = Pass; + CodeMirror.signal = signal; + CodeMirror.Line = Line; + CodeMirror.changeEnd = changeEnd; + CodeMirror.scrollbarModel = scrollbarModel; + CodeMirror.Pos = Pos; + CodeMirror.cmpPos = cmp; + CodeMirror.modes = modes; + CodeMirror.mimeModes = mimeModes; + CodeMirror.resolveMode = resolveMode; + CodeMirror.getMode = getMode; + CodeMirror.modeExtensions = modeExtensions; + CodeMirror.extendMode = extendMode; + CodeMirror.copyState = copyState; + CodeMirror.startState = startState; + CodeMirror.innerMode = innerMode; + CodeMirror.commands = commands; + CodeMirror.keyMap = keyMap; + CodeMirror.keyName = keyName; + CodeMirror.isModifierKey = isModifierKey; + CodeMirror.lookupKey = lookupKey; + CodeMirror.normalizeKeyMap = normalizeKeyMap; + CodeMirror.StringStream = StringStream; + CodeMirror.SharedTextMarker = SharedTextMarker; + CodeMirror.TextMarker = TextMarker; + CodeMirror.LineWidget = LineWidget; + CodeMirror.e_preventDefault = e_preventDefault; + CodeMirror.e_stopPropagation = e_stopPropagation; + CodeMirror.e_stop = e_stop; + CodeMirror.addClass = addClass; + CodeMirror.contains = contains; + CodeMirror.rmClass = rmClass; + CodeMirror.keyNames = keyNames; + } + + // EDITOR CONSTRUCTOR + + defineOptions(CodeMirror); + + addEditorMethods(CodeMirror); + + // Set up methods on CodeMirror's prototype to redirect to the editor's document. + var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); + for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) + { CodeMirror.prototype[prop] = (function(method) { + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]); } } + + eventMixin(Doc); + CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; + + // Extra arguments are stored as the mode's dependencies, which is + // used by (legacy) mechanisms like loadmode.js to automatically + // load a mode. (Preferred mechanism is the require/define calls.) + CodeMirror.defineMode = function(name/*, mode, …*/) { + if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; } + defineMode.apply(this, arguments); + }; + + CodeMirror.defineMIME = defineMIME; + + // Minimal default mode. + CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); + CodeMirror.defineMIME("text/plain", "null"); + + // EXTENSIONS + + CodeMirror.defineExtension = function (name, func) { + CodeMirror.prototype[name] = func; + }; + CodeMirror.defineDocExtension = function (name, func) { + Doc.prototype[name] = func; + }; + + CodeMirror.fromTextArea = fromTextArea; + + addLegacyProps(CodeMirror); + + CodeMirror.version = "5.65.0"; + + return CodeMirror; + +}))); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/django.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/django.js new file mode 100644 index 0000000000..07be123986 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/django.js @@ -0,0 +1,356 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), + require("../../addon/mode/overlay")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../htmlmixed/htmlmixed", + "../../addon/mode/overlay"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("django:inner", function() { + var keywords = ["block", "endblock", "for", "endfor", "true", "false", "filter", "endfilter", + "loop", "none", "self", "super", "if", "elif", "endif", "as", "else", "import", + "with", "endwith", "without", "context", "ifequal", "endifequal", "ifnotequal", + "endifnotequal", "extends", "include", "load", "comment", "endcomment", + "empty", "url", "static", "trans", "blocktrans", "endblocktrans", "now", + "regroup", "lorem", "ifchanged", "endifchanged", "firstof", "debug", "cycle", + "csrf_token", "autoescape", "endautoescape", "spaceless", "endspaceless", + "ssi", "templatetag", "verbatim", "endverbatim", "widthratio"], + filters = ["add", "addslashes", "capfirst", "center", "cut", "date", + "default", "default_if_none", "dictsort", + "dictsortreversed", "divisibleby", "escape", "escapejs", + "filesizeformat", "first", "floatformat", "force_escape", + "get_digit", "iriencode", "join", "last", "length", + "length_is", "linebreaks", "linebreaksbr", "linenumbers", + "ljust", "lower", "make_list", "phone2numeric", "pluralize", + "pprint", "random", "removetags", "rjust", "safe", + "safeseq", "slice", "slugify", "stringformat", "striptags", + "time", "timesince", "timeuntil", "title", "truncatechars", + "truncatechars_html", "truncatewords", "truncatewords_html", + "unordered_list", "upper", "urlencode", "urlize", + "urlizetrunc", "wordcount", "wordwrap", "yesno"], + operators = ["==", "!=", "<", ">", "<=", ">="], + wordOperators = ["in", "not", "or", "and"]; + + keywords = new RegExp("^\\b(" + keywords.join("|") + ")\\b"); + filters = new RegExp("^\\b(" + filters.join("|") + ")\\b"); + operators = new RegExp("^\\b(" + operators.join("|") + ")\\b"); + wordOperators = new RegExp("^\\b(" + wordOperators.join("|") + ")\\b"); + + // We have to return "null" instead of null, in order to avoid string + // styling as the default, when using Django templates inside HTML + // element attributes + function tokenBase (stream, state) { + // Attempt to identify a variable, template or comment tag respectively + if (stream.match("{{")) { + state.tokenize = inVariable; + return "tag"; + } else if (stream.match("{%")) { + state.tokenize = inTag; + return "tag"; + } else if (stream.match("{#")) { + state.tokenize = inComment; + return "comment"; + } + + // Ignore completely any stream series that do not match the + // Django template opening tags. + while (stream.next() != null && !stream.match(/\{[{%#]/, false)) {} + return null; + } + + // A string can be included in either single or double quotes (this is + // the delimiter). Mark everything as a string until the start delimiter + // occurs again. + function inString (delimiter, previousTokenizer) { + return function (stream, state) { + if (!state.escapeNext && stream.eat(delimiter)) { + state.tokenize = previousTokenizer; + } else { + if (state.escapeNext) { + state.escapeNext = false; + } + + var ch = stream.next(); + + // Take into account the backslash for escaping characters, such as + // the string delimiter. + if (ch == "\\") { + state.escapeNext = true; + } + } + + return "string"; + }; + } + + // Apply Django template variable syntax highlighting + function inVariable (stream, state) { + // Attempt to match a dot that precedes a property + if (state.waitDot) { + state.waitDot = false; + + if (stream.peek() != ".") { + return "null"; + } + + // Dot followed by a non-word character should be considered an error. + if (stream.match(/\.\W+/)) { + return "error"; + } else if (stream.eat(".")) { + state.waitProperty = true; + return "null"; + } else { + throw Error ("Unexpected error while waiting for property."); + } + } + + // Attempt to match a pipe that precedes a filter + if (state.waitPipe) { + state.waitPipe = false; + + if (stream.peek() != "|") { + return "null"; + } + + // Pipe followed by a non-word character should be considered an error. + if (stream.match(/\.\W+/)) { + return "error"; + } else if (stream.eat("|")) { + state.waitFilter = true; + return "null"; + } else { + throw Error ("Unexpected error while waiting for filter."); + } + } + + // Highlight properties + if (state.waitProperty) { + state.waitProperty = false; + if (stream.match(/\b(\w+)\b/)) { + state.waitDot = true; // A property can be followed by another property + state.waitPipe = true; // A property can be followed by a filter + return "property"; + } + } + + // Highlight filters + if (state.waitFilter) { + state.waitFilter = false; + if (stream.match(filters)) { + return "variable-2"; + } + } + + // Ignore all white spaces + if (stream.eatSpace()) { + state.waitProperty = false; + return "null"; + } + + // Identify numbers + if (stream.match(/\b\d+(\.\d+)?\b/)) { + return "number"; + } + + // Identify strings + if (stream.match("'")) { + state.tokenize = inString("'", state.tokenize); + return "string"; + } else if (stream.match('"')) { + state.tokenize = inString('"', state.tokenize); + return "string"; + } + + // Attempt to find the variable + if (stream.match(/\b(\w+)\b/) && !state.foundVariable) { + state.waitDot = true; + state.waitPipe = true; // A property can be followed by a filter + return "variable"; + } + + // If found closing tag reset + if (stream.match("}}")) { + state.waitProperty = null; + state.waitFilter = null; + state.waitDot = null; + state.waitPipe = null; + state.tokenize = tokenBase; + return "tag"; + } + + // If nothing was found, advance to the next character + stream.next(); + return "null"; + } + + function inTag (stream, state) { + // Attempt to match a dot that precedes a property + if (state.waitDot) { + state.waitDot = false; + + if (stream.peek() != ".") { + return "null"; + } + + // Dot followed by a non-word character should be considered an error. + if (stream.match(/\.\W+/)) { + return "error"; + } else if (stream.eat(".")) { + state.waitProperty = true; + return "null"; + } else { + throw Error ("Unexpected error while waiting for property."); + } + } + + // Attempt to match a pipe that precedes a filter + if (state.waitPipe) { + state.waitPipe = false; + + if (stream.peek() != "|") { + return "null"; + } + + // Pipe followed by a non-word character should be considered an error. + if (stream.match(/\.\W+/)) { + return "error"; + } else if (stream.eat("|")) { + state.waitFilter = true; + return "null"; + } else { + throw Error ("Unexpected error while waiting for filter."); + } + } + + // Highlight properties + if (state.waitProperty) { + state.waitProperty = false; + if (stream.match(/\b(\w+)\b/)) { + state.waitDot = true; // A property can be followed by another property + state.waitPipe = true; // A property can be followed by a filter + return "property"; + } + } + + // Highlight filters + if (state.waitFilter) { + state.waitFilter = false; + if (stream.match(filters)) { + return "variable-2"; + } + } + + // Ignore all white spaces + if (stream.eatSpace()) { + state.waitProperty = false; + return "null"; + } + + // Identify numbers + if (stream.match(/\b\d+(\.\d+)?\b/)) { + return "number"; + } + + // Identify strings + if (stream.match("'")) { + state.tokenize = inString("'", state.tokenize); + return "string"; + } else if (stream.match('"')) { + state.tokenize = inString('"', state.tokenize); + return "string"; + } + + // Attempt to match an operator + if (stream.match(operators)) { + return "operator"; + } + + // Attempt to match a word operator + if (stream.match(wordOperators)) { + return "keyword"; + } + + // Attempt to match a keyword + var keywordMatch = stream.match(keywords); + if (keywordMatch) { + if (keywordMatch[0] == "comment") { + state.blockCommentTag = true; + } + return "keyword"; + } + + // Attempt to match a variable + if (stream.match(/\b(\w+)\b/)) { + state.waitDot = true; + state.waitPipe = true; // A property can be followed by a filter + return "variable"; + } + + // If found closing tag reset + if (stream.match("%}")) { + state.waitProperty = null; + state.waitFilter = null; + state.waitDot = null; + state.waitPipe = null; + // If the tag that closes is a block comment tag, we want to mark the + // following code as comment, until the tag closes. + if (state.blockCommentTag) { + state.blockCommentTag = false; // Release the "lock" + state.tokenize = inBlockComment; + } else { + state.tokenize = tokenBase; + } + return "tag"; + } + + // If nothing was found, advance to the next character + stream.next(); + return "null"; + } + + // Mark everything as comment inside the tag and the tag itself. + function inComment (stream, state) { + if (stream.match(/^.*?#\}/)) state.tokenize = tokenBase + else stream.skipToEnd() + return "comment"; + } + + // Mark everything as a comment until the `blockcomment` tag closes. + function inBlockComment (stream, state) { + if (stream.match(/\{%\s*endcomment\s*%\}/, false)) { + state.tokenize = inTag; + stream.match("{%"); + return "tag"; + } else { + stream.next(); + return "comment"; + } + } + + return { + startState: function () { + return {tokenize: tokenBase}; + }, + token: function (stream, state) { + return state.tokenize(stream, state); + }, + blockCommentStart: "{% comment %}", + blockCommentEnd: "{% endcomment %}" + }; + }); + + CodeMirror.defineMode("django", function(config) { + var htmlBase = CodeMirror.getMode(config, "text/html"); + var djangoInner = CodeMirror.getMode(config, "django:inner"); + return CodeMirror.overlayMode(htmlBase, djangoInner); + }); + + CodeMirror.defineMIME("text/x-django", "django"); +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldcode.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldcode.js new file mode 100644 index 0000000000..721bc087e2 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldcode.js @@ -0,0 +1,159 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + function doFold(cm, pos, options, force) { + if (options && options.call) { + var finder = options; + options = null; + } else { + var finder = getOption(cm, options, "rangeFinder"); + } + if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); + var minSize = getOption(cm, options, "minFoldSize"); + + function getRange(allowFolded) { + var range = finder(cm, pos); + if (!range || range.to.line - range.from.line < minSize) return null; + if (force === "fold") return range; + + var marks = cm.findMarksAt(range.from); + for (var i = 0; i < marks.length; ++i) { + if (marks[i].__isFold) { + if (!allowFolded) return null; + range.cleared = true; + marks[i].clear(); + } + } + return range; + } + + var range = getRange(true); + if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) { + pos = CodeMirror.Pos(pos.line - 1, 0); + range = getRange(false); + } + if (!range || range.cleared || force === "unfold") return; + + var myWidget = makeWidget(cm, options, range); + CodeMirror.on(myWidget, "mousedown", function(e) { + myRange.clear(); + CodeMirror.e_preventDefault(e); + }); + var myRange = cm.markText(range.from, range.to, { + replacedWith: myWidget, + clearOnEnter: getOption(cm, options, "clearOnEnter"), + __isFold: true + }); + myRange.on("clear", function(from, to) { + CodeMirror.signal(cm, "unfold", cm, from, to); + }); + CodeMirror.signal(cm, "fold", cm, range.from, range.to); + } + + function makeWidget(cm, options, range) { + var widget = getOption(cm, options, "widget"); + + if (typeof widget == "function") { + widget = widget(range.from, range.to); + } + + if (typeof widget == "string") { + var text = document.createTextNode(widget); + widget = document.createElement("span"); + widget.appendChild(text); + widget.className = "CodeMirror-foldmarker"; + } else if (widget) { + widget = widget.cloneNode(true) + } + return widget; + } + + // Clumsy backwards-compatible interface + CodeMirror.newFoldFunction = function(rangeFinder, widget) { + return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); }; + }; + + // New-style interface + CodeMirror.defineExtension("foldCode", function(pos, options, force) { + doFold(this, pos, options, force); + }); + + CodeMirror.defineExtension("isFolded", function(pos) { + var marks = this.findMarksAt(pos); + for (var i = 0; i < marks.length; ++i) + if (marks[i].__isFold) return true; + }); + + CodeMirror.commands.toggleFold = function(cm) { + cm.foldCode(cm.getCursor()); + }; + CodeMirror.commands.fold = function(cm) { + cm.foldCode(cm.getCursor(), null, "fold"); + }; + CodeMirror.commands.unfold = function(cm) { + cm.foldCode(cm.getCursor(), { scanUp: false }, "unfold"); + }; + CodeMirror.commands.foldAll = function(cm) { + cm.operation(function() { + for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) + cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, "fold"); + }); + }; + CodeMirror.commands.unfoldAll = function(cm) { + cm.operation(function() { + for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) + cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, "unfold"); + }); + }; + + CodeMirror.registerHelper("fold", "combine", function() { + var funcs = Array.prototype.slice.call(arguments, 0); + return function(cm, start) { + for (var i = 0; i < funcs.length; ++i) { + var found = funcs[i](cm, start); + if (found) return found; + } + }; + }); + + CodeMirror.registerHelper("fold", "auto", function(cm, start) { + var helpers = cm.getHelpers(start, "fold"); + for (var i = 0; i < helpers.length; i++) { + var cur = helpers[i](cm, start); + if (cur) return cur; + } + }); + + var defaultOptions = { + rangeFinder: CodeMirror.fold.auto, + widget: "\u2194", + minFoldSize: 0, + scanUp: false, + clearOnEnter: true + }; + + CodeMirror.defineOption("foldOptions", null); + + function getOption(cm, options, name) { + if (options && options[name] !== undefined) + return options[name]; + var editorOptions = cm.options.foldOptions; + if (editorOptions && editorOptions[name] !== undefined) + return editorOptions[name]; + return defaultOptions[name]; + } + + CodeMirror.defineExtension("foldOption", function(options, name) { + return getOption(this, options, name); + }); +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldgutter.css b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldgutter.css new file mode 100644 index 0000000000..ad19ae2d3e --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldgutter.css @@ -0,0 +1,20 @@ +.CodeMirror-foldmarker { + color: blue; + text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; + font-family: arial; + line-height: .3; + cursor: pointer; +} +.CodeMirror-foldgutter { + width: .7em; +} +.CodeMirror-foldgutter-open, +.CodeMirror-foldgutter-folded { + cursor: pointer; +} +.CodeMirror-foldgutter-open:after { + content: "\25BE"; +} +.CodeMirror-foldgutter-folded:after { + content: "\25B8"; +} diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldgutter.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldgutter.js new file mode 100644 index 0000000000..7d46a609b0 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/foldgutter.js @@ -0,0 +1,163 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("./foldcode")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "./foldcode"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineOption("foldGutter", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.clearGutter(cm.state.foldGutter.options.gutter); + cm.state.foldGutter = null; + cm.off("gutterClick", onGutterClick); + cm.off("changes", onChange); + cm.off("viewportChange", onViewportChange); + cm.off("fold", onFold); + cm.off("unfold", onFold); + cm.off("swapDoc", onChange); + } + if (val) { + cm.state.foldGutter = new State(parseOptions(val)); + updateInViewport(cm); + cm.on("gutterClick", onGutterClick); + cm.on("changes", onChange); + cm.on("viewportChange", onViewportChange); + cm.on("fold", onFold); + cm.on("unfold", onFold); + cm.on("swapDoc", onChange); + } + }); + + var Pos = CodeMirror.Pos; + + function State(options) { + this.options = options; + this.from = this.to = 0; + } + + function parseOptions(opts) { + if (opts === true) opts = {}; + if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter"; + if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open"; + if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded"; + return opts; + } + + function isFolded(cm, line) { + var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0)); + for (var i = 0; i < marks.length; ++i) { + if (marks[i].__isFold) { + var fromPos = marks[i].find(-1); + if (fromPos && fromPos.line === line) + return marks[i]; + } + } + } + + function marker(spec) { + if (typeof spec == "string") { + var elt = document.createElement("div"); + elt.className = spec + " CodeMirror-guttermarker-subtle"; + return elt; + } else { + return spec.cloneNode(true); + } + } + + function updateFoldInfo(cm, from, to) { + var opts = cm.state.foldGutter.options, cur = from - 1; + var minSize = cm.foldOption(opts, "minFoldSize"); + var func = cm.foldOption(opts, "rangeFinder"); + // we can reuse the built-in indicator element if its className matches the new state + var clsFolded = typeof opts.indicatorFolded == "string" && classTest(opts.indicatorFolded); + var clsOpen = typeof opts.indicatorOpen == "string" && classTest(opts.indicatorOpen); + cm.eachLine(from, to, function(line) { + ++cur; + var mark = null; + var old = line.gutterMarkers; + if (old) old = old[opts.gutter]; + if (isFolded(cm, cur)) { + if (clsFolded && old && clsFolded.test(old.className)) return; + mark = marker(opts.indicatorFolded); + } else { + var pos = Pos(cur, 0); + var range = func && func(cm, pos); + if (range && range.to.line - range.from.line >= minSize) { + if (clsOpen && old && clsOpen.test(old.className)) return; + mark = marker(opts.indicatorOpen); + } + } + if (!mark && !old) return; + cm.setGutterMarker(line, opts.gutter, mark); + }); + } + + // copied from CodeMirror/src/util/dom.js + function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } + + function updateInViewport(cm) { + var vp = cm.getViewport(), state = cm.state.foldGutter; + if (!state) return; + cm.operation(function() { + updateFoldInfo(cm, vp.from, vp.to); + }); + state.from = vp.from; state.to = vp.to; + } + + function onGutterClick(cm, line, gutter) { + var state = cm.state.foldGutter; + if (!state) return; + var opts = state.options; + if (gutter != opts.gutter) return; + var folded = isFolded(cm, line); + if (folded) folded.clear(); + else cm.foldCode(Pos(line, 0), opts); + } + + function onChange(cm) { + var state = cm.state.foldGutter; + if (!state) return; + var opts = state.options; + state.from = state.to = 0; + clearTimeout(state.changeUpdate); + state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600); + } + + function onViewportChange(cm) { + var state = cm.state.foldGutter; + if (!state) return; + var opts = state.options; + clearTimeout(state.changeUpdate); + state.changeUpdate = setTimeout(function() { + var vp = cm.getViewport(); + if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) { + updateInViewport(cm); + } else { + cm.operation(function() { + if (vp.from < state.from) { + updateFoldInfo(cm, vp.from, state.from); + state.from = vp.from; + } + if (vp.to > state.to) { + updateFoldInfo(cm, state.to, vp.to); + state.to = vp.to; + } + }); + } + }, opts.updateViewportTimeSpan || 400); + } + + function onFold(cm, from) { + var state = cm.state.foldGutter; + if (!state) return; + var line = from.line; + if (line >= state.from && line < state.to) + updateFoldInfo(cm, line, line + 1); + } +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/fullscreen.css b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/fullscreen.css new file mode 100644 index 0000000000..11c25c813f --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/fullscreen.css @@ -0,0 +1,6 @@ +.CodeMirror-fullscreen { + position: fixed; + top: 0; left: 0px; right: 0; bottom: 0; + height: auto; + z-index: 9; +} diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/fullscreen.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/fullscreen.js new file mode 100644 index 0000000000..eda7300f12 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/fullscreen.js @@ -0,0 +1,41 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { + if (old == CodeMirror.Init) old = false; + if (!old == !val) return; + if (val) setFullscreen(cm); + else setNormal(cm); + }); + + function setFullscreen(cm) { + var wrap = cm.getWrapperElement(); + cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, + width: wrap.style.width, height: wrap.style.height}; + wrap.style.width = ""; + wrap.style.height = "auto"; + wrap.className += " CodeMirror-fullscreen"; + document.documentElement.style.overflow = "hidden"; + cm.refresh(); + } + + function setNormal(cm) { + var wrap = cm.getWrapperElement(); + wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); + document.documentElement.style.overflow = ""; + var info = cm.state.fullScreenRestore; + wrap.style.width = info.width; wrap.style.height = info.height; + window.scrollTo(info.scrollLeft, info.scrollTop); + cm.refresh(); + } +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/indent-fold.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/indent-fold.js new file mode 100644 index 0000000000..0cc1126440 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/indent-fold.js @@ -0,0 +1,48 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +function lineIndent(cm, lineNo) { + var text = cm.getLine(lineNo) + var spaceTo = text.search(/\S/) + if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1)))) + return -1 + return CodeMirror.countColumn(text, null, cm.getOption("tabSize")) +} + +CodeMirror.registerHelper("fold", "indent", function(cm, start) { + var myIndent = lineIndent(cm, start.line) + if (myIndent < 0) return + var lastLineInFold = null + + // Go through lines until we find a line that definitely doesn't belong in + // the block we're folding, or to the end. + for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) { + var indent = lineIndent(cm, i) + if (indent == -1) { + } else if (indent > myIndent) { + // Lines with a greater indent are considered part of the block. + lastLineInFold = i; + } else { + // If this line has non-space, non-comment content, and is + // indented less or equal to the start line, it is the start of + // another block. + break; + } + } + if (lastLineInFold) return { + from: CodeMirror.Pos(start.line, cm.getLine(start.line).length), + to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) + }; +}); + +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/matchbrackets.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/matchbrackets.js new file mode 100644 index 0000000000..43dc8840f5 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/matchbrackets.js @@ -0,0 +1,160 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && + (document.documentMode == null || document.documentMode < 8); + + var Pos = CodeMirror.Pos; + + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; + + function bracketRegex(config) { + return config && config.bracketRegex || /[(){}[\]]/ + } + + function findMatchingBracket(cm, where, config) { + var line = cm.getLineHandle(where.line), pos = where.ch - 1; + var afterCursor = config && config.afterCursor + if (afterCursor == null) + afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) + var re = bracketRegex(config) + + // A cursor is defined as between two characters, but in in vim command mode + // (i.e. not insert mode), the cursor is visually represented as a + // highlighted box on top of the 2nd character. Otherwise, we allow matches + // from before or after the cursor. + var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || + re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)]; + if (!match) return null; + var dir = match.charAt(1) == ">" ? 1 : -1; + if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); + + var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config); + if (found == null) return null; + return {from: Pos(where.line, pos), to: found && found.pos, + match: found && found.ch == match.charAt(0), forward: dir > 0}; + } + + // bracketRegex is used to specify which type of bracket to scan + // should be a regexp, e.g. /[[\]]/ + // + // Note: If "where" is on an open bracket, then this bracket is ignored. + // + // Returns false when no bracket was found, null when it reached + // maxScanLines and gave up + function scanForBracket(cm, where, dir, style, config) { + var maxScanLen = (config && config.maxScanLineLength) || 10000; + var maxScanLines = (config && config.maxScanLines) || 1000; + + var stack = []; + var re = bracketRegex(config) + var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) + : Math.max(cm.firstLine() - 1, where.line - maxScanLines); + for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { + var line = cm.getLine(lineNo); + if (!line) continue; + var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; + if (line.length > maxScanLen) continue; + if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); + for (; pos != end; pos += dir) { + var ch = line.charAt(pos); + if (re.test(ch) && (style === undefined || + (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) { + var match = matching[ch]; + if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch); + else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; + else stack.pop(); + } + } + } + return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; + } + + function matchBrackets(cm, autoclear, config) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000, + highlightNonMatching = config && config.highlightNonMatching; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config); + if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } + + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textarea whenever this fires. + if (ie_lt8 && cm.state.focused) cm.focus(); + + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + } + + function doMatchBrackets(cm) { + cm.operation(function() { + if (cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); + }); + } + + function clearHighlighted(cm) { + if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) { + cm.state.matchBrackets.currentlyHighlighted(); + cm.state.matchBrackets.currentlyHighlighted = null; + } + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.off("cursorActivity", doMatchBrackets); + cm.off("focus", doMatchBrackets) + cm.off("blur", clearHighlighted) + clearHighlighted(cm); + } + if (val) { + cm.state.matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + cm.on("focus", doMatchBrackets) + cm.on("blur", clearHighlighted) + } + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){ + // Backwards-compatibility kludge + if (oldConfig || typeof config == "boolean") { + if (!oldConfig) { + config = config ? {strict: true} : null + } else { + oldConfig.strict = config + config = oldConfig + } + } + return findMatchingBracket(this, pos, config) + }); + CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ + return scanForBracket(this, pos, dir, style, config); + }); +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/midnight.css b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/midnight.css new file mode 100644 index 0000000000..fc26474a4a --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/midnight.css @@ -0,0 +1,39 @@ +/* Based on the theme at http://bonsaiden.github.com/JavaScript-Garden */ + +/**/ +.cm-s-midnight .CodeMirror-activeline-background { background: #253540; } + +.cm-s-midnight.CodeMirror { + background: #0F192A; + color: #D1EDFF; +} + +.cm-s-midnight div.CodeMirror-selected { background: #314D67; } +.cm-s-midnight .CodeMirror-line::selection, .cm-s-midnight .CodeMirror-line > span::selection, .cm-s-midnight .CodeMirror-line > span > span::selection { background: rgba(49, 77, 103, .99); } +.cm-s-midnight .CodeMirror-line::-moz-selection, .cm-s-midnight .CodeMirror-line > span::-moz-selection, .cm-s-midnight .CodeMirror-line > span > span::-moz-selection { background: rgba(49, 77, 103, .99); } +.cm-s-midnight .CodeMirror-gutters { background: #0F192A; border-right: 1px solid; } +.cm-s-midnight .CodeMirror-guttermarker { color: white; } +.cm-s-midnight .CodeMirror-guttermarker-subtle { color: #d0d0d0; } +.cm-s-midnight .CodeMirror-linenumber { color: #D0D0D0; } +.cm-s-midnight .CodeMirror-cursor { border-left: 1px solid #F8F8F0; } + +.cm-s-midnight span.cm-comment { color: #428BDD; } +.cm-s-midnight span.cm-atom { color: #AE81FF; } +.cm-s-midnight span.cm-number { color: #D1EDFF; } + +.cm-s-midnight span.cm-property, .cm-s-midnight span.cm-attribute { color: #A6E22E; } +.cm-s-midnight span.cm-keyword { color: #E83737; } +.cm-s-midnight span.cm-string { color: #1DC116; } + +.cm-s-midnight span.cm-variable { color: #FFAA3E; } +.cm-s-midnight span.cm-variable-2 { color: #FFAA3E; } +.cm-s-midnight span.cm-def { color: #4DD; } +.cm-s-midnight span.cm-bracket { color: #D1EDFF; } +.cm-s-midnight span.cm-tag { color: #449; } +.cm-s-midnight span.cm-link { color: #AE81FF; } +.cm-s-midnight span.cm-error { background: #F92672; color: #F8F8F0; } + +.cm-s-midnight .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/overlay.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/overlay.js new file mode 100644 index 0000000000..016e3c28cc --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/overlay.js @@ -0,0 +1,90 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +// Utility function that allows modes to be combined. The mode given +// as the base argument takes care of most of the normal mode +// functionality, but a second (typically simple) mode is used, which +// can override the style of text. Both modes get to parse all of the +// text, but when both assign a non-null style to a piece of code, the +// overlay wins, unless the combine argument was true and not overridden, +// or state.overlay.combineTokens was true, in which case the styles are +// combined. + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.overlayMode = function(base, overlay, combine) { + return { + startState: function() { + return { + base: CodeMirror.startState(base), + overlay: CodeMirror.startState(overlay), + basePos: 0, baseCur: null, + overlayPos: 0, overlayCur: null, + streamSeen: null + }; + }, + copyState: function(state) { + return { + base: CodeMirror.copyState(base, state.base), + overlay: CodeMirror.copyState(overlay, state.overlay), + basePos: state.basePos, baseCur: null, + overlayPos: state.overlayPos, overlayCur: null + }; + }, + + token: function(stream, state) { + if (stream != state.streamSeen || + Math.min(state.basePos, state.overlayPos) < stream.start) { + state.streamSeen = stream; + state.basePos = state.overlayPos = stream.start; + } + + if (stream.start == state.basePos) { + state.baseCur = base.token(stream, state.base); + state.basePos = stream.pos; + } + if (stream.start == state.overlayPos) { + stream.pos = stream.start; + state.overlayCur = overlay.token(stream, state.overlay); + state.overlayPos = stream.pos; + } + stream.pos = Math.min(state.basePos, state.overlayPos); + + // state.overlay.combineTokens always takes precedence over combine, + // unless set to null + if (state.overlayCur == null) return state.baseCur; + else if (state.baseCur != null && + state.overlay.combineTokens || + combine && state.overlay.combineTokens == null) + return state.baseCur + " " + state.overlayCur; + else return state.overlayCur; + }, + + indent: base.indent && function(state, textAfter, line) { + return base.indent(state.base, textAfter, line); + }, + electricChars: base.electricChars, + + innerMode: function(state) { return {state: state.base, mode: base}; }, + + blankLine: function(state) { + var baseToken, overlayToken; + if (base.blankLine) baseToken = base.blankLine(state.base); + if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay); + + return overlayToken == null ? + baseToken : + (combine && baseToken != null ? baseToken + " " + overlayToken : overlayToken); + } + }; +}; + +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/python.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/python.js new file mode 100644 index 0000000000..9e532ea1b3 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/python.js @@ -0,0 +1,402 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b"); + } + + var wordOperators = wordRegexp(["and", "or", "not", "is"]); + var commonKeywords = ["as", "assert", "break", "class", "continue", + "def", "del", "elif", "else", "except", "finally", + "for", "from", "global", "if", "import", + "lambda", "pass", "raise", "return", + "try", "while", "with", "yield", "in"]; + var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr", + "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", + "enumerate", "eval", "filter", "float", "format", "frozenset", + "getattr", "globals", "hasattr", "hash", "help", "hex", "id", + "input", "int", "isinstance", "issubclass", "iter", "len", + "list", "locals", "map", "max", "memoryview", "min", "next", + "object", "oct", "open", "ord", "pow", "property", "range", + "repr", "reversed", "round", "set", "setattr", "slice", + "sorted", "staticmethod", "str", "sum", "super", "tuple", + "type", "vars", "zip", "__import__", "NotImplemented", + "Ellipsis", "__debug__"]; + CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins)); + + function top(state) { + return state.scopes[state.scopes.length - 1]; + } + + CodeMirror.defineMode("python", function(conf, parserConf) { + var ERRORCLASS = "error"; + + var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/; + // (Backwards-compatibility with old, cumbersome config system) + var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters, + parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@]|\.\.\.)/] + for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1) + + var hangingIndent = parserConf.hangingIndent || conf.indentUnit; + + var myKeywords = commonKeywords, myBuiltins = commonBuiltins; + if (parserConf.extra_keywords != undefined) + myKeywords = myKeywords.concat(parserConf.extra_keywords); + + if (parserConf.extra_builtins != undefined) + myBuiltins = myBuiltins.concat(parserConf.extra_builtins); + + var py3 = !(parserConf.version && Number(parserConf.version) < 3) + if (py3) { + // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator + var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/; + myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]); + myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]); + var stringPrefixes = new RegExp("^(([rbuf]|(br)|(rb)|(fr)|(rf))?('{3}|\"{3}|['\"]))", "i"); + } else { + var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; + myKeywords = myKeywords.concat(["exec", "print"]); + myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile", + "file", "intern", "long", "raw_input", "reduce", "reload", + "unichr", "unicode", "xrange", "False", "True", "None"]); + var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i"); + } + var keywords = wordRegexp(myKeywords); + var builtins = wordRegexp(myBuiltins); + + // tokenizers + function tokenBase(stream, state) { + var sol = stream.sol() && state.lastToken != "\\" + if (sol) state.indent = stream.indentation() + // Handle scope changes + if (sol && top(state).type == "py") { + var scopeOffset = top(state).offset; + if (stream.eatSpace()) { + var lineOffset = stream.indentation(); + if (lineOffset > scopeOffset) + pushPyScope(state); + else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#") + state.errorToken = true; + return null; + } else { + var style = tokenBaseInner(stream, state); + if (scopeOffset > 0 && dedent(stream, state)) + style += " " + ERRORCLASS; + return style; + } + } + return tokenBaseInner(stream, state); + } + + function tokenBaseInner(stream, state, inFormat) { + if (stream.eatSpace()) return null; + + // Handle Comments + if (!inFormat && stream.match(/^#.*/)) return "comment"; + + // Handle Number Literals + if (stream.match(/^[0-9\.]/, false)) { + var floatLiteral = false; + // Floats + if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } + if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; } + if (stream.match(/^\.\d+/)) { floatLiteral = true; } + if (floatLiteral) { + // Float literals may be "imaginary" + stream.eat(/J/i); + return "number"; + } + // Integers + var intLiteral = false; + // Hex + if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true; + // Binary + if (stream.match(/^0b[01_]+/i)) intLiteral = true; + // Octal + if (stream.match(/^0o[0-7_]+/i)) intLiteral = true; + // Decimal + if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) { + // Decimal literals may be "imaginary" + stream.eat(/J/i); + // TODO - Can you have imaginary longs? + intLiteral = true; + } + // Zero by itself with no other piece of number. + if (stream.match(/^0(?![\dx])/i)) intLiteral = true; + if (intLiteral) { + // Integer literals may be "long" + stream.eat(/L/i); + return "number"; + } + } + + // Handle Strings + if (stream.match(stringPrefixes)) { + var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1; + if (!isFmtString) { + state.tokenize = tokenStringFactory(stream.current(), state.tokenize); + return state.tokenize(stream, state); + } else { + state.tokenize = formatStringFactory(stream.current(), state.tokenize); + return state.tokenize(stream, state); + } + } + + for (var i = 0; i < operators.length; i++) + if (stream.match(operators[i])) return "operator" + + if (stream.match(delimiters)) return "punctuation"; + + if (state.lastToken == "." && stream.match(identifiers)) + return "property"; + + if (stream.match(keywords) || stream.match(wordOperators)) + return "keyword"; + + if (stream.match(builtins)) + return "builtin"; + + if (stream.match(/^(self|cls)\b/)) + return "variable-2"; + + if (stream.match(identifiers)) { + if (state.lastToken == "def" || state.lastToken == "class") + return "def"; + return "variable"; + } + + // Handle non-detected items + stream.next(); + return inFormat ? null :ERRORCLASS; + } + + function formatStringFactory(delimiter, tokenOuter) { + while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0) + delimiter = delimiter.substr(1); + + var singleline = delimiter.length == 1; + var OUTCLASS = "string"; + + function tokenNestedExpr(depth) { + return function(stream, state) { + var inner = tokenBaseInner(stream, state, true) + if (inner == "punctuation") { + if (stream.current() == "{") { + state.tokenize = tokenNestedExpr(depth + 1) + } else if (stream.current() == "}") { + if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1) + else state.tokenize = tokenString + } + } + return inner + } + } + + function tokenString(stream, state) { + while (!stream.eol()) { + stream.eatWhile(/[^'"\{\}\\]/); + if (stream.eat("\\")) { + stream.next(); + if (singleline && stream.eol()) + return OUTCLASS; + } else if (stream.match(delimiter)) { + state.tokenize = tokenOuter; + return OUTCLASS; + } else if (stream.match('{{')) { + // ignore {{ in f-str + return OUTCLASS; + } else if (stream.match('{', false)) { + // switch to nested mode + state.tokenize = tokenNestedExpr(0) + if (stream.current()) return OUTCLASS; + else return state.tokenize(stream, state) + } else if (stream.match('}}')) { + return OUTCLASS; + } else if (stream.match('}')) { + // single } in f-string is an error + return ERRORCLASS; + } else { + stream.eat(/['"]/); + } + } + if (singleline) { + if (parserConf.singleLineStringErrors) + return ERRORCLASS; + else + state.tokenize = tokenOuter; + } + return OUTCLASS; + } + tokenString.isString = true; + return tokenString; + } + + function tokenStringFactory(delimiter, tokenOuter) { + while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0) + delimiter = delimiter.substr(1); + + var singleline = delimiter.length == 1; + var OUTCLASS = "string"; + + function tokenString(stream, state) { + while (!stream.eol()) { + stream.eatWhile(/[^'"\\]/); + if (stream.eat("\\")) { + stream.next(); + if (singleline && stream.eol()) + return OUTCLASS; + } else if (stream.match(delimiter)) { + state.tokenize = tokenOuter; + return OUTCLASS; + } else { + stream.eat(/['"]/); + } + } + if (singleline) { + if (parserConf.singleLineStringErrors) + return ERRORCLASS; + else + state.tokenize = tokenOuter; + } + return OUTCLASS; + } + tokenString.isString = true; + return tokenString; + } + + function pushPyScope(state) { + while (top(state).type != "py") state.scopes.pop() + state.scopes.push({offset: top(state).offset + conf.indentUnit, + type: "py", + align: null}) + } + + function pushBracketScope(stream, state, type) { + var align = stream.match(/^[\s\[\{\(]*(?:#|$)/, false) ? null : stream.column() + 1 + state.scopes.push({offset: state.indent + hangingIndent, + type: type, + align: align}) + } + + function dedent(stream, state) { + var indented = stream.indentation(); + while (state.scopes.length > 1 && top(state).offset > indented) { + if (top(state).type != "py") return true; + state.scopes.pop(); + } + return top(state).offset != indented; + } + + function tokenLexer(stream, state) { + if (stream.sol()) { + state.beginningOfLine = true; + state.dedent = false; + } + + var style = state.tokenize(stream, state); + var current = stream.current(); + + // Handle decorators + if (state.beginningOfLine && current == "@") + return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS; + + if (/\S/.test(current)) state.beginningOfLine = false; + + if ((style == "variable" || style == "builtin") + && state.lastToken == "meta") + style = "meta"; + + // Handle scope changes. + if (current == "pass" || current == "return") + state.dedent = true; + + if (current == "lambda") state.lambda = true; + if (current == ":" && !state.lambda && top(state).type == "py" && stream.match(/^\s*(?:#|$)/, false)) + pushPyScope(state); + + if (current.length == 1 && !/string|comment/.test(style)) { + var delimiter_index = "[({".indexOf(current); + if (delimiter_index != -1) + pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1)); + + delimiter_index = "])}".indexOf(current); + if (delimiter_index != -1) { + if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent + else return ERRORCLASS; + } + } + if (state.dedent && stream.eol() && top(state).type == "py" && state.scopes.length > 1) + state.scopes.pop(); + + return style; + } + + var external = { + startState: function(basecolumn) { + return { + tokenize: tokenBase, + scopes: [{offset: basecolumn || 0, type: "py", align: null}], + indent: basecolumn || 0, + lastToken: null, + lambda: false, + dedent: 0 + }; + }, + + token: function(stream, state) { + var addErr = state.errorToken; + if (addErr) state.errorToken = false; + var style = tokenLexer(stream, state); + + if (style && style != "comment") + state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style; + if (style == "punctuation") style = null; + + if (stream.eol() && state.lambda) + state.lambda = false; + return addErr ? style + " " + ERRORCLASS : style; + }, + + indent: function(state, textAfter) { + if (state.tokenize != tokenBase) + return state.tokenize.isString ? CodeMirror.Pass : 0; + + var scope = top(state) + var closing = scope.type == textAfter.charAt(0) || + scope.type == "py" && !state.dedent && /^(else:|elif |except |finally:)/.test(textAfter) + if (scope.align != null) + return scope.align - (closing ? 1 : 0) + else + return scope.offset - (closing ? hangingIndent : 0) + }, + + electricInput: /^\s*([\}\]\)]|else:|elif |except |finally:)$/, + closeBrackets: {triples: "'\""}, + lineComment: "#", + fold: "indent" + }; + return external; + }); + + CodeMirror.defineMIME("text/x-python", "python"); + + var words = function(str) { return str.split(" "); }; + + CodeMirror.defineMIME("text/x-cython", { + name: "python", + extra_keywords: words("by cdef cimport cpdef ctypedef enum except "+ + "extern gil include nogil property public "+ + "readonly struct union DEF IF ELIF ELSE") + }); + +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/xml.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/xml.js new file mode 100644 index 0000000000..4e36106b49 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/xml.js @@ -0,0 +1,417 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +var htmlConfig = { + autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, + 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, + 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, + 'track': true, 'wbr': true, 'menuitem': true}, + implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, + 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, + 'th': true, 'tr': true}, + contextGrabbers: { + 'dd': {'dd': true, 'dt': true}, + 'dt': {'dd': true, 'dt': true}, + 'li': {'li': true}, + 'option': {'option': true, 'optgroup': true}, + 'optgroup': {'optgroup': true}, + 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, + 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, + 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, + 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, + 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, + 'rp': {'rp': true, 'rt': true}, + 'rt': {'rp': true, 'rt': true}, + 'tbody': {'tbody': true, 'tfoot': true}, + 'td': {'td': true, 'th': true}, + 'tfoot': {'tbody': true}, + 'th': {'td': true, 'th': true}, + 'thead': {'tbody': true, 'tfoot': true}, + 'tr': {'tr': true} + }, + doNotIndent: {"pre": true}, + allowUnquoted: true, + allowMissing: true, + caseFold: true +} + +var xmlConfig = { + autoSelfClosers: {}, + implicitlyClosed: {}, + contextGrabbers: {}, + doNotIndent: {}, + allowUnquoted: false, + allowMissing: false, + allowMissingTagName: false, + caseFold: false +} + +CodeMirror.defineMode("xml", function(editorConf, config_) { + var indentUnit = editorConf.indentUnit + var config = {} + var defaults = config_.htmlMode ? htmlConfig : xmlConfig + for (var prop in defaults) config[prop] = defaults[prop] + for (var prop in config_) config[prop] = config_[prop] + + // Return variables for tokenizers + var type, setStyle; + + function inText(stream, state) { + function chain(parser) { + state.tokenize = parser; + return parser(stream, state); + } + + var ch = stream.next(); + if (ch == "<") { + if (stream.eat("!")) { + if (stream.eat("[")) { + if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); + else return null; + } else if (stream.match("--")) { + return chain(inBlock("comment", "-->")); + } else if (stream.match("DOCTYPE", true, true)) { + stream.eatWhile(/[\w\._\-]/); + return chain(doctype(1)); + } else { + return null; + } + } else if (stream.eat("?")) { + stream.eatWhile(/[\w\._\-]/); + state.tokenize = inBlock("meta", "?>"); + return "meta"; + } else { + type = stream.eat("/") ? "closeTag" : "openTag"; + state.tokenize = inTag; + return "tag bracket"; + } + } else if (ch == "&") { + var ok; + if (stream.eat("#")) { + if (stream.eat("x")) { + ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); + } else { + ok = stream.eatWhile(/[\d]/) && stream.eat(";"); + } + } else { + ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); + } + return ok ? "atom" : "error"; + } else { + stream.eatWhile(/[^&<]/); + return null; + } + } + inText.isInText = true; + + function inTag(stream, state) { + var ch = stream.next(); + if (ch == ">" || (ch == "/" && stream.eat(">"))) { + state.tokenize = inText; + type = ch == ">" ? "endTag" : "selfcloseTag"; + return "tag bracket"; + } else if (ch == "=") { + type = "equals"; + return null; + } else if (ch == "<") { + state.tokenize = inText; + state.state = baseState; + state.tagName = state.tagStart = null; + var next = state.tokenize(stream, state); + return next ? next + " tag error" : "tag error"; + } else if (/[\'\"]/.test(ch)) { + state.tokenize = inAttribute(ch); + state.stringStartCol = stream.column(); + return state.tokenize(stream, state); + } else { + stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/); + return "word"; + } + } + + function inAttribute(quote) { + var closure = function(stream, state) { + while (!stream.eol()) { + if (stream.next() == quote) { + state.tokenize = inTag; + break; + } + } + return "string"; + }; + closure.isInAttribute = true; + return closure; + } + + function inBlock(style, terminator) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.match(terminator)) { + state.tokenize = inText; + break; + } + stream.next(); + } + return style; + } + } + + function doctype(depth) { + return function(stream, state) { + var ch; + while ((ch = stream.next()) != null) { + if (ch == "<") { + state.tokenize = doctype(depth + 1); + return state.tokenize(stream, state); + } else if (ch == ">") { + if (depth == 1) { + state.tokenize = inText; + break; + } else { + state.tokenize = doctype(depth - 1); + return state.tokenize(stream, state); + } + } + } + return "meta"; + }; + } + + function lower(tagName) { + return tagName && tagName.toLowerCase(); + } + + function Context(state, tagName, startOfLine) { + this.prev = state.context; + this.tagName = tagName || ""; + this.indent = state.indented; + this.startOfLine = startOfLine; + if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) + this.noIndent = true; + } + function popContext(state) { + if (state.context) state.context = state.context.prev; + } + function maybePopContext(state, nextTagName) { + var parentTagName; + while (true) { + if (!state.context) { + return; + } + parentTagName = state.context.tagName; + if (!config.contextGrabbers.hasOwnProperty(lower(parentTagName)) || + !config.contextGrabbers[lower(parentTagName)].hasOwnProperty(lower(nextTagName))) { + return; + } + popContext(state); + } + } + + function baseState(type, stream, state) { + if (type == "openTag") { + state.tagStart = stream.column(); + return tagNameState; + } else if (type == "closeTag") { + return closeTagNameState; + } else { + return baseState; + } + } + function tagNameState(type, stream, state) { + if (type == "word") { + state.tagName = stream.current(); + setStyle = "tag"; + return attrState; + } else if (config.allowMissingTagName && type == "endTag") { + setStyle = "tag bracket"; + return attrState(type, stream, state); + } else { + setStyle = "error"; + return tagNameState; + } + } + function closeTagNameState(type, stream, state) { + if (type == "word") { + var tagName = stream.current(); + if (state.context && state.context.tagName != tagName && + config.implicitlyClosed.hasOwnProperty(lower(state.context.tagName))) + popContext(state); + if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) { + setStyle = "tag"; + return closeState; + } else { + setStyle = "tag error"; + return closeStateErr; + } + } else if (config.allowMissingTagName && type == "endTag") { + setStyle = "tag bracket"; + return closeState(type, stream, state); + } else { + setStyle = "error"; + return closeStateErr; + } + } + + function closeState(type, _stream, state) { + if (type != "endTag") { + setStyle = "error"; + return closeState; + } + popContext(state); + return baseState; + } + function closeStateErr(type, stream, state) { + setStyle = "error"; + return closeState(type, stream, state); + } + + function attrState(type, _stream, state) { + if (type == "word") { + setStyle = "attribute"; + return attrEqState; + } else if (type == "endTag" || type == "selfcloseTag") { + var tagName = state.tagName, tagStart = state.tagStart; + state.tagName = state.tagStart = null; + if (type == "selfcloseTag" || + config.autoSelfClosers.hasOwnProperty(lower(tagName))) { + maybePopContext(state, tagName); + } else { + maybePopContext(state, tagName); + state.context = new Context(state, tagName, tagStart == state.indented); + } + return baseState; + } + setStyle = "error"; + return attrState; + } + function attrEqState(type, stream, state) { + if (type == "equals") return attrValueState; + if (!config.allowMissing) setStyle = "error"; + return attrState(type, stream, state); + } + function attrValueState(type, stream, state) { + if (type == "string") return attrContinuedState; + if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;} + setStyle = "error"; + return attrState(type, stream, state); + } + function attrContinuedState(type, stream, state) { + if (type == "string") return attrContinuedState; + return attrState(type, stream, state); + } + + return { + startState: function(baseIndent) { + var state = {tokenize: inText, + state: baseState, + indented: baseIndent || 0, + tagName: null, tagStart: null, + context: null} + if (baseIndent != null) state.baseIndent = baseIndent + return state + }, + + token: function(stream, state) { + if (!state.tagName && stream.sol()) + state.indented = stream.indentation(); + + if (stream.eatSpace()) return null; + type = null; + var style = state.tokenize(stream, state); + if ((style || type) && style != "comment") { + setStyle = null; + state.state = state.state(type || style, stream, state); + if (setStyle) + style = setStyle == "error" ? style + " error" : setStyle; + } + return style; + }, + + indent: function(state, textAfter, fullLine) { + var context = state.context; + // Indent multi-line strings (e.g. css). + if (state.tokenize.isInAttribute) { + if (state.tagStart == state.indented) + return state.stringStartCol + 1; + else + return state.indented + indentUnit; + } + if (context && context.noIndent) return CodeMirror.Pass; + if (state.tokenize != inTag && state.tokenize != inText) + return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; + // Indent the starts of attribute names. + if (state.tagName) { + if (config.multilineTagIndentPastTag !== false) + return state.tagStart + state.tagName.length + 2; + else + return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1); + } + if (config.alignCDATA && /$/, + blockCommentStart: "", + + configuration: config.htmlMode ? "html" : "xml", + helperType: config.htmlMode ? "html" : "xml", + + skipAttribute: function(state) { + if (state.state == attrValueState) + state.state = attrState + }, + + xmlCurrentTag: function(state) { + return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null + }, + + xmlCurrentContext: function(state) { + var context = [] + for (var cx = state.context; cx; cx = cx.prev) + context.push(cx.tagName) + return context.reverse() + } + }; +}); + +CodeMirror.defineMIME("text/xml", "xml"); +CodeMirror.defineMIME("application/xml", "xml"); +if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) + CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true}); + +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/yaml.js b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/yaml.js new file mode 100644 index 0000000000..d464941026 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/codemirror/yaml.js @@ -0,0 +1,120 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("yaml", function() { + + var cons = ['true', 'false', 'on', 'off', 'yes', 'no']; + var keywordRegex = new RegExp("\\b(("+cons.join(")|(")+"))$", 'i'); + + return { + token: function(stream, state) { + var ch = stream.peek(); + var esc = state.escaped; + state.escaped = false; + /* comments */ + if (ch == "#" && (stream.pos == 0 || /\s/.test(stream.string.charAt(stream.pos - 1)))) { + stream.skipToEnd(); + return "comment"; + } + + if (stream.match(/^('([^']|\\.)*'?|"([^"]|\\.)*"?)/)) + return "string"; + + if (state.literal && stream.indentation() > state.keyCol) { + stream.skipToEnd(); return "string"; + } else if (state.literal) { state.literal = false; } + if (stream.sol()) { + state.keyCol = 0; + state.pair = false; + state.pairStart = false; + /* document start */ + if(stream.match('---')) { return "def"; } + /* document end */ + if (stream.match('...')) { return "def"; } + /* array list item */ + if (stream.match(/\s*-\s+/)) { return 'meta'; } + } + /* inline pairs/lists */ + if (stream.match(/^(\{|\}|\[|\])/)) { + if (ch == '{') + state.inlinePairs++; + else if (ch == '}') + state.inlinePairs--; + else if (ch == '[') + state.inlineList++; + else + state.inlineList--; + return 'meta'; + } + + /* list separator */ + if (state.inlineList > 0 && !esc && ch == ',') { + stream.next(); + return 'meta'; + } + /* pairs separator */ + if (state.inlinePairs > 0 && !esc && ch == ',') { + state.keyCol = 0; + state.pair = false; + state.pairStart = false; + stream.next(); + return 'meta'; + } + + /* start of value of a pair */ + if (state.pairStart) { + /* block literals */ + if (stream.match(/^\s*(\||\>)\s*/)) { state.literal = true; return 'meta'; }; + /* references */ + if (stream.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i)) { return 'variable-2'; } + /* numbers */ + if (state.inlinePairs == 0 && stream.match(/^\s*-?[0-9\.\,]+\s?$/)) { return 'number'; } + if (state.inlinePairs > 0 && stream.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/)) { return 'number'; } + /* keywords */ + if (stream.match(keywordRegex)) { return 'keyword'; } + } + + /* pairs (associative arrays) -> key */ + if (!state.pair && stream.match(/^\s*(?:[,\[\]{}&*!|>'"%@`][^\s'":]|[^,\[\]{}#&*!|>'"%@`])[^#]*?(?=\s*:($|\s))/)) { + state.pair = true; + state.keyCol = stream.indentation(); + return "atom"; + } + if (state.pair && stream.match(/^:\s*/)) { state.pairStart = true; return 'meta'; } + + /* nothing found, continue */ + state.pairStart = false; + state.escaped = (ch == '\\'); + stream.next(); + return null; + }, + startState: function() { + return { + pair: false, + pairStart: false, + keyCol: 0, + inlinePairs: 0, + inlineList: 0, + literal: false, + escaped: false + }; + }, + lineComment: "#", + fold: "indent" + }; +}); + +CodeMirror.defineMIME("text/x-yaml", "yaml"); +CodeMirror.defineMIME("text/yaml", "yaml"); + +}); diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/colorful.css b/src/hct_mis_api/apps/steficon/static/admin/power_query/colorful.css new file mode 100644 index 0000000000..39d57e960a --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/colorful.css @@ -0,0 +1,74 @@ +pre { line-height: 125%; } +td.linenos pre { color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; } +span.linenos { color: #000000; background-color: #f0f0f0; padding-left: 5px; padding-right: 5px; } +td.linenos pre.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #ffffff; } +.highlight .c { color: #888888 } /* Comment */ +.highlight .err { color: #FF0000; background-color: #FFAAAA } /* Error */ +.highlight .k { color: #008800; font-weight: bold } /* Keyword */ +.highlight .o { color: #333333 } /* Operator */ +.highlight .ch { color: #888888 } /* Comment.Hashbang */ +.highlight .cm { color: #888888 } /* Comment.Multiline */ +.highlight .cp { color: #557799 } /* Comment.Preproc */ +.highlight .cpf { color: #888888 } /* Comment.PreprocFile */ +.highlight .c1 { color: #888888 } /* Comment.Single */ +.highlight .cs { color: #cc0000; font-weight: bold } /* Comment.Special */ +.highlight .gd { color: #A00000 } /* Generic.Deleted */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #FF0000 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #0044DD } /* Generic.Traceback */ +.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #003388; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #333399; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #6600EE; font-weight: bold } /* Literal.Number */ +.highlight .s { background-color: #fff0f0 } /* Literal.String */ +.highlight .na { color: #0000CC } /* Name.Attribute */ +.highlight .nb { color: #007020 } /* Name.Builtin */ +.highlight .nc { color: #BB0066; font-weight: bold } /* Name.Class */ +.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ +.highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #880000; font-weight: bold } /* Name.Entity */ +.highlight .ne { color: #FF0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #0066BB; font-weight: bold } /* Name.Function */ +.highlight .nl { color: #997700; font-weight: bold } /* Name.Label */ +.highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.highlight .nt { color: #007700 } /* Name.Tag */ +.highlight .nv { color: #996633 } /* Name.Variable */ +.highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mb { color: #6600EE; font-weight: bold } /* Literal.Number.Bin */ +.highlight .mf { color: #6600EE; font-weight: bold } /* Literal.Number.Float */ +.highlight .mh { color: #005588; font-weight: bold } /* Literal.Number.Hex */ +.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ +.highlight .mo { color: #4400EE; font-weight: bold } /* Literal.Number.Oct */ +.highlight .sa { background-color: #fff0f0 } /* Literal.String.Affix */ +.highlight .sb { background-color: #fff0f0 } /* Literal.String.Backtick */ +.highlight .sc { color: #0044DD } /* Literal.String.Char */ +.highlight .dl { background-color: #fff0f0 } /* Literal.String.Delimiter */ +.highlight .sd { color: #DD4422 } /* Literal.String.Doc */ +.highlight .s2 { background-color: #fff0f0 } /* Literal.String.Double */ +.highlight .se { color: #666666; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */ +.highlight .sh { background-color: #fff0f0 } /* Literal.String.Heredoc */ +.highlight .si { background-color: #eeeeee } /* Literal.String.Interpol */ +.highlight .sx { color: #DD2200; background-color: #fff0f0 } /* Literal.String.Other */ +.highlight .sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */ +.highlight .s1 { background-color: #fff0f0 } /* Literal.String.Single */ +.highlight .ss { color: #AA6600 } /* Literal.String.Symbol */ +.highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #0066BB; font-weight: bold } /* Name.Function.Magic */ +.highlight .vc { color: #336699 } /* Name.Variable.Class */ +.highlight .vg { color: #dd7700; font-weight: bold } /* Name.Variable.Global */ +.highlight .vi { color: #3333BB } /* Name.Variable.Instance */ +.highlight .vm { color: #996633 } /* Name.Variable.Magic */ +.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/diff.css b/src/hct_mis_api/apps/steficon/static/admin/power_query/diff.css new file mode 100644 index 0000000000..e4793e1e52 --- /dev/null +++ b/src/hct_mis_api/apps/steficon/static/admin/power_query/diff.css @@ -0,0 +1,53 @@ +/*.pager{*/ +/* padding: 10px;*/ +/* margin-bottom: 20px;*/ +/*}*/ +table.diff, table.diff td { + font-family: monospace, sans-serif; +} +table.diff{ + font-size: 14px; + border: medium; + width: 100%; +} +table.diff td { + font-size: 16px; +} + +table.diff td.diff_header.lineno{ + width: 20px!important; + max-width: 20px!important; +} +table.diff td.code{ + width: 300px!important; +} + +th.diff_header { + font-weight: bold; + width:50%; + max-width:50%; +} + +td.diff_header { + text-align: right; + max-width:30px; + width:30px; +} + +.diff_next { + display: none; + max-width:30px; + width:30px; +} + +.diff_add { + background-color: #aaffaa +} + +.diff_chg { + background-color: #ffff77 +} + +.diff_sub { + background-color: #ffaaaa +} diff --git a/src/hct_mis_api/apps/steficon/static/admin/power_query/editor.png b/src/hct_mis_api/apps/steficon/static/admin/power_query/editor.png new file mode 100644 index 0000000000000000000000000000000000000000..ffea4e6aa163ba2cf184e2970ef11724bce4ef72 GIT binary patch literal 16556 zcmdUXcU+TK-?y!|)~bjoNHq#o3W6&_#V9gbWhkf!sEA^sA`TEE3X&j6oXke5B9UD{ z6a@*OAV|n10#OLFfApU?aLWvMjdoa=ml-*p}@bf|+t9?FB z=wm*9htIlC`S~dNi2L{3ybd3CcXU5|6zg5)ZVGq zru<~E&GP5nn8ty)rn3VZGAs?p#>3--at}WHeyN%|!r($&aMWwHMFlIL#QyN>G`-6D zaY;318u z@10*2Jz3nyq~NxtxMW_Zo`+{Wu{L{XJ7Vl(md-Z9_*#_vaUCn~g@rm6-Z0yY(qUbs zF6i`SceT~CUv)OEb$VaZJkq~}Jp&o^XlVfK*uz`Gua6O-VS!Cge=gPZ#ueez8eU*; z(u-k4AlIlejF%bXJb=DqKq#g%y$;`+C#9tvVk6EI3PYXqtl#ou_~%o8BfIbphiLK+ zTfpr`H1^JUl0geiTsjQuL) zICys`Nqldg2ITt`#*m<1L(9m&A8+vw^dkA%(s`ftB9T`D4 zNw-JIHO#fn`h+)I#KbtNI=p^tUn0sH<HO>~!f!)2W}jROdA}jG>sH-Q#S3m~3ZwUGM`9ebN4emWc`W9)$E3e1=}AR^-5N!C zPrXh>Ho)m(6->)Wa$VD_6jz;;dN&CH)D(5Ml>|wKRS`R4FkMM!Ra8^H(pYL}Of%;JnIUga~G)_?lq?BI-5# zN2r!gkLmXEpv=aZm8P)+Y%7_mW-pi+TiL6(q0{N~d1_f&=9yZ#vGxxHut13Y8+Cux z{W_uhh;hF8Dt40jKzF^rc}f?0qz*O)+aiJ!cKY{to`!ybYPtIMItJIMeRE?Rsg?HT z<`nUYLT>xL>7KLIRit$3X4d1G&mlrJd|ozSQ0s3a@LcoPth7q z&KS@U3$iHtyAiJ4VaV~#U5Kt8x|zu2j)3UQmM>SsI;hPAHF9?R2);uNXIzQSt4af( zZ$7T_q4F(^tI@8ca}{XH1KIB|M(7pjRi*vymC58%F){3s3c)Q4FPIJ&6Q{{O&6{Fh zF|l7Vi{Pe9=m1;1znuA-c>oEz!9x2e7Y5;i{*CO%qmRK@HhN6jOD7ZRmUFMkekD4X zY3Po;`CewvC1H)jCL-9qr@0&*G4cKgYrlbk`Z?aK&gCV{?K*zee(D&yVJ%ak^F0gI0542?rl$60Eqe6_!2)TJ7kFZUj|Yw0#iuEquiI(Zs`Q z0<~ql^u88S>w>Hs6578XO+!v|IgNEcZbB%!RTb-n}lIIF*ojqq0t2JqiO_fW_1$t2R#?xIM{4d$jnX~a#a zNMdk~SKo&|Lnwbi$+K`D_HXbZ*p@NhNh413??)(*qf;8OjS~Id-ar6rPRKqrUMevF zFGgebw7G|9Yv?8Hv)Q`afZ26?#SElL;p?L}+tx2^TC~N|PLR~6CiJK$U04v!-%agV zUJ@+z{GR-hW96dS@nO|GDm3?mOP>K4ya6Q&S$4=C&;7W4?N+#kYWh4}h?r=50&7!v z2{L>#?!OkLj;kCb9pGBE8IPEw2>wr|TJ&8Ks4xUcj5lUn*tro_JZ?#an3jeygbXoe zr=po>;^6-I3ZRYd0tmAQiN75}JvOmY(cOp)`o=CqYn|+OJcaIEh{$#@ylcU*p_RS~ zgtGQQ0arAow98;oHT&C4q%@xN8kBVt@-CZ&Tg^i6(x%dxuQ&)3+NL+kfNj`bbwE5!GJIIAA9~`Vk{98CE#gmLoWs!9|zN3IYRI8yd=yl2@_@} zkaUG6n#cAIBm5OZn34aL4GKO;3I}W;8Z$_B6EdaWoig3XeQl6d!|1t@?|(EyYcN~y zT@GT-&iG>c`M!7u?L*y`{+4T^@u|eBY8(?!WS}dTL&=o zH1Y?tbG1Qz&gQ1 ztQtTNc3G6071UXCLz8w7bP9*CA(kYl5@3oO6xQ;dl&o*J$(g4H!22{VM+9?dWeB#F zOkSG?BZ&PrWv0lv2W`1DDTp_hkZcOE_n_DD!P+ zRvF<@@f!ie8WTyncy+ErzW^3lG4+hOd!cOd1DsG01ZNH+V-#L>Kb?hk9o~!z6Ts?% z-g6MEqL}Xj%A+NuXmFCr{iKZvznAv+OK9BnfG$WY;ouqon7gj{J{sL%Vrh=j`vPWChJ+5Gq&zqN@oo>+HNx0; zfV+(enAnQ6dS-Ck%r1oVHg&&*82{dB7%`pu1x7+%i}Dd|A%41pGr3kV@VMi6w_fQa zKGMWJWJh`RKJL5{HS%Rp1%Tu*vN^#BoWN%mDV;KOc_MPMRdAfMgz_Jdl>l7CDns@z zx#R892taN+&?CdyCE=mB?6Ymoy9%{lJr$I_3?gA;Tl1Ou?P3)cx@2k65HY%~(PL~WquYv{-KZzVX5h9Vml3uwUwRkK!tF@=NJRk3ytfYt4INS}8}ATR zUl`p4f7%uH!m|fQJ#7S^Hh!j29M~V=a0`380$ouLrr1U<4Bk;HroH23p2QdXQhQiZ z@RN?vLk2L;*wRUF0jNk`m8NkNyj)DnmQ3KTn*s7IHffyVK9X}2@QjUD5rysO?Smxr z@C)X?U*-Gm`jVN~3JoLnau90(H%Jky^cDLFU@lT?CH#eUgCjFb1fazo6V;_lSRwN3 zwclzseib_W3>8^`%ysUw52nWVkkFjQdYH@m-&QWem{kH0&C<$fLpL`;qtZZK!W5Bx zFvNZWd4~bwLsJl+!;Q?IM+!=NX>ko>lZbVce3fBLZkgN2royoCuU>F1MNJ7qahuDl z*7VwsX=3xKIA4Zk9IEb{hT4ScjB`0+rUp8=SydEEb?v7=c6#!2VO!hVQHihP7)%H2 za<(sumq{Y!Y47bq@4EweqsIXr%w@IBZ$}So+5RuMx5cT5?la&MO=jEe_=e${{;cQ= z8LiKm-!&4hNv8|_-JsZ`dx|`N)n+w@+hV6Vta#gEVp7W@b3J024eyWeMXc}3jS4)s zFaIrxe3{xPA%6dUWx)l9=C*q?xhBlHb2nYK_Hrq_v(HBGoeL zl$hn*FAgk-Wp0%a{T0(^p6u;WP;7I`9P&cSDt7Xn@cq@y%%fTtxd#0_I*GC&A^Uqd zUgv@7kVEh`cVA8DxC~agLb=LiVn9CL3_x{a@Ja`v3PHpgBU5_3`Y`%Y4KqTA)L?pReq%I~F zQSLCmqkMS33bbhw!21f#7U?dXNxTh^ojQaLP(KqoqYHuG0ab{t;c#^ImDTKJkYA(3 zK$mbu=iE$libl@cS(8mBio{#+aGl$dTd7TWOfc3A`AP zEXDMB77q=3{V$wtFA}FF1O#9nj8qlh*5llp4i34iWIr`k96beREE`|q{D4~fU^NNj zCuBG)EL)4#pIFA*wfq)Iu3N^`gpL$TX&^ORwqKKD?=Wr_qGdFL`Q%@vpbhV&!EHbPdQ0ppF4agH$L5K6XGasU~fB5!c}Lk z0%-(EK(RDU;vl!Rol^Dgsn>9@^#{9N%@r|!-qA3RH5iF~=U{4R83z+ZHiC4#k| zKk19{?OXYv!U-u%iyS-swU|hF{`oU@fuLiYcfLO{86GAfE+M2tZx< z*+H8Wicf?s{^=aPKnr7Bg&z2&BoOanH^NlpYm7^~jiGi*rb+*O7B!_&RMlPToTqwO2f}Id{-X}WyNg%<+pV}gA;{k(G|=F zobkNSSM@-<7Ad&xRQZ_zuOJ2H$5z4?Ghqi<%o0I_ZTwVW^ns5n*&bl4OGr*upmM1I z*3*AYl5LXkHMsyE(XPSF61~meufUNJ#Hq9w7oF3f8+X2|K#Qm1a)dB5=6#^$y5!Sz zLORKXy#ewtwP!SF(Wk;#qbsa6?{>Aeaz24qiN;3>h)85>{5J>Mt?=(<%tAVyf4`z0 zybsVbv$%j!6!jl|twfl6EeVc&0N6W)zlxmn*dA0RBLTyie;>TqLPBy7;D5XC)Owhx z>yA}V6|A=qj13z_wVqs2HavW~n0x!UGqfDF+g!eKkKlwgsFC?cEXR$1Ww;5F(u3F^ z*tv06Sh)?s6%|T)#jN3b%h=-Uz7GbOT_?InH;~#W9>&(iD~rzLCvryy+tJ#mcg~!(G_L{WdW5YrjdI|Kr{j~ zD!vCA->k%vXT2H(H{{<^HO{6$E(4>h1Ff#@wcKl87M^^l_0VVj(Nk zxn>*ov235;+7E8sNHO90GMU47)wlH#D~}h;4H3XYCe5k~gyMMwH%QAw=m^}1l3!C1 zr)7LSJB0A zVY9T(a{(Kv-q9E;vpHWeXE+RzfXi7(RR=$eHB#G6gJylIO2L_`mZo4H*d3yOQO)FN z-{t@@tEzUx?c=VX5U*q4WC1#0qAr0%7580POiZdKUB*`Q(3$n-oOk zc2J1GY2aEG)UlGobg}sxc8$s*r5ZN7TNTWvg1JJOOR}NcDZbX(G3q|09qZtN4!|!! zcF0`JH^tw)nQ$xYV(o881H*c1Ei&Mx;D-mD8-F9`e{{eA=>0N)99x>EBR zqec|aauhjJ+V({5lIU?)L^1EA0r_;c3xT8o9VY`y83n1OE9|6tnb%6@gifAc@ywdr z@i7&e?szvqzm#5dmO6RBw3aaEJK_E^V1O)6a#0f#^CcV=C*!hi+XkyXJUOJi_0x_{~@D-g+p2<=BkDs{8t>&|!dq z?UYq&YW;0sM&Y>51D?AdffT~7?t}5gnEwGWILq$_QTO(Mjd+HUEM8&G zo{8>i(cAMdCNSPVBH#t|$`idfR$N?J&p6It!}g6imBCM4hzQs`Ql9~5*m`@Pe2IewOeir9_fxQr-3$cLe&y-@)Ob zV&+S)j?^$_O0u76gQFqVXWYAmuzuj$l~NM2h7jxAA*QL`zFYQep_zXae34AMoR@hW zw<+WeGT&5|J){Rs@=d{@+3#cfwag0-sE1WrZ4yTBSI%b{+h7P_Q6Up-1HY+J6Sulm zp>TnrB-Zare#1qq${O03kPYTQ8E{FJ8wL)vvj`X;Z-4~FBm(bTAweT&p9H2zrPQtx zpbUct%+}s^9uI%5<@njwduL_Bg_55$xMB-ta0S*`W^MqR^METNU6pY`SGb}_PAH$L zt8tUSKAxGNP7|Xa_!@U1H85L4NgEOz%g+6TI`=VClEcJ=DNoLm*;xamdVxBog$FFt z6Vs2udK=4UjSm|l-$vcgNBQ`bb529CdBVTOHFmoH5f#wer-_sn)x?tyQkVa*%Q5qo zeXc=e>v!|^lq+lw+xHbWXY-@#y_P1W$d9@Qc67xK_1{W_-ReN*%f7J(t&Qy=gf42l5+*Z3_J=AZ`S65q=!s;jU13 z>*aE$uo=3;#8{$s$VkNsI8<(ldOX2p0MU5%`?*_x5G}r!=HXU%pVJYggt7915x9&yzN8kt|_KNVM)23+M-v&J2bv8{oZZ<-syO^vFx zi&nn#J*!&LvD1V9YK}#Y_LXOX6Bb(o@K+&r1l2W6pXHu`S<+8b1rFgyCVTaWJhUu? zyiNBVK5OPP;+_S0UroAu+|e>}&Fvn~ogZy9_mqyoq@)4`DHGs&6UjLuRtNH1vYtY! zyokX{&fIGHHuOS=4tG=dJKx~e4OC+FT#8lrDh|RlziN*~qh!1Zh?K|R<7_PyaP=Q( zJ8xfVCMIT4wsVh|4aoezgqSB9@Rft88<2O%JVHmR+$W{x!f!3h_IY55IybSlkv%KZ zN7A!WdomQjfk%PtW$=Sc@iIxFT-LwcVRrX;+6r4AK|f_tHv%u!+;&UDY-Z9bzI{^d z^`70cz_A1TVg`qLdAZCv{je^F>0(+p!Jz@#Owg&)Y~%4DJU89_yY2xAf4ST@!_ ziC`A={kJ9ymLVX`>GDx2^9*GA zNbdbPQeFhqF6mqUDK)&74Cw`Jo=-@=uXvs&QU!l3_JY=_5KTd$%-N6yDMrp9wgtLn zRRwS~rjvtqC@s+_V5L3)yK$?3%aDCkNt!_Eh|ot-%?TV3QHY6J$jC7VZ_YN()}RKT z&ZdDhOH8kTxS^~joUh0HR&DT=<{s4i-#}iKe>@Ru&k7rIn%ia^JfT+f6Yt2j*LO4C> zf!N|s%~qUj*zc(U{w|9!C1ZR_-KWWINzf}#vRP1t zc9p)!wJC#Jq6UQ=%Q+6Wvqj|L8)d+zo$PX)yOsMPX$b;Z;=pCiiIql1r&bmtwEt|QX~Dt;TG8>94}BUr6b z;j;-hE{0O~^XiAt|H7g+Asq@6=as$V=})9llM(4{7Vy(6iWa~p+GN{wOss(T z803)N<|#M3FH*bSd?1yiMK4!({!cU{W^w%l4N=R=20FM6P@V1n!0kw(E@U73UjUEN zyn{=QIF&zr+tN3Sd=%gD(K9_EmLu*LEz_$T`M`{v}Jj#vUS~MW9xcGlA%!snM%5h@Pl_4TBRZ5-K(wyv{#3>s^R1o%ME; zrLWJU+DvQxPGY#KhnyF_@N_OKXxF_t8#v%VbF44H>4rnKY2>>`_Sh)TEA(lT+0VrM zmiwDJ0ayRRVc$Riv@{d7x7+ERFC+XW5cB-}px-fNn$osnO7@RB4i4-8XGBCZSTv!+%garRBbJMLk zW7;_OduG>iZEfU=!XLL8nU=V`blRiEr{_!v4o{rmoFFM-u8xp=iK0cEH zms7U0UV`4rJvL|s95MGW19owL{9jI5fBiQ}>z56S@JZm&>u6aKX2OLPe}O9iAJe8b zx##bfG8l)a)8yShrH_es^fBuK?&S{|=E|0+bv55WwvqDw(nJqLF2qAYr|Vh4oD||` z@Ig&2OROpRfcF1245?cxD0#Le$yO1Mg#Ny1hu*^dbC0(yShl}h)6C+s^A6OI%ZXN} zKR}4Gtp@bQh(#tq9l5N4|*B3|Gt+Y z*rO+m-ZNp0{h5t9E278KkkE0&vzWOr9Yrky&#y9mTSDmY9|yC)jF|_9eI!RQ?2mpZ z@l6``tlGAu&PR(!{ob5vO_Yqh>SkQ{?ge&Ojgl1h=peh%$I%9PfY7mS9k1cDvZ%O2 zO2Z%{gM%kk32!xy!Tff$qf0>Rn{k|IQV-L!I@;?PLhYJlbatg>aix0$TkxA@eu2Wb@C8l>VVxL%srKF`*S!Ru7b3%In{^pdJfZk3lL?i;q}7dIQ5PxVCz2oU56M?z88f zPv^HpF~<_;{c<1}FW9GLO*v9L?(=XO(imju&gFDhb|}?;qwdWj&wu?vc&vlet_H`; z%IF+1&AIUKy;1VjN#{atf_CJ+DCtR8aG=xKt$^P3k%~dxva$oPDz3vUA?%(AXoyt} za1;k*tjdNTFstjOphub`CdyJDLxs=`JW061H3%C;DYIK;*=ssQdf|$p(!z+z&8=1~ zrmKo}JYPAgFfe?Zgfp~?((cOXNUvjwE2wMBy1}1DVQpgTV`t*DidObP>zyb`ttZTO zU!N>LK(P(fP(KS?&zgc&6@kFjH}=xT`1Difzhq_Mm<9yXrg3& zEPtwf5qRG}@h}EejX{H8QcwvB9mX5gqsP8bKmQLKp(c6>i*H57C{V@e=i!;JH`$jp zcuNC0i0rwwj?@#wGmyFyF2cDn$yCS?BT+oAcVu@Y~Bo9V<+nz)1DB~~%?TS5-HPW_tDQPL04h5VX| z|4H?9p3_E45h+8JZ)N2-!CsVAS5}MPSHm8x98vcYaz`a4x1gnW?e23MN;Em4+Fn(! zXq4*-!_~r|T#G$awyNKbIy~M@b>L8+|Hv#d*f5sofYxB~&o$2Xl?9KUuW_Y3+E4;8 ztUgpc+z2a=l*pQWo#?CjeEL!;4T%Z`KMXj{ZTj?c0;5%=ejPK;u~AfD!O{7Rj2NXS zg2R%CkMOiu!SemZF^jv%zukAI&m|H9^gR5sL=ui+IY(VpqXHc}e^=X{*WfOUcZFys zG5uG0NUSV+PwI_rxE37Gm_VP}W_U9Bfzmz$#yXMf=g1d-)*D+a0+n?ds4RsFQhDGG zOV?LG-~6@sr2_q>_@x6218v9d5UO4A8FO0>$LY`zNc=)2(J z$w3@(P5||>lz&hkdlBI9Be;$8zX3r%Bs3VVbBSBdvEe4;IqWvy@=jIEgO82VZkyqU zy?|f4Y z+XAN_tiGvYDEsjtidyFBj4mnMvR7?lTIU7IYMzZESfu!?0SjB`@oUC{$Wk(U|YE6W}8_{HU7m04WGkSu*s zOEL`r3lNh3NP7J(o7fu`lxCOmdR4cS^lrX!pYv#*B%`a}s_8bF5T9 zilNVXr!>K)2N$Fhw&}OB_b30~*rs2$QrOCB2Q%gH25nuefc?`#iYZn5&0TPF<$n`C zvLrG5W5}_fa5kvCyw_aPX@i4%?vPraIIuTayRfoUZ^BPE%FL*0|Ss4A-`yJqxjE4~_LO^sE z0jo;h)ulO7vbmPySkTGRCFs!4ZD<(@g$xV-mOfe;uejvIMubvww`E>HHvQ0`15ev< zsj0J7^rjG29ssWQHqg`oEL+r_7_4OTA1=e77U{@t zZGa&>PghEFkW-WZFO&o5SWLPg;F2V(mA!@3LH+zQi#7-t9)UuK3`uGP>AK^$Tt{;z zsmQY$T!kq{tZEL`tCcdoO&b6B8w*Rq_ zI)iFl!1~;-Ro>PebEvR+)D1#g)#ri;o-ADg361iS0+7Ou5MVBmknCaw2>az1pfZo* zfjsgYMS0)>SY@EP0&F$O2XA9y71j0=i1X(bKfFN^V}6?rZpLLLY@UrV?nV#Xo2#SG z2m0t9oKF)ITby-1tm$V8NnUK<8U8LsR!l9n{X6$yfaP*5hBWV>$GI|jQ6v#!OUf^c zQ@!$PK&t-ndfngGNW?X9(8z!Pf}QVH^S9*vCaUXaWc8Lk6$sLYv%fFozOrMX^$9w& z)dtIy=F7h;ka2Y|AzXts@9o*0=GQ+fG=_*(a0J_3yGN9T?aw+RzPGRjV?nRwUH0gT zw4->p4B0i0C?;khfQ#uMyOuXk;e5Hz?JO&lQOZ|NN+r^;vRgB$e++!ftL97isQ(ni z+*)(*MikV6CrYO9n3s{z-|8d7tv)!C~3^hrg+OeMkH8F5=yT?A={UeDYe*h|6)) zzhwn;24jI!hl-&3jz@NhzIV?@M0;%mS_jXsbL+29AoqOY?xbgyIt*HDOzMC3%f{Fj zfueFPWli<9xr|x1v}<6K&cjp~519P>NbJ0n$v?TFI$x%T+1{s?Y(p~Nx3@`chlzkv z#!#J&j^sEkA13v)4>PuG5`#vbtZgL%+O$yc)&5Y2z&Y7oZ)xGnbF+nOb}9e@MIX;i z^fnmBXGO%j?>{{~)9un|REYo(d_ZB;X}Pf6A=Zu$)=5naFI0da(066goE`Gy$RXFW zdVE+Ek>;dzQz%3$jCK5F{~dq_^prf+`Owpm^KQP@aAW+>i+^t?1Ey%esnBZG70sec z3C5=-RF0tANm`#z>z-CGl1)MFhl=x103|10g)9gJOFo3EE9cM6Mn4`fGj6ZgAqiV5 zC=YziO1l)BKHKVjE^~V{1yV?eS|F%VPq&QHkJ=L>)xgK>2Wi(_#}>BrHuAqWueD~8 zKU?GJ`(+uyPE9$mtiy5SPrni)SYRbZd!MA_Za18Sgaw#CX+eE9?Ej(LV)=WcOwvyX z=^$|y10S4r8V!CXjboYVwXM~ghaNuN#|IP#r)92aY|#ajrZV|(fJ8c&VIkzK8Y$$Y z2o|vwYOq?rd+`$>o*1gtnR#rJ5)^@4S3(+F)r*1`ylql)-;Qm%2NkKEj+?Yf$(P(e z$#d*bvl;WK-a%z{U0wF(o`BDWapITXXBNZKE=gYk-bqH>S2TpXM!)~8bMxZgWc0yGb`rV_3f3P zE^&`4b90W^;w->GpFnvlVdX;lmn&hLt-?fr^&Vxtl`_XfnfW~l+)AJRydsP&@bTFH zsq7PBJyjnX+GWqPAExMwU0)Ae@A7om?S@f7Ul|;~(cADxCls^85(fi9V$f<8Nq5lu zTb7B4&s9LU%umiUa1~#9kt#T`&|A0m;$|9G{qRYmEEu?0JHMh|1oMnNiQ(y2`bz|q zw#ITo^A`{int#1g4#eG!B?G#O!IW4-nI%r}EGKU4yw>}=n?`xqnutBfg^FsKf4naP zm||+ZjPd33+FK0?_h`Ie5Nd3fG|fcaQsXVTB_!MGwUgYFW?M#W(0%;l*Q+<@9az|L z;ic!F9yCM&Xo(wShyhsYBo4Mwj-z#*mwOg5oEwpo?@oHl$gI#qz5Lhta;2~`nJy=YMko+#xBa~{dLG1 zbS+`<(9}_`df`_QA_QV}$k_Rh@|8WsXP*BzdO{F7b7e{2?5k}wP~(8ap_9=8oE}ki z4#OGC6c;AHJ;kh1<^1~clg9tAwtH8B>e{c6YJT9d25qNd(z~bGu9tA=*m1SJPM^~* z=Dd~g!q>3VsU9UDdnUiy_-*td^@*&vIuEHUi#>5%uAchZ@}iu)dysaaJT}I#X*eoj zFDg0KL0xT`J*E{vGYJ_KljYTvlblTXXWVvsHbF0UMCQsR972-JWqmabS6k@mewam_ z7$L~D{!VdTc3QsvO_s13rn)cL{a#t3LP+ZM@=p30!%ohVwZmV8+`h^Y>y&h2y@fvQ z@)UXW@M^baTI`%-qJ5CbgsL(Qbimg_||h7m^BIKDsAeLglyy`mhb@vn9RuLuFpr zpXYD?fyUZ;1s)IS4NF$3WGmV%*r>Rk{Z#=RNCf4ImB>Xut(Oc5wS#9&9k%*Se~veo znp=Z`pYVCRdCSgYdDjl)59^=#$(I>uT)aoqWT_--(+b}Bn}fb*!%TtorIg7hg`>59 z64+M4L&;&iP*{M$pP;J_o(&wcFoOe(L${{FzJ-OvVbvB!*-mZtf4!R`%&lGy%&yOX zax6bpX+ubEq zAvqfle3}hHA4*wE24Mpbm$|OHXl8QQ^a#1LtB44pBZ-h%7a2_1{jpE-*FOKV%3V5 zg#%9l+MDCIrLQtVVtC~50%B`iRN%f1LXr?N#Ui3#W(Py)-!{Rvv4pv2>dMYV;hC{K zk}JP+Vq<@0oT%-la_jX4+29Qx1RtDLDVvvY(Aw)7D1LvFsS$+rPM6fK;E!A@G^0KW z+*xCOecS50v5J&C&7pGj8EMs|P=%DN&o*pU`m!9*8gA-CrimQ`R&~Lj}v#KVWZGD zP9ij?U8K-h9iQ*y$}ybAFZkz^G4H0V>h;fWb7rEHKJO}sRfD^(4=;ynw;BALa#KDK1cP+RZc+7+K!-++dO)3L2;}oI9s)@ zRXF)H3`sYV$y!ZFj0lA;x)&+d}mHja{$0K{-O$Mc#L#Ld!r zL{Lyt4g0M+syRBi1GvBQ3=dg;x2_X&`1E+`T;j0Kus?746?@$V#G0e*d2iq{&-qr~ zIEG=Bl-s3PFura>R$oDpr*~?i6jI3F@6H|`;LbWtCWh^1c)pvn$52dSv=R){{G`SG zp9g0M<+2G^o~wb5H3wtr{NiafW<>1ivf-e}q3ZI)J`fYUC+(TW4{=+n1&Nbblr5HM z4EszBdoLuN45gPG?bI=t8h_j@6DS|L|GoZ$#VDoN0X?2}KlwPu z!7WfWmqZw0$Sq#dWp+WA`>MIY);dZhIZt0&R3yW8K9D8*J znr9e-Rk7=55aqIFu5UnPgJB5kNXz1}QN@ohYA1tEu=raR6~rLnosB`jQ(0eHq~_HO z)%_DOq!X64e)P}iq=4SQNPb5yG3oSIhzkA zxeen_`&S9YshxyYXWq$T{>YEb+Qswj1v8#oIZ?fudu5?gsmrFWf)~SVh2V3V zF@SzSQ@cv}a@+P@biPt4JJdq{hT!9HKk%zieT!IQya7JPf3jbB{0V|Eqk+q zNMemfx1$uX4MJ#_3$@JqE1U2DW_B}pl?5cMv;C%2V5XKNRI*4o{wJ6Z9xmp`BEPDF z&;8tO1tKbIe3x$118efepc?~NmJSevX`0sJdhnsjV7e(M9mY=plp^S!K!Z}T&1V9r)o)gZEez|ZA z3$TE-;MeremyyB=no9^DHBJj=CNo z$=|W2*=lpGw<8v&w6$Y=S8!j4c3IUrjovsR-uZRmJns_HA@&Sb%rGHZ^U}CPT>k zVpYd~HELa0B3w=e-T-lFxDReA()Ga@a|^Ul=T~<~U)-A4YsM z7N|ib0a@acumAwqLx!bNVgTUXHvWDv9KU7*K+)jcm<9mbH*5kj-OhW8j()c7IY65B zc&1H3zdd3n|A96{_Vw}pXOFyLZ=mr!p4qjbR%14AIezE_`K|#U%v*E}5COQ@CJX)p zb4}lvA$C#I3SfN`luD(lP^0)aJ1FFrZz`J(6$;0c7=EHyUaWCM5I#y{qX6K%6#C7Vl$di#L!FD_oqL$mr0e%ft#~2XpLmAPeS;4 zO?UomX=N*j`Sxr2zh20Z(QDhml#Pd&U%qDMIL<8SP2qv`E<|W+XS*|d^1TQs!GQjh zy3KBA;%z=+;yK~=c~JtmTPA<$vCF{F^$vDs+(v+wOSIT0VVhukk24Unl9>m<%Ek0A zgK()7HR9-+hZM3If>w4lqz%it0qe_8X1M0QTKs_V@k#Mlo}Vfg_<+Y-Z1%8JpG9xi z)0W95$+L$SB*3&~r^4#$S$w||_PH=XOd(cI{G`AyG%`fNW$kCm)M6OyzyS-O2f#Sf z`gW+Z{ob=>*d5?;3~;oNd1wZ}KRCb`>dUf(KqAaELD^{5m#L=)h;qOv*nP|h1E%He zQ#p*eJBV=8mHgw>LLRUq21wv)Xrv85<{d;F6(a5e{d`^{+pGZdxQEr6pC7pk7X?Ga zi&*l*m8BRM5S4)~uUF#{0A67cf_DA&0K@OLD9lN10!w_<@bIh#AYRxI#&^ah9fW{w z_uN}9r{T-aH>-UdN+Q7}L4-ef5E51xq{Q<(<| zXM$sFG&TbyNK1yvp01#>E6os-wpKjw>nF9cM*>;JD3PN_NImdS!f?~y6dSd|K`}gt z5C&EC=NB^+0PB!FX65{Vu>asg$Hbig&EBm>7zrF8(}0SQ*Py(e0ALD5s^Sv-pqmSc z)Z}VRy}1xLw{Vlv(B5ECyAs^e68Pp*+Y0~$&(%w`_A)<3uWkYob)BTOZSD2?7-V}; zS$RXb#y%0`dMEFa=IHY8h835x<)W0(0)7xVVHwZuyNCiu9N}d>Ygl#h{9`N%R4ijN zIT=oqA`q=WSZ!Vm3wu?JVK{>!nQ1z!e|e^eV}4-5C9?#XWII8BzpVi$z{S8r)5Dz? znYE(_Hyh!AibgDz!+28)0MFO<-mZuIfP6oZC#0}F{NKSA(sEFHP&^++vNK{WGr*8l z8oz41{1+?h$7og&Ok1jg*Gq*PUJQS(t4mLNPy{5ZoWnvs$=n=Rd<*?FMLqw#d0#wy zpIxm+B-f3hLueIZ>8s94KRfl;cu(DDg7dmU1bR@4qchlOG>;(}5E_K1 zs(4|_ja?+4?Evu~5Fum1t+{ieH($uIs@wP%Tb5p?$XMY(WgPry;)Bl1xY+SWyt0eMFe|nI_8p>5lf z1giE;|D0W5a*d|cUO5+Gkh7A%-qcd?h`%0*ZG0TrcMn)Lg)^_V_silaLIaywB>;qf z>2>j=#wAI?z>>)qHkfvEG5k#!m_M6rge!5q#_Iouqla`rw#Gv(oa?TOl|30{=9+Jk zFnui_sc*4>aY=zzxtlkA6Ua{UHtDrwy5?gSv@V1B54Oh{vuGs*9f%TE>oz7}$eVATEq?yqg+ReN>QEH{jVl69-X%{U4 zq7#>EKD1fCM z;7p+g)aNMh$T}RG|5j&Y2oV7hhSTqTfWpu;Rg-5hrK@Y48!dvLm2Wf|9lN6dVUd2{ zx2F&y@!*zsKSq0Ubvi8t$N&$$PSNL`ef%oEVP)7^PZ&THJh{ptUhKQqbk9ESlZ+%NTSbL}`Kv-ZYoMF3M&EViRwBIj-1 zXejvqe~^#i{Szg51rR{&u-nQV=FYY{fiV(-HUdQ zB$w3x)$H8)!iF4ulHop$rf38l;43!46Aa1W*)vKK|a~brrQrVSYf+JZaGj zkpa6Wp==Q*+a>=y=TBt!{Uq@Y?y23^;$dYA> zQARr!$zPt{%EM#Plls;I8!(c69f$UBZD2i)3@hltqPL|dzVDeDl>&I#$zAAuh{f|F z?2(|q4+Wq}=6m}cUyE8_ntT}gdi8lOKdXb1u&t{ul650D_Da3|g^3(27Wk)U7>3bi zoFUYSWG{IaC0v9D1jZ|d3t<0e$>vI5>$mWG2Ru;9+k)O=B?>Au1Bar$5|nV^Cn+^+ z?2T_O8uy20hShQv2UIJk4-9GzdgZ-Dr}#hpy`1C1f}rrl${AHd-Qf)=gOkQ5N{`XP F{}0iuO&9WeU37c>m&gHGR{sr_XB{0 zLnM$8gJ1J!yLs?yZS*ehXphL_(Xsxg0)bsXI!8|R&N zo@WQ9hU;Tg9Ag0ckyo-sioIj#@iz4FwvLbYupjS*+}XAU{&CUj8W)v!ARczC-u>k` z$N}|PmYXc*c2DZ=lR?N)wx&GDDlML|E?6}_e7cwXux{q54`m~S@xOa@qy*K1;?Bnl z&too#o_+|iZuo)6yNNcaBS3Gk>dV-Ih|Pk;nM;lJZa2dyA`QWpyciM=;RNXCtaWSJ zQT(q2`VD=D~=Jem8J~=Dm@7M|f`xmkyW~NZ}FU8&=i3=X#Br zceVp8nAb_2Sj6y4a7YEDCa49sdLI)+9=tAo;)4+nv4)FJF8l*=|5o`W$`J00YxCF= zg}djr0!fyKFJQ99Qj4Q?S94vr0HRw5KD+8l$Z|ox>tMnB-a81ow=Y-)_hFzLSfNaR`xgk*#3kvPm zluY!Q)rnCBcmSlXTi*IA#`BEQM%ez zqvX9iGu&a#@zdG1M>x@&yB4&qj@;1_I4d#iPX3;4XqviJxjU9%EHwZ4yOBFr=^8Dr z2c%sM7S*tPhF7^H=kO-xrdOSql0!Fl^LGmvS&N1bHpmt9Pbq>b?NZOGT2fb4wGWvU z^zZT=B-t-l?h2o*tnF`M*fB0yS|O4CYJ=FXB;~{Qw%Jvby@R9`?Ik}n)}R_rfAH_` zp&smtDbi<1q}#rJA>7)iCRQd>fjVCzL%>ise{s$gdL1Ohd`H-q9YARUMu`=T(IYY2 zd*Mq4^F?W4nlA8K`&TXRdIMWFF03h>_nUaI5HKBLx(Zpo_>3T&ft)9TYJIh7>bz;A z+WT%XOKGo`ZNNtR){@BjyezH-& z7O_MnAAFaXC($$vEXPegz8CHh605BMH!WoOt|m3&U#a%*pS%GdoWTrZz^-V$>&%j%aaXE#Yhi42__hmdplTFr+he#g3Mw+xgN;=ey zvN;2^vFSj%NypbO+}|@^PEU)ec<-<|oPOm~0TA|+Xlp*@m8O(C{b*%_;_ z@194Kv05tnQ@cSIht?Y_QzJbuJqYA%eQ+Xfr!Z#VtLtE%(iPg7BEv`6 z-$Udxqg@N9*ep*h70rJ0)>IpLtTE@R?X%nCUj$*icI`glvU9sgG+-?hI_!~;p*a+$ zqn*GOS5QhZW0ijUq06nf{9%6M4mUet!n^DI{7?Ggb>msX9?7*}WJDPsp1HL(Z(?9%GmvjPrz6RATxULDvGPAa%O|79$ zHqzsog5Nl9OpCd$hriYr6EzYhW}yCY)bE+{tw*X8&01;`Z(6nPDtvr@E6#Owq2y0@ zyIn}4Z(P{*&y4A$X3-ASb<;}j3Jfpi0znh3XIXGN3jd_srZDo}UGIBlwcM!aGl;ql z!Z$Vx&dJA(<*5ZUX>)FuWhM-+rJ~6`ZzX5qzf=@=6Z5sLVvhy0THhSgMd;MO4EMjm zOxsDQ(2&O{aD@ zM&G}Qybgj7Jm`?HZcM!&Q7WEZ=>p6m!eY$`PR@`2B6?Bkh>k5fi_P1o?kX}=0hVg< zF(UE3ApGcIcI)y;0?-5lCbn{^xWKLfb}C|Yh5aheU8bWqsY?$BT(?dHMfHF%R4*%B zCIbY&Pu?w%ezuN%W1|oBGB_;M@raVxZ)W_&dMcVQS^blB?m+A76H)gVPg2C$hqSR^ zXf$Eq$iBh6Qgw}T&keHAzVs>7Pv%cwh-~@+t7X8{eWm+*TY?sPD(gEH>cjG<4-ON> zv<#<9Z-`o-*lipj-=_K@e8`g2q3DpG(WUx2;oUsDj)?>O-7h<27Pn#usm7ITw@a5I zd_SCeq$^jK*3Udm{u{@dr{ zBe7f==`ysdd;Id+L9m+d&uyz;@1}Tj^Ja80Q0F>S*-e?<*%W`7dp~PTOaY)NoYL-H z3Irroc3ps=ExPuoRs^GYY^w*55TL_iTPxqu0#| zR2sgbUOZwy_9<)T@)eruF(u&e*>&)4h;^p|N7Gst8ywwYUp%y$yjtA>hxbyWRkS@! zqb}%qN7wCbil(ps=MbV}!cUES7+l5P9cPBqd48#>wnVm)q1Jty}6qZ9kanfN4&l7IM7rh)oiqJ z;8S@MF?-l^$K0p0H3}lba+&ncw&A%U%ct)|P-!`6fbbYe^_W}MNW))(8v9^_$vg+*i{&n2+nh(Jw%kTyyJtS4(AK?bBg-tC} z8B0}s-)}M2BwI7yUHz@mD;(1)51wbvf43bw+u``cnq6U8%eHHPEuo5QREf#QmJM4Y z&otLBI6UmShy$U$@KEq)*ivijr7G@k9Xf8xR?JHItfDV{ckMV&(X&#yV;hL_Fu3?; zTj!-xx_z5i4QuXmv1UGC=MT@Xz8WN9R-5wvWiMt??}RBG4Sp+rcvwCat6U;WS3P;& zx>sf~Y0c%Ml0b1;=ha{T$2ik(_37NNHU?E0r-L zyfdH{VpA3JeN!b~xne*~ri2q9VsuC)LWOc=ZzOQrnxlWM;5_V_iopS$qn1=Zz^UxK zJ`B3q9t;h3t&x^M4-m7gKBtM(UMlj7&Ywn{2(?Fa0UYo)nxvRV7(3V%BsU_zI>$tY zXaV9Y?zby*^zxsvkE{#lH*ls{8G`K~46yxw$n&lJ?0!Md7HRsdi&ynE^gvX#^PNl{ zNlM5$R3I7Ua(7PF7(pFWd(o%Mb&D1sBAc>Z59LS(OM=qZ`x{#JaTr!I9b^9(qy7o6 zMim2QNy%E$cZWtbPsFW`pZBDlLE)jH_ew5;GFn`ccel6FQ2O*R#2G5@xODOXUUg(^ z=LW|7@-b6(<_;|Qxv}jJ-tuzt<|S99gw7FOiVxb!P`mM#?byv_MCt3$qcH_3#ei6= z%pv7#a~&veYSm}-sA%f`7x_xPAIzAUYNz=RT@l1bR_!8-{?Fo*BYUY?MgeD4sggkJ zl7>JjphK!}-ahHw_AEnm1~Abq9lv06=EmRl zby#2!`6`D}qOb09BL)(`G<~N>E!$<6o~1x*)=UTKh`xEG9uRE7FzGGVlYEDvNdks`FeL|>4!Vn6k|~e$ z!9;*0-DvJde2$cmsz(_11QVvKDt0A}j8I)=tU{|cBgli=x||Ph5Zoqt!i(CFcm>}s zsql+4sghv5;)fG6DL>+qr+IZoa2dD#U}qnJ-t^pg%*1ypfE2d6dT%i_Q=(5zE=rXA zY)2@$7IrRqV}%3}NofCWULOf#eMKOkt8^SgKoT!4nb#llLl8(RXtVISuyYs+@C14$ z*#FL6Gpfl~qRNfnd) zq<6v{fS{cKoDRgn5pi;ngd{#(Q3tRDa~OpGHGqXWeZO>JaMaH*PvF!p$jo#|aljIi zza#N;e6Sd%FIp7&A&Iw~SM0))Wsolgg}psHD=Y6 zq%ckeDWCaFjIH@Gb!Pe{1^Bpv(A%ymyeeuc%zUQF)Hp2xNA)d3K?+8k+zY2tM^gJ! z6%1S#haYI>A?wy(O1HY?2`b}nPfB%3`J9VinZX$xS^JI4zf_(fJ1q;A#5e@+F0k>LqMcOt20}?O-N`TGWGLnt`J|xOI z(Q2Gu3;;*IDx`hyspVuX0BkNSiL$0vfg1oYinaTNmW!s8AZXT!Ge2~>v{kf6k|(;hR5fP z5rFL8FhN@{Y+broEh9(W^*T;;g)9`+j&}7H0YH3qolSALeVTI{f-WRQH_B{&A%guE zFxbM2jBb``7!}GgAda^+3yb*%WugO6-`b~a$_HnxNCK{tZ28}rD%KeU$V(I6=f>83 zkwFkTRhHxH5_Nvv4ui0lcZ;5$sM)oo269DozAR`_#?A-c79rS1N;JX!2v#nFP_zoo zvmV@>CI<2boRjx48-`O|mIDVdfH_pwxT$)XpDzV;I!oP!_0x}DlWO3lUFUTp6P`HT zNF$do?(<_cf^McLJW#vHk_Uug1AhFSTna*GU)Xul%vXv6pl)w*7nPql+Y#>v2qCFg zIfgILk`=M4@NAjf~s;8!)g`4N3Hf(n}DQN1CIo-)~!gQSA> zKzX~;@_gO2ED(!?);Y4&+NU3YSe;*$OyRLH91@dsb_1|EG ziKj;Lzys*UH*eP8DpZ;>a9HCa+_&HX{oQmjAe7SZ84oi5)e^}fE8*yGR59!LfOy(E zuv@T-j{v#CCZEacDDvHPxx#DJ8fhEAV03VI!=IRuaunHTfUQIF5rV?q(zfpfMmz)^ zck=ntLlFK|je71iGx7oJCDg49Gk!#XkmH&!|L1d*k4i6E`fhug-yHT<)eZ1^9|H}J zY;0%FX(3|F)uv43Nj>wX>j-ps~qi71OsK?Al?L@3^Mu=Kain>fap8xll0 zGFeioGzI>30Co+yPQR6(y2kmSlp?$mG8x@6vP~THS86|3{z<7>h@FrU8e?E+(o3%M z_l3^6^S>SxS<)!d!qHlLB#=J+*tT}GVcB%Z_Y7XGCxs1@cuU_uU*eWJ3Y2IflZ0F; z(4XE%Yk>LBqlvsNodDcnXqHHKX{6NLLjh(jt)WK<%YcON|L4@X8VI0{Hyy((@nNm% zR^a=M>mvYt)3HXpTNMJy$zY2PuV^Z~h)UWLv{G9XbG4yFGv zG;4lTTsl*Wg!N#yWl3>6ljJsFw36UT(rlseSxe&? zH*_K-f7R}76fVvO6}YTuwSJ!?&cF1VfCR)7%(P@-+I&HC9HiufRj1l936SMi>hJb4 zOoG(n%F~~-hv_ruc=$Ll>oHkmis8jE4!#k`g7l&|^&D|-98;EN;{>SRYNTZ&Z`P@{ rmWhJ;T;}H7;V&Ei(-&d2kVHj*{SSBtGRs}eS8O@&+PAaRo=p2c1kHHM literal 0 HcmV?d00001 diff --git a/src/hct_mis_api/apps/steficon/widget.py b/src/hct_mis_api/apps/steficon/widget.py index ad6a135ef0..a12452ca00 100644 --- a/src/hct_mis_api/apps/steficon/widget.py +++ b/src/hct_mis_api/apps/steficon/widget.py @@ -3,11 +3,54 @@ from django import forms from django.contrib.contenttypes.models import ContentType from django.db.models import Q +from django.templatetags.static import static if TYPE_CHECKING: from django.db.models.fields import _ChoicesCallable +class FormatterEditor(forms.Textarea): + template_name = "steficon/widgets/codewidget.html" + + def __init__(self, *args: Any, **kwargs: Any) -> None: + theme = kwargs.pop("theme", "midnight") + super().__init__(*args, **kwargs) + self.attrs["class"] = "formatter-editor" + self.attrs["theme"] = theme + + class Media: + css = { + "all": ( + static("admin/steficon/codemirror/codemirror.css"), + static("admin/steficon/codemirror/fullscreen.css"), + static("admin/steficon/codemirror/foldgutter.css"), + static("admin/steficon/codemirror/midnight.css"), + static("admin/steficon/codemirror/abcdef.css"), + ) + } + js = ( + static("admin/steficon/codemirror/codemirror.js"), + static("admin/steficon/codemirror/fullscreen.js"), + static("admin/steficon/codemirror/active-line.js"), + static("admin/steficon/codemirror/foldcode.js"), + static("admin/steficon/codemirror/foldgutter.js"), + static("admin/steficon/codemirror/indent-fold.js"), + static("admin/steficon/codemirror/overlay.js"), + ) + + +class PythonFormatterEditor(FormatterEditor): + def __init__(self, *args: Any, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.attrs["class"] = "python-editor" + + class Media(FormatterEditor.Media): + js = FormatterEditor.Media.js + ( + static("admin/steficon/codemirror/python.js"), + static("admin/steficon/codemirror/django.js"), + ) # type: ignore + + class ContentTypeChoiceField(forms.ModelChoiceField): def __init__( self, diff --git a/src/hct_mis_api/config/fragments/smart_admin.py b/src/hct_mis_api/config/fragments/smart_admin.py index a19fed948a..24df9e8ac8 100644 --- a/src/hct_mis_api/config/fragments/smart_admin.py +++ b/src/hct_mis_api/config/fragments/smart_admin.py @@ -20,9 +20,6 @@ "constance", "flags", ], - "Power Query & Reports": [ - "power_query", - ], "Rule Engine": [ "steficon", ], diff --git a/src/hct_mis_api/config/settings.py b/src/hct_mis_api/config/settings.py index 2c2e16ce03..f80c669915 100644 --- a/src/hct_mis_api/config/settings.py +++ b/src/hct_mis_api/config/settings.py @@ -192,7 +192,6 @@ "hct_mis_api.apps.payment.apps.PaymentConfig", "hct_mis_api.apps.program.apps.ProgramConfig", "hct_mis_api.apps.changelog.apps.ChangelogConfig", - "power_query.apps.Config", "hct_mis_api.apps.targeting.apps.TargetingConfig", "hct_mis_api.apps.utils.apps.UtilsConfig", "hct_mis_api.apps.registration_datahub.apps.Config", @@ -277,7 +276,6 @@ ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 7 AUTHENTICATION_BACKENDS = [ - "hct_mis_api.libs.power_query.backends.PowerQueryBackend", "django.contrib.auth.backends.ModelBackend", "social_core.backends.azuread_tenant.AzureADTenantOAuth2", ] diff --git a/src/hct_mis_api/libs/power_query/__init__.py b/src/hct_mis_api/libs/power_query/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/libs/power_query/backends.py b/src/hct_mis_api/libs/power_query/backends.py deleted file mode 100644 index 97b0e8ac82..0000000000 --- a/src/hct_mis_api/libs/power_query/backends.py +++ /dev/null @@ -1,48 +0,0 @@ -from typing import TYPE_CHECKING, Any, Optional, Union - -from django.contrib.auth.backends import ModelBackend -from django.contrib.auth.models import Permission -from django.db.models import Model - -from power_query.models import Report, ReportDocument - -from hct_mis_api.apps.account.models import User - -if TYPE_CHECKING: - from django.contrib.auth.models import AbstractBaseUser, AnonymousUser - - -class PowerQueryBackend(ModelBackend): - def get_office_permissions(self, user_obj: User, office_slug: str) -> Any: - key = f"_perm_{office_slug}" - if not hasattr(user_obj, key): - permissions = Permission.objects.filter( - group__user_groups__user=user_obj, group__user_groups__business_area__slug=office_slug - ) - setattr( - user_obj, - key, - { - f"{ct}.{name}" - for ct, name in permissions.values_list("content_type__app_label", "codename").order_by() - }, - ) - return getattr(user_obj, key) - - def has_perm( - self, user_obj: Union["AbstractBaseUser", "AnonymousUser"], perm: str, obj: Optional[Model] = None - ) -> bool: - if not isinstance(user_obj, User): - return False - if isinstance(obj, Report): - if obj.owner == user_obj or obj.limit_access_to.filter(pk=user_obj.pk).exists(): - return True - elif isinstance(obj, ReportDocument): - if obj.report.owner == user_obj or obj.limit_access_to.filter(pk=user_obj.pk).exists(): - return True - if "business_area" not in obj.arguments: - return False - if "business_area" in obj.arguments: - ba = obj.arguments["business_area"] - return user_obj.is_active and perm in self.get_office_permissions(user_obj, ba) - return False diff --git a/src/hct_mis_api/libs/power_query/defaults.py b/src/hct_mis_api/libs/power_query/defaults.py deleted file mode 100644 index b986513b94..0000000000 --- a/src/hct_mis_api/libs/power_query/defaults.py +++ /dev/null @@ -1,57 +0,0 @@ -from typing import Any, Dict - -from django.contrib.auth import get_user_model -from django.contrib.contenttypes.models import ContentType - -from power_query.defaults import create_defaults -from power_query.models import Formatter, Parametrizer - -from hct_mis_api.apps.account.models import Partner, User -from hct_mis_api.apps.core.models import BusinessArea -from hct_mis_api.apps.household.models import Household - -SYSTEM_PARAMETRIZER: Dict[str, Dict[str, Any]] = { - "active-business-areas": { - "name": "Active Business Areas", - "value": lambda: { - "business_area": list(BusinessArea.objects.filter(active=True).values_list("slug", flat=True)) - }, - }, - "all-partners": { - "name": "All Partners", - "value": lambda: {"partner": list(Partner.objects.values_list("name", flat=True))}, - }, -} - - -def hope_create_defaults() -> None: - if get_user_model().objects.filter(is_superuser=True).exists(): - create_defaults() - fmt_html = Formatter.objects.get(name="Dataset To HTML") - for code, params in SYSTEM_PARAMETRIZER.items(): - Parametrizer.objects.update_or_create( - name=params["name"], code=code, defaults={"system": True, "value": params["value"]()} - ) - from power_query.models import Query, Report - - q, __ = Query.objects.update_or_create( - name="Households by BusinessArea", - defaults=dict( - target=ContentType.objects.get_for_model(Household), - code="""ba=BusinessAreaManager.get(slug=args['business_area']) - result=conn.filter(business_area=ba) - extra={"ba": ba} - """, - parametrizer=Parametrizer.objects.get(code="active-business-areas"), - owner=User.objects.filter(is_superuser=True).first(), - ), - ) - - Report.objects.update_or_create( - name="Household by BusinessArea", - defaults={ - "query": q, - "formatter": fmt_html, - "document_title": "Household by BusinessArea: %(business_area)s", - }, - ) diff --git a/src/hct_mis_api/libs/power_query/management/__init__.py b/src/hct_mis_api/libs/power_query/management/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/libs/power_query/management/commands/__init__.py b/src/hct_mis_api/libs/power_query/management/commands/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/hct_mis_api/libs/power_query/management/commands/pq.py b/src/hct_mis_api/libs/power_query/management/commands/pq.py deleted file mode 100644 index dd347409c3..0000000000 --- a/src/hct_mis_api/libs/power_query/management/commands/pq.py +++ /dev/null @@ -1,102 +0,0 @@ -from pathlib import Path -from typing import Any, Dict - -from django.apps import apps -from django.contrib.contenttypes.models import ContentType -from django.core.management import BaseCommand, CommandParser - - -class Command(BaseCommand): - requires_migrations_checks = False - - def add_arguments(self, parser: CommandParser) -> None: - subparsers = parser.add_subparsers(title="command", dest="command", required=True) - CommandParser(add_help=False) - test = subparsers.add_parser("test") - test.add_argument("filename") - test.add_argument("--target") - - execute = subparsers.add_parser("execute") - execute.add_argument("id") - execute.add_argument( - "--persist", - action="store_true", - default=False, - ) - - run = subparsers.add_parser("run") - run.add_argument("id") - run.add_argument( - "--persist", - action="store_true", - default=False, - ) - run.add_argument( - "--arguments", - "-a", - action="store", - nargs="+", - default=[], - ) - - subparsers.add_parser("list") - - def _list(self, *args: Any, **options: Any) -> None: - from power_query.models import Query as PowerQuery - - for q in PowerQuery.objects.all(): - self.stdout.write(f"#{q.id:>5} {q.name[:30]:<32} {q.last_run}") - - def _test(self, *args: Any, **options: Any) -> None: - from power_query.models import Query as PowerQuery - - code = Path(options["filename"]).read_text() - target = options["target"] - model = ContentType.objects.get_for_model(apps.get_model(target)) - pq = PowerQuery(name="Test", target=model, code=code) - arguments = {} - result, info = pq.run(persist=False, arguments=arguments) - for entry in result: - print(type(entry), entry) - - def _run(self, *args: Any, **options: Any) -> None: - from power_query.models import Query as PowerQuery - - query_args: Dict[str, str] = {} - try: - for a in options["arguments"]: - k, v = a.split("=") - query_args[k] = v - pq = PowerQuery.objects.get(pk=options["id"]) - result, info = pq.run(persist=options["persist"], arguments=query_args) - for k, v in info.items(): - self.stdout.write(f"{k}: {v}") - self.stdout.write("=" * 80) - for entry in result: - self.stdout.write(str(entry)) - except Exception as e: - self.stdout.write(f"Error: {e.__class__.__name__}") - self.stdout.write(str(e)) - - def _execute(self, *args: Any, **options: Any) -> None: - from power_query.models import Query as PowerQuery - - try: - pq = PowerQuery.objects.get(pk=options["id"]) - result = pq.execute_matrix(persist=options["persist"]) - for entry in result: - self.stdout.write(str(entry)) - except Exception as e: - self.stdout.write(f"Error: {e.__class__.__name__}") - self.stdout.write(str(e)) - raise - - def handle(self, *args: Any, **options: Any) -> None: - if options["command"] == "list": - self._list(*args, **options) - elif options["command"] == "execute": - self._execute(*args, **options) - elif options["command"] == "test": - self._test(*args, **options) - elif options["command"] == "run": - self._run(*args, **options) diff --git a/src/hct_mis_api/urls.py b/src/hct_mis_api/urls.py index 22a56b66ac..5ece36ba40 100644 --- a/src/hct_mis_api/urls.py +++ b/src/hct_mis_api/urls.py @@ -90,10 +90,6 @@ f"{settings.ADMIN_PANEL_URL}/advanced_filters/", include("advanced_filters.urls"), ), - path( - "power_query/", - include("power_query.urls"), - ), path( "changelog/", include("hct_mis_api.apps.changelog.urls"), diff --git a/tests/unit/libs/__init__.py b/tests/unit/libs/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/unit/libs/power_query/__init__.py b/tests/unit/libs/power_query/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/unit/libs/power_query/test_auth.py b/tests/unit/libs/power_query/test_auth.py deleted file mode 100644 index f99bbfc13a..0000000000 --- a/tests/unit/libs/power_query/test_auth.py +++ /dev/null @@ -1,60 +0,0 @@ -from django.test import TestCase, override_settings -from django.urls import reverse - -import pytest -from power_query.fixtures import ( - FormatterFactory, - ParametrizerFactory, - QueryFactory, - ReportFactory, -) -from power_query.models import Formatter, Query, Report - -from hct_mis_api.apps.account.fixtures import BusinessAreaFactory, UserFactory -from hct_mis_api.apps.household.fixtures import create_household -from hct_mis_api.libs.power_query.defaults import hope_create_defaults -from tests.unit.libs.power_query.utils import user_grant_office_permission - - -@pytest.mark.skip(reason="This test is not working") -@override_settings(POWER_QUERY_DB_ALIAS="default") -@pytest.mark.xfail(reason="This test is failing") -class TestPowerQuery(TestCase): - databases = {"default"} - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls.superuser = UserFactory(is_superuser=True, is_staff=True, is_active=True) - - # code should be unique but the test depends on both BAs having the same, empty code - cls.ba1 = BusinessAreaFactory() - cls.hh1 = create_household({"business_area": cls.ba1}) - cls.user1 = UserFactory(is_superuser=False, is_staff=False, is_active=True) - cls.user2 = UserFactory(is_superuser=False, is_staff=False, is_active=True) - cls.user3 = UserFactory(is_superuser=False, is_staff=False, is_active=True) - hope_create_defaults() - - p = ParametrizerFactory() - - cls.query1: Query = QueryFactory(name="Query1", code="result=conn.all()", parametrizer=p) - res = cls.query1.execute_matrix() - assert res - cls.formatter: Formatter = FormatterFactory(name="Queryset To HTML") - cls.report1: Report = ReportFactory(formatter=cls.formatter, query=cls.query1) - cls.report1.execute() - - def test_access_granted(self) -> None: - with self.settings(POWER_QUERY_DB_ALIAS="default"): - url = reverse("power_query:document", args=[self.report1.pk, self.report1.documents.first().pk]) - self.client.login(username=self.user1.username, password="password") - with user_grant_office_permission(self.user1, self.ba1, "power_query.view_reportdocument"): - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - def test_access_forbidden(self) -> None: - with self.settings(POWER_QUERY_DB_ALIAS="default"): - url = reverse("power_query:document", args=[self.report1.pk, self.report1.documents.first().pk]) - self.client.login(username=self.user2.username, password="password") - response = self.client.get(url) - self.assertEqual(response.status_code, 403) diff --git a/tests/unit/libs/power_query/test_backend.py b/tests/unit/libs/power_query/test_backend.py deleted file mode 100644 index 08d02f5b96..0000000000 --- a/tests/unit/libs/power_query/test_backend.py +++ /dev/null @@ -1,55 +0,0 @@ -from django.test import TestCase, override_settings - -from power_query.fixtures import ParametrizerFactory, QueryFactory -from power_query.models import Formatter, Query - -from hct_mis_api.apps.account.fixtures import BusinessAreaFactory, UserFactory -from hct_mis_api.libs.power_query.backends import PowerQueryBackend -from hct_mis_api.libs.power_query.defaults import hope_create_defaults -from tests.unit.libs.power_query.utils import user_grant_office_permission - - -@override_settings(POWER_QUERY_DB_ALIAS="default") -class TestBackend(TestCase): - databases = {"default"} - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - from power_query.fixtures import ReportFactory - from power_query.models import Report - - cls.superuser = UserFactory(is_superuser=True, is_staff=True, is_active=True) - cls.ba = BusinessAreaFactory() - cls.backend: PowerQueryBackend = PowerQueryBackend() - cls.user1 = UserFactory(is_superuser=False, is_staff=False, is_active=True) - cls.user2 = UserFactory(is_superuser=False, is_staff=False, is_active=True) - hope_create_defaults() - - p = ParametrizerFactory() - cls.query1: Query = QueryFactory(parametrizer=p) - fmt = Formatter.objects.get(name="Queryset To HTML") - cls.report: Report = ReportFactory(formatter=fmt, query=cls.query1) - cls.report.limit_access_to.add(cls.user2) - cls.report.execute(run_query=True) - cls.document = cls.report.documents.first() - assert cls.document - - def test_get_office_permissions(self) -> None: - with user_grant_office_permission(self.user1, self.ba, "power_query.change_report"): - assert self.backend.get_office_permissions(self.user1, self.ba.slug) == {"power_query.change_report"} - - def test_report_has_perm(self) -> None: - assert self.backend.has_perm(self.report.owner, "power_query.change_report", self.report) - - def test_report_limit_access(self) -> None: - assert self.backend.has_perm(self.user2, "power_query.change_report", self.report) - - def test_report_has_perm_fail(self) -> None: - assert not self.backend.has_perm(self.user1, "power_query.change_report", self.report) - - def test_report_has_perm_fail_miss(self) -> None: - assert not self.backend.has_perm(None, "power_query.change_report", self.report) - - def test_document_has_perm(self) -> None: - assert self.backend.has_perm(self.report.owner, "power_query.change_report", self.document) diff --git a/tests/unit/libs/power_query/test_params.py b/tests/unit/libs/power_query/test_params.py deleted file mode 100644 index 5f30d7ed6b..0000000000 --- a/tests/unit/libs/power_query/test_params.py +++ /dev/null @@ -1,35 +0,0 @@ -from django.test import TestCase, override_settings - -from power_query.fixtures import ParametrizerFactory, QueryFactory - - -@override_settings(POWER_QUERY_DB_ALIAS="default") -class TestPowerQuery(TestCase): - databases = {"default"} - - def test_create_defaults(self) -> None: - from hct_mis_api.libs.power_query.defaults import create_defaults - - create_defaults() - - def test_parameter(self) -> None: - p = ParametrizerFactory() - p.refresh() - - def test_parameter_custom_source(self) -> None: - from django.contrib.contenttypes.models import ContentType - - from power_query.models import Parametrizer, Query - - from hct_mis_api.apps.account.fixtures import BusinessAreaFactory - from hct_mis_api.apps.core.models import BusinessArea - - ba = BusinessAreaFactory() - q: Query = QueryFactory( - target=ContentType.objects.get_for_model(BusinessArea), - code='result = { "business_area": [ b.slug for b in conn.all()] }', - ) - p: Parametrizer = ParametrizerFactory(source=q, code="c1", name="", value="") - p.refresh() - p.refresh_from_db() - assert p.value == {"business_area": [ba.slug]} diff --git a/tests/unit/libs/power_query/test_views.py b/tests/unit/libs/power_query/test_views.py deleted file mode 100644 index 82803dcb5e..0000000000 --- a/tests/unit/libs/power_query/test_views.py +++ /dev/null @@ -1,129 +0,0 @@ -import base64 - -from django.test import TestCase, override_settings -from django.urls import reverse - -import pytest -from power_query.defaults import create_defaults -from power_query.fixtures import ( - FormatterFactory, - ParametrizerFactory, - QueryFactory, - ReportFactory, -) -from power_query.models import Query, Report - -from hct_mis_api.apps.account.fixtures import BusinessAreaFactory, UserFactory - - -@pytest.mark.skip(reason="This test is not working") -@override_settings(POWER_QUERY_DB_ALIAS="default") -@pytest.mark.xfail(reason="This test is failing") -class TestPowerQueryViews(TestCase): - databases = {"default"} - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - from power_query.models import Query, Report - - cls.superuser = UserFactory(is_superuser=True, is_staff=True, is_active=True) - cls.user1 = UserFactory(is_superuser=False, is_staff=False, is_active=True) - cls.user2 = UserFactory(is_superuser=False, is_staff=False, is_active=True) - BusinessAreaFactory() - create_defaults() - cls.formatter_html = FormatterFactory(name="Queryset To HTML") - # hh = ContentType.objects.get(app_label="household", model="household") - - p = ParametrizerFactory() - cls.query: Query = QueryFactory(name="HH", parametrizer=p) - cls.query.execute_matrix() - cls.report1: Report = ReportFactory( - name="Report1", formatter=cls.formatter_html, query=cls.query, owner=cls.user1 - ) - cls.report2: Report = ReportFactory( - name="Report2", formatter=cls.formatter_html, query=cls.query, owner=cls.user2 - ) - cls.report2.execute() - cls.report3: Report = ReportFactory( - name="Report3", formatter=cls.formatter_html, query=cls.query, owner=cls.user1 - ) - cls.report3.limit_access_to.add(cls.user2) - cls.report3.save() - cls.report3.execute() - - def test_valid_report(self) -> None: - url = reverse("power_query:report", args=[self.report2.pk]) - self.client.login(username=self.report2.owner.username, password="password") - response = self.client.get(url) - - self.assertEqual(response.status_code, 200) - self.assertContains(response, b">Report2<") - - def test_valid_fetch(self) -> None: - with self.settings(POWER_QUERY_DB_ALIAS="default"): - url = reverse("power_query:data", args=[self.report2.documents.first().pk]) - username, password = self.report2.owner.username, "password" - token = "Basic " + base64.b64encode(f"{username}:{password}".encode()).decode("ascii") - response = self.client.get(url, HTTP_AUTHORIZATION=token) - self.assertEqual(response.status_code, 200) - self.assertContains(response, b">Report2<") - - def test_permission_owner(self) -> None: - with self.settings(POWER_QUERY_DB_ALIAS="default"): - url = reverse("power_query:document", args=[self.report2.pk, self.report2.documents.first().pk]) - self.client.login(username=self.report1.owner.username, password="password") - response = self.client.get(url) - self.assertEqual(response.status_code, 403) - - def test_permission_business_area(self) -> None: - with self.settings(POWER_QUERY_DB_ALIAS="default"): - url = reverse("power_query:document", args=[self.report2.pk, self.report2.documents.first().pk]) - self.client.login(username=self.report1.owner.username, password="password") - response = self.client.get(url) - self.assertEqual(response.status_code, 403) - - def test_permission_limit_access_to(self) -> None: - url = reverse("power_query:report", args=[self.report3.pk]) - self.client.login(username=self.report2.owner.username, password="password") - response = self.client.get(url) - - self.assertEqual(response.status_code, 200) - self.assertContains(response, b">Report3<") - url = reverse("power_query:document", args=[self.report3.pk, self.report3.documents.first().pk]) - response = self.client.get(url) - self.assertEqual(response.status_code, 403) - self.report3.documents.first().limit_access_to.add(self.user2) - self.report3.documents.first().save() - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - -@pytest.mark.skip(reason="This test is not working") -@override_settings(POWER_QUERY_DB_ALIAS="default") -@pytest.mark.xfail(reason="This test is failing") -class TestPowerQueryBasicAuth(TestCase): - databases = {"default"} - - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - cls.superuser = UserFactory(is_superuser=True, is_staff=True, is_active=True) - cls.user1 = UserFactory(is_superuser=False, is_staff=False, is_active=True) - cls.user2 = UserFactory(is_superuser=False, is_staff=False, is_active=True) - BusinessAreaFactory() - create_defaults() - - cls.formatter_json = FormatterFactory(name="Queryset To JSON", content_type="json", code="") - cls.query: Query = QueryFactory() - cls.query.execute_matrix() - cls.report1: Report = ReportFactory(formatter=cls.formatter_json, query=cls.query, owner=cls.user1) - cls.report2: Report = ReportFactory(formatter=cls.formatter_json, query=cls.query, owner=cls.user2) - cls.report2.execute() - - def test_valid_fetch(self) -> None: - url = reverse("power_query:data", args=[self.report2.documents.first().pk]) - username, password = self.report2.owner.username, "password" - token = "Basic " + base64.b64encode(f"{username}:{password}".encode()).decode("ascii") - response = self.client.get(url, HTTP_AUTHORIZATION=token) - self.assertEqual(response.status_code, 200) diff --git a/tests/unit/libs/power_query/utils.py b/tests/unit/libs/power_query/utils.py deleted file mode 100644 index 38f9a34d01..0000000000 --- a/tests/unit/libs/power_query/utils.py +++ /dev/null @@ -1,58 +0,0 @@ -import operator -from functools import reduce -from typing import Any - -from django.contrib.auth.models import Group, Permission -from django.db.models import Q - -from hct_mis_api.apps.account.models import User, UserGroup -from hct_mis_api.apps.core.models import BusinessArea - - -class user_grant_office_permission(object): - def __init__(self, user: User, office: BusinessArea, permissions: Any) -> None: - self.user = user - self.office = office - if isinstance(permissions, str): - self.permissions = [permissions] - else: - self.permissions = permissions - - def __enter__(self) -> None: - if hasattr(self.user, "_group_perm_cache"): - del self.user._group_perm_cache - - if hasattr(self.user, "_perm_cache"): - del self.user._perm_cache - - key = f"_perm_{self.office.slug}" - if hasattr(self.user, key): - delattr(self.user, key) - - or_queries = [] - if self.permissions: - self.group, _ = Group.objects.get_or_create(name="context_group") - - for permission in self.permissions: - app, perm = permission.split(".") - or_queries.append(Q(**{"codename": perm, "content_type__app_label": app})) - self.group.permissions.set(Permission.objects.filter(reduce(operator.or_, or_queries))) - self.group.save() - self.user_group, _ = UserGroup.objects.get_or_create( - user=self.user, group=self.group, business_area=self.office - ) - - def __exit__(self, e_typ: Any = None, e_val: Any = None, trcbak: Any = None) -> None: - if all((e_typ, e_val, trcbak)): - raise e_typ(e_val) from e_val - if self.group: - self.user_group.delete() - self.group.delete() - - def start(self) -> None: - """Activate a patch, returning any created mock.""" - self.__enter__() - - def stop(self) -> None: - """Stop an active patch.""" - return self.__exit__() From fa1935e470a1d8752a4e86ce916b58be4634b846 Mon Sep 17 00:00:00 2001 From: Paulina Kujawa Date: Thu, 10 Oct 2024 14:43:09 +0200 Subject: [PATCH 100/202] rest api updates + tests --- src/hct_mis_api/api/endpoints/core/filters.py | 15 ++ src/hct_mis_api/api/endpoints/core/views.py | 22 +++ .../api/endpoints/lookups/__init__.py | 3 +- src/hct_mis_api/api/endpoints/lookups/base.py | 15 +- .../api/endpoints/program/filters.py | 28 ++++ .../api/endpoints/program/views.py | 21 +++ src/hct_mis_api/api/endpoints/serializers.py | 10 ++ src/hct_mis_api/api/urls.py | 3 +- src/hct_mis_api/config/fragments/drf.py | 3 +- tests/unit/api/test_lookups.py | 51 +++++++ tests/unit/api/test_program.py | 133 ++++++++++-------- 11 files changed, 241 insertions(+), 63 deletions(-) create mode 100644 src/hct_mis_api/api/endpoints/core/filters.py create mode 100644 src/hct_mis_api/api/endpoints/program/filters.py create mode 100644 tests/unit/api/test_lookups.py diff --git a/src/hct_mis_api/api/endpoints/core/filters.py b/src/hct_mis_api/api/endpoints/core/filters.py new file mode 100644 index 0000000000..8edf2657bd --- /dev/null +++ b/src/hct_mis_api/api/endpoints/core/filters.py @@ -0,0 +1,15 @@ +from django_filters import DateFromToRangeFilter +from django_filters.rest_framework import FilterSet + +from hct_mis_api.apps.core.models import BusinessArea + + +class BusinessAreaFilter(FilterSet): + updated_at = DateFromToRangeFilter() + + class Meta: + model = BusinessArea + fields = ( + "active", + "updated_at", + ) diff --git a/src/hct_mis_api/api/endpoints/core/views.py b/src/hct_mis_api/api/endpoints/core/views.py index 5a3abd7b79..ace581f6c9 100644 --- a/src/hct_mis_api/api/endpoints/core/views.py +++ b/src/hct_mis_api/api/endpoints/core/views.py @@ -1,10 +1,32 @@ +from typing import TYPE_CHECKING, Any + from rest_framework.generics import ListAPIView +from rest_framework.response import Response from hct_mis_api.api.endpoints.base import HOPEAPIView +from hct_mis_api.api.endpoints.core.filters import BusinessAreaFilter from hct_mis_api.api.endpoints.core.serializers import BusinessAreaSerializer from hct_mis_api.apps.core.models import BusinessArea +if TYPE_CHECKING: + from rest_framework.request import Request + class BusinessAreaListView(HOPEAPIView, ListAPIView): serializer_class = BusinessAreaSerializer queryset = BusinessArea.objects.all() + + def list(self, request: "Request", *args: Any, **kwargs: Any) -> Response: + queryset = self.queryset + + filterset = BusinessAreaFilter(request.GET, queryset=queryset) + if filterset.is_valid(): + queryset = filterset.qs + + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) diff --git a/src/hct_mis_api/api/endpoints/lookups/__init__.py b/src/hct_mis_api/api/endpoints/lookups/__init__.py index 5782862d81..f6a24e5bb6 100644 --- a/src/hct_mis_api/api/endpoints/lookups/__init__.py +++ b/src/hct_mis_api/api/endpoints/lookups/__init__.py @@ -1,10 +1,11 @@ from hct_mis_api.api.endpoints.lookups.area import AreaList, AreaTypeList # noqa: F401 from hct_mis_api.api.endpoints.lookups.base import ( # noqa: F401 - Country, + CountryAPIView, DataCollectingPolicy, DocumentType, MaritalStatus, ObservedDisability, + ProgramStatuses, Relationship, ResidenceStatus, Roles, diff --git a/src/hct_mis_api/api/endpoints/lookups/base.py b/src/hct_mis_api/api/endpoints/lookups/base.py index 737b463a67..18d4ae51e6 100644 --- a/src/hct_mis_api/api/endpoints/lookups/base.py +++ b/src/hct_mis_api/api/endpoints/lookups/base.py @@ -1,9 +1,10 @@ from typing import TYPE_CHECKING, Any, Optional -from django_countries import Countries from rest_framework.response import Response from hct_mis_api.api.endpoints.base import HOPEAPIView +from hct_mis_api.api.endpoints.serializers import CountrySerializer +from hct_mis_api.apps.geo.models import Country from hct_mis_api.apps.household.models import ( COLLECT_TYPES, IDENTIFICATION_TYPE_CHOICE, @@ -25,9 +26,12 @@ def get(self, request: "Request", format: Optional[Any] = None) -> Response: return Response(dict(IDENTIFICATION_TYPE_CHOICE)) -class Country(HOPEAPIView): +class CountryAPIView(HOPEAPIView): + serializer_class = CountrySerializer + def get(self, request: "Request", format: Optional[Any] = None) -> Response: - return Response(dict(Countries())) + serializer = self.serializer_class(Country.objects.all(), many=True) + return Response(serializer.data) class ResidenceStatus(HOPEAPIView): @@ -78,3 +82,8 @@ def get(self, request: "Request", format: Optional[Any] = None) -> Response: class ProgramScope(HOPEAPIView): def get(self, request: "Request", format: Optional[Any] = None) -> Response: return Response(dict(Program.SCOPE_CHOICE)) + + +class ProgramStatuses(HOPEAPIView): + def get(self, request: "Request", format: Optional[Any] = None) -> Response: + return Response(dict(Program.STATUS_CHOICE)) diff --git a/src/hct_mis_api/api/endpoints/program/filters.py b/src/hct_mis_api/api/endpoints/program/filters.py new file mode 100644 index 0000000000..e1ca22b5c7 --- /dev/null +++ b/src/hct_mis_api/api/endpoints/program/filters.py @@ -0,0 +1,28 @@ +from django.db.models.query import QuerySet + +from django_filters import BooleanFilter, CharFilter, DateFromToRangeFilter +from django_filters.rest_framework import FilterSet + +from hct_mis_api.apps.program.models import Program + + +class ProgramFilter(FilterSet): + business_area = CharFilter(field_name="business_area__slug") + active = BooleanFilter(method="is_active_filter") + updated_at = DateFromToRangeFilter() + + class Meta: + model = Program + fields = ( + "business_area", + "active", + "updated_at", + "status", + ) + + def is_active_filter(self, queryset: "QuerySet[Program]", name: str, value: bool) -> "QuerySet[Program]": + if value is True: + return queryset.filter(status=Program.ACTIVE) + elif value is False: + return queryset.exclude(status=Program.ACTIVE) + return queryset diff --git a/src/hct_mis_api/api/endpoints/program/views.py b/src/hct_mis_api/api/endpoints/program/views.py index 78e77da2f3..3402938df8 100644 --- a/src/hct_mis_api/api/endpoints/program/views.py +++ b/src/hct_mis_api/api/endpoints/program/views.py @@ -1,10 +1,31 @@ +from typing import TYPE_CHECKING, Any + from rest_framework.generics import ListAPIView +from rest_framework.response import Response from hct_mis_api.api.endpoints.base import HOPEAPIView +from hct_mis_api.api.endpoints.program.filters import ProgramFilter from hct_mis_api.api.endpoints.program.serializers import ProgramGlobalSerializer from hct_mis_api.apps.program.models import Program +if TYPE_CHECKING: + from rest_framework.request import Request + class ProgramGlobalListView(HOPEAPIView, ListAPIView): serializer_class = ProgramGlobalSerializer queryset = Program.objects.all() + + def list(self, request: "Request", *args: Any, **kwargs: Any) -> Response: + queryset = self.queryset + filterset = ProgramFilter(request.GET, queryset=queryset) + if filterset.is_valid(): + queryset = filterset.qs + + page = self.paginate_queryset(queryset) + if page is not None: + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = self.serializer_class(queryset, many=True) + return Response(serializer.data) diff --git a/src/hct_mis_api/api/endpoints/serializers.py b/src/hct_mis_api/api/endpoints/serializers.py index 0485a2e91e..163262a532 100644 --- a/src/hct_mis_api/api/endpoints/serializers.py +++ b/src/hct_mis_api/api/endpoints/serializers.py @@ -1,7 +1,17 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from hct_mis_api.apps.geo.models import Country + class RejectPolicy(models.TextChoices): STRICT = "STRICT", _("STRICT") LAX = "LAX", _("Lax") + + +class CountrySerializer(serializers.ModelSerializer): + class Meta: + model = Country + fields = ("name", "short_name", "iso_code2", "iso_code3", "iso_num") diff --git a/src/hct_mis_api/api/urls.py b/src/hct_mis_api/api/urls.py index 0871c71f28..86bdf21af0 100644 --- a/src/hct_mis_api/api/urls.py +++ b/src/hct_mis_api/api/urls.py @@ -22,7 +22,7 @@ path("areatypes/", endpoints.lookups.AreaTypeList().as_view(), name="areatype-list"), path("constance/", ConstanceSettingsAPIView().as_view(), name="constance-list"), path("lookups/document/", endpoints.lookups.DocumentType().as_view(), name="document-list"), - path("lookups/country/", endpoints.lookups.Country().as_view(), name="country-list"), + path("lookups/country/", endpoints.lookups.CountryAPIView().as_view(), name="country-list"), path("lookups/residencestatus/", endpoints.lookups.ResidenceStatus().as_view(), name="residencestatus-list"), path("lookups/maritalstatus/", endpoints.lookups.MaritalStatus().as_view(), name="maritalstatus-list"), path( @@ -36,6 +36,7 @@ ), path("lookups/role/", endpoints.lookups.Roles().as_view(), name="role-list"), path("lookups/sex/", endpoints.lookups.Sex().as_view(), name="sex-list"), + path("lookups/program-statuses/", endpoints.lookups.ProgramStatuses().as_view(), name="program-statuses-list"), path("business_areas/", endpoints.core.BusinessAreaListView.as_view(), name="business-area-list"), path("programs/", ProgramGlobalListView.as_view(), name="program-global-list"), path( diff --git a/src/hct_mis_api/config/fragments/drf.py b/src/hct_mis_api/config/fragments/drf.py index 11b5ee8354..543421b577 100644 --- a/src/hct_mis_api/config/fragments/drf.py +++ b/src/hct_mis_api/config/fragments/drf.py @@ -1,9 +1,10 @@ REST_FRAMEWORK = { "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination", - "PAGE_SIZE": 10, + "PAGE_SIZE": 50, "TEST_REQUEST_DEFAULT_FORMAT": "json", "DEFAULT_AUTHENTICATION_CLASSES": [ "hct_mis_api.api.utils.CsrfExemptSessionAuthentication", ], "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", + "SECURE_SSL_REDIRECT": True, } diff --git a/tests/unit/api/test_lookups.py b/tests/unit/api/test_lookups.py new file mode 100644 index 0000000000..d6e1bc0270 --- /dev/null +++ b/tests/unit/api/test_lookups.py @@ -0,0 +1,51 @@ +from rest_framework import status +from rest_framework.reverse import reverse + +from hct_mis_api.apps.geo.fixtures import CountryFactory +from hct_mis_api.apps.program.models import Program +from tests.unit.api.base import HOPEApiTestCase + + +class APIProgramStatuesTests(HOPEApiTestCase): + databases = {"default"} + user_permissions = [] + + def test_get_program_statues(self) -> None: + url = reverse("api:program-statuses-list") + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.json(), dict(Program.STATUS_CHOICE)) + + +class APICountriesTests(HOPEApiTestCase): + databases = {"default"} + user_permissions = [] + + def test_get_countries(self) -> None: + country_afghanistan = CountryFactory() + country_poland = CountryFactory( + name="Poland", + short_name="Poland", + iso_code2="PL", + iso_code3="POL", + iso_num="0620", + ) + url = reverse("api:country-list") + response = self.client.get(url) + assert response.status_code == status.HTTP_200_OK + assert response.json() == [ + { + "name": country_afghanistan.name, + "short_name": country_afghanistan.short_name, + "iso_code2": country_afghanistan.iso_code2, + "iso_code3": country_afghanistan.iso_code3, + "iso_num": country_afghanistan.iso_num, + }, + { + "name": country_poland.name, + "short_name": country_poland.short_name, + "iso_code2": country_poland.iso_code2, + "iso_code3": country_poland.iso_code3, + "iso_num": country_poland.iso_num, + }, + ] diff --git a/tests/unit/api/test_program.py b/tests/unit/api/test_program.py index 1f006f50ba..021660149f 100644 --- a/tests/unit/api/test_program.py +++ b/tests/unit/api/test_program.py @@ -6,6 +6,7 @@ from hct_mis_api.api.models import APIToken, Grant from hct_mis_api.apps.account.fixtures import BusinessAreaFactory from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory +from hct_mis_api.apps.core.models import BusinessArea from hct_mis_api.apps.program.fixtures import ProgramFactory from hct_mis_api.apps.program.models import Program from tests.unit.api.base import HOPEApiTestCase @@ -156,39 +157,69 @@ class APIGlobalProgramTests(HOPEApiTestCase): def setUpTestData(cls) -> None: super().setUpTestData() cls.list_url = reverse("api:program-global-list") - - def test_list_program(self) -> None: - program1: Program = ProgramFactory( + cls.program1: Program = ProgramFactory( budget=10000, start_date="2022-01-12", end_date="2022-09-12", - business_area=self.business_area, + business_area=cls.business_area, population_goal=200, status=Program.ACTIVE, ) - program2: Program = ProgramFactory( + cls.program2: Program = ProgramFactory( budget=200, start_date="2022-01-10", end_date="2022-09-10", - business_area=self.business_area, + business_area=cls.business_area, population_goal=200, status=Program.DRAFT, ) # program from another BA - also listed as we do not filter by BA - business_area2 = BusinessAreaFactory(name="Ukraine") - program_from_another_ba = ProgramFactory( + cls.business_area2 = BusinessAreaFactory(name="Ukraine") + cls.program_from_another_ba = ProgramFactory( budget=200, start_date="2022-01-10", end_date="2022-09-10", - business_area=business_area2, + business_area=cls.business_area2, population_goal=400, status=Program.ACTIVE, ) - program1.refresh_from_db() - program2.refresh_from_db() - program_from_another_ba.refresh_from_db() + cls.program1.refresh_from_db() + cls.program2.refresh_from_db() + cls.program_from_another_ba.refresh_from_db() + + def expected_response(program: Program, business_area: BusinessArea) -> dict: + return { + "budget": str(program.budget), + "business_area_code": business_area.code, + "cash_plus": program.cash_plus, + "end_date": program.end_date.strftime("%Y-%m-%d"), + "frequency_of_payments": program.frequency_of_payments, + "id": str(program.id), + "name": program.name, + "population_goal": program.population_goal, + "programme_code": program.programme_code, + "scope": program.scope, + "sector": program.sector, + "status": program.status, + "start_date": program.start_date.strftime("%Y-%m-%d"), + } + + cls.program_from_another_ba_expected_response = expected_response( + cls.program_from_another_ba, + cls.business_area2, + ) + + cls.program1_expected_response = expected_response( + cls.program1, + cls.business_area, + ) + cls.program2_expected_response = expected_response( + cls.program2, + cls.business_area, + ) + def test_list_program(self) -> None: response = self.client.get(self.list_url) assert response.status_code == 403 @@ -197,56 +228,44 @@ def test_list_program(self) -> None: self.assertEqual(response.status_code, 200) self.assertEqual(len(response.json()["results"]), 3) self.assertIn( - { - "budget": str(program1.budget), - "business_area_code": self.business_area.code, - "cash_plus": program1.cash_plus, - "end_date": program1.end_date.strftime("%Y-%m-%d"), - "frequency_of_payments": program1.frequency_of_payments, - "id": str(program1.id), - "name": program1.name, - "population_goal": program1.population_goal, - "programme_code": program1.programme_code, - "scope": program1.scope, - "sector": program1.sector, - "status": program1.status, - "start_date": program1.start_date.strftime("%Y-%m-%d"), - }, + self.program1_expected_response, response.json()["results"], ) self.assertIn( - { - "budget": str(program2.budget), - "business_area_code": self.business_area.code, - "cash_plus": program2.cash_plus, - "end_date": program2.end_date.strftime("%Y-%m-%d"), - "frequency_of_payments": program2.frequency_of_payments, - "id": str(program2.id), - "name": program2.name, - "population_goal": program2.population_goal, - "programme_code": program2.programme_code, - "scope": program2.scope, - "sector": program2.sector, - "status": program2.status, - "start_date": program2.start_date.strftime("%Y-%m-%d"), - }, + self.program2_expected_response, response.json()["results"], ) self.assertIn( - { - "budget": str(program_from_another_ba.budget), - "business_area_code": business_area2.code, - "cash_plus": program_from_another_ba.cash_plus, - "end_date": program_from_another_ba.end_date.strftime("%Y-%m-%d"), - "frequency_of_payments": program_from_another_ba.frequency_of_payments, - "id": str(program_from_another_ba.id), - "name": program_from_another_ba.name, - "population_goal": program_from_another_ba.population_goal, - "programme_code": program_from_another_ba.programme_code, - "scope": program_from_another_ba.scope, - "sector": program_from_another_ba.sector, - "status": program_from_another_ba.status, - "start_date": program_from_another_ba.start_date.strftime("%Y-%m-%d"), - }, + self.program_from_another_ba_expected_response, + response.json()["results"], + ) + + def test_list_program_filter_business_area(self) -> None: + with token_grant_permission(self.token, Grant.API_READ_ONLY): + response = self.client.get(self.list_url, {"business_area": "afghanistan"}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.json()["results"]), 2) + self.assertNotIn( + self.program_from_another_ba_expected_response, + response.json()["results"], + ) + + def test_list_program_filter_active(self) -> None: + with token_grant_permission(self.token, Grant.API_READ_ONLY): + response = self.client.get(self.list_url, {"active": "true"}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.json()["results"]), 2) + self.assertNotIn( + self.program2_expected_response, + response.json()["results"], + ) + + def test_list_program_filter_status(self) -> None: + with token_grant_permission(self.token, Grant.API_READ_ONLY): + response = self.client.get(self.list_url, {"status": Program.DRAFT}) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(response.json()["results"]), 1) + self.assertIn( + self.program2_expected_response, response.json()["results"], ) From c1b1027a6aa0a9459608b1f209a93e3d98a71731 Mon Sep 17 00:00:00 2001 From: Maciej Szewczyk Date: Thu, 10 Oct 2024 23:19:49 +0200 Subject: [PATCH 101/202] snapshots --- .../steficon/fakeApolloAllSteficonRules.ts | 3 +- src/frontend/package.json | 3 + .../CashPlanDetails.test.tsx.snap | 66 +- .../DocumentSearchField.test.tsx.snap | 10 +- .../GrievancesSection.test.tsx.snap | 8 +- .../PaymentVerificationSection.test.tsx.snap | 2 +- ...otalAmountTransferredSection.test.tsx.snap | 10 +- ...mberOfChildrenReachedSection.test.tsx.snap | 8 +- ...erOfHouseholdsReachedSection.test.tsx.snap | 8 +- ...rOfIndividualsReachedSection.test.tsx.snap | 8 +- .../TicketsByCategorySection.test.tsx.snap | 2 +- ...ByLocationAndCategorySection.test.tsx.snap | 2 +- .../TicketsByStatusSection.test.tsx.snap | 2 +- .../GrievancesDetails.test.tsx.snap | 98 +- .../PaymentGrievanceDetails.test.tsx.snap | 6 +- .../PaymentPlanTargeting.test.tsx.snap | 10 +- .../CreateSetUpFspHeader.test.tsx.snap | 6 +- .../DeliveryMechanismRow.test.tsx.snap | 2 +- .../SetUpFspButtonActions.test.tsx | 3 +- .../SetUpFspCore/SetUpFspCore.test.tsx | 2 +- .../__snapshots__/EditFspHeader.test.tsx.snap | 6 +- .../EditPaymentPlanHeader.test.tsx.snap | 12 +- .../DeliveryMechanismWarning.test.tsx.snap | 4 +- .../__snapshots__/FspHeader.test.tsx.snap | 6 +- .../__snapshots__/TotalAmount.test.tsx.snap | 4 +- .../AcceptanceProcessStepper.test.tsx | 2 +- .../AcceptanceProcessRow.test.tsx.snap | 10 +- .../__snapshots__/GreyInfoCard.test.tsx.snap | 8 +- .../__snapshots__/MessageDialog.test.tsx.snap | 2 +- .../Entitlement/Entitlement.test.tsx | 9 +- .../Entitlement/Entitlement.tsx | 2 +- .../__snapshots__/Entitlement.test.tsx.snap | 250 +- ...meByDeliveryMechanismSection.test.tsx.snap | 24 +- .../__snapshots__/FspSection.test.tsx.snap | 44 +- ...tXlsxPaymentPlanPaymentListPerFsp.test.tsx | 3 +- ...PaymentPlanPaymentListPerFsp.test.tsx.snap | 2 +- .../PaymentPlanDetails.test.tsx.snap | 50 +- .../ApprovePaymentPlan.test.tsx | 3 +- .../AuthorizePaymentPlan.test.tsx | 3 +- .../DeletePaymentPlan.test.tsx | 3 +- .../AcceptedPaymentPlanHeaderButtons.test.tsx | 25 +- ...nApprovalPaymentPlanHeaderButtons.test.tsx | 3 +- ...orizationPaymentPlanHeaderButtons.test.tsx | 3 +- .../InReviewPaymentPlanHeaderButtons.test.tsx | 3 +- .../LockedPaymentPlanHeaderButtons.test.tsx | 3 +- .../OpenPaymentPlanHeaderButtons.test.tsx | 3 +- .../LockPaymentPlan.test.tsx | 3 +- .../MarkAsReleasedPaymentPlan.test.tsx | 3 +- .../PaymentPlanDetailsHeader.test.tsx | 3 +- .../RejectPaymentPlan.test.tsx | 3 +- .../PaymentPlanDetailsResults.test.tsx | 2 +- .../PaymentPlanDetailsResults.test.tsx.snap | 48 +- .../WarningMissingAmount.test.tsx.snap | 2 +- .../CreatePaymentPlanHeader.test.tsx.snap | 6 +- .../PaymentPlanTargeting.test.tsx.snap | 10 +- .../CreateSetUpFspHeader.test.tsx.snap | 6 +- .../DeliveryMechanismRow.test.tsx.snap | 2 +- .../SetUpFspButtonActions.test.tsx | 2 +- .../SetUpFspCore/SetUpFspCore.test.tsx | 2 +- .../__snapshots__/EditFspHeader.test.tsx.snap | 6 +- .../EditPaymentPlanHeader.test.tsx.snap | 12 +- .../DeliveryMechanismWarning.test.tsx.snap | 4 +- .../__snapshots__/FspHeader.test.tsx.snap | 6 +- .../__snapshots__/TotalAmount.test.tsx.snap | 4 +- .../AcceptanceProcessStepper.test.tsx | 2 +- .../AcceptanceProcessRow.test.tsx.snap | 10 +- .../__snapshots__/GreyInfoCard.test.tsx.snap | 8 +- .../__snapshots__/MessageDialog.test.tsx.snap | 2 +- .../Entitlement/Entitlement.test.tsx | 9 +- .../Entitlement/Entitlement.tsx | 2 +- .../__snapshots__/Entitlement.test.tsx.snap | 250 +- ...meByDeliveryMechanismSection.test.tsx.snap | 24 +- .../__snapshots__/FspSection.test.tsx.snap | 44 +- ...tXlsxPaymentPlanPaymentListPerFsp.test.tsx | 3 +- ...PaymentPlanPaymentListPerFsp.test.tsx.snap | 2 +- .../PaymentPlanDetails.test.tsx.snap | 50 +- .../ApprovePaymentPlan.test.tsx | 3 +- .../AuthorizePaymentPlan.test.tsx | 3 +- .../DeletePaymentPlan.test.tsx | 3 +- .../AcceptedPaymentPlanHeaderButtons.test.tsx | 25 +- ...nApprovalPaymentPlanHeaderButtons.test.tsx | 3 +- ...orizationPaymentPlanHeaderButtons.test.tsx | 3 +- .../InReviewPaymentPlanHeaderButtons.test.tsx | 3 +- .../LockedPaymentPlanHeaderButtons.test.tsx | 3 +- .../OpenPaymentPlanHeaderButtons.test.tsx | 3 +- .../LockPaymentPlan.test.tsx | 3 +- .../MarkAsReleasedPaymentPlan.test.tsx | 3 +- .../PaymentPlanDetailsHeader.test.tsx | 3 +- .../RejectPaymentPlan.test.tsx | 3 +- .../PeoplePaymentPlanDetailsResults.test.tsx | 2 +- ...plePaymentPlanDetailsResults.test.tsx.snap | 40 +- .../WarningMissingAmount.test.tsx.snap | 2 +- .../PeopleBioData/PeopleBioData.test.tsx | 3 +- .../__snapshots__/PeopleBioData.test.tsx.snap | 160 +- ...ionalRegistrationInformation.test.tsx.snap | 4 +- .../HouseholdDetails.test.tsx.snap | 86 +- ...ionalRegistrationInformation.test.tsx.snap | 16 +- .../IndividualBioData.test.tsx | 2 +- .../IndividualBioData.test.tsx.snap | 138 +- .../ProgramDetails.test.tsx.snap | 72 +- .../RegistrationDetails.test.tsx.snap | 132 +- .../HouseholdDetails.test.tsx.snap | 48 +- ...tionIndividualVulnerabilites.test.tsx.snap | 16 +- ...egistrationIndividualBioData.test.tsx.snap | 114 +- .../CommunicationTable.test.tsx | 2 +- .../CommunicationTable.test.tsx.snap | 138 +- .../tables/Feedback/FeedbackTable.test.tsx | 7 +- .../__snapshots__/FeedbackTable.test.tsx.snap | 68 +- .../ProgrammesTable/ProgrammesTable.test.tsx | 2 +- .../ProgrammesTable.test.tsx.snap | 96 +- .../ReportingTable/ReportingTable.test.tsx | 2 +- .../ReportingTable.test.tsx.snap | 106 +- .../tables/UsersTable/UsersTable.test.tsx | 7 +- .../__snapshots__/UsersTable.test.tsx.snap | 70 +- .../PaymentPlansTable.test.tsx | 2 +- .../PaymentPlansTable.test.tsx.snap | 108 +- .../PaymentsTable/PaymentsTable.test.tsx | 2 +- .../__snapshots__/PaymentsTable.test.tsx.snap | 176 +- .../PeoplePaymentPlansTable.test.tsx | 2 +- .../PeoplePaymentPlansTable.test.tsx.snap | 108 +- .../PeoplePaymentsTable.test.tsx | 2 +- .../PeoplePaymentsTable.test.tsx.snap | 152 +- .../CashPlanTable/CashPlanTable.test.tsx | 2 +- .../__snapshots__/CashPlanTable.test.tsx.snap | 112 +- ...entRecordAndPaymentHouseholdTable.test.tsx | 2 +- ...cordAndPaymentHouseholdTable.test.tsx.snap | 80 +- ...aymentRecordAndPaymentPeopleTable.test.tsx | 2 +- ...tRecordAndPaymentPeopleTable.test.tsx.snap | 76 +- .../PaymentRecordTable.test.tsx | 8 +- .../PaymentRecordTable.test.tsx.snap | 104 +- .../PaymentVerificationTable.test.tsx | 2 +- .../PaymentVerificationTable.test.tsx.snap | 84 +- .../VerificationRecordsTable.test.tsx | 2 +- .../VerificationRecordsTable.test.tsx.snap | 158 +- .../PeopleListTable/PeopleListTable.test.tsx | 2 +- .../PeopleListTable.test.tsx.snap | 70 +- .../HouseholdCompositionTable.test.tsx.snap | 64 +- .../HouseholdTable/HouseholdTable.test.tsx | 2 +- .../HouseholdTable.test.tsx.snap | 74 +- .../IndividualsListTable.test.tsx | 2 +- .../IndividualsListTable.test.tsx.snap | 72 +- ...HouseholdImportedIndividualsTable.test.tsx | 2 +- ...holdImportedIndividualsTable.test.tsx.snap | 80 +- .../ImportedHouseholdTable.test.tsx | 6 +- .../ImportedHouseholdTable.test.tsx.snap | 124 +- .../ImportedIndividualsTable.test.tsx | 2 +- .../ImportedIndividualsTable.test.tsx.snap | 132 +- .../ImportedPeopleTable.test.tsx | 2 +- .../ImportedPeopleTable.test.tsx.snap | 132 +- ...istrationDataImportForPeopleTable.test.tsx | 16 +- ...tionDataImportForPeopleTable.test.tsx.snap | 148 +- .../RegistrationDataImportTable.test.tsx | 6 +- .../RegistrationDataImportTable.test.tsx.snap | 148 +- .../TargetPopulationForPeopleTable.test.tsx | 7 +- ...rgetPopulationForPeopleTable.test.tsx.snap | 164 +- .../TargetPopulationTable.test.tsx | 7 +- .../TargetPopulationTable.test.tsx.snap | 164 +- src/frontend/yarn.lock | 4261 ++++++++--------- 158 files changed, 4924 insertions(+), 4617 deletions(-) diff --git a/src/frontend/fixtures/steficon/fakeApolloAllSteficonRules.ts b/src/frontend/fixtures/steficon/fakeApolloAllSteficonRules.ts index 4e42f96fd6..772abb5842 100644 --- a/src/frontend/fixtures/steficon/fakeApolloAllSteficonRules.ts +++ b/src/frontend/fixtures/steficon/fakeApolloAllSteficonRules.ts @@ -8,12 +8,11 @@ export const fakeApolloAllSteficonRules = [ enabled: true, deprecated: false, type: 'PAYMENT_PLAN', - first: 5, }, }, result: { data: { - allStepiconRules: { + allSteficonRules: { pageInfo: { hasNextPage: true, hasPreviousPage: false, diff --git a/src/frontend/package.json b/src/frontend/package.json index 0decd555c2..81cdb3e5b1 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -125,5 +125,8 @@ "react-test-renderer": "^18.2.0", "ts-jest": "^29.1.2", "typescript": "~5.3.3" + }, + "resolutions": { + "braces": "^3.0.3" } } diff --git a/src/frontend/src/components/core/CashPlanDetails/__snapshots__/CashPlanDetails.test.tsx.snap b/src/frontend/src/components/core/CashPlanDetails/__snapshots__/CashPlanDetails.test.tsx.snap index 4b21ddfa2f..cbe2a62c86 100644 --- a/src/frontend/src/components/core/CashPlanDetails/__snapshots__/CashPlanDetails.test.tsx.snap +++ b/src/frontend/src/components/core/CashPlanDetails/__snapshots__/CashPlanDetails.test.tsx.snap @@ -3,7 +3,7 @@ exports[`components/core/CashPlanDetails should render 1`] = `
    Status @@ -41,14 +41,14 @@ exports[`components/core/CashPlanDetails should render 1`] = ` data-cy="label-Status" >
    DISTRIBUTION COMPLETED @@ -63,7 +63,7 @@ exports[`components/core/CashPlanDetails should render 1`] = ` >
    Plan Start Date @@ -72,7 +72,7 @@ exports[`components/core/CashPlanDetails should render 1`] = ` data-cy="label-Plan Start Date" >