diff --git a/src/.flake8 b/.flake8 similarity index 98% rename from src/.flake8 rename to .flake8 index 5734c6b738..2de1c3d64e 100644 --- a/src/.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 ca25a814cc..9a0c457330 100755 --- a/.github/helpers/dev.sh +++ b/.github/helpers/dev.sh @@ -17,10 +17,10 @@ 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/ + ./tests/unit/ ;; "lint") mkdir -p ./lint-results diff --git a/.github/helpers/docker-compose.selenium.yml b/.github/helpers/docker-compose.selenium.yml index c03d1381a5..7ac51dba81 100644 --- a/.github/helpers/docker-compose.selenium.yml +++ b/.github/helpers/docker-compose.selenium.yml @@ -4,11 +4,11 @@ volumes: services: backend: volumes: - - ../src/report/screenshot/:/code/screenshot/ - - ../src/report/:/code/report/ + - ../../tests/test-coverage:/code/test-coverage + - ../../tests/report/:/code/tests/selenium/output_data/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/.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 abddcdc030..1f1c2c97f1 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:./coverage.xml --html-report=./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 @@ -273,14 +272,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: ./test-coverage/coverage.xml flags: e2e token: ${{ secrets.CODECOV_TOKEN }} verbose: true @@ -297,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 }}' diff --git a/.gitignore b/.gitignore index 1719e999f2..691473bbb2 100644 --- a/.gitignore +++ b/.gitignore @@ -101,6 +101,8 @@ ENV/ env.bak/ venv.bak/ +.pdm-python + # Spyder project settings .spyderproject .spyproject @@ -129,3 +131,10 @@ venv.bak/ cov.json .vscode/ +output_data/ +.coverage + +archive/ +output.json +pytest_html_report.html +compose.dist.yml 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/codecov.yml b/codecov.yml index 02b73140fc..72f96c556e 100644 --- a/codecov.yml +++ b/codecov.yml @@ -9,3 +9,19 @@ coverage: patch: default: 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/*" diff --git a/development_tools/compose.yml b/development_tools/compose.yml index 9f3b6c1716..943c62bc7c 100644 --- a/development_tools/compose.yml +++ b/development_tools/compose.yml @@ -25,11 +25,10 @@ services: ports: - "8080:8000" volumes: - - ../src:/code/ - - ../tests:/tests/ + - ../.:/code/ - 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: @@ -125,9 +124,9 @@ services: env_file: - .env ports: - - "5433:5432" + - "5432:5432" healthcheck: - test: [ "CMD-SHELL", "pg_isready -U postgres" ] + test: [ "CMD-SHELL", "su - postgres -c 'pg_isready -h db -U postgres'" ] interval: 10s timeout: 10s retries: 5 @@ -138,8 +137,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_init.sh old mode 100644 new mode 100755 similarity index 79% rename from development_tools/local_selenium_env.sh rename to development_tools/local_selenium_init.sh index bd230dd602..b175319558 --- a/development_tools/local_selenium_env.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 @@ -38,5 +42,14 @@ 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 +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 +pushd $MAIN_DIR/src/frontend +yarn +yarn build-for-backend +popd diff --git a/docker/Dockerfile b/docker/Dockerfile index c95249d117..e546489360 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -41,21 +41,19 @@ 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}" - +ENV DJANGO_SETTINGS_MODULE=hct_mis_api.config.settings 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:$CODE/test/:$PYTHONPATH RUN pdm sync --no-editable --no-self --no-isolation WORKDIR $CODE -COPY ./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 ./src/ ./src/ +COPY ./tests ./tests +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 + -FROM be-dist AS dist +## Dist (backend only) image +FROM base AS dist -COPY --chown=hope:hope --from=frontend-builder /fe-build/build ./hct_mis_api/apps/web/static/web + +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 + +USER hope + +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 88% rename from src/pyproject.toml rename to pyproject.toml index 6ff76eef89..7d5ef6e541 100644 --- a/src/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,20 +67,17 @@ 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] django_settings_module = "hct_mis_api.settings" -[tool.pdm] -distribution = true - [tool.pdm.dev-dependencies] dev = [ "mypy==0.982", @@ -131,14 +129,22 @@ 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" description = "HCT MIS is UNICEF's humanitarian cash transfer platform." authors = [ - {name = "Tivix"}, + { name = "Tivix" }, ] dependencies = [ "setuptools==71.1.0", @@ -237,7 +243,7 @@ dependencies = [ ] requires-python = "==3.11.*" readme = "README.md" -license = {text = "None"} +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 501870fbe7..0000000000 Binary files a/src/data/RDI-VALID.xlsx and /dev/null differ diff --git a/src/frontend/data/schema.graphql b/src/frontend/data/schema.graphql index e1a3a1379d..c101609d6e 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 { @@ -963,6 +953,52 @@ type DeliveredQuantityNode { currency: String } +enum DeliveryMechanismDataDeliveryMechanismChoice { + 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 DeliveryMechanismDataNode implements Node { + id: ID! + rdiMergeStatus: DeliveryMechanismDataRdiMergeStatus! + createdAt: DateTime! + updatedAt: DateTime! + individual: IndividualNode! + deliveryMechanismChoice: DeliveryMechanismDataDeliveryMechanismChoice + deliveryMechanism: DeliveryMechanismNode! + data: JSONString! + isValid: Boolean + validationErrors: JSONString! + possibleDuplicateOf: DeliveryMechanismDataNode + possibleDuplicates(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismDataNodeConnection! + name: String + individualTabData: JSONString +} + +type DeliveryMechanismDataNodeConnection { + pageInfo: PageInfo! + edges: [DeliveryMechanismDataNodeEdge]! + totalCount: Int + edgeCount: Int +} + +type DeliveryMechanismDataNodeEdge { + node: DeliveryMechanismDataNode + cursor: String! +} + input DeliveryMechanismDataObjectType { label: String! approveStatus: Boolean! @@ -975,6 +1011,11 @@ input DeliveryMechanismDataPayloadFieldObjectType { previousValue: String } +enum DeliveryMechanismDataRdiMergeStatus { + PENDING + MERGED +} + type DeliveryMechanismNode implements Node { id: ID! createdAt: DateTime! @@ -991,6 +1032,7 @@ type DeliveryMechanismNode implements Node { 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! + deliverymechanismdataSet(offset: Int, before: String, after: String, first: Int, last: Int): DeliveryMechanismDataNodeConnection! } type DeliveryMechanismNodeConnection { @@ -2518,11 +2560,10 @@ 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! + deliveryMechanismsData: [DeliveryMechanismDataNode] 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! @@ -2876,6 +2917,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 +2989,6 @@ type PartnerNode { name: String parent: PartnerNode isUn: Boolean! - permissions: JSONString! lft: Int! rght: Int! treeId: Int! @@ -2977,7 +3018,6 @@ type PartnerType { name: String! parent: PartnerNode isUn: Boolean! - permissions: JSONString! lft: Int! rght: Int! treeId: Int! @@ -3330,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 @@ -3352,6 +3393,7 @@ type PaymentPlanNode implements Node { unsuccessfulPaymentsCount: Int canSendToPaymentGateway: Boolean canSplit: Boolean + supportingDocuments: [PaymentPlanSupportingDocumentNode] } type PaymentPlanNodeConnection { @@ -3378,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 @@ -3774,7 +3835,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 +4245,7 @@ type RegistrationDataImportNode implements Node { goldenRecordPossibleDuplicatesCountAndPercentage: [CountAndPercentageNode] goldenRecordUniqueCountAndPercentage: [CountAndPercentageNode] totalHouseholdsCountWithValidPhoneNo: Int - isDeduplicated: String + biometricDeduplicated: String canMerge: Boolean biometricDeduplicationEnabled: Boolean } @@ -5020,6 +5080,7 @@ type TicketIndividualDataUpdateDetailsNodeEdge { type TicketNeedsAdjudicationDetailsExtraDataNode { goldenRecords: [DeduplicationResultNode] possibleDuplicate: [DeduplicationResultNode] + dedupEngineSimilarityPair: DeduplicationEngineSimilarityPairNode } type TicketNeedsAdjudicationDetailsNode implements Node { @@ -5036,7 +5097,6 @@ type TicketNeedsAdjudicationDetailsNode implements Node { scoreMin: Float! scoreMax: Float! isCrossArea: Boolean! - dedupEngineSimilarityPair: DeduplicationEngineSimilarityPairNode selectedIndividual: IndividualNode possibleDuplicate: IndividualNode hasDuplicatedDocument: Boolean @@ -5334,12 +5394,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/package.json b/src/frontend/package.json index 6420184892..31926d3e61 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -6,13 +6,13 @@ "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", "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..b9241a0d54 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; - program: ProgramNode; - similarityScore?: Maybe; - ticketneedsadjudicationdetailsSet: TicketNeedsAdjudicationDetailsNodeConnection; -}; - - -export type DeduplicationEngineSimilarityPairNodeTicketneedsadjudicationdetailsSetArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - offset?: InputMaybe; -}; - -export type DeduplicationEngineSimilarityPairNodeConnection = { - __typename?: 'DeduplicationEngineSimilarityPairNodeConnection'; - edgeCount?: Maybe; - edges: Array>; - pageInfo: PageInfo; - totalCount?: Maybe; +export type DeduplicationEngineSimilarityPairIndividualNode = { + __typename?: 'DeduplicationEngineSimilarityPairIndividualNode'; + fullName?: Maybe; + photo?: Maybe; + unicefId?: Maybe; }; -export type DeduplicationEngineSimilarityPairNodeEdge = { - __typename?: 'DeduplicationEngineSimilarityPairNodeEdge'; - cursor: Scalars['String']['output']; - node?: Maybe; +export type DeduplicationEngineSimilarityPairNode = { + __typename?: 'DeduplicationEngineSimilarityPairNode'; + individual1?: Maybe; + individual2?: Maybe; + similarityScore?: Maybe; }; export type DeduplicationResultNode = { @@ -1465,6 +1445,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?: Maybe; + 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 +1515,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 +1541,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; @@ -3428,8 +3481,6 @@ export type IndividualNode = Node & { age?: Maybe; ageAtRegistration?: Maybe; bankAccountInfo?: Maybe; - biometricDuplicates1: DeduplicationEngineSimilarityPairNodeConnection; - biometricDuplicates2: DeduplicationEngineSimilarityPairNodeConnection; birthDate: Scalars['Date']['output']; blockchainName: Scalars['String']['output']; businessArea: UserBusinessAreaNode; @@ -3445,6 +3496,7 @@ export type IndividualNode = Node & { deduplicationGoldenRecordResults?: Maybe>>; deduplicationGoldenRecordStatus: IndividualDeduplicationGoldenRecordStatus; deleteIndividualTicketDetails: TicketDeleteIndividualDetailsNodeConnection; + deliveryMechanismsData?: Maybe>>; detailId?: Maybe; disability: IndividualDisability; disabilityCertificatePicture?: Maybe; @@ -3534,24 +3586,6 @@ export type IndividualNode = Node & { }; -export type IndividualNodeBiometricDuplicates1Args = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - offset?: InputMaybe; -}; - - -export type IndividualNodeBiometricDuplicates2Args = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - offset?: InputMaybe; -}; - - export type IndividualNodeCollectorPaymentsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -4104,6 +4138,7 @@ export type Mutations = { updatePaymentVerificationReceivedAndReceivedAmount?: Maybe; updatePaymentVerificationStatusAndReceivedAmount?: Maybe; updateProgram?: Maybe; + updateProgramPartners?: Maybe; updateTargetPopulation?: Maybe; uploadImportDataXlsxFileAsync?: Maybe; }; @@ -4591,6 +4626,12 @@ export type MutationsUpdateProgramArgs = { }; +export type MutationsUpdateProgramPartnersArgs = { + programData?: InputMaybe; + version?: InputMaybe; +}; + + export type MutationsUpdateTargetPopulationArgs = { input: UpdateTargetPopulationInput; version?: InputMaybe; @@ -4674,7 +4715,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 +4801,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']; @@ -5152,6 +5191,7 @@ export type PaymentPlanNode = Node & { deliveryMechanisms?: Maybe>>; dispersionEndDate?: Maybe; dispersionStartDate?: Maybe; + documents: PaymentPlanSupportingDocumentNodeConnection; endDate?: Maybe; exchangeRate?: Maybe; excludeHouseholdError: Scalars['String']['output']; @@ -5184,6 +5224,7 @@ export type PaymentPlanNode = Node & { statusDate: Scalars['DateTime']['output']; steficonAppliedDate?: Maybe; steficonRule?: Maybe; + supportingDocuments?: Maybe>>; targetPopulation: TargetPopulationNode; totalDeliveredQuantity?: Maybe; totalDeliveredQuantityUsd?: Maybe; @@ -5214,6 +5255,15 @@ export type PaymentPlanNodeApprovalProcessArgs = { }; +export type PaymentPlanNodeDocumentsArgs = { + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + offset?: InputMaybe; +}; + + export type PaymentPlanNodeFollowUpsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -5267,6 +5317,28 @@ export enum PaymentPlanStatus { Preparing = 'PREPARING' } +export type PaymentPlanSupportingDocumentNode = Node & { + __typename?: 'PaymentPlanSupportingDocumentNode'; + createdBy?: Maybe; + 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>; + pageInfo: PageInfo; +}; + +export type PaymentPlanSupportingDocumentNodeEdge = { + __typename?: 'PaymentPlanSupportingDocumentNodeEdge'; + cursor: Scalars['String']['output']; + node?: Maybe; +}; + export type PaymentRecordAndPaymentNode = { __typename?: 'PaymentRecordAndPaymentNode'; caId?: Maybe; @@ -5743,7 +5815,6 @@ export type ProgramNode = Node & { createdAt: Scalars['DateTime']['output']; cycles?: Maybe; dataCollectingType?: Maybe; - deduplicationEngineSimilarityPairs: DeduplicationEngineSimilarityPairNodeConnection; deduplicationSetId?: Maybe; description: Scalars['String']['output']; endDate?: Maybe; @@ -5832,15 +5903,6 @@ export type ProgramNodeCyclesArgs = { }; -export type ProgramNodeDeduplicationEngineSimilarityPairsArgs = { - after?: InputMaybe; - before?: InputMaybe; - first?: InputMaybe; - last?: InputMaybe; - offset?: InputMaybe; -}; - - export type ProgramNodeFeedbackSetArgs = { after?: InputMaybe; before?: InputMaybe; @@ -7426,6 +7488,7 @@ export type RegistrationDataImportNode = Node & { batchPossibleDuplicates: Scalars['Int']['output']; batchUnique: Scalars['Int']['output']; batchUniqueCountAndPercentage?: Maybe>>; + biometricDeduplicated?: Maybe; biometricDeduplicationEnabled?: Maybe; businessArea?: Maybe; canMerge?: Maybe; @@ -7451,7 +7514,6 @@ export type RegistrationDataImportNode = Node & { importDate: Scalars['DateTime']['output']; importedBy?: Maybe; individuals: IndividualNodeConnection; - isDeduplicated?: Maybe; messages: CommunicationMessageNodeConnection; name: Scalars['String']['output']; numberOfHouseholds: Scalars['Int']['output']; @@ -8631,6 +8693,7 @@ export type TicketIndividualDataUpdateDetailsNodeEdge = { export type TicketNeedsAdjudicationDetailsExtraDataNode = { __typename?: 'TicketNeedsAdjudicationDetailsExtraDataNode'; + dedupEngineSimilarityPair?: Maybe; goldenRecords?: Maybe>>; possibleDuplicate?: Maybe>>; }; @@ -8638,7 +8701,6 @@ export type TicketNeedsAdjudicationDetailsExtraDataNode = { export type TicketNeedsAdjudicationDetailsNode = Node & { __typename?: 'TicketNeedsAdjudicationDetailsNode'; createdAt: Scalars['DateTime']['output']; - dedupEngineSimilarityPair?: Maybe; extraData?: Maybe; goldenRecordsIndividual: IndividualNode; hasDuplicatedDocument?: Maybe; @@ -8990,8 +9052,6 @@ export type UpdateProgramInput = { frequencyOfPayments?: InputMaybe; id: Scalars['String']['input']; name?: InputMaybe; - partnerAccess?: InputMaybe; - partners?: InputMaybe>>; pduFields?: InputMaybe>>; populationGoal?: InputMaybe; programmeCode?: InputMaybe; @@ -9000,6 +9060,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; @@ -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, 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 | 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 } }; @@ -9698,9 +9772,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 } | 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 }; @@ -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, 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 | 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']; @@ -10179,6 +10253,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; }>; @@ -10212,7 +10294,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 +10757,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, 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 | 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']; @@ -10756,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, 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 | 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 | 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; @@ -11088,7 +11170,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; }>; @@ -11291,7 +11373,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 +11413,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']; @@ -11696,6 +11778,11 @@ export const IndividualDetailedFragmentDoc = gql` accountHolderName bankBranchName } + deliveryMechanismsData { + name + isValid + individualTabData + } documents { edges { node { @@ -12146,20 +12233,6 @@ export const GrievanceTicketDetailedFragmentDoc = gql` } needsAdjudicationTicketDetails { id - dedupEngineSimilarityPair { - isDuplicate - similarityScore - individual1 { - unicefId - fullName - photo - } - individual2 { - unicefId - fullName - photo - } - } hasDuplicatedDocument extraData { goldenRecords { @@ -12172,6 +12245,19 @@ export const GrievanceTicketDetailedFragmentDoc = gql` proximityToScore score } + dedupEngineSimilarityPair { + similarityScore + individual1 { + unicefId + fullName + photo + } + individual2 { + unicefId + fullName + photo + } + } } goldenRecordsIndividual { id @@ -12595,7 +12681,7 @@ export const RegistrationMinimalFragmentDoc = gql` refuseReason totalHouseholdsCountWithValidPhoneNo adminUrl - isDeduplicated + biometricDeduplicated } `; export const RegistrationDetailedFragmentDoc = gql` @@ -15581,6 +15667,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) { @@ -19272,6 +19394,7 @@ export const PaymentPlanDocument = gql` availablePaymentRecordsCount bankReconciliationSuccess bankReconciliationError + exchangeRate createdBy { id firstName @@ -19520,6 +19643,11 @@ export const PaymentPlanDocument = gql` unicefId } unsuccessfulPaymentsCount + supportingDocuments { + id + title + file + } } } `; @@ -24604,7 +24732,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 ) | ( 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 */ @@ -24715,9 +24843,8 @@ export type ResolversTypes = { Date: ResolverTypeWrapper; DateTime: ResolverTypeWrapper; Decimal: ResolverTypeWrapper; + DeduplicationEngineSimilarityPairIndividualNode: ResolverTypeWrapper; DeduplicationEngineSimilarityPairNode: ResolverTypeWrapper; - DeduplicationEngineSimilarityPairNodeConnection: ResolverTypeWrapper; - DeduplicationEngineSimilarityPairNodeEdge: ResolverTypeWrapper; DeduplicationResultNode: ResolverTypeWrapper; DeleteHouseholdApproveMutation: ResolverTypeWrapper; DeletePaymentPlanMutation: ResolverTypeWrapper; @@ -24727,8 +24854,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; @@ -24910,6 +25042,9 @@ export type ResolversTypes = { PaymentPlanNodeConnection: ResolverTypeWrapper; PaymentPlanNodeEdge: ResolverTypeWrapper; PaymentPlanStatus: PaymentPlanStatus; + PaymentPlanSupportingDocumentNode: ResolverTypeWrapper; + PaymentPlanSupportingDocumentNodeConnection: ResolverTypeWrapper; + PaymentPlanSupportingDocumentNodeEdge: ResolverTypeWrapper; PaymentRecordAndPaymentNode: ResolverTypeWrapper; PaymentRecordDeliveryTypeChoice: PaymentRecordDeliveryTypeChoice; PaymentRecordEntitlementCardStatus: PaymentRecordEntitlementCardStatus; @@ -25124,6 +25259,8 @@ export type ResolversTypes = { UpdatePaymentVerificationStatusAndReceivedAmount: ResolverTypeWrapper; UpdateProgram: ResolverTypeWrapper; UpdateProgramInput: UpdateProgramInput; + UpdateProgramPartners: ResolverTypeWrapper; + UpdateProgramPartnersInput: UpdateProgramPartnersInput; UpdateTargetPopulationInput: UpdateTargetPopulationInput; UpdateTargetPopulationMutation: ResolverTypeWrapper; Upload: ResolverTypeWrapper; @@ -25249,9 +25386,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; @@ -25261,6 +25397,9 @@ export type ResolversParentTypes = { DeleteTargetPopulationMutationInput: DeleteTargetPopulationMutationInput; DeleteTargetPopulationMutationPayload: DeleteTargetPopulationMutationPayload; DeliveredQuantityNode: DeliveredQuantityNode; + DeliveryMechanismDataNode: DeliveryMechanismDataNode; + DeliveryMechanismDataNodeConnection: DeliveryMechanismDataNodeConnection; + DeliveryMechanismDataNodeEdge: DeliveryMechanismDataNodeEdge; DeliveryMechanismDataObjectType: DeliveryMechanismDataObjectType; DeliveryMechanismDataPayloadFieldObjectType: DeliveryMechanismDataPayloadFieldObjectType; DeliveryMechanismNode: DeliveryMechanismNode; @@ -25411,6 +25550,9 @@ export type ResolversParentTypes = { PaymentPlanNode: PaymentPlanNode; PaymentPlanNodeConnection: PaymentPlanNodeConnection; PaymentPlanNodeEdge: PaymentPlanNodeEdge; + PaymentPlanSupportingDocumentNode: PaymentPlanSupportingDocumentNode; + PaymentPlanSupportingDocumentNodeConnection: PaymentPlanSupportingDocumentNodeConnection; + PaymentPlanSupportingDocumentNodeEdge: PaymentPlanSupportingDocumentNodeEdge; PaymentRecordAndPaymentNode: PaymentRecordAndPaymentNode; PaymentRecordNode: PaymentRecordNode; PaymentRecordNodeConnection: PaymentRecordNodeConnection; @@ -25588,6 +25730,8 @@ export type ResolversParentTypes = { UpdatePaymentVerificationStatusAndReceivedAmount: UpdatePaymentVerificationStatusAndReceivedAmount; UpdateProgram: UpdateProgram; UpdateProgramInput: UpdateProgramInput; + UpdateProgramPartners: UpdateProgramPartners; + UpdateProgramPartnersInput: UpdateProgramPartnersInput; UpdateTargetPopulationInput: UpdateTargetPopulationInput; UpdateTargetPopulationMutation: UpdateTargetPopulationMutation; Upload: Scalars['Upload']['output']; @@ -26267,28 +26411,17 @@ export interface DecimalScalarConfig extends GraphQLScalarTypeConfig = { - id?: Resolver; - individual1?: Resolver; - individual2?: Resolver; - isDuplicate?: Resolver, ParentType, ContextType>; - program?: Resolver; - similarityScore?: Resolver, ParentType, ContextType>; - ticketneedsadjudicationdetailsSet?: Resolver>; - __isTypeOf?: IsTypeOfResolverFn; -}; - -export type DeduplicationEngineSimilarityPairNodeConnectionResolvers = { - edgeCount?: Resolver, ParentType, ContextType>; - edges?: Resolver>, ParentType, ContextType>; - pageInfo?: Resolver; - totalCount?: Resolver, ParentType, ContextType>; +export type DeduplicationEngineSimilarityPairIndividualNodeResolvers = { + fullName?: Resolver, ParentType, ContextType>; + photo?: Resolver, ParentType, ContextType>; + unicefId?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; -export type DeduplicationEngineSimilarityPairNodeEdgeResolvers = { - cursor?: Resolver; - node?: Resolver, ParentType, ContextType>; +export type DeduplicationEngineSimilarityPairNodeResolvers = { + individual1?: Resolver, ParentType, ContextType>; + individual2?: Resolver, ParentType, ContextType>; + similarityScore?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -26341,9 +26474,42 @@ export type DeliveredQuantityNodeResolvers; }; +export type DeliveryMechanismDataNodeResolvers = { + createdAt?: Resolver; + data?: Resolver; + deliveryMechanism?: Resolver; + deliveryMechanismChoice?: Resolver, ParentType, ContextType>; + 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; @@ -27402,8 +27568,6 @@ export type IndividualNodeResolvers, ParentType, ContextType>; ageAtRegistration?: Resolver, ParentType, ContextType>; bankAccountInfo?: Resolver, ParentType, ContextType>; - biometricDuplicates1?: Resolver>; - biometricDuplicates2?: Resolver>; birthDate?: Resolver; blockchainName?: Resolver; businessArea?: Resolver; @@ -27419,6 +27583,7 @@ export type IndividualNodeResolvers>>, ParentType, ContextType>; deduplicationGoldenRecordStatus?: Resolver; deleteIndividualTicketDetails?: Resolver>; + deliveryMechanismsData?: Resolver>>, ParentType, ContextType>; detailId?: Resolver, ParentType, ContextType>; disability?: Resolver; disabilityCertificatePicture?: Resolver, ParentType, ContextType>; @@ -27763,6 +27928,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>; }; @@ -27773,7 +27939,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' | '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; }; @@ -27828,7 +27994,6 @@ export type PartnerNodeResolvers, ParentType, ContextType>; parent?: Resolver, ParentType, ContextType>; partnerSet?: Resolver, ParentType, ContextType>; - permissions?: Resolver; programs?: Resolver>; rght?: Resolver; treeId?: Resolver; @@ -27858,7 +28023,6 @@ export type PartnerTypeResolvers; parent?: Resolver, ParentType, ContextType>; partnerSet?: Resolver, ParentType, ContextType>; - permissions?: Resolver; programs?: Resolver>; rght?: Resolver; treeId?: Resolver; @@ -27986,6 +28150,7 @@ export type PaymentPlanNodeResolvers>>, ParentType, ContextType>; dispersionEndDate?: Resolver, ParentType, ContextType>; dispersionStartDate?: Resolver, ParentType, ContextType>; + documents?: Resolver>; endDate?: Resolver, ParentType, ContextType>; exchangeRate?: Resolver, ParentType, ContextType>; excludeHouseholdError?: Resolver; @@ -28018,6 +28183,7 @@ export type PaymentPlanNodeResolvers; steficonAppliedDate?: Resolver, ParentType, ContextType>; steficonRule?: Resolver, ParentType, ContextType>; + supportingDocuments?: Resolver>>, ParentType, ContextType>; targetPopulation?: Resolver; totalDeliveredQuantity?: Resolver, ParentType, ContextType>; totalDeliveredQuantityUsd?: Resolver, ParentType, ContextType>; @@ -28053,6 +28219,28 @@ export type PaymentPlanNodeEdgeResolvers; }; +export type PaymentPlanSupportingDocumentNodeResolvers = { + createdBy?: Resolver, ParentType, ContextType>; + file?: Resolver; + id?: Resolver; + paymentPlan?: Resolver; + title?: Resolver; + uploadedAt?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type PaymentPlanSupportingDocumentNodeConnectionResolvers = { + edges?: Resolver>, ParentType, ContextType>; + pageInfo?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type PaymentPlanSupportingDocumentNodeEdgeResolvers = { + cursor?: Resolver; + node?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type PaymentRecordAndPaymentNodeResolvers = { caId?: Resolver, ParentType, ContextType>; currency?: Resolver, ParentType, ContextType>; @@ -28334,7 +28522,6 @@ export type ProgramNodeResolvers; cycles?: Resolver, ParentType, ContextType, Partial>; dataCollectingType?: Resolver, ParentType, ContextType>; - deduplicationEngineSimilarityPairs?: Resolver>; deduplicationSetId?: Resolver, ParentType, ContextType>; description?: Resolver; endDate?: Resolver, ParentType, ContextType>; @@ -28669,6 +28856,7 @@ export type RegistrationDataImportNodeResolvers; batchUnique?: Resolver; batchUniqueCountAndPercentage?: Resolver>>, ParentType, ContextType>; + biometricDeduplicated?: Resolver, ParentType, ContextType>; biometricDeduplicationEnabled?: Resolver, ParentType, ContextType>; businessArea?: Resolver, ParentType, ContextType>; canMerge?: Resolver, ParentType, ContextType>; @@ -28694,7 +28882,6 @@ export type RegistrationDataImportNodeResolvers; importedBy?: Resolver, ParentType, ContextType>; individuals?: Resolver>; - isDeduplicated?: Resolver, ParentType, ContextType>; messages?: Resolver>; name?: Resolver; numberOfHouseholds?: Resolver; @@ -29437,6 +29624,7 @@ export type TicketIndividualDataUpdateDetailsNodeEdgeResolvers = { + dedupEngineSimilarityPair?: Resolver, ParentType, ContextType>; goldenRecords?: Resolver>>, ParentType, ContextType>; possibleDuplicate?: Resolver>>, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -29444,7 +29632,6 @@ export type TicketNeedsAdjudicationDetailsExtraDataNodeResolvers = { createdAt?: Resolver; - dedupEngineSimilarityPair?: Resolver, ParentType, ContextType>; extraData?: Resolver, ParentType, ContextType>; goldenRecordsIndividual?: Resolver; hasDuplicatedDocument?: Resolver, ParentType, ContextType>; @@ -29690,6 +29877,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>; @@ -29974,9 +30167,8 @@ export type Resolvers = { Date?: GraphQLScalarType; DateTime?: GraphQLScalarType; Decimal?: GraphQLScalarType; + DeduplicationEngineSimilarityPairIndividualNode?: DeduplicationEngineSimilarityPairIndividualNodeResolvers; DeduplicationEngineSimilarityPairNode?: DeduplicationEngineSimilarityPairNodeResolvers; - DeduplicationEngineSimilarityPairNodeConnection?: DeduplicationEngineSimilarityPairNodeConnectionResolvers; - DeduplicationEngineSimilarityPairNodeEdge?: DeduplicationEngineSimilarityPairNodeEdgeResolvers; DeduplicationResultNode?: DeduplicationResultNodeResolvers; DeleteHouseholdApproveMutation?: DeleteHouseholdApproveMutationResolvers; DeletePaymentPlanMutation?: DeletePaymentPlanMutationResolvers; @@ -29985,6 +30177,9 @@ export type Resolvers = { DeleteRegistrationDataImport?: DeleteRegistrationDataImportResolvers; DeleteTargetPopulationMutationPayload?: DeleteTargetPopulationMutationPayloadResolvers; DeliveredQuantityNode?: DeliveredQuantityNodeResolvers; + DeliveryMechanismDataNode?: DeliveryMechanismDataNodeResolvers; + DeliveryMechanismDataNodeConnection?: DeliveryMechanismDataNodeConnectionResolvers; + DeliveryMechanismDataNodeEdge?: DeliveryMechanismDataNodeEdgeResolvers; DeliveryMechanismNode?: DeliveryMechanismNodeResolvers; DeliveryMechanismNodeConnection?: DeliveryMechanismNodeConnectionResolvers; DeliveryMechanismNodeEdge?: DeliveryMechanismNodeEdgeResolvers; @@ -30107,6 +30302,9 @@ export type Resolvers = { PaymentPlanNode?: PaymentPlanNodeResolvers; PaymentPlanNodeConnection?: PaymentPlanNodeConnectionResolvers; PaymentPlanNodeEdge?: PaymentPlanNodeEdgeResolvers; + PaymentPlanSupportingDocumentNode?: PaymentPlanSupportingDocumentNodeResolvers; + PaymentPlanSupportingDocumentNodeConnection?: PaymentPlanSupportingDocumentNodeConnectionResolvers; + PaymentPlanSupportingDocumentNodeEdge?: PaymentPlanSupportingDocumentNodeEdgeResolvers; PaymentRecordAndPaymentNode?: PaymentRecordAndPaymentNodeResolvers; PaymentRecordNode?: PaymentRecordNodeResolvers; PaymentRecordNodeConnection?: PaymentRecordNodeConnectionResolvers; @@ -30258,6 +30456,7 @@ export type Resolvers = { UpdatePaymentVerificationReceivedAndReceivedAmount?: UpdatePaymentVerificationReceivedAndReceivedAmountResolvers; UpdatePaymentVerificationStatusAndReceivedAmount?: UpdatePaymentVerificationStatusAndReceivedAmountResolvers; UpdateProgram?: UpdateProgramResolvers; + UpdateProgramPartners?: UpdateProgramPartnersResolvers; UpdateTargetPopulationMutation?: UpdateTargetPopulationMutationResolvers; Upload?: GraphQLScalarType; UploadImportDataXLSXFileAsync?: UploadImportDataXlsxFileAsyncResolvers; diff --git a/src/frontend/src/__generated__/introspection-result.ts b/src/frontend/src/__generated__/introspection-result.ts index 30b0cfea56..be91b923e8 100644 --- a/src/frontend/src/__generated__/introspection-result.ts +++ b/src/frontend/src/__generated__/introspection-result.ts @@ -16,7 +16,7 @@ "CommunicationMessageNode", "CommunicationMessageRecipientMapNode", "DataCollectingTypeNode", - "DeduplicationEngineSimilarityPairNode", + "DeliveryMechanismDataNode", "DeliveryMechanismNode", "DeliveryMechanismPerPaymentPlanNode", "DocumentNode", @@ -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 => { + const response = await api.get( + `${businessAreaSlug}/programs/${programId}/payment-plans/${paymentPlanId}/supporting-documents/${fileId}/download/`, + ); + return response; +}; 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/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/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/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/apollo/queries/paymentmodule/PaymentPlan.ts b/src/frontend/src/apollo/queries/paymentmodule/PaymentPlan.ts index 99c164147a..a0590581b6 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 @@ -261,6 +262,11 @@ export const PAYMENT_PLAN_QUERY = gql` unicefId } unsuccessfulPaymentsCount + supportingDocuments { + id + title + file + } } } `; 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/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` 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 ( {' '} + /> {dontShowFilename || !acceptedFilename ? t('UPLOAD FILE') : acceptedFilename} 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 = ({
- {t('Face images matching suggests:')} {faceMatchResult} + {t('Face images matching suggests: Duplicates')}
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 ( 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({ {dispersionEndDate} + + + + + {exchangeRate} + + + + + + + + + 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 = ({ {dispersionEndDate} + + + + {exchangeRate} + + + + + + + + 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 +
+
+
+ + FX Rate Applied + +
+ - +
+
+
+ +
{ + 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(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 ( + + + + {t('Supporting Documents')} + + + {canUploadFile && ( + + + + )} + + {documents.length > 0 && ( + + {isExpanded ? : } + + )} + + + + + {documents.length === 0 && ( + + {t('No documents uploaded')} + + )} + + + + + {documents.map((doc) => ( + + + + + {doc.title} + {doc.file} + + + {canDownloadFile && ( + + handleSupportingDocumentDownloadClick(doc.id) + } + data-cy="download-button" + > + + + )} + {canRemoveFile && ( + + confirm({ + title: confirmationModalTitle, + content: confirmationText, + type: 'error', + }).then(() => + handleRemove( + businessArea, + programId, + paymentPlan.id, + doc.id, + ), + ) + } + > + + + )} + + + + + ))} + + + + {canUploadFile && ( + setOpenImport(false)} + scroll="paper" + aria-labelledby="form-dialog-title" + > + + {t('Select File to Upload')} + + + + + {t( + 'The system accepts the following file extensions: XLSX, PDF, images (jpg, jpeg, png). File size must be ≤ 10MB.', + )} + + + <> + { + 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" + /> + {errorMessage} + + + { + setTitle(e.target.value); + setTitleError(''); + }} + fullWidth + margin="normal" + data-cy="title-input" + error={!!titleError} + helperText={titleError} + /> + + + + + + + + {t('Upload')} + + + + )} + + ); +}; 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/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({ {dispersionEndDate} + + + + + {exchangeRate} + + + + + + + + + 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 = ({ {dispersionEndDate} + + + + {exchangeRate} + + + + + + + + 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
+
+
+
+ + FX Rate Applied + +
+ - +
+
+
+ +
theme.palette.error.dark}; @@ -96,7 +97,16 @@ export const PeriodDataUpdatesUploadDialog = (): React.ReactElement => { aria-labelledby="form-dialog-title" > - {t('Periodic Data Updates')} + + + {t('Select Files to Upload')} + + {t( + 'The system accepts the following file extensions: XLSX, PDF, images (jpg, jpeg, png). File size must be \\u2264 10MB.', + )} + + + <> , 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/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/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/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/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; 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/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/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/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/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> } 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/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/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/frontend/src/utils/en.json b/src/frontend/src/utils/en.json index d93c736f5f..75d4ee7696 100644 --- a/src/frontend/src/utils/en.json +++ b/src/frontend/src/utils/en.json @@ -899,5 +899,13 @@ "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.", + "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/__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"] 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/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/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 4becfe0a3f..e5c1bb55b3 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" ] @@ -345,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/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/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/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/permissions.py b/src/hct_mis_api/apps/account/permissions.py index 0438ce5d24..3a5b197a25 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() @@ -109,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/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..2e3c8ae9e5 100644 --- a/src/hct_mis_api/apps/account/signals.py +++ b/src/hct_mis_api/apps/account/signals.py @@ -6,8 +6,7 @@ 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 @receiver(post_save, sender=UserRole) @@ -41,34 +40,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/admin.py b/src/hct_mis_api/apps/core/admin.py index 12272eeab1..91fd4c590e 100644 --- a/src/hct_mis_api/apps/core/admin.py +++ b/src/hct_mis_api/apps/core/admin.py @@ -45,10 +45,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 ( - 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.forms import DataCollectingTypeForm from hct_mis_api.apps.core.models import ( BusinessArea, CountryCodeMap, @@ -66,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, @@ -692,30 +690,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..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__) @@ -100,173 +74,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/management/commands/initdemo.py b/src/hct_mis_api/apps/core/management/commands/initdemo.py index 6d72be2bf0..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,110 +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}/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", - "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", - "ntrncic@unicef.org", - "aafaour@unicef.org", - "aboncenne@unicef.org", - "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", - "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}")) 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..72e6b040b1 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() 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/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/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..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 @@ -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,24 @@ 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, + ) + + photos = sorted([str(individual.photo.name) for individual in distinct_individuals]) + service = BiometricDeduplicationService() + try: + service.report_false_positive_duplicate( + 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") + def close_needs_adjudication_ticket_service(grievance_ticket: GrievanceTicket, user: AbstractUser) -> None: ticket_details = grievance_ticket.ticket_details @@ -168,6 +190,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 +202,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/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/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 202fcaa529..7ab8ae8220 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() @@ -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) diff --git a/src/hct_mis_api/apps/household/schema.py b/src/hct_mis_api/apps/household/schema.py index ba0d921a0e..dca7079fbf 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(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, + parent.business_area, + program_id, + ): + return parent.delivery_mechanisms_data.none() + + return parent.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/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/src/hct_mis_api/apps/payment/admin.py b/src/hct_mis_api/apps/payment/admin.py index a5f2414d21..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, @@ -348,12 +349,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 +444,7 @@ def clean(self) -> Optional[Dict[str, Any]]: class FspXlsxTemplatePerDeliveryMechanismAdminInline(admin.TabularInline): + form = FspXlsxTemplatePerDeliveryMechanismForm model = FspXlsxTemplatePerDeliveryMechanism extra = 0 readonly_fields = ("created_by",) @@ -471,8 +511,14 @@ 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) 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/aurora/__init__.py b/src/hct_mis_api/apps/payment/api/urls/__init__.py similarity index 100% rename from src/hct_mis_api/aurora/__init__.py rename to src/hct_mis_api/apps/payment/api/urls/__init__.py 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/fixtures.py b/src/hct_mis_api/apps/payment/fixtures.py index 23419c4244..bb5878a813 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() @@ -958,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/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/migrations/0145_migration.py b/src/hct_mis_api/apps/payment/migrations/0145_migration.py new file mode 100644 index 0000000000..7749049ce4 --- /dev/null +++ b/src/hct_mis_api/apps/payment/migrations/0145_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', '0144_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/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 7b727a94b2..b99367a57a 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" @@ -2255,7 +2238,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) @@ -2300,10 +2287,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 = {} @@ -2373,6 +2364,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 @@ -2543,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/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> 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/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/schema.py b/src/hct_mis_api/apps/program/schema.py index a8593d2289..2127c6070d 100644 --- a/src/hct_mis_api/apps/program/schema.py +++ b/src/hct_mis_api/apps/program/schema.py @@ -268,10 +268,32 @@ 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 + program=program, + deduplication_engine_status__in=[ + RegistrationDataImport.DEDUP_ENGINE_IN_PROGRESS, + RegistrationDataImport.DEDUP_ENGINE_PROCESSING, + ], ).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/program/utils.py b/src/hct_mis_api/apps/program/utils.py index fbdd5115b5..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 @@ -518,7 +519,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/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/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 f7b7ceb215..f103784a65 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 @@ -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")), @@ -364,30 +366,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 +381,6 @@ class DeduplicationEngineSimilarityPair(models.Model): decimal_places=2, ) - objects = DeduplicationEngineSimilarityPairManager() - class Meta: unique_together = ("individual1", "individual2") constraints = [ @@ -415,6 +391,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 +424,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..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 @@ -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 IgnoredFilenamesPair: + first: str # individual.photo.name + second: str # individual.photo.name + + class DeduplicationEngineAPI(BaseAPI): API_KEY_ENV_NAME = "DEDUPLICATION_ENGINE_API_KEY" API_URL_ENV_NAME = "DEDUPLICATION_ENGINE_API_URL" @@ -53,6 +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/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)) @@ -88,3 +101,11 @@ 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: IgnoredFilenamesPair, deduplication_set_id: str + ) -> None: + self._post( + 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/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 04116986e0..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 @@ -16,7 +16,9 @@ DeduplicationEngineAPI, DeduplicationImage, DeduplicationSet, + DeduplicationSetConfig, DeduplicationSetData, + IgnoredFilenamesPair, 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=1 - (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,28 +145,48 @@ 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"]) - @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.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) 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_PROCESSING, ) for rdi in rdis: rdi.dedup_engine_batch_duplicates = self.get_duplicate_individuals_for_rdi_against_batch_count(rdi) @@ -174,9 +198,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) @@ -185,26 +209,15 @@ 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]: """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""" @@ -221,20 +234,22 @@ 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.duplicates() - .filter( - Q(individual1__in=rdi_individuals) | Q(individual2__in=rdi_individuals), + 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), - Q(individual1__rdi_merge_status=MergeStatusModel.MERGED) - & Q(individual2__rdi_merge_status=MergeStatusModel.MERGED), program=rdi.program, ) + .exclude(Q(individual1__in=other_pending_individuals) | Q(individual2__in=other_pending_individuals)) .distinct() ) @@ -252,8 +267,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), @@ -309,9 +323,19 @@ 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}," - 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_photo: str, individual2_photo: str, deduplication_set_id: str + ) -> None: + 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/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/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/src/hct_mis_api/apps/registration_datahub/validators.py b/src/hct_mis_api/apps/registration_datahub/validators.py index 85e3d44366..f69c067291 100644 --- a/src/hct_mis_api/apps/registration_datahub/validators.py +++ b/src/hct_mis_api/apps/registration_datahub/validators.py @@ -1460,6 +1460,28 @@ def _get_field_type_error( logger.exception(e) raise + @staticmethod + def validate_collectors_unique(household_collectors_data: list) -> Optional[dict]: + 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) + return None + def validate_everything( self, submissions: List, business_area: BusinessArea, skip_validate_pictures: Optional[bool] = False ) -> List: @@ -1554,11 +1576,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 +1666,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/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/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..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 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, @@ -94,6 +97,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", diff --git a/src/hct_mis_api/config/settings.py b/src/hct_mis_api/config/settings.py index 6aafb0a0a0..435bc933d0 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 @@ -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/migrations/__init__.py b/src/hct_mis_api/contrib/__init__.py similarity index 100% rename from src/hct_mis_api/aurora/migrations/__init__.py rename to src/hct_mis_api/contrib/__init__.py diff --git a/src/hct_mis_api/aurora/services/__init__.py b/src/hct_mis_api/contrib/aurora/__init__.py similarity index 100% rename from src/hct_mis_api/aurora/services/__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/contrib/aurora/migrations/__init__.py b/src/hct_mis_api/contrib/aurora/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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/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/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/src/.coveragerc b/tests/.coveragerc similarity index 96% rename from src/.coveragerc rename to tests/.coveragerc index 8e5e354932..7942061b4e 100644 --- a/src/.coveragerc +++ b/tests/.coveragerc @@ -18,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 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 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 31a638fb7d..ab6945a2d4 100644 --- a/tests/selenium/conftest.py +++ b/tests/selenium/conftest.py @@ -9,45 +9,87 @@ 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, @@ -56,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 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 @@ -87,12 +114,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", "")] @@ -187,10 +222,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) @@ -497,6 +532,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 @@ -594,9 +635,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()) 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..aff1b421d2 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"]' @@ -21,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"]' @@ -220,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/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 2d12ee8e54..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"]' @@ -21,7 +22,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 +192,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..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"]' @@ -42,7 +43,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 +117,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 +129,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 +237,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/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 c4ddb45b38..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"]' @@ -16,7 +17,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/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 e251d493b0..9e3a97e2be 100644 --- a/tests/selenium/payment_module/test_payment_plans.py +++ b/tests/selenium/payment_module/test_payment_plans.py @@ -6,14 +6,8 @@ import openpyxl import pytest from dateutil.relativedelta import relativedelta -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 @@ -40,11 +34,20 @@ ) 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) -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 +125,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 @@ -233,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/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 822e39dd12..c07729e89b 100644 --- a/tests/selenium/people/test_people_periodic_data_update.py +++ b/tests/selenium/people/test_people_periodic_data_update.py @@ -2,6 +2,8 @@ from datetime import datetime from time import sleep +from django.conf import settings + import pytest from dateutil.relativedelta import relativedelta @@ -55,8 +57,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/program_details/test_program_details.py b/tests/selenium/program_details/test_program_details.py index 453186ac8f..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) @@ -266,7 +270,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( @@ -277,6 +281,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 +294,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 +755,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 +765,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/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 7b251a291d..f9d673b533 100644 --- a/tests/selenium/programme_management/test_programme_management.py +++ b/tests/selenium/programme_management/test_programme_management.py @@ -1,20 +1,28 @@ import random from datetime import datetime +from time import sleep from django.conf import settings from django.core.management import call_command 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 +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 +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) @@ -368,7 +376,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() @@ -434,7 +442,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() @@ -492,7 +500,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) @@ -562,7 +570,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() @@ -623,7 +631,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() @@ -659,7 +667,6 @@ def test_create_programme_back_scenarios( @pytest.mark.night @pytest.mark.usefixtures("login") class TestManualCalendar: - @freeze_time("2024-09-09") @pytest.mark.parametrize( "test_data", [ @@ -708,7 +715,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", ), @@ -719,7 +726,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", ), @@ -762,6 +769,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 +785,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 +793,97 @@ 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 + 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() + + pageProgrammeManagement.choosePartnerOption("UNHCR") + + 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 + 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") + + programme_edit_url = pageProgrammeManagement.driver.current_url + pageProgrammeManagement.getButtonSave().click() + + # Check Details page + sleep(10) + 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) == 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", [ @@ -836,7 +931,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 @@ -846,6 +941,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 +990,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/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 46ef641e61..3710661764 100644 --- a/tests/selenium/programme_population/test_periodic_data_templates.py +++ b/tests/selenium/programme_population/test_periodic_data_templates.py @@ -1,11 +1,9 @@ import os from time import sleep +from django.conf import settings + import pytest -from tests.selenium.page_object.programme_population.periodic_data_update_templates import ( - PeriodicDatUpdateTemplates, - PeriodicDatUpdateTemplatesDetails, -) from selenium.webdriver.common.by import By from hct_mis_api.apps.core.fixtures import create_afghanistan @@ -25,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) @@ -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 @@ -145,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 ) @@ -291,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 ) 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..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,14 +2,10 @@ from tempfile import NamedTemporaryFile, _TemporaryFileWrapper from typing import Any +from django.conf import settings + import openpyxl import pytest -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, -) from hct_mis_api.apps.core.fixtures import DataCollectingTypeFactory, create_afghanistan from hct_mis_api.apps.core.models import ( @@ -38,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) @@ -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 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 0d561c6e65..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) @@ -145,7 +149,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 diff --git a/tests/selenium/targeting/test_targeting.py b/tests/selenium/targeting/test_targeting.py index 5b5b1b127e..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) @@ -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 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/account/test_admin.py b/tests/unit/apps/account/test_admin.py index 8088a9917e..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 - -from hct_mis_api.apps.account.fixtures import UserFactory -from hct_mis_api.apps.account.models import User - - -class RoleTest(WebTest): - @classmethod - def setUpTestData(cls) -> None: - super().setUpTestData() - 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 - print(res.json) - jres = json.loads(unquote(res.json["data"])) - models = set([item["model"] for item in jres]) - assert len(models) == 1 - assert models == {"account.role"} +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 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/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/cash_assist_datahub/test_pull_from_datahub.py b/tests/unit/apps/cash_assist_datahub/test_pull_from_datahub.py index f820e0301d..6a51e4c2e0 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: @@ -75,6 +73,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/core/test_edopomoga_tp_creation.py b/tests/unit/apps/core/test_edopomoga_tp_creation.py deleted file mode 100644 index 5a55c73dd2..0000000000 --- a/tests/unit/apps/core/test_edopomoga_tp_creation.py +++ /dev/null @@ -1,97 +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 - -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 - - -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) 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/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/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..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 @@ -104,15 +103,16 @@ class TestGrievanceApproveAutomaticMutation(APITestCase): selectedDuplicates { unicefId } - dedupEngineSimilarityPair { - individual1 { - fullName - } - individual2 { - fullName - } - similarityScore - isDuplicate + extraData{ + dedupEngineSimilarityPair { + individual1 { + fullName + } + individual2 { + fullName + } + similarityScore + } } } } @@ -239,18 +239,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..1e6e1c8e19 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 = [ @@ -105,7 +111,10 @@ def test_create_needs_adjudication_ticket_with_the_same_ind(self) -> None: "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_2.deduplication_golden_record_results = { + "duplicates": [], + "possible_duplicates": [], + } ind.save() ind_2.save() create_needs_adjudication_tickets( @@ -158,7 +167,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 +178,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 +191,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"), }, ] @@ -221,7 +239,8 @@ def test_create_na_tickets_biometrics(self) -> None: 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) @@ -230,22 +249,28 @@ def test_create_na_tickets_biometrics(self) -> None: 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.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_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/grievance/test_services_utils.py b/tests/unit/apps/grievance/test_services_utils.py index eaf45cd1bb..08198b31f6 100644 --- a/tests/unit/apps/grievance/test_services_utils.py +++ b/tests/unit/apps/grievance/test_services_utils.py @@ -1,7 +1,9 @@ +import uuid from typing import Any 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 @@ -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, @@ -53,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 @@ -80,7 +85,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") @@ -166,7 +175,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( @@ -208,13 +223,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( @@ -236,8 +271,18 @@ def test_validate_individual_for_need_adjudication(self) -> None: 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() @@ -265,12 +310,22 @@ 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], @@ -314,11 +369,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 +422,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] @@ -401,3 +476,86 @@ 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: MagicMock + ) -> None: + user = UserFactory() + ba = BusinessAreaFactory(slug="afghanistan") + 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}, + { + "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) + 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, + 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, + ), + ) + if not ticket: + raise ValueError("Ticket not created") + ticket.registration_data_import = rdi + ticket.save() + + 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.photo.name), + str(ind_2.photo.name), + str(deduplication_set_id), + ) 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_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 0000000000..c4658974b9 Binary files /dev/null and b/tests/unit/apps/household/test_file/invalid_updated_test_file_wrong_phone_no.xlsx differ 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 0b7e2058a9..36d82ba38a 100644 --- a/tests/unit/apps/household/test_individual_query.py +++ b/tests/unit/apps/household/test_individual_query.py @@ -30,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 @@ -204,13 +209,17 @@ 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 @@ -275,7 +284,12 @@ def setUpTestData(cls) -> None: ) 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.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) @@ -330,7 +344,10 @@ def test_individual_query_single(self, _: Any, permissions: List[Permissions]) - 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( @@ -391,7 +408,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( @@ -447,7 +467,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( @@ -540,7 +563,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( @@ -562,7 +588,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( @@ -584,7 +613,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( @@ -610,7 +642,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 +657,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 +743,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 +792,107 @@ 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")}, + ) diff --git a/tests/unit/apps/household/test_individual_xlsx_update.py b/tests/unit/apps/household/test_individual_xlsx_update.py index 390970ab51..be12933fab 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 @@ -30,17 +31,20 @@ 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() + 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.xlsx" + 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_test_file.xlsx") + return File(BytesIO(content), name="invalid_updated_phone_no_test_file.xlsx") class TestIndividualXlsxUpdate(APITestCase): @@ -71,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, @@ -196,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), + ) 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/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_models1.py b/tests/unit/apps/payment/test_models1.py index 29a06832f2..fcd7a8918f 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,7 +148,12 @@ 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") + 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) @@ -431,7 +455,11 @@ 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) @@ -500,7 +528,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,22 +541,21 @@ 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, 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/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) 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/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/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..5b5ce7f041 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") @@ -310,6 +313,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/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_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/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..57cc5d7fde 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) 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/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/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..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,35 +141,3 @@ def test_linked_rdi(self) -> None: self.rdi_datahub.linked_rdi, 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_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 a0eb301f6e..9086a23d74 100644 --- a/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py +++ b/tests/unit/apps/registration_datahub/test_biometric_deduplication_service.py @@ -22,7 +22,9 @@ DeduplicationEngineAPI, DeduplicationImage, DeduplicationSet, + DeduplicationSetConfig, DeduplicationSetData, + IgnoredFilenamesPair, SimilarityPair, ) from hct_mis_api.apps.registration_datahub.services.biometric_deduplication import ( @@ -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 @@ -62,6 +70,9 @@ 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=1 - (self.program.business_area.biometric_deduplication_threshold / 100) + ), ) ) @@ -84,7 +95,7 @@ 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) @@ -92,8 +103,10 @@ def test_get_deduplication_set(self, mock_get_deduplication_set: mock.Mock) -> N 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") @@ -107,11 +120,18 @@ def test_upload_individuals_success(self, mock_bulk_upload_images: mock.Mock) -> 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 + @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") @@ -123,7 +143,8 @@ def test_upload_individuals_failure(self, mock_bulk_upload_images: mock.Mock) -> 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) @@ -140,7 +161,8 @@ def test_process_deduplication_set(self, mock_process_deduplication: mock.Mock) 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() @@ -148,12 +170,16 @@ def test_process_deduplication_set(self, mock_process_deduplication: mock.Mock) 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): 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) @@ -204,10 +230,12 @@ 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") @@ -228,7 +256,8 @@ def test_upload_and_process_deduplication_set( 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 @@ -296,14 +325,25 @@ 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_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(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_PROCESSING rdi.save() service.mark_rdis_as_deduplication_error(str(self.program.deduplication_set_id)) @@ -317,13 +357,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) @@ -363,8 +406,16 @@ def test_get_duplicates_for_rdi_against_population(self) -> None: assert list( 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) @@ -377,13 +428,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) @@ -417,15 +471,27 @@ 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( 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": ind3.id, + "similarity_score": Decimal("70.00"), + }, + { + "individual1": ind4.id, + "individual2": ind5.id, + "similarity_score": Decimal("70.00"), + }, + { + "individual1": ind3.id, + "individual2": ind4.id, + "similarity_score": Decimal("90.00"), + }, ] def test_get_duplicates_for_rdi_against_batch(self) -> None: @@ -435,13 +501,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) @@ -481,7 +550,11 @@ def test_get_duplicates_for_rdi_against_batch(self) -> None: assert list( 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) assert duplicate_individuals_count == 2 @@ -493,7 +566,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() @@ -507,11 +581,19 @@ 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() @@ -538,7 +620,7 @@ 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) @@ -554,7 +636,8 @@ 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_PROCESSING, ) service.get_duplicate_individuals_for_rdi_against_batch_count = mock.Mock(return_value=8) @@ -593,3 +676,16 @@ def test_update_rdis_deduplication_statistics(self) -> None: 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( + IgnoredFilenamesPair(first="123", second="456"), + str(deduplication_set_id), + ) diff --git a/tests/unit/apps/registration_datahub/test_celery_tasks.py b/tests/unit/apps/registration_datahub/test_celery_tasks.py index dd516ca871..386b3097bd 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 = { @@ -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() 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_deduplication_engine_api.py b/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py index cd2cf87a70..2227780518 100644 --- a/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py +++ b/tests/unit/apps/registration_datahub/test_deduplication_engine_api.py @@ -12,12 +12,20 @@ DeduplicationEngineAPI, DeduplicationImage, DeduplicationSet, + DeduplicationSetConfig, + IgnoredFilenamesPair, ) @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 @@ -40,11 +48,12 @@ def test_create_deduplication_set(self, mock_post: mock.Mock) -> None: 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)) @patch("hct_mis_api.apps.registration_datahub.apis.deduplication_engine.DeduplicationEngineAPI._get") @@ -106,5 +115,25 @@ 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( + IgnoredFilenamesPair(first="123", second="456"), + deduplication_set_id, + ) + + post_mock.assert_called_once_with( + f"deduplication_sets/{deduplication_set_id}/ignored/filenames/", + { + "first": "123", + "second": "456", + }, ) 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_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_kobo_validators_methods.py b/tests/unit/apps/registration_datahub/test_kobo_validators_methods.py index fa8b0db7b4..f8132715a9 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) 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( 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_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) 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_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, ) 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/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..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,6 +146,7 @@ def setUpTestData(cls) -> None: targeting_criteria=targeting_criteria, business_area=cls.business_area, program=cls.program, + program_cycle=cls.cycle, ) cls.target_population_size_2.save() cls.target_population_size_2 = full_rebuild(cls.target_population_size_2) @@ -174,6 +176,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + 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) @@ -220,6 +223,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + 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) @@ -254,6 +258,7 @@ def setUpTestData(cls) -> None: status=TargetPopulation.STATUS_LOCKED, business_area=cls.business_area, program=cls.program, + 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) @@ -266,6 +271,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 +298,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/conftest.py b/tests/unit/conftest.py index 7b7437a9ce..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 = os.path.join(settings.PROJECT_ROOT, "../../tests/unit") + settings.TESTS_ROOT = "/code/tests/unit" settings.CACHES = { "default": { "BACKEND": "hct_mis_api.apps.core.memcache.LocMemCache", 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(), - ) 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)